summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2015-07-19 14:06:10 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2015-07-19 14:06:10 +0200
commit7f19dfdba76e9d127f8f6c1e1c3c547755e7d7c1 (patch)
tree092f4511f027460df5d0e9189717d4a05ecfb1fe
parentd35c16931056ec4b07706e3955f6198ce0e43cf7 (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.
-rw-r--r--src/cachemanager.cc237
-rw-r--r--src/cachemanager.h13
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__*/