void PannerNodeEngine::EqualPowerPanningFunction(const AudioBlock& aInput, AudioBlock* aOutput) { float azimuth, elevation, gainL, gainR, normalizedAzimuth, distanceGain, coneGain; int inputChannels = aInput.ChannelCount(); // If both the listener are in the same spot, and no cone gain is specified, // this node is noop. if (mListenerPosition == mPosition && mConeInnerAngle == 360 && mConeOuterAngle == 360) { *aOutput = aInput; return; } // The output of this node is always stereo, no matter what the inputs are. aOutput->AllocateChannels(2); ComputeAzimuthAndElevation(azimuth, elevation); coneGain = ComputeConeGain(); // The following algorithm is described in the spec. // Clamp azimuth in the [-90, 90] range. azimuth = min(180.f, max(-180.f, azimuth)); // Wrap around if (azimuth < -90.f) { azimuth = -180.f - azimuth; } else if (azimuth > 90) { azimuth = 180.f - azimuth; } // Normalize the value in the [0, 1] range. if (inputChannels == 1) { normalizedAzimuth = (azimuth + 90.f) / 180.f; } else { if (azimuth <= 0) { normalizedAzimuth = (azimuth + 90.f) / 90.f; } else { normalizedAzimuth = azimuth / 90.f; } } distanceGain = ComputeDistanceGain(); // Actually compute the left and right gain. gainL = cos(0.5 * M_PI * normalizedAzimuth); gainR = sin(0.5 * M_PI * normalizedAzimuth); // Compute the output. ApplyStereoPanning(aInput, aOutput, gainL, gainR, azimuth <= 0); aOutput->mVolume = aInput.mVolume * distanceGain * coneGain; }
void ProcessBlock(AudioNodeStream* aStream, GraphTime aFrom, const AudioBlock& aInput, AudioBlock* aOutput, bool* aFinished) override { // This node is not connected to anything. Per spec, we don't fire the // onaudioprocess event. We also want to clear out the input and output // buffer queue, and output a null buffer. if (!mIsConnected) { aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); mSharedBuffers->Reset(); mInputWriteIndex = 0; return; } // The input buffer is allocated lazily when non-null input is received. if (!aInput.IsNull() && !mInputBuffer) { mInputBuffer = ThreadSharedFloatArrayBufferList:: Create(mInputChannelCount, mBufferSize, fallible); if (mInputBuffer && mInputWriteIndex) { // Zero leading for null chunks that were skipped. for (uint32_t i = 0; i < mInputChannelCount; ++i) { float* channelData = mInputBuffer->GetDataForWrite(i); PodZero(channelData, mInputWriteIndex); } } } // First, record our input buffer, if its allocation succeeded. uint32_t inputChannelCount = mInputBuffer ? mInputBuffer->GetChannels() : 0; for (uint32_t i = 0; i < inputChannelCount; ++i) { float* writeData = mInputBuffer->GetDataForWrite(i) + mInputWriteIndex; if (aInput.IsNull()) { PodZero(writeData, aInput.GetDuration()); } else { MOZ_ASSERT(aInput.GetDuration() == WEBAUDIO_BLOCK_SIZE, "sanity check"); MOZ_ASSERT(aInput.ChannelCount() == inputChannelCount); AudioBlockCopyChannelWithScale(static_cast<const float*>(aInput.mChannelData[i]), aInput.mVolume, writeData); } } mInputWriteIndex += aInput.GetDuration(); // Now, see if we have data to output // Note that we need to do this before sending the buffer to the main // thread so that our delay time is updated. *aOutput = mSharedBuffers->GetOutputBuffer(); if (mInputWriteIndex >= mBufferSize) { SendBuffersToMainThread(aStream, aFrom); mInputWriteIndex -= mBufferSize; } }
void DelayBuffer::Write(const AudioBlock& aInputChunk) { // We must have a reference to the buffer if there are channels MOZ_ASSERT(aInputChunk.IsNull() == !aInputChunk.ChannelCount()); #ifdef DEBUG MOZ_ASSERT(!mHaveWrittenBlock); mHaveWrittenBlock = true; #endif if (!EnsureBuffer()) { return; } if (mCurrentChunk == mLastReadChunk) { mLastReadChunk = -1; // invalidate cache } mChunks[mCurrentChunk] = aInputChunk.AsAudioChunk(); }
void PannerNodeEngine::EqualPowerPanningFunction(const AudioBlock& aInput, AudioBlock* aOutput, StreamTime tick) { float azimuth, elevation, gainL, gainR, normalizedAzimuth, distanceGain, coneGain; int inputChannels = aInput.ChannelCount(); // Optimize the case where the position and orientation is constant for this // processing block: we can just apply a constant gain on the left and right // channel if (mPositionX.HasSimpleValue() && mPositionY.HasSimpleValue() && mPositionZ.HasSimpleValue() && mOrientationX.HasSimpleValue() && mOrientationY.HasSimpleValue() && mOrientationZ.HasSimpleValue()) { ThreeDPoint position = ConvertAudioParamTimelineTo3DP(mPositionX, mPositionY, mPositionZ, tick); ThreeDPoint orientation = ConvertAudioParamTimelineTo3DP(mOrientationX, mOrientationY, mOrientationZ, tick); if (!orientation.IsZero()) { orientation.Normalize(); } // If both the listener are in the same spot, and no cone gain is specified, // this node is noop. if (mListenerPosition == position && mConeInnerAngle == 360 && mConeOuterAngle == 360) { *aOutput = aInput; return; } // The output of this node is always stereo, no matter what the inputs are. aOutput->AllocateChannels(2); ComputeAzimuthAndElevation(position, azimuth, elevation); coneGain = ComputeConeGain(position, orientation); // The following algorithm is described in the spec. // Clamp azimuth in the [-90, 90] range. azimuth = min(180.f, max(-180.f, azimuth)); // Wrap around if (azimuth < -90.f) { azimuth = -180.f - azimuth; } else if (azimuth > 90) { azimuth = 180.f - azimuth; } // Normalize the value in the [0, 1] range. if (inputChannels == 1) { normalizedAzimuth = (azimuth + 90.f) / 180.f; } else { if (azimuth <= 0) { normalizedAzimuth = (azimuth + 90.f) / 90.f; } else { normalizedAzimuth = azimuth / 90.f; } } distanceGain = ComputeDistanceGain(position); // Actually compute the left and right gain. gainL = cos(0.5 * M_PI * normalizedAzimuth); gainR = sin(0.5 * M_PI * normalizedAzimuth); // Compute the output. ApplyStereoPanning(aInput, aOutput, gainL, gainR, azimuth <= 0); aOutput->mVolume = aInput.mVolume * distanceGain * coneGain; } else { float positionX[WEBAUDIO_BLOCK_SIZE]; float positionY[WEBAUDIO_BLOCK_SIZE]; float positionZ[WEBAUDIO_BLOCK_SIZE]; float orientationX[WEBAUDIO_BLOCK_SIZE]; float orientationY[WEBAUDIO_BLOCK_SIZE]; float orientationZ[WEBAUDIO_BLOCK_SIZE]; // The output of this node is always stereo, no matter what the inputs are. aOutput->AllocateChannels(2); if (!mPositionX.HasSimpleValue()) { mPositionX.GetValuesAtTime(tick, positionX, WEBAUDIO_BLOCK_SIZE); } else { positionX[0] = mPositionX.GetValueAtTime(tick); } if (!mPositionY.HasSimpleValue()) { mPositionY.GetValuesAtTime(tick, positionY, WEBAUDIO_BLOCK_SIZE); } else { positionY[0] = mPositionY.GetValueAtTime(tick); } if (!mPositionZ.HasSimpleValue()) { mPositionZ.GetValuesAtTime(tick, positionZ, WEBAUDIO_BLOCK_SIZE); } else { positionZ[0] = mPositionZ.GetValueAtTime(tick); } if (!mOrientationX.HasSimpleValue()) { mOrientationX.GetValuesAtTime(tick, orientationX, WEBAUDIO_BLOCK_SIZE); } else { orientationX[0] = mOrientationX.GetValueAtTime(tick); } if (!mOrientationY.HasSimpleValue()) { mOrientationY.GetValuesAtTime(tick, orientationY, WEBAUDIO_BLOCK_SIZE); } else { orientationY[0] = mOrientationY.GetValueAtTime(tick); } if (!mOrientationZ.HasSimpleValue()) { mOrientationZ.GetValuesAtTime(tick, orientationZ, WEBAUDIO_BLOCK_SIZE); } else { orientationZ[0] = mOrientationZ.GetValueAtTime(tick); } float computedGain[2*WEBAUDIO_BLOCK_SIZE + 4]; bool onLeft[WEBAUDIO_BLOCK_SIZE]; float* alignedComputedGain = ALIGNED16(computedGain); ASSERT_ALIGNED16(alignedComputedGain); for (size_t counter = 0; counter < WEBAUDIO_BLOCK_SIZE; ++counter) { ThreeDPoint position(mPositionX.HasSimpleValue() ? positionX[0] : positionX[counter], mPositionY.HasSimpleValue() ? positionY[0] : positionY[counter], mPositionZ.HasSimpleValue() ? positionZ[0] : positionZ[counter]); ThreeDPoint orientation(mOrientationX.HasSimpleValue() ? orientationX[0] : orientationX[counter], mOrientationY.HasSimpleValue() ? orientationY[0] : orientationY[counter], mOrientationZ.HasSimpleValue() ? orientationZ[0] : orientationZ[counter]); if (!orientation.IsZero()) { orientation.Normalize(); } ComputeAzimuthAndElevation(position, azimuth, elevation); coneGain = ComputeConeGain(position, orientation); // The following algorithm is described in the spec. // Clamp azimuth in the [-90, 90] range. azimuth = min(180.f, max(-180.f, azimuth)); // Wrap around if (azimuth < -90.f) { azimuth = -180.f - azimuth; } else if (azimuth > 90) { azimuth = 180.f - azimuth; } // Normalize the value in the [0, 1] range. if (inputChannels == 1) { normalizedAzimuth = (azimuth + 90.f) / 180.f; } else { if (azimuth <= 0) { normalizedAzimuth = (azimuth + 90.f) / 90.f; } else { normalizedAzimuth = azimuth / 90.f; } } distanceGain = ComputeDistanceGain(position); // Actually compute the left and right gain. float gainL = cos(0.5 * M_PI * normalizedAzimuth) * aInput.mVolume * distanceGain * coneGain; float gainR = sin(0.5 * M_PI * normalizedAzimuth) * aInput.mVolume * distanceGain * coneGain; alignedComputedGain[counter] = gainL; alignedComputedGain[WEBAUDIO_BLOCK_SIZE + counter] = gainR; onLeft[counter] = azimuth <= 0; } // Apply the gain to the output buffer ApplyStereoPanning(aInput, aOutput, alignedComputedGain, &alignedComputedGain[WEBAUDIO_BLOCK_SIZE], onLeft); } }