react-native-audio-api 0.3.0-rc2 → 0.3.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/CMakeLists.txt +2 -2
- package/android/src/main/cpp/AudioAPIInstaller/AudioAPIInstaller.cpp +3 -9
- package/android/src/main/cpp/AudioAPIInstaller/AudioAPIInstaller.h +0 -2
- package/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.h +25 -28
- package/common/cpp/HostObjects/AudioBufferHostObject.h +79 -13
- package/common/cpp/HostObjects/AudioBufferSourceNodeHostObject.h +93 -14
- package/common/cpp/HostObjects/AudioContextHostObject.h +10 -21
- package/common/cpp/HostObjects/AudioDestinationNodeHostObject.h +3 -16
- package/common/cpp/HostObjects/AudioNodeHostObject.h +48 -11
- package/common/cpp/HostObjects/AudioParamHostObject.h +56 -14
- package/common/cpp/HostObjects/AudioScheduledSourceNodeHostObject.h +23 -16
- package/common/cpp/HostObjects/BaseAudioContextHostObject.h +131 -14
- package/common/cpp/HostObjects/BiquadFilterNodeHostObject.h +76 -18
- package/common/cpp/HostObjects/Constants.h +2 -0
- package/common/cpp/HostObjects/GainNodeHostObject.h +10 -15
- package/common/cpp/HostObjects/OscillatorNodeHostObject.h +40 -17
- package/common/cpp/HostObjects/PeriodicWaveHostObject.h +4 -17
- package/common/cpp/HostObjects/StereoPannerNodeHostObject.h +10 -17
- package/common/cpp/core/AudioBufferSourceNode.cpp +180 -73
- package/common/cpp/core/AudioBufferSourceNode.h +41 -1
- package/common/cpp/core/AudioDestinationNode.h +1 -1
- package/common/cpp/core/AudioScheduledSourceNode.cpp +86 -21
- package/common/cpp/core/AudioScheduledSourceNode.h +12 -4
- package/common/cpp/core/OscillatorNode.cpp +7 -2
- package/common/cpp/jsi/JsiHostObject.cpp +85 -0
- package/common/cpp/jsi/JsiHostObject.h +94 -0
- package/common/cpp/{utils → jsi}/JsiPromise.cpp +10 -9
- package/common/cpp/jsi/JsiPromise.h +48 -0
- package/common/cpp/utils/AudioUtils.cpp +26 -0
- package/common/cpp/utils/AudioUtils.h +16 -0
- package/ios/AudioAPIModule.mm +2 -6
- package/ios/AudioDecoder/AudioDecoder.m +9 -10
- package/ios/AudioPlayer/AudioPlayer.m +2 -1
- package/lib/module/core/AudioBufferSourceNode.js +15 -0
- package/lib/module/core/AudioBufferSourceNode.js.map +1 -1
- package/lib/module/index.js +18 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/core/AudioBufferSourceNode.d.ts +7 -0
- package/lib/typescript/core/AudioBufferSourceNode.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +6 -2
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/interfaces.d.ts +4 -0
- package/lib/typescript/interfaces.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/AudioBufferSourceNode.ts +23 -0
- package/src/index.ts +24 -0
- package/src/interfaces.ts +5 -0
- package/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.cpp +0 -58
- package/common/cpp/AudioAPIInstaller/AudioAPIInstallerWrapper.h +0 -38
- package/common/cpp/AudioAPIInstaller/android/AudioAPIInstallerWrapper.cpp +0 -16
- package/common/cpp/AudioAPIInstaller/ios/AudioAPIInstallerWrapper.cpp +0 -12
- package/common/cpp/HostObjects/AudioBufferHostObject.cpp +0 -150
- package/common/cpp/HostObjects/AudioBufferSourceNodeHostObject.cpp +0 -79
- package/common/cpp/HostObjects/AudioContextHostObject.cpp +0 -55
- package/common/cpp/HostObjects/AudioDestinationNodeHostObject.cpp +0 -33
- package/common/cpp/HostObjects/AudioNodeHostObject.cpp +0 -102
- package/common/cpp/HostObjects/AudioParamHostObject.cpp +0 -115
- package/common/cpp/HostObjects/AudioScheduledSourceNodeHostObject.cpp +0 -73
- package/common/cpp/HostObjects/BaseAudioContextHostObject.cpp +0 -250
- package/common/cpp/HostObjects/BiquadFilterNodeHostObject.cpp +0 -125
- package/common/cpp/HostObjects/GainNodeHostObject.cpp +0 -41
- package/common/cpp/HostObjects/OscillatorNodeHostObject.cpp +0 -88
- package/common/cpp/HostObjects/PeriodicWaveHostObject.cpp +0 -33
- package/common/cpp/HostObjects/StereoPannerNodeHostObject.cpp +0 -41
- package/common/cpp/utils/JsiPromise.h +0 -48
- package/common/cpp/wrappers/AudioBufferSourceNodeWrapper.cpp +0 -45
- package/common/cpp/wrappers/AudioBufferSourceNodeWrapper.h +0 -26
- package/common/cpp/wrappers/AudioBufferWrapper.cpp +0 -46
- package/common/cpp/wrappers/AudioBufferWrapper.h +0 -30
- package/common/cpp/wrappers/AudioContextWrapper.cpp +0 -17
- package/common/cpp/wrappers/AudioContextWrapper.h +0 -19
- package/common/cpp/wrappers/AudioDestinationNodeWrapper.h +0 -16
- package/common/cpp/wrappers/AudioNodeWrapper.cpp +0 -37
- package/common/cpp/wrappers/AudioNodeWrapper.h +0 -25
- package/common/cpp/wrappers/AudioParamWrapper.cpp +0 -42
- package/common/cpp/wrappers/AudioParamWrapper.h +0 -25
- package/common/cpp/wrappers/AudioScheduledSourceNodeWrapper.cpp +0 -23
- package/common/cpp/wrappers/AudioScheduledSourceNodeWrapper.h +0 -23
- package/common/cpp/wrappers/BaseAudioContextWrapper.cpp +0 -83
- package/common/cpp/wrappers/BaseAudioContextWrapper.h +0 -51
- package/common/cpp/wrappers/BiquadFilterNodeWrapper.cpp +0 -60
- package/common/cpp/wrappers/BiquadFilterNodeWrapper.h +0 -37
- package/common/cpp/wrappers/GainNodeWrapper.cpp +0 -14
- package/common/cpp/wrappers/GainNodeWrapper.h +0 -20
- package/common/cpp/wrappers/OscillatorNodeWrapper.cpp +0 -44
- package/common/cpp/wrappers/OscillatorNodeWrapper.h +0 -31
- package/common/cpp/wrappers/PeriodicWaveWrapper.h +0 -17
- package/common/cpp/wrappers/StereoPannerNodeWrapper.cpp +0 -16
- package/common/cpp/wrappers/StereoPannerNodeWrapper.h +0 -21
|
@@ -3,14 +3,27 @@
|
|
|
3
3
|
#include "AudioArray.h"
|
|
4
4
|
#include "AudioBufferSourceNode.h"
|
|
5
5
|
#include "AudioBus.h"
|
|
6
|
+
#include "AudioParam.h"
|
|
7
|
+
#include "AudioUtils.h"
|
|
6
8
|
#include "BaseAudioContext.h"
|
|
9
|
+
#include "Constants.h"
|
|
7
10
|
|
|
8
11
|
namespace audioapi {
|
|
9
12
|
|
|
10
13
|
AudioBufferSourceNode::AudioBufferSourceNode(BaseAudioContext *context)
|
|
11
|
-
: AudioScheduledSourceNode(context),
|
|
14
|
+
: AudioScheduledSourceNode(context),
|
|
15
|
+
loop_(false),
|
|
16
|
+
loopStart_(0),
|
|
17
|
+
loopEnd_(0),
|
|
18
|
+
vReadIndex_(0.0) {
|
|
12
19
|
numberOfInputs_ = 0;
|
|
13
20
|
buffer_ = std::shared_ptr<AudioBuffer>(nullptr);
|
|
21
|
+
|
|
22
|
+
detuneParam_ =
|
|
23
|
+
std::make_shared<AudioParam>(context, 0.0, -MAX_DETUNE, MAX_DETUNE);
|
|
24
|
+
playbackRateParam_ = std::make_shared<AudioParam>(
|
|
25
|
+
context, 1.0, MOST_NEGATIVE_SINGLE_FLOAT, MOST_POSITIVE_SINGLE_FLOAT);
|
|
26
|
+
|
|
14
27
|
isInitialized_ = true;
|
|
15
28
|
}
|
|
16
29
|
|
|
@@ -18,6 +31,23 @@ bool AudioBufferSourceNode::getLoop() const {
|
|
|
18
31
|
return loop_;
|
|
19
32
|
}
|
|
20
33
|
|
|
34
|
+
double AudioBufferSourceNode::getLoopStart() const {
|
|
35
|
+
return loopStart_;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
double AudioBufferSourceNode::getLoopEnd() const {
|
|
39
|
+
return loopEnd_;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
std::shared_ptr<AudioParam> AudioBufferSourceNode::getDetuneParam() const {
|
|
43
|
+
return detuneParam_;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
std::shared_ptr<AudioParam> AudioBufferSourceNode::getPlaybackRateParam()
|
|
47
|
+
const {
|
|
48
|
+
return playbackRateParam_;
|
|
49
|
+
}
|
|
50
|
+
|
|
21
51
|
std::shared_ptr<AudioBuffer> AudioBufferSourceNode::getBuffer() const {
|
|
22
52
|
return buffer_;
|
|
23
53
|
}
|
|
@@ -26,120 +56,197 @@ void AudioBufferSourceNode::setLoop(bool loop) {
|
|
|
26
56
|
loop_ = loop;
|
|
27
57
|
}
|
|
28
58
|
|
|
59
|
+
void AudioBufferSourceNode::setLoopStart(double loopStart) {
|
|
60
|
+
loopStart_ = loopStart;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
void AudioBufferSourceNode::setLoopEnd(double loopEnd) {
|
|
64
|
+
loopEnd_ = loopEnd;
|
|
65
|
+
}
|
|
66
|
+
|
|
29
67
|
void AudioBufferSourceNode::setBuffer(
|
|
30
68
|
const std::shared_ptr<AudioBuffer> &buffer) {
|
|
31
69
|
if (!buffer) {
|
|
32
70
|
buffer_ = std::shared_ptr<AudioBuffer>(nullptr);
|
|
71
|
+
alignedBus_ = std::shared_ptr<AudioBus>(nullptr);
|
|
33
72
|
return;
|
|
34
73
|
}
|
|
35
74
|
|
|
36
75
|
buffer_ = buffer;
|
|
76
|
+
alignedBus_ = std::make_shared<AudioBus>(
|
|
77
|
+
context_->getSampleRate(), buffer_->getLength());
|
|
78
|
+
|
|
79
|
+
alignedBus_->zero();
|
|
80
|
+
alignedBus_->sum(buffer_->bus_.get());
|
|
37
81
|
}
|
|
38
82
|
|
|
39
|
-
// Note: AudioBus copy method will use memcpy if the source buffer and system
|
|
40
|
-
// processing bus have same channel count, otherwise it will use the summing
|
|
41
|
-
// function taking care of up/down mixing.
|
|
42
83
|
void AudioBufferSourceNode::processNode(
|
|
43
84
|
AudioBus *processingBus,
|
|
44
85
|
int framesToProcess) {
|
|
86
|
+
size_t startOffset = 0;
|
|
87
|
+
size_t offsetLength = 0;
|
|
88
|
+
|
|
89
|
+
updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength);
|
|
90
|
+
float playbackRate = getPlaybackRateValue(startOffset);
|
|
91
|
+
|
|
45
92
|
// No audio data to fill, zero the output and return.
|
|
46
|
-
if (!isPlaying() || !
|
|
93
|
+
if (!isPlaying() || !alignedBus_ || alignedBus_->getSize() == 0 ||
|
|
94
|
+
!playbackRate) {
|
|
47
95
|
processingBus->zero();
|
|
48
96
|
return;
|
|
49
97
|
}
|
|
50
98
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
99
|
+
if (std::fabs(playbackRate) == 1.0) {
|
|
100
|
+
processWithoutInterpolation(
|
|
101
|
+
processingBus, startOffset, offsetLength, playbackRate);
|
|
102
|
+
} else {
|
|
103
|
+
processWithInterpolation(
|
|
104
|
+
processingBus, startOffset, offsetLength, playbackRate);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
55
107
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
108
|
+
/**
|
|
109
|
+
* Helper functions
|
|
110
|
+
*/
|
|
60
111
|
|
|
61
|
-
|
|
62
|
-
|
|
112
|
+
void AudioBufferSourceNode::processWithoutInterpolation(
|
|
113
|
+
AudioBus *processingBus,
|
|
114
|
+
size_t startOffset,
|
|
115
|
+
size_t offsetLength,
|
|
116
|
+
float playbackRate) {
|
|
117
|
+
size_t direction = playbackRate < 0 ? -1 : 1;
|
|
63
118
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (framesToProcess < buffer_->getLength()) {
|
|
67
|
-
int outputBusIndex = 0;
|
|
68
|
-
int framesToCopy = 0;
|
|
119
|
+
auto readIndex = static_cast<size_t>(vReadIndex_);
|
|
120
|
+
size_t writeIndex = startOffset;
|
|
69
121
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
buffer_->getLength() - bufferIndex_);
|
|
122
|
+
auto frameStart = static_cast<size_t>(getVirtualStartFrame());
|
|
123
|
+
auto frameEnd = static_cast<size_t>(getVirtualEndFrame());
|
|
124
|
+
size_t frameDelta = frameEnd - frameStart;
|
|
74
125
|
|
|
75
|
-
|
|
76
|
-
buffer_->bus_.get(), bufferIndex_, outputBusIndex, framesToCopy);
|
|
126
|
+
size_t framesLeft = offsetLength;
|
|
77
127
|
|
|
78
|
-
|
|
79
|
-
|
|
128
|
+
if (loop_ && (readIndex >= frameEnd || readIndex < frameStart)) {
|
|
129
|
+
readIndex = frameStart + (readIndex - frameStart) % frameDelta;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
while (framesLeft > 0) {
|
|
133
|
+
size_t framesToEnd = frameEnd - readIndex;
|
|
134
|
+
size_t framesToCopy = std::min(framesToEnd, framesLeft);
|
|
135
|
+
framesToCopy = framesToCopy > 0 ? framesToCopy : 0;
|
|
80
136
|
|
|
81
|
-
|
|
82
|
-
|
|
137
|
+
// Direction is forward, we can normally copy the data
|
|
138
|
+
if (direction == 1) {
|
|
139
|
+
processingBus->copy(
|
|
140
|
+
alignedBus_.get(), readIndex, writeIndex, framesToCopy);
|
|
141
|
+
} else {
|
|
142
|
+
for (int i = 0; i < framesToCopy; i += 1) {
|
|
143
|
+
for (int j = 0; j < processingBus->getNumberOfChannels(); j += 1) {
|
|
144
|
+
(*processingBus->getChannel(j))[writeIndex + i] =
|
|
145
|
+
(*alignedBus_->getChannel(j))[readIndex - i];
|
|
146
|
+
}
|
|
83
147
|
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
writeIndex += framesToCopy;
|
|
151
|
+
readIndex += framesToCopy * direction;
|
|
152
|
+
framesLeft -= framesToCopy;
|
|
84
153
|
|
|
85
|
-
|
|
154
|
+
if (readIndex >= frameEnd || readIndex < frameStart) {
|
|
155
|
+
readIndex -= direction * frameDelta;
|
|
86
156
|
|
|
87
157
|
if (!loop_) {
|
|
158
|
+
processingBus->zero(writeIndex, framesLeft);
|
|
88
159
|
playbackState_ = PlaybackState::FINISHED;
|
|
89
160
|
disable();
|
|
90
|
-
|
|
91
|
-
if (framesToProcess - outputBusIndex > 0) {
|
|
92
|
-
processingBus->zero(outputBusIndex, framesToProcess - outputBusIndex);
|
|
93
|
-
}
|
|
161
|
+
break;
|
|
94
162
|
}
|
|
95
163
|
}
|
|
96
|
-
|
|
97
|
-
return;
|
|
98
164
|
}
|
|
99
165
|
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
processingBus
|
|
106
|
-
|
|
166
|
+
// update reading index for next render quantum
|
|
167
|
+
vReadIndex_ = readIndex;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
void AudioBufferSourceNode::processWithInterpolation(
|
|
171
|
+
AudioBus *processingBus,
|
|
172
|
+
size_t startOffset,
|
|
173
|
+
size_t offsetLength,
|
|
174
|
+
float playbackRate) {
|
|
175
|
+
size_t direction = playbackRate < 0 ? -1 : 1;
|
|
107
176
|
|
|
108
|
-
|
|
109
|
-
disable();
|
|
177
|
+
size_t writeIndex = startOffset;
|
|
110
178
|
|
|
111
|
-
|
|
112
|
-
|
|
179
|
+
double vFrameStart = getVirtualStartFrame();
|
|
180
|
+
double vFrameEnd = getVirtualEndFrame();
|
|
181
|
+
double vFrameDelta = vFrameEnd - vFrameStart;
|
|
113
182
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
// the loop, which will also carry over some buffer frames to the next render
|
|
117
|
-
// quantum.
|
|
118
|
-
int processingBusPosition = 0;
|
|
119
|
-
int bufferSize = buffer_->getLength();
|
|
120
|
-
int remainingFrames = framesToProcess - framesToProcess / bufferSize;
|
|
121
|
-
|
|
122
|
-
// Do we have some frames left in the buffer from the previous render quantum,
|
|
123
|
-
// if yes copy them over and reset the buffer position.
|
|
124
|
-
if (bufferIndex_ > 0) {
|
|
125
|
-
processingBus->copy(buffer_->bus_.get(), 0, bufferIndex_);
|
|
126
|
-
processingBusPosition += bufferIndex_;
|
|
127
|
-
bufferIndex_ = 0;
|
|
128
|
-
}
|
|
183
|
+
auto frameStart = static_cast<size_t>(vFrameStart);
|
|
184
|
+
auto frameEnd = static_cast<size_t>(vFrameEnd);
|
|
129
185
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
186
|
+
size_t framesLeft = offsetLength;
|
|
187
|
+
|
|
188
|
+
// Wrap to the start of the loop if necessary
|
|
189
|
+
if (loop_ && (vReadIndex_ >= vFrameEnd || vReadIndex_ < vFrameStart)) {
|
|
190
|
+
vReadIndex_ =
|
|
191
|
+
vFrameStart + std::fmod(vReadIndex_ - vFrameStart, vFrameDelta);
|
|
134
192
|
}
|
|
135
193
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
194
|
+
while (framesLeft > 0) {
|
|
195
|
+
auto readIndex = static_cast<size_t>(vReadIndex_);
|
|
196
|
+
size_t nextReadIndex = readIndex + 1;
|
|
197
|
+
float factor = vReadIndex_ - readIndex;
|
|
198
|
+
|
|
199
|
+
if (nextReadIndex >= frameEnd) {
|
|
200
|
+
nextReadIndex = loop_ ? frameStart : readIndex;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
for (int i = 0; i < processingBus->getNumberOfChannels(); i += 1) {
|
|
204
|
+
float *destination = processingBus->getChannel(i)->getData();
|
|
205
|
+
const float *source = alignedBus_->getChannel(i)->getData();
|
|
206
|
+
|
|
207
|
+
destination[writeIndex] = AudioUtils::linearInterpolate(
|
|
208
|
+
source, readIndex, nextReadIndex, factor);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
writeIndex += 1;
|
|
212
|
+
vReadIndex_ += playbackRate * direction;
|
|
213
|
+
framesLeft -= 1;
|
|
214
|
+
|
|
215
|
+
if (vReadIndex_ < vFrameStart || vReadIndex_ >= vFrameEnd) {
|
|
216
|
+
vReadIndex_ -= direction * vFrameDelta;
|
|
217
|
+
|
|
218
|
+
if (!loop_) {
|
|
219
|
+
processingBus->zero(writeIndex, framesLeft);
|
|
220
|
+
playbackState_ = PlaybackState::FINISHED;
|
|
221
|
+
disable();
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
142
225
|
}
|
|
143
226
|
}
|
|
144
227
|
|
|
228
|
+
float AudioBufferSourceNode::getPlaybackRateValue(size_t &startOffset) {
|
|
229
|
+
double time =
|
|
230
|
+
context_->getCurrentTime() + startOffset / context_->getSampleRate();
|
|
231
|
+
|
|
232
|
+
return playbackRateParam_->getValueAtTime(time) *
|
|
233
|
+
std::pow(2.0f, detuneParam_->getValueAtTime(time) / 1200.0f);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
double AudioBufferSourceNode::getVirtualStartFrame() {
|
|
237
|
+
double loopStartFrame = loopStart_ * context_->getSampleRate();
|
|
238
|
+
|
|
239
|
+
return loop_ && loopStartFrame >= 0 && loopStart_ < loopEnd_ ? loopStartFrame
|
|
240
|
+
: 0.0;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
double AudioBufferSourceNode::getVirtualEndFrame() {
|
|
244
|
+
double inputBufferLength = alignedBus_->getSize();
|
|
245
|
+
double loopEndFrame = loopEnd_ * context_->getSampleRate();
|
|
246
|
+
|
|
247
|
+
return loop_ && loopEndFrame > 0 && loopStart_ < loopEnd_
|
|
248
|
+
? std::min(loopEndFrame, inputBufferLength)
|
|
249
|
+
: inputBufferLength;
|
|
250
|
+
}
|
|
251
|
+
|
|
145
252
|
} // namespace audioapi
|
|
@@ -8,23 +8,63 @@
|
|
|
8
8
|
namespace audioapi {
|
|
9
9
|
|
|
10
10
|
class AudioBus;
|
|
11
|
+
class AudioParam;
|
|
11
12
|
|
|
12
13
|
class AudioBufferSourceNode : public AudioScheduledSourceNode {
|
|
13
14
|
public:
|
|
14
15
|
explicit AudioBufferSourceNode(BaseAudioContext *context);
|
|
15
16
|
|
|
16
17
|
[[nodiscard]] bool getLoop() const;
|
|
18
|
+
[[nodiscard]] double getLoopStart() const;
|
|
19
|
+
[[nodiscard]] double getLoopEnd() const;
|
|
20
|
+
|
|
21
|
+
[[nodiscard]] std::shared_ptr<AudioParam> getDetuneParam() const;
|
|
22
|
+
[[nodiscard]] std::shared_ptr<AudioParam> getPlaybackRateParam() const;
|
|
23
|
+
|
|
17
24
|
[[nodiscard]] std::shared_ptr<AudioBuffer> getBuffer() const;
|
|
25
|
+
|
|
18
26
|
void setLoop(bool loop);
|
|
27
|
+
void setLoopStart(double loopStart);
|
|
28
|
+
void setLoopEnd(double loopEnd);
|
|
29
|
+
|
|
19
30
|
void setBuffer(const std::shared_ptr<AudioBuffer> &buffer);
|
|
20
31
|
|
|
21
32
|
protected:
|
|
22
33
|
void processNode(AudioBus *processingBus, int framesToProcess) override;
|
|
23
34
|
|
|
24
35
|
private:
|
|
36
|
+
// Looping related properties
|
|
25
37
|
bool loop_;
|
|
38
|
+
double loopStart_;
|
|
39
|
+
double loopEnd_;
|
|
40
|
+
|
|
41
|
+
// playback rate aka pitch change params
|
|
42
|
+
std::shared_ptr<AudioParam> detuneParam_;
|
|
43
|
+
std::shared_ptr<AudioParam> playbackRateParam_;
|
|
44
|
+
|
|
45
|
+
// internal helper
|
|
46
|
+
double vReadIndex_;
|
|
47
|
+
|
|
48
|
+
// User provided buffer
|
|
26
49
|
std::shared_ptr<AudioBuffer> buffer_;
|
|
27
|
-
|
|
50
|
+
std::shared_ptr<AudioBus> alignedBus_;
|
|
51
|
+
|
|
52
|
+
float getPlaybackRateValue(size_t &startOffset);
|
|
53
|
+
|
|
54
|
+
double getVirtualStartFrame();
|
|
55
|
+
double getVirtualEndFrame();
|
|
56
|
+
|
|
57
|
+
void processWithoutInterpolation(
|
|
58
|
+
AudioBus *processingBus,
|
|
59
|
+
size_t startOffset,
|
|
60
|
+
size_t offsetLength,
|
|
61
|
+
float playbackRate);
|
|
62
|
+
|
|
63
|
+
void processWithInterpolation(
|
|
64
|
+
AudioBus *processingBus,
|
|
65
|
+
size_t startOffset,
|
|
66
|
+
size_t offsetLength,
|
|
67
|
+
float playbackRate);
|
|
28
68
|
};
|
|
29
69
|
|
|
30
70
|
} // namespace audioapi
|
|
@@ -23,7 +23,7 @@ class AudioDestinationNode : public AudioNode {
|
|
|
23
23
|
protected:
|
|
24
24
|
// DestinationNode is triggered by AudioContext using renderAudio
|
|
25
25
|
// processNode function is not necessary and is never called.
|
|
26
|
-
void processNode(AudioBus *, int) final{};
|
|
26
|
+
void processNode(AudioBus *, int) final {};
|
|
27
27
|
|
|
28
28
|
private:
|
|
29
29
|
std::size_t currentSampleFrame_;
|
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
#include "AudioScheduledSourceNode.h"
|
|
2
|
+
#include "AudioArray.h"
|
|
3
|
+
#include "AudioBus.h"
|
|
2
4
|
#include "AudioNodeManager.h"
|
|
5
|
+
#include "AudioUtils.h"
|
|
3
6
|
#include "BaseAudioContext.h"
|
|
4
7
|
|
|
5
8
|
namespace audioapi {
|
|
6
9
|
|
|
7
10
|
AudioScheduledSourceNode::AudioScheduledSourceNode(BaseAudioContext *context)
|
|
8
|
-
: AudioNode(context),
|
|
11
|
+
: AudioNode(context),
|
|
12
|
+
playbackState_(PlaybackState::UNSCHEDULED),
|
|
13
|
+
startTime_(-1.0),
|
|
14
|
+
stopTime_(-1.0) {
|
|
9
15
|
numberOfInputs_ = 0;
|
|
10
|
-
isInitialized_ = true;
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
void AudioScheduledSourceNode::start(double time) {
|
|
14
|
-
context_->getNodeManager()->addSourceNode(shared_from_this());
|
|
15
|
-
|
|
16
19
|
playbackState_ = PlaybackState::SCHEDULED;
|
|
17
|
-
|
|
20
|
+
startTime_ = time;
|
|
21
|
+
|
|
22
|
+
context_->getNodeManager()->addSourceNode(shared_from_this());
|
|
18
23
|
}
|
|
19
24
|
|
|
20
25
|
void AudioScheduledSourceNode::stop(double time) {
|
|
21
|
-
|
|
26
|
+
stopTime_ = time;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
bool AudioScheduledSourceNode::isUnscheduled() {
|
|
30
|
+
return playbackState_ == PlaybackState::UNSCHEDULED;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
bool AudioScheduledSourceNode::isScheduled() {
|
|
34
|
+
return playbackState_ == PlaybackState::SCHEDULED;
|
|
22
35
|
}
|
|
23
36
|
|
|
24
37
|
bool AudioScheduledSourceNode::isPlaying() {
|
|
@@ -29,25 +42,77 @@ bool AudioScheduledSourceNode::isFinished() {
|
|
|
29
42
|
return playbackState_ == PlaybackState::FINISHED;
|
|
30
43
|
}
|
|
31
44
|
|
|
32
|
-
void AudioScheduledSourceNode::
|
|
33
|
-
|
|
34
|
-
|
|
45
|
+
void AudioScheduledSourceNode::updatePlaybackInfo(
|
|
46
|
+
AudioBus *processingBus,
|
|
47
|
+
int framesToProcess,
|
|
48
|
+
size_t &startOffset,
|
|
49
|
+
size_t &nonSilentFramesToProcess) {
|
|
50
|
+
if (!isInitialized_) {
|
|
51
|
+
startOffset = 0;
|
|
52
|
+
nonSilentFramesToProcess = 0;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
35
55
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
56
|
+
int sampleRate = context_->getSampleRate();
|
|
57
|
+
|
|
58
|
+
size_t firstFrame = context_->getCurrentSampleFrame();
|
|
59
|
+
size_t lastFrame = firstFrame + framesToProcess;
|
|
60
|
+
|
|
61
|
+
size_t startFrame = std::max(
|
|
62
|
+
AudioUtils::timeToSampleFrame(startTime_, sampleRate), firstFrame);
|
|
63
|
+
size_t stopFrame = stopTime_ == -1.0
|
|
64
|
+
? std::numeric_limits<size_t>::max()
|
|
65
|
+
: std::max(
|
|
66
|
+
AudioUtils::timeToSampleFrame(stopTime_, sampleRate), firstFrame);
|
|
67
|
+
|
|
68
|
+
if (isUnscheduled() || isFinished()) {
|
|
69
|
+
startOffset = 0;
|
|
70
|
+
nonSilentFramesToProcess = 0;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
40
73
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
74
|
+
if (isScheduled()) {
|
|
75
|
+
// not yet playing
|
|
76
|
+
if (startFrame > lastFrame) {
|
|
77
|
+
startOffset = 0;
|
|
78
|
+
nonSilentFramesToProcess = 0;
|
|
79
|
+
return;
|
|
47
80
|
}
|
|
48
81
|
|
|
49
|
-
|
|
50
|
-
|
|
82
|
+
// start playing
|
|
83
|
+
// zero first frames before starting frame
|
|
84
|
+
playbackState_ = PlaybackState::PLAYING;
|
|
85
|
+
startOffset = std::max(startFrame, firstFrame) - firstFrame > 0
|
|
86
|
+
? std::max(startFrame, firstFrame) - firstFrame
|
|
87
|
+
: 0;
|
|
88
|
+
nonSilentFramesToProcess =
|
|
89
|
+
std::min(lastFrame, stopFrame) - startFrame - startOffset;
|
|
90
|
+
processingBus->zero(0, startOffset);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// the node is playing
|
|
95
|
+
|
|
96
|
+
// stop will happen in this render quantum
|
|
97
|
+
// zero remaining frames after stop frame
|
|
98
|
+
if (stopFrame < lastFrame && stopFrame >= firstFrame) {
|
|
99
|
+
startOffset = 0;
|
|
100
|
+
nonSilentFramesToProcess = stopFrame - firstFrame;
|
|
101
|
+
processingBus->zero(stopFrame - firstFrame, lastFrame - stopFrame);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// mark as finished in first silent render quantum
|
|
106
|
+
if (stopFrame < firstFrame) {
|
|
107
|
+
startOffset = 0;
|
|
108
|
+
nonSilentFramesToProcess = 0;
|
|
109
|
+
playbackState_ = PlaybackState::FINISHED;
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// normal "mid-buffer" playback
|
|
114
|
+
startOffset = 0;
|
|
115
|
+
nonSilentFramesToProcess = framesToProcess;
|
|
51
116
|
}
|
|
52
117
|
|
|
53
118
|
} // namespace audioapi
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include <algorithm>
|
|
3
4
|
#include <atomic>
|
|
4
5
|
#include <chrono>
|
|
5
6
|
#include <functional>
|
|
6
7
|
#include <iostream>
|
|
8
|
+
#include <limits>
|
|
7
9
|
#include <memory>
|
|
8
10
|
#include <thread>
|
|
9
11
|
|
|
@@ -19,16 +21,22 @@ class AudioScheduledSourceNode : public AudioNode {
|
|
|
19
21
|
void start(double time);
|
|
20
22
|
void stop(double time);
|
|
21
23
|
|
|
22
|
-
bool
|
|
24
|
+
bool isUnscheduled();
|
|
25
|
+
bool isScheduled();
|
|
23
26
|
bool isPlaying();
|
|
27
|
+
bool isFinished();
|
|
24
28
|
|
|
25
29
|
protected:
|
|
26
30
|
std::atomic<PlaybackState> playbackState_;
|
|
31
|
+
void updatePlaybackInfo(
|
|
32
|
+
AudioBus *processingBus,
|
|
33
|
+
int framesToProcess,
|
|
34
|
+
size_t &startOffset,
|
|
35
|
+
size_t &nonSilentFramesToProcess);
|
|
27
36
|
|
|
28
37
|
private:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
void waitAndExecute(double time, const std::function<void(double)> &fun);
|
|
38
|
+
double startTime_;
|
|
39
|
+
double stopTime_;
|
|
32
40
|
};
|
|
33
41
|
|
|
34
42
|
} // namespace audioapi
|
|
@@ -40,15 +40,20 @@ void OscillatorNode::setPeriodicWave(
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
void OscillatorNode::processNode(AudioBus *processingBus, int framesToProcess) {
|
|
43
|
+
size_t startOffset = 0;
|
|
44
|
+
size_t offsetLength = 0;
|
|
45
|
+
|
|
46
|
+
updatePlaybackInfo(processingBus, framesToProcess, startOffset, offsetLength);
|
|
47
|
+
|
|
43
48
|
if (!isPlaying()) {
|
|
44
49
|
processingBus->zero();
|
|
45
50
|
return;
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
double time = context_->getCurrentTime();
|
|
49
53
|
double deltaTime = 1.0 / context_->getSampleRate();
|
|
54
|
+
double time = context_->getCurrentTime() + startOffset * deltaTime;
|
|
50
55
|
|
|
51
|
-
for (
|
|
56
|
+
for (size_t i = startOffset; i < offsetLength; i += 1) {
|
|
52
57
|
auto detuneRatio =
|
|
53
58
|
std::pow(2.0f, detuneParam_->getValueAtTime(time) / 1200.0f);
|
|
54
59
|
auto detunedFrequency =
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#include "JsiHostObject.h"
|
|
2
|
+
|
|
3
|
+
namespace audioapi {
|
|
4
|
+
JsiHostObject::JsiHostObject() {
|
|
5
|
+
propertyGetters_ = std::make_unique<std::unordered_map<
|
|
6
|
+
std::string,
|
|
7
|
+
jsi::Value (JsiHostObject::*)(jsi::Runtime &)>>();
|
|
8
|
+
propertyFunctions_ = std::make_unique<std::unordered_map<
|
|
9
|
+
std::string,
|
|
10
|
+
jsi::Value (JsiHostObject::*)(
|
|
11
|
+
jsi::Runtime &, const jsi::Value &, const jsi::Value *, size_t)>>();
|
|
12
|
+
propertySetters_ = std::make_unique<std::unordered_map<
|
|
13
|
+
std::string,
|
|
14
|
+
void (JsiHostObject::*)(jsi::Runtime &, const jsi::Value &)>>();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
std::vector<jsi::PropNameID> JsiHostObject::getPropertyNames(jsi::Runtime &rt) {
|
|
18
|
+
std::vector<jsi::PropNameID> propertyNames;
|
|
19
|
+
propertyNames.reserve(
|
|
20
|
+
propertyGetters_->size() + propertyFunctions_->size() +
|
|
21
|
+
propertySetters_->size());
|
|
22
|
+
|
|
23
|
+
for (const auto &it : *propertyGetters_) {
|
|
24
|
+
propertyNames.push_back(jsi::PropNameID::forUtf8(rt, it.first));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
for (const auto &it : *propertyFunctions_) {
|
|
28
|
+
propertyNames.push_back(jsi::PropNameID::forAscii(rt, it.first));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
for (const auto &it : *propertySetters_) {
|
|
32
|
+
propertyNames.push_back(jsi::PropNameID::forAscii(rt, it.first));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return propertyNames;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
jsi::Value JsiHostObject::get(
|
|
39
|
+
jsi::Runtime &runtime,
|
|
40
|
+
const jsi::PropNameID &name) {
|
|
41
|
+
auto nameAsString = name.utf8(runtime);
|
|
42
|
+
|
|
43
|
+
auto propertyGetter = propertyGetters_->find(nameAsString);
|
|
44
|
+
if (propertyGetter != propertyGetters_->end()) {
|
|
45
|
+
auto dispatcher = [this, &propertyGetter](jsi::Runtime &runtime) {
|
|
46
|
+
return (this->*(propertyGetter->second))(runtime);
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return dispatcher(runtime);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
auto propertyFunction = propertyFunctions_->find(nameAsString);
|
|
53
|
+
if (propertyFunction != propertyFunctions_->end()) {
|
|
54
|
+
auto dispatcher = [this, &propertyFunction](
|
|
55
|
+
jsi::Runtime &runtime,
|
|
56
|
+
const jsi::Value &thisVal,
|
|
57
|
+
const jsi::Value *args,
|
|
58
|
+
size_t count) -> jsi::Value {
|
|
59
|
+
return (this->*(propertyFunction->second))(runtime, thisVal, args, count);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
return jsi::Function::createFromHostFunction(runtime, name, 0, dispatcher);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return jsi::Value::undefined();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
void JsiHostObject::set(
|
|
69
|
+
jsi::Runtime &runtime,
|
|
70
|
+
const jsi::PropNameID &name,
|
|
71
|
+
const jsi::Value &value) {
|
|
72
|
+
auto nameAsString = name.utf8(runtime);
|
|
73
|
+
|
|
74
|
+
auto propertySetter = propertySetters_->find(nameAsString);
|
|
75
|
+
|
|
76
|
+
if (propertySetter != propertySetters_->end()) {
|
|
77
|
+
auto dispatcher = [this, &propertySetter](
|
|
78
|
+
jsi::Runtime &runtime, const jsi::Value &value) {
|
|
79
|
+
return (this->*(propertySetter->second))(runtime, value);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return dispatcher(runtime, value);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} // namespace audioapi
|