diff --git a/include-gpl/audio/audiooutput.h b/include-gpl/audio/audiooutput.h index 10816ac51..7523efffd 100644 --- a/include-gpl/audio/audiooutput.h +++ b/include-gpl/audio/audiooutput.h @@ -31,7 +31,7 @@ class AudioOutputPipe; class SDRANGELOVE_API AudioOutput : QIODevice { public: AudioOutput(); - ~AudioOutput(); + virtual ~AudioOutput(); bool start(int device, int rate); void stop(); @@ -47,9 +47,9 @@ private: AudioFifos m_audioFifos; std::vector m_mixBuffer; - bool open(OpenMode mode); - qint64 readData(char* data, qint64 maxLen); - qint64 writeData(const char* data, qint64 len); + //virtual bool open(OpenMode mode); + virtual qint64 readData(char* data, qint64 maxLen); + virtual qint64 writeData(const char* data, qint64 len); friend class AudioOutputPipe; }; diff --git a/include-gpl/dsp/dspengine.h b/include-gpl/dsp/dspengine.h index 77d1fc1e7..cd9350241 100644 --- a/include-gpl/dsp/dspengine.h +++ b/include-gpl/dsp/dspengine.h @@ -98,7 +98,7 @@ private: typedef std::list ThreadedSampleSinks; ThreadedSampleSinks m_threadedSampleSinks; //!< sample sinks on their own threads (usually channels) - AudioOutput m_audioSink; + AudioOutput m_audioOutput; uint m_sampleRate; quint64 m_centerFrequency; diff --git a/include/dsp/threadedsamplesink.h b/include/dsp/threadedsamplesink.h index a3461b219..ef8aafea4 100644 --- a/include/dsp/threadedsamplesink.h +++ b/include/dsp/threadedsamplesink.h @@ -46,7 +46,7 @@ public: void stop(); //!< this thread exit() and wait() bool sendWaitSink(Message& cmd); //!< Send message to sink synchronously - void feed(SampleVector::const_iterator& begin, SampleVector::const_iterator& end, bool positiveOnly); //!< Feed sink with samples + void feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly); //!< Feed sink with samples QString getSampleSinkObjectName() const; diff --git a/plugins/channel/am/amdemod.cpp b/plugins/channel/am/amdemod.cpp index a241b15dd..7fd09dc9a 100644 --- a/plugins/channel/am/amdemod.cpp +++ b/plugins/channel/am/amdemod.cpp @@ -63,7 +63,7 @@ void AMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_itera { Complex ci; - if (m_audioFifo->size() <= 0) + if (m_audioFifo->size() == 0) { return; } @@ -73,68 +73,67 @@ void AMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_itera Complex c(it->real() / 32768.0, it->imag() / 32768.0); c *= m_nco.nextIQ(); + if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) { - if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) + m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0)); + + Real magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); + m_movingAverage.feed(magsq); + + if (m_movingAverage.average() >= m_squelchLevel) { - m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0)); - - Real magsq = ci.real() * ci.real() + ci.imag() * ci.imag(); - m_movingAverage.feed(magsq); - - if (m_movingAverage.average() >= m_squelchLevel) - { - m_squelchState = m_running.m_audioSampleRate/ 20; - } - - qint16 sample; - - if (m_squelchState > 0) - { - m_squelchState--; - Real demod = sqrt(magsq); - - demod = m_lowpass.filter(demod); - - if (demod < -1) - { - demod = -1; - } - else if (demod > 1) - { - demod = 1; - } - - m_volumeAGC.feed(demod); - - demod *= (0.003 / m_volumeAGC.getValue()); - demod *= m_running.m_volume; - sample = demod * 32700 * 16; - - } - else - { - m_volumeAGC.close(); - sample = 0; - } - - m_audioBuffer[m_audioBufferFill].l = sample; - m_audioBuffer[m_audioBufferFill].r = sample; - ++m_audioBufferFill; - - if (m_audioBufferFill >= m_audioBuffer.size()) - { - uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1); - - if (res != m_audioBufferFill) - { - qDebug("lost %u audio samples", m_audioBufferFill - res); - } - - m_audioBufferFill = 0; - } - - m_interpolatorDistanceRemain += m_interpolatorDistance; + m_squelchState = m_running.m_audioSampleRate/ 20; } + + qint16 sample; + + if (m_squelchState > 0) + { + m_squelchState--; + Real demod = sqrt(magsq); + + demod = m_lowpass.filter(demod); + + if (demod < -1) + { + demod = -1; + } + else if (demod > 1) + { + demod = 1; + } + + m_volumeAGC.feed(demod); + + demod *= (0.003 / m_volumeAGC.getValue()); + demod *= m_running.m_volume; + sample = demod * 32700 * 16; + + } + else + { + m_volumeAGC.close(); + sample = 0; + } + + m_audioBuffer[m_audioBufferFill].l = sample; + m_audioBuffer[m_audioBufferFill].r = sample; + ++m_audioBufferFill; + + if (m_audioBufferFill >= m_audioBuffer.size()) + { + uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1); + + /* FIXME: Not necessarily bad, There is a race between threads but generally it works i.e. samples are not lost + if (res != m_audioBufferFill) + { + qDebug("AMDemod::feed: %u/%u audio samples lost", m_audioBufferFill - res, m_audioBufferFill); + }*/ + + m_audioBufferFill = 0; + } + + m_interpolatorDistanceRemain += m_interpolatorDistance; } } @@ -142,10 +141,11 @@ void AMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_itera { uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1); + /* SAme remark as above if (res != m_audioBufferFill) { - qDebug("lost %u samples", m_audioBufferFill - res); - } + qDebug("AMDemod::feed: %u samples written vs %u requested", res, m_audioBufferFill); + }*/ m_audioBufferFill = 0; } diff --git a/plugins/channel/nfm/nfmdemod.cpp b/plugins/channel/nfm/nfmdemod.cpp index a68b2d347..484aa0adf 100644 --- a/plugins/channel/nfm/nfmdemod.cpp +++ b/plugins/channel/nfm/nfmdemod.cpp @@ -107,15 +107,19 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter { Complex ci; - if(m_audioFifo->size() <= 0) + if (m_audioFifo->size() == 0) + { return; + } - for(SampleVector::const_iterator it = begin; it != end; ++it) { + for (SampleVector::const_iterator it = begin; it != end; ++it) + { Complex c(it->real() / 32768.0, it->imag() / 32768.0); c *= m_nco.nextIQ(); { - if(m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) { + if (m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) + { m_sampleBuffer.push_back(Sample(ci.real() * 32767.0, ci.imag() * 32767.0)); qint16 sample; @@ -160,7 +164,8 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter // AF processing - if(m_afSquelch.analyze(&demod)) { + if(m_afSquelch.analyze(&demod)) + { m_squelchOpen = m_afSquelch.open(); } @@ -176,14 +181,16 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter if (m_ctcssDetector.getDetectedTone(maxToneIndex)) { - if (maxToneIndex+1 != m_ctcssIndex) { + if (maxToneIndex+1 != m_ctcssIndex) + { m_nfmDemodGUI->setCtcssFreq(m_ctcssDetector.getToneSet()[maxToneIndex]); m_ctcssIndex = maxToneIndex+1; } } else { - if (m_ctcssIndex != 0) { + if (m_ctcssIndex != 0) + { m_nfmDemodGUI->setCtcssFreq(0); m_ctcssIndex = 0; } @@ -204,7 +211,8 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter } else { - if (m_ctcssIndex != 0) { + if (m_ctcssIndex != 0) + { m_nfmDemodGUI->setCtcssFreq(0); m_ctcssIndex = 0; } @@ -216,10 +224,17 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter m_audioBuffer[m_audioBufferFill].l = sample; m_audioBuffer[m_audioBufferFill].r = sample; ++m_audioBufferFill; - if(m_audioBufferFill >= m_audioBuffer.size()) { + + if (m_audioBufferFill >= m_audioBuffer.size()) + { uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1); - if(res != m_audioBufferFill) + + /* FIXME: Not necessarily bad, There is a race between threads but generally it works i.e. samples are not lost + if (res != m_audioBufferFill) + { qDebug("lost %u audio samples", m_audioBufferFill - res); + }*/ + m_audioBufferFill = 0; } @@ -227,10 +242,17 @@ void NFMDemod::feed(SampleVector::const_iterator begin, SampleVector::const_iter } } } - if(m_audioBufferFill > 0) { + + if (m_audioBufferFill > 0) + { uint res = m_audioFifo->write((const quint8*)&m_audioBuffer[0], m_audioBufferFill, 1); - if(res != m_audioBufferFill) + + /* Same remark as above + if (res != m_audioBufferFill) + { qDebug("lost %u samples", m_audioBufferFill - res); + }*/ + m_audioBufferFill = 0; } diff --git a/sdrbase/audio/audiofifo.cpp b/sdrbase/audio/audiofifo.cpp index dd475caa6..97f65de46 100644 --- a/sdrbase/audio/audiofifo.cpp +++ b/sdrbase/audio/audiofifo.cpp @@ -22,16 +22,17 @@ #define MIN(x, y) ((x) < (y) ? (x) : (y)) AudioFifo::AudioFifo() : - m_fifo(NULL) + m_fifo(0) { m_size = 0; m_fill = 0; m_head = 0; m_tail = 0; + m_sampleSize = 0; } AudioFifo::AudioFifo(uint sampleSize, uint numSamples) : - m_fifo(NULL) + m_fifo(0) { QMutexLocker mutexLocker(&m_mutex); @@ -42,9 +43,10 @@ AudioFifo::~AudioFifo() { QMutexLocker mutexLocker(&m_mutex); - if(m_fifo != NULL) { + if (m_fifo != 0) + { delete[] m_fifo; - m_fifo = NULL; + m_fifo = 0; } m_writeWaitCondition.wakeOne(); @@ -67,39 +69,63 @@ uint AudioFifo::write(const quint8* data, uint numSamples, int timeout) uint remaining; uint copyLen; - if(m_fifo == NULL) + if(m_fifo == 0) + { return 0; + } time.start(); m_mutex.lock(); if(timeout == 0) + { total = MIN(numSamples, m_size - m_fill); - else total = numSamples; + } + else + { + total = numSamples; + } remaining = total; - while(remaining > 0) { - if(isFull()) { - if(time.elapsed() < timeout) { + + while (remaining > 0) + { + if (isFull()) + { + if (time.elapsed() < timeout) + { m_writeWaitLock.lock(); m_mutex.unlock(); int ms = timeout - time.elapsed(); + if(ms < 1) + { ms = 1; + } + bool ok = m_writeWaitCondition.wait(&m_writeWaitLock, ms); m_writeWaitLock.unlock(); + if(!ok) + { return total - remaining; + } + m_mutex.lock(); - if(m_fifo == NULL) { + + if(m_fifo == 0) + { m_mutex.unlock(); return 0; } - } else { + } + else + { m_mutex.unlock(); return total - remaining; } } + copyLen = MIN(remaining, m_size - m_fill); copyLen = MIN(copyLen, m_size - m_tail); memcpy(m_fifo + (m_tail * m_sampleSize), data, copyLen * m_sampleSize); @@ -122,35 +148,58 @@ uint AudioFifo::read(quint8* data, uint numSamples, int timeout) uint remaining; uint copyLen; - if(m_fifo == NULL) + if(m_fifo == 0) + { return 0; + } time.start(); m_mutex.lock(); if(timeout == 0) + { total = MIN(numSamples, m_fill); - else total = numSamples; + } + else + { + total = numSamples; + } remaining = total; - while(remaining > 0) { - if(isEmpty()) { - if(time.elapsed() < timeout) { + + while(remaining > 0) + { + if(isEmpty()) + { + if(time.elapsed() < timeout) + { m_readWaitLock.lock(); m_mutex.unlock(); int ms = timeout - time.elapsed(); + if(ms < 1) + { ms = 1; + } + bool ok = m_readWaitCondition.wait(&m_readWaitLock, ms); m_readWaitLock.unlock(); + if(!ok) + { return total - remaining; + } + m_mutex.lock(); - if(m_fifo == NULL) { + + if(m_fifo == 0) + { m_mutex.unlock(); return 0; } - } else { + } + else + { m_mutex.unlock(); return total - remaining; } @@ -176,7 +225,10 @@ uint AudioFifo::drain(uint numSamples) QMutexLocker mutexLocker(&m_mutex); if(numSamples > m_fill) + { numSamples = m_fill; + } + m_head = (m_head + numSamples) % m_size; m_fill -= numSamples; @@ -197,7 +249,8 @@ void AudioFifo::clear() bool AudioFifo::create(uint sampleSize, uint numSamples) { - if(m_fifo != NULL) { + if(m_fifo != 0) + { delete[] m_fifo; m_fifo = NULL; } @@ -208,7 +261,8 @@ bool AudioFifo::create(uint sampleSize, uint numSamples) m_head = 0; m_tail = 0; - if((m_fifo = new qint8[numSamples * m_sampleSize]) == NULL) { + if((m_fifo = new qint8[numSamples * m_sampleSize]) == 0) + { qDebug("out of memory"); return false; } diff --git a/sdrbase/audio/audiooutput.cpp b/sdrbase/audio/audiooutput.cpp index 2b1d6a2ba..a74faf039 100644 --- a/sdrbase/audio/audiooutput.cpp +++ b/sdrbase/audio/audiooutput.cpp @@ -34,8 +34,12 @@ AudioOutput::~AudioOutput() stop(); QMutexLocker mutexLocker(&m_mutex); - for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) + + for (AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) + { delete *it; + } + m_audioFifos.clear(); } @@ -43,30 +47,55 @@ bool AudioOutput::start(int device, int rate) { QMutexLocker mutexLocker(&m_mutex); - Q_UNUSED(device); - Q_UNUSED(rate); + //Q_UNUSED(device); + //Q_UNUSED(rate); QAudioFormat format; - QAudioDeviceInfo devInfo(QAudioDeviceInfo::defaultOutputDevice()); + QAudioDeviceInfo devInfo; - format.setSampleRate(48000); + if (device < 0) + { + devInfo = QAudioDeviceInfo::defaultOutputDevice(); + qWarning("AudioOutput::start: using default device %s", qPrintable(devInfo.defaultOutputDevice().deviceName())); + } + else + { + QList devicesInfo = QAudioDeviceInfo::availableDevices(QAudio::AudioOutput); + + if (device < devicesInfo.size()) + { + devInfo = devicesInfo[device]; + qWarning("AudioOutput::start: using audio device #%d: %s", device, qPrintable(devInfo.defaultOutputDevice().deviceName())); + } + else + { + devInfo = QAudioDeviceInfo::defaultOutputDevice(); + qWarning("AudioOutput::start: audio device #%d does not exist. Using default device %s", device, qPrintable(devInfo.defaultOutputDevice().deviceName())); + } + } + + //QAudioDeviceInfo devInfo(QAudioDeviceInfo::defaultOutputDevice()); + + format.setSampleRate(rate); format.setChannelCount(2); format.setSampleSize(16); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::SignedInt); - if(!devInfo.isFormatSupported(format)) { - qWarning("48kHz S16_LE audio format not supported"); + if (!devInfo.isFormatSupported(format)) + { + qWarning("AudioOutput::start: %d Hz S16_LE audio format not supported", rate); format = devInfo.nearestFormat(format); } - if(format.sampleSize() != 16) { - qWarning("Audio device ( %s ) failed", qPrintable(devInfo.defaultOutputDevice().deviceName())); + if (format.sampleSize() != 16) + { + qWarning("AudioOutput::start: Audio device ( %s ) failed", qPrintable(devInfo.defaultOutputDevice().deviceName())); return false; } - m_audioOutput = new QAudioOutput(format); + m_audioOutput = new QAudioOutput(devInfo, format); QIODevice::open(QIODevice::ReadOnly); @@ -80,11 +109,13 @@ void AudioOutput::stop() { QMutexLocker mutexLocker(&m_mutex); - if(m_audioOutput != NULL) { + if (m_audioOutput != 0) + { m_audioOutput->stop(); delete m_audioOutput; - m_audioOutput = NULL; + m_audioOutput = 0; } + QIODevice::close(); } @@ -102,34 +133,53 @@ void AudioOutput::removeFifo(AudioFifo* audioFifo) m_audioFifos.remove(audioFifo); } +/* bool AudioOutput::open(OpenMode mode) { Q_UNUSED(mode); return false; -} +}*/ qint64 AudioOutput::readData(char* data, qint64 maxLen) { + //qDebug("AudioOutput::readData: %lld", maxLen); QMutexLocker mutexLocker(&m_mutex); unsigned int framesPerBuffer = maxLen / 4; - if(framesPerBuffer == 0) - return 0; - if(m_mixBuffer.size() < framesPerBuffer * 2) { - m_mixBuffer.resize(framesPerBuffer * 2); // allocate 2 qint32 per frame (stereo) - if(m_mixBuffer.size() != framesPerBuffer * 2) - return 0; + if (framesPerBuffer == 0) + { + return 0; } + + if (m_mixBuffer.size() < framesPerBuffer * 2) + { + m_mixBuffer.resize(framesPerBuffer * 2); // allocate 2 qint32 per frame (stereo) + + if (m_mixBuffer.size() != framesPerBuffer * 2) + { + return 0; + } + } + memset(&m_mixBuffer[0], 0x00, 2 * framesPerBuffer * sizeof(m_mixBuffer[0])); // start with silence // sum up a block from all fifos - for(AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) { + + for (AudioFifos::iterator it = m_audioFifos.begin(); it != m_audioFifos.end(); ++it) + { // use outputBuffer as temp - yes, one memcpy could be saved - uint samples = (*it)->read((quint8*)data, framesPerBuffer, 1); - const qint16* src = (const qint16*)data; + uint samples = (*it)->read((quint8*) data, framesPerBuffer, 1); + const qint16* src = (const qint16*) data; std::vector::iterator dst = m_mixBuffer.begin(); - for(uint i = 0; i < samples; i++) { + + if (samples != framesPerBuffer) + { + qDebug("AudioOutput::readData: read %d samples vs %d requested", samples, framesPerBuffer); + } + + for (uint i = 0; i < samples; i++) + { *dst += *src; ++src; ++dst; @@ -140,22 +190,40 @@ qint64 AudioOutput::readData(char* data, qint64 maxLen) } // convert to int16 + std::vector::const_iterator src = m_mixBuffer.begin(); - qint16* dst = (qint16*)data; - for(uint i = 0; i < framesPerBuffer; i++) { + qint16* dst = (qint16*) data; + + for (uint i = 0; i < framesPerBuffer; i++) + { // left channel + qint32 s = *src++; + if(s < -32768) + { s = -32768; - else if(s > 32767) + } + else if (s > 32767) + { s = 32767; + } + *dst++ = s; + // right channel + s = *src++; + if(s < -32768) + { s = -32768; - else if(s > 32767) + } + else if (s > 32767) + { s = 32767; + } + *dst++ = s; } diff --git a/sdrbase/dsp/dspengine.cpp b/sdrbase/dsp/dspengine.cpp index bf279df5d..442e5a8e1 100644 --- a/sdrbase/dsp/dspengine.cpp +++ b/sdrbase/dsp/dspengine.cpp @@ -271,16 +271,25 @@ void DSPEngine::work() if (part1begin != part1end) { // correct stuff - if (m_dcOffsetCorrection) { + if (m_dcOffsetCorrection) + { dcOffset(part1begin, part1end); } - if (m_iqImbalanceCorrection) { + if (m_iqImbalanceCorrection) + { imbalance(part1begin, part1end); } - // feed data to handlers - for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) { + // feed data to direct sinks + for (SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); ++it) + { + (*it)->feed(part1begin, part1end, positiveOnly); + } + + // feed data to threaded sinks + for (ThreadedSampleSinks::const_iterator it = m_threadedSampleSinks.begin(); it != m_threadedSampleSinks.end(); ++it) + { (*it)->feed(part1begin, part1end, positiveOnly); } } @@ -289,16 +298,25 @@ void DSPEngine::work() if(part2begin != part2end) { // correct stuff - if(m_dcOffsetCorrection) { + if (m_dcOffsetCorrection) + { dcOffset(part2begin, part2end); } - if(m_iqImbalanceCorrection) { + if (m_iqImbalanceCorrection) + { imbalance(part2begin, part2end); } - // feed data to handlers - for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) { + // feed data to direct sinks + for (SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) + { + (*it)->feed(part2begin, part2end, positiveOnly); + } + + // feed data to threaded sinks + for (ThreadedSampleSinks::const_iterator it = m_threadedSampleSinks.begin(); it != m_threadedSampleSinks.end(); ++it) + { (*it)->feed(part2begin, part2end, positiveOnly); } } @@ -344,7 +362,7 @@ DSPEngine::State DSPEngine::gotoIdle() m_sampleSource->stop(); m_deviceDescription.clear(); - m_audioSink.stop(); + m_audioOutput.stop(); m_sampleRate = 0; return StIdle; @@ -444,7 +462,7 @@ DSPEngine::State DSPEngine::gotoRunning() return gotoError("Could not start sample source"); } - m_audioSink.start(0, 48000); + m_audioOutput.start(-1, 48000); // Use default output device at 48 kHz for(SampleSinks::const_iterator it = m_sampleSinks.begin(); it != m_sampleSinks.end(); it++) { @@ -591,11 +609,11 @@ void DSPEngine::handleSynchronousMessages() } else if (DSPAddAudioSink::match(*message)) { - m_audioSink.addFifo(((DSPAddAudioSink*) message)->getAudioFifo()); + m_audioOutput.addFifo(((DSPAddAudioSink*) message)->getAudioFifo()); } else if (DSPRemoveAudioSink::match(*message)) { - m_audioSink.removeFifo(((DSPRemoveAudioSink*) message)->getAudioFifo()); + m_audioOutput.removeFifo(((DSPRemoveAudioSink*) message)->getAudioFifo()); } m_syncMessenger.done(m_state); diff --git a/sdrbase/dsp/threadedsamplesink.cpp b/sdrbase/dsp/threadedsamplesink.cpp index d539de251..95ec7a872 100644 --- a/sdrbase/dsp/threadedsamplesink.cpp +++ b/sdrbase/dsp/threadedsamplesink.cpp @@ -38,7 +38,7 @@ void ThreadedSampleSink::run() exec(); } -void ThreadedSampleSink::feed(SampleVector::const_iterator& begin, SampleVector::const_iterator& end, bool positiveOnly) +void ThreadedSampleSink::feed(SampleVector::const_iterator begin, SampleVector::const_iterator end, bool positiveOnly) { m_sampleSink->feed(begin, end, positiveOnly); }