Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "IIRFilterNode.h"
8 : #include "AudioNodeEngine.h"
9 :
10 : #include "blink/IIRFilter.h"
11 :
12 : #include "nsGkAtoms.h"
13 :
14 : namespace mozilla {
15 : namespace dom {
16 :
17 0 : NS_IMPL_ISUPPORTS_INHERITED0(IIRFilterNode, AudioNode)
18 :
19 0 : class IIRFilterNodeEngine final : public AudioNodeEngine
20 : {
21 : public:
22 0 : IIRFilterNodeEngine(AudioNode* aNode, AudioDestinationNode* aDestination,
23 : const AudioDoubleArray &aFeedforward,
24 : const AudioDoubleArray &aFeedback,
25 : uint64_t aWindowID)
26 0 : : AudioNodeEngine(aNode)
27 0 : , mDestination(aDestination->Stream())
28 : , mFeedforward(aFeedforward)
29 : , mFeedback(aFeedback)
30 0 : , mWindowID(aWindowID)
31 : {
32 0 : }
33 :
34 0 : void ProcessBlock(AudioNodeStream* aStream,
35 : GraphTime aFrom,
36 : const AudioBlock& aInput,
37 : AudioBlock* aOutput,
38 : bool* aFinished) override
39 : {
40 : float inputBuffer[WEBAUDIO_BLOCK_SIZE + 4];
41 0 : float* alignedInputBuffer = ALIGNED16(inputBuffer);
42 0 : ASSERT_ALIGNED16(alignedInputBuffer);
43 :
44 0 : if (aInput.IsNull()) {
45 0 : if (!mIIRFilters.IsEmpty()) {
46 0 : bool allZero = true;
47 0 : for (uint32_t i = 0; i < mIIRFilters.Length(); ++i) {
48 0 : allZero &= mIIRFilters[i]->buffersAreZero();
49 : }
50 :
51 : // all filter buffer values are zero, so the output will be zero
52 : // as well.
53 0 : if (allZero) {
54 0 : mIIRFilters.Clear();
55 0 : aStream->ScheduleCheckForInactive();
56 :
57 : RefPtr<PlayingRefChangeHandler> refchanged =
58 0 : new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::RELEASE);
59 0 : aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
60 0 : refchanged.forget());
61 :
62 0 : aOutput->SetNull(WEBAUDIO_BLOCK_SIZE);
63 0 : return;
64 : }
65 :
66 0 : PodZero(alignedInputBuffer, WEBAUDIO_BLOCK_SIZE);
67 : }
68 0 : } else if(mIIRFilters.Length() != aInput.ChannelCount()){
69 0 : if (mIIRFilters.IsEmpty()) {
70 : RefPtr<PlayingRefChangeHandler> refchanged =
71 0 : new PlayingRefChangeHandler(aStream, PlayingRefChangeHandler::ADDREF);
72 0 : aStream->Graph()->DispatchToMainThreadAfterStreamStateUpdate(
73 0 : refchanged.forget());
74 : } else {
75 0 : WebAudioUtils::LogToDeveloperConsole(mWindowID,
76 0 : "IIRFilterChannelCountChangeWarning");
77 : }
78 :
79 : // Adjust the number of filters based on the number of channels
80 0 : mIIRFilters.SetLength(aInput.ChannelCount());
81 0 : for (size_t i = 0; i < aInput.ChannelCount(); ++i) {
82 0 : mIIRFilters[i] = new blink::IIRFilter(&mFeedforward, &mFeedback);
83 : }
84 : }
85 :
86 0 : uint32_t numberOfChannels = mIIRFilters.Length();
87 0 : aOutput->AllocateChannels(numberOfChannels);
88 :
89 0 : for (uint32_t i = 0; i < numberOfChannels; ++i) {
90 : const float* input;
91 0 : if (aInput.IsNull()) {
92 0 : input = alignedInputBuffer;
93 : } else {
94 0 : input = static_cast<const float*>(aInput.mChannelData[i]);
95 0 : if (aInput.mVolume != 1.0) {
96 0 : AudioBlockCopyChannelWithScale(input, aInput.mVolume, alignedInputBuffer);
97 0 : input = alignedInputBuffer;
98 : }
99 : }
100 :
101 0 : mIIRFilters[i]->process(input,
102 : aOutput->ChannelFloatsForWrite(i),
103 0 : aInput.GetDuration());
104 : }
105 : }
106 :
107 0 : bool IsActive() const override
108 : {
109 0 : return !mIIRFilters.IsEmpty();
110 : }
111 :
112 0 : size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
113 : {
114 : // Not owned:
115 : // - mDestination - probably not owned
116 : // - AudioParamTimelines - counted in the AudioNode
117 0 : size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
118 0 : amount += mIIRFilters.ShallowSizeOfExcludingThis(aMallocSizeOf);
119 0 : return amount;
120 : }
121 :
122 0 : size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override
123 : {
124 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
125 : }
126 :
127 : private:
128 : AudioNodeStream* mDestination;
129 : nsTArray<nsAutoPtr<blink::IIRFilter>> mIIRFilters;
130 : AudioDoubleArray mFeedforward;
131 : AudioDoubleArray mFeedback;
132 : uint64_t mWindowID;
133 : };
134 :
135 0 : IIRFilterNode::IIRFilterNode(AudioContext* aContext,
136 : const Sequence<double>& aFeedforward,
137 0 : const Sequence<double>& aFeedback)
138 : : AudioNode(aContext,
139 : 2,
140 : ChannelCountMode::Max,
141 0 : ChannelInterpretation::Speakers)
142 : {
143 0 : mFeedforward.SetLength(aFeedforward.Length());
144 0 : PodCopy(mFeedforward.Elements(), aFeedforward.Elements(), aFeedforward.Length());
145 0 : mFeedback.SetLength(aFeedback.Length());
146 0 : PodCopy(mFeedback.Elements(), aFeedback.Elements(), aFeedback.Length());
147 :
148 : // Scale coefficients -- we guarantee that mFeedback != 0 when creating
149 : // the IIRFilterNode.
150 0 : double scale = mFeedback[0];
151 0 : double* elements = mFeedforward.Elements();
152 0 : for (size_t i = 0; i < mFeedforward.Length(); ++i) {
153 0 : elements[i] /= scale;
154 : }
155 :
156 0 : elements = mFeedback.Elements();
157 0 : for (size_t i = 0; i < mFeedback.Length(); ++i) {
158 0 : elements[i] /= scale;
159 : }
160 :
161 : // We check that this is exactly equal to one later in blink/IIRFilter.cpp
162 0 : elements[0] = 1.0;
163 :
164 0 : uint64_t windowID = aContext->GetParentObject()->WindowID();
165 0 : IIRFilterNodeEngine* engine = new IIRFilterNodeEngine(this, aContext->Destination(), mFeedforward, mFeedback, windowID);
166 0 : mStream = AudioNodeStream::Create(aContext, engine,
167 : AudioNodeStream::NO_STREAM_FLAGS,
168 0 : aContext->Graph());
169 0 : }
170 :
171 : /* static */ already_AddRefed<IIRFilterNode>
172 0 : IIRFilterNode::Create(AudioContext& aAudioContext,
173 : const IIRFilterOptions& aOptions,
174 : ErrorResult& aRv)
175 : {
176 0 : if (aAudioContext.CheckClosed(aRv)) {
177 0 : return nullptr;
178 : }
179 :
180 0 : if (aOptions.mFeedforward.Length() == 0 || aOptions.mFeedforward.Length() > 20) {
181 0 : aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
182 0 : return nullptr;
183 : }
184 :
185 0 : if (aOptions.mFeedback.Length() == 0 || aOptions.mFeedback.Length() > 20) {
186 0 : aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
187 0 : return nullptr;
188 : }
189 :
190 0 : bool feedforwardAllZeros = true;
191 0 : for (size_t i = 0; i < aOptions.mFeedforward.Length(); ++i) {
192 0 : if (aOptions.mFeedforward.Elements()[i] != 0.0) {
193 0 : feedforwardAllZeros = false;
194 : }
195 : }
196 :
197 0 : if (feedforwardAllZeros || aOptions.mFeedback.Elements()[0] == 0.0) {
198 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
199 0 : return nullptr;
200 : }
201 :
202 : RefPtr<IIRFilterNode> audioNode =
203 0 : new IIRFilterNode(&aAudioContext, aOptions.mFeedforward, aOptions.mFeedback);
204 :
205 0 : audioNode->Initialize(aOptions, aRv);
206 0 : if (NS_WARN_IF(aRv.Failed())) {
207 0 : return nullptr;
208 : }
209 :
210 0 : return audioNode.forget();
211 : }
212 :
213 : size_t
214 0 : IIRFilterNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
215 : {
216 0 : size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
217 0 : return amount;
218 : }
219 :
220 : size_t
221 0 : IIRFilterNode::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
222 : {
223 0 : return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
224 : }
225 :
226 : JSObject*
227 0 : IIRFilterNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
228 : {
229 0 : return IIRFilterNodeBinding::Wrap(aCx, this, aGivenProto);
230 : }
231 :
232 : void
233 0 : IIRFilterNode::GetFrequencyResponse(const Float32Array& aFrequencyHz,
234 : const Float32Array& aMagResponse,
235 : const Float32Array& aPhaseResponse)
236 : {
237 0 : aFrequencyHz.ComputeLengthAndData();
238 0 : aMagResponse.ComputeLengthAndData();
239 0 : aPhaseResponse.ComputeLengthAndData();
240 :
241 0 : uint32_t length = std::min(std::min(aFrequencyHz.Length(),
242 0 : aMagResponse.Length()),
243 0 : aPhaseResponse.Length());
244 0 : if (!length) {
245 0 : return;
246 : }
247 :
248 0 : auto frequencies = MakeUnique<float[]>(length);
249 0 : float* frequencyHz = aFrequencyHz.Data();
250 0 : const double nyquist = Context()->SampleRate() * 0.5;
251 :
252 : // Normalize the frequencies
253 0 : for (uint32_t i = 0; i < length; ++i) {
254 0 : if (frequencyHz[i] >= 0 && frequencyHz[i] <= nyquist) {
255 0 : frequencies[i] = static_cast<float>(frequencyHz[i] / nyquist);
256 : } else {
257 0 : frequencies[i] = std::numeric_limits<float>::quiet_NaN();
258 : }
259 : }
260 :
261 0 : blink::IIRFilter filter(&mFeedforward, &mFeedback);
262 0 : filter.getFrequencyResponse(int(length), frequencies.get(), aMagResponse.Data(), aPhaseResponse.Data());
263 : }
264 :
265 : } // namespace dom
266 : } // namespace mozilla
|