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.
Files changed (89) hide show
  1. package/android/CMakeLists.txt +2 -2
  2. package/android/src/main/cpp/AudioAPIInstaller/AudioAPIInstaller.cpp +3 -9
  3. package/android/src/main/cpp/AudioAPIInstaller/AudioAPIInstaller.h +0 -2
  4. package/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.h +25 -28
  5. package/common/cpp/HostObjects/AudioBufferHostObject.h +79 -13
  6. package/common/cpp/HostObjects/AudioBufferSourceNodeHostObject.h +93 -14
  7. package/common/cpp/HostObjects/AudioContextHostObject.h +10 -21
  8. package/common/cpp/HostObjects/AudioDestinationNodeHostObject.h +3 -16
  9. package/common/cpp/HostObjects/AudioNodeHostObject.h +48 -11
  10. package/common/cpp/HostObjects/AudioParamHostObject.h +56 -14
  11. package/common/cpp/HostObjects/AudioScheduledSourceNodeHostObject.h +23 -16
  12. package/common/cpp/HostObjects/BaseAudioContextHostObject.h +131 -14
  13. package/common/cpp/HostObjects/BiquadFilterNodeHostObject.h +76 -18
  14. package/common/cpp/HostObjects/Constants.h +2 -0
  15. package/common/cpp/HostObjects/GainNodeHostObject.h +10 -15
  16. package/common/cpp/HostObjects/OscillatorNodeHostObject.h +40 -17
  17. package/common/cpp/HostObjects/PeriodicWaveHostObject.h +4 -17
  18. package/common/cpp/HostObjects/StereoPannerNodeHostObject.h +10 -17
  19. package/common/cpp/core/AudioBufferSourceNode.cpp +180 -73
  20. package/common/cpp/core/AudioBufferSourceNode.h +41 -1
  21. package/common/cpp/core/AudioDestinationNode.h +1 -1
  22. package/common/cpp/core/AudioScheduledSourceNode.cpp +86 -21
  23. package/common/cpp/core/AudioScheduledSourceNode.h +12 -4
  24. package/common/cpp/core/OscillatorNode.cpp +7 -2
  25. package/common/cpp/jsi/JsiHostObject.cpp +85 -0
  26. package/common/cpp/jsi/JsiHostObject.h +94 -0
  27. package/common/cpp/{utils → jsi}/JsiPromise.cpp +10 -9
  28. package/common/cpp/jsi/JsiPromise.h +48 -0
  29. package/common/cpp/utils/AudioUtils.cpp +26 -0
  30. package/common/cpp/utils/AudioUtils.h +16 -0
  31. package/ios/AudioAPIModule.mm +2 -6
  32. package/ios/AudioDecoder/AudioDecoder.m +9 -10
  33. package/ios/AudioPlayer/AudioPlayer.m +2 -1
  34. package/lib/module/core/AudioBufferSourceNode.js +15 -0
  35. package/lib/module/core/AudioBufferSourceNode.js.map +1 -1
  36. package/lib/module/index.js +18 -0
  37. package/lib/module/index.js.map +1 -1
  38. package/lib/typescript/core/AudioBufferSourceNode.d.ts +7 -0
  39. package/lib/typescript/core/AudioBufferSourceNode.d.ts.map +1 -1
  40. package/lib/typescript/index.d.ts +6 -2
  41. package/lib/typescript/index.d.ts.map +1 -1
  42. package/lib/typescript/interfaces.d.ts +4 -0
  43. package/lib/typescript/interfaces.d.ts.map +1 -1
  44. package/package.json +1 -1
  45. package/src/core/AudioBufferSourceNode.ts +23 -0
  46. package/src/index.ts +24 -0
  47. package/src/interfaces.ts +5 -0
  48. package/common/cpp/AudioAPIInstaller/AudioAPIInstallerHostObject.cpp +0 -58
  49. package/common/cpp/AudioAPIInstaller/AudioAPIInstallerWrapper.h +0 -38
  50. package/common/cpp/AudioAPIInstaller/android/AudioAPIInstallerWrapper.cpp +0 -16
  51. package/common/cpp/AudioAPIInstaller/ios/AudioAPIInstallerWrapper.cpp +0 -12
  52. package/common/cpp/HostObjects/AudioBufferHostObject.cpp +0 -150
  53. package/common/cpp/HostObjects/AudioBufferSourceNodeHostObject.cpp +0 -79
  54. package/common/cpp/HostObjects/AudioContextHostObject.cpp +0 -55
  55. package/common/cpp/HostObjects/AudioDestinationNodeHostObject.cpp +0 -33
  56. package/common/cpp/HostObjects/AudioNodeHostObject.cpp +0 -102
  57. package/common/cpp/HostObjects/AudioParamHostObject.cpp +0 -115
  58. package/common/cpp/HostObjects/AudioScheduledSourceNodeHostObject.cpp +0 -73
  59. package/common/cpp/HostObjects/BaseAudioContextHostObject.cpp +0 -250
  60. package/common/cpp/HostObjects/BiquadFilterNodeHostObject.cpp +0 -125
  61. package/common/cpp/HostObjects/GainNodeHostObject.cpp +0 -41
  62. package/common/cpp/HostObjects/OscillatorNodeHostObject.cpp +0 -88
  63. package/common/cpp/HostObjects/PeriodicWaveHostObject.cpp +0 -33
  64. package/common/cpp/HostObjects/StereoPannerNodeHostObject.cpp +0 -41
  65. package/common/cpp/utils/JsiPromise.h +0 -48
  66. package/common/cpp/wrappers/AudioBufferSourceNodeWrapper.cpp +0 -45
  67. package/common/cpp/wrappers/AudioBufferSourceNodeWrapper.h +0 -26
  68. package/common/cpp/wrappers/AudioBufferWrapper.cpp +0 -46
  69. package/common/cpp/wrappers/AudioBufferWrapper.h +0 -30
  70. package/common/cpp/wrappers/AudioContextWrapper.cpp +0 -17
  71. package/common/cpp/wrappers/AudioContextWrapper.h +0 -19
  72. package/common/cpp/wrappers/AudioDestinationNodeWrapper.h +0 -16
  73. package/common/cpp/wrappers/AudioNodeWrapper.cpp +0 -37
  74. package/common/cpp/wrappers/AudioNodeWrapper.h +0 -25
  75. package/common/cpp/wrappers/AudioParamWrapper.cpp +0 -42
  76. package/common/cpp/wrappers/AudioParamWrapper.h +0 -25
  77. package/common/cpp/wrappers/AudioScheduledSourceNodeWrapper.cpp +0 -23
  78. package/common/cpp/wrappers/AudioScheduledSourceNodeWrapper.h +0 -23
  79. package/common/cpp/wrappers/BaseAudioContextWrapper.cpp +0 -83
  80. package/common/cpp/wrappers/BaseAudioContextWrapper.h +0 -51
  81. package/common/cpp/wrappers/BiquadFilterNodeWrapper.cpp +0 -60
  82. package/common/cpp/wrappers/BiquadFilterNodeWrapper.h +0 -37
  83. package/common/cpp/wrappers/GainNodeWrapper.cpp +0 -14
  84. package/common/cpp/wrappers/GainNodeWrapper.h +0 -20
  85. package/common/cpp/wrappers/OscillatorNodeWrapper.cpp +0 -44
  86. package/common/cpp/wrappers/OscillatorNodeWrapper.h +0 -31
  87. package/common/cpp/wrappers/PeriodicWaveWrapper.h +0 -17
  88. package/common/cpp/wrappers/StereoPannerNodeWrapper.cpp +0 -16
  89. 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), loop_(false), bufferIndex_(0) {
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() || !buffer_ || buffer_->getLength() == 0) {
93
+ if (!isPlaying() || !alignedBus_ || alignedBus_->getSize() == 0 ||
94
+ !playbackRate) {
47
95
  processingBus->zero();
48
96
  return;
49
97
  }
50
98
 
51
- // Easiest case, the buffer is the same length as the number of frames to
52
- // process, just copy the data.
53
- if (framesToProcess == buffer_->getLength()) {
54
- processingBus->copy(buffer_->bus_.get());
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
- if (!loop_) {
57
- playbackState_ = PlaybackState::FINISHED;
58
- disable();
59
- }
108
+ /**
109
+ * Helper functions
110
+ */
60
111
 
61
- return;
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
- // The buffer is longer than the number of frames to process.
65
- // We have to keep track of where we are in the buffer.
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
- while (framesToProcess - outputBusIndex > 0) {
71
- framesToCopy = std::min(
72
- framesToProcess - outputBusIndex,
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
- processingBus->copy(
76
- buffer_->bus_.get(), bufferIndex_, outputBusIndex, framesToCopy);
126
+ size_t framesLeft = offsetLength;
77
127
 
78
- bufferIndex_ += framesToCopy;
79
- outputBusIndex += framesToCopy;
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
- if (bufferIndex_ < buffer_->getLength()) {
82
- continue;
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
- bufferIndex_ %= buffer_->getLength();
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
- // processing bus is longer than the source buffer
101
- if (!loop_) {
102
- // If we don't loop the buffer, copy it once and zero the remaining
103
- // processing bus frames.
104
- processingBus->copy(buffer_->bus_.get());
105
- processingBus->zero(
106
- buffer_->getLength(), framesToProcess - buffer_->getLength());
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
- playbackState_ = PlaybackState::FINISHED;
109
- disable();
177
+ size_t writeIndex = startOffset;
110
178
 
111
- return;
112
- }
179
+ double vFrameStart = getVirtualStartFrame();
180
+ double vFrameEnd = getVirtualEndFrame();
181
+ double vFrameDelta = vFrameEnd - vFrameStart;
113
182
 
114
- // If we loop the buffer, we need to loop the buffer framesToProcess /
115
- // bufferSize times There might also be a remainder of frames to copy after
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
- // Copy the entire buffer n times to the processing bus.
131
- while (processingBusPosition + bufferSize <= framesToProcess) {
132
- processingBus->copy(buffer_->bus_.get());
133
- processingBusPosition += bufferSize;
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
- // Fill in the remaining frames from the processing buffer and update buffer
137
- // index for next render quantum.
138
- if (remainingFrames > 0) {
139
- processingBus->copy(
140
- buffer_->bus_.get(), 0, processingBusPosition, remainingFrames);
141
- bufferIndex_ = remainingFrames;
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
- int bufferIndex_;
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), playbackState_(PlaybackState::UNSCHEDULED) {
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
- waitAndExecute(time, [this](double time) { startPlayback(); });
20
+ startTime_ = time;
21
+
22
+ context_->getNodeManager()->addSourceNode(shared_from_this());
18
23
  }
19
24
 
20
25
  void AudioScheduledSourceNode::stop(double time) {
21
- waitAndExecute(time, [this](double time) { stopPlayback(); });
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::startPlayback() {
33
- playbackState_ = PlaybackState::PLAYING;
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
- void AudioScheduledSourceNode::stopPlayback() {
37
- playbackState_ = PlaybackState::FINISHED;
38
- disable();
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
- void AudioScheduledSourceNode::waitAndExecute(
42
- double time,
43
- const std::function<void(double)> &fun) {
44
- std::thread([this, time, fun]() {
45
- while (context_->getCurrentTime() < time) {
46
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
74
+ if (isScheduled()) {
75
+ // not yet playing
76
+ if (startFrame > lastFrame) {
77
+ startOffset = 0;
78
+ nonSilentFramesToProcess = 0;
79
+ return;
47
80
  }
48
81
 
49
- fun(time);
50
- }).detach();
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 isFinished();
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
- void startPlayback();
30
- void stopPlayback();
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 (int i = 0; i < framesToProcess; i += 1) {
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