/* ------------------------------------------------------------------ This file is part of the Open Ephys GUI Copyright (C) 2013 Open Ephys ------------------------------------------------------------------ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "AudioResamplingNode.h" #include <stdio.h> AudioResamplingNode::AudioResamplingNode() : GenericProcessor("Resampling Node"), sourceBufferSampleRate(40000.0), destBufferSampleRate(44100.0), ratio(1.0), lastRatio(1.0), destBuffer(0), tempBuffer(0), destBufferIsTempBuffer(true), isTransmitting(false), destBufferPos(0) { settings.numInputs = 2; settings.numOutputs = 2; setPlayConfigDetails(getNumInputs(), // number of inputs getNumOutputs(), // number of outputs 44100.0, // sampleRate 128); // blockSize filter = new Dsp::SmoothedFilterDesign <Dsp::RBJ::Design::LowPass, 1> (1024); if (destBufferIsTempBuffer) destBufferWidth = 1024; else destBufferWidth = 1000; destBufferTimebaseSecs = 1.0; destBuffer = new AudioSampleBuffer(16, destBufferWidth); tempBuffer = new AudioSampleBuffer(16, destBufferWidth); continuousDataBuffer = new int16[10000]; } AudioResamplingNode::~AudioResamplingNode() { filter = 0; deleteAndZero(destBuffer); deleteAndZero(tempBuffer); } void AudioResamplingNode::setParameter(int parameterIndex, float newValue) { editor->updateParameterButtons(parameterIndex); switch (parameterIndex) { case 0: destBufferTimebaseSecs = newValue; break; case 1: destBufferWidth = roundToInt(newValue); } // reset to zero and clear if timebase or width has changed. destBufferPos = 0; destBuffer->clear(); } void AudioResamplingNode::prepareToPlay(double sampleRate_, int estimatedSamplesPerBlock) { std::cout << "AudioResamplingNode preparing to play." << std::endl; if (destBufferIsTempBuffer) { destBufferSampleRate = sampleRate_; tempBuffer->setSize(getNumInputs(), 4096); } else { destBufferSampleRate = float(destBufferWidth) / destBufferTimebaseSecs; destBuffer->setSize(getNumInputs(), destBufferWidth); } destBuffer->clear(); tempBuffer->clear(); destBufferPos = 0; std::cout << "Temp buffer size: " << tempBuffer->getNumChannels() << " x " << tempBuffer->getNumSamples() << std::endl; updateFilter(); } void AudioResamplingNode::updateFilter() { double cutoffFreq = (ratio > 1.0) ? 2 * destBufferSampleRate // downsample : destBufferSampleRate / 2; // upsample double sampleFreq = (ratio > 1.0) ? sourceBufferSampleRate // downsample : destBufferSampleRate; // upsample Dsp::Params params; params[0] = sampleFreq; // sample rate params[1] = cutoffFreq; // cutoff frequency params[2] = 1.25; //Q // filter->setParams(params); } void AudioResamplingNode::releaseResources() { } void AudioResamplingNode::process(AudioSampleBuffer& buffer, MidiBuffer& midiMessages, int& nSamples) { int nSamps = nSamples; int valuesNeeded; if (destBufferIsTempBuffer) { ratio = float(nSamps) / float(buffer.getNumSamples()); valuesNeeded = buffer.getNumSamples(); } else { ratio = sourceBufferSampleRate / destBufferSampleRate; valuesNeeded = (int) buffer.getNumSamples() / ratio; //std::cout << std::endl; //std::cout << "Ratio: " << ratio << std::endl; //std::cout << "Values needed: " << valuesNeeded << std::endl; } if (lastRatio != ratio) { updateFilter(); lastRatio = ratio; } if (ratio > 1.0001) { // pre-apply filter before downsampling filter->process(nSamps, buffer.getArrayOfChannels()); } // initialize variables tempBuffer->clear(); int sourceBufferPos = 0; int sourceBufferSize = buffer.getNumSamples(); float subSampleOffset = 0.0; int nextPos = (sourceBufferPos + 1) % sourceBufferSize; int tempBufferPos; //int totalSamples = 0; // code modified from "juce_ResamplingAudioSource.cpp": for (tempBufferPos = 0; tempBufferPos < valuesNeeded; tempBufferPos++) { float gain = 1.0; float alpha = (float) subSampleOffset; float invAlpha = 1.0f - alpha; for (int channel = 0; channel < buffer.getNumChannels(); ++channel) { tempBuffer->addFrom(channel, // destChannel tempBufferPos, // destSampleOffset buffer, // source channel, // sourceChannel sourceBufferPos,// sourceSampleOffset 1, // number of samples invAlpha*gain); // gain to apply to source tempBuffer->addFrom(channel, // destChannel tempBufferPos, // destSampleOffset buffer, // source channel, // sourceChannel nextPos, // sourceSampleOffset 1, // number of samples alpha*gain); // gain to apply to source } subSampleOffset += ratio; while (subSampleOffset >= 1.0) { if (++sourceBufferPos >= sourceBufferSize) sourceBufferPos = 0; nextPos = (sourceBufferPos + 1) % sourceBufferSize; subSampleOffset -= 1.0; } } // std::cout << sourceBufferPos << " " << tempBufferPos << std::endl; if (ratio < 0.9999) { filter->process(tempBufferPos, tempBuffer->getArrayOfChannels()); // apply the filter after upsampling ///////filter->process (totalSamples, buffer.getArrayOfChannels()); } else if (ratio <= 1.0001) { // no resampling is being applied, no need to filter, BUT... // keep the filter stoked with samples to avoid discontinuities } if (destBufferIsTempBuffer) { // copy the temp buffer into the original buffer buffer = AudioSampleBuffer(tempBuffer->getArrayOfChannels(), 2, tempBufferPos);//buffer.getNumSamples()); } else { //std::cout << "Copying into dest buffer..." << std::endl; // copy the temp buffer into the destination buffer int pos = 0; while (*tempBuffer->getSampleData(0,pos) != 0) pos++; int spaceAvailable = destBufferWidth - destBufferPos; int blockSize1 = (spaceAvailable > pos) ? pos : spaceAvailable; int blockSize2 = (spaceAvailable > pos) ? 0 : (pos - spaceAvailable); for (int channel = 0; channel < destBuffer->getNumChannels(); channel++) { // copy first block destBuffer->copyFrom(channel, //destChannel destBufferPos, //destStartSample *tempBuffer, //source channel, //sourceChannel 0, //sourceStartSample blockSize1 //numSamples ); // copy second block destBuffer->copyFrom(channel, //destChannel 0, //destStartSample *tempBuffer, //source channel, //sourceChannel blockSize1, //sourceStartSample blockSize2 //numSamples ); } //destBufferPos = (spaceAvailable > tempBufferPos) ? destBufferPos destBufferPos += pos; destBufferPos %= destBufferWidth; //std::cout << "Temp buffer position: " << tempBufferPos << std::endl; //std::cout << "Resampling node value:" << *destBuffer->getSampleData(0,0) << std::endl; } } void AudioResamplingNode::writeContinuousBuffer(float* data, int nSamples, int channel) { // find file and write samples to disk timestamp = timer.getHighResolutionTicks(); AudioDataConverters::convertFloatToInt16BE(data, continuousDataBuffer, nSamples); //int16 samps = nSamples; fwrite(×tamp, // ptr 8, // size of each element 1, // count file); // ptr to FILE object fwrite(&nSamples, // ptr sizeof(nSamples), // size of each element 1, // count file); // ptr to FILE object fwrite(continuousDataBuffer, // ptr 2, // size of each element nSamples, // count file); // ptr to FILE object // FIXME: check that return value of each fwrite() equals "count"; // otherwise, there was an error. }