react-native-audio-api 0.12.1 → 0.12.2

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 (107) hide show
  1. package/README.md +8 -3
  2. package/RNAudioAPI.podspec +0 -2
  3. package/android/src/main/cpp/audioapi/CMakeLists.txt +8 -2
  4. package/android/src/main/cpp/audioapi/android/AudioAPIModule.cpp +7 -29
  5. package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.cpp +83 -0
  6. package/android/src/main/cpp/audioapi/android/JniEventPayloadParser.h +14 -0
  7. package/android/src/main/cpp/audioapi/android/core/AndroidAudioRecorder.cpp +4 -3
  8. package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +1 -0
  9. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.cpp +37 -21
  10. package/android/src/main/cpp/audioapi/android/core/utils/AndroidRecorderCallback.h +1 -1
  11. package/common/cpp/audioapi/AudioAPIModuleInstaller.h +21 -0
  12. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.cpp +26 -4
  13. package/common/cpp/audioapi/HostObjects/sources/AudioBufferQueueSourceNodeHostObject.h +1 -0
  14. package/common/cpp/audioapi/HostObjects/sources/AudioFileSourceNodeHostObject.cpp +2 -2
  15. package/common/cpp/audioapi/HostObjects/utils/AudioDecoderHostObject.cpp +3 -3
  16. package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.cpp +60 -0
  17. package/common/cpp/audioapi/HostObjects/utils/AudioFileUtilsHostObject.h +24 -0
  18. package/common/cpp/audioapi/HostObjects/utils/NodeOptionsParser.h +2 -2
  19. package/common/cpp/audioapi/core/OfflineAudioContext.cpp +0 -1
  20. package/common/cpp/audioapi/core/OfflineAudioContext.h +0 -1
  21. package/common/cpp/audioapi/core/effects/ConvolverNode.cpp +4 -10
  22. package/common/cpp/audioapi/core/effects/ConvolverNode.h +0 -4
  23. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +13 -11
  24. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +5 -9
  25. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +47 -139
  26. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +11 -8
  27. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +29 -114
  28. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +6 -8
  29. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.cpp +9 -11
  30. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.h +1 -1
  31. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +2 -2
  32. package/common/cpp/audioapi/core/types/AudioFormat.h +3 -1
  33. package/common/cpp/audioapi/core/utils/AudioDecoder.cpp +120 -91
  34. package/common/cpp/audioapi/core/utils/AudioDecoder.h +24 -101
  35. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.cpp +862 -0
  36. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.h +164 -0
  37. package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +2 -4
  38. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +7 -12
  39. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +2 -0
  40. package/common/cpp/audioapi/core/utils/buffer/BufferProcessingDirection.h +6 -0
  41. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.cpp +110 -0
  42. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.h +75 -0
  43. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.cpp +129 -0
  44. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.h +55 -0
  45. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.cpp +95 -0
  46. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.h +52 -0
  47. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +65 -157
  48. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +52 -33
  49. package/common/cpp/audioapi/events/AudioEventPayload.h +87 -0
  50. package/common/cpp/audioapi/events/IAudioEventHandlerRegistry.h +12 -12
  51. package/common/cpp/audioapi/libs/decoding/IncrementalAudioDecoder.h +12 -10
  52. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +152 -78
  53. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +6 -6
  54. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.cpp +34 -20
  55. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.h +4 -4
  56. package/common/cpp/audioapi/utils/AudioArray.hpp +6 -1
  57. package/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +1 -1
  58. package/common/cpp/audioapi/utils/TaskOffloader.hpp +3 -5
  59. package/ios/audioapi/ios/AudioAPIModule.h +2 -1
  60. package/ios/audioapi/ios/AudioAPIModule.mm +4 -25
  61. package/ios/audioapi/ios/core/IOSAudioPlayer.h +16 -2
  62. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +90 -24
  63. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +1 -1
  64. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +64 -20
  65. package/ios/audioapi/ios/system/AudioSessionManager.mm +18 -7
  66. package/ios/audioapi/ios/system/SystemNotificationManager.mm +22 -22
  67. package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +10 -13
  68. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js +1 -1
  69. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js.map +1 -1
  70. package/lib/commonjs/api.js +8 -0
  71. package/lib/commonjs/api.js.map +1 -1
  72. package/lib/commonjs/api.web.js +5 -0
  73. package/lib/commonjs/api.web.js.map +1 -1
  74. package/lib/commonjs/core/AudioFileUtils.js +35 -0
  75. package/lib/commonjs/core/AudioFileUtils.js.map +1 -0
  76. package/lib/commonjs/mock/index.js +15 -1
  77. package/lib/commonjs/mock/index.js.map +1 -1
  78. package/lib/module/AudioAPIModule/AudioAPIModule.js +1 -1
  79. package/lib/module/AudioAPIModule/AudioAPIModule.js.map +1 -1
  80. package/lib/module/api.js +1 -0
  81. package/lib/module/api.js.map +1 -1
  82. package/lib/module/api.web.js +3 -0
  83. package/lib/module/api.web.js.map +1 -1
  84. package/lib/module/core/AudioFileUtils.js +31 -0
  85. package/lib/module/core/AudioFileUtils.js.map +1 -0
  86. package/lib/module/mock/index.js +14 -1
  87. package/lib/module/mock/index.js.map +1 -1
  88. package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts.map +1 -1
  89. package/lib/typescript/api.d.ts +1 -0
  90. package/lib/typescript/api.d.ts.map +1 -1
  91. package/lib/typescript/api.web.d.ts +1 -0
  92. package/lib/typescript/api.web.d.ts.map +1 -1
  93. package/lib/typescript/core/AudioFileUtils.d.ts +2 -0
  94. package/lib/typescript/core/AudioFileUtils.d.ts.map +1 -0
  95. package/lib/typescript/interfaces.d.ts +3 -0
  96. package/lib/typescript/interfaces.d.ts.map +1 -1
  97. package/lib/typescript/mock/index.d.ts +3 -1
  98. package/lib/typescript/mock/index.d.ts.map +1 -1
  99. package/package.json +3 -3
  100. package/scripts/download-prebuilt-binaries.sh +34 -1
  101. package/src/AudioAPIModule/AudioAPIModule.ts +1 -0
  102. package/src/AudioAPIModule/globals.d.ts +3 -0
  103. package/src/api.ts +1 -0
  104. package/src/api.web.ts +7 -0
  105. package/src/core/AudioFileUtils.ts +49 -0
  106. package/src/interfaces.ts +7 -0
  107. package/src/mock/index.ts +29 -0
@@ -41,10 +41,6 @@ class ConvolverNode : public AudioNode {
41
41
  int framesToProcess) override;
42
42
 
43
43
  private:
44
- std::shared_ptr<DSPAudioBuffer> processInputs(
45
- const std::shared_ptr<DSPAudioBuffer> &outputBuffer,
46
- int framesToProcess,
47
- bool checkIsAlreadyProcessed) override;
48
44
  void onInputDisabled() override;
49
45
  const float gainCalibrationSampleRate_;
50
46
  size_t remainingSegments_;
@@ -8,8 +8,6 @@
8
8
 
9
9
  #include <algorithm>
10
10
  #include <memory>
11
- #include <string>
12
- #include <unordered_map>
13
11
 
14
12
  namespace audioapi {
15
13
  AudioBufferBaseSourceNode::AudioBufferBaseSourceNode(
@@ -85,10 +83,10 @@ std::shared_ptr<DSPAudioBuffer> AudioBufferBaseSourceNode::processNode(
85
83
  void AudioBufferBaseSourceNode::sendOnPositionChangedEvent() {
86
84
  if (onPositionChangedCallbackId_ != 0 &&
87
85
  onPositionChangedTimeInFrames_ > onPositionChangedIntervalInFrames_) {
88
- std::unordered_map<std::string, EventValue> body = {{"value", getCurrentPosition()}};
89
-
90
- audioEventHandlerRegistry_->invokeHandlerWithEventBody(
91
- AudioEvent::POSITION_CHANGED, onPositionChangedCallbackId_, body);
86
+ audioEventHandlerRegistry_->dispatchEvent(
87
+ AudioEvent::POSITION_CHANGED,
88
+ onPositionChangedCallbackId_,
89
+ DoubleValuePayload{.value = getCurrentPosition()});
92
90
 
93
91
  onPositionChangedTimeInFrames_ = 0;
94
92
  }
@@ -119,7 +117,8 @@ void AudioBufferBaseSourceNode::processWithPitchCorrection(
119
117
 
120
118
  playbackRateBuffer_->zero();
121
119
 
122
- auto framesNeededToStretch = static_cast<int>(playbackRate * static_cast<float>(framesToProcess));
120
+ auto framesNeededToStretch =
121
+ std::abs(static_cast<int>(playbackRate * static_cast<float>(framesToProcess)));
123
122
 
124
123
  updatePlaybackInfo(
125
124
  playbackRateBuffer_,
@@ -134,7 +133,7 @@ void AudioBufferBaseSourceNode::processWithPitchCorrection(
134
133
  return;
135
134
  }
136
135
 
137
- processWithoutInterpolation(playbackRateBuffer_, startOffset, offsetLength, playbackRate);
136
+ runBufferProcessor(playbackRateBuffer_, startOffset, offsetLength, playbackRate, false);
138
137
 
139
138
  stretch_->process(
140
139
  playbackRateBuffer_.get()[0],
@@ -178,16 +177,19 @@ void AudioBufferBaseSourceNode::processWithoutPitchCorrection(
178
177
  }
179
178
 
180
179
  if (std::fabs(computedPlaybackRate) == 1.0) {
181
- processWithoutInterpolation(processingBuffer, startOffset, offsetLength, computedPlaybackRate);
180
+ runBufferProcessor(processingBuffer, startOffset, offsetLength, computedPlaybackRate, false);
182
181
  } else {
183
- processWithInterpolation(processingBuffer, startOffset, offsetLength, computedPlaybackRate);
182
+ runBufferProcessor(processingBuffer, startOffset, offsetLength, computedPlaybackRate, true);
184
183
  }
185
184
 
186
185
  sendOnPositionChangedEvent();
187
186
  }
188
187
 
189
188
  float AudioBufferBaseSourceNode::getComputedPlaybackRateValue(int framesToProcess, double time) {
190
- auto playbackRate = playbackRateParam_->processKRateParam(framesToProcess, time);
189
+ auto playbackRate = std::clamp(
190
+ playbackRateParam_->processKRateParam(framesToProcess, time),
191
+ MIN_PLAYBACK_RATE,
192
+ MAX_PLAYBACK_RATE);
191
193
  auto detune = std::pow(
192
194
  2.0f, //NOLINT(cppcoreguidelines-avoid-magic-numbers, readability-magic-numbers)
193
195
  detuneParam_->processKRateParam(framesToProcess, time) / static_cast<float>(OCTAVE_RANGE));
@@ -45,17 +45,12 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode {
45
45
 
46
46
  virtual bool isEmpty() const = 0;
47
47
 
48
- virtual void processWithoutInterpolation(
48
+ virtual void runBufferProcessor(
49
49
  const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
50
50
  size_t startOffset,
51
51
  size_t offsetLength,
52
- float playbackRate) = 0;
53
-
54
- virtual void processWithInterpolation(
55
- const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
56
- size_t startOffset,
57
- size_t offsetLength,
58
- float playbackRate) = 0;
52
+ float playbackRate,
53
+ bool interpolate) = 0;
59
54
 
60
55
  private:
61
56
  // pitch correction parameters
@@ -64,7 +59,7 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode {
64
59
  std::shared_ptr<signalsmith::stretch::SignalsmithStretch<float>> stretch_;
65
60
  std::shared_ptr<DSPAudioBuffer> playbackRateBuffer_;
66
61
  static constexpr float MAX_PLAYBACK_RATE = 3.0f;
67
- static constexpr float MIN_PLAYBACK_RATE = 0.0f;
62
+ static constexpr float MIN_PLAYBACK_RATE = -3.0f;
68
63
 
69
64
  // k-rate params
70
65
  const std::shared_ptr<AudioParam> detuneParam_;
@@ -79,6 +74,7 @@ class AudioBufferBaseSourceNode : public AudioScheduledSourceNode {
79
74
  void processWithPitchCorrection(
80
75
  const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
81
76
  int framesToProcess);
77
+
82
78
  void processWithoutPitchCorrection(
83
79
  const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
84
80
  int framesToProcess);
@@ -4,6 +4,7 @@
4
4
  #include <audioapi/core/utils/AudioGraphManager.h>
5
5
  #include <audioapi/core/utils/Constants.h>
6
6
  #include <audioapi/core/utils/Locker.h>
7
+ #include <audioapi/core/utils/buffer/QueueBufferProcessor.h>
7
8
  #include <audioapi/dsp/AudioUtils.hpp>
8
9
  #include <audioapi/events/AudioEventHandlerRegistry.h>
9
10
  #include <audioapi/types/NodeOptions.h>
@@ -11,8 +12,6 @@
11
12
 
12
13
  #include <algorithm>
13
14
  #include <memory>
14
- #include <string>
15
- #include <unordered_map>
16
15
  #include <utility>
17
16
 
18
17
  namespace audioapi {
@@ -28,6 +27,22 @@ AudioBufferQueueSourceNode::AudioBufferQueueSourceNode(
28
27
  }
29
28
 
30
29
  isInitialized_.store(true, std::memory_order_release);
30
+
31
+ auto graphManager = context->getGraphManager();
32
+
33
+ auto onBufferConsumed = [this, graphManager](
34
+ size_t bufferId,
35
+ std::shared_ptr<AudioBuffer> buffer,
36
+ bool isLastInQueue,
37
+ bool fireBufferEndedEvent) {
38
+ playedBuffersDuration_ += buffer->getDuration();
39
+ if (fireBufferEndedEvent) {
40
+ sendOnBufferEndedEvent(bufferId, isLastInQueue);
41
+ }
42
+ graphManager->addAudioBufferForDestruction(std::move(buffer));
43
+ };
44
+
45
+ processor_ = std::make_unique<QueueBufferProcessor>(&buffers_, onBufferConsumed);
31
46
  }
32
47
 
33
48
  void AudioBufferQueueSourceNode::stop(double when) {
@@ -132,6 +147,14 @@ void AudioBufferQueueSourceNode::unregisterOnBufferEndedCallback(uint64_t callba
132
147
  audioEventHandlerRegistry_->unregisterHandler(AudioEvent::BUFFER_ENDED, callbackId);
133
148
  }
134
149
 
150
+ void AudioBufferQueueSourceNode::setChannelCount(int channelCount) {
151
+ if (channelCount_ != channelCount) {
152
+ channelCount_ = channelCount;
153
+ audioBuffer_ = std::make_shared<DSPAudioBuffer>(
154
+ RENDER_QUANTUM_SIZE, channelCount_, getContextSampleRate());
155
+ }
156
+ }
157
+
135
158
  double AudioBufferQueueSourceNode::getCurrentPosition() const {
136
159
  return dsp::sampleFrameToTime(static_cast<int>(vReadIndex_), getContextSampleRate()) +
137
160
  playedBuffersDuration_;
@@ -139,11 +162,10 @@ double AudioBufferQueueSourceNode::getCurrentPosition() const {
139
162
 
140
163
  void AudioBufferQueueSourceNode::sendOnBufferEndedEvent(size_t bufferId, bool isLastBufferInQueue) {
141
164
  if (onBufferEndedCallbackId_ != 0) {
142
- std::unordered_map<std::string, EventValue> body = {
143
- {"bufferId", std::to_string(bufferId)}, {"isLastBufferInQueue", isLastBufferInQueue}};
144
-
145
- audioEventHandlerRegistry_->invokeHandlerWithEventBody(
146
- AudioEvent::BUFFER_ENDED, onBufferEndedCallbackId_, body);
165
+ audioEventHandlerRegistry_->dispatchEvent(
166
+ AudioEvent::BUFFER_ENDED,
167
+ onBufferEndedCallbackId_,
168
+ BufferEndedPayload{.bufferId = bufferId, .isLastBufferInQueue = isLastBufferInQueue});
147
169
  }
148
170
  }
149
171
 
@@ -155,147 +177,33 @@ bool AudioBufferQueueSourceNode::isEmpty() const {
155
177
  return buffers_.empty();
156
178
  }
157
179
 
158
- // todo: refactor so its less complex and more readable
159
- void AudioBufferQueueSourceNode::processWithoutInterpolation(
180
+ void AudioBufferQueueSourceNode::runBufferProcessor(
160
181
  const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
161
182
  size_t startOffset,
162
183
  size_t offsetLength,
163
- float playbackRate) {
164
- if (auto context = context_.lock()) {
165
- auto readIndex = static_cast<size_t>(vReadIndex_);
166
- size_t writeIndex = startOffset;
167
-
168
- auto data = buffers_.front();
169
- auto bufferId = data.first;
170
- auto buffer = data.second;
171
-
172
- size_t framesLeft = offsetLength;
173
-
174
- while (framesLeft > 0) {
175
- size_t framesToEnd = buffer->getSize() - readIndex;
176
- size_t framesToCopy = std::min(framesToEnd, framesLeft);
177
- framesToCopy = framesToCopy > 0 ? framesToCopy : 0;
178
-
179
- assert(readIndex >= 0);
180
- assert(writeIndex >= 0);
181
- assert(readIndex + framesToCopy <= buffer->getSize());
182
- assert(writeIndex + framesToCopy <= processingBuffer->getSize());
183
-
184
- processingBuffer->copy(*buffer, readIndex, writeIndex, framesToCopy);
185
-
186
- writeIndex += framesToCopy;
187
- readIndex += framesToCopy;
188
- framesLeft -= framesToCopy;
189
-
190
- if (readIndex >= buffer->getSize()) {
191
- playedBuffersDuration_ += buffer->getDuration();
192
- buffers_.pop_front();
193
-
194
- if (!(buffers_.empty() && addExtraTailFrames_)) {
195
- sendOnBufferEndedEvent(bufferId, buffers_.empty());
196
- }
197
-
198
- if (buffers_.empty()) {
199
- if (addExtraTailFrames_) {
200
- buffers_.emplace_back(bufferId, tailBuffer_);
201
- addExtraTailFrames_ = false;
202
- } else {
203
- context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer));
204
- processingBuffer->zero(writeIndex, framesLeft);
205
- readIndex = 0;
206
-
207
- break;
208
- }
209
- }
210
-
211
- context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer));
212
- data = buffers_.front();
213
- bufferId = data.first;
214
- buffer = data.second;
215
- readIndex = 0;
216
- }
217
- }
184
+ float playbackRate,
185
+ bool interpolate) {
186
+ if (!processingBuffer) {
187
+ return;
188
+ }
218
189
 
219
- // update reading index for next render quantum
220
- vReadIndex_ = static_cast<double>(readIndex);
190
+ if (buffers_.empty()) {
191
+ processingBuffer->zero(startOffset, offsetLength);
192
+ return;
221
193
  }
222
- }
223
194
 
224
- // todo: refactor so its less complex and more readable
225
- void AudioBufferQueueSourceNode::processWithInterpolation(
226
- const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
227
- size_t startOffset,
228
- size_t offsetLength,
229
- float playbackRate) {
230
- if (auto context = context_.lock()) {
231
- size_t writeIndex = startOffset;
232
- size_t framesLeft = offsetLength;
233
-
234
- auto data = buffers_.front();
235
- auto bufferId = data.first;
236
- auto buffer = data.second;
237
-
238
- while (framesLeft > 0) {
239
- auto readIndex = static_cast<size_t>(vReadIndex_);
240
- size_t nextReadIndex = readIndex + 1;
241
- auto factor = static_cast<float>(vReadIndex_ - static_cast<double>(readIndex));
242
-
243
- bool crossBufferInterpolation = false;
244
- std::shared_ptr<AudioBuffer> nextBuffer = nullptr;
245
-
246
- if (nextReadIndex >= buffer->getSize()) {
247
- if (buffers_.size() > 1) {
248
- auto tempQueue = buffers_;
249
- tempQueue.pop_front();
250
- nextBuffer = tempQueue.front().second;
251
- nextReadIndex = 0;
252
- crossBufferInterpolation = true;
253
- } else {
254
- nextReadIndex = readIndex;
255
- }
256
- }
195
+ if (addExtraTailFrames_ && tailBuffer_ != nullptr) {
196
+ processor_->setPendingTail(tailBuffer_);
197
+ }
257
198
 
258
- for (size_t i = 0; i < processingBuffer->getNumberOfChannels(); i += 1) {
259
- const auto destination = processingBuffer->getChannel(i)->span();
260
- const auto currentSource = buffer->getChannel(i)->span();
261
-
262
- if (crossBufferInterpolation) {
263
- const auto nextSource = nextBuffer->getChannel(i)->span();
264
- float currentSample = currentSource[readIndex];
265
- float nextSample = nextSource[nextReadIndex];
266
- destination[writeIndex] = currentSample + factor * (nextSample - currentSample);
267
- } else {
268
- destination[writeIndex] =
269
- dsp::linearInterpolate(currentSource, readIndex, nextReadIndex, factor);
270
- }
271
- }
199
+ processor_->setPosition(vReadIndex_);
200
+ processor_->process(processingBuffer, startOffset, offsetLength, playbackRate, interpolate);
272
201
 
273
- writeIndex += 1;
274
- // queue source node always use positive playbackRate
275
- vReadIndex_ += std::abs(playbackRate);
276
- framesLeft -= 1;
277
-
278
- if (vReadIndex_ >= static_cast<double>(buffer->getSize())) {
279
- playedBuffersDuration_ += buffer->getDuration();
280
- buffers_.pop_front();
281
-
282
- sendOnBufferEndedEvent(bufferId, buffers_.empty());
283
-
284
- if (buffers_.empty()) {
285
- context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer));
286
- processingBuffer->zero(writeIndex, framesLeft);
287
- vReadIndex_ = 0.0;
288
- break;
289
- }
290
-
291
- vReadIndex_ = vReadIndex_ - static_cast<double>(buffer->getSize());
292
- context->getGraphManager()->addAudioBufferForDestruction(std::move(buffer));
293
- data = buffers_.front();
294
- bufferId = data.first;
295
- buffer = data.second;
296
- }
297
- }
202
+ if (processor_->didConsumeTail()) {
203
+ addExtraTailFrames_ = false;
298
204
  }
205
+
206
+ vReadIndex_ = processor_->getPosition();
299
207
  }
300
208
 
301
209
  } // namespace audioapi
@@ -8,6 +8,7 @@
8
8
  #include <cstddef>
9
9
  #include <list>
10
10
  #include <memory>
11
+ #include "audioapi/core/utils/buffer/QueueBufferProcessor.h"
11
12
 
12
13
  namespace audioapi {
13
14
 
@@ -48,6 +49,11 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode {
48
49
 
49
50
  void unregisterOnBufferEndedCallback(uint64_t callbackId);
50
51
 
52
+ /// @brief Set the channel count of the node. Channel count is set only once when the first buffer is enqueued.
53
+ /// @param channelCount The channel count to set.
54
+ /// @note Audio Thread only
55
+ void setChannelCount(int channelCount);
56
+
51
57
  protected:
52
58
  double getCurrentPosition() const override;
53
59
 
@@ -55,17 +61,12 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode {
55
61
 
56
62
  bool isEmpty() const final;
57
63
 
58
- void processWithoutInterpolation(
64
+ void runBufferProcessor(
59
65
  const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
60
66
  size_t startOffset,
61
67
  size_t offsetLength,
62
- float playbackRate) final;
63
-
64
- void processWithInterpolation(
65
- const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
66
- size_t startOffset,
67
- size_t offsetLength,
68
- float playbackRate) final;
68
+ float playbackRate,
69
+ bool interpolate) final;
69
70
 
70
71
  private:
71
72
  // User provided buffers
@@ -78,6 +79,8 @@ class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode {
78
79
  double playedBuffersDuration_ = 0;
79
80
 
80
81
  uint64_t onBufferEndedCallbackId_ = 0; // 0 means no callback
82
+
83
+ std::unique_ptr<QueueBufferProcessor> processor_;
81
84
  };
82
85
 
83
86
  } // namespace audioapi
@@ -9,7 +9,9 @@
9
9
  #include <audioapi/types/NodeOptions.h>
10
10
  #include <audioapi/utils/AudioArray.hpp>
11
11
 
12
+ #include <audioapi/core/utils/buffer/SingleBufferProcessor.h>
12
13
  #include <algorithm>
14
+ #include <cstddef>
13
15
  #include <memory>
14
16
  #include <utility>
15
17
 
@@ -23,11 +25,13 @@ AudioBufferSourceNode::AudioBufferSourceNode(
23
25
  loopSkip_(options.loopSkip),
24
26
  loopStart_(options.loopStart),
25
27
  loopEnd_(options.loopEnd) {
28
+ processor_ = std::make_unique<SingleBufferProcessor>();
26
29
  isInitialized_.store(true, std::memory_order_release);
27
30
  }
28
31
 
29
32
  void AudioBufferSourceNode::setLoop(bool loop) {
30
33
  loop_ = loop;
34
+ processor_->setLoop(loop_);
31
35
  }
32
36
 
33
37
  void AudioBufferSourceNode::setLoopSkip(bool loopSkip) {
@@ -67,6 +71,7 @@ void AudioBufferSourceNode::setBuffer(
67
71
  channelCount_ = 1;
68
72
 
69
73
  buffer_ = nullptr;
74
+ processor_->setBuffer(nullptr);
70
75
  return;
71
76
  }
72
77
 
@@ -74,6 +79,7 @@ void AudioBufferSourceNode::setBuffer(
74
79
  audioBuffer_ = audioBuffer;
75
80
  channelCount_ = static_cast<int>(buffer_->getNumberOfChannels());
76
81
  loopEnd_ = buffer_->getDuration();
82
+ processor_->setBuffer(buffer_);
77
83
  }
78
84
 
79
85
  void AudioBufferSourceNode::start(double when, double offset, double duration) {
@@ -114,8 +120,8 @@ double AudioBufferSourceNode::getCurrentPosition() const {
114
120
 
115
121
  void AudioBufferSourceNode::sendOnLoopEndedEvent() {
116
122
  if (onLoopEndedCallbackId_ != 0) {
117
- audioEventHandlerRegistry_->invokeHandlerWithEventBody(
118
- AudioEvent::LOOP_ENDED, onLoopEndedCallbackId_, {});
123
+ audioEventHandlerRegistry_->dispatchEvent(
124
+ AudioEvent::LOOP_ENDED, onLoopEndedCallbackId_, EmptyPayload{});
119
125
  }
120
126
  }
121
127
 
@@ -127,129 +133,38 @@ bool AudioBufferSourceNode::isEmpty() const {
127
133
  return buffer_ == nullptr;
128
134
  }
129
135
 
130
- // todo: refactor so its less complex and more readable
131
- void AudioBufferSourceNode::processWithoutInterpolation(
136
+ void AudioBufferSourceNode::runBufferProcessor(
132
137
  const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
133
138
  size_t startOffset,
134
139
  size_t offsetLength,
135
- float playbackRate) {
136
- size_t direction = playbackRate < 0.0f ? -1 : 1;
137
-
138
- auto readIndex = static_cast<size_t>(vReadIndex_);
139
- size_t writeIndex = startOffset;
140
-
141
- auto frameStart = static_cast<size_t>(getVirtualStartFrame(getContextSampleRate()));
142
- auto frameEnd = static_cast<size_t>(getVirtualEndFrame(getContextSampleRate()));
143
- size_t frameDelta = frameEnd - frameStart;
144
-
145
- size_t framesLeft = offsetLength;
146
-
147
- // if we are moving towards loop, we do nothing because we will achieve it
148
- // otherwise, we wrap to the start of the loop if necessary
149
- if (loop_ &&
150
- ((readIndex >= frameEnd && direction == 1) || (readIndex < frameStart && direction == -1))) {
151
- readIndex = frameStart +
152
- (static_cast<int64_t>(readIndex) - static_cast<int64_t>(frameStart)) % frameDelta;
140
+ float playbackRate,
141
+ bool interpolate) {
142
+ if (!processingBuffer) {
143
+ return;
153
144
  }
154
145
 
155
- while (framesLeft > 0) {
156
- size_t framesToEnd = frameEnd - readIndex;
157
- size_t framesToCopy = std::min(framesToEnd, framesLeft);
158
- framesToCopy = framesToCopy > 0 ? framesToCopy : 0;
159
-
160
- assert(readIndex >= 0);
161
- assert(writeIndex >= 0);
162
- assert(readIndex + framesToCopy <= buffer_->getSize());
163
- assert(writeIndex + framesToCopy <= processingBuffer->getSize());
164
-
165
- // Direction is forward, we can normally copy the data
166
- if (direction == 1) {
167
- processingBuffer->copy(*buffer_, readIndex, writeIndex, framesToCopy);
168
- } else {
169
- for (size_t ch = 0; ch < processingBuffer->getNumberOfChannels(); ch += 1) {
170
- processingBuffer->getChannel(ch)->copyReverse(
171
- *buffer_->getChannel(ch), readIndex, writeIndex, framesToCopy);
172
- }
173
- }
174
-
175
- writeIndex += framesToCopy;
176
- readIndex += framesToCopy * direction;
177
- framesLeft -= framesToCopy;
178
-
179
- // if we are moving towards loop, we do nothing because we will achieve it
180
- // otherwise, we wrap to the start of the loop if necessary
181
- if ((readIndex >= frameEnd && direction == 1) || (readIndex < frameStart && direction == -1)) {
182
- readIndex -= direction * frameDelta;
146
+ const float sampleRate = getContextSampleRate();
147
+ const double startFrame = getVirtualStartFrame(sampleRate);
148
+ const double endFrame = getVirtualEndFrame(sampleRate);
183
149
 
184
- if (!loop_) {
185
- processingBuffer->zero(writeIndex, framesLeft);
186
- playbackState_ = PlaybackState::STOP_SCHEDULED;
187
- break;
188
- }
189
-
190
- sendOnLoopEndedEvent();
191
- }
150
+ // start(when, offset=duration) sets vReadIndex_ to endFrame, so clamp it
151
+ if (playbackRate < 0 && vReadIndex_ >= endFrame && endFrame > startFrame) {
152
+ vReadIndex_ = endFrame - 1.0;
192
153
  }
193
154
 
194
- // update reading index for next render quantum
195
- vReadIndex_ = static_cast<double>(readIndex);
196
- }
197
-
198
- void AudioBufferSourceNode::processWithInterpolation(
199
- const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
200
- size_t startOffset,
201
- size_t offsetLength,
202
- float playbackRate) {
203
- size_t direction = playbackRate < 0.0f ? -1 : 1;
204
-
205
- size_t writeIndex = startOffset;
155
+ processor_->setPosition(vReadIndex_);
156
+ processor_->setEndFrame(static_cast<size_t>(endFrame));
157
+ processor_->setStartFrame(static_cast<size_t>(startFrame));
158
+ processor_->process(processingBuffer, startOffset, offsetLength, playbackRate, interpolate);
206
159
 
207
- auto vFrameStart = getVirtualStartFrame(getContextSampleRate());
208
- auto vFrameEnd = getVirtualEndFrame(getContextSampleRate());
209
- auto vFrameDelta = vFrameEnd - vFrameStart;
210
-
211
- auto frameStart = static_cast<size_t>(vFrameStart);
212
- auto frameEnd = static_cast<size_t>(vFrameEnd);
213
-
214
- size_t framesLeft = offsetLength;
215
-
216
- // Wrap to the start of the loop if necessary
217
- if (loop_ && (vReadIndex_ >= vFrameEnd || vReadIndex_ < vFrameStart)) {
218
- vReadIndex_ = vFrameStart + std::fmod(vReadIndex_ - vFrameStart, vFrameDelta);
219
- }
220
-
221
- while (framesLeft > 0) {
222
- auto readIndex = static_cast<size_t>(vReadIndex_);
223
- size_t nextReadIndex = readIndex + 1;
224
- auto factor = static_cast<float>(vReadIndex_ - static_cast<double>(readIndex));
225
-
226
- if (nextReadIndex >= frameEnd) {
227
- nextReadIndex = loop_ ? frameStart : readIndex;
228
- }
229
-
230
- for (size_t i = 0; i < processingBuffer->getNumberOfChannels(); i++) {
231
- auto destination = processingBuffer->getChannel(i)->span();
232
- const auto source = buffer_->getChannel(i)->span();
233
-
234
- destination[writeIndex] = dsp::linearInterpolate(source, readIndex, nextReadIndex, factor);
235
- }
236
-
237
- writeIndex += 1;
238
- vReadIndex_ += playbackRate * static_cast<double>(direction);
239
- framesLeft -= 1;
240
-
241
- if (vReadIndex_ < vFrameStart || vReadIndex_ >= vFrameEnd) {
242
- vReadIndex_ -= static_cast<double>(direction) * vFrameDelta;
243
-
244
- if (!loop_) {
245
- processingBuffer->zero(writeIndex, framesLeft);
246
- playbackState_ = PlaybackState::STOP_SCHEDULED;
247
- break;
248
- }
249
-
250
- sendOnLoopEndedEvent();
160
+ if (processor_->atBoundary()) {
161
+ if (processor_->shouldStop()) {
162
+ playbackState_ = PlaybackState::STOP_SCHEDULED;
251
163
  }
164
+ sendOnLoopEndedEvent();
252
165
  }
166
+
167
+ vReadIndex_ = processor_->getPosition();
253
168
  }
254
169
 
255
170
  double AudioBufferSourceNode::getVirtualStartFrame(float sampleRate) const {
@@ -4,6 +4,7 @@
4
4
  #include <audioapi/libs/signalsmith-stretch/signalsmith-stretch.h>
5
5
  #include <audioapi/utils/AudioBuffer.hpp>
6
6
 
7
+ #include <audioapi/core/utils/buffer/SingleBufferProcessor.h>
7
8
  #include <cstddef>
8
9
  #include <memory>
9
10
 
@@ -52,17 +53,12 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode {
52
53
 
53
54
  bool isEmpty() const final;
54
55
 
55
- void processWithoutInterpolation(
56
+ void runBufferProcessor(
56
57
  const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
57
58
  size_t startOffset,
58
59
  size_t offsetLength,
59
- float playbackRate) final;
60
-
61
- void processWithInterpolation(
62
- const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
63
- size_t startOffset,
64
- size_t offsetLength,
65
- float playbackRate) final;
60
+ float playbackRate,
61
+ bool interpolate) final;
66
62
 
67
63
  private:
68
64
  // Looping related properties
@@ -79,6 +75,8 @@ class AudioBufferSourceNode : public AudioBufferBaseSourceNode {
79
75
 
80
76
  double getVirtualStartFrame(float sampleRate) const;
81
77
  double getVirtualEndFrame(float sampleRate);
78
+
79
+ std::unique_ptr<SingleBufferProcessor> processor_;
82
80
  };
83
81
 
84
82
  } // namespace audioapi