diff options
| author | Bent Bisballe Nyeng <deva@aasimon.org> | 2015-07-19 14:06:10 +0200 | 
|---|---|---|
| committer | Bent Bisballe Nyeng <deva@aasimon.org> | 2015-07-19 14:06:10 +0200 | 
| commit | 7f19dfdba76e9d127f8f6c1e1c3c547755e7d7c1 (patch) | |
| tree | 092f4511f027460df5d0e9189717d4a05ecfb1fe /src | |
| parent | d35c16931056ec4b07706e3955f6198ce0e43cf7 (diff) | |
Make cachemanager behave as if it isn't there when CHUNK_MULTIPLIER is big enough to contain all data in an audio file. Add refcounted file handling in cachemanager.
Diffstat (limited to 'src')
| -rw-r--r-- | src/cachemanager.cc | 237 | ||||
| -rw-r--r-- | src/cachemanager.h | 13 | 
2 files changed, 171 insertions, 79 deletions
| diff --git a/src/cachemanager.cc b/src/cachemanager.cc index 4053f05..63f99bc 100644 --- a/src/cachemanager.cc +++ b/src/cachemanager.cc @@ -34,23 +34,48 @@  #include <hugin.hpp> -#define	BUFFER_SIZE	4092 -  #define CHUNKSIZE(x) (x * CHUNK_MULTIPLIER) -static size_t readChunk(std::string filename, int filechannel, size_t pos, -                        size_t num_samples, sample_t* buf) -{ +class AFile { +public: +  AFile(std::string filename) +    : ref(0) +    , filename(filename) +  { +    fh = sf_open(filename.c_str(), SFM_READ, &sf_info); +    if(!fh) { +      ERR(audiofile,"SNDFILE Error (%s): %s\n", +          filename.c_str(), sf_strerror(fh)); +      //return 0; +    } +  } + +  ~AFile() +  { +    sf_close(fh); +    fh = NULL; +  } + +  int ref; +  SNDFILE* fh;    SF_INFO sf_info; -  SNDFILE *fh = sf_open(filename.c_str(), SFM_READ, &sf_info); -  if(!fh) { -    ERR(audiofile,"SNDFILE Error (%s): %s\n", -        filename.c_str(), sf_strerror(fh)); -    return 0; +  std::string filename; +}; + +static void readChunk(AFile* file, int filechannel, size_t pos, +                      size_t num_samples, sample_t* buf) +{ +  SNDFILE* fh = file->fh; +  SF_INFO& sf_info = file->sf_info; + +  if(fh == NULL) { +    printf("File handle is null.\n"); +    return;    }    if(pos > sf_info.frames) { -    return 0; +    printf("pos > sf_info.frames\n"); +    return;    }    sf_seek(fh, pos, SEEK_SET); @@ -58,31 +83,24 @@ static size_t readChunk(std::string filename, int filechannel, size_t pos,    size_t size = sf_info.frames - pos;    if(size > num_samples) size = num_samples; -  sample_t* data = buf; -  if(sf_info.channels == 1) { -    size = sf_readf_float(fh, data, size); -  } else { -    // check filechannel exists -    if(filechannel >= sf_info.channels) { -      filechannel = sf_info.channels - 1; -    } -    sample_t buffer[BUFFER_SIZE]; -    int readsize = BUFFER_SIZE / sf_info.channels; -    int totalread = 0; -    int read; -    do { -      read = sf_readf_float(fh, buffer, readsize); -      for (int i = 0; i < read && totalread < (int)size; i++) { -        data[totalread++] = buffer[i * sf_info.channels + filechannel]; -      } -    } while(read > 0 && totalread < (int)size && totalread < sf_info.frames); -    // set data size to total bytes read -    size = totalread; +  static sample_t *read_buffer = NULL; +  static size_t read_buffer_size = 0; + +  if((size * sf_info.channels) > read_buffer_size) { +    delete[] read_buffer; +    read_buffer_size = size * sf_info.channels; +    read_buffer = new sample_t[read_buffer_size]; +    // TODO: This buffer is never free'd on app shutdown.    } -  sf_close(fh); +  size_t read_size = sf_readf_float(fh, read_buffer, size); +  (void)read_size; -  return size; +  size_t channel = filechannel; +  sample_t *data = buf; +  for (size_t i = 0; i < size; i++) { +    data[i] = read_buffer[(i * sf_info.channels) + channel]; +  }  }  CacheManager::CacheManager() @@ -124,10 +142,16 @@ void CacheManager::deinit()  }  // Invariant: initial_samples_needed < preloaded audio data  -// Proposal: preloaded > 2 x CHUNKSIZE? So that we can fill c.front immediatly on open  sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed,                               int channel, cacheid_t &id)  { +  if(!file->isValid()) { +    // File preload not yet ready - skip this sample. +    id = CACHE_DUMMYID; +    assert(nodata); +    return nodata; +  } +    {      MutexAutolock l(m_ids);      if(availableids.empty()) { @@ -143,36 +167,67 @@ sample_t *CacheManager::open(AudioFile *file, size_t initial_samples_needed,      return nodata;    } +  AFile *afile = NULL; +  if(files.find(file->filename) == files.end()) { +    afile = new AFile(file->filename); +    files[file->filename] = afile; +  } else { +    afile = files[file->filename]; +  } + +  // Increase ref count. +  afile->ref++; +    cache_t c; -  c.file = file; +  c.file = afile;    c.channel = channel; -  c.pos = initial_samples_needed; -  c.localpos = 0; -  c.front = new sample_t[CHUNKSIZE(framesize)]; -  c.back = new sample_t[CHUNKSIZE(framesize)]; - -  size_t size = CHUNKSIZE(framesize); -  if(size > (file->preloadedsize - c.pos)) size = (file->preloadedsize - c.pos); -  memcpy(c.front, c.file->data + c.pos, size * sizeof(sample_t)); + +  // next call to 'next' will read from this point. +  c.localpos = initial_samples_needed; + +  c.front = NULL; // This is allocated when needed. +  c.back = NULL; // This is allocated when needed. + +  // cropped_size is the preload chunk size cropped to sample length. +  size_t cropped_size = file->preloadedsize - c.localpos; +  cropped_size /= framesize; +  cropped_size *= framesize; +  cropped_size += initial_samples_needed; + +  if(file->preloadedsize == file->size) { +    // We have preloaded the entire file, so use it. +    cropped_size = file->preloadedsize; +  } + +  c.preloaded_samples = file->data; +  c.preloaded_samples_size = cropped_size; +    c.ready = false; -  c.pos += size; -  // Increase audio ref count +  // next read from disk will read from this point. +  c.pos = cropped_size;//c.preloaded_samples_size;    {      MutexAutolock l(m_ids);      id2cache[id] = c;    } +  // NOTE: Below this point we can no longer write to 'c'.    // Only load next buffer if there are more data in the file to be loaded...    if(c.pos < file->size) { +    cache_t& c = id2cache[id]; // Create new writeable 'c'. + +    if(c.back == NULL) { +      c.back = new sample_t[CHUNKSIZE(framesize)]; +    } +      cevent_t e =        createLoadNextEvent(c.file, c.channel, c.pos, c.back);      e.ready = &id2cache[id].ready;      pushEvent(e);    } -  return file->data; // preloaded data +  return c.preloaded_samples; // return preloaded data  }  sample_t *CacheManager::next(cacheid_t id, size_t &size)  @@ -185,32 +240,58 @@ sample_t *CacheManager::next(cacheid_t id, size_t &size)    }    cache_t& c = id2cache[id]; -  if(c.localpos < CHUNKSIZE(framesize)) { -    sample_t *s = c.front + c.localpos; -    c.localpos += size; -    return s; + +  if(c.preloaded_samples) { + +    // We are playing from memory: +    if(c.localpos < c.preloaded_samples_size) { +      sample_t *s = c.preloaded_samples + c.localpos; +      c.localpos += framesize; +      return s; +    } + +    c.preloaded_samples = NULL; // Start using samples from disk. + +  } else { + +    // We are playing from cache: +    if(c.localpos < CHUNKSIZE(framesize)) { +      sample_t *s = c.front + c.localpos; +      c.localpos += framesize; +      return s; +    }    }    if(!c.ready) { -    //printf("#%d: NOT READY!\n", id); // TODO: Count and show in UI? +    printf("#%d: NOT READY!\n", id); // TODO: Count and show in UI? +    return nodata;    }    // Swap buffers -  sample_t *tmp = c.front; -  c.front = c.back; -  c.back = tmp; +  std::swap(c.front, c.back); -  c.localpos = size; // Next time we go here we have already read the first frame. +  // Next time we go here we have already read the first frame. +  c.localpos = framesize;    c.pos += CHUNKSIZE(framesize); -  if(c.pos < c.file->size) { +  if(c.pos < c.file->sf_info.frames) { +    if(c.back == NULL) { +      c.back = new sample_t[CHUNKSIZE(framesize)]; +    } +      cevent_t e = createLoadNextEvent(c.file, c.channel, c.pos, c.back);      c.ready = false;      e.ready = &c.ready;      pushEvent(e);    }  +  if(!c.front) { +    printf("We shouldn't get here... ever!\n"); +    assert(false); +    return nodata; +  } +    return c.front;  } @@ -226,33 +307,40 @@ void CacheManager::close(cacheid_t id)  void CacheManager::setFrameSize(size_t framesize)  { -  this->framesize = framesize; -  delete[] nodata; -  nodata = new sample_t[framesize]; +  if(framesize > this->framesize) { +    delete[] nodata; +    nodata = new sample_t[framesize]; -  for(size_t i = 0; i < framesize; i++) { -    nodata[i] = 0; +    for(size_t i = 0; i < framesize; i++) { +      nodata[i] = 0; +    }    } + +  this->framesize = framesize;  }  void CacheManager::handleLoadNextEvent(cevent_t &e)   { -#if 0 // memcpy -  size_t size = CHUNKSIZE(framesize); -  if(size > (e.file->size - e.pos)) { -    size = (e.file->size - e.pos); -  } -  memcpy(e.buffer, e.file->data + e.pos, size * sizeof(sample_t)); -#elif 1 // diskread -  //memset(e.buffer, 0, CHUNKSIZE(framesize) * sizeof(sample_t)); -  readChunk(e.file->filename, e.channel, e.pos, CHUNKSIZE(framesize), e.buffer); -#endif +  assert(files.find(e.file->filename) != files.end()); +  readChunk(files[e.file->filename], e.channel, e.pos, CHUNKSIZE(framesize), e.buffer);    *e.ready = true;  }  void CacheManager::handleCloseEvent(cevent_t &e)   {    cache_t& c = id2cache[e.id]; + +  auto f = files.find(c.file->filename); + +  if(f != files.end()) { +    // Decrease ref count and close file if needed (in AFile destructor). +    files[c.file->filename]->ref--; +    if(files[c.file->filename]->ref == 0) { +      delete f->second; +      files.erase(f); +    } +  } +    delete[] c.front;    delete[] c.back; @@ -260,8 +348,6 @@ void CacheManager::handleCloseEvent(cevent_t &e)      MutexAutolock l(m_ids);      availableids.push_back(e.id);    } - -  // TODO: Count down ref counter on c.file and close it if 0.  } @@ -308,8 +394,7 @@ void CacheManager::pushEvent(cevent_t e)      return;    } -  // Check that if event should be merged (Maybe by event queue (ie. push -  // in front). +  // TODO: Check if event should be merged.    {      MutexAutolock l(m_events);      eventqueue.push_back(e); @@ -319,7 +404,7 @@ void CacheManager::pushEvent(cevent_t e)  }  CacheManager::cevent_t -CacheManager::createLoadNextEvent(AudioFile *file, size_t channel, size_t pos, +CacheManager::createLoadNextEvent(AFile *file, size_t channel, size_t pos,                                    sample_t* buffer)  {    cevent_t e; diff --git a/src/cachemanager.h b/src/cachemanager.h index d98d66c..a70af35 100644 --- a/src/cachemanager.h +++ b/src/cachemanager.h @@ -45,6 +45,7 @@  class AudioFile;  typedef int cacheid_t; +class AFile;  //TODO: @@ -139,13 +140,17 @@ private:    sample_t *nodata;    typedef struct { -    AudioFile *file; +    AFile *file;      size_t channel;      size_t pos; //< File possition      volatile bool ready;      sample_t *front;      sample_t *back;      size_t localpos; //< Intra buffer (front) position. + +    sample_t* preloaded_samples; // NULL means not active. +    size_t preloaded_samples_size; +    } cache_t;    typedef enum { @@ -163,11 +168,11 @@ private:      size_t pos;      sample_t *buffer;      volatile bool *ready; -    AudioFile *file;      size_t channel; +    AFile *file;    } cevent_t; -  cevent_t createLoadNextEvent(AudioFile *file, size_t channel, size_t pos, +  cevent_t createLoadNextEvent(AFile *file, size_t channel, size_t pos,                                 sample_t* buffer);    cevent_t createCloseEvent(cacheid_t id); @@ -190,6 +195,8 @@ private:    Semaphore sem;    Semaphore sem_run;    bool running; + +  std::map<std::string, AFile*> files;  };  #endif/*__DRUMGIZMO_CACHEMANAGER_H__*/ | 
