void AudioBufferSourceNode::process(size_t framesToProcess) { AudioBus* outputBus = output(0)->bus(); if (!isInitialized()) { outputBus->zero(); return; } // The audio thread can't block on this lock, so we call tryLock() instead. MutexTryLocker tryLocker(m_processLock); if (tryLocker.locked()) { if (!buffer()) { outputBus->zero(); return; } // After calling setBuffer() with a buffer having a different number of channels, there can in rare cases be a slight delay // before the output bus is updated to the new number of channels because of use of tryLocks() in the context's updating system. // In this case, if the the buffer has just been changed and we're not quite ready yet, then just output silence. if (numberOfChannels() != buffer()->numberOfChannels()) { outputBus->zero(); return; } size_t quantumFrameOffset; size_t bufferFramesToProcess; updateSchedulingInfo(framesToProcess, outputBus, quantumFrameOffset, bufferFramesToProcess); if (!bufferFramesToProcess) { outputBus->zero(); return; } for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) m_destinationChannels[i] = outputBus->channel(i)->mutableData(); // Render by reading directly from the buffer. if (!renderFromBuffer(outputBus, quantumFrameOffset, bufferFramesToProcess)) { outputBus->zero(); return; } // Apply the gain (in-place) to the output bus. float totalGain = gain()->value() * m_buffer->gain(); outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain); outputBus->clearSilentFlag(); } else { // Too bad - the tryLock() failed. We must be in the middle of changing buffers and were already outputting silence anyway. outputBus->zero(); } }
void AudioBufferSourceNode::process(size_t framesToProcess) { AudioBus* outputBus = output(0)->bus(); if (!isInitialized()) { outputBus->zero(); return; } // The audio thread can't block on this lock, so we call tryLock() instead. MutexTryLocker tryLocker(m_processLock); if (tryLocker.locked()) { if (!buffer()) { outputBus->zero(); return; } size_t quantumFrameOffset; size_t bufferFramesToProcess; updateSchedulingInfo(framesToProcess, outputBus, quantumFrameOffset, bufferFramesToProcess); if (!bufferFramesToProcess) { outputBus->zero(); return; } for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) m_destinationChannels[i] = outputBus->channel(i)->mutableData(); // Render by reading directly from the buffer. if (!renderFromBuffer(outputBus, quantumFrameOffset, bufferFramesToProcess)) { outputBus->zero(); return; } // Apply the gain (in-place) to the output bus. float totalGain = gain()->value() * m_buffer->gain(); outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain); outputBus->clearSilentFlag(); } else { // Too bad - the tryLock() failed. We must be in the middle of changing buffers and were already outputting silence anyway. outputBus->zero(); } }
void AudioBufferSourceNode::process(ContextRenderLock& r, size_t framesToProcess) { AudioBus* outputBus = output(0)->bus(r); if (!buffer() || !isInitialized() || ! r.context()) { outputBus->zero(); return; } // After calling setBuffer() with a buffer having a different number of channels, there can in rare cases be a slight delay // before the output bus is updated to the new number of channels because of use of tryLocks() in the context's updating system. // In this case, if the the buffer has just been changed and we're not quite ready yet, then just output silence. if (numberOfChannels(r) != buffer()->numberOfChannels()) { outputBus->zero(); return; } if (m_startRequested) { // Do sanity checking of grain parameters versus buffer size. double bufferDuration = buffer()->duration(); double grainOffset = std::max(0.0, m_requestGrainOffset); m_grainOffset = std::min(bufferDuration, grainOffset); m_grainOffset = grainOffset; // Handle default/unspecified duration. double maxDuration = bufferDuration - grainOffset; double grainDuration = m_requestGrainDuration; if (!grainDuration) grainDuration = maxDuration; grainDuration = std::max(0.0, grainDuration); grainDuration = std::min(maxDuration, grainDuration); m_grainDuration = grainDuration; m_isGrain = true; m_startTime = m_requestWhen; // We call timeToSampleFrame here since at playbackRate == 1 we don't want to go through linear interpolation // at a sub-sample position since it will degrade the quality. // When aligned to the sample-frame the playback will be identical to the PCM data stored in the buffer. // Since playbackRate == 1 is very common, it's worth considering quality. m_virtualReadIndex = AudioUtilities::timeToSampleFrame(m_grainOffset, buffer()->sampleRate()); m_startRequested = false; } size_t quantumFrameOffset; size_t bufferFramesToProcess; updateSchedulingInfo(r, framesToProcess, outputBus, quantumFrameOffset, bufferFramesToProcess); if (!bufferFramesToProcess) { outputBus->zero(); return; } for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) { m_destinationChannels[i] = outputBus->channel(i)->mutableData(); } // Render by reading directly from the buffer. if (!renderFromBuffer(r, outputBus, quantumFrameOffset, bufferFramesToProcess)) { outputBus->zero(); return; } // Apply the gain (in-place) to the output bus. float totalGain = gain()->value(r) * m_buffer->gain(); outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain); outputBus->clearSilentFlag(); }