diff options
author | Bent Bisballe Nyeng <deva@aasimon.org> | 2018-08-03 20:02:04 +0200 |
---|---|---|
committer | Bent Bisballe Nyeng <deva@aasimon.org> | 2018-08-05 16:19:57 +0200 |
commit | a0e2b9398a06ca2ea164c2ffd6fd89f713b93598 (patch) | |
tree | f070de47673f32903eae30ddbc57232e2b13db4e /src | |
parent | ea743668192e0921ab46d5e863df5b754ce82656 (diff) |
Add support for partial buffers in cache and rendering engine - fixes dropouts on framesize changes for example when looping.
Diffstat (limited to 'src')
-rw-r--r-- | src/audiocache.cc | 27 | ||||
-rw-r--r-- | src/drumgizmo.cc | 155 | ||||
-rw-r--r-- | src/drumgizmo.h | 1 | ||||
-rw-r--r-- | src/events.h | 12 |
4 files changed, 89 insertions, 106 deletions
diff --git a/src/audiocache.cc b/src/audiocache.cc index 2e9eaf8..a2d26d9 100644 --- a/src/audiocache.cc +++ b/src/audiocache.cc @@ -152,8 +152,6 @@ sample_t* AudioCache::open(const AudioFile& file, sample_t* AudioCache::next(cacheid_t id, std::size_t& size) { - size = framesize; - if(id == CACHE_DUMMYID) { settings.number_of_underruns.fetch_add(1); @@ -165,19 +163,14 @@ sample_t* AudioCache::next(cacheid_t id, std::size_t& size) if(c.preloaded_samples) { - // We are playing from memory: if(c.localpos < c.preloaded_samples_size) { sample_t* s = c.preloaded_samples + c.localpos; + // If only a partial frame is returned. Reflect this in the size + size = std::min(size, c.preloaded_samples_size - c.localpos); - if((c.localpos + framesize) > c.preloaded_samples_size) - { - // Only a partial frame is returned. Reflect this in the size - size = c.preloaded_samples_size - c.localpos; - } - - c.localpos += framesize; + c.localpos += size; return s; } @@ -186,7 +179,6 @@ sample_t* AudioCache::next(cacheid_t id, std::size_t& size) } else { - // We are playing from cache: if(c.localpos < chunk_size) { @@ -194,13 +186,15 @@ sample_t* AudioCache::next(cacheid_t id, std::size_t& size) { // Just return silence. settings.number_of_underruns.fetch_add(1); - c.localpos += framesize; // Skip these samples so we don't loose sync. + c.localpos += size; // Skip these samples so we don't loose sync. assert(nodata); return nodata; } sample_t* s = c.front + c.localpos; - c.localpos += framesize; + // If only a partial frame is returned. Reflect this in the size + size = std::min(size, chunk_size - c.localpos); + c.localpos += size; return s; } } @@ -210,7 +204,7 @@ sample_t* AudioCache::next(cacheid_t id, std::size_t& size) { // Just return silence. settings.number_of_underruns.fetch_add(1); - c.localpos += framesize; // Skip these samples so we don't loose sync. + c.localpos += size; // Skip these samples so we don't loose sync. assert(nodata); return nodata; } @@ -219,7 +213,7 @@ sample_t* AudioCache::next(cacheid_t id, std::size_t& size) std::swap(c.front, c.back); // Next time we go here we have already read the first frame. - c.localpos = framesize; + c.localpos = size; c.pos += chunk_size; @@ -239,7 +233,6 @@ sample_t* AudioCache::next(cacheid_t id, std::size_t& size) // We should always have a front buffer at this point. assert(c.front); - return c.front; } @@ -266,8 +259,6 @@ void AudioCache::close(cacheid_t id) void AudioCache::setFrameSize(std::size_t framesize) { - DEBUG(cache, "%s\n", __PRETTY_FUNCTION__); - // Make sure the event handler thread is stalled while we set the framesize // state. std::lock_guard<AudioCacheEventHandler> event_handler_lock(event_handler); diff --git a/src/drumgizmo.cc b/src/drumgizmo.cc index 2d3e410..81db5c5 100644 --- a/src/drumgizmo.cc +++ b/src/drumgizmo.cc @@ -93,12 +93,6 @@ void DrumGizmo::setFrameSize(size_t framesize) this->framesize = framesize; - // Remove all active events as they are cached using the old framesize. - for(std::size_t ch = 0; ch < MAX_NUM_CHANNELS; ++ch) - { - activeevents[ch].clear(); - } - // Update framesize in drumkitloader and cachemanager: loader.setFrameSize(framesize); audio_cache.setFrameSize(framesize); @@ -240,11 +234,69 @@ bool DrumGizmo::run(size_t pos, sample_t *samples, size_t nsamples) return true; } -#undef SSE // SSE broken for now ... so disable it. -#ifdef SSE -#define N 8 -typedef float vNsf __attribute__ ((vector_size(sizeof(sample_t)*N))); -#endif/*SSE*/ +void DrumGizmo::renderSampleEvent(EventSample& evt, int pos, sample_t *s, std::size_t sz) +{ + size_t n = 0; // default start point is 0. + + // If we are not at offset 0 in current buffer: + if(evt.offset > (size_t)pos) + { + n = evt.offset - pos; + } + + size_t end = sz; // default end point is the end of the buffer. + + // Find the end point intra-buffer + if((evt.t + end - n) > evt.sample_size) + { + end = evt.sample_size - evt.t + n; + } + + // This should not be necessary but make absolutely sure that we do + // not write over the end of the buffer. + if(end > sz) + { + end = sz; + } + + size_t t = 0; // Internal buffer counter + +repeat: + float scale = 1.0f; + for(; (n < end) && (t < (evt.buffer_size - evt.buffer_ptr)); ++n) + { + assert(n >= 0); + assert(n < sz); + + assert(t >= 0); + assert(t < evt.buffer_size - evt.buffer_ptr); + + if(evt.rampdownInProgress() && evt.rampdown_count > 0) + { + scale = std::min((float)evt.rampdown_count/evt.ramp_length, 1.f); + evt.rampdown_count--; + } + + s[n] += evt.buffer[evt.buffer_ptr + t] * evt.scale * scale; + ++t; + } + + // Add internal buffer counter to "global" event counter. + evt.t += t;//evt.buffer_size; + evt.buffer_ptr += t; + + if(n != sz && evt.t < evt.sample_size) + { + evt.buffer_size = sz - n;// Hint new size + + // More samples needed for current buffer + evt.buffer = audio_cache.next(evt.cache_id, evt.buffer_size); + + evt.buffer_ptr = 0; + t = 0; + goto repeat; + } +} void DrumGizmo::getSamples(int ch, int pos, sample_t* s, size_t sz) { @@ -260,7 +312,8 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t* s, size_t sz) Event* event = *i; Event::type_t type = event->getType(); - switch(type) { + switch(type) + { case Event::sample: { EventSample& evt = *static_cast<EventSample*>(event); @@ -272,9 +325,9 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t* s, size_t sz) break; } - // Don't handle event now is is scheduled for a future iteration? if(evt.offset > (pos + sz)) { + // Don't handle event now. It is scheduled for a future iteration. continue; } @@ -290,86 +343,24 @@ void DrumGizmo::getSamples(int ch, int pos, sample_t* s, size_t sz) } evt.buffer_size = initial_chunksize; + evt.sample_size = af.size; } { std::lock_guard<std::mutex> guard(af.mutex); - size_t n = 0; // default start point is 0. - - // If we are not at offset 0 in current buffer: - if(evt.offset > (size_t)pos) - { - n = evt.offset - pos; - } - - size_t end = sz; // default end point is the end of the buffer. - - // Find the end point intra-buffer - if((evt.t + end - n) > af.size) - { - end = af.size - evt.t + n; - } + renderSampleEvent(evt, pos, s, sz); - // This should not be necessary but make absolutely sure that we do - // not write over the end of the buffer. - if(end > sz) + if((evt.t >= evt.sample_size) || (evt.rampdown_count == 0)) { - end = sz; - } - - size_t t = 0; // Internal buffer counter - if(!evt.rampdownInProgress()) - { -#ifdef SSE - size_t optend = ((end - n) / N) * N + n; - - // Force source addr to be 16 byte aligned... - // (might skip 1 or 2 samples) - while((size_t)&evt.buffer[t] % 16) - { - ++t; - } - - for(; (n < optend) && (t < evt.buffer_size); n += N) - { - *(vNsf*)&(s[n]) += *(vNsf*)&(evt.buffer[t]) * evt.scale; - t += N; - } -#endif - for(; (n < end) && (t < evt.buffer_size); ++n) - { - assert(n >= 0); - assert(n < sz); - - assert(t >= 0); - assert(t < evt.buffer_size); - - s[n] += evt.buffer[t] * evt.scale; - ++t; - } - } - else - { // Ramp down in progress. - for(; (n < end) && (t < evt.buffer_size) && evt.rampdown_count; ++n) - { - float scale = std::min((float)evt.rampdown_count/evt.ramp_length, 1.f); - s[n] += evt.buffer[t] * evt.scale * scale; - ++t; - evt.rampdown_count--; - } + removeevent = true; } - // Add internal buffer counter to "global" event counter. - evt.t += evt.buffer_size; - - if((evt.t < af.size) && (evt.rampdown_count != 0)) + if(evt.buffer_ptr >= evt.buffer_size && removeevent == false) { + evt.buffer_size = sz; evt.buffer = audio_cache.next(evt.cache_id, evt.buffer_size); - } - else - { - removeevent = true; + evt.buffer_ptr = 0; } if(removeevent) diff --git a/src/drumgizmo.h b/src/drumgizmo.h index 562e2ba..0f2c20e 100644 --- a/src/drumgizmo.h +++ b/src/drumgizmo.h @@ -56,6 +56,7 @@ public: bool run(size_t pos, sample_t *samples, size_t nsamples); void stop(); + void renderSampleEvent(EventSample& evt, int pos, sample_t *s, std::size_t sz); void getSamples(int ch, int pos, sample_t *s, size_t sz); //! Get the current engine latency in samples. diff --git a/src/events.h b/src/events.h index b78dd79..c28283e 100644 --- a/src/events.h +++ b/src/events.h @@ -31,8 +31,6 @@ #include <string> #include <mutex> -#include <sndfile.h> - #include "audiofile.h" #include "audio.h" #include "audiocache.h" @@ -62,7 +60,8 @@ public: timepos_t offset; //< Global position (ie. not relative to buffer) }; -class EventSample : public Event +class EventSample + : public Event { public: EventSample(channel_t c, float g, AudioFile* af, @@ -91,10 +90,12 @@ public: cacheid_t cache_id; sample_t* buffer; - size_t buffer_size; + std::size_t buffer_size; + std::size_t buffer_ptr{0}; //< Internal pointer into the current buffer + std::size_t sample_size{0}; //< Total number of audio samples in this sample. float gain; - unsigned int t; + unsigned int t; //< Internal sample position. AudioFile* file; std::string group; void* instrument; @@ -118,4 +119,3 @@ private: std::multimap<timepos_t, Event*> queue; std::mutex mutex; }; - |