uint32_t AudioSink::DrainConverter(uint32_t aMaxFrames) { MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); if (!mConverter || !mLastProcessedPacket || !aMaxFrames) { // nothing to drain. return 0; } RefPtr<AudioData> lastPacket = mLastProcessedPacket.ref(); mLastProcessedPacket.reset(); // To drain we simply provide an empty packet to the audio converter. AlignedAudioBuffer convertedData = mConverter->Process(AudioSampleBuffer(AlignedAudioBuffer())).Forget(); uint32_t frames = convertedData.Length() / mOutputChannels; if (!convertedData.SetLength(std::min(frames, aMaxFrames) * mOutputChannels)) { // This can never happen as we were reducing the length of convertData. mErrored = true; return 0; } RefPtr<AudioData> data = CreateAudioFromBuffer(Move(convertedData), lastPacket); if (!data) { return 0; } mProcessedQueue.Push(data); return data->mFrames; }
void AudioSink::NotifyAudioNeeded() { MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn(), "Not called from the owner's thread"); // Always ensure we have two processed frames pending to allow for processing // latency. while (mAudioQueue.GetSize() && (mAudioQueue.IsFinished() || mProcessedQueueLength < LOW_AUDIO_USECS || mProcessedQueue.GetSize() < 2)) { RefPtr<AudioData> data = mAudioQueue.PopFront(); // Ignore the element with 0 frames and try next. if (!data->mFrames) { continue; } if (!mConverter || (data->mRate != mConverter->InputConfig().Rate() || data->mChannels != mConverter->InputConfig().Channels())) { SINK_LOG_V("Audio format changed from %u@%uHz to %u@%uHz", mConverter? mConverter->InputConfig().Channels() : 0, mConverter ? mConverter->InputConfig().Rate() : 0, data->mChannels, data->mRate); DrainConverter(); // mFramesParsed indicates the current playtime in frames at the current // input sampling rate. Recalculate it per the new sampling rate. if (mFramesParsed) { // We minimize overflow. uint32_t oldRate = mConverter->InputConfig().Rate(); uint32_t newRate = data->mRate; CheckedInt64 result = SaferMultDiv(mFramesParsed, newRate, oldRate); if (!result.isValid()) { NS_WARNING("Int overflow in AudioSink"); mErrored = true; return; } mFramesParsed = result.value(); } mConverter = MakeUnique<AudioConverter>( AudioConfig(data->mChannels, data->mRate), AudioConfig(mOutputChannels, mOutputRate)); } // See if there's a gap in the audio. If there is, push silence into the // audio hardware, so we can play across the gap. // Calculate the timestamp of the next chunk of audio in numbers of // samples. CheckedInt64 sampleTime = TimeUnitToFrames(data->mTime - mStartTime, data->mRate); // Calculate the number of frames that have been pushed onto the audio hardware. CheckedInt64 missingFrames = sampleTime - mFramesParsed; if (!missingFrames.isValid()) { NS_WARNING("Int overflow in AudioSink"); mErrored = true; return; } if (missingFrames.value() > AUDIO_FUZZ_FRAMES) { // The next audio packet begins some time after the end of the last packet // we pushed to the audio hardware. We must push silence into the audio // hardware so that the next audio packet begins playback at the correct // time. missingFrames = std::min<int64_t>(INT32_MAX, missingFrames.value()); mFramesParsed += missingFrames.value(); RefPtr<AudioData> silenceData; AlignedAudioBuffer silenceBuffer(missingFrames.value() * data->mChannels); if (!silenceBuffer) { NS_WARNING("OOM in AudioSink"); mErrored = true; return; } if (mConverter->InputConfig() != mConverter->OutputConfig()) { AlignedAudioBuffer convertedData = mConverter->Process(AudioSampleBuffer(Move(silenceBuffer))).Forget(); silenceData = CreateAudioFromBuffer(Move(convertedData), data); } else { silenceData = CreateAudioFromBuffer(Move(silenceBuffer), data); } PushProcessedAudio(silenceData); } mLastEndTime = data->GetEndTime(); mFramesParsed += data->mFrames; if (mConverter->InputConfig() != mConverter->OutputConfig()) { // We must ensure that the size in the buffer contains exactly the number // of frames, in case one of the audio producer over allocated the buffer. AlignedAudioBuffer buffer(Move(data->mAudioData)); buffer.SetLength(size_t(data->mFrames) * data->mChannels); AlignedAudioBuffer convertedData = mConverter->Process(AudioSampleBuffer(Move(buffer))).Forget(); data = CreateAudioFromBuffer(Move(convertedData), data); } if (PushProcessedAudio(data)) { mLastProcessedPacket = Some(data); } } if (mAudioQueue.IsFinished()) { // We have reached the end of the data, drain the resampler. DrainConverter(); mProcessedQueue.Finish(); } }
WavetableOscillator::WavetableOscillator() : baseHz(110.0), wavetableSampleRate(44100), level("level", 0, 1, 1), position("position", 0, 127, 0), phase("phase", 0, 1, 0), wavetable(128, std::vector<float>(401)) { /* for (int i = 0; i < wavetable[0].size(); i++) { wavetable[0][i] = sinf(2 * float_Pi * ((float)i / wavetable[0].size())); }*/ const int WAVEFORM_SAMPLESIZE = 401; //const int MAX_TOKENS_PER_LINE = 20; //const char* const DELIMITER = " "; File fsA(File::getCurrentWorkingDirectory().getFullPathName() + "/A.txt"); File fsB(File::getCurrentWorkingDirectory().getFullPathName() + "/B.txt"); AudioSampleBuffer bufferA, bufferB; bufferA = AudioSampleBuffer(); bufferB = AudioSampleBuffer(); // traverse the entire first file StringArray destLines; fsA.readLines(destLines); int i = 0; for (; i < destLines.strings.size(); i++) { wavetable[0][i] = destLines.strings[i].getFloatValue(); } // make sure that up to 401 is filed, continue where <i> left off from while loop for (; i < WAVEFORM_SAMPLESIZE; i++) { wavetable[0][i] = 0.0; } // for traversing each float of the waveform int j = 0; int wavetable_depth = wavetable.size()-1; // traverse the entire second file StringArray destLines2; fsB.readLines(destLines2); for (; j < destLines2.strings.size(); j++) { wavetable[wavetable_depth][j] = destLines2.strings[j].getFloatValue(); } // make sure that up to 401 is filed, continue where <i> left off from while loop for (; j < WAVEFORM_SAMPLESIZE; j++) { wavetable[wavetable_depth][j] = 0.0; } // fill in rest of wavetable for (int i = 1; i < wavetable.size()-1; i++) { double weight = (double) i / (double) wavetable[i].size(); for (int j = 0; j < wavetable[i].size(); j++) { wavetable[i][j] = (1.0 - weight) * wavetable[0][j] + weight * wavetable[wavetable_depth][j]; } } std::cout << "We made it"; }
void ResamplingNode::process(AudioSampleBuffer& buffer, MidiBuffer& midiMessages, int& nSamples) { //std::cout << "Resampling node sample count: " << nSamples << std::endl; ///buffer.getNumSamples() << std::endl; // save data at the beginning of each round of processing //writeContinuousBuffer(buffer.getSampleData(0), nSamples, 0); int nSamps = float(nSamples); int valuesNeeded = int(nSamps / ratio); if (ratio > 1.0001) { // pre-apply filter before downsampling filter->process(nSamples, buffer.getArrayOfChannels()); } // initialize variables tempBuffer->clear(); int sourceBufferPos = 0; int sourceBufferSize = buffer.getNumSamples(); float subSampleOffset = 0.0; int nextPos = (sourceBufferPos + 1) % sourceBufferSize; int tempBufferPos; // 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 } // copy the tempBuffer back into the original buffer buffer = AudioSampleBuffer(tempBuffer->getArrayOfChannels(), 2, tempBufferPos);//buffer.getNumSamples()); nSamples = valuesNeeded; }
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; } }