react-native-audio-api 0.6.2-rc.6 → 0.6.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.
- 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 +104 -0
- package/common/cpp/audioapi/core/sources/AudioBufferBaseSourceNode.h +54 -0
- package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.cpp +12 -129
- package/common/cpp/audioapi/core/sources/AudioBufferQueueSourceNode.h +8 -37
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.cpp +4 -93
- package/common/cpp/audioapi/core/sources/AudioBufferSourceNode.h +4 -30
- package/common/cpp/audioapi/core/sources/AudioScheduledSourceNode.cpp +1 -11
- 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/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 +29 -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.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 +23 -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.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 +13 -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 +2 -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 +33 -0
- package/src/core/AudioBufferQueueSourceNode.ts +3 -35
- package/src/core/AudioBufferSourceNode.ts +10 -33
- package/src/core/AudioScheduledSourceNode.ts +2 -2
- 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,104 @@
|
|
|
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 (onPositionChangedTime_ > onPositionChangedInterval_) {
|
|
51
|
+
std::unordered_map<std::string, EventValue> body = {
|
|
52
|
+
{"value", getCurrentPosition()}};
|
|
53
|
+
|
|
54
|
+
context_->audioEventHandlerRegistry_->invokeHandlerWithEventBody(
|
|
55
|
+
"positionChanged", onPositionChangedCallbackId_, body);
|
|
56
|
+
|
|
57
|
+
onPositionChangedTime_ = 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
onPositionChangedTime_ += RENDER_QUANTUM_SIZE;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void AudioBufferBaseSourceNode::processWithPitchCorrection(
|
|
64
|
+
const std::shared_ptr<AudioBus> &processingBus,
|
|
65
|
+
int framesToProcess) {
|
|
66
|
+
size_t startOffset = 0;
|
|
67
|
+
size_t offsetLength = 0;
|
|
68
|
+
|
|
69
|
+
auto time = context_->getCurrentTime();
|
|
70
|
+
auto playbackRate = std::clamp(
|
|
71
|
+
playbackRateParam_->processKRateParam(framesToProcess, time), 0.0f, 3.0f);
|
|
72
|
+
auto detune = std::clamp(
|
|
73
|
+
detuneParam_->processKRateParam(framesToProcess, time) / 100.0f,
|
|
74
|
+
-12.0f,
|
|
75
|
+
12.0f);
|
|
76
|
+
|
|
77
|
+
playbackRateBus_->zero();
|
|
78
|
+
|
|
79
|
+
auto framesNeededToStretch =
|
|
80
|
+
static_cast<int>(playbackRate * static_cast<float>(framesToProcess));
|
|
81
|
+
|
|
82
|
+
updatePlaybackInfo(
|
|
83
|
+
playbackRateBus_, framesNeededToStretch, startOffset, offsetLength);
|
|
84
|
+
|
|
85
|
+
if (playbackRate == 0.0f || (!isPlaying() && !isStopScheduled())) {
|
|
86
|
+
processingBus->zero();
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
processWithoutInterpolation(
|
|
91
|
+
playbackRateBus_, startOffset, offsetLength, playbackRate);
|
|
92
|
+
|
|
93
|
+
stretch_->process(
|
|
94
|
+
playbackRateBus_.get()[0],
|
|
95
|
+
framesNeededToStretch,
|
|
96
|
+
processingBus.get()[0],
|
|
97
|
+
framesToProcess);
|
|
98
|
+
|
|
99
|
+
if (detune != 0.0f) {
|
|
100
|
+
stretch_->setTransposeSemitones(detune);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
} // 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;
|
|
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) {
|
|
@@ -119,97 +70,31 @@ void AudioBufferQueueSourceNode::processNode(
|
|
|
119
70
|
processWithPitchCorrection(processingBus, framesToProcess);
|
|
120
71
|
|
|
121
72
|
handleStopScheduled();
|
|
73
|
+
sendOnPositionChangedEvent();
|
|
122
74
|
} else {
|
|
123
75
|
processingBus->zero();
|
|
124
76
|
}
|
|
125
77
|
}
|
|
126
78
|
|
|
127
|
-
double AudioBufferQueueSourceNode::
|
|
79
|
+
double AudioBufferQueueSourceNode::getCurrentPosition() const {
|
|
128
80
|
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);
|
|
81
|
+
static_cast<int>(vReadIndex_), context_->getSampleRate()) +
|
|
82
|
+
playedBuffersDuration_;
|
|
154
83
|
}
|
|
155
84
|
|
|
156
85
|
/**
|
|
157
86
|
* Helper functions
|
|
158
87
|
*/
|
|
159
88
|
|
|
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
89
|
void AudioBufferQueueSourceNode::processWithoutInterpolation(
|
|
204
90
|
const std::shared_ptr<AudioBus> &processingBus,
|
|
205
91
|
size_t startOffset,
|
|
206
|
-
size_t offsetLength
|
|
92
|
+
size_t offsetLength,
|
|
93
|
+
float playbackRate) {
|
|
207
94
|
auto readIndex = static_cast<size_t>(vReadIndex_);
|
|
208
95
|
size_t writeIndex = startOffset;
|
|
209
96
|
|
|
210
|
-
auto
|
|
211
|
-
bufferId_ = queueData.first;
|
|
212
|
-
auto buffer = queueData.second;
|
|
97
|
+
auto buffer = buffers_.front();
|
|
213
98
|
|
|
214
99
|
size_t framesLeft = offsetLength;
|
|
215
100
|
|
|
@@ -230,7 +115,7 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation(
|
|
|
230
115
|
framesLeft -= framesToCopy;
|
|
231
116
|
|
|
232
117
|
if (readIndex >= buffer->getLength()) {
|
|
233
|
-
|
|
118
|
+
playedBuffersDuration_ += buffer->getDuration();
|
|
234
119
|
buffers_.pop();
|
|
235
120
|
|
|
236
121
|
if (buffers_.empty()) {
|
|
@@ -242,9 +127,7 @@ void AudioBufferQueueSourceNode::processWithoutInterpolation(
|
|
|
242
127
|
}
|
|
243
128
|
break;
|
|
244
129
|
} else {
|
|
245
|
-
|
|
246
|
-
bufferId_ = queueData.first;
|
|
247
|
-
buffer = queueData.second;
|
|
130
|
+
buffer = buffers_.front();
|
|
248
131
|
|
|
249
132
|
readIndex = 0;
|
|
250
133
|
}
|
|
@@ -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
|