react-native-audio-api 0.6.2-rc.6 → 0.6.3-rc.1
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.
- package/android/src/main/java/com/swmansion/audioapi/system/LockScreenManager.kt +25 -0
- package/android/src/main/java/com/swmansion/audioapi/system/MediaSessionCallback.kt +13 -0
- package/common/cpp/audioapi/HostObjects/AudioBufferBaseSourceNodeHostObject.h +60 -0
- package/common/cpp/audioapi/HostObjects/AudioBufferQueueSourceNodeHostObject.h +5 -67
- package/common/cpp/audioapi/HostObjects/AudioBufferSourceNodeHostObject.h +26 -56
- package/common/cpp/audioapi/HostObjects/BaseAudioContextHostObject.h +9 -0
- package/common/cpp/audioapi/HostObjects/CustomProcessorNodeHostObject.h +54 -0
- package/common/cpp/audioapi/core/BaseAudioContext.cpp +9 -0
- package/common/cpp/audioapi/core/BaseAudioContext.h +2 -0
- package/common/cpp/audioapi/core/effects/CustomProcessorNode.cpp +173 -0
- package/common/cpp/audioapi/core/effects/CustomProcessorNode.h +186 -0
- package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.cpp +106 -0
- package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +54 -0
- package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +11 -129
- package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +8 -37
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +5 -93
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +4 -30
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +8 -12
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.h +2 -3
- package/common/cpp/audioapi/core/sources/OscillatorNode.cpp +0 -5
- package/common/cpp/audioapi/core/sources/OscillatorNode.h +0 -1
- package/common/cpp/audioapi/core/utils/AudioNodeDestructor.cpp +3 -1
- package/common/cpp/audioapi/core/utils/AudioNodeManager.cpp +1 -1
- package/common/cpp/audioapi/events/AudioEventHandlerRegistry.cpp +1 -1
- package/ios/audioapi/ios/system/AudioEngine.h +1 -0
- package/ios/audioapi/ios/system/AudioEngine.mm +14 -3
- package/ios/audioapi/ios/system/AudioSessionManager.h +1 -0
- package/ios/audioapi/ios/system/AudioSessionManager.mm +10 -0
- package/ios/audioapi/ios/system/NotificationManager.mm +2 -4
- package/lib/commonjs/api.js +7 -0
- package/lib/commonjs/api.js.map +1 -1
- package/lib/commonjs/core/AudioBufferBaseSourceNode.js +35 -0
- package/lib/commonjs/core/AudioBufferBaseSourceNode.js.map +1 -0
- package/lib/commonjs/core/AudioBufferQueueSourceNode.js +5 -22
- package/lib/commonjs/core/AudioBufferQueueSourceNode.js.map +1 -1
- package/lib/commonjs/core/AudioBufferSourceNode.js +4 -21
- package/lib/commonjs/core/AudioBufferSourceNode.js.map +1 -1
- package/lib/commonjs/core/AudioScheduledSourceNode.js +7 -2
- package/lib/commonjs/core/AudioScheduledSourceNode.js.map +1 -1
- package/lib/commonjs/core/BaseAudioContext.js +4 -0
- package/lib/commonjs/core/BaseAudioContext.js.map +1 -1
- package/lib/commonjs/core/CustomProcessorNode.js +23 -0
- package/lib/commonjs/core/CustomProcessorNode.js.map +1 -0
- package/lib/commonjs/web-core/AudioScheduledSourceNode.js +1 -4
- package/lib/commonjs/web-core/AudioScheduledSourceNode.js.map +1 -1
- package/lib/module/api.js +1 -0
- package/lib/module/api.js.map +1 -1
- package/lib/module/core/AudioBufferBaseSourceNode.js +29 -0
- package/lib/module/core/AudioBufferBaseSourceNode.js.map +1 -0
- package/lib/module/core/AudioBufferQueueSourceNode.js +5 -22
- package/lib/module/core/AudioBufferQueueSourceNode.js.map +1 -1
- package/lib/module/core/AudioBufferSourceNode.js +4 -21
- package/lib/module/core/AudioBufferSourceNode.js.map +1 -1
- package/lib/module/core/AudioScheduledSourceNode.js +7 -2
- package/lib/module/core/AudioScheduledSourceNode.js.map +1 -1
- package/lib/module/core/BaseAudioContext.js +4 -0
- package/lib/module/core/BaseAudioContext.js.map +1 -1
- package/lib/module/core/CustomProcessorNode.js +17 -0
- package/lib/module/core/CustomProcessorNode.js.map +1 -0
- package/lib/module/web-core/AudioScheduledSourceNode.js +1 -4
- package/lib/module/web-core/AudioScheduledSourceNode.js.map +1 -1
- package/lib/typescript/api.d.ts +1 -0
- package/lib/typescript/api.d.ts.map +1 -1
- package/lib/typescript/core/AudioBufferBaseSourceNode.d.ts +14 -0
- package/lib/typescript/core/AudioBufferBaseSourceNode.d.ts.map +1 -0
- package/lib/typescript/core/AudioBufferQueueSourceNode.d.ts +3 -12
- package/lib/typescript/core/AudioBufferQueueSourceNode.d.ts.map +1 -1
- package/lib/typescript/core/AudioBufferSourceNode.d.ts +2 -11
- package/lib/typescript/core/AudioBufferSourceNode.d.ts.map +1 -1
- package/lib/typescript/core/AudioScheduledSourceNode.d.ts +3 -2
- package/lib/typescript/core/AudioScheduledSourceNode.d.ts.map +1 -1
- package/lib/typescript/core/BaseAudioContext.d.ts +2 -0
- package/lib/typescript/core/BaseAudioContext.d.ts.map +1 -1
- package/lib/typescript/core/CustomProcessorNode.d.ts +12 -0
- package/lib/typescript/core/CustomProcessorNode.d.ts.map +1 -0
- package/lib/typescript/events/types.d.ts +2 -7
- package/lib/typescript/events/types.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +16 -13
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/lib/typescript/types.d.ts +1 -0
- package/lib/typescript/types.d.ts.map +1 -1
- package/lib/typescript/web-core/AudioScheduledSourceNode.d.ts +2 -1
- package/lib/typescript/web-core/AudioScheduledSourceNode.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/api.ts +1 -0
- package/src/core/AudioBufferBaseSourceNode.ts +41 -0
- package/src/core/AudioBufferQueueSourceNode.ts +3 -35
- package/src/core/AudioBufferSourceNode.ts +10 -33
- package/src/core/AudioScheduledSourceNode.ts +12 -5
- package/src/core/BaseAudioContext.ts +8 -0
- package/src/core/CustomProcessorNode.ts +28 -0
- package/src/events/types.ts +2 -8
- package/src/interfaces.ts +25 -23
- package/src/types.ts +2 -0
- package/src/web-core/AudioScheduledSourceNode.tsx +3 -6
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <audioapi/core/AudioNode.h>
|
|
4
|
+
#include <audioapi/core/AudioParam.h>
|
|
5
|
+
#include <memory>
|
|
6
|
+
#include <string>
|
|
7
|
+
#include <cstring>
|
|
8
|
+
#include <functional>
|
|
9
|
+
#include <map>
|
|
10
|
+
#include <unordered_map>
|
|
11
|
+
#include <vector>
|
|
12
|
+
#include <any>
|
|
13
|
+
#include <utility>
|
|
14
|
+
|
|
15
|
+
namespace audioapi {
|
|
16
|
+
|
|
17
|
+
class AudioBus;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @brief Interface for defining custom audio processors that can be attached to nodes.
|
|
21
|
+
*/
|
|
22
|
+
class CustomAudioProcessor {
|
|
23
|
+
public:
|
|
24
|
+
virtual ~CustomAudioProcessor() = default;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @brief Processes audio data in-place.
|
|
28
|
+
* @param channelData Array of float pointers for each channel.
|
|
29
|
+
* @param numChannels Number of audio channels.
|
|
30
|
+
* @param numFrames Number of frames per channel.
|
|
31
|
+
*/
|
|
32
|
+
virtual void processInPlace(float** channelData, int numChannels, int numFrames) {
|
|
33
|
+
// Default: do nothing
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @brief Processes audio data from input to output buffers.
|
|
38
|
+
* Default behavior copies input to output and then calls in-place processing.
|
|
39
|
+
* @param input Input audio buffers.
|
|
40
|
+
* @param output Output audio buffers.
|
|
41
|
+
* @param numChannels Number of channels.
|
|
42
|
+
* @param numFrames Number of frames per channel.
|
|
43
|
+
*/
|
|
44
|
+
virtual void processThrough(float** input, float** output, int numChannels, int numFrames) {
|
|
45
|
+
for (int ch = 0; ch < numChannels; ++ch) {
|
|
46
|
+
std::memcpy(output[ch], input[ch], sizeof(float) * numFrames);
|
|
47
|
+
}
|
|
48
|
+
processInPlace(output, numChannels, numFrames);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @brief Audio node that wraps a user-defined processor for custom audio processing.
|
|
54
|
+
*/
|
|
55
|
+
class CustomProcessorNode : public AudioNode {
|
|
56
|
+
public:
|
|
57
|
+
/**
|
|
58
|
+
* @brief Enumerates supported audio processing modes.
|
|
59
|
+
*/
|
|
60
|
+
enum class ProcessorMode {
|
|
61
|
+
ProcessInPlace,
|
|
62
|
+
ProcessThrough
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @brief Constructs a new CustomProcessorNode attached to a given context with a processor identifier.
|
|
67
|
+
* @param context Audio context this node belongs to.
|
|
68
|
+
* @param identifier The identifier of the processor to instantiate and bind.
|
|
69
|
+
*/
|
|
70
|
+
CustomProcessorNode(BaseAudioContext* context, const std::string& identifier);
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @brief Destroys the node and removes it from active tracking.
|
|
74
|
+
*/
|
|
75
|
+
~CustomProcessorNode();
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* @brief Gets the custom AudioParam associated with this node.
|
|
79
|
+
* @return Shared pointer to an AudioParam.
|
|
80
|
+
*/
|
|
81
|
+
std::shared_ptr<AudioParam> getCustomProcessorParam() const;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @brief Gets the processor mode.
|
|
85
|
+
* @return The current processing mode as an enum.
|
|
86
|
+
*/
|
|
87
|
+
ProcessorMode getProcessorMode() const;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @brief Sets the processor mode.
|
|
91
|
+
* @param processorMode Processing mode to apply.
|
|
92
|
+
*/
|
|
93
|
+
void setProcessorMode(ProcessorMode processorMode);
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @brief Registers a factory for creating processors for a given identifier.
|
|
97
|
+
* @param identifier Unique string key.
|
|
98
|
+
* @param factory Function that returns a shared CustomAudioProcessor.
|
|
99
|
+
*/
|
|
100
|
+
static void registerProcessorFactory(
|
|
101
|
+
const std::string& identifier,
|
|
102
|
+
std::function<std::shared_ptr<CustomAudioProcessor>()> factory);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @brief Unregisters a processor factory.
|
|
106
|
+
* @param identifier Key to remove.
|
|
107
|
+
*/
|
|
108
|
+
static void unregisterProcessorFactory(const std::string& identifier);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @brief Alias for control callbacks accepting arbitrary parameters.
|
|
112
|
+
*/
|
|
113
|
+
using GenericControlHandler = std::function<void(const std::vector<std::any>&)>;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @brief Registers a control handler for automation (e.g., gain or filter changes).
|
|
117
|
+
* @param identifier Control name key.
|
|
118
|
+
* @param handler Function to handle dynamic updates.
|
|
119
|
+
*/
|
|
120
|
+
static void registerControlHandler(const std::string& identifier, GenericControlHandler handler);
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* @brief Unregisters a control handler by identifier.
|
|
124
|
+
* @param identifier The key to remove.
|
|
125
|
+
*/
|
|
126
|
+
static void unregisterControlHandler(const std::string& identifier);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @brief Invokes a control handler using variadic arguments.
|
|
130
|
+
* @tparam Args Parameter types.
|
|
131
|
+
* @param identifier Key associated with the control handler.
|
|
132
|
+
* @param args Arguments passed to the handler.
|
|
133
|
+
*/
|
|
134
|
+
template <typename... Args>
|
|
135
|
+
static void applyControlToNode(const std::string& identifier, Args&&... args) {
|
|
136
|
+
auto it = s_controlHandlersByIdentifier.find(identifier);
|
|
137
|
+
if (it != s_controlHandlersByIdentifier.end()) {
|
|
138
|
+
std::vector<std::any> paramList = { std::forward<Args>(args)... };
|
|
139
|
+
it->second(paramList);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
protected:
|
|
144
|
+
/**
|
|
145
|
+
* @brief Core audio processing callback invoked each render cycle.
|
|
146
|
+
* @param processingBus The audio bus containing buffers.
|
|
147
|
+
* @param framesToProcess Number of frames to process.
|
|
148
|
+
*/
|
|
149
|
+
void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
|
|
150
|
+
|
|
151
|
+
private:
|
|
152
|
+
/**
|
|
153
|
+
* @brief In-place audio processing.
|
|
154
|
+
* @param bus The shared audio bus buffer.
|
|
155
|
+
* @param frames Number of frames to process.
|
|
156
|
+
*/
|
|
157
|
+
void processInPlace(const std::shared_ptr<AudioBus>& bus, int frames);
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @brief Processes audio using separate input and output buffers.
|
|
161
|
+
* @param bus Audio bus used for both input and output access.
|
|
162
|
+
* @param frames Number of frames to process.
|
|
163
|
+
*/
|
|
164
|
+
void processThrough(const std::shared_ptr<AudioBus>& bus, int frames);
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @brief Ensures that preallocated output buffers match the required frame and channel dimensions.
|
|
168
|
+
* This avoids real-time heap allocations.
|
|
169
|
+
*/
|
|
170
|
+
void ensurePreallocatedBuffers(int numChannels, int numFrames);
|
|
171
|
+
|
|
172
|
+
std::shared_ptr<AudioParam> customProcessorParam_; ///< Optional real-time modifiable parameter.
|
|
173
|
+
ProcessorMode processorMode_ = ProcessorMode::ProcessInPlace; ///< Determines processing style.
|
|
174
|
+
std::shared_ptr<CustomAudioProcessor> processor_; ///< Processor instance.
|
|
175
|
+
|
|
176
|
+
std::vector<std::vector<float>> preallocatedOutputBuffers_; ///< Storage for preallocated channel data.
|
|
177
|
+
std::vector<float*> preallocatedOutputPointers_; ///< Pointers to preallocatedOutputBuffers_ for processor API.
|
|
178
|
+
|
|
179
|
+
static void notifyProcessorChanged(const std::string& identifier); ///< Notify and update all nodes with given identifier.
|
|
180
|
+
|
|
181
|
+
static std::map<std::string, std::function<std::shared_ptr<CustomAudioProcessor>()>> s_processorFactoriesByIdentifier; ///< Global registry of processor factories.
|
|
182
|
+
static std::unordered_map<std::string, GenericControlHandler> s_controlHandlersByIdentifier; ///< Global registry of control handlers.
|
|
183
|
+
static std::unordered_map<std::string, std::vector<CustomProcessorNode*>> s_activeNodes; ///< Tracks active node instances by identifier.
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
} // namespace audioapi
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#include <audioapi/core/AudioParam.h>
|
|
2
|
+
#include <audioapi/core/BaseAudioContext.h>
|
|
3
|
+
#include <audioapi/core/Constants.h>
|
|
4
|
+
#include <audioapi/core/sources/AudioBufferBaseSourceNode.h>
|
|
5
|
+
#include <audioapi/events/AudioEventHandlerRegistry.h>
|
|
6
|
+
#include <audioapi/utils/AudioArray.h>
|
|
7
|
+
#include <audioapi/utils/AudioBus.h>
|
|
8
|
+
|
|
9
|
+
namespace audioapi {
|
|
10
|
+
AudioBufferBaseSourceNode::AudioBufferBaseSourceNode(BaseAudioContext *context)
|
|
11
|
+
: AudioScheduledSourceNode(context), vReadIndex_(0.0) {
|
|
12
|
+
onPositionChangedInterval_ = static_cast<int>(context->getSampleRate() * 0.1);
|
|
13
|
+
|
|
14
|
+
detuneParam_ = std::make_shared<AudioParam>(
|
|
15
|
+
0.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context);
|
|
16
|
+
playbackRateParam_ = std::make_shared<AudioParam>(
|
|
17
|
+
1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context);
|
|
18
|
+
|
|
19
|
+
playbackRateBus_ = std::make_shared<AudioBus>(
|
|
20
|
+
RENDER_QUANTUM_SIZE * 3, channelCount_, context_->getSampleRate());
|
|
21
|
+
|
|
22
|
+
stretch_ =
|
|
23
|
+
std::make_shared<signalsmith::stretch::SignalsmithStretch<float>>();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
std::shared_ptr<AudioParam> AudioBufferBaseSourceNode::getDetuneParam() const {
|
|
27
|
+
return detuneParam_;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
std::shared_ptr<AudioParam> AudioBufferBaseSourceNode::getPlaybackRateParam()
|
|
31
|
+
const {
|
|
32
|
+
return playbackRateParam_;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
void AudioBufferBaseSourceNode::setOnPositionChangedCallbackId(
|
|
36
|
+
uint64_t callbackId) {
|
|
37
|
+
onPositionChangedCallbackId_ = callbackId;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
void AudioBufferBaseSourceNode::setOnPositionChangedInterval(int interval) {
|
|
41
|
+
onPositionChangedInterval_ = static_cast<int>(
|
|
42
|
+
context_->getSampleRate() * static_cast<float>(interval) / 1000);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
std::mutex &AudioBufferBaseSourceNode::getBufferLock() {
|
|
46
|
+
return bufferLock_;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
void AudioBufferBaseSourceNode::sendOnPositionChangedEvent() {
|
|
50
|
+
if (onPositionChangedCallbackId_ != 0 &&
|
|
51
|
+
onPositionChangedTime_ > onPositionChangedInterval_) {
|
|
52
|
+
std::unordered_map<std::string, EventValue> body = {
|
|
53
|
+
{"value", getCurrentPosition()}};
|
|
54
|
+
|
|
55
|
+
context_->audioEventHandlerRegistry_->invokeHandlerWithEventBody(
|
|
56
|
+
"positionChanged", onPositionChangedCallbackId_, body);
|
|
57
|
+
|
|
58
|
+
onPositionChangedTime_ = 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
onPositionChangedTime_ += RENDER_QUANTUM_SIZE;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
void AudioBufferBaseSourceNode::processWithPitchCorrection(
|
|
65
|
+
const std::shared_ptr<AudioBus> &processingBus,
|
|
66
|
+
int framesToProcess) {
|
|
67
|
+
size_t startOffset = 0;
|
|
68
|
+
size_t offsetLength = 0;
|
|
69
|
+
|
|
70
|
+
auto time = context_->getCurrentTime();
|
|
71
|
+
auto playbackRate = std::clamp(
|
|
72
|
+
playbackRateParam_->processKRateParam(framesToProcess, time), 0.0f, 3.0f);
|
|
73
|
+
auto detune = std::clamp(
|
|
74
|
+
detuneParam_->processKRateParam(framesToProcess, time) / 100.0f,
|
|
75
|
+
-12.0f,
|
|
76
|
+
12.0f);
|
|
77
|
+
|
|
78
|
+
playbackRateBus_->zero();
|
|
79
|
+
|
|
80
|
+
auto framesNeededToStretch =
|
|
81
|
+
static_cast<int>(playbackRate * static_cast<float>(framesToProcess));
|
|
82
|
+
|
|
83
|
+
updatePlaybackInfo(
|
|
84
|
+
playbackRateBus_, framesNeededToStretch, startOffset, offsetLength);
|
|
85
|
+
|
|
86
|
+
if (playbackRate == 0.0f || (!isPlaying() && !isStopScheduled())) {
|
|
87
|
+
processingBus->zero();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
processWithoutInterpolation(
|
|
92
|
+
playbackRateBus_, startOffset, offsetLength, playbackRate);
|
|
93
|
+
|
|
94
|
+
stretch_->process(
|
|
95
|
+
playbackRateBus_.get()[0],
|
|
96
|
+
framesNeededToStretch,
|
|
97
|
+
processingBus.get()[0],
|
|
98
|
+
framesToProcess);
|
|
99
|
+
|
|
100
|
+
if (detune != 0.0f) {
|
|
101
|
+
stretch_->setTransposeSemitones(detune);
|
|
102
|
+
}
|
|
103
|
+
sendOnPositionChangedEvent();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
} // namespace audioapi
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <audioapi/core/sources/AudioScheduledSourceNode.h>
|
|
4
|
+
#include <audioapi/libs/signalsmith-stretch/signalsmith-stretch.h>
|
|
5
|
+
|
|
6
|
+
#include <memory>
|
|
7
|
+
|
|
8
|
+
namespace audioapi {
|
|
9
|
+
|
|
10
|
+
class AudioBus;
|
|
11
|
+
class AudioParam;
|
|
12
|
+
|
|
13
|
+
class AudioBufferBaseSourceNode : public AudioScheduledSourceNode {
|
|
14
|
+
public:
|
|
15
|
+
explicit AudioBufferBaseSourceNode(BaseAudioContext *context);
|
|
16
|
+
|
|
17
|
+
[[nodiscard]] std::shared_ptr<AudioParam> getDetuneParam() const;
|
|
18
|
+
[[nodiscard]] std::shared_ptr<AudioParam> getPlaybackRateParam() const;
|
|
19
|
+
|
|
20
|
+
void setOnPositionChangedCallbackId(uint64_t callbackId);
|
|
21
|
+
void setOnPositionChangedInterval(int interval);
|
|
22
|
+
|
|
23
|
+
protected:
|
|
24
|
+
std::mutex bufferLock_;
|
|
25
|
+
|
|
26
|
+
// pitch correction
|
|
27
|
+
std::shared_ptr<signalsmith::stretch::SignalsmithStretch<float>> stretch_;
|
|
28
|
+
std::shared_ptr<AudioBus> playbackRateBus_;
|
|
29
|
+
|
|
30
|
+
// k-rate params
|
|
31
|
+
std::shared_ptr<AudioParam> detuneParam_;
|
|
32
|
+
std::shared_ptr<AudioParam> playbackRateParam_;
|
|
33
|
+
|
|
34
|
+
// internal helper
|
|
35
|
+
double vReadIndex_;
|
|
36
|
+
|
|
37
|
+
uint64_t onPositionChangedCallbackId_ = 0; // 0 means no callback
|
|
38
|
+
int onPositionChangedInterval_;
|
|
39
|
+
int onPositionChangedTime_ = 0;
|
|
40
|
+
|
|
41
|
+
std::mutex &getBufferLock();
|
|
42
|
+
virtual double getCurrentPosition() const = 0;
|
|
43
|
+
|
|
44
|
+
void sendOnPositionChangedEvent();
|
|
45
|
+
void processWithPitchCorrection(const std::shared_ptr<AudioBus> &processingBus,
|
|
46
|
+
int framesToProcess);
|
|
47
|
+
virtual void processWithoutInterpolation(
|
|
48
|
+
const std::shared_ptr<AudioBus>& processingBus,
|
|
49
|
+
size_t startOffset,
|
|
50
|
+
size_t offsetLength,
|
|
51
|
+
float playbackRate) = 0;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
} // namespace audioapi
|
|
@@ -12,23 +12,10 @@ namespace audioapi {
|
|
|
12
12
|
|
|
13
13
|
AudioBufferQueueSourceNode::AudioBufferQueueSourceNode(
|
|
14
14
|
BaseAudioContext *context)
|
|
15
|
-
:
|
|
15
|
+
: AudioBufferBaseSourceNode(context) {
|
|
16
16
|
buffers_ = {};
|
|
17
|
-
|
|
18
|
-
detuneParam_ = std::make_shared<AudioParam>(
|
|
19
|
-
0.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context);
|
|
20
|
-
playbackRateParam_ = std::make_shared<AudioParam>(
|
|
21
|
-
1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT, context);
|
|
22
|
-
|
|
23
|
-
playbackRateBus_ = std::make_shared<AudioBus>(
|
|
24
|
-
RENDER_QUANTUM_SIZE * 3, channelCount_, context_->getSampleRate());
|
|
25
|
-
|
|
26
|
-
stretch_ =
|
|
27
|
-
std::make_shared<signalsmith::stretch::SignalsmithStretch<float>>();
|
|
28
17
|
stretch_->presetDefault(channelCount_, context_->getSampleRate(), true);
|
|
29
18
|
|
|
30
|
-
onPositionChangedInterval_ = static_cast<int>(context_->getSampleRate() / 10);
|
|
31
|
-
|
|
32
19
|
isInitialized_ = true;
|
|
33
20
|
}
|
|
34
21
|
|
|
@@ -38,23 +25,6 @@ AudioBufferQueueSourceNode::~AudioBufferQueueSourceNode() {
|
|
|
38
25
|
buffers_ = {};
|
|
39
26
|
}
|
|
40
27
|
|
|
41
|
-
std::shared_ptr<AudioParam> AudioBufferQueueSourceNode::getDetuneParam() const {
|
|
42
|
-
return detuneParam_;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
std::shared_ptr<AudioParam> AudioBufferQueueSourceNode::getPlaybackRateParam()
|
|
46
|
-
const {
|
|
47
|
-
return playbackRateParam_;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
void AudioBufferQueueSourceNode::start(double when, double offset) {
|
|
51
|
-
AudioScheduledSourceNode::start(when);
|
|
52
|
-
|
|
53
|
-
if (offset >= 0.0) {
|
|
54
|
-
vReadIndex_ = static_cast<double>(context_->getSampleRate() * offset);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
28
|
void AudioBufferQueueSourceNode::stop(double when) {
|
|
59
29
|
AudioScheduledSourceNode::stop(when);
|
|
60
30
|
isPaused_ = false;
|
|
@@ -67,10 +37,9 @@ void AudioBufferQueueSourceNode::pause() {
|
|
|
67
37
|
|
|
68
38
|
void AudioBufferQueueSourceNode::enqueueBuffer(
|
|
69
39
|
const std::shared_ptr<AudioBuffer> &buffer,
|
|
70
|
-
int bufferId,
|
|
71
40
|
bool isLastBuffer) {
|
|
72
41
|
auto locker = Locker(getBufferLock());
|
|
73
|
-
buffers_.emplace(
|
|
42
|
+
buffers_.emplace(buffer);
|
|
74
43
|
|
|
75
44
|
isLastBuffer_ = isLastBuffer;
|
|
76
45
|
}
|
|
@@ -84,28 +53,10 @@ void AudioBufferQueueSourceNode::disable() {
|
|
|
84
53
|
return;
|
|
85
54
|
}
|
|
86
55
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
std::string state = "stopped";
|
|
90
|
-
|
|
91
|
-
// if it has not been stopped, it is ended
|
|
92
|
-
if (stopTime_ < 0) {
|
|
93
|
-
state = "ended";
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
std::unordered_map<std::string, EventValue> body = {
|
|
97
|
-
{"value", getStopTime()}, {"state", state}, {"bufferId", bufferId_}};
|
|
98
|
-
|
|
99
|
-
context_->audioEventHandlerRegistry_->invokeHandlerWithEventBody(
|
|
100
|
-
"ended", onEndedCallbackId_, body);
|
|
101
|
-
|
|
56
|
+
AudioScheduledSourceNode::disable();
|
|
102
57
|
buffers_ = {};
|
|
103
58
|
}
|
|
104
59
|
|
|
105
|
-
std::mutex &AudioBufferQueueSourceNode::getBufferLock() {
|
|
106
|
-
return bufferLock_;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
60
|
void AudioBufferQueueSourceNode::processNode(
|
|
110
61
|
const std::shared_ptr<AudioBus> &processingBus,
|
|
111
62
|
int framesToProcess) {
|
|
@@ -124,92 +75,25 @@ void AudioBufferQueueSourceNode::processNode(
|
|
|
124
75
|
}
|
|
125
76
|
}
|
|
126
77
|
|
|
127
|
-
double AudioBufferQueueSourceNode::
|
|
78
|
+
double AudioBufferQueueSourceNode::getCurrentPosition() const {
|
|
128
79
|
return dsp::sampleFrameToTime(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
void AudioBufferQueueSourceNode::setOnPositionChangedCallbackId(
|
|
133
|
-
uint64_t callbackId) {
|
|
134
|
-
onPositionChangedCallbackId_ = callbackId;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
void AudioBufferQueueSourceNode::sendOnPositionChangedEvent() {
|
|
138
|
-
if (onPositionChangedTime_ > onPositionChangedInterval_) {
|
|
139
|
-
std::unordered_map<std::string, EventValue> body = {
|
|
140
|
-
{"value", position_ + getStopTime()}};
|
|
141
|
-
|
|
142
|
-
context_->audioEventHandlerRegistry_->invokeHandlerWithEventBody(
|
|
143
|
-
"positionChanged", onPositionChangedCallbackId_, body);
|
|
144
|
-
|
|
145
|
-
onPositionChangedTime_ = 0;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
onPositionChangedTime_ += RENDER_QUANTUM_SIZE;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
void AudioBufferQueueSourceNode::setOnPositionChangedInterval(int interval) {
|
|
152
|
-
onPositionChangedInterval_ = static_cast<int>(
|
|
153
|
-
context_->getSampleRate() * static_cast<float>(interval) / 1000);
|
|
80
|
+
static_cast<int>(vReadIndex_), context_->getSampleRate()) +
|
|
81
|
+
playedBuffersDuration_;
|
|
154
82
|
}
|
|
155
83
|
|
|
156
84
|
/**
|
|
157
85
|
* Helper functions
|
|
158
86
|
*/
|
|
159
87
|
|
|
160
|
-
void AudioBufferQueueSourceNode::processWithPitchCorrection(
|
|
161
|
-
const std::shared_ptr<AudioBus> &processingBus,
|
|
162
|
-
int framesToProcess) {
|
|
163
|
-
size_t startOffset = 0;
|
|
164
|
-
size_t offsetLength = 0;
|
|
165
|
-
|
|
166
|
-
auto time = context_->getCurrentTime();
|
|
167
|
-
auto playbackRate = std::clamp(
|
|
168
|
-
playbackRateParam_->processKRateParam(framesToProcess, time), 0.0f, 3.0f);
|
|
169
|
-
auto detune = std::clamp(
|
|
170
|
-
detuneParam_->processKRateParam(framesToProcess, time) / 100.0f,
|
|
171
|
-
-12.0f,
|
|
172
|
-
12.0f);
|
|
173
|
-
|
|
174
|
-
playbackRateBus_->zero();
|
|
175
|
-
|
|
176
|
-
auto framesNeededToStretch =
|
|
177
|
-
static_cast<int>(playbackRate * static_cast<float>(framesToProcess));
|
|
178
|
-
|
|
179
|
-
updatePlaybackInfo(
|
|
180
|
-
playbackRateBus_, framesNeededToStretch, startOffset, offsetLength);
|
|
181
|
-
|
|
182
|
-
if (playbackRate == 0.0f || (!isPlaying() && !isStopScheduled())) {
|
|
183
|
-
processingBus->zero();
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// Send position changed event
|
|
188
|
-
sendOnPositionChangedEvent();
|
|
189
|
-
|
|
190
|
-
processWithoutInterpolation(playbackRateBus_, startOffset, offsetLength);
|
|
191
|
-
|
|
192
|
-
stretch_->process(
|
|
193
|
-
playbackRateBus_.get()[0],
|
|
194
|
-
framesNeededToStretch,
|
|
195
|
-
processingBus.get()[0],
|
|
196
|
-
framesToProcess);
|
|
197
|
-
|
|
198
|
-
if (detune != 0.0f) {
|
|
199
|
-
stretch_->setTransposeSemitones(detune);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
88
|
void AudioBufferQueueSourceNode::processWithoutInterpolation(
|
|
204
89
|
const std::shared_ptr<AudioBus> &processingBus,
|
|
205
90
|
size_t startOffset,
|
|
206
|
-
size_t offsetLength
|
|
91
|
+
size_t offsetLength,
|
|
92
|
+
float playbackRate) {
|
|
207
93
|
auto readIndex = static_cast<size_t>(vReadIndex_);
|
|
208
94
|
size_t writeIndex = startOffset;
|
|
209
95
|
|
|
210
|
-
auto
|
|
211
|
-
bufferId_ = queueData.first;
|
|
212
|
-
auto buffer = queueData.second;
|
|
96
|
+
auto buffer = buffers_.front();
|
|
213
97
|
|
|
214
98
|
size_t framesLeft = offsetLength;
|
|
215
99
|
|
|
@@ -230,7 +114,7 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation(
|
|
|
230
114
|
framesLeft -= framesToCopy;
|
|
231
115
|
|
|
232
116
|
if (readIndex >= buffer->getLength()) {
|
|
233
|
-
|
|
117
|
+
playedBuffersDuration_ += buffer->getDuration();
|
|
234
118
|
buffers_.pop();
|
|
235
119
|
|
|
236
120
|
if (buffers_.empty()) {
|
|
@@ -242,9 +126,7 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation(
|
|
|
242
126
|
}
|
|
243
127
|
break;
|
|
244
128
|
} else {
|
|
245
|
-
|
|
246
|
-
bufferId_ = queueData.first;
|
|
247
|
-
buffer = queueData.second;
|
|
129
|
+
buffer = buffers_.front();
|
|
248
130
|
|
|
249
131
|
readIndex = 0;
|
|
250
132
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
3
|
#include <audioapi/core/sources/AudioBuffer.h>
|
|
4
|
-
#include <audioapi/core/sources/
|
|
4
|
+
#include <audioapi/core/sources/AudioBufferBaseSourceNode.h>
|
|
5
5
|
#include <audioapi/libs/signalsmith-stretch/signalsmith-stretch.h>
|
|
6
6
|
|
|
7
7
|
#include <memory>
|
|
@@ -15,63 +15,34 @@ namespace audioapi {
|
|
|
15
15
|
class AudioBus;
|
|
16
16
|
class AudioParam;
|
|
17
17
|
|
|
18
|
-
class AudioBufferQueueSourceNode : public
|
|
18
|
+
class AudioBufferQueueSourceNode : public AudioBufferBaseSourceNode {
|
|
19
19
|
public:
|
|
20
20
|
explicit AudioBufferQueueSourceNode(BaseAudioContext *context);
|
|
21
21
|
~AudioBufferQueueSourceNode() override;
|
|
22
22
|
|
|
23
|
-
[[nodiscard]] std::shared_ptr<AudioParam> getDetuneParam() const;
|
|
24
|
-
[[nodiscard]] std::shared_ptr<AudioParam> getPlaybackRateParam() const;
|
|
25
|
-
|
|
26
|
-
void start(double when, double offset);
|
|
27
23
|
void stop(double when) override;
|
|
28
24
|
void pause();
|
|
29
25
|
|
|
30
|
-
void enqueueBuffer(const std::shared_ptr<AudioBuffer> &buffer,
|
|
26
|
+
void enqueueBuffer(const std::shared_ptr<AudioBuffer> &buffer, bool isLastBuffer);
|
|
31
27
|
void disable() override;
|
|
32
28
|
|
|
33
|
-
void setOnPositionChangedCallbackId(uint64_t callbackId);
|
|
34
|
-
void setOnPositionChangedInterval(int interval);
|
|
35
|
-
void sendOnPositionChangedEvent();
|
|
36
|
-
|
|
37
29
|
protected:
|
|
38
|
-
std::mutex &getBufferLock();
|
|
39
30
|
void processNode(const std::shared_ptr<AudioBus>& processingBus, int framesToProcess) override;
|
|
40
|
-
double
|
|
31
|
+
double getCurrentPosition() const override;
|
|
41
32
|
|
|
42
33
|
private:
|
|
43
|
-
std::mutex bufferLock_;
|
|
44
|
-
|
|
45
|
-
// pitch correction
|
|
46
|
-
std::shared_ptr<signalsmith::stretch::SignalsmithStretch<float>> stretch_;
|
|
47
|
-
std::shared_ptr<AudioBus> playbackRateBus_;
|
|
48
|
-
|
|
49
|
-
// k-rate params
|
|
50
|
-
std::shared_ptr<AudioParam> detuneParam_;
|
|
51
|
-
std::shared_ptr<AudioParam> playbackRateParam_;
|
|
52
|
-
|
|
53
|
-
// internal helper
|
|
54
|
-
double vReadIndex_;
|
|
55
|
-
|
|
56
34
|
// User provided buffers
|
|
57
|
-
std::queue<std::
|
|
58
|
-
int bufferId_ = 0;
|
|
35
|
+
std::queue<std::shared_ptr<AudioBuffer>> buffers_;
|
|
59
36
|
bool isLastBuffer_ = false;
|
|
60
37
|
bool isPaused_ = false;
|
|
61
38
|
|
|
62
|
-
|
|
63
|
-
uint64_t onPositionChangedCallbackId_ = 0;
|
|
64
|
-
int onPositionChangedInterval_;
|
|
65
|
-
int onPositionChangedTime_ = 0;
|
|
66
|
-
double position_ = 0;
|
|
67
|
-
|
|
68
|
-
void processWithPitchCorrection(const std::shared_ptr<AudioBus> &processingBus,
|
|
69
|
-
int framesToProcess);
|
|
39
|
+
double playedBuffersDuration_ = 0;
|
|
70
40
|
|
|
71
41
|
void processWithoutInterpolation(
|
|
72
42
|
const std::shared_ptr<AudioBus>& processingBus,
|
|
73
43
|
size_t startOffset,
|
|
74
|
-
size_t offsetLength
|
|
44
|
+
size_t offsetLength,
|
|
45
|
+
float playbackRate) override;
|
|
75
46
|
};
|
|
76
47
|
|
|
77
48
|
} // namespace audioapi
|