react-native-audio-api 0.11.0-nightly-568a154-20251222 → 0.11.0-nightly-94b7f30-20251224

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 (207) hide show
  1. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +1 -1
  2. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +11 -3
  3. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.cpp +47 -79
  4. package/android/src/main/cpp/audioapi/android/core/utils/ffmpegBackend/FFmpegFileWriter.h +3 -2
  5. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +2 -0
  6. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.cpp +9 -1
  7. package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +1 -0
  8. package/common/cpp/audioapi/HostObjects/effects/DelayNodeHostObject.cpp +6 -2
  9. package/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.cpp +72 -0
  10. package/common/cpp/audioapi/HostObjects/effects/WaveShaperNodeHostObject.h +23 -0
  11. package/common/cpp/audioapi/core/AudioContext.cpp +15 -13
  12. package/common/cpp/audioapi/core/AudioContext.h +2 -1
  13. package/common/cpp/audioapi/core/AudioNode.cpp +39 -24
  14. package/common/cpp/audioapi/core/AudioNode.h +3 -3
  15. package/common/cpp/audioapi/core/AudioParam.cpp +9 -6
  16. package/common/cpp/audioapi/core/AudioParam.h +2 -2
  17. package/common/cpp/audioapi/core/BaseAudioContext.cpp +32 -21
  18. package/common/cpp/audioapi/core/BaseAudioContext.h +5 -1
  19. package/common/cpp/audioapi/core/analysis/AnalyserNode.cpp +8 -11
  20. package/common/cpp/audioapi/core/analysis/AnalyserNode.h +1 -1
  21. package/common/cpp/audioapi/core/destinations/AudioDestinationNode.cpp +9 -3
  22. package/common/cpp/audioapi/core/destinations/AudioDestinationNode.h +1 -1
  23. package/common/cpp/audioapi/core/effects/BiquadFilterNode.cpp +18 -9
  24. package/common/cpp/audioapi/core/effects/BiquadFilterNode.h +1 -1
  25. package/common/cpp/audioapi/core/effects/ConvolverNode.cpp +3 -3
  26. package/common/cpp/audioapi/core/effects/ConvolverNode.h +1 -1
  27. package/common/cpp/audioapi/core/effects/DelayNode.cpp +20 -11
  28. package/common/cpp/audioapi/core/effects/DelayNode.h +1 -1
  29. package/common/cpp/audioapi/core/effects/GainNode.cpp +12 -4
  30. package/common/cpp/audioapi/core/effects/GainNode.h +1 -1
  31. package/common/cpp/audioapi/core/effects/IIRFilterNode.cpp +6 -3
  32. package/common/cpp/audioapi/core/effects/IIRFilterNode.h +1 -1
  33. package/common/cpp/audioapi/core/effects/StereoPannerNode.cpp +7 -4
  34. package/common/cpp/audioapi/core/effects/StereoPannerNode.h +1 -1
  35. package/common/cpp/audioapi/core/effects/WaveShaperNode.cpp +79 -0
  36. package/common/cpp/audioapi/core/effects/WaveShaperNode.h +66 -0
  37. package/common/cpp/audioapi/core/effects/WorkletNode.cpp +2 -2
  38. package/common/cpp/audioapi/core/effects/WorkletNode.h +2 -2
  39. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.cpp +7 -4
  40. package/common/cpp/audioapi/core/effects/WorkletProcessingNode.h +6 -2
  41. package/common/cpp/audioapi/core/sources/AudioBuffer.cpp +2 -3
  42. package/common/cpp/audioapi/core/sources/AudioBuffer.h +1 -1
  43. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +59 -25
  44. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +4 -2
  45. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +18 -11
  46. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +3 -1
  47. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +37 -21
  48. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +3 -3
  49. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +11 -11
  50. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +4 -2
  51. package/common/cpp/audioapi/core/sources/ConstantSourceNode.cpp +16 -8
  52. package/common/cpp/audioapi/core/sources/ConstantSourceNode.h +1 -1
  53. package/common/cpp/audioapi/core/sources/OscillatorNode.cpp +30 -18
  54. package/common/cpp/audioapi/core/sources/OscillatorNode.h +1 -1
  55. package/common/cpp/audioapi/core/sources/RecorderAdapterNode.cpp +4 -4
  56. package/common/cpp/audioapi/core/sources/RecorderAdapterNode.h +1 -1
  57. package/common/cpp/audioapi/core/sources/StreamerNode.cpp +24 -10
  58. package/common/cpp/audioapi/core/sources/StreamerNode.h +4 -3
  59. package/common/cpp/audioapi/core/sources/WorkletSourceNode.cpp +11 -4
  60. package/common/cpp/audioapi/core/sources/WorkletSourceNode.h +6 -2
  61. package/common/cpp/audioapi/core/types/OverSampleType.h +7 -0
  62. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +1 -0
  63. package/common/cpp/audioapi/dsp/Resampler.cpp +200 -0
  64. package/common/cpp/audioapi/dsp/Resampler.h +65 -0
  65. package/common/cpp/audioapi/dsp/WaveShaper.cpp +105 -0
  66. package/common/cpp/audioapi/dsp/WaveShaper.h +46 -0
  67. package/common/cpp/audioapi/utils/AudioArray.cpp +5 -0
  68. package/common/cpp/audioapi/utils/AudioArray.h +6 -0
  69. package/common/cpp/test/RunTests.sh +1 -1
  70. package/common/cpp/test/src/AudioParamTest.cpp +10 -10
  71. package/common/cpp/test/src/AudioScheduledSourceTest.cpp +31 -15
  72. package/common/cpp/test/src/ConstantSourceTest.cpp +16 -14
  73. package/common/cpp/test/src/DelayTest.cpp +14 -13
  74. package/common/cpp/test/src/GainTest.cpp +10 -9
  75. package/common/cpp/test/src/IIRFilterTest.cpp +4 -4
  76. package/common/cpp/test/src/OscillatorTest.cpp +2 -2
  77. package/common/cpp/test/src/StereoPannerTest.cpp +14 -12
  78. package/common/cpp/test/src/biquad/BiquadFilterTest.cpp +25 -25
  79. package/common/cpp/test/src/biquad/BiquadFilterTest.h +3 -5
  80. package/common/cpp/test/src/core/effects/WaveShaperNodeTest.cpp +76 -0
  81. package/common/cpp/test/src/dsp/ResamplerTest.cpp +117 -0
  82. package/ios/audioapi/ios/AudioAPIModule.mm +4 -4
  83. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +1 -1
  84. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +9 -3
  85. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js +0 -3
  86. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js.map +1 -1
  87. package/lib/commonjs/AudioAPIModule/AudioAPIModule.web.js +20 -0
  88. package/lib/commonjs/AudioAPIModule/AudioAPIModule.web.js.map +1 -0
  89. package/lib/commonjs/AudioAPIModule/ModuleInterfaces.js +6 -0
  90. package/lib/commonjs/AudioAPIModule/ModuleInterfaces.js.map +1 -0
  91. package/lib/commonjs/api.js +16 -0
  92. package/lib/commonjs/api.js.map +1 -1
  93. package/lib/commonjs/api.web.js +23 -0
  94. package/lib/commonjs/api.web.js.map +1 -1
  95. package/lib/commonjs/core/BaseAudioContext.js +4 -0
  96. package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
  97. package/lib/commonjs/core/WaveShaperNode.js +38 -0
  98. package/lib/commonjs/core/WaveShaperNode.js.map +1 -0
  99. package/lib/commonjs/specs/NativeAudioAPIModule.js.map +1 -1
  100. package/lib/commonjs/specs/NativeAudioAPIModule.web.js +47 -0
  101. package/lib/commonjs/specs/NativeAudioAPIModule.web.js.map +1 -0
  102. package/lib/commonjs/system/AudioManager.js.map +1 -1
  103. package/lib/commonjs/system/types.js +4 -0
  104. package/lib/commonjs/web-core/AudioContext.js +4 -0
  105. package/lib/commonjs/web-core/AudioContext.js.map +1 -1
  106. package/lib/commonjs/web-core/OfflineAudioContext.js +4 -0
  107. package/lib/commonjs/web-core/OfflineAudioContext.js.map +1 -1
  108. package/lib/commonjs/web-core/WaveShaperNode.js +38 -0
  109. package/lib/commonjs/web-core/WaveShaperNode.js.map +1 -0
  110. package/lib/commonjs/web-system/AudioManager.js +30 -0
  111. package/lib/commonjs/web-system/AudioManager.js.map +1 -0
  112. package/lib/commonjs/web-system/index.js +12 -0
  113. package/lib/commonjs/web-system/index.js.map +1 -1
  114. package/lib/module/AudioAPIModule/AudioAPIModule.js +0 -4
  115. package/lib/module/AudioAPIModule/AudioAPIModule.js.map +1 -1
  116. package/lib/module/AudioAPIModule/AudioAPIModule.web.js +16 -0
  117. package/lib/module/AudioAPIModule/AudioAPIModule.web.js.map +1 -0
  118. package/lib/module/AudioAPIModule/ModuleInterfaces.js +4 -0
  119. package/lib/module/AudioAPIModule/ModuleInterfaces.js.map +1 -0
  120. package/lib/module/AudioAPIModule/index.js +1 -1
  121. package/lib/module/AudioAPIModule/index.js.map +1 -1
  122. package/lib/module/api.js +2 -0
  123. package/lib/module/api.js.map +1 -1
  124. package/lib/module/api.web.js +3 -1
  125. package/lib/module/api.web.js.map +1 -1
  126. package/lib/module/core/BaseAudioContext.js +4 -0
  127. package/lib/module/core/BaseAudioContext.js.map +1 -1
  128. package/lib/module/core/WaveShaperNode.js +32 -0
  129. package/lib/module/core/WaveShaperNode.js.map +1 -0
  130. package/lib/module/specs/NativeAudioAPIModule.js.map +1 -1
  131. package/lib/module/specs/NativeAudioAPIModule.web.js +44 -0
  132. package/lib/module/specs/NativeAudioAPIModule.web.js.map +1 -0
  133. package/lib/module/specs/index.js +1 -1
  134. package/lib/module/specs/index.js.map +1 -1
  135. package/lib/module/system/AudioManager.js.map +1 -1
  136. package/lib/module/system/types.js +2 -0
  137. package/lib/module/web-core/AudioContext.js +4 -0
  138. package/lib/module/web-core/AudioContext.js.map +1 -1
  139. package/lib/module/web-core/OfflineAudioContext.js +4 -0
  140. package/lib/module/web-core/OfflineAudioContext.js.map +1 -1
  141. package/lib/module/web-core/WaveShaperNode.js +32 -0
  142. package/lib/module/web-core/WaveShaperNode.js.map +1 -0
  143. package/lib/module/web-system/AudioManager.js +26 -0
  144. package/lib/module/web-system/AudioManager.js.map +1 -0
  145. package/lib/module/web-system/index.js +1 -0
  146. package/lib/module/web-system/index.js.map +1 -1
  147. package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts +2 -10
  148. package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts.map +1 -1
  149. package/lib/typescript/AudioAPIModule/AudioAPIModule.web.d.ts +13 -0
  150. package/lib/typescript/AudioAPIModule/AudioAPIModule.web.d.ts.map +1 -0
  151. package/lib/typescript/AudioAPIModule/ModuleInterfaces.d.ts +18 -0
  152. package/lib/typescript/AudioAPIModule/ModuleInterfaces.d.ts.map +1 -0
  153. package/lib/typescript/api.d.ts +2 -0
  154. package/lib/typescript/api.d.ts.map +1 -1
  155. package/lib/typescript/api.web.d.ts +3 -1
  156. package/lib/typescript/api.web.d.ts.map +1 -1
  157. package/lib/typescript/core/BaseAudioContext.d.ts +2 -0
  158. package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
  159. package/lib/typescript/core/WaveShaperNode.d.ts +9 -0
  160. package/lib/typescript/core/WaveShaperNode.d.ts.map +1 -0
  161. package/lib/typescript/interfaces.d.ts +8 -2
  162. package/lib/typescript/interfaces.d.ts.map +1 -1
  163. package/lib/typescript/specs/NativeAudioAPIModule.d.ts +1 -1
  164. package/lib/typescript/specs/NativeAudioAPIModule.d.ts.map +1 -1
  165. package/lib/typescript/specs/NativeAudioAPIModule.web.d.ts +34 -0
  166. package/lib/typescript/specs/NativeAudioAPIModule.web.d.ts.map +1 -0
  167. package/lib/typescript/system/AudioManager.d.ts +2 -2
  168. package/lib/typescript/system/AudioManager.d.ts.map +1 -1
  169. package/lib/typescript/system/notification/types.d.ts +1 -1
  170. package/lib/typescript/system/notification/types.d.ts.map +1 -1
  171. package/lib/typescript/system/types.d.ts +17 -0
  172. package/lib/typescript/system/types.d.ts.map +1 -1
  173. package/lib/typescript/types.d.ts +1 -0
  174. package/lib/typescript/types.d.ts.map +1 -1
  175. package/lib/typescript/web-core/AudioContext.d.ts +2 -0
  176. package/lib/typescript/web-core/AudioContext.d.ts.map +1 -1
  177. package/lib/typescript/web-core/BaseAudioContext.d.ts +3 -1
  178. package/lib/typescript/web-core/BaseAudioContext.d.ts.map +1 -1
  179. package/lib/typescript/web-core/OfflineAudioContext.d.ts +2 -0
  180. package/lib/typescript/web-core/OfflineAudioContext.d.ts.map +1 -1
  181. package/lib/typescript/web-core/WaveShaperNode.d.ts +9 -0
  182. package/lib/typescript/web-core/WaveShaperNode.d.ts.map +1 -0
  183. package/lib/typescript/web-system/AudioManager.d.ts +24 -0
  184. package/lib/typescript/web-system/AudioManager.d.ts.map +1 -0
  185. package/lib/typescript/web-system/index.d.ts +1 -0
  186. package/lib/typescript/web-system/index.d.ts.map +1 -1
  187. package/package.json +1 -1
  188. package/src/AudioAPIModule/AudioAPIModule.ts +6 -17
  189. package/src/AudioAPIModule/AudioAPIModule.web.ts +18 -0
  190. package/src/AudioAPIModule/ModuleInterfaces.ts +25 -0
  191. package/src/api.ts +2 -0
  192. package/src/api.web.ts +3 -0
  193. package/src/core/BaseAudioContext.ts +5 -0
  194. package/src/core/WaveShaperNode.ts +43 -0
  195. package/src/interfaces.ts +9 -1
  196. package/src/specs/NativeAudioAPIModule.ts +5 -3
  197. package/src/specs/NativeAudioAPIModule.web.ts +93 -0
  198. package/src/system/AudioManager.ts +19 -14
  199. package/src/system/notification/types.ts +1 -1
  200. package/src/system/types.ts +22 -0
  201. package/src/types.ts +2 -0
  202. package/src/web-core/AudioContext.tsx +5 -0
  203. package/src/web-core/BaseAudioContext.tsx +3 -1
  204. package/src/web-core/OfflineAudioContext.tsx +5 -0
  205. package/src/web-core/WaveShaperNode.tsx +42 -0
  206. package/src/web-system/AudioManager.ts +33 -0
  207. package/src/web-system/index.ts +1 -0
@@ -16,19 +16,21 @@
16
16
 
17
17
  namespace audioapi {
18
18
 
19
- AudioScheduledSourceNode::AudioScheduledSourceNode(BaseAudioContext *context)
19
+ AudioScheduledSourceNode::AudioScheduledSourceNode(std::shared_ptr<BaseAudioContext> context)
20
20
  : AudioNode(context),
21
21
  startTime_(-1.0),
22
22
  stopTime_(-1.0),
23
- playbackState_(PlaybackState::UNSCHEDULED) {
23
+ playbackState_(PlaybackState::UNSCHEDULED),
24
+ audioEventHandlerRegistry_(context->audioEventHandlerRegistry_) {
24
25
  numberOfInputs_ = 0;
25
- audioEventHandlerRegistry_ = context_->audioEventHandlerRegistry_;
26
26
  }
27
27
 
28
28
  void AudioScheduledSourceNode::start(double when) {
29
29
  #if !RN_AUDIO_API_TEST
30
- if (auto context = dynamic_cast<AudioContext *>(context_)) {
31
- context->start();
30
+ if (std::shared_ptr<BaseAudioContext> context = context_.lock()) {
31
+ if (auto audioContext = dynamic_cast<AudioContext *>(context.get())) {
32
+ audioContext->start();
33
+ }
32
34
  }
33
35
  #endif // RN_AUDIO_API_TEST
34
36
 
@@ -72,18 +74,16 @@ void AudioScheduledSourceNode::updatePlaybackInfo(
72
74
  const std::shared_ptr<AudioBus> &processingBus,
73
75
  int framesToProcess,
74
76
  size_t &startOffset,
75
- size_t &nonSilentFramesToProcess) {
77
+ size_t &nonSilentFramesToProcess,
78
+ float sampleRate,
79
+ size_t currentSampleFrame) {
76
80
  if (!isInitialized_) {
77
81
  startOffset = 0;
78
82
  nonSilentFramesToProcess = 0;
79
83
  return;
80
84
  }
81
85
 
82
- assert(context_ != nullptr);
83
-
84
- auto sampleRate = context_->getSampleRate();
85
-
86
- size_t firstFrame = context_->getCurrentSampleFrame();
86
+ auto firstFrame = currentSampleFrame;
87
87
  size_t lastFrame = firstFrame + framesToProcess - 1;
88
88
 
89
89
  size_t startFrame = std::max(dsp::timeToSampleFrame(startTime_, sampleRate), firstFrame);
@@ -26,7 +26,7 @@ class AudioScheduledSourceNode : public AudioNode {
26
26
  // STOP_SCHEDULED: The node is scheduled to stop at a specific time, but is still playing.
27
27
  // FINISHED: The node has finished playing.
28
28
  enum class PlaybackState { UNSCHEDULED, SCHEDULED, PLAYING, STOP_SCHEDULED, FINISHED };
29
- explicit AudioScheduledSourceNode(BaseAudioContext *context);
29
+ explicit AudioScheduledSourceNode(std::shared_ptr<BaseAudioContext> context);
30
30
 
31
31
  virtual void start(double when);
32
32
  virtual void stop(double when);
@@ -54,7 +54,9 @@ class AudioScheduledSourceNode : public AudioNode {
54
54
  const std::shared_ptr<AudioBus> &processingBus,
55
55
  int framesToProcess,
56
56
  size_t &startOffset,
57
- size_t &nonSilentFramesToProcess);
57
+ size_t &nonSilentFramesToProcess,
58
+ float sampleRate,
59
+ size_t currentSampleFrame);
58
60
 
59
61
  void handleStopScheduled();
60
62
  };
@@ -6,10 +6,14 @@
6
6
  #include <memory>
7
7
 
8
8
  namespace audioapi {
9
- ConstantSourceNode::ConstantSourceNode(BaseAudioContext *context)
10
- : AudioScheduledSourceNode(context) {
11
- offsetParam_ = std::make_shared<AudioParam>(
12
- 1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context);
9
+ ConstantSourceNode::ConstantSourceNode(std::shared_ptr<BaseAudioContext> context)
10
+ : AudioScheduledSourceNode(context),
11
+ offsetParam_(
12
+ std::make_shared<AudioParam>(
13
+ 1.0,
14
+ MOST_NEGATIVE_SINGLE_FLOAT,
15
+ MOST_POSITIVE_SINGLE_FLOAT,
16
+ context)) {
13
17
  isInitialized_ = true;
14
18
  }
15
19
 
@@ -23,15 +27,19 @@ std::shared_ptr<AudioBus> ConstantSourceNode::processNode(
23
27
  size_t startOffset = 0;
24
28
  size_t offsetLength = 0;
25
29
 
26
- updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength);
27
-
28
- if (!isPlaying() && !isStopScheduled()) {
30
+ std::shared_ptr<BaseAudioContext> context = context_.lock();
31
+ if (context == nullptr) {
29
32
  processingBus->zero();
30
33
  return processingBus;
31
34
  }
32
35
 
33
- auto offsetBus = offsetParam_->processARateParam(framesToProcess, context_->getCurrentTime());
36
+ updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength, context->getSampleRate(), context->getCurrentSampleFrame());
34
37
 
38
+ if (!isPlaying() && !isStopScheduled()) {
39
+ processingBus->zero();
40
+ return processingBus;
41
+ }
42
+ auto offsetBus = offsetParam_->processARateParam(framesToProcess, context->getCurrentTime());
35
43
  auto offsetChannelData = offsetBus->getChannel(0)->getData();
36
44
 
37
45
  for (int channel = 0; channel < processingBus->getNumberOfChannels(); ++channel) {
@@ -13,7 +13,7 @@ class AudioBus;
13
13
 
14
14
  class ConstantSourceNode : public AudioScheduledSourceNode {
15
15
  public:
16
- explicit ConstantSourceNode(BaseAudioContext *context);
16
+ explicit ConstantSourceNode(std::shared_ptr<BaseAudioContext> context);
17
17
 
18
18
  [[nodiscard]] std::shared_ptr<AudioParam> getOffsetParam() const;
19
19
 
@@ -8,19 +8,23 @@
8
8
 
9
9
  namespace audioapi {
10
10
 
11
- OscillatorNode::OscillatorNode(BaseAudioContext *context) : AudioScheduledSourceNode(context) {
12
- frequencyParam_ = std::make_shared<AudioParam>(
13
- 444.0, -context_->getNyquistFrequency(), context_->getNyquistFrequency(), context);
14
- detuneParam_ = std::make_shared<AudioParam>(
15
- 0.0,
16
- -1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT,
17
- 1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT,
18
- context);
19
- type_ = OscillatorType::SINE;
20
- periodicWave_ = context_->getBasicWaveForm(type_);
21
-
22
- audioBus_ = std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, 1, context_->getSampleRate());
23
-
11
+ OscillatorNode::OscillatorNode(std::shared_ptr<BaseAudioContext> context)
12
+ : AudioScheduledSourceNode(context),
13
+ frequencyParam_(
14
+ std::make_shared<AudioParam>(
15
+ 444.0,
16
+ -context->getNyquistFrequency(),
17
+ context->getNyquistFrequency(),
18
+ context)),
19
+ detuneParam_(
20
+ std::make_shared<AudioParam>(
21
+ 0.0,
22
+ -1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT,
23
+ 1200 * LOG2_MOST_POSITIVE_SINGLE_FLOAT,
24
+ context)),
25
+ type_(OscillatorType::SINE),
26
+ periodicWave_(context->getBasicWaveForm(type_)) {
27
+ audioBus_ = std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, 1, context->getSampleRate());
24
28
  isInitialized_ = true;
25
29
  }
26
30
 
@@ -37,8 +41,10 @@ std::string OscillatorNode::getType() {
37
41
  }
38
42
 
39
43
  void OscillatorNode::setType(const std::string &type) {
40
- type_ = OscillatorNode::fromString(type);
41
- periodicWave_ = context_->getBasicWaveForm(type_);
44
+ if (std::shared_ptr<BaseAudioContext> context = context_.lock()) {
45
+ type_ = OscillatorNode::fromString(type);
46
+ periodicWave_ = context->getBasicWaveForm(type_);
47
+ }
42
48
  }
43
49
 
44
50
  void OscillatorNode::setPeriodicWave(const std::shared_ptr<PeriodicWave> &periodicWave) {
@@ -52,15 +58,21 @@ std::shared_ptr<AudioBus> OscillatorNode::processNode(
52
58
  size_t startOffset = 0;
53
59
  size_t offsetLength = 0;
54
60
 
55
- updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength);
61
+ std::shared_ptr<BaseAudioContext> context = context_.lock();
62
+ if (context == nullptr) {
63
+ processingBus->zero();
64
+ return processingBus;
65
+ }
66
+
67
+ updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength, context->getSampleRate(), context->getCurrentSampleFrame());
56
68
 
57
69
  if (!isPlaying() && !isStopScheduled()) {
58
70
  processingBus->zero();
59
71
  return processingBus;
60
72
  }
61
73
 
62
- auto time = context_->getCurrentTime() +
63
- static_cast<double>(startOffset) * 1.0 / context_->getSampleRate();
74
+ auto time =
75
+ context->getCurrentTime() + static_cast<double>(startOffset) * 1.0 / context->getSampleRate();
64
76
  auto detuneParamValues = detuneParam_->processARateParam(framesToProcess, time);
65
77
  auto frequencyParamValues = frequencyParam_->processARateParam(framesToProcess, time);
66
78
 
@@ -16,7 +16,7 @@ class AudioBus;
16
16
 
17
17
  class OscillatorNode : public AudioScheduledSourceNode {
18
18
  public:
19
- explicit OscillatorNode(BaseAudioContext *context);
19
+ explicit OscillatorNode(std::shared_ptr<BaseAudioContext> context);
20
20
 
21
21
  [[nodiscard]] std::shared_ptr<AudioParam> getFrequencyParam() const;
22
22
  [[nodiscard]] std::shared_ptr<AudioParam> getDetuneParam() const;
@@ -9,8 +9,7 @@
9
9
 
10
10
  namespace audioapi {
11
11
 
12
- RecorderAdapterNode::RecorderAdapterNode(BaseAudioContext *context) noexcept(
13
- std::is_nothrow_constructible<AudioNode, BaseAudioContext *>::value)
12
+ RecorderAdapterNode::RecorderAdapterNode(std::shared_ptr<BaseAudioContext> context)
14
13
  : AudioNode(context) {
15
14
  // It should be marked as initialized only after it is connected to the
16
15
  // recorder. Internall buffer size is based on the recorder's buffer length.
@@ -18,7 +17,8 @@ RecorderAdapterNode::RecorderAdapterNode(BaseAudioContext *context) noexcept(
18
17
  }
19
18
 
20
19
  void RecorderAdapterNode::init(size_t bufferSize, int channelCount) {
21
- if (isInitialized_) {
20
+ std::shared_ptr<BaseAudioContext> context = context_.lock();
21
+ if (isInitialized_ || context == nullptr) {
22
22
  return;
23
23
  }
24
24
 
@@ -42,7 +42,7 @@ void RecorderAdapterNode::init(size_t bufferSize, int channelCount) {
42
42
  // context output and not enforcing anything on the system output/input configuration.
43
43
  // A lot of words for a couple of lines of implementation :shrug:
44
44
  adapterOutputBus_ =
45
- std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, channelCount_, context_->getSampleRate());
45
+ std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate());
46
46
  isInitialized_ = true;
47
47
  }
48
48
 
@@ -19,7 +19,7 @@ class AudioBus;
19
19
  /// @note it will push silence if it is not connected to any Recorder
20
20
  class RecorderAdapterNode : public AudioNode {
21
21
  public:
22
- explicit RecorderAdapterNode(BaseAudioContext *context);
22
+ explicit RecorderAdapterNode(std::shared_ptr<BaseAudioContext> context);
23
23
 
24
24
  /// @brief Initialize the RecorderAdapterNode with a buffer size and channel count.
25
25
  /// @note This method should be called ONLY ONCE when the buffer size is known.
@@ -22,7 +22,7 @@
22
22
 
23
23
  namespace audioapi {
24
24
  #if !RN_AUDIO_API_FFMPEG_DISABLED
25
- StreamerNode::StreamerNode(BaseAudioContext *context)
25
+ StreamerNode::StreamerNode(std::shared_ptr<BaseAudioContext> context)
26
26
  : AudioScheduledSourceNode(context),
27
27
  fmtCtx_(nullptr),
28
28
  codecCtx_(nullptr),
@@ -37,7 +37,7 @@ StreamerNode::StreamerNode(BaseAudioContext *context)
37
37
  maxResampledSamples_(0),
38
38
  processedSamples_(0) {}
39
39
  #else
40
- StreamerNode::StreamerNode(BaseAudioContext *context) : AudioScheduledSourceNode(context) {}
40
+ StreamerNode::StreamerNode(std::shared_ptr<BaseAudioContext> context) : AudioScheduledSourceNode(context) {}
41
41
  #endif // RN_AUDIO_API_FFMPEG_DISABLED
42
42
 
43
43
  StreamerNode::~StreamerNode() {
@@ -48,6 +48,11 @@ StreamerNode::~StreamerNode() {
48
48
 
49
49
  bool StreamerNode::initialize(const std::string &input_url) {
50
50
  #if !RN_AUDIO_API_FFMPEG_DISABLED
51
+ std::shared_ptr<BaseAudioContext> context = context_.lock();
52
+ if (context == nullptr) {
53
+ return false;
54
+ }
55
+
51
56
  if (isInitialized_) {
52
57
  cleanup();
53
58
  }
@@ -58,7 +63,7 @@ bool StreamerNode::initialize(const std::string &input_url) {
58
63
  return false;
59
64
  }
60
65
 
61
- if (!findAudioStream() || !setupDecoder() || !setupResampler()) {
66
+ if (!findAudioStream() || !setupDecoder() || !setupResampler(context->getSampleRate())) {
62
67
  if (VERBOSE)
63
68
  printf("Failed to find/setup audio stream\n");
64
69
  cleanup();
@@ -77,7 +82,7 @@ bool StreamerNode::initialize(const std::string &input_url) {
77
82
 
78
83
  channelCount_ = codecpar_->ch_layout.nb_channels;
79
84
  audioBus_ =
80
- std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, channelCount_, context_->getSampleRate());
85
+ std::make_shared<AudioBus>(RENDER_QUANTUM_SIZE, channelCount_, context->getSampleRate());
81
86
 
82
87
  auto [sender, receiver] = channels::spsc::channel<
83
88
  StreamingData,
@@ -100,7 +105,12 @@ std::shared_ptr<AudioBus> StreamerNode::processNode(
100
105
  #if !RN_AUDIO_API_FFMPEG_DISABLED
101
106
  size_t startOffset = 0;
102
107
  size_t offsetLength = 0;
103
- updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength);
108
+ std::shared_ptr<BaseAudioContext> context = context_.lock();
109
+ if (context == nullptr) {
110
+ processingBus->zero();
111
+ return processingBus;
112
+ }
113
+ updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength, context->getSampleRate(), context->getCurrentSampleFrame());
104
114
  isNodeFinished_.store(isFinished(), std::memory_order_release);
105
115
 
106
116
  if (!isPlaying() && !isStopScheduled()) {
@@ -146,7 +156,7 @@ std::shared_ptr<AudioBus> StreamerNode::processNode(
146
156
  }
147
157
 
148
158
  #if !RN_AUDIO_API_FFMPEG_DISABLED
149
- bool StreamerNode::setupResampler() {
159
+ bool StreamerNode::setupResampler(float outSampleRate) {
150
160
  // Allocate resampler context
151
161
  swrCtx_ = swr_alloc();
152
162
  if (swrCtx_ == nullptr) {
@@ -160,7 +170,7 @@ bool StreamerNode::setupResampler() {
160
170
 
161
171
  // Set output parameters (float)
162
172
  av_opt_set_chlayout(swrCtx_, "out_chlayout", &codecCtx_->ch_layout, 0);
163
- av_opt_set_int(swrCtx_, "out_sample_rate", context_->getSampleRate(), 0);
173
+ av_opt_set_int(swrCtx_, "out_sample_rate", outSampleRate, 0);
164
174
  av_opt_set_sample_fmt(swrCtx_, "out_sample_fmt", AV_SAMPLE_FMT_FLTP, 0);
165
175
 
166
176
  // Initialize the resampler
@@ -193,7 +203,11 @@ void StreamerNode::streamAudio() {
193
203
  if (avcodec_receive_frame(codecCtx_, frame_) != 0) {
194
204
  return;
195
205
  }
196
- if (!processFrameWithResampler(frame_)) {
206
+ std::shared_ptr<BaseAudioContext> context = context_.lock();
207
+ if (context == nullptr) {
208
+ return;
209
+ }
210
+ if (!processFrameWithResampler(frame_, context)) {
197
211
  return;
198
212
  }
199
213
  }
@@ -201,7 +215,7 @@ void StreamerNode::streamAudio() {
201
215
  }
202
216
  }
203
217
 
204
- bool StreamerNode::processFrameWithResampler(AVFrame *frame) {
218
+ bool StreamerNode::processFrameWithResampler(AVFrame *frame, std::shared_ptr<BaseAudioContext> context) {
205
219
  // Check if we need to reallocate the resampled buffer
206
220
  int out_samples = swr_get_out_samples(swrCtx_, frame->nb_samples);
207
221
  if (out_samples > maxResampledSamples_) {
@@ -241,7 +255,7 @@ bool StreamerNode::processFrameWithResampler(AVFrame *frame) {
241
255
  auto bus = AudioBus(
242
256
  static_cast<size_t>(converted_samples),
243
257
  codecCtx_->ch_layout.nb_channels,
244
- context_->getSampleRate());
258
+ context->getSampleRate());
245
259
  for (int ch = 0; ch < codecCtx_->ch_layout.nb_channels; ch++) {
246
260
  auto *src = reinterpret_cast<float *>(resampledData_[ch]);
247
261
  float *dst = bus.getChannel(ch)->getData();
@@ -62,7 +62,7 @@ class AudioBus;
62
62
 
63
63
  class StreamerNode : public AudioScheduledSourceNode {
64
64
  public:
65
- explicit StreamerNode(BaseAudioContext *context);
65
+ explicit StreamerNode(std::shared_ptr<BaseAudioContext> context);
66
66
  ~StreamerNode() override;
67
67
 
68
68
  /**
@@ -106,16 +106,17 @@ class StreamerNode : public AudioScheduledSourceNode {
106
106
 
107
107
  /**
108
108
  * @brief Setting up the resampler
109
+ * @param outSampleRate Sample rate for the output audio
109
110
  * @return true if successful, false otherwise
110
111
  */
111
- bool setupResampler();
112
+ bool setupResampler(float outSampleRate);
112
113
 
113
114
  /**
114
115
  * @brief Resample the audio frame, change its sample format and channel layout
115
116
  * @param frame The AVFrame to resample
116
117
  * @return true if successful, false otherwise
117
118
  */
118
- bool processFrameWithResampler(AVFrame *frame);
119
+ bool processFrameWithResampler(AVFrame *frame, std::shared_ptr<BaseAudioContext> context);
119
120
 
120
121
  /**
121
122
  * @brief Thread function to continuously read and process audio frames
@@ -5,7 +5,9 @@
5
5
 
6
6
  namespace audioapi {
7
7
 
8
- WorkletSourceNode::WorkletSourceNode(BaseAudioContext *context, WorkletsRunner &&workletRunner)
8
+ WorkletSourceNode::WorkletSourceNode(
9
+ std::shared_ptr<BaseAudioContext> context,
10
+ WorkletsRunner &&workletRunner)
9
11
  : AudioScheduledSourceNode(context), workletRunner_(std::move(workletRunner)) {
10
12
  isInitialized_ = true;
11
13
 
@@ -29,7 +31,12 @@ std::shared_ptr<AudioBus> WorkletSourceNode::processNode(
29
31
  size_t startOffset = 0;
30
32
  size_t nonSilentFramesToProcess = framesToProcess;
31
33
 
32
- updatePlaybackInfo(processingBus, framesToProcess, startOffset, nonSilentFramesToProcess);
34
+ std::shared_ptr<BaseAudioContext> context = context_.lock();
35
+ if (context == nullptr) {
36
+ processingBus->zero();
37
+ return processingBus;
38
+ }
39
+ updatePlaybackInfo(processingBus, framesToProcess, startOffset, nonSilentFramesToProcess, context->getSampleRate(), context->getCurrentSampleFrame());
33
40
 
34
41
  if (nonSilentFramesToProcess == 0) {
35
42
  processingBus->zero();
@@ -39,7 +46,7 @@ std::shared_ptr<AudioBus> WorkletSourceNode::processNode(
39
46
  size_t outputChannelCount = processingBus->getNumberOfChannels();
40
47
 
41
48
  auto result = workletRunner_.executeOnRuntimeSync(
42
- [this, nonSilentFramesToProcess, startOffset](jsi::Runtime &rt) {
49
+ [this, nonSilentFramesToProcess, startOffset, time = context->getCurrentTime()](jsi::Runtime &rt) {
43
50
  auto jsiArray = jsi::Array(rt, this->outputBuffsHandles_.size());
44
51
  for (size_t i = 0; i < this->outputBuffsHandles_.size(); ++i) {
45
52
  auto arrayBuffer = jsi::ArrayBuffer(rt, this->outputBuffsHandles_[i]);
@@ -52,7 +59,7 @@ std::shared_ptr<AudioBus> WorkletSourceNode::processNode(
52
59
  return workletRunner_.callUnsafe(
53
60
  jsiArray,
54
61
  jsi::Value(rt, static_cast<int>(nonSilentFramesToProcess)),
55
- jsi::Value(rt, this->context_->getCurrentTime()),
62
+ jsi::Value(rt, time),
56
63
  jsi::Value(rt, static_cast<int>(startOffset)));
57
64
  });
58
65
 
@@ -16,7 +16,9 @@ namespace audioapi {
16
16
  #if RN_AUDIO_API_TEST
17
17
  class WorkletSourceNode : public AudioScheduledSourceNode {
18
18
  public:
19
- explicit WorkletSourceNode(BaseAudioContext *context, WorkletsRunner &&workletRunner)
19
+ explicit WorkletSourceNode(
20
+ std::shared_ptr<BaseAudioContext> context,
21
+ WorkletsRunner &&workletRunner)
20
22
  : AudioScheduledSourceNode(context) {}
21
23
 
22
24
  protected:
@@ -30,7 +32,9 @@ class WorkletSourceNode : public AudioScheduledSourceNode {
30
32
 
31
33
  class WorkletSourceNode : public AudioScheduledSourceNode {
32
34
  public:
33
- explicit WorkletSourceNode(BaseAudioContext *context, WorkletsRunner &&workletRunner);
35
+ explicit WorkletSourceNode(
36
+ std::shared_ptr<BaseAudioContext> context,
37
+ WorkletsRunner &&workletRunner);
34
38
 
35
39
  protected:
36
40
  std::shared_ptr<AudioBus> processNode(
@@ -0,0 +1,7 @@
1
+ #pragma once
2
+
3
+ namespace audioapi {
4
+
5
+ enum class OverSampleType { OVERSAMPLE_NONE, OVERSAMPLE_2X, OVERSAMPLE_4X };
6
+
7
+ } // namespace audioapi
@@ -54,6 +54,7 @@ void AudioRecorderCallback::emitAudioData(bool flush) {
54
54
  if (sizeLimit == 0) {
55
55
  return;
56
56
  }
57
+
57
58
  while (circularBus_[0]->getNumberOfAvailableFrames() >= sizeLimit) {
58
59
  auto bus = std::make_shared<AudioBus>(sizeLimit, channelCount_, sampleRate_);
59
60
 
@@ -0,0 +1,200 @@
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
+
29
+ #include <audioapi/dsp/Resampler.h>
30
+ #include <algorithm>
31
+ #include <cmath>
32
+ #include <cstring>
33
+ #include <memory>
34
+ #include <numbers>
35
+
36
+ #if defined(__ARM_NEON)
37
+ #include <arm_neon.h>
38
+ #endif
39
+
40
+ // based on WebKit UpSampler and DownSampler implementation
41
+
42
+ namespace audioapi {
43
+
44
+ Resampler::Resampler(int maxBlockSize, int kernelSize):
45
+ kernelSize_(kernelSize),
46
+ kernel_(std::make_shared<AudioArray>(kernelSize)),
47
+ stateBuffer_(std::make_shared<AudioArray>(2 * maxBlockSize)) {
48
+ stateBuffer_->zero();
49
+ }
50
+
51
+ // https://en.wikipedia.org/wiki/Window_function
52
+ float Resampler::computeBlackmanWindow(double x) const {
53
+ double alpha = 0.16;
54
+ double a0 = 0.5 * (1.0 - alpha);
55
+ double a1 = 0.5;
56
+ double a2 = 0.5 * alpha;
57
+ double n = x / kernelSize_;
58
+ return static_cast<float>(a0 - a1 * std::cos(2.0 * PI * n) + a2 * std::cos(4.0 * PI * n));
59
+ }
60
+
61
+ float Resampler::computeConvolution(const float *stateStart, const float *kernelStart) const {
62
+ float sum = 0.0f;
63
+ int k = 0;
64
+
65
+ #ifdef __ARM_NEON
66
+ float32x4_t vSum = vdupq_n_f32(0.0f);
67
+
68
+ // process 4 samples at a time
69
+ for (; k <= kernelSize_ - 4; k += 4) {
70
+ float32x4_t vState = vld1q_f32(stateStart + k);
71
+ float32x4_t vKernel = vld1q_f32(kernelStart + k);
72
+
73
+ // fused multiply-add: vSum += vState * vKernel
74
+ vSum = vmlaq_f32(vSum, vState, vKernel);
75
+ }
76
+
77
+ // horizontal reduction: Sum the 4 lanes of vSum into a single float
78
+ sum += vgetq_lane_f32(vSum, 0);
79
+ sum += vgetq_lane_f32(vSum, 1);
80
+ sum += vgetq_lane_f32(vSum, 2);
81
+ sum += vgetq_lane_f32(vSum, 3);
82
+ #endif
83
+ for (; k < kernelSize_; ++k) {
84
+ sum += stateStart[k] * kernelStart[k];
85
+ }
86
+
87
+ return sum;
88
+ }
89
+
90
+ void Resampler::reset() {
91
+ if (stateBuffer_) {
92
+ stateBuffer_->zero();
93
+ }
94
+ }
95
+
96
+ UpSampler::UpSampler(int maxBlockSize, int kernelSize) : Resampler(maxBlockSize, kernelSize) {
97
+ initializeKernel();
98
+ }
99
+
100
+ void UpSampler::initializeKernel() {
101
+ auto kData = kernel_->getData();
102
+ int halfSize = kernelSize_ / 2;
103
+ double subSampleOffset = -0.5;
104
+
105
+ for (int i = 0; i < kernelSize_; ++i) {
106
+ // we want to sample the sinc function halfway between integer points
107
+ auto x = static_cast<double>(i - halfSize) - subSampleOffset;
108
+
109
+ // https://en.wikipedia.org/wiki/Sinc_filter
110
+ // sets cutoff frequency to nyquist
111
+ double sinc = (std::abs(x) < 1e-9) ? 1.0 : std::sin(x * PI) / (x * PI);
112
+
113
+ // apply window in order smooth out the edges, because sinc extends to infinity in both directions
114
+ kData[i] = static_cast<float>(sinc * computeBlackmanWindow(i - subSampleOffset));
115
+ }
116
+
117
+ // reverse kernel to match convolution implementation
118
+ std::reverse(kData, kData + kernelSize_);
119
+ }
120
+
121
+ int UpSampler::process(
122
+ const std::shared_ptr<AudioArray> &input,
123
+ const std::shared_ptr<AudioArray> &output,
124
+ int framesToProcess) {
125
+
126
+ const float *inputData = input->getData();
127
+ float *outputData = output->getData();
128
+ float *state = stateBuffer_->getData();
129
+ const float *kernel = kernel_->getData();
130
+
131
+ // copy new input [ HISTORY | NEW DATA ]
132
+ std::memcpy(state + kernelSize_, inputData, framesToProcess * sizeof(float));
133
+
134
+ int halfKernel = kernelSize_ / 2;
135
+
136
+ for (int i = 0; i < framesToProcess; ++i) {
137
+ // direct copy for even samples with half kernel latency compensation
138
+ outputData[2 * i] = state[kernelSize_ + i - halfKernel];
139
+
140
+ // convolution for odd samples
141
+ // symmetric Linear Phase filter has latency of half kernel size
142
+ outputData[2 * i + 1] = computeConvolution(&state[i + 1], kernel);
143
+ }
144
+
145
+ // move new data to history [ NEW DATA | EMPTY ]
146
+ std::memmove(state, state + framesToProcess, kernelSize_ * sizeof(float));
147
+
148
+ return framesToProcess * 2;
149
+ }
150
+
151
+ DownSampler::DownSampler(int maxBlockSize, int kernelSize) : Resampler(maxBlockSize, kernelSize) {
152
+ initializeKernel();
153
+ }
154
+
155
+ void DownSampler::initializeKernel() {
156
+ auto kData = kernel_->getData();
157
+ int halfSize = kernelSize_ / 2;
158
+
159
+ for (int i = 0; i < kernelSize_; ++i) {
160
+ // we want to sample the sinc function halfway between integer points
161
+ auto x = static_cast<double>(i - halfSize);
162
+
163
+ // https://en.wikipedia.org/wiki/Sinc_filter
164
+ // sets cutoff frequency to nyquist / 2
165
+ double sinc = (std::abs(x) < 1e-9) ? 1.0 : std::sin(x * PI * 0.5) / (x * PI * 0.5);
166
+ sinc *= 0.5;
167
+
168
+ // apply window in order smooth out the edges, because sinc extends to infinity in both directions
169
+ kData[i] = static_cast<float>(sinc * computeBlackmanWindow(i));
170
+ }
171
+
172
+ // reverse kernel to match convolution implementation
173
+ std::reverse(kData, kData + kernelSize_);
174
+ }
175
+
176
+ int DownSampler::process(
177
+ const std::shared_ptr<AudioArray> &input,
178
+ const std::shared_ptr<AudioArray> &output,
179
+ int framesToProcess) {
180
+ const float *inputData = input->getData();
181
+ float *outputData = output->getData();
182
+ float *state = stateBuffer_->getData();
183
+ const float *kernel = kernel_->getData();
184
+
185
+ // copy new input [ HISTORY | NEW DATA ]
186
+ std::memcpy(state + kernelSize_, inputData, framesToProcess * sizeof(float));
187
+
188
+ auto outputCount = framesToProcess / 2;
189
+
190
+ for (int i = 0; i < outputCount; ++i) {
191
+ // convolution for downsampled samples
192
+ outputData[i] = computeConvolution(&state[2 * i + 1], kernel);
193
+ }
194
+
195
+ // move new data to history [ NEW DATA | EMPTY ]
196
+ std::memmove(state, state + framesToProcess, kernelSize_ * sizeof(float));
197
+
198
+ return outputCount;
199
+ }
200
+ } // namespace audioapi