diff --git a/AudioDevice.hpp b/AudioDevice.hpp index 2f4cfd313..5139c35e4 100644 --- a/AudioDevice.hpp +++ b/AudioDevice.hpp @@ -37,7 +37,6 @@ public: size_t bytesPerFrame () const {return sizeof (qint16) * (Mono == m_channel ? 1 : 2);} Channel channel () const {return m_channel;} - void channel (Channel newChannel) {m_channel = newChannel;} protected: AudioDevice (QObject * parent = 0) @@ -45,7 +44,7 @@ protected: { } - void store (char const * source, qint64 numFrames, qint16 * dest) + void store (char const * source, size_t numFrames, qint16 * dest) { qint16 const * begin (reinterpret_cast (source)); for ( qint16 const * i = begin; i != begin + numFrames * (bytesPerFrame () / sizeof (qint16)); i += bytesPerFrame () / sizeof (qint16)) diff --git a/Detector.cpp b/Detector.cpp index 20d4e6dfa..240d17571 100644 --- a/Detector.cpp +++ b/Detector.cpp @@ -10,6 +10,11 @@ extern "C" { void fil4_(qint16*, qint32*, qint16*, qint32*); } +namespace +{ + unsigned const downsampleFactor = 4; +} + Detector::Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned framesPerSignal, QObject * parent) : AudioDevice (parent) , m_frameRate (frameRate) @@ -17,6 +22,8 @@ Detector::Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned , m_framesPerSignal (framesPerSignal) , m_monitoring (false) , m_starting (false) + , m_buffer (new short [framesPerSignal * downsampleFactor]) + , m_bufferPos (0) { clear (); } @@ -41,63 +48,64 @@ void Detector::clear () qint64 Detector::writeData (char const * data, qint64 maxSize) { - bool overrun (false); - int excess (0); if (m_monitoring) { Q_ASSERT (!(maxSize % static_cast (bytesPerFrame ()))); // no torn frames - qint64 framesAcceptable (sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]) - jt9com_.kin); - qint64 framesAccepted (qMin (static_cast (maxSize / bytesPerFrame ()), framesAcceptable)); + // these are in terms of input frames (not down sampled) + size_t framesAcceptable ((sizeof (jt9com_.d2) / sizeof (jt9com_.d2[0]) - jt9com_.kin) * downsampleFactor); + size_t framesAccepted (qMin (static_cast (maxSize / bytesPerFrame ()), framesAcceptable)); - overrun = framesAccepted < static_cast (maxSize / bytesPerFrame ()); - if (overrun) + if (framesAccepted < static_cast (maxSize / bytesPerFrame ())) { - qDebug () << "dropped " << maxSize / sizeof (jt9com_.d2[0]) - framesAccepted << " frames of data on the floor!"; + qDebug () << "dropped " << maxSize / bytesPerFrame () - framesAccepted << " frames of data on the floor!"; } - Q_ASSERT (2 == bytesPerFrame ()); // only mono until fil4 can do stereo - excess = framesAccepted % 4; - qint32 m_n1,m_n2; - m_n1 = framesAccepted - excess; - fil4_((qint16 *)data, &m_n1, m_translate, &m_n2); - store ((char const *) m_translate, m_n2, &jt9com_.d2[jt9com_.kin]); - - unsigned lastSignalIndex (jt9com_.kin / m_framesPerSignal); - jt9com_.kin += m_n2; - unsigned currentSignalIndex (jt9com_.kin / m_framesPerSignal); - - if (currentSignalIndex != lastSignalIndex && m_monitoring) + for (unsigned remaining = framesAccepted; remaining; ) { - Q_EMIT framesWritten (currentSignalIndex * m_framesPerSignal); - } - - if (!secondInPeriod ()) - { - if (!m_starting) + size_t numFramesProcessed (qMin (m_framesPerSignal * downsampleFactor - m_bufferPos, remaining)); + store (&data[(framesAccepted - remaining) * bytesPerFrame ()], numFramesProcessed, &m_buffer[m_bufferPos]); + m_bufferPos += numFramesProcessed; + if (m_bufferPos == m_framesPerSignal * downsampleFactor && m_monitoring) { - // next samples will be in new period so wrap around to - // start of buffer - // - // we don't bother calling reset () since we expect to fill - // the whole buffer and don't need to waste cycles zeroing - jt9com_.kin = 0; - m_starting = true; + qint32 framesToProcess (m_framesPerSignal * downsampleFactor); + qint32 framesAfterDownSample; + fil4_(&m_buffer[0], &framesToProcess, &jt9com_.d2[jt9com_.kin], &framesAfterDownSample); + m_bufferPos = 0; + + jt9com_.kin += framesAfterDownSample; + Q_EMIT framesWritten (jt9com_.kin); } - } - else if (m_starting) - { - m_starting = false; + + if (!secondInPeriod ()) + { + if (!m_starting) + { + // next samples will be in new period so wrap around to + // start of buffer + // + // we don't bother calling reset () since we expect to fill + // the whole buffer and don't need to waste cycles zeroing + jt9com_.kin = 0; + m_bufferPos = 0; + m_starting = true; + } + } + else if (m_starting) + { + m_starting = false; + } + remaining -= numFramesProcessed; } } else { jt9com_.kin = 0; + m_bufferPos = 0; } - return maxSize - (overrun ? 0 : excess * bytesPerFrame ()); - // we drop any data past the end of the buffer on - // the floor until the next period starts + return maxSize; // we drop any data past the end of the buffer on + // the floor until the next period starts } unsigned Detector::secondInPeriod () const diff --git a/Detector.hpp b/Detector.hpp index 77375b935..46fb32f27 100644 --- a/Detector.hpp +++ b/Detector.hpp @@ -3,6 +3,8 @@ #include "AudioDevice.hpp" +#include + // // output device that distributes data in predefined chunks via a signal // @@ -20,6 +22,10 @@ public: // if the data buffer were not global storage and fixed size then we // might want maximum size passed as constructor arguments // + // we down sample by a factor of 4 + // + // the framesPerSignal argument is the number after down sampling + // Detector (unsigned frameRate, unsigned periodLengthInSeconds, unsigned framesPerSignal, QObject * parent = 0); bool isMonitoring () const {return m_monitoring;} @@ -35,7 +41,7 @@ protected: private: // these are private because we want thread safety, must be called via Qt queued connections Q_SLOT void open (AudioDevice::Channel channel = Mono) {AudioDevice::open (QIODevice::WriteOnly, channel);} - Q_SLOT void setMonitoring (bool newState) {m_monitoring = newState;} + Q_SLOT void setMonitoring (bool newState) {m_monitoring = newState; m_bufferPos = 0;} Q_SLOT bool reset (); Q_SLOT void close () {AudioDevice::close ();} @@ -46,10 +52,15 @@ private: unsigned m_frameRate; unsigned m_period; - unsigned m_framesPerSignal; + qint32 m_framesPerSignal; // after down sampling bool volatile m_monitoring; bool m_starting; - qint16 m_translate[20000]; + QScopedArrayPointer m_buffer; // de-interleaved sample buffer + // big enough for all the + // samples for one increment of + // data (a signals worth) at + // the input sample rate + unsigned m_bufferPos; }; #endif diff --git a/mainwindow.cpp b/mainwindow.cpp index b0fa2237e..8b34676ca 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -1,4 +1,4 @@ -//-------------------------------------------------------------- MainWindow +//--------------------------------------------------------------- MainWindow #include "mainwindow.h" #include "ui_mainwindow.h"