From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- dom/media/webaudio/BiquadFilterNode.cpp | 349 ++++++++++++++++++++++++++++++++ 1 file changed, 349 insertions(+) create mode 100644 dom/media/webaudio/BiquadFilterNode.cpp (limited to 'dom/media/webaudio/BiquadFilterNode.cpp') diff --git a/dom/media/webaudio/BiquadFilterNode.cpp b/dom/media/webaudio/BiquadFilterNode.cpp new file mode 100644 index 0000000000..772043a5f2 --- /dev/null +++ b/dom/media/webaudio/BiquadFilterNode.cpp @@ -0,0 +1,349 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "BiquadFilterNode.h" +#include "AlignmentUtils.h" +#include "AudioNodeEngine.h" +#include "AudioNodeTrack.h" +#include "AudioDestinationNode.h" +#include "PlayingRefChangeHandler.h" +#include "WebAudioUtils.h" +#include "blink/Biquad.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/ErrorResult.h" +#include "AudioParamTimeline.h" +#include "Tracing.h" + +namespace mozilla::dom { + +NS_IMPL_CYCLE_COLLECTION_INHERITED(BiquadFilterNode, AudioNode, mFrequency, + mDetune, mQ, mGain) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BiquadFilterNode) +NS_INTERFACE_MAP_END_INHERITING(AudioNode) + +NS_IMPL_ADDREF_INHERITED(BiquadFilterNode, AudioNode) +NS_IMPL_RELEASE_INHERITED(BiquadFilterNode, AudioNode) + +static void SetParamsOnBiquad(WebCore::Biquad& aBiquad, float aSampleRate, + BiquadFilterType aType, double aFrequency, + double aQ, double aGain, double aDetune) { + const double nyquist = aSampleRate * 0.5; + double normalizedFrequency = aFrequency / nyquist; + + if (aDetune) { + normalizedFrequency *= std::exp2(aDetune / 1200); + } + + switch (aType) { + case BiquadFilterType::Lowpass: + aBiquad.setLowpassParams(normalizedFrequency, aQ); + break; + case BiquadFilterType::Highpass: + aBiquad.setHighpassParams(normalizedFrequency, aQ); + break; + case BiquadFilterType::Bandpass: + aBiquad.setBandpassParams(normalizedFrequency, aQ); + break; + case BiquadFilterType::Lowshelf: + aBiquad.setLowShelfParams(normalizedFrequency, aGain); + break; + case BiquadFilterType::Highshelf: + aBiquad.setHighShelfParams(normalizedFrequency, aGain); + break; + case BiquadFilterType::Peaking: + aBiquad.setPeakingParams(normalizedFrequency, aQ, aGain); + break; + case BiquadFilterType::Notch: + aBiquad.setNotchParams(normalizedFrequency, aQ); + break; + case BiquadFilterType::Allpass: + aBiquad.setAllpassParams(normalizedFrequency, aQ); + break; + default: + MOZ_ASSERT_UNREACHABLE("We should never see the alternate names here"); + break; + } +} + +class BiquadFilterNodeEngine final : public AudioNodeEngine { + public: + BiquadFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination, + uint64_t aWindowID) + : AudioNodeEngine(aNode), + mDestination(aDestination->Track()) + // Keep the default values in sync with the default values in + // BiquadFilterNode::BiquadFilterNode + , + mType(BiquadFilterType::Lowpass), + mFrequency(350.f), + mDetune(0.f), + mQ(1.f), + mGain(0.f), + mWindowID(aWindowID) {} + + enum Parameters { TYPE, FREQUENCY, DETUNE, Q, GAIN }; + void SetInt32Parameter(uint32_t aIndex, int32_t aValue) override { + switch (aIndex) { + case TYPE: + mType = static_cast(aValue); + break; + default: + NS_ERROR("Bad BiquadFilterNode Int32Parameter"); + } + } + void RecvTimelineEvent(uint32_t aIndex, AudioTimelineEvent& aEvent) override { + MOZ_ASSERT(mDestination); + + WebAudioUtils::ConvertAudioTimelineEventToTicks(aEvent, mDestination); + + switch (aIndex) { + case FREQUENCY: + mFrequency.InsertEvent(aEvent); + break; + case DETUNE: + mDetune.InsertEvent(aEvent); + break; + case Q: + mQ.InsertEvent(aEvent); + break; + case GAIN: + mGain.InsertEvent(aEvent); + break; + default: + NS_ERROR("Bad BiquadFilterNodeEngine TimelineParameter"); + } + } + + void ProcessBlock(AudioNodeTrack* aTrack, GraphTime aFrom, + const AudioBlock& aInput, AudioBlock* aOutput, + bool* aFinished) override { + TRACE("BiquadFilterNode::ProcessBlock"); + float inputBuffer[WEBAUDIO_BLOCK_SIZE + 4]; + float* alignedInputBuffer = ALIGNED16(inputBuffer); + ASSERT_ALIGNED16(alignedInputBuffer); + + if (aInput.IsNull()) { + bool hasTail = false; + for (uint32_t i = 0; i < mBiquads.Length(); ++i) { + if (mBiquads[i].hasTail()) { + hasTail = true; + break; + } + } + if (!hasTail) { + if (!mBiquads.IsEmpty()) { + mBiquads.Clear(); + aTrack->ScheduleCheckForInactive(); + + RefPtr refchanged = + new PlayingRefChangeHandler(aTrack, + PlayingRefChangeHandler::RELEASE); + aTrack->Graph()->DispatchToMainThreadStableState(refchanged.forget()); + } + + aOutput->SetNull(WEBAUDIO_BLOCK_SIZE); + return; + } + + PodArrayZero(inputBuffer); + + } else if (mBiquads.Length() != aInput.ChannelCount()) { + if (mBiquads.IsEmpty()) { + RefPtr refchanged = + new PlayingRefChangeHandler(aTrack, + PlayingRefChangeHandler::ADDREF); + aTrack->Graph()->DispatchToMainThreadStableState(refchanged.forget()); + } else { // Help people diagnose bug 924718 + WebAudioUtils::LogToDeveloperConsole( + mWindowID, "BiquadFilterChannelCountChangeWarning"); + } + + // Adjust the number of biquads based on the number of channels + mBiquads.SetLength(aInput.ChannelCount()); + } + + uint32_t numberOfChannels = mBiquads.Length(); + aOutput->AllocateChannels(numberOfChannels); + + TrackTime pos = mDestination->GraphTimeToTrackTime(aFrom); + + double freq = mFrequency.GetValueAtTime(pos); + double q = mQ.GetValueAtTime(pos); + double gain = mGain.GetValueAtTime(pos); + double detune = mDetune.GetValueAtTime(pos); + + for (uint32_t i = 0; i < numberOfChannels; ++i) { + const float* input; + if (aInput.IsNull()) { + input = alignedInputBuffer; + } else { + input = static_cast(aInput.mChannelData[i]); + if (aInput.mVolume != 1.0) { + AudioBlockCopyChannelWithScale(input, aInput.mVolume, + alignedInputBuffer); + input = alignedInputBuffer; + } + } + SetParamsOnBiquad(mBiquads[i], aTrack->mSampleRate, mType, freq, q, gain, + detune); + + mBiquads[i].process(input, aOutput->ChannelFloatsForWrite(i), + aInput.GetDuration()); + } + } + + bool IsActive() const override { return !mBiquads.IsEmpty(); } + + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override { + // Not owned: + // - mDestination - probably not owned + // - AudioParamTimelines - counted in the AudioNode + size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf); + amount += mBiquads.ShallowSizeOfExcludingThis(aMallocSizeOf); + return amount; + } + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); + } + + private: + RefPtr mDestination; + BiquadFilterType mType; + AudioParamTimeline mFrequency; + AudioParamTimeline mDetune; + AudioParamTimeline mQ; + AudioParamTimeline mGain; + nsTArray mBiquads; + uint64_t mWindowID; +}; + +BiquadFilterNode::BiquadFilterNode(AudioContext* aContext) + : AudioNode(aContext, 2, ChannelCountMode::Max, + ChannelInterpretation::Speakers), + mType(BiquadFilterType::Lowpass) { + mFrequency = CreateAudioParam( + BiquadFilterNodeEngine::FREQUENCY, u"frequency"_ns, 350.f, + -(aContext->SampleRate() / 2), aContext->SampleRate() / 2); + mDetune = CreateAudioParam(BiquadFilterNodeEngine::DETUNE, u"detune"_ns, 0.f); + mQ = CreateAudioParam(BiquadFilterNodeEngine::Q, u"Q"_ns, 1.f); + mGain = CreateAudioParam(BiquadFilterNodeEngine::GAIN, u"gain"_ns, 0.f); + + uint64_t windowID = 0; + if (aContext->GetParentObject()) { + windowID = aContext->GetParentObject()->WindowID(); + } + BiquadFilterNodeEngine* engine = + new BiquadFilterNodeEngine(this, aContext->Destination(), windowID); + mTrack = AudioNodeTrack::Create( + aContext, engine, AudioNodeTrack::NO_TRACK_FLAGS, aContext->Graph()); +} + +/* static */ +already_AddRefed BiquadFilterNode::Create( + AudioContext& aAudioContext, const BiquadFilterOptions& aOptions, + ErrorResult& aRv) { + RefPtr audioNode = new BiquadFilterNode(&aAudioContext); + + audioNode->Initialize(aOptions, aRv); + if (NS_WARN_IF(aRv.Failed())) { + return nullptr; + } + + audioNode->SetType(aOptions.mType); + audioNode->Q()->SetInitialValue(aOptions.mQ); + audioNode->Detune()->SetInitialValue(aOptions.mDetune); + audioNode->Frequency()->SetInitialValue(aOptions.mFrequency); + audioNode->Gain()->SetInitialValue(aOptions.mGain); + + return audioNode.forget(); +} + +size_t BiquadFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { + size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf); + + if (mFrequency) { + amount += mFrequency->SizeOfIncludingThis(aMallocSizeOf); + } + + if (mDetune) { + amount += mDetune->SizeOfIncludingThis(aMallocSizeOf); + } + + if (mQ) { + amount += mQ->SizeOfIncludingThis(aMallocSizeOf); + } + + if (mGain) { + amount += mGain->SizeOfIncludingThis(aMallocSizeOf); + } + + return amount; +} + +size_t BiquadFilterNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); +} + +JSObject* BiquadFilterNode::WrapObject(JSContext* aCx, + JS::Handle aGivenProto) { + return BiquadFilterNode_Binding::Wrap(aCx, this, aGivenProto); +} + +void BiquadFilterNode::SetType(BiquadFilterType aType) { + mType = aType; + SendInt32ParameterToTrack(BiquadFilterNodeEngine::TYPE, + static_cast(aType)); +} + +void BiquadFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz, + const Float32Array& aMagResponse, + const Float32Array& aPhaseResponse, + ErrorResult& aRv) { + aFrequencyHz.ComputeState(); + aMagResponse.ComputeState(); + aPhaseResponse.ComputeState(); + + if (!(aFrequencyHz.Length() == aMagResponse.Length() && + aMagResponse.Length() == aPhaseResponse.Length())) { + aRv.ThrowInvalidAccessError("Parameter lengths must match"); + return; + } + + uint32_t length = aFrequencyHz.Length(); + if (!length) { + return; + } + + auto frequencies = MakeUnique(length); + float* frequencyHz = aFrequencyHz.Data(); + const double nyquist = Context()->SampleRate() * 0.5; + + // Normalize the frequencies + for (uint32_t i = 0; i < length; ++i) { + if (frequencyHz[i] >= 0 && frequencyHz[i] <= nyquist) { + frequencies[i] = static_cast(frequencyHz[i] / nyquist); + } else { + frequencies[i] = std::numeric_limits::quiet_NaN(); + } + } + + const double currentTime = Context()->CurrentTime(); + + double freq = mFrequency->GetValueAtTime(currentTime); + double q = mQ->GetValueAtTime(currentTime); + double gain = mGain->GetValueAtTime(currentTime); + double detune = mDetune->GetValueAtTime(currentTime); + + WebCore::Biquad biquad; + SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain, + detune); + biquad.getFrequencyResponse(int(length), frequencies.get(), + aMagResponse.Data(), aPhaseResponse.Data()); +} + +} // namespace mozilla::dom -- cgit v1.2.3