react-native-audio-api 0.7.0-nightly-fba4835-20250722 → 0.7.0-nightly-4fc09d1-20250724

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,7 @@ BiquadFilterNode::BiquadFilterNode(BaseAudioContext *context)
26
26
  context);
27
27
  type_ = BiquadFilterType::LOWPASS;
28
28
  isInitialized_ = true;
29
+ channelCountMode_ = ChannelCountMode::MAX;
29
30
  }
30
31
 
31
32
  std::string BiquadFilterNode::getType() {
@@ -56,17 +57,28 @@ std::shared_ptr<AudioParam> BiquadFilterNode::getGainParam() const {
56
57
  // https://www.dsprelated.com/freebooks/filters/Frequency_Response_Analysis.html
57
58
  // https://www.dsprelated.com/freebooks/filters/Transfer_Function_Analysis.html
58
59
  //
59
- // frequency response - H(z) = (b0 + b1*z^(-1) + b2*z^(-2))/(1 + a1*z^(-1) +
60
- // a2*z^(-2)) = ((b0 * z + b1) * z + b2) / ((z + a1) * z + a2) phase response -
61
- // angle of the frequency response
60
+ // frequency response - H(z)
61
+ // b0 + b1 * z^(-1) + b2 * z^(-2)
62
+ // H(z) = -------------------------------
63
+ // 1 + a1 * z^(-1) + a2 * z^(-2)
64
+ //
65
+ // b0 + (b1 + b2 * z1) * z1
66
+ // = --------------------------
67
+ // (1 + (a1 + a2 * z1) * z1
68
+ //
69
+ // where z1 = 1/z and z = e^(j * pi * frequency)
70
+ // z1 = e^(-j * pi * frequency)
71
+ //
72
+ // phase response - angle of the frequency response
73
+ //
62
74
 
63
75
  void BiquadFilterNode::getFrequencyResponse(
64
76
  const float *frequencyArray,
65
77
  float *magResponseOutput,
66
78
  float *phaseResponseOutput,
67
79
  const int length) {
68
- applyFilter();
69
80
 
81
+ // Local copies for micro-optimization
70
82
  float b0 = b0_;
71
83
  float b1 = b1_;
72
84
  float b2 = b2_;
@@ -74,10 +86,17 @@ void BiquadFilterNode::getFrequencyResponse(
74
86
  float a2 = a2_;
75
87
 
76
88
  for (size_t i = 0; i < length; i++) {
77
- auto omega = PI * frequencyArray[i] / context_->getNyquistFrequency();
89
+ if (frequencyArray[i] < 0.0 || frequencyArray[i] > 1.0) {
90
+ magResponseOutput[i] = std::nanf("");
91
+ phaseResponseOutput[i] = std::nanf("");
92
+ continue;
93
+ }
94
+
95
+ auto omega = -PI * frequencyArray[i];
78
96
  auto z = std::complex<float>(cos(omega), sin(omega));
79
- auto response = ((b0 * z + b1) * z + b2) / ((z + a1) * z + a2);
80
- magResponseOutput[i] = static_cast<float>(abs(response));
97
+ auto response = (b0 + (b1 + b2 * z) * z) /
98
+ (std::complex<float>(1, 0) + (a1 + a2 * z) * z);
99
+ magResponseOutput[i] = static_cast<float>(std::abs(response));
81
100
  phaseResponseOutput[i] =
82
101
  static_cast<float>(atan2(imag(response), real(response)));
83
102
  }
@@ -106,11 +125,12 @@ void BiquadFilterNode::setNormalizedCoefficients(
106
125
  }
107
126
 
108
127
  void BiquadFilterNode::setLowpassCoefficients(float frequency, float Q) {
109
- frequency = std::clamp(frequency, 0.0f, 1.0f);
110
- if (frequency == 1.0) {
128
+ // Limit frequency to [0, 1] range
129
+ if (frequency >= 1.0) {
111
130
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
112
131
  return;
113
132
  }
133
+
114
134
  if (frequency <= 0.0) {
115
135
  setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
116
136
  return;
@@ -129,8 +149,7 @@ void BiquadFilterNode::setLowpassCoefficients(float frequency, float Q) {
129
149
  }
130
150
 
131
151
  void BiquadFilterNode::setHighpassCoefficients(float frequency, float Q) {
132
- frequency = std::clamp(frequency, 0.0f, 1.0f);
133
- if (frequency == 1.0) {
152
+ if (frequency >= 1.0) {
134
153
  setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
135
154
  return;
136
155
  }
@@ -152,14 +171,13 @@ void BiquadFilterNode::setHighpassCoefficients(float frequency, float Q) {
152
171
  }
153
172
 
154
173
  void BiquadFilterNode::setBandpassCoefficients(float frequency, float Q) {
155
- frequency = std::clamp(frequency, 0.0f, 1.0f);
156
- Q = std::max(0.0f, Q);
157
-
174
+ // Limit frequency to [0, 1] range
158
175
  if (frequency <= 0.0 || frequency >= 1.0) {
159
176
  setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
160
177
  return;
161
178
  }
162
179
 
180
+ // Limit Q to positive values
163
181
  if (Q <= 0.0) {
164
182
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
165
183
  return;
@@ -167,17 +185,16 @@ void BiquadFilterNode::setBandpassCoefficients(float frequency, float Q) {
167
185
 
168
186
  float w0 = PI * frequency;
169
187
  float alpha = std::sin(w0) / (2 * Q);
170
- float k = std::cos(w0);
188
+ float cosW = std::cos(w0);
171
189
 
172
190
  setNormalizedCoefficients(
173
- alpha, 0.0f, -alpha, 1.0f + alpha, -2 * k, 1.0f - alpha);
191
+ alpha, 0.0f, -alpha, 1.0f + alpha, -2 * cosW, 1.0f - alpha);
174
192
  }
175
193
 
176
194
  void BiquadFilterNode::setLowshelfCoefficients(float frequency, float gain) {
177
- frequency = std::clamp(frequency, 0.0f, 1.0f);
178
195
  float A = std::pow(10.0f, gain / 40.0f);
179
196
 
180
- if (frequency == 1.0) {
197
+ if (frequency >= 1.0) {
181
198
  setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
182
199
  return;
183
200
  }
@@ -188,25 +205,23 @@ void BiquadFilterNode::setLowshelfCoefficients(float frequency, float gain) {
188
205
  }
189
206
 
190
207
  float w0 = PI * frequency;
191
- float alpha =
192
- 0.5f * std::sin(w0) * std::sqrt((A + 1 / A) * (1 / 1.0f - 1) + 2);
193
- float k = std::cos(w0);
194
- float k2 = 2.0f * std::sqrt(A) * alpha;
208
+ float alpha = 0.5f * std::sin(w0) * std::sqrt(2.0f);
209
+ float cosW = std::cos(w0);
210
+ float gamma = 2.0f * std::sqrt(A) * alpha;
195
211
 
196
212
  setNormalizedCoefficients(
197
- A * (A + 1 - (A - 1) * k + k2),
198
- 2.0f * A * (A - 1 - (A + 1) * k),
199
- A * (A + 1 - (A - 1) * k - k2),
200
- A + 1 + (A - 1) * k + k2,
201
- -2.0f * (A - 1 + (A + 1) * k),
202
- A + 1 + (A - 1) * k - k2);
213
+ A * (A + 1 - (A - 1) * cosW + gamma),
214
+ 2.0f * A * (A - 1 - (A + 1) * cosW),
215
+ A * (A + 1 - (A - 1) * cosW - gamma),
216
+ A + 1 + (A - 1) * cosW + gamma,
217
+ -2.0f * (A - 1 + (A + 1) * cosW),
218
+ A + 1 + (A - 1) * cosW - gamma);
203
219
  }
204
220
 
205
221
  void BiquadFilterNode::setHighshelfCoefficients(float frequency, float gain) {
206
- frequency = std::clamp(frequency, 0.0f, 1.0f);
207
222
  float A = std::pow(10.0f, gain / 40.0f);
208
223
 
209
- if (frequency == 1.0) {
224
+ if (frequency >= 1.0) {
210
225
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
211
226
  return;
212
227
  }
@@ -217,26 +232,25 @@ void BiquadFilterNode::setHighshelfCoefficients(float frequency, float gain) {
217
232
  }
218
233
 
219
234
  float w0 = PI * frequency;
220
- float alpha =
221
- 0.5f * std::sin(w0) * std::sqrt((A + 1 / A) * (1 / 1.0f - 1) + 2);
222
- float k = std::cos(w0);
223
- float k2 = 2.0f * std::sqrt(A) * alpha;
235
+ // In the original formula: sqrt((A + 1/A) * (1/S - 1) + 2), but we assume
236
+ // the maximum value S = 1, so it becomes 0 + 2 under the square root
237
+ float alpha = 0.5f * std::sin(w0) * std::sqrt(2.0f);
238
+ float cosW = std::cos(w0);
239
+ float gamma = 2.0f * std::sqrt(A) * alpha;
224
240
 
225
241
  setNormalizedCoefficients(
226
- A * (A + 1 + (A - 1) * k + k2),
227
- -2.0f * A * (A - 1 + (A + 1) * k),
228
- A * (A + 1 + (A - 1) * k - k2),
229
- A + 1 - (A - 1) * k + k2,
230
- 2.0f * (A - 1 - (A + 1) * k),
231
- A + 1 - (A - 1) * k - k2);
242
+ A * (A + 1 + (A - 1) * cosW + gamma),
243
+ -2.0f * A * (A - 1 + (A + 1) * cosW),
244
+ A * (A + 1 + (A - 1) * cosW - gamma),
245
+ A + 1 - (A - 1) * cosW + gamma,
246
+ 2.0f * (A - 1 - (A + 1) * cosW),
247
+ A + 1 - (A - 1) * cosW - gamma);
232
248
  }
233
249
 
234
250
  void BiquadFilterNode::setPeakingCoefficients(
235
251
  float frequency,
236
252
  float Q,
237
253
  float gain) {
238
- frequency = std::clamp(frequency, 0.0f, 1.0f);
239
- Q = std::max(0.0f, Q);
240
254
  float A = std::pow(10.0f, gain / 40.0f);
241
255
 
242
256
  if (frequency <= 0.0 || frequency >= 1.0) {
@@ -251,21 +265,18 @@ void BiquadFilterNode::setPeakingCoefficients(
251
265
 
252
266
  float w0 = PI * frequency;
253
267
  float alpha = std::sin(w0) / (2 * Q);
254
- float k = std::cos(w0);
268
+ float cosW = std::cos(w0);
255
269
 
256
270
  setNormalizedCoefficients(
257
271
  1 + alpha * A,
258
- -2 * k,
272
+ -2 * cosW,
259
273
  1 - alpha * A,
260
274
  1 + alpha / A,
261
- -2 * k,
275
+ -2 * cosW,
262
276
  1 - alpha / A);
263
277
  }
264
278
 
265
279
  void BiquadFilterNode::setNotchCoefficients(float frequency, float Q) {
266
- frequency = std::clamp(frequency, 0.0f, 1.0f);
267
- Q = std::max(0.0f, Q);
268
-
269
280
  if (frequency <= 0.0 || frequency >= 1.0) {
270
281
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
271
282
  return;
@@ -278,15 +289,13 @@ void BiquadFilterNode::setNotchCoefficients(float frequency, float Q) {
278
289
 
279
290
  float w0 = PI * frequency;
280
291
  float alpha = std::sin(w0) / (2 * Q);
281
- float k = std::cos(w0);
292
+ float cosW = std::cos(w0);
282
293
 
283
- setNormalizedCoefficients(1.0f, -2 * k, 1.0f, 1 + alpha, -2 * k, 1 - alpha);
294
+ setNormalizedCoefficients(
295
+ 1.0f, -2 * cosW, 1.0f, 1 + alpha, -2 * cosW, 1 - alpha);
284
296
  }
285
297
 
286
298
  void BiquadFilterNode::setAllpassCoefficients(float frequency, float Q) {
287
- frequency = std::clamp(frequency, 0.0f, 1.0f);
288
- Q = std::max(0.0f, Q);
289
-
290
299
  if (frequency <= 0.0 || frequency >= 1.0) {
291
300
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
292
301
  return;
@@ -299,55 +308,47 @@ void BiquadFilterNode::setAllpassCoefficients(float frequency, float Q) {
299
308
 
300
309
  float w0 = PI * frequency;
301
310
  float alpha = std::sin(w0) / (2 * Q);
302
- float k = std::cos(w0);
311
+ float cosW = std::cos(w0);
303
312
 
304
313
  setNormalizedCoefficients(
305
- 1 - alpha, -2 * k, 1 + alpha, 1 + alpha, -2 * k, 1 - alpha);
314
+ 1 - alpha, -2 * cosW, 1 + alpha, 1 + alpha, -2 * cosW, 1 - alpha);
306
315
  }
307
316
 
308
- void BiquadFilterNode::applyFilter() {
309
- double currentTime = context_->getCurrentTime();
310
-
311
- float frequencyParamValue =
312
- frequencyParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime);
313
- float normalizedFrequency =
314
- frequencyParamValue / context_->getNyquistFrequency();
315
-
316
- float detuneValue =
317
- detuneParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime);
317
+ void BiquadFilterNode::updateCoefficientsForFrame(
318
+ float frequency,
319
+ float detune,
320
+ float Q,
321
+ float gain) {
322
+ float normalizedFrequency = frequency / context_->getNyquistFrequency();
318
323
 
319
- if (detuneValue != 0.0) {
320
- normalizedFrequency *= std::pow(2.0f, detuneValue / 1200.0f);
324
+ if (detune != 0.0f) {
325
+ normalizedFrequency *= std::pow(2.0f, detune / 1200.0f);
321
326
  }
322
327
 
323
- auto qparamValue =
324
- QParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime);
325
- auto gainParamValue =
326
- gainParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime);
327
328
  switch (type_) {
328
329
  case BiquadFilterType::LOWPASS:
329
- setLowpassCoefficients(normalizedFrequency, qparamValue);
330
+ setLowpassCoefficients(normalizedFrequency, Q);
330
331
  break;
331
332
  case BiquadFilterType::HIGHPASS:
332
- setHighpassCoefficients(normalizedFrequency, qparamValue);
333
+ setHighpassCoefficients(normalizedFrequency, Q);
333
334
  break;
334
335
  case BiquadFilterType::BANDPASS:
335
- setBandpassCoefficients(normalizedFrequency, qparamValue);
336
+ setBandpassCoefficients(normalizedFrequency, Q);
336
337
  break;
337
338
  case BiquadFilterType::LOWSHELF:
338
- setLowshelfCoefficients(normalizedFrequency, gainParamValue);
339
+ setLowshelfCoefficients(normalizedFrequency, gain);
339
340
  break;
340
341
  case BiquadFilterType::HIGHSHELF:
341
- setHighshelfCoefficients(normalizedFrequency, gainParamValue);
342
+ setHighshelfCoefficients(normalizedFrequency, gain);
342
343
  break;
343
344
  case BiquadFilterType::PEAKING:
344
- setPeakingCoefficients(normalizedFrequency, qparamValue, gainParamValue);
345
+ setPeakingCoefficients(normalizedFrequency, Q, gain);
345
346
  break;
346
347
  case BiquadFilterType::NOTCH:
347
- setNotchCoefficients(normalizedFrequency, qparamValue);
348
+ setNotchCoefficients(normalizedFrequency, Q);
348
349
  break;
349
350
  case BiquadFilterType::ALLPASS:
350
- setAllpassCoefficients(normalizedFrequency, qparamValue);
351
+ setAllpassCoefficients(normalizedFrequency, Q);
351
352
  break;
352
353
  default:
353
354
  break;
@@ -357,24 +358,34 @@ void BiquadFilterNode::applyFilter() {
357
358
  void BiquadFilterNode::processNode(
358
359
  const std::shared_ptr<AudioBus> &processingBus,
359
360
  int framesToProcess) {
360
- resetCoefficients();
361
- applyFilter();
362
-
363
- for (int c = 0; c < processingBus->getNumberOfChannels(); c += 1) {
361
+ double currentTime = context_->getCurrentTime();
362
+ auto frequencyValues =
363
+ frequencyParam_->processARateParam(framesToProcess, currentTime)
364
+ ->getChannel(0)
365
+ ->getData();
366
+ auto detuneValues =
367
+ detuneParam_->processARateParam(framesToProcess, currentTime)
368
+ ->getChannel(0)
369
+ ->getData();
370
+ auto qValues = QParam_->processARateParam(framesToProcess, currentTime)
371
+ ->getChannel(0)
372
+ ->getData();
373
+ auto gainValues = gainParam_->processARateParam(framesToProcess, currentTime)
374
+ ->getChannel(0)
375
+ ->getData();
376
+
377
+ for (int c = 0; c < processingBus->getNumberOfChannels(); c++) {
364
378
  float x1 = x1_;
365
379
  float x2 = x2_;
366
380
  float y1 = y1_;
367
381
  float y2 = y2_;
368
382
 
369
- float b0 = b0_;
370
- float b1 = b1_;
371
- float b2 = b2_;
372
- float a1 = a1_;
373
- float a2 = a2_;
383
+ for (int i = 0; i < framesToProcess; i++) {
384
+ updateCoefficientsForFrame(
385
+ frequencyValues[i], detuneValues[i], qValues[i], gainValues[i]);
374
386
 
375
- for (int i = 0; i < framesToProcess; i += 1) {
376
387
  float input = (*processingBus->getChannel(c))[i];
377
- float output = b0 * input + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
388
+ float output = b0_ * input + b1_ * x1 + b2_ * x2 - a1_ * y1 - a2_ * y2;
378
389
 
379
390
  (*processingBus->getChannel(c))[i] = output;
380
391
 
@@ -383,6 +394,10 @@ void BiquadFilterNode::processNode(
383
394
  y2 = y1;
384
395
  y1 = output;
385
396
  }
397
+ x1_ = x1;
398
+ x2_ = x2;
399
+ y1_ = y1;
400
+ y2_ = y2;
386
401
  }
387
402
  }
388
403
 
@@ -33,7 +33,9 @@ class BiquadFilterNode : public AudioNode {
33
33
  int length);
34
34
 
35
35
  protected:
36
- void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
36
+ void processNode(
37
+ const std::shared_ptr<AudioBus> &processingBus,
38
+ int framesToProcess) override;
37
39
 
38
40
  private:
39
41
  std::shared_ptr<AudioParam> frequencyParam_;
@@ -119,7 +121,11 @@ class BiquadFilterNode : public AudioNode {
119
121
  void setPeakingCoefficients(float frequency, float Q, float gain);
120
122
  void setNotchCoefficients(float frequency, float Q);
121
123
  void setAllpassCoefficients(float frequency, float Q);
122
- void applyFilter();
124
+ void updateCoefficientsForFrame(
125
+ float frequency,
126
+ float detune,
127
+ float q,
128
+ float gain);
123
129
  };
124
130
 
125
131
  } // namespace audioapi
@@ -10,7 +10,7 @@ namespace audioapi {
10
10
 
11
11
  StereoPannerNode::StereoPannerNode(BaseAudioContext *context)
12
12
  : AudioNode(context) {
13
- channelCountMode_ = ChannelCountMode::EXPLICIT;
13
+ channelCountMode_ = ChannelCountMode::CLAMPED_MAX;
14
14
  panParam_ = std::make_shared<AudioParam>(0.0, -1.0f, 1.0f, context);
15
15
  isInitialized_ = true;
16
16
  }
@@ -31,26 +31,42 @@ void StereoPannerNode::processNode(
31
31
  ->getChannel(0)
32
32
  ->getData();
33
33
 
34
- for (int i = 0; i < framesToProcess; i += 1) {
35
- auto pan = panParamValues[i];
34
+ // Input is mono
35
+ if (processingBus->getNumberOfChannels() == 1) {
36
+ for (int i = 0; i < framesToProcess; i++) {
37
+ auto pan = std::clamp(panParamValues[i], -1.0f, 1.0f);
38
+ auto x = (pan + 1) / 2;
36
39
 
37
- auto x = (pan <= 0 ? pan + 1 : pan);
40
+ auto gainL = static_cast<float>(cos(x * PI / 2));
41
+ auto gainR = static_cast<float>(sin(x * PI / 2));
38
42
 
39
- auto gainL = static_cast<float>(cos(x * PI / 2));
40
- auto gainR = static_cast<float>(sin(x * PI / 2));
43
+ float input = (*left)[i];
41
44
 
42
- float inputL = (*left)[i];
43
- float inputR = (*right)[i];
44
-
45
- if (pan <= 0) {
46
- (*left)[i] = inputL + inputR * gainL;
47
- (*right)[i] = inputR * gainR;
48
- } else {
49
- (*left)[i] = inputL * gainL;
50
- (*right)[i] = inputR + inputL * gainR;
45
+ (*left)[i] = input * gainL;
46
+ (*right)[i] = input * gainR;
47
+ time += deltaTime;
51
48
  }
49
+ } else { // Input is stereo
50
+ for (int i = 0; i < framesToProcess; i++) {
51
+ auto pan = std::clamp(panParamValues[i], -1.0f, 1.0f);
52
+ auto x = (pan <= 0 ? pan + 1 : pan);
53
+
54
+ auto gainL = static_cast<float>(cos(x * PI / 2));
55
+ auto gainR = static_cast<float>(sin(x * PI / 2));
56
+
57
+ float inputL = (*left)[i];
58
+ float inputR = (*right)[i];
52
59
 
53
- time += deltaTime;
60
+ if (pan <= 0) {
61
+ (*left)[i] = inputL + inputR * gainL;
62
+ (*right)[i] = inputR * gainR;
63
+ } else {
64
+ (*left)[i] = inputL * gainL;
65
+ (*right)[i] = inputR + inputL * gainR;
66
+ }
67
+
68
+ time += deltaTime;
69
+ }
54
70
  }
55
71
  }
56
72
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-api",
3
- "version": "0.7.0-nightly-fba4835-20250722",
3
+ "version": "0.7.0-nightly-4fc09d1-20250724",
4
4
  "description": "react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification",
5
5
  "bin": {
6
6
  "setup-rn-audio-api-web": "./scripts/setup-rn-audio-api-web.js"