diff --git a/Source/Processors/AudioMonitor/AudioMonitor.cpp b/Source/Processors/AudioMonitor/AudioMonitor.cpp index 5ebecbd10..af2dacbbe 100644 --- a/Source/Processors/AudioMonitor/AudioMonitor.cpp +++ b/Source/Processors/AudioMonitor/AudioMonitor.cpp @@ -25,22 +25,88 @@ #include "AudioMonitorEditor.h" #include -AudioMonitor::AudioMonitor() - : GenericProcessor ("Audio Monitor"), +AudioMonitorSettings::AudioMonitorSettings() + : numChannels (0), + sampleRate (0), destBufferSampleRate (44100.0f), estimatedSamples (1024) { - tempBuffer = std::make_unique(); +} + +void AudioMonitorSettings::createFilters (int nChans, float sampleRate_) +{ + numChannels = nChans; + sampleRate = sampleRate_; + + bandpassfilters.clear(); + antialiasingfilters.clear(); - for (int i = 0; i < MAX_CHANNELS; i++) + Dsp::Params bandpassParams; + bandpassParams[0] = sampleRate; // sample rate + bandpassParams[1] = 2; // order + bandpassParams[2] = (7000 + 100) / 2; // center frequency + bandpassParams[3] = 7000 - 100; // bandwidth + + for (int n = 0; n < numChannels; ++n) { bandpassfilters.add (new Dsp::SmoothedFilterDesign, // order 1, // number of channels (must be const) Dsp::DirectFormII> (1)); // realization + bandpassfilters.getLast()->setParams (bandpassParams); + antialiasingfilters.add (new Dsp::SmoothedFilterDesign (1024)); } + + updateAntiAliasingFilterParameters(); +} + +void AudioMonitorSettings::updateAntiAliasingFilterParameters() +{ + Dsp::Params antialiasingParams; + antialiasingParams[0] = destBufferSampleRate; // sample rate + antialiasingParams[1] = destBufferSampleRate / 2; // cutoff frequency + antialiasingParams[2] = 1.25; //Q // + + for (int i = 0; i < numChannels; ++i) + { + antialiasingfilters[i]->setParams (antialiasingParams); + } +} + +void AudioMonitorSettings::setOutputSampleRate (double outputSampleRate, double estimatedSamplesPerBlock) +{ + destBufferSampleRate = outputSampleRate; + estimatedSamples = estimatedSamplesPerBlock; + + numSamplesExpected = (sampleRate / destBufferSampleRate) * estimatedSamples; + ratio = sampleRate / destBufferSampleRate; + + samplesInBackupBuffer.clear(); + samplesInOverflowBuffer.clear(); + + bufferA.clear(); + bufferB.clear(); + bufferSwap.clear(); + + for (int i = 0; i < numChannels; i++) + { + samplesInBackupBuffer.emplace (i, 0.0f); + samplesInOverflowBuffer.emplace (i, 0.0f); + + bufferA.emplace (i, std::make_unique> (1, (int) destBufferSampleRate)); + bufferB.emplace (i, std::make_unique> (1, (int) destBufferSampleRate)); + bufferSwap.emplace (i, false); + } + + updateAntiAliasingFilterParameters(); +} + +AudioMonitor::AudioMonitor() + : GenericProcessor ("Audio Monitor") +{ + tempBuffer = std::make_unique (1, 4096); } void AudioMonitor::registerParameters() @@ -64,8 +130,7 @@ void AudioMonitor::registerParameters() Parameter::STREAM_SCOPE, "channels", "Channels", - "Channels to monitor", - 4); + "Channels to monitor"); addCategoricalParameter ( Parameter::STREAM_SCOPE, @@ -87,8 +152,15 @@ void AudioMonitor::updateSettings() { updatePlaybackBuffer(); + settings.update (getDataStreams()); + for (auto stream : dataStreams) { + // create filters for each channel + settings[stream->getStreamId()]->createFilters (stream->getChannelCount(), + stream->getSampleRate()); + + // update the spike channel parameter with the available spike channels Array spikeChannelNames; spikeChannelNames.add ("No spike channel"); for (auto spikeChan : stream->getSpikeChannels()) @@ -115,48 +187,10 @@ void AudioMonitor::updatePlaybackBuffer() void AudioMonitor::prepareToPlay (double sampleRate_, int estimatedSamplesPerBlock) { - destBufferSampleRate = sampleRate_; - estimatedSamples = estimatedSamplesPerBlock; - recreateBuffers(); -} - -void AudioMonitor::recreateBuffers() -{ - numSamplesExpected.clear(); - sourceBufferSampleRate.clear(); - - ratio.clear(); - - for (int i = 0; i < getNumInputs(); i++) - { - numSamplesExpected.emplace (i, - continuousChannels[i]->getSampleRate() - / destBufferSampleRate - * estimatedSamples); - - sourceBufferSampleRate.emplace (i, continuousChannels[i]->getSampleRate()); - - ratio.emplace (i, sourceBufferSampleRate[i] / destBufferSampleRate); - } - - samplesInBackupBuffer.clear(); - samplesInOverflowBuffer.clear(); - - bufferA.clear(); - bufferB.clear(); - bufferSwap.clear(); - - for (int i = 0; i < MAX_CHANNELS; i++) + for (auto stream : dataStreams) { - samplesInBackupBuffer.emplace (i, 0.0f); - samplesInOverflowBuffer.emplace (i, 0.0f); - - bufferA.emplace (i, std::make_unique> (1, 44100)); - bufferB.emplace (i, std::make_unique> (1, 44100)); - bufferSwap.emplace (i, false); + settings[stream->getStreamId()]->setOutputSampleRate (sampleRate_, estimatedSamplesPerBlock); } - - tempBuffer->setSize (1, 4096); } void AudioMonitor::parameterValueChanged (Parameter* param) @@ -191,28 +225,6 @@ void AudioMonitor::parameterValueChanged (Parameter* param) } } -void AudioMonitor::updateFilter (int i, uint16 streamId) -{ - Dsp::Params params1; - params1[0] = getDataStream (streamId)->getSampleRate(); // sample rate - params1[1] = 2; // order - params1[2] = (7000 + 100) / 2; // center frequency - params1[3] = 7000 - 100; // bandwidth - - bandpassfilters[i]->setParams (params1); - - double cutoffFreq = destBufferSampleRate / 2; // upsample - - double sampleFreq = destBufferSampleRate; // upsample - - Dsp::Params params2; - params2[0] = sampleFreq; // sample rate - params2[1] = cutoffFreq; // cutoff frequency - params2[2] = 1.25; //Q // - - antialiasingfilters[i]->setParams (params2); -} - void AudioMonitor::handleBroadcastMessage (const String& msg, const int64 messageTimeMilliseconds) { LOGC ("Audio Monitor received message: ", msg); @@ -227,7 +239,7 @@ void AudioMonitor::handleBroadcastMessage (const String& msg, const int64 messag if (command.equalsIgnoreCase ("SELECT")) { - if(parts.size() == 3) + if (parts.size() == 3) { String command = parts[2]; @@ -237,7 +249,6 @@ void AudioMonitor::handleBroadcastMessage (const String& msg, const int64 messag for (auto stream : getDataStreams()) stream->getParameter ("channels")->setNextValue (ch); - } } else if (parts.size() >= 4) @@ -277,11 +288,6 @@ void AudioMonitor::handleBroadcastMessage (const String& msg, const int64 messag void AudioMonitor::setSelectedStream (uint16 streamId) { selectedStream = streamId; - - for (int i = 0; i < MAX_CHANNELS; i++) - { - updateFilter (i, selectedStream); - } } void AudioMonitor::process (AudioBuffer& buffer) @@ -301,6 +307,7 @@ void AudioMonitor::process (AudioBuffer& buffer) if (stream->getStreamId() == selectedStream && (*stream)["enable_stream"]) { + auto streamSettings = settings[selectedStream]; AudioSampleBuffer* overflowBuffer; AudioSampleBuffer* backupBuffer; @@ -314,32 +321,34 @@ void AudioMonitor::process (AudioBuffer& buffer) tempBuffer->clear(); - if (! bufferSwap[i]) + if (! streamSettings->bufferSwap[localIndex]) { - overflowBuffer = bufferA[i].get(); - backupBuffer = bufferB[i].get(); + overflowBuffer = streamSettings->bufferA[localIndex].get(); + backupBuffer = streamSettings->bufferB[localIndex].get(); - bufferSwap[i] = true; + streamSettings->bufferSwap[localIndex] = true; } else { - overflowBuffer = bufferB[i].get(); - backupBuffer = bufferA[i].get(); + overflowBuffer = streamSettings->bufferB[localIndex].get(); + backupBuffer = streamSettings->bufferA[localIndex].get(); - bufferSwap[i] = false; + streamSettings->bufferSwap[localIndex] = false; } backupBuffer->clear(); - samplesInOverflowBuffer[i] = samplesInBackupBuffer[i]; // size of buffer after last round - samplesInBackupBuffer[i] = 0; + streamSettings->samplesInOverflowBuffer[localIndex] = streamSettings->samplesInBackupBuffer[localIndex]; // size of buffer after last round + streamSettings->samplesInBackupBuffer[localIndex] = 0; double orphanedSamples = 0; // 1. copy overflow buffer double samplesToCopyFromOverflowBuffer = - ((samplesInOverflowBuffer[i] <= numSamplesExpected[globalIndex]) ? samplesInOverflowBuffer[i] : numSamplesExpected[globalIndex]); + ((streamSettings->samplesInOverflowBuffer[localIndex] <= streamSettings->numSamplesExpected) + ? streamSettings->samplesInOverflowBuffer[localIndex] + : streamSettings->numSamplesExpected); // LOGD("Number of samples to copy: ", samplesToCopyFromOverflowBuffer); @@ -356,7 +365,7 @@ void AudioMonitor::process (AudioBuffer& buffer) 1.0f // gain to apply ); - double leftoverSamples = samplesInOverflowBuffer[i] - samplesToCopyFromOverflowBuffer; + double leftoverSamples = streamSettings->samplesInOverflowBuffer[localIndex] - samplesToCopyFromOverflowBuffer; //std::cout << "Copying to backup buffer: " << leftoverSamples << std::endl; @@ -372,17 +381,18 @@ void AudioMonitor::process (AudioBuffer& buffer) ); } - samplesInBackupBuffer[i] = leftoverSamples; + streamSettings->samplesInBackupBuffer[localIndex] = leftoverSamples; } - double remainingSamples = double (numSamplesExpected[globalIndex]) - samplesToCopyFromOverflowBuffer; + double remainingSamples = double (streamSettings->numSamplesExpected) - samplesToCopyFromOverflowBuffer; double samplesAvailable = double (getNumSamplesInBlock (selectedStream)); //std::cout << "Remaining samples: " << remainingSamples << std::endl; //std::cout << "Samples available: " << samplesAvailable << std::endl; - double samplesToCopyFromIncomingBuffer = ((remainingSamples <= samplesAvailable) ? remainingSamples : samplesAvailable); + double samplesToCopyFromIncomingBuffer = + ((remainingSamples <= samplesAvailable) ? remainingSamples : samplesAvailable); //std::cout << "Copying from incoming buffer: " << samplesToCopyFromIncomingBuffer << std::endl; @@ -402,10 +412,11 @@ void AudioMonitor::process (AudioBuffer& buffer) //std::cout << "Orphaned samples: " << orphanedSamples << std::endl; - if (orphanedSamples > 0 && (samplesInBackupBuffer[i] + orphanedSamples < backupBuffer->getNumSamples())) + if (orphanedSamples > 0 + && (streamSettings->samplesInBackupBuffer[localIndex] + orphanedSamples < backupBuffer->getNumSamples())) { backupBuffer->addFrom (0, // destination channel - samplesInBackupBuffer[i], // destination start sample + streamSettings->samplesInBackupBuffer[localIndex], // destination start sample buffer, // source globalIndex, // source channel (int) remainingSamples, // source start sample @@ -413,7 +424,7 @@ void AudioMonitor::process (AudioBuffer& buffer) 1.0f // gain to apply ); - samplesInBackupBuffer[i] = samplesInBackupBuffer[i] + orphanedSamples; + streamSettings->samplesInBackupBuffer[localIndex] += orphanedSamples; } //std::cout << "Total copied: " << samplesToCopyFromOverflowBuffer + samplesToCopyFromIncomingBuffer << std::endl; @@ -427,7 +438,7 @@ void AudioMonitor::process (AudioBuffer& buffer) if (totalCopied == 0) continue; - bandpassfilters[i]->process (totalCopied, &ptr); + streamSettings->bandpassfilters[localIndex]->process (totalCopied, &ptr); /*if (i == 0) { @@ -480,7 +491,7 @@ void AudioMonitor::process (AudioBuffer& buffer) 1, // number of samples alpha); // gain to apply to source - subSampleOffset += ratio[globalIndex]; + subSampleOffset += streamSettings->ratio; while (subSampleOffset >= 1.0) { diff --git a/Source/Processors/AudioMonitor/AudioMonitor.h b/Source/Processors/AudioMonitor/AudioMonitor.h index c75f4b0ab..8432bd30e 100644 --- a/Source/Processors/AudioMonitor/AudioMonitor.h +++ b/Source/Processors/AudioMonitor/AudioMonitor.h @@ -29,10 +29,46 @@ #include "../Dsp/Dsp.h" #include "../GenericProcessor/GenericProcessor.h" -#define MAX_CHANNELS 4 +/** + Creates and holds the filter settings for one input stream in the AudioMonitor. +*/ +class AudioMonitorSettings +{ +public: + AudioMonitorSettings(); + + int numChannels; + float sampleRate; + double destBufferSampleRate; + double estimatedSamples; + + /** Bandpass filters (1 per channel)*/ + OwnedArray bandpassfilters; + + /** Antialiasing filters (1 per channel)*/ + OwnedArray antialiasingfilters; + std::map>> bufferA; + std::map>> bufferB; + + /** Per-channel buffer state information*/ + std::map samplesInBackupBuffer; + std::map samplesInOverflowBuffer; + std::map bufferSwap; + double numSamplesExpected; + double ratio; + + /** Creates new filters when input settings change*/ + void createFilters (int numChannels, float sampleRate); + + /** Updates filters when parameters change*/ + void updateAntiAliasingFilterParameters(); + + /** Re-sets the copy buffers prior to starting acquisition*/ + void setOutputSampleRate (double outputSampleRate, double estimatedSamplesPerBlock); +}; /** - Reads data from a file. + Streams data from incoming continuous channels to the computer’s audio output. @see GenericProcessor */ @@ -69,9 +105,6 @@ class AudioMonitor : public GenericProcessor /** Resets the connections prior to a new round of data acquisition. */ void resetConnections() override; - /** Updates the bandpass filter parameters, given the currently monitored stream*/ - void updateFilter (int i, uint16 streamId); - /** Allows other processors to configure the Audio Monitor during acquisition*/ void handleBroadcastMessage (const String& message, const int64 messageTimeMillis) override; @@ -79,29 +112,8 @@ class AudioMonitor : public GenericProcessor void setSelectedStream (uint16 streamId); private: - /** Re-sets the copy buffers prior to acquisition*/ - void recreateBuffers(); - - std::map>> bufferA; - std::map>> bufferB; - - /** Per-channel buffer state information*/ - std::map samplesInBackupBuffer; - std::map samplesInOverflowBuffer; - std::map sourceBufferSampleRate; - std::map bufferSwap; - - std::map numSamplesExpected; - std::map ratio; - - double destBufferSampleRate; - double estimatedSamples; - - /** 4 bandpass filters (1 per selected channel)*/ - OwnedArray bandpassfilters; - - /** 4 antialiasing filters (1 per selected channel)*/ - OwnedArray antialiasingfilters; + /** AudioMonitor settings for each input stream*/ + StreamSettings settings; /** Holds the data for one channel, before it's copied to the output*/ std::unique_ptr> tempBuffer; diff --git a/Source/Processors/Parameter/Parameter.cpp b/Source/Processors/Parameter/Parameter.cpp index b8bd59964..747febf48 100755 --- a/Source/Processors/Parameter/Parameter.cpp +++ b/Source/Processors/Parameter/Parameter.cpp @@ -648,12 +648,13 @@ SelectedChannelsParameter::SelectedChannelsParameter (ParameterOwner* owner, maxSelectableChannels (maxSelectableChannels_), channelCount (0) { - if (defaultValue_.size() == 0) + if (defaultValue_.size() == 0 && maxSelectableChannels != std::numeric_limits::max()) { //Set default selected to the first maxSelectableChannels channels Array values; for (int i = 0; i < maxSelectableChannels; i++) values.add (i); + currentValue = values; } else