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(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. // Careful - this is a tryLock() and not an autolocker, so we must unlock() before every return. if (m_processLock.tryLock()) { // Check if it's time to start playing. double sampleRate = this->sampleRate(); size_t quantumStartFrame = context()->currentSampleFrame(); size_t quantumEndFrame = quantumStartFrame + framesToProcess; size_t startFrame = AudioUtilities::timeToSampleFrame(m_startTime, sampleRate); size_t endFrame = m_endTime == UnknownTime ? 0 : AudioUtilities::timeToSampleFrame(m_endTime, sampleRate); // If we know the end time and it's already passed, then don't bother doing any more rendering this cycle. if (m_endTime != UnknownTime && endFrame <= quantumStartFrame) { m_virtualReadIndex = 0; finish(); } if (m_playbackState == UNSCHEDULED_STATE || m_playbackState == FINISHED_STATE || !buffer() || startFrame >= quantumEndFrame) { // FIXME: can optimize here by propagating silent hint instead of forcing the whole chain to process silence. outputBus->zero(); m_processLock.unlock(); return; } if (m_playbackState == SCHEDULED_STATE) { // Increment the active source count only if we're transitioning from SCHEDULED_STATE to PLAYING_STATE. m_playbackState = PLAYING_STATE; context()->incrementActiveSourceCount(); } size_t quantumFrameOffset = startFrame > quantumStartFrame ? startFrame - quantumStartFrame : 0; quantumFrameOffset = min(quantumFrameOffset, framesToProcess); // clamp to valid range size_t bufferFramesToProcess = framesToProcess - quantumFrameOffset; for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) m_destinationChannels[i] = outputBus->channel(i)->mutableData(); // Render by reading directly from the buffer. renderFromBuffer(outputBus, quantumFrameOffset, bufferFramesToProcess); // Apply the gain (in-place) to the output bus. float totalGain = gain()->value() * m_buffer->gain(); outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain); // If the end time is somewhere in the middle of this time quantum, then simply zero out the // frames starting at the end time. if (m_endTime != UnknownTime && endFrame >= quantumStartFrame && endFrame < quantumEndFrame) { size_t zeroStartFrame = endFrame - quantumStartFrame; size_t framesToZero = framesToProcess - zeroStartFrame; bool isSafe = zeroStartFrame < framesToProcess && framesToZero <= framesToProcess && zeroStartFrame + framesToZero <= framesToProcess; ASSERT(isSafe); if (isSafe) { for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) memset(m_destinationChannels[i] + zeroStartFrame, 0, sizeof(float) * framesToZero); } m_virtualReadIndex = 0; finish(); } m_processLock.unlock(); } 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. // Careful - this is a tryLock() and not an autolocker, so we must unlock() before every return. if (m_processLock.tryLock()) { // Check if it's time to start playing. float sampleRate = this->sampleRate(); double quantumStartTime = context()->currentTime(); double quantumEndTime = quantumStartTime + framesToProcess / sampleRate; // If we know the end time and it's already passed, then don't bother doing any more rendering this cycle. if (m_endTime != UnknownTime && m_endTime <= quantumStartTime) { m_isPlaying = false; m_virtualReadIndex = 0; finish(); } if (!m_isPlaying || m_hasFinished || !buffer() || m_startTime >= quantumEndTime) { // FIXME: can optimize here by propagating silent hint instead of forcing the whole chain to process silence. outputBus->zero(); m_processLock.unlock(); return; } double quantumTimeOffset = m_startTime > quantumStartTime ? m_startTime - quantumStartTime : 0; size_t quantumFrameOffset = static_cast<unsigned>(quantumTimeOffset * sampleRate); quantumFrameOffset = min(quantumFrameOffset, framesToProcess); // clamp to valid range size_t bufferFramesToProcess = framesToProcess - quantumFrameOffset; // Render by reading directly from the buffer. renderFromBuffer(outputBus, quantumFrameOffset, bufferFramesToProcess); // Apply the gain (in-place) to the output bus. double totalGain = gain()->value() * m_buffer->gain(); outputBus->copyWithGainFrom(*outputBus, &m_lastGain, totalGain); // If the end time is somewhere in the middle of this time quantum, then simply zero out the // frames starting at the end time. if (m_endTime != UnknownTime && m_endTime >= quantumStartTime && m_endTime < quantumEndTime) { size_t zeroStartFrame = narrowPrecisionToFloat((m_endTime - quantumStartTime) * sampleRate); size_t framesToZero = framesToProcess - zeroStartFrame; bool isSafe = zeroStartFrame < framesToProcess && framesToZero <= framesToProcess && zeroStartFrame + framesToZero <= framesToProcess; ASSERT(isSafe); if (isSafe) { for (unsigned i = 0; i < outputBus->numberOfChannels(); ++i) memset(outputBus->channel(i)->data() + zeroStartFrame, 0, sizeof(float) * framesToZero); } m_isPlaying = false; m_virtualReadIndex = 0; finish(); } m_processLock.unlock(); } 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(); }