/* ============================================================================== This file was auto-generated! It contains the basic framework code for an ARA playback renderer implementation. ============================================================================== */ %%araplaybackrenderer_headers%% //============================================================================== void %%araplaybackrenderer_class_name%%::prepareToPlay (double sampleRateIn, int maximumSamplesPerBlockIn, int numChannelsIn, juce::AudioProcessor::ProcessingPrecision, AlwaysNonRealtime alwaysNonRealtime) { numChannels = numChannelsIn; sampleRate = sampleRateIn; maximumSamplesPerBlock = maximumSamplesPerBlockIn; useBufferedAudioSourceReader = alwaysNonRealtime == AlwaysNonRealtime::no; } void %%araplaybackrenderer_class_name%%::releaseResources() { } //============================================================================== bool %%araplaybackrenderer_class_name%%::processBlock (juce::AudioBuffer& buffer, juce::AudioProcessor::Realtime realtime, const juce::AudioPlayHead::PositionInfo& positionInfo) noexcept { const auto numSamples = buffer.getNumSamples(); jassert (numSamples <= maximumSamplesPerBlock); jassert (numChannels == buffer.getNumChannels()); jassert (realtime == juce::AudioProcessor::Realtime::no || useBufferedAudioSourceReader); const auto timeInSamples = positionInfo.getTimeInSamples().orFallback (0); const auto isPlaying = positionInfo.getIsPlaying(); bool success = true; bool didRenderAnyRegion = false; if (isPlaying) { const auto blockRange = juce::Range::withStartAndLength (timeInSamples, numSamples); for (const auto& playbackRegion : getPlaybackRegions()) { // Evaluate region borders in song time, calculate sample range to render in song time. // Note that this example does not use head- or tailtime, so the includeHeadAndTail // parameter is set to false here - this might need to be adjusted in actual plug-ins. const auto playbackSampleRange = playbackRegion->getSampleRange (sampleRate, juce::ARAPlaybackRegion::IncludeHeadAndTail::no); auto renderRange = blockRange.getIntersectionWith (playbackSampleRange); if (renderRange.isEmpty()) continue; // Evaluate region borders in modification/source time and calculate offset between // song and source samples, then clip song samples accordingly // (if an actual plug-in supports time stretching, this must be taken into account here). juce::Range modificationSampleRange { playbackRegion->getStartInAudioModificationSamples(), playbackRegion->getEndInAudioModificationSamples() }; const auto modificationSampleOffset = modificationSampleRange.getStart() - playbackSampleRange.getStart(); renderRange = renderRange.getIntersectionWith (modificationSampleRange.movedToStartAt (playbackSampleRange.getStart())); if (renderRange.isEmpty()) continue; // Now calculate the samples in renderRange for this PlaybackRegion based on the ARA model // graph. If didRenderAnyRegion is true, add the region's output samples in renderRange to // the buffer. Otherwise the buffer needs to be initialised so the sample value must be // overwritten. const int numSamplesToRead = (int) renderRange.getLength(); const int startInBuffer = (int) (renderRange.getStart() - blockRange.getStart()); const auto startInSource = renderRange.getStart() + modificationSampleOffset; for (int c = 0; c < numChannels; ++c) { auto* channelData = buffer.getWritePointer (c); for (int i = 0; i < numSamplesToRead; ++i) { // Calculate region output sample at index startInSource + i ... float sample = 0.0f; if (didRenderAnyRegion) channelData[startInBuffer + i] += sample; else channelData[startInBuffer + i] = sample; } } // If rendering first region, clear any excess at start or end of the region. if (! didRenderAnyRegion) { if (startInBuffer != 0) buffer.clear (0, startInBuffer); const int endInBuffer = startInBuffer + numSamples; const int remainingSamples = numSamples - endInBuffer; if (remainingSamples != 0) buffer.clear (endInBuffer, remainingSamples); didRenderAnyRegion = true; } } } if (! didRenderAnyRegion) buffer.clear(); return success; }