react-native-audio-api 0.10.0-nightly-2d11b56-20251021 → 0.10.0-nightly-325e87a-20251023

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.
Files changed (48) hide show
  1. package/RNAudioAPI.podspec +3 -0
  2. package/android/build.gradle +26 -2
  3. package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +5 -0
  4. package/android/src/main/cpp/audioapi/android/AudioAPIModule.h +1 -0
  5. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +4 -2
  6. package/android/src/main/java/com/swmansion/audioapi/AudioAPIModule.kt +29 -1
  7. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +20 -0
  8. package/common/cpp/audioapi/HostObjects/effects/BiquadFilterNodeHostObject.cpp +1 -1
  9. package/common/cpp/audioapi/core/AudioParam.cpp +2 -2
  10. package/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +69 -32
  11. package/common/cpp/audioapi/core/effects/BiquadFilterNode.h +37 -1
  12. package/common/cpp/audioapi/core/utils/Constants.h +2 -1
  13. package/common/cpp/audioapi/core/utils/worklets/SafeIncludes.h +0 -10
  14. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +2 -3
  15. package/common/cpp/test/CMakeLists.txt +3 -0
  16. package/common/cpp/test/src/biquad/BiquadFilterChromium.cpp +389 -0
  17. package/common/cpp/test/src/biquad/BiquadFilterChromium.h +64 -0
  18. package/common/cpp/test/src/biquad/BiquadFilterTest.cpp +284 -0
  19. package/common/cpp/test/src/biquad/BiquadFilterTest.h +40 -0
  20. package/ios/audioapi/ios/AudioAPIModule.h +2 -1
  21. package/ios/audioapi/ios/AudioAPIModule.mm +2 -0
  22. package/ios/audioapi/ios/core/IOSAudioRecorder.h +1 -2
  23. package/ios/audioapi/ios/system/AudioSessionManager.mm +9 -0
  24. package/lib/commonjs/core/AudioContext.js +1 -1
  25. package/lib/commonjs/core/AudioContext.js.map +1 -1
  26. package/lib/commonjs/core/BaseAudioContext.js +16 -25
  27. package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
  28. package/lib/commonjs/core/OfflineAudioContext.js +1 -1
  29. package/lib/commonjs/core/OfflineAudioContext.js.map +1 -1
  30. package/lib/commonjs/utils/index.js +19 -4
  31. package/lib/commonjs/utils/index.js.map +1 -1
  32. package/lib/module/core/AudioContext.js +2 -2
  33. package/lib/module/core/AudioContext.js.map +1 -1
  34. package/lib/module/core/BaseAudioContext.js +17 -26
  35. package/lib/module/core/BaseAudioContext.js.map +1 -1
  36. package/lib/module/core/OfflineAudioContext.js +2 -2
  37. package/lib/module/core/OfflineAudioContext.js.map +1 -1
  38. package/lib/module/utils/index.js +15 -1
  39. package/lib/module/utils/index.js.map +1 -1
  40. package/lib/typescript/core/AudioContext.d.ts.map +1 -1
  41. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  42. package/lib/typescript/utils/index.d.ts +4 -1
  43. package/lib/typescript/utils/index.d.ts.map +1 -1
  44. package/package.json +1 -1
  45. package/src/core/AudioContext.ts +3 -2
  46. package/src/core/BaseAudioContext.ts +44 -60
  47. package/src/core/OfflineAudioContext.ts +2 -2
  48. package/src/utils/index.ts +22 -1
@@ -1,4 +1,5 @@
1
1
  require "json"
2
+ require_relative './scripts/rnaa_utils'
2
3
 
3
4
  package_json = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
5
 
@@ -8,6 +9,8 @@ folly_flags = "-DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-
8
9
  fabric_flags = $new_arch_enabled ? '-DRCT_NEW_ARCH_ENABLED' : ''
9
10
  version_flag = "-DAUDIOAPI_VERSION=#{package_json['version']}"
10
11
 
12
+ worklets_preprocessor_flag = check_if_worklets_enabled() ? '-DRN_AUDIO_API_ENABLE_WORKLETS=1' : ''
13
+
11
14
  Pod::Spec.new do |s|
12
15
  s.name = "RNAudioAPI"
13
16
  s.version = package_json["version"]
@@ -74,6 +74,30 @@ def resolveReactNativeWorkletsDirectory() {
74
74
  return null;
75
75
  }
76
76
 
77
+ def validateWorkletsVersion() {
78
+ def validationScript = file("${projectDir}/../scripts/validate-worklets-version.js")
79
+ if (!validationScript.exists()) {
80
+ logger.error("[AudioAPI] Worklets validation script not found at ${validationScript.absolutePath}")
81
+ return false
82
+ }
83
+
84
+ try {
85
+ def process = ["node", validationScript.absolutePath].execute()
86
+ process.waitForProcessOutput(System.out, System.err)
87
+ def exitCode = process.exitValue()
88
+
89
+ if (exitCode == 0) {
90
+ return true
91
+ } else {
92
+ logger.warn("[AudioAPI] Worklets version validation failed")
93
+ return false
94
+ }
95
+ } catch (Exception e) {
96
+ logger.error("[AudioAPI] Failed to validate worklets version: ${e.message}")
97
+ return false
98
+ }
99
+ }
100
+
77
101
  def toPlatformFileString(String path) {
78
102
  if (Os.isFamily(Os.FAMILY_WINDOWS)) {
79
103
  path = path.replace(File.separatorChar, '/' as char)
@@ -92,7 +116,7 @@ static def supportsNamespace() {
92
116
 
93
117
  def reactNativeRootDir = resolveReactNativeDirectory()
94
118
  def reactNativeWorkletsRootDir = resolveReactNativeWorkletsDirectory()
95
- def isWorkletsAvailable = reactNativeWorkletsRootDir != null
119
+ def isWorkletsAvailable = reactNativeWorkletsRootDir != null && validateWorkletsVersion()
96
120
 
97
121
  def reactProperties = new Properties()
98
122
  file("$reactNativeRootDir/ReactAndroid/gradle.properties").withInputStream { reactProperties.load(it) }
@@ -123,7 +147,7 @@ android {
123
147
  defaultConfig {
124
148
  minSdkVersion getExtOrIntegerDefault("minSdkVersion")
125
149
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
126
-
150
+
127
151
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
128
152
  buildConfigField "boolean", "RN_AUDIO_API_ENABLE_WORKLETS", "${isWorkletsAvailable}"
129
153
 
@@ -51,6 +51,7 @@ void AudioAPIModule::registerNatives() {
51
51
  makeNativeMethod(
52
52
  "invokeHandlerWithEventNameAndEventBody",
53
53
  AudioAPIModule::invokeHandlerWithEventNameAndEventBody),
54
+ makeNativeMethod("closeAllContexts", AudioAPIModule::closeAllContexts),
54
55
  });
55
56
  }
56
57
 
@@ -101,4 +102,8 @@ void AudioAPIModule::invokeHandlerWithEventNameAndEventBody(
101
102
  eventName->toStdString(), body);
102
103
  }
103
104
  }
105
+
106
+ void AudioAPIModule::closeAllContexts() {
107
+ AudioAPIModuleInstaller::closeAllContexts();
108
+ }
104
109
  } // namespace audioapi
@@ -34,6 +34,7 @@ class AudioAPIModule : public jni::HybridClass<AudioAPIModule> {
34
34
 
35
35
  void injectJSIBindings();
36
36
  void invokeHandlerWithEventNameAndEventBody(jni::alias_ref<jni::JString> eventName, jni::alias_ref<jni::JMap<jstring, jobject>> eventBody);
37
+ void closeAllContexts();
37
38
 
38
39
  private:
39
40
  friend HybridBase;
@@ -4,6 +4,7 @@
4
4
  #include <audioapi/core/utils/Constants.h>
5
5
  #include <audioapi/utils/AudioArray.h>
6
6
  #include <audioapi/utils/AudioBus.h>
7
+ #include <jni.h>
7
8
 
8
9
  namespace audioapi {
9
10
 
@@ -49,7 +50,8 @@ bool AudioPlayer::openAudioStream() {
49
50
 
50
51
  bool AudioPlayer::start() {
51
52
  if (mStream_) {
52
- nativeAudioPlayer_->start();
53
+ jni::ThreadScope::WithClassLoader(
54
+ [this]() { nativeAudioPlayer_->start(); });
53
55
  auto result = mStream_->requestStart();
54
56
  return result == oboe::Result::OK;
55
57
  }
@@ -59,7 +61,7 @@ bool AudioPlayer::start() {
59
61
 
60
62
  void AudioPlayer::stop() {
61
63
  if (mStream_) {
62
- nativeAudioPlayer_->stop();
64
+ jni::ThreadScope::WithClassLoader([this]() { nativeAudioPlayer_->stop(); });
63
65
  mStream_->requestStop();
64
66
  }
65
67
  }
@@ -1,6 +1,8 @@
1
1
  package com.swmansion.audioapi
2
2
 
3
3
  import com.facebook.jni.HybridData
4
+ import com.facebook.react.bridge.LifecycleEventListener
5
+ import com.facebook.react.bridge.NativeModule
4
6
  import com.facebook.react.bridge.Promise
5
7
  import com.facebook.react.bridge.ReactApplicationContext
6
8
  import com.facebook.react.bridge.ReadableArray
@@ -16,7 +18,8 @@ import java.lang.ref.WeakReference
16
18
  @ReactModule(name = AudioAPIModule.NAME)
17
19
  class AudioAPIModule(
18
20
  reactContext: ReactApplicationContext,
19
- ) : NativeAudioAPIModuleSpec(reactContext) {
21
+ ) : NativeAudioAPIModuleSpec(reactContext),
22
+ LifecycleEventListener {
20
23
  companion object {
21
24
  const val NAME = NativeAudioAPIModuleSpec.NAME
22
25
  }
@@ -24,6 +27,7 @@ class AudioAPIModule(
24
27
  val reactContext: WeakReference<ReactApplicationContext> = WeakReference(reactContext)
25
28
 
26
29
  private val mHybridData: HybridData
30
+ private var reanimatedModule: NativeModule? = null
27
31
 
28
32
  external fun initHybrid(
29
33
  workletsModule: Any?,
@@ -38,6 +42,8 @@ class AudioAPIModule(
38
42
  eventBody: Map<String, Any>,
39
43
  )
40
44
 
45
+ private external fun closeAllContexts()
46
+
41
47
  init {
42
48
  try {
43
49
  System.loadLibrary("react-native-audio-api")
@@ -47,6 +53,8 @@ class AudioAPIModule(
47
53
  if (BuildConfig.RN_AUDIO_API_ENABLE_WORKLETS) {
48
54
  try {
49
55
  workletsModule = reactContext.getNativeModule("WorkletsModule")
56
+ reanimatedModule = reactContext.getNativeModule("ReanimatedModule")
57
+ reanimatedModule
50
58
  } catch (ex: Exception) {
51
59
  throw RuntimeException("WorkletsModule not found - make sure react-native-worklets is properly installed")
52
60
  }
@@ -64,6 +72,26 @@ class AudioAPIModule(
64
72
  return true
65
73
  }
66
74
 
75
+ override fun onHostResume() {
76
+ // do nothing
77
+ }
78
+
79
+ override fun onHostPause() {
80
+ closeAllContexts()
81
+ }
82
+
83
+ override fun onHostDestroy() {
84
+ // do nothing
85
+ }
86
+
87
+ override fun initialize() {
88
+ reactContext.get()?.addLifecycleEventListener(this)
89
+ }
90
+
91
+ override fun invalidate() {
92
+ // think about cleaning up resources, singletons etc.
93
+ }
94
+
67
95
  override fun getDevicePreferredSampleRate(): Double = MediaSessionManager.getDevicePreferredSampleRate()
68
96
 
69
97
  override fun setAudioSessionActivity(
@@ -16,12 +16,16 @@
16
16
  #include <audioapi/core/utils/worklets/SafeIncludes.h>
17
17
 
18
18
  #include <memory>
19
+ #include <vector>
19
20
 
20
21
  namespace audioapi {
21
22
 
22
23
  using namespace facebook;
23
24
 
24
25
  class AudioAPIModuleInstaller {
26
+ private:
27
+ inline static std::vector<std::weak_ptr<AudioContext>> contexts_ = {};
28
+
25
29
  public:
26
30
  static void injectJSIBindings(
27
31
  jsi::Runtime *jsiRuntime,
@@ -61,6 +65,19 @@ class AudioAPIModuleInstaller {
61
65
  *jsiRuntime, audioEventHandlerRegistryHostObject));
62
66
  }
63
67
 
68
+ static void closeAllContexts() {
69
+ for (auto it = contexts_.begin(); it != contexts_.end(); ++it) {
70
+ auto weakContext = *it;
71
+
72
+ if (auto context = weakContext.lock()) {
73
+ context->close();
74
+ }
75
+
76
+ it = contexts_.erase(it);
77
+ --it;
78
+ }
79
+ }
80
+
64
81
  private:
65
82
  static jsi::Function getCreateAudioContextFunction(
66
83
  jsi::Runtime *jsiRuntime,
@@ -95,6 +112,8 @@ class AudioAPIModuleInstaller {
95
112
  initSuspended,
96
113
  audioEventHandlerRegistry,
97
114
  runtimeRegistry);
115
+ AudioAPIModuleInstaller::contexts_.push_back(audioContext);
116
+
98
117
  auto audioContextHostObject =
99
118
  std::make_shared<AudioContextHostObject>(
100
119
  audioContext, &runtime, jsCallInvoker);
@@ -138,6 +157,7 @@ class AudioAPIModuleInstaller {
138
157
  sampleRate,
139
158
  audioEventHandlerRegistry,
140
159
  runtimeRegistry);
160
+
141
161
  auto audioContextHostObject =
142
162
  std::make_shared<OfflineAudioContextHostObject>(
143
163
  offlineAudioContext, &runtime, jsCallInvoker);
@@ -67,7 +67,7 @@ JSI_HOST_FUNCTION_IMPL(BiquadFilterNodeHostObject, getFrequencyResponse) {
67
67
  .getArrayBuffer(runtime);
68
68
  auto frequencyArray =
69
69
  reinterpret_cast<float *>(arrayBufferFrequency.data(runtime));
70
- auto length = static_cast<int>(arrayBufferFrequency.size(runtime));
70
+ auto length = static_cast<size_t>(arrayBufferFrequency.size(runtime));
71
71
 
72
72
  auto arrayBufferMag = args[1]
73
73
  .getObject(runtime)
@@ -56,7 +56,7 @@ float AudioParam::getValueAtTime(double time) {
56
56
  void AudioParam::setValueAtTime(float value, double startTime) {
57
57
  auto event = [value, startTime](AudioParam &param) {
58
58
  // Ignore events scheduled before the end of existing automation
59
- if (startTime <= param.getQueueEndTime()) {
59
+ if (startTime < param.getQueueEndTime()) {
60
60
  return;
61
61
  }
62
62
 
@@ -87,7 +87,7 @@ void AudioParam::setValueAtTime(float value, double startTime) {
87
87
  void AudioParam::linearRampToValueAtTime(float value, double endTime) {
88
88
  auto event = [value, endTime](AudioParam &param) {
89
89
  // Ignore events scheduled before the end of existing automation
90
- if (endTime <= param.getQueueEndTime()) {
90
+ if (endTime < param.getQueueEndTime()) {
91
91
  return;
92
92
  }
93
93
 
@@ -1,3 +1,31 @@
1
+ /*
2
+ * Copyright (C) 2010 Google Inc. All rights reserved.
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without
5
+ * modification, are permitted provided that the following conditions
6
+ * are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright
9
+ * notice, this list of conditions and the following disclaimer.
10
+ * 2. Redistributions in binary form must reproduce the above copyright
11
+ * notice, this list of conditions and the following disclaimer in the
12
+ * documentation and/or other materials provided with the distribution.
13
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14
+ * its contributors may be used to endorse or promote products derived
15
+ * from this software without specific prior written permission.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
1
29
  #include <audioapi/core/BaseAudioContext.h>
2
30
  #include <audioapi/core/effects/BiquadFilterNode.h>
3
31
  #include <audioapi/utils/AudioArray.h>
@@ -13,14 +41,14 @@ BiquadFilterNode::BiquadFilterNode(BaseAudioContext *context)
13
41
  frequencyParam_ = std::make_shared<AudioParam>(
14
42
  350.0, 0.0f, context->getNyquistFrequency(), context);
15
43
  detuneParam_ = std::make_shared<AudioParam>(
16
- 0.0,
44
+ 0.0f,
17
45
  -1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT,
18
46
  1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT,
19
47
  context);
20
48
  QParam_ = std::make_shared<AudioParam>(
21
- 1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context);
49
+ 1.0f, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context);
22
50
  gainParam_ = std::make_shared<AudioParam>(
23
- 0.0,
51
+ 0.0f,
24
52
  MOST_NEGATIVE_SINGLE_FLOAT,
25
53
  40 * LOG10_MOST_POSITIVE_SINGLE_FLOAT,
26
54
  context);
@@ -76,27 +104,35 @@ void BiquadFilterNode::getFrequencyResponse(
76
104
  const float *frequencyArray,
77
105
  float *magResponseOutput,
78
106
  float *phaseResponseOutput,
79
- const int length) {
107
+ const size_t length) {
108
+ #ifndef AUDIO_API_TEST_SUITE
80
109
  applyFilter();
110
+ #endif
81
111
 
82
- // Local copies for micro-optimization
83
- float b0 = b0_;
84
- float b1 = b1_;
85
- float b2 = b2_;
86
- float a1 = a1_;
87
- float a2 = a2_;
112
+ // Use double precision for later calculations
113
+ double b0 = static_cast<double>(b0_);
114
+ double b1 = static_cast<double>(b1_);
115
+ double b2 = static_cast<double>(b2_);
116
+ double a1 = static_cast<double>(a1_);
117
+ double a2 = static_cast<double>(a2_);
118
+
119
+ float nyquist = context_->getNyquistFrequency();
88
120
 
89
121
  for (size_t i = 0; i < length; i++) {
90
- if (frequencyArray[i] < 0.0 || frequencyArray[i] > 1.0) {
122
+ // Convert from frequency in Hz to normalized frequency [0, 1]
123
+ float normalizedFreq = frequencyArray[i] / nyquist;
124
+
125
+ if (normalizedFreq < 0.0f || normalizedFreq > 1.0f) {
126
+ // Out-of-bounds frequencies should return NaN.
91
127
  magResponseOutput[i] = std::nanf("");
92
128
  phaseResponseOutput[i] = std::nanf("");
93
129
  continue;
94
130
  }
95
131
 
96
- auto omega = -PI * frequencyArray[i] / context_->getNyquistFrequency();
97
- auto z = std::complex<float>(cos(omega), sin(omega));
132
+ double omega = -PI * normalizedFreq;
133
+ auto z = std::complex<double>(std::cos(omega), std::sin(omega));
98
134
  auto response = (b0 + (b1 + b2 * z) * z) /
99
- (std::complex<float>(1, 0) + (a1 + a2 * z) * z);
135
+ (std::complex<double>(1, 0) + (a1 + a2 * z) * z);
100
136
  magResponseOutput[i] = static_cast<float>(std::abs(response));
101
137
  phaseResponseOutput[i] =
102
138
  static_cast<float>(atan2(imag(response), real(response)));
@@ -120,17 +156,16 @@ void BiquadFilterNode::setNormalizedCoefficients(
120
156
 
121
157
  void BiquadFilterNode::setLowpassCoefficients(float frequency, float Q) {
122
158
  // Limit frequency to [0, 1] range
123
- if (frequency >= 1.0) {
159
+ if (frequency >= 1.0f) {
124
160
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
125
161
  return;
126
162
  }
127
163
 
128
- if (frequency <= 0.0) {
164
+ if (frequency <= 0.0f) {
129
165
  setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
130
166
  return;
131
167
  }
132
168
 
133
- Q = std::max(0.0f, Q);
134
169
  float g = std::pow(10.0f, 0.05f * Q);
135
170
 
136
171
  float theta = PI * frequency;
@@ -143,16 +178,15 @@ void BiquadFilterNode::setLowpassCoefficients(float frequency, float Q) {
143
178
  }
144
179
 
145
180
  void BiquadFilterNode::setHighpassCoefficients(float frequency, float Q) {
146
- if (frequency >= 1.0) {
181
+ if (frequency >= 1.0f) {
147
182
  setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
148
183
  return;
149
184
  }
150
- if (frequency <= 0.0) {
185
+ if (frequency <= 0.0f) {
151
186
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
152
187
  return;
153
188
  }
154
189
 
155
- Q = std::max(0.0f, Q);
156
190
  float g = std::pow(10.0f, 0.05f * Q);
157
191
 
158
192
  float theta = PI * frequency;
@@ -166,13 +200,13 @@ void BiquadFilterNode::setHighpassCoefficients(float frequency, float Q) {
166
200
 
167
201
  void BiquadFilterNode::setBandpassCoefficients(float frequency, float Q) {
168
202
  // Limit frequency to [0, 1] range
169
- if (frequency <= 0.0 || frequency >= 1.0) {
203
+ if (frequency <= 0.0f || frequency >= 1.0f) {
170
204
  setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
171
205
  return;
172
206
  }
173
207
 
174
208
  // Limit Q to positive values
175
- if (Q <= 0.0) {
209
+ if (Q <= 0.0f) {
176
210
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
177
211
  return;
178
212
  }
@@ -188,12 +222,12 @@ void BiquadFilterNode::setBandpassCoefficients(float frequency, float Q) {
188
222
  void BiquadFilterNode::setLowshelfCoefficients(float frequency, float gain) {
189
223
  float A = std::pow(10.0f, gain / 40.0f);
190
224
 
191
- if (frequency >= 1.0) {
225
+ if (frequency >= 1.0f) {
192
226
  setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
193
227
  return;
194
228
  }
195
229
 
196
- if (frequency <= 0.0) {
230
+ if (frequency <= 0.0f) {
197
231
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
198
232
  return;
199
233
  }
@@ -215,12 +249,12 @@ void BiquadFilterNode::setLowshelfCoefficients(float frequency, float gain) {
215
249
  void BiquadFilterNode::setHighshelfCoefficients(float frequency, float gain) {
216
250
  float A = std::pow(10.0f, gain / 40.0f);
217
251
 
218
- if (frequency >= 1.0) {
252
+ if (frequency >= 1.0f) {
219
253
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
220
254
  return;
221
255
  }
222
256
 
223
- if (frequency <= 0.0) {
257
+ if (frequency <= 0.0f) {
224
258
  setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
225
259
  return;
226
260
  }
@@ -247,12 +281,12 @@ void BiquadFilterNode::setPeakingCoefficients(
247
281
  float gain) {
248
282
  float A = std::pow(10.0f, gain / 40.0f);
249
283
 
250
- if (frequency <= 0.0 || frequency >= 1.0) {
284
+ if (frequency <= 0.0f || frequency >= 1.0f) {
251
285
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
252
286
  return;
253
287
  }
254
288
 
255
- if (Q <= 0.0) {
289
+ if (Q <= 0.0f) {
256
290
  setNormalizedCoefficients(A * A, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
257
291
  return;
258
292
  }
@@ -271,12 +305,12 @@ void BiquadFilterNode::setPeakingCoefficients(
271
305
  }
272
306
 
273
307
  void BiquadFilterNode::setNotchCoefficients(float frequency, float Q) {
274
- if (frequency <= 0.0 || frequency >= 1.0) {
308
+ if (frequency <= 0.0f || frequency >= 1.0f) {
275
309
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
276
310
  return;
277
311
  }
278
312
 
279
- if (Q <= 0.0) {
313
+ if (Q <= 0.0f) {
280
314
  setNormalizedCoefficients(0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
281
315
  return;
282
316
  }
@@ -290,12 +324,12 @@ void BiquadFilterNode::setNotchCoefficients(float frequency, float Q) {
290
324
  }
291
325
 
292
326
  void BiquadFilterNode::setAllpassCoefficients(float frequency, float Q) {
293
- if (frequency <= 0.0 || frequency >= 1.0) {
327
+ if (frequency <= 0.0f || frequency >= 1.0f) {
294
328
  setNormalizedCoefficients(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
295
329
  return;
296
330
  }
297
331
 
298
- if (Q <= 0.0) {
332
+ if (Q <= 0.0f) {
299
333
  setNormalizedCoefficients(-1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f);
300
334
  return;
301
335
  }
@@ -318,6 +352,9 @@ void BiquadFilterNode::applyFilter() {
318
352
  auto Q = QParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime);
319
353
  auto gain = gainParam_->processKRateParam(RENDER_QUANTUM_SIZE, currentTime);
320
354
 
355
+ // NyquistFrequency is half of the sample rate.
356
+ // Normalized frequency is therefore:
357
+ // frequency / (sampleRate / 2) = (2 * frequency) / sampleRate
321
358
  float normalizedFrequency = frequency / context_->getNyquistFrequency();
322
359
  if (detune != 0.0f) {
323
360
  normalizedFrequency *= std::pow(2.0f, detune / 1200.0f);
@@ -1,8 +1,39 @@
1
+ /*
2
+ * Copyright (C) 2010 Google Inc. All rights reserved.
3
+ *
4
+ * Redistribution and use in source and binary forms, with or without
5
+ * modification, are permitted provided that the following conditions
6
+ * are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright
9
+ * notice, this list of conditions and the following disclaimer.
10
+ * 2. Redistributions in binary form must reproduce the above copyright
11
+ * notice, this list of conditions and the following disclaimer in the
12
+ * documentation and/or other materials provided with the distribution.
13
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14
+ * its contributors may be used to endorse or promote products derived
15
+ * from this software without specific prior written permission.
16
+ *
17
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+ */
28
+
1
29
  #pragma once
2
30
 
3
31
  #include <audioapi/core/AudioNode.h>
4
32
  #include <audioapi/core/AudioParam.h>
5
33
  #include <audioapi/core/types/BiquadFilterType.h>
34
+ #ifdef AUDIO_API_TEST_SUITE
35
+ #include <gtest/gtest_prod.h>
36
+ #endif
6
37
 
7
38
  #include <algorithm>
8
39
  #include <cmath>
@@ -17,6 +48,11 @@ namespace audioapi {
17
48
  class AudioBus;
18
49
 
19
50
  class BiquadFilterNode : public AudioNode {
51
+ #ifdef AUDIO_API_TEST_SUITE
52
+ friend class BiquadFilterTest;
53
+ FRIEND_TEST(BiquadFilterTest, GetFrequencyResponse);
54
+ #endif
55
+
20
56
  public:
21
57
  explicit BiquadFilterNode(BaseAudioContext *context);
22
58
 
@@ -30,7 +66,7 @@ class BiquadFilterNode : public AudioNode {
30
66
  const float *frequencyArray,
31
67
  float *magResponseOutput,
32
68
  float *phaseResponseOutput,
33
- int length);
69
+ size_t length);
34
70
 
35
71
  protected:
36
72
  std::shared_ptr<AudioBus> processNode(
@@ -1,5 +1,6 @@
1
1
  #pragma once
2
2
 
3
+ #include <numbers>
3
4
  #include <cmath>
4
5
  #include <limits>
5
6
 
@@ -19,7 +20,7 @@ static constexpr float MOST_POSITIVE_SINGLE_FLOAT = static_cast<float>(std::nume
19
20
  static constexpr float MOST_NEGATIVE_SINGLE_FLOAT = static_cast<float>(std::numeric_limits<float>::lowest());
20
21
  static float LOG2_MOST_POSITIVE_SINGLE_FLOAT = std::log2(MOST_POSITIVE_SINGLE_FLOAT);
21
22
  static float LOG10_MOST_POSITIVE_SINGLE_FLOAT = std::log10(MOST_POSITIVE_SINGLE_FLOAT);
22
- static constexpr float PI = static_cast<float>(M_PI);
23
+ static constexpr float PI = std::numbers::pi_v<float>;
23
24
 
24
25
  // buffer sizes
25
26
  static constexpr size_t PROMISE_VENDOR_THREAD_POOL_WORKER_COUNT = 4;
@@ -5,16 +5,6 @@
5
5
  #include <string>
6
6
  #include <memory>
7
7
 
8
- #ifdef __APPLE__
9
- /// We cannot make any conditional logic inside podspec but it should automatically compile those files
10
- /// they should be accessible if someone has react-native-worklets in node_modules
11
- #if __has_include(<worklets/WorkletRuntime/WorkletRuntime.h>)
12
- #define RN_AUDIO_API_ENABLE_WORKLETS 1
13
- #else
14
- #define RN_AUDIO_API_ENABLE_WORKLETS 0
15
- #endif
16
- #endif
17
-
18
8
  #ifndef RN_AUDIO_API_TEST
19
9
  #define RN_AUDIO_API_TEST 0
20
10
  #endif
@@ -277,8 +277,7 @@ decodeWithMemoryBlock(const void *data, size_t size, int sample_rate) {
277
277
  MemoryIOContext io_ctx{static_cast<const uint8_t *>(data), size, 0};
278
278
 
279
279
  constexpr size_t buffer_size = 4096;
280
- auto io_buffer = std::unique_ptr<uint8_t, decltype(&av_free)>(
281
- static_cast<uint8_t *>(av_malloc(buffer_size)), &av_free);
280
+ uint8_t *io_buffer = static_cast<uint8_t *>(av_malloc(buffer_size));
282
281
  if (io_buffer == nullptr) {
283
282
  return nullptr;
284
283
  }
@@ -286,7 +285,7 @@ decodeWithMemoryBlock(const void *data, size_t size, int sample_rate) {
286
285
  auto avio_ctx =
287
286
  std::unique_ptr<AVIOContext, std::function<void(AVIOContext *)>>(
288
287
  avio_alloc_context(
289
- io_buffer.get(),
288
+ io_buffer,
290
289
  buffer_size,
291
290
  0,
292
291
  &io_ctx,
@@ -12,6 +12,7 @@ FetchContent_Declare(
12
12
  googletest
13
13
  URL https://github.com/google/googletest/archive/3983f67e32fb3e9294487b9d4f9586efa6e5d088.zip
14
14
  )
15
+
15
16
  # For Windows: Prevent overriding the parent project's compiler/linker settings
16
17
  set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
17
18
  FetchContent_MakeAvailable(googletest)
@@ -57,6 +58,8 @@ target_include_directories(rnaudioapi PUBLIC
57
58
  ${JSI_DIR}
58
59
  "${REACT_NATIVE_DIR}/ReactCommon"
59
60
  "${REACT_NATIVE_DIR}/ReactCommon/callinvoker"
61
+ ${gtest_SOURCE_DIR}/include
62
+ ${gmock_SOURCE_DIR}/include
60
63
  )
61
64
 
62
65
  target_include_directories(rnaudioapi_libs PUBLIC