summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2018-08-03 20:02:04 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2018-08-05 16:19:57 +0200
commita0e2b9398a06ca2ea164c2ffd6fd89f713b93598 (patch)
treef070de47673f32903eae30ddbc57232e2b13db4e
parentea743668192e0921ab46d5e863df5b754ce82656 (diff)
Add support for partial buffers in cache and rendering engine - fixes dropouts on framesize changes for example when looping.
-rw-r--r--src/audiocache.cc27
-rw-r--r--src/drumgizmo.cc155
-rw-r--r--src/drumgizmo.h1
-rw-r--r--src/events.h12
-rw-r--r--test/dgreftest/compareoutputengine.cc21
-rw-r--r--test/dgreftest/compareoutputengine.h2
-rw-r--r--test/dgreftest/dgreftest.cc24
7 files changed, 128 insertions, 114 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;
};
-
diff --git a/test/dgreftest/compareoutputengine.cc b/test/dgreftest/compareoutputengine.cc
index 33dfe2a..04145b0 100644
--- a/test/dgreftest/compareoutputengine.cc
+++ b/test/dgreftest/compareoutputengine.cc
@@ -33,6 +33,7 @@ CompareOutputEngine::CompareOutputEngine()
, info{}
, file{"output"}
{
+ info = {};
info.samplerate = 44100;
info.channels = 1;
info.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
@@ -44,9 +45,9 @@ CompareOutputEngine::~CompareOutputEngine()
sf_close(handle);
}
-bool CompareOutputEngine::init(const Channels& data)
+bool CompareOutputEngine::init(const Channels& channels)
{
- info.channels = data.size();
+ info.channels = channels.size();
handle = sf_open(file.c_str(), SFM_READ, &info);
if(handle == nullptr)
@@ -116,8 +117,11 @@ void CompareOutputEngine::run(int ch, sample_t* samples, size_t nsamples)
void CompareOutputEngine::post(size_t nsamples)
{
- sample_t ref_buffer[sizeof(buffer) / sizeof(sample_t)];
- sf_readf_float(handle, ref_buffer, nsamples);
+ nsamples = sf_readf_float(handle, ref_buffer, nsamples);
+ if(nsamples == 0)
+ {
+ return;
+ }
for(std::size_t i = 0; i < nsamples; ++i)
{
@@ -126,10 +130,17 @@ void CompareOutputEngine::post(size_t nsamples)
if(buffer[i * info.channels + ch] != ref_buffer[i * info.channels + ch])
{
++diff_samples;
+
+ // Use this to quit on first bad sample.
+ //std::cerr << "ch: " << ch << ", pos: " << pos + i <<
+ // " expected: " << ref_buffer[i * info.channels + ch] <<
+ // " got: " << buffer[i * info.channels + ch] << std::endl;
+ //exit(1);
+
}
}
}
-
+ pos += nsamples;
}
size_t CompareOutputEngine::getSamplerate() const
diff --git a/test/dgreftest/compareoutputengine.h b/test/dgreftest/compareoutputengine.h
index a82116c..89a3a83 100644
--- a/test/dgreftest/compareoutputengine.h
+++ b/test/dgreftest/compareoutputengine.h
@@ -53,4 +53,6 @@ private:
std::string file;
sample_t buffer[4096 * 16];
std::size_t diff_samples{0};
+ sample_t ref_buffer[sizeof(buffer) / sizeof(sample_t)];
+ size_t pos{0};
};
diff --git a/test/dgreftest/dgreftest.cc b/test/dgreftest/dgreftest.cc
index ea38091..a4eb897 100644
--- a/test/dgreftest/dgreftest.cc
+++ b/test/dgreftest/dgreftest.cc
@@ -135,14 +135,32 @@ int main(int argc, char* argv[])
size_t nsamples = oe->getBufferSize();
sample_t *samples = (sample_t *)malloc(nsamples * sizeof(sample_t));
- drumgizmo.setFrameSize(oe->getBufferSize());
+ drumgizmo.setFrameSize(nsamples);
ie.start();
oe->start();
- while(drumgizmo.run(pos, samples, nsamples) == true)
+ size_t framesize = nsamples;
+ int dir = -1;
+ while(drumgizmo.run(pos, samples, framesize) == true)
{
- pos += nsamples;
+ pos += framesize;
+
+ framesize += dir;
+
+ if(framesize < 1)
+ {
+ framesize = 1;
+ dir = 1;
+ }
+
+ if(framesize >= nsamples)
+ {
+ framesize = nsamples;
+ dir = -1;
+ }
+
+ drumgizmo.setFrameSize(framesize);
}
ie.stop();