react-native-audio-api 0.12.0 → 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 (110) 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 +5 -4
  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/effects/WorkletProcessingNode.cpp +1 -1
  24. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +13 -11
  25. package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +5 -9
  26. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +47 -139
  27. package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +11 -8
  28. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +29 -114
  29. package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +6 -8
  30. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.cpp +9 -11
  31. package/common/cpp/audioapi/core/sources/AudioFileSourceNode.h +1 -1
  32. package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +2 -2
  33. package/common/cpp/audioapi/core/types/AudioFormat.h +3 -1
  34. package/common/cpp/audioapi/core/utils/AudioDecoder.cpp +120 -91
  35. package/common/cpp/audioapi/core/utils/AudioDecoder.h +24 -101
  36. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.cpp +862 -0
  37. package/common/cpp/audioapi/core/utils/AudioFileConcatenator.h +164 -0
  38. package/common/cpp/audioapi/core/utils/AudioFileWriter.cpp +2 -4
  39. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.cpp +7 -12
  40. package/common/cpp/audioapi/core/utils/AudioRecorderCallback.h +2 -0
  41. package/common/cpp/audioapi/core/utils/buffer/BufferProcessingDirection.h +6 -0
  42. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.cpp +110 -0
  43. package/common/cpp/audioapi/core/utils/buffer/BufferProcessorBase.h +75 -0
  44. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.cpp +129 -0
  45. package/common/cpp/audioapi/core/utils/buffer/QueueBufferProcessor.h +55 -0
  46. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.cpp +95 -0
  47. package/common/cpp/audioapi/core/utils/buffer/SingleBufferProcessor.h +52 -0
  48. package/common/cpp/audioapi/core/utils/param/ParamControlQueue.cpp +12 -8
  49. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +65 -157
  50. package/common/cpp/audioapi/events/AudioEventHandlerRegistry.h +52 -33
  51. package/common/cpp/audioapi/events/AudioEventPayload.h +87 -0
  52. package/common/cpp/audioapi/events/IAudioEventHandlerRegistry.h +12 -12
  53. package/common/cpp/audioapi/libs/decoding/IncrementalAudioDecoder.h +12 -10
  54. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.cpp +152 -78
  55. package/common/cpp/audioapi/libs/ffmpeg/FFmpegDecoding.h +6 -6
  56. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.cpp +34 -20
  57. package/common/cpp/audioapi/libs/miniaudio/MiniAudioDecoding.h +4 -4
  58. package/common/cpp/audioapi/utils/AudioArray.hpp +6 -1
  59. package/common/cpp/audioapi/utils/CrossThreadEventScheduler.hpp +1 -1
  60. package/common/cpp/audioapi/utils/TaskOffloader.hpp +3 -5
  61. package/ios/audioapi/ios/AudioAPIModule.h +2 -1
  62. package/ios/audioapi/ios/AudioAPIModule.mm +4 -25
  63. package/ios/audioapi/ios/core/IOSAudioPlayer.h +16 -2
  64. package/ios/audioapi/ios/core/IOSAudioPlayer.mm +90 -24
  65. package/ios/audioapi/ios/core/IOSAudioRecorder.mm +1 -1
  66. package/ios/audioapi/ios/core/utils/IOSRecorderCallback.mm +64 -20
  67. package/ios/audioapi/ios/system/AudioSessionManager.mm +18 -7
  68. package/ios/audioapi/ios/system/SystemNotificationManager.mm +22 -22
  69. package/ios/audioapi/ios/system/notification/PlaybackNotification.mm +10 -13
  70. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js +1 -1
  71. package/lib/commonjs/AudioAPIModule/AudioAPIModule.js.map +1 -1
  72. package/lib/commonjs/api.js +8 -0
  73. package/lib/commonjs/api.js.map +1 -1
  74. package/lib/commonjs/api.web.js +6 -1
  75. package/lib/commonjs/api.web.js.map +1 -1
  76. package/lib/commonjs/core/AudioFileUtils.js +35 -0
  77. package/lib/commonjs/core/AudioFileUtils.js.map +1 -0
  78. package/lib/commonjs/mock/index.js +15 -1
  79. package/lib/commonjs/mock/index.js.map +1 -1
  80. package/lib/module/AudioAPIModule/AudioAPIModule.js +1 -1
  81. package/lib/module/AudioAPIModule/AudioAPIModule.js.map +1 -1
  82. package/lib/module/api.js +1 -0
  83. package/lib/module/api.js.map +1 -1
  84. package/lib/module/api.web.js +4 -1
  85. package/lib/module/api.web.js.map +1 -1
  86. package/lib/module/core/AudioFileUtils.js +31 -0
  87. package/lib/module/core/AudioFileUtils.js.map +1 -0
  88. package/lib/module/mock/index.js +14 -1
  89. package/lib/module/mock/index.js.map +1 -1
  90. package/lib/typescript/AudioAPIModule/AudioAPIModule.d.ts.map +1 -1
  91. package/lib/typescript/api.d.ts +2 -0
  92. package/lib/typescript/api.d.ts.map +1 -1
  93. package/lib/typescript/api.web.d.ts +3 -1
  94. package/lib/typescript/api.web.d.ts.map +1 -1
  95. package/lib/typescript/core/AudioFileUtils.d.ts +2 -0
  96. package/lib/typescript/core/AudioFileUtils.d.ts.map +1 -0
  97. package/lib/typescript/interfaces.d.ts +3 -0
  98. package/lib/typescript/interfaces.d.ts.map +1 -1
  99. package/lib/typescript/mock/index.d.ts +3 -1
  100. package/lib/typescript/mock/index.d.ts.map +1 -1
  101. package/package.json +10 -4
  102. package/scripts/download-prebuilt-binaries.sh +34 -1
  103. package/scripts/validate-worklets-version.js +1 -1
  104. package/src/AudioAPIModule/AudioAPIModule.ts +1 -0
  105. package/src/AudioAPIModule/globals.d.ts +3 -0
  106. package/src/api.ts +2 -0
  107. package/src/api.web.ts +10 -2
  108. package/src/core/AudioFileUtils.ts +49 -0
  109. package/src/interfaces.ts +7 -0
  110. package/src/mock/index.ts +29 -0
@@ -0,0 +1,164 @@
1
+ #pragma once
2
+
3
+ #include <audioapi/libs/miniaudio/miniaudio.h>
4
+ #include <audioapi/utils/Result.hpp>
5
+
6
+ #include <memory>
7
+ #include <string>
8
+ #include <vector>
9
+
10
+ #if !RN_AUDIO_API_FFMPEG_DISABLED
11
+ struct AVDictionary;
12
+ struct AVFormatContext;
13
+ struct AVStream;
14
+ #endif // RN_AUDIO_API_FFMPEG_DISABLED
15
+
16
+ namespace audioapi {
17
+
18
+ using AudioFileConcatResult = Result<std::string, std::string>;
19
+
20
+ class MiniAudioDecoderGuard {
21
+ public:
22
+ MiniAudioDecoderGuard() = default;
23
+ MiniAudioDecoderGuard(const MiniAudioDecoderGuard &) = delete;
24
+ MiniAudioDecoderGuard &operator=(const MiniAudioDecoderGuard &) = delete;
25
+
26
+ MiniAudioDecoderGuard(MiniAudioDecoderGuard &&other) noexcept;
27
+ MiniAudioDecoderGuard &operator=(MiniAudioDecoderGuard &&other) noexcept;
28
+
29
+ // Closes the owned miniaudio decoder.
30
+ ~MiniAudioDecoderGuard();
31
+
32
+ // Opens an input file for decoded PCM reads.
33
+ [[nodiscard]] static Result<MiniAudioDecoderGuard, std::string> open(
34
+ const std::string &filePath,
35
+ ma_format outputFormat = ma_format_unknown);
36
+
37
+ // Returns the owned miniaudio decoder.
38
+ [[nodiscard]] ma_decoder *get();
39
+
40
+ // Returns the normalized file path represented by this input.
41
+ [[nodiscard]] const std::string &filePath() const;
42
+
43
+ // Returns the decoded output sample rate.
44
+ [[nodiscard]] ma_uint32 sampleRate() const;
45
+
46
+ // Returns the decoded output channel count.
47
+ [[nodiscard]] ma_uint32 channels() const;
48
+
49
+ // Returns the decoded output sample format.
50
+ [[nodiscard]] ma_format format() const;
51
+
52
+ private:
53
+ void close();
54
+
55
+ std::string filePath_;
56
+ std::unique_ptr<ma_decoder> decoder_{nullptr};
57
+ bool initialized_{false};
58
+ };
59
+
60
+ class MiniAudioEncoderGuard {
61
+ public:
62
+ MiniAudioEncoderGuard() = default;
63
+ MiniAudioEncoderGuard(const MiniAudioEncoderGuard &) = delete;
64
+ MiniAudioEncoderGuard &operator=(const MiniAudioEncoderGuard &) = delete;
65
+
66
+ // Closes the owned miniaudio encoder.
67
+ ~MiniAudioEncoderGuard();
68
+
69
+ // Opens a WAV output file with the provided PCM parameters.
70
+ [[nodiscard]] AudioFileConcatResult
71
+ open(const std::string &outputPath, ma_format format, ma_uint32 sampleRate, ma_uint32 channels);
72
+
73
+ // Writes decoded PCM frames into the WAV output file.
74
+ [[nodiscard]] AudioFileConcatResult
75
+ write(const std::string &inputPath, const void *frames, ma_uint64 frameCount);
76
+
77
+ private:
78
+ void close();
79
+
80
+ ma_encoder encoder_{};
81
+ bool initialized_{false};
82
+ };
83
+
84
+ #if !RN_AUDIO_API_FFMPEG_DISABLED
85
+ class AVDictionaryGuard {
86
+ public:
87
+ AVDictionaryGuard() = default;
88
+ AVDictionaryGuard(const AVDictionaryGuard &) = delete;
89
+ AVDictionaryGuard &operator=(const AVDictionaryGuard &) = delete;
90
+
91
+ // Releases the owned FFmpeg dictionary.
92
+ ~AVDictionaryGuard();
93
+
94
+ // Returns the dictionary pointer address expected by FFmpeg option APIs.
95
+ [[nodiscard]] AVDictionary **ptr();
96
+
97
+ private:
98
+ AVDictionary *dictionary_{nullptr};
99
+ };
100
+
101
+ class InputFormatContext {
102
+ public:
103
+ InputFormatContext() = default;
104
+
105
+ // Takes ownership of an opened FFmpeg input context and selected audio stream.
106
+ InputFormatContext(std::string filePath, AVFormatContext *context, int audioStreamIndex);
107
+
108
+ InputFormatContext(const InputFormatContext &) = delete;
109
+ InputFormatContext &operator=(const InputFormatContext &) = delete;
110
+
111
+ // Moves ownership without closing the transferred FFmpeg context.
112
+ InputFormatContext(InputFormatContext &&other) noexcept;
113
+ InputFormatContext &operator=(InputFormatContext &&other) noexcept;
114
+
115
+ // Closes the owned FFmpeg input context.
116
+ ~InputFormatContext();
117
+
118
+ // Returns the owned FFmpeg input context.
119
+ [[nodiscard]] AVFormatContext *context() const;
120
+
121
+ // Returns the selected audio stream from the input context.
122
+ [[nodiscard]] AVStream *audioStream() const;
123
+
124
+ // Returns the selected audio stream index from the input context.
125
+ [[nodiscard]] int audioStreamIndex() const;
126
+
127
+ // Returns the normalized file path represented by this input.
128
+ [[nodiscard]] const std::string &filePath() const;
129
+
130
+ private:
131
+ void close();
132
+
133
+ std::string filePath_;
134
+ AVFormatContext *context_{nullptr};
135
+ int audioStreamIndex_{-1};
136
+ };
137
+
138
+ class OutputFormatContext {
139
+ public:
140
+ // Takes ownership of an allocated FFmpeg output context.
141
+ explicit OutputFormatContext(AVFormatContext *context);
142
+ OutputFormatContext(const OutputFormatContext &) = delete;
143
+ OutputFormatContext &operator=(const OutputFormatContext &) = delete;
144
+
145
+ // Closes the output file handle and frees the owned FFmpeg output context.
146
+ ~OutputFormatContext();
147
+
148
+ // Returns the owned FFmpeg output context.
149
+ [[nodiscard]] AVFormatContext *get() const;
150
+
151
+ private:
152
+ AVFormatContext *context_{nullptr};
153
+ };
154
+ #endif // RN_AUDIO_API_FFMPEG_DISABLED
155
+
156
+ // Concatenates compatible local audio files into a single output file.
157
+ [[nodiscard]] AudioFileConcatResult concatAudioFiles(
158
+ const std::vector<std::string> &inputPaths,
159
+ const std::string &outputPath);
160
+
161
+ // Converts a local path or file:// URL into a decoded filesystem path.
162
+ [[nodiscard]] std::string normalizeFilePath(const std::string &path);
163
+
164
+ } // namespace audioapi
@@ -2,7 +2,6 @@
2
2
  #include <audioapi/events/AudioEventHandlerRegistry.h>
3
3
  #include <memory>
4
4
  #include <string>
5
- #include <unordered_map>
6
5
 
7
6
  namespace audioapi {
8
7
 
@@ -29,9 +28,8 @@ void AudioFileWriter::invokeOnErrorCallback(const std::string &message) {
29
28
  return;
30
29
  }
31
30
 
32
- std::unordered_map<std::string, EventValue> eventPayload = {{"message", message}};
33
- audioEventHandlerRegistry_->invokeHandlerWithEventBody(
34
- AudioEvent::RECORDER_ERROR, callbackId, eventPayload);
31
+ audioEventHandlerRegistry_->dispatchEvent(
32
+ AudioEvent::RECORDER_ERROR, callbackId, StringPayload{.name = "message", .reason = message});
35
33
  }
36
34
 
37
35
  bool AudioFileWriter::isFileOpen() {
@@ -8,7 +8,6 @@
8
8
  #include <algorithm>
9
9
  #include <memory>
10
10
  #include <string>
11
- #include <unordered_map>
12
11
 
13
12
  namespace audioapi {
14
13
 
@@ -67,15 +66,12 @@ void AudioRecorderCallback::emitAudioData(bool flush) {
67
66
  void AudioRecorderCallback::invokeCallback(
68
67
  const std::shared_ptr<AudioBuffer> &buffer,
69
68
  int numFrames) {
70
- auto audioBufferHostObject = std::make_shared<AudioBufferHostObject>(buffer);
71
-
72
- std::unordered_map<std::string, EventValue> eventPayload = {};
73
- eventPayload.insert({"buffer", audioBufferHostObject});
74
- eventPayload.insert({"numFrames", numFrames});
75
-
76
69
  if (audioEventHandlerRegistry_) {
77
- audioEventHandlerRegistry_->invokeHandlerWithEventBody(
78
- AudioEvent::AUDIO_READY, callbackId_, eventPayload);
70
+ audioEventHandlerRegistry_->dispatchEvent(
71
+ AudioEvent::AUDIO_READY,
72
+ callbackId_,
73
+ AudioReadyPayload{
74
+ .buffer = std::make_shared<AudioBufferHostObject>(buffer), .numFrames = numFrames});
79
75
  }
80
76
  }
81
77
 
@@ -96,9 +92,8 @@ void AudioRecorderCallback::invokeOnErrorCallback(const std::string &message) {
96
92
  return;
97
93
  }
98
94
 
99
- std::unordered_map<std::string, EventValue> eventPayload = {{"message", message}};
100
- audioEventHandlerRegistry_->invokeHandlerWithEventBody(
101
- AudioEvent::RECORDER_ERROR, callbackId, eventPayload);
95
+ audioEventHandlerRegistry_->dispatchEvent(
96
+ AudioEvent::RECORDER_ERROR, callbackId, StringPayload{.name = "message", .reason = message});
102
97
  }
103
98
 
104
99
  } // namespace audioapi
@@ -8,6 +8,7 @@
8
8
  #include <audioapi/utils/TaskOffloader.hpp>
9
9
  #include <atomic>
10
10
  #include <memory>
11
+ #include <mutex>
11
12
  #include <string>
12
13
  #include <vector>
13
14
 
@@ -59,6 +60,7 @@ class AudioRecorderCallback {
59
60
  static constexpr auto RECORDER_CALLBACK_SPSC_WAIT_STRATEGY =
60
61
  channels::spsc::WaitStrategy::ATOMIC_WAIT;
61
62
  static constexpr auto RECORDER_CALLBACK_CHANNEL_CAPACITY = 64;
63
+ std::mutex destructionAudioGuard_; // eliminates race between deconstruction and audio thread
62
64
  };
63
65
 
64
66
  } // namespace audioapi
@@ -0,0 +1,6 @@
1
+ #pragma once
2
+
3
+ #include <cstdint>
4
+ namespace audioapi {
5
+ enum class BufferProcessingDirection : std::int8_t { FORWARD = 1, REVERSE = -1 };
6
+ } // namespace audioapi
@@ -0,0 +1,110 @@
1
+ #include <audioapi/core/utils/buffer/BufferProcessingDirection.h>
2
+ #include <audioapi/core/utils/buffer/BufferProcessorBase.h>
3
+ #include <audioapi/dsp/AudioUtils.hpp>
4
+ #include <audioapi/utils/AudioBuffer.hpp>
5
+ #include <audioapi/utils/Macros.h>
6
+ #include <algorithm>
7
+ #include <cstddef>
8
+ #include <memory>
9
+
10
+ namespace audioapi {
11
+
12
+ void BufferProcessorBase::process(
13
+ const std::shared_ptr<DSPAudioBuffer> &output,
14
+ size_t writeIndex,
15
+ size_t framesLeft,
16
+ double rate,
17
+ bool interpolate) {
18
+ setProcessingDirection(rate);
19
+ if (interpolate) {
20
+ renderInterpolated(output, writeIndex, framesLeft, rate);
21
+ } else {
22
+ renderBlock(output, writeIndex, framesLeft, rate);
23
+ }
24
+ }
25
+
26
+ void BufferProcessorBase::setProcessingDirection(double rate) {
27
+ direction_ = rate > 0.0 ? BufferProcessingDirection::FORWARD : BufferProcessingDirection::REVERSE;
28
+ }
29
+
30
+ bool BufferProcessorBase::shouldProcessFurther() {
31
+ if (!atBoundary()) {
32
+ return true; // Keep going
33
+ }
34
+
35
+ handleBoundary();
36
+
37
+ // Check if we should loop
38
+ return !shouldStop();
39
+ }
40
+
41
+ void BufferProcessorBase::renderBlock(
42
+ const std::shared_ptr<DSPAudioBuffer> &output,
43
+ size_t writeIndex,
44
+ size_t framesLeft,
45
+ double rate) {
46
+ while (framesLeft > 0) {
47
+ const size_t toCopy = std::min(remainingInContiguousBlock(), framesLeft);
48
+
49
+ if (toCopy > 0) {
50
+ const AudioBuffer *buffer = getBuffer().get();
51
+ const size_t readIndex = currentIndex();
52
+
53
+ if (direction_ == BufferProcessingDirection::REVERSE) {
54
+ for (size_t ch = 0; ch < output->getNumberOfChannels(); ++ch) {
55
+ output->getChannel(ch)->copyReverse(
56
+ *buffer->getChannel(ch), readIndex, writeIndex, toCopy);
57
+ }
58
+ } else {
59
+ output->copy(*buffer, readIndex, writeIndex, toCopy);
60
+ }
61
+
62
+ consume(toCopy);
63
+ writeIndex += toCopy;
64
+ framesLeft -= toCopy;
65
+ }
66
+
67
+ if (!shouldProcessFurther()) {
68
+ output->zero(writeIndex, framesLeft);
69
+ break;
70
+ }
71
+ }
72
+ }
73
+
74
+ void BufferProcessorBase::renderInterpolated(
75
+ const std::shared_ptr<DSPAudioBuffer> &output,
76
+ size_t writeIndex,
77
+ size_t framesLeft,
78
+ double rate) {
79
+ const size_t numChannels = output->getNumberOfChannels();
80
+ for (size_t i = 0; i < framesLeft; ++i) {
81
+ const CursorState state = advance(rate);
82
+ if (state.atEndOfBuffer) {
83
+ output->zero(writeIndex, framesLeft - i);
84
+ return;
85
+ }
86
+
87
+ const AudioBuffer *currentBuffer = getBuffer().get();
88
+ for (size_t ch = 0; ch < numChannels; ++ch) {
89
+ auto destination = output->getChannel(ch)->span();
90
+ auto source = currentBuffer->getChannel(ch)->span();
91
+ if (state.isCrossBuffer) {
92
+ auto nextSource = getNextBuffer()->getChannel(ch)->span();
93
+ const float currentSample = source[state.index];
94
+ const float nextSample = nextSource[state.nextIndex];
95
+ destination[writeIndex] = currentSample + state.factor * (nextSample - currentSample);
96
+ } else {
97
+ destination[writeIndex] =
98
+ dsp::linearInterpolate(source, state.index, state.nextIndex, state.factor);
99
+ }
100
+ }
101
+
102
+ writeIndex++;
103
+
104
+ if (!shouldProcessFurther()) {
105
+ output->zero(writeIndex, framesLeft - i - 1);
106
+ return;
107
+ }
108
+ }
109
+ }
110
+ }; // namespace audioapi
@@ -0,0 +1,75 @@
1
+ #pragma once
2
+
3
+ #include <audioapi/core/utils/buffer/BufferProcessingDirection.h>
4
+ #include <audioapi/dsp/AudioUtils.hpp>
5
+ #include <audioapi/utils/AudioBuffer.hpp>
6
+ #include <audioapi/utils/Macros.h>
7
+ #include <cstddef>
8
+ #include <memory>
9
+
10
+ namespace audioapi {
11
+
12
+ struct CursorState {
13
+ size_t index = 0;
14
+ size_t nextIndex = 0;
15
+ float factor = 0.0f;
16
+ bool atEndOfBuffer = false;
17
+ bool isCrossBuffer = false;
18
+ };
19
+
20
+ class BufferProcessorBase {
21
+ public:
22
+ virtual ~BufferProcessorBase() = default;
23
+ DELETE_COPY_AND_MOVE(BufferProcessorBase);
24
+
25
+ void process(
26
+ const std::shared_ptr<DSPAudioBuffer> &output,
27
+ size_t writeIndex,
28
+ size_t framesLeft,
29
+ double rate,
30
+ bool interpolate);
31
+
32
+ [[nodiscard]] double getPosition() const {
33
+ return position_;
34
+ }
35
+
36
+ void setPosition(double position) {
37
+ position_ = position;
38
+ }
39
+
40
+ [[nodiscard]] virtual bool atBoundary() const = 0;
41
+ [[nodiscard]] virtual bool shouldStop() const = 0;
42
+
43
+ protected:
44
+ BufferProcessorBase() = default;
45
+
46
+ virtual CursorState advance(double rate) = 0;
47
+ virtual void consume(size_t frames) = 0;
48
+ [[nodiscard]] virtual size_t remainingInContiguousBlock() const = 0;
49
+ [[nodiscard]] virtual size_t currentIndex() const = 0;
50
+ [[nodiscard]] virtual std::shared_ptr<const AudioBuffer> getBuffer() const = 0;
51
+ [[nodiscard]] virtual std::shared_ptr<const AudioBuffer> getNextBuffer() const = 0;
52
+ virtual void handleBoundary() = 0;
53
+
54
+ double position_ = 0;
55
+ BufferProcessingDirection direction_ = BufferProcessingDirection::FORWARD;
56
+
57
+ private:
58
+ void setProcessingDirection(double rate);
59
+
60
+ void renderBlock(
61
+ const std::shared_ptr<DSPAudioBuffer> &output,
62
+ size_t writeIndex,
63
+ size_t framesLeft,
64
+ double rate);
65
+
66
+ void renderInterpolated(
67
+ const std::shared_ptr<DSPAudioBuffer> &output,
68
+ size_t writeIndex,
69
+ size_t framesLeft,
70
+ double rate);
71
+
72
+ [[nodiscard]] bool shouldProcessFurther();
73
+ };
74
+
75
+ } // namespace audioapi
@@ -0,0 +1,129 @@
1
+ #include <audioapi/core/utils/buffer/QueueBufferProcessor.h>
2
+
3
+ #include <cstddef>
4
+ #include <iterator>
5
+ #include <list>
6
+ #include <memory>
7
+ #include <utility>
8
+
9
+ namespace audioapi {
10
+
11
+ QueueBufferProcessor::QueueBufferProcessor(
12
+ std::list<std::pair<size_t, std::shared_ptr<AudioBuffer>>> *buffers,
13
+ OnBufferConsumed onBufferConsumed)
14
+ : buffers_(buffers), onBufferConsumed_(std::move(onBufferConsumed)) {}
15
+
16
+ CursorState QueueBufferProcessor::advance(double rate) {
17
+ if (buffers_->empty()) {
18
+ return {.atEndOfBuffer = true};
19
+ }
20
+
21
+ const size_t bufferSize = buffers_->front().second->getSize();
22
+ const double currentPos = position_;
23
+ const auto index = static_cast<size_t>(currentPos);
24
+ const auto factor = static_cast<float>(currentPos - static_cast<double>(index));
25
+
26
+ size_t nextIndex = index + 1;
27
+ bool isCrossBuffer = false;
28
+ bool atEnd = false;
29
+
30
+ if (nextIndex >= bufferSize) {
31
+ const bool hasFollowUp =
32
+ std::next(buffers_->begin()) != buffers_->end() || pendingTailBuffer_ != nullptr;
33
+
34
+ if (hasFollowUp) {
35
+ nextIndex = 0;
36
+ isCrossBuffer = true;
37
+ } else {
38
+ nextIndex = index;
39
+ atEnd = (currentPos + rate) >= static_cast<double>(bufferSize);
40
+ }
41
+ }
42
+
43
+ position_ += rate;
44
+
45
+ return {
46
+ .index = index,
47
+ .nextIndex = nextIndex,
48
+ .factor = factor,
49
+ .atEndOfBuffer = atEnd,
50
+ .isCrossBuffer = isCrossBuffer};
51
+ }
52
+
53
+ void QueueBufferProcessor::consume(size_t frames) {
54
+ position_ += static_cast<double>(frames);
55
+ }
56
+
57
+ size_t QueueBufferProcessor::remainingInContiguousBlock() const {
58
+ if (buffers_->empty()) {
59
+ return 0;
60
+ }
61
+ const size_t size = buffers_->front().second->getSize();
62
+ const size_t pos = currentIndex();
63
+ return pos < size ? size - pos : 0;
64
+ }
65
+
66
+ size_t QueueBufferProcessor::currentIndex() const {
67
+ return static_cast<size_t>(position_);
68
+ }
69
+
70
+ std::shared_ptr<const AudioBuffer> QueueBufferProcessor::getBuffer() const {
71
+ if (buffers_->empty()) {
72
+ return nullptr;
73
+ }
74
+ return buffers_->front().second;
75
+ }
76
+
77
+ std::shared_ptr<const AudioBuffer> QueueBufferProcessor::getNextBuffer() const {
78
+ if (buffers_->empty()) {
79
+ return pendingTailBuffer_;
80
+ }
81
+ auto it = std::next(buffers_->begin());
82
+ if (it != buffers_->end()) {
83
+ return it->second;
84
+ }
85
+ if (pendingTailBuffer_ != nullptr) {
86
+ return pendingTailBuffer_;
87
+ }
88
+ return buffers_->front().second;
89
+ }
90
+
91
+ bool QueueBufferProcessor::atBoundary() const {
92
+ if (buffers_->empty()) {
93
+ return true;
94
+ }
95
+ return position_ >= static_cast<double>(buffers_->front().second->getSize());
96
+ }
97
+
98
+ bool QueueBufferProcessor::shouldStop() const {
99
+ return buffers_->empty();
100
+ }
101
+
102
+ void QueueBufferProcessor::handleBoundary() {
103
+ if (buffers_->empty()) {
104
+ return;
105
+ }
106
+
107
+ auto bufferId = buffers_->front().first;
108
+ auto buffer = std::move(buffers_->front().second);
109
+ const auto consumedSize = static_cast<double>(buffer->getSize());
110
+ buffers_->pop_front();
111
+ position_ -= consumedSize;
112
+
113
+ bool queueEmptyAfterPop = buffers_->empty();
114
+ const bool willAppendTail = queueEmptyAfterPop && pendingTailBuffer_ != nullptr;
115
+ bool fireBufferEndedEvent = !willAppendTail;
116
+
117
+ if (onBufferConsumed_) {
118
+ onBufferConsumed_(bufferId, buffer, queueEmptyAfterPop, fireBufferEndedEvent);
119
+ }
120
+
121
+ if (willAppendTail) {
122
+ // Tail reuses the last real bufferId so the final onBufferEnded carries it.
123
+ buffers_->emplace_back(bufferId, std::move(pendingTailBuffer_));
124
+ pendingTailBuffer_ = nullptr;
125
+ tailConsumed_ = true;
126
+ }
127
+ }
128
+
129
+ } // namespace audioapi
@@ -0,0 +1,55 @@
1
+ #pragma once
2
+
3
+ #include <audioapi/core/utils/buffer/BufferProcessorBase.h>
4
+ #include <audioapi/utils/AudioBuffer.hpp>
5
+
6
+ #include <audioapi/utils/FatFunction.hpp>
7
+ #include <cstddef>
8
+ #include <list>
9
+ #include <memory>
10
+ #include <utility>
11
+
12
+ namespace audioapi {
13
+
14
+ inline constexpr size_t ON_BUFFER_CONSUMED_CALLBACK_SIZE = 64;
15
+
16
+ using OnBufferConsumed = FatFunction<
17
+ ON_BUFFER_CONSUMED_CALLBACK_SIZE,
18
+ void(size_t &, const std::shared_ptr<AudioBuffer> &, bool &, bool &)>;
19
+
20
+ class QueueBufferProcessor : public BufferProcessorBase {
21
+ public:
22
+ QueueBufferProcessor(
23
+ std::list<std::pair<size_t, std::shared_ptr<AudioBuffer>>> *buffers,
24
+ OnBufferConsumed onBufferConsumed = {});
25
+
26
+ /// Arm an in-place tail buffer. When the main queue would drain during
27
+ /// processing, handleBoundary() appends this tail instead of stopping.
28
+ void setPendingTail(std::shared_ptr<AudioBuffer> tailBuffer) {
29
+ pendingTailBuffer_ = std::move(tailBuffer);
30
+ }
31
+
32
+ [[nodiscard]] bool didConsumeTail() const {
33
+ return tailConsumed_;
34
+ }
35
+
36
+ [[nodiscard]] bool atBoundary() const override;
37
+ [[nodiscard]] bool shouldStop() const override;
38
+
39
+ protected:
40
+ CursorState advance(double rate) override;
41
+ void consume(size_t frames) override;
42
+ [[nodiscard]] size_t remainingInContiguousBlock() const override;
43
+ [[nodiscard]] size_t currentIndex() const override;
44
+ [[nodiscard]] std::shared_ptr<const AudioBuffer> getBuffer() const override;
45
+ [[nodiscard]] std::shared_ptr<const AudioBuffer> getNextBuffer() const override;
46
+ void handleBoundary() override;
47
+
48
+ private:
49
+ std::list<std::pair<size_t, std::shared_ptr<AudioBuffer>>> *buffers_;
50
+ OnBufferConsumed onBufferConsumed_;
51
+ std::shared_ptr<AudioBuffer> pendingTailBuffer_ = nullptr;
52
+ bool tailConsumed_ = false;
53
+ };
54
+
55
+ } // namespace audioapi