react-native-audio-api 0.5.1 → 0.5.3

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.
@@ -109,39 +109,4 @@ std::shared_ptr<AudioBus> AudioDecoder::decodeWithMemoryBlock(
109
109
  return audioBus;
110
110
  }
111
111
 
112
- // std::shared_ptr<AudioBus> AudioDecoder::decode(ma_decoder &decoder) const {
113
- // ma_uint64 totalFrameCount;
114
- // ma_decoder_get_length_in_pcm_frames(&decoder, &totalFrameCount);
115
- //
116
- // auto audioBus = std::make_shared<AudioBus>(
117
- // static_cast<int>(totalFrameCount), 2, sampleRate_);
118
- // auto *buffer = new float[totalFrameCount * 2];
119
- //
120
- // ma_uint64 framesDecoded;
121
- // ma_decoder_read_pcm_frames(&decoder, buffer, totalFrameCount,
122
- // &framesDecoded); if (framesDecoded == 0) {
123
- // __android_log_print(
124
- // ANDROID_LOG_ERROR,
125
- // "AudioDecoder",
126
- // "Failed to decode");
127
- //
128
- // delete[] buffer;
129
- // ma_decoder_uninit(&decoder);
130
- //
131
- // return nullptr;
132
- // }
133
- //
134
- // for (int i = 0; i < decoder.outputChannels; ++i) {
135
- // auto channelData = audioBus->getChannel(i)->getData();
136
- //
137
- // for (ma_uint64 j = 0; j < framesDecoded; ++j) {
138
- // channelData[j] = buffer[j * decoder.outputChannels + i];
139
- // }
140
- // }
141
- //
142
- // delete[] buffer;
143
- // ma_decoder_uninit(&decoder);
144
- //
145
- // return audioBus;
146
- // }
147
112
  } // namespace audioapi
@@ -14,7 +14,7 @@ AudioPlayer::AudioPlayer(
14
14
  builder.setSharingMode(SharingMode::Exclusive)
15
15
  ->setFormat(AudioFormat::Float)
16
16
  ->setFormatConversionAllowed(true)
17
- ->setPerformanceMode(PerformanceMode::LowLatency)
17
+ ->setPerformanceMode(PerformanceMode::None)
18
18
  ->setChannelCount(channelCount_)
19
19
  ->setSampleRateConversionQuality(SampleRateConversionQuality::Medium)
20
20
  ->setDataCallback(this)
@@ -35,7 +35,7 @@ AudioPlayer::AudioPlayer(
35
35
  builder.setSharingMode(SharingMode::Exclusive)
36
36
  ->setFormat(AudioFormat::Float)
37
37
  ->setFormatConversionAllowed(true)
38
- ->setPerformanceMode(PerformanceMode::LowLatency)
38
+ ->setPerformanceMode(PerformanceMode::None)
39
39
  ->setChannelCount(2)
40
40
  ->setSampleRateConversionQuality(SampleRateConversionQuality::Medium)
41
41
  ->setDataCallback(this)
@@ -31,6 +31,10 @@ class AudioBufferHostObject : public JsiHostObject {
31
31
  JSI_EXPORT_FUNCTION(AudioBufferHostObject, copyToChannel));
32
32
  }
33
33
 
34
+ [[nodiscard]] size_t getSizeInBytes() const {
35
+ return audioBuffer_->getLength() * audioBuffer_->getNumberOfChannels() * sizeof(float);
36
+ }
37
+
34
38
  JSI_PROPERTY_GETTER(sampleRate) {
35
39
  return {audioBuffer_->getSampleRate()};
36
40
  }
@@ -48,8 +52,6 @@ class AudioBufferHostObject : public JsiHostObject {
48
52
  }
49
53
 
50
54
  JSI_HOST_FUNCTION(getChannelData) {
51
- // this method could cause a crash if channelData is already deallocated,
52
- // but we handle deallocation internally so it should be safe
53
55
  auto channel = static_cast<int>(args[0].getNumber());
54
56
  auto channelData = reinterpret_cast<uint8_t *>(audioBuffer_->getChannelData(channel));
55
57
  auto length = static_cast<int>(audioBuffer_->getLength());
@@ -109,7 +109,11 @@ class BaseAudioContextHostObject : public JsiHostObject {
109
109
  auto sampleRate = static_cast<float>(args[2].getNumber());
110
110
  auto buffer = BaseAudioContext::createBuffer(numberOfChannels, length, sampleRate);
111
111
  auto bufferHostObject = std::make_shared<AudioBufferHostObject>(buffer);
112
- return jsi::Object::createFromHostObject(runtime, bufferHostObject);
112
+
113
+ auto jsiObject = jsi::Object::createFromHostObject(runtime, bufferHostObject);
114
+ jsiObject.setExternalMemoryPressure(runtime, bufferHostObject->getSizeInBytes());
115
+
116
+ return jsiObject;
113
117
  }
114
118
 
115
119
  JSI_HOST_FUNCTION(createPeriodicWave) {
@@ -158,7 +162,9 @@ class BaseAudioContextHostObject : public JsiHostObject {
158
162
  }
159
163
 
160
164
  promise->resolve([audioBufferHostObject = std::move(audioBufferHostObject)](jsi::Runtime &runtime) {
161
- return jsi::Object::createFromHostObject(runtime, audioBufferHostObject);
165
+ auto jsiObject = jsi::Object::createFromHostObject(runtime, audioBufferHostObject);
166
+ jsiObject.setExternalMemoryPressure(runtime, audioBufferHostObject->getSizeInBytes());
167
+ return jsiObject;
162
168
  });
163
169
  }).detach();
164
170
  });
@@ -182,7 +188,9 @@ class BaseAudioContextHostObject : public JsiHostObject {
182
188
  }
183
189
 
184
190
  promise->resolve([audioBufferHostObject = std::move(audioBufferHostObject)](jsi::Runtime &runtime) {
185
- return jsi::Object::createFromHostObject(runtime, audioBufferHostObject);
191
+ auto jsiObject = jsi::Object::createFromHostObject(runtime, audioBufferHostObject);
192
+ jsiObject.setExternalMemoryPressure(runtime, audioBufferHostObject->getSizeInBytes());
193
+ return jsiObject;
186
194
  });
187
195
  }).detach();
188
196
  });
@@ -254,7 +254,9 @@ void AudioNode::cleanup() {
254
254
  }
255
255
 
256
256
  for (const auto &inputNode : inputNodes_) {
257
- inputNode->disconnectNode(shared_from_this());
257
+ if (inputNode) {
258
+ inputNode->disconnectNode(shared_from_this());
259
+ }
258
260
  }
259
261
 
260
262
  outputNodes_.clear();
@@ -32,7 +32,7 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
32
32
 
33
33
  bool isEnabled() const;
34
34
  void enable();
35
- void disable();
35
+ virtual void disable();
36
36
 
37
37
  protected:
38
38
  friend class AudioNodeManager;
@@ -121,7 +121,7 @@ int PeriodicWave::getNumberOfPartialsPerRange(int rangeIndex) const {
121
121
 
122
122
  // The very top range will have all the partials culled.
123
123
  int numberOfPartials =
124
- floor(static_cast<float>(getMaxNumberOfPartials()) * cullingScale);
124
+ int(static_cast<float>(getMaxNumberOfPartials()) * cullingScale);
125
125
 
126
126
  return numberOfPartials;
127
127
  }
@@ -109,6 +109,11 @@ void AudioBufferSourceNode::start(double when, double offset, double duration) {
109
109
  vReadIndex_ = static_cast<double>(buffer_->getSampleRate() * offset);
110
110
  }
111
111
 
112
+ void AudioBufferSourceNode::disable() {
113
+ AudioNode::disable();
114
+ buffer_ = nullptr;
115
+ }
116
+
112
117
  std::mutex &AudioBufferSourceNode::getBufferLock() {
113
118
  return bufferLock_;
114
119
  }
@@ -160,9 +165,19 @@ void AudioBufferSourceNode::processNode(
160
165
  updatePlaybackInfo(
161
166
  playbackRateBus_, framesNeededToStretch, startOffset, offsetLength);
162
167
 
168
+ if (playbackRate == 0.0f || !isPlaying() || !buffer_) {
169
+ processingBus->zero();
170
+ return;
171
+ }
172
+
163
173
  processWithoutInterpolation(
164
174
  playbackRateBus_, startOffset, offsetLength, playbackRate);
165
175
 
176
+ if (!buffer_) {
177
+ processingBus->zero();
178
+ return;
179
+ }
180
+
166
181
  auto stretch = buffer_->stretch_;
167
182
 
168
183
  stretch->process(
@@ -171,7 +186,9 @@ void AudioBufferSourceNode::processNode(
171
186
  processingBus.get()[0],
172
187
  framesToProcess);
173
188
 
174
- stretch->setTransposeSemitones(detune);
189
+ if (detune != 0.0f) {
190
+ stretch->setTransposeSemitones(detune);
191
+ }
175
192
  }
176
193
 
177
194
  handleStopScheduled();
@@ -233,6 +250,10 @@ void AudioBufferSourceNode::processWithoutInterpolation(
233
250
 
234
251
  if (!loop_) {
235
252
  processingBus->zero(writeIndex, framesLeft);
253
+
254
+ if (onendedCallback_) {
255
+ onendedCallback_(getStopTime());
256
+ }
236
257
  playbackState_ = PlaybackState::FINISHED;
237
258
  disable();
238
259
  break;
@@ -30,6 +30,7 @@ class AudioBufferSourceNode : public AudioScheduledSourceNode {
30
30
  void setBuffer(const std::shared_ptr<AudioBuffer> &buffer);
31
31
 
32
32
  void start(double when, double offset, double duration = -1);
33
+ void disable() override;
33
34
 
34
35
  protected:
35
36
  std::mutex &getBufferLock();
@@ -36,6 +36,8 @@ class AudioScheduledSourceNode : public AudioNode {
36
36
  protected:
37
37
  PlaybackState playbackState_;
38
38
 
39
+ std::function<void(double)> onendedCallback_;
40
+
39
41
  void updatePlaybackInfo(
40
42
  const std::shared_ptr<AudioBus>& processingBus,
41
43
  int framesToProcess,
@@ -47,8 +49,6 @@ class AudioScheduledSourceNode : public AudioNode {
47
49
  private:
48
50
  double startTime_;
49
51
  double stopTime_;
50
-
51
- std::function<void(double)> onendedCallback_;
52
52
  };
53
53
 
54
54
  } // namespace audioapi
@@ -698,4 +698,5 @@ void linearToDecibels(
698
698
  outputVector[i] = dsp::linearToDecibels(inputVector[i]);
699
699
  }
700
700
  }
701
+
701
702
  } // namespace audioapi::dsp
@@ -46,4 +46,5 @@ void multiply(const float *inputVector1, const float *inputVector2, float *outpu
46
46
  float maximumMagnitude(const float *inputVector, size_t numberOfElementsToProcess);
47
47
 
48
48
  void linearToDecibels(const float *inputVector, float *outputVector, size_t numberOfElementsToProcess);
49
+
49
50
  } // namespace audioapi::dsp
@@ -1,6 +1,15 @@
1
1
  #include <audioapi/jsi/JsiHostObject.h>
2
2
 
3
+ // set this value to 1 in order to debug the construction/destruction
4
+ #define JSI_DEBUG_ALLOCATIONS 1
5
+
3
6
  namespace audioapi {
7
+
8
+ #if JSI_DEBUG_ALLOCATIONS
9
+ int objCounter = 0;
10
+ std::vector<JsiHostObject *> objects;
11
+ #endif
12
+
4
13
  JsiHostObject::JsiHostObject() {
5
14
  getters_ = std::make_unique<std::unordered_map<
6
15
  std::string,
@@ -12,6 +21,23 @@ JsiHostObject::JsiHostObject() {
12
21
  setters_ = std::make_unique<std::unordered_map<
13
22
  std::string,
14
23
  void (JsiHostObject::*)(jsi::Runtime &, const jsi::Value &)>>();
24
+
25
+ #if JSI_DEBUG_ALLOCATIONS
26
+ objects.push_back(this);
27
+ objCounter++;
28
+ #endif
29
+ }
30
+
31
+ JsiHostObject::~JsiHostObject() {
32
+ #if JSI_DEBUG_ALLOCATIONS
33
+ for (size_t i = 0; i < objects.size(); ++i) {
34
+ if (objects.at(i) == this) {
35
+ objects.erase(objects.begin() + i);
36
+ break;
37
+ }
38
+ }
39
+ objCounter--;
40
+ #endif
15
41
  }
16
42
 
17
43
  std::vector<jsi::PropNameID> JsiHostObject::getPropertyNames(jsi::Runtime &rt) {
@@ -48,6 +48,7 @@ using namespace facebook;
48
48
  class JsiHostObject : public jsi::HostObject {
49
49
  public:
50
50
  JsiHostObject();
51
+ ~JsiHostObject() override;
51
52
 
52
53
  std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
53
54
 
@@ -16,6 +16,8 @@ typedef void (^RenderAudioBlock)(AudioBufferList *outputBuffer, int numFrames);
16
16
  @property (nonatomic, assign) float sampleRate;
17
17
  @property (nonatomic, assign) int channelCount;
18
18
  @property (nonatomic, assign) bool isRunning;
19
+ @property (nonatomic, assign) bool isInterrupted;
20
+ @property (nonatomic, assign) bool configurationChanged;
19
21
 
20
22
  - (instancetype)initWithRenderAudioBlock:(RenderAudioBlock)renderAudio channelCount:(int)channelCount;
21
23
 
@@ -42,5 +44,6 @@ typedef void (^RenderAudioBlock)(AudioBufferList *outputBuffer, int numFrames);
42
44
  - (void)connectAudioEngine;
43
45
 
44
46
  - (void)handleEngineConfigurationChange:(NSNotification *)notification;
47
+ - (void)handleInterruption:(NSNotification *)notification;
45
48
 
46
49
  @end
@@ -9,6 +9,8 @@
9
9
  self.audioEngine = [[AVAudioEngine alloc] init];
10
10
  self.audioEngine.mainMixerNode.outputVolume = 1;
11
11
  self.isRunning = true;
12
+ self.isInterrupted = false;
13
+ self.configurationChanged = false;
12
14
 
13
15
  [self setupAndInitAudioSession];
14
16
  [self setupAndInitNotificationHandlers];
@@ -44,6 +46,8 @@
44
46
  self.audioEngine = [[AVAudioEngine alloc] init];
45
47
  self.audioEngine.mainMixerNode.outputVolume = 1;
46
48
  self.isRunning = true;
49
+ self.isInterrupted = false;
50
+ self.configurationChanged = false;
47
51
 
48
52
  [self setupAndInitAudioSession];
49
53
  [self setupAndInitNotificationHandlers];
@@ -94,7 +98,7 @@
94
98
  [self.audioSession setActive:false error:&error];
95
99
 
96
100
  if (error != nil) {
97
- @throw error;
101
+ NSLog(@"Error while deactivating audio session: %@", [error debugDescription]);
98
102
  }
99
103
  }
100
104
 
@@ -140,21 +144,28 @@
140
144
  self.audioSession = [AVAudioSession sharedInstance];
141
145
  }
142
146
 
143
- [self.audioSession setCategory:AVAudioSessionCategoryPlayback
144
- mode:AVAudioSessionModeDefault
145
- options:AVAudioSessionCategoryOptionDuckOthers | AVAudioSessionCategoryOptionAllowBluetooth |
146
- AVAudioSessionCategoryOptionAllowAirPlay
147
- error:&error];
147
+ [self.audioSession setPreferredIOBufferDuration:0.022 error:&error];
148
148
 
149
149
  if (error != nil) {
150
- NSLog(@"Error while configuring audio session: %@", [error localizedDescription]);
150
+ NSLog(@"Error while setting buffer size in audio session: %@", [error debugDescription]);
151
+ return;
152
+ }
153
+
154
+ [self.audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
155
+
156
+ if (error != nil) {
157
+ NSLog(@"Error while configuring audio session: %@", [error debugDescription]);
158
+ return;
151
159
  }
152
160
 
153
161
  [self.audioSession setActive:true error:&error];
154
162
 
155
163
  if (error != nil) {
156
- NSLog(@"Error while activating audio session: %@", [error localizedDescription]);
164
+ NSLog(@"Error while activating audio session: %@", [error debugDescription]);
165
+ return;
157
166
  }
167
+
168
+ self.isInterrupted = false;
158
169
  }
159
170
 
160
171
  - (void)setupAndInitNotificationHandlers
@@ -167,6 +178,10 @@
167
178
  selector:@selector(handleEngineConfigurationChange:)
168
179
  name:AVAudioEngineConfigurationChangeNotification
169
180
  object:nil];
181
+ [self.notificationCenter addObserver:self
182
+ selector:@selector(handleInterruption:)
183
+ name:AVAudioSessionInterruptionNotification
184
+ object:nil];
170
185
  }
171
186
 
172
187
  - (void)connectAudioEngine
@@ -182,18 +197,47 @@
182
197
  NSError *error = nil;
183
198
 
184
199
  if (![self.audioEngine startAndReturnError:&error]) {
185
- NSLog(@"Error starting audio engine: %@", [error localizedDescription]);
200
+ NSLog(@"Error starting audio engine: %@", [error debugDescription]);
186
201
  }
187
202
  }
203
+
204
+ self.configurationChanged = false;
188
205
  }
189
206
 
190
207
  - (void)handleEngineConfigurationChange:(NSNotification *)notification
191
208
  {
192
- if (!self.isRunning) {
209
+ if (!self.isRunning || self.isInterrupted) {
210
+ self.configurationChanged = true;
193
211
  return;
194
212
  }
195
213
 
196
- [self connectAudioEngine];
214
+ dispatch_async(dispatch_get_main_queue(), ^{
215
+ [self connectAudioEngine];
216
+ });
217
+ }
218
+
219
+ - (void)handleInterruption:(NSNotification *)notification
220
+ {
221
+ NSError *error;
222
+ UInt8 type = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] intValue];
223
+ UInt8 option = [[notification.userInfo valueForKey:AVAudioSessionInterruptionOptionKey] intValue];
224
+
225
+ if (type == AVAudioSessionInterruptionTypeBegan) {
226
+ self.isInterrupted = true;
227
+ return;
228
+ }
229
+
230
+ if (type != AVAudioSessionInterruptionTypeEnded || option != AVAudioSessionInterruptionOptionShouldResume) {
231
+ return;
232
+ }
233
+
234
+ [self setupAndInitAudioSession];
235
+
236
+ if (self.configurationChanged && self.isRunning) {
237
+ dispatch_async(dispatch_get_main_queue(), ^{
238
+ [self connectAudioEngine];
239
+ });
240
+ }
197
241
  }
198
242
 
199
243
  @end
@@ -1,6 +1,7 @@
1
1
  #import <AVFoundation/AVFoundation.h>
2
2
 
3
3
  #include <audioapi/core/Constants.h>
4
+ #include <audioapi/dsp/VectorMath.h>
4
5
  #include <audioapi/ios/core/IOSAudioPlayer.h>
5
6
  #include <audioapi/utils/AudioArray.h>
6
7
  #include <audioapi/utils/AudioBus.h>
@@ -17,14 +18,11 @@ IOSAudioPlayer::IOSAudioPlayer(const std::function<void(std::shared_ptr<AudioBus
17
18
  int framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE);
18
19
  renderAudio_(audioBus_, framesToProcess);
19
20
 
20
- // TODO: optimize this with SIMD?
21
21
  for (int channel = 0; channel < channelCount_; channel += 1) {
22
22
  float *outputChannel = (float *)outputData->mBuffers[channel].mData;
23
23
  auto *inputChannel = audioBus_->getChannel(channel)->getData();
24
24
 
25
- for (int i = 0; i < framesToProcess; i++) {
26
- outputChannel[processedFrames + i] = inputChannel[i];
27
- }
25
+ memcpy(outputChannel + processedFrames, inputChannel, framesToProcess * sizeof(float));
28
26
  }
29
27
 
30
28
  processedFrames += framesToProcess;
@@ -45,14 +43,11 @@ IOSAudioPlayer::IOSAudioPlayer(const std::function<void(std::shared_ptr<AudioBus
45
43
  int framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE);
46
44
  renderAudio_(audioBus_, framesToProcess);
47
45
 
48
- // TODO: optimize this with SIMD?
49
46
  for (int channel = 0; channel < channelCount_; channel += 1) {
50
47
  float *outputChannel = (float *)outputData->mBuffers[channel].mData;
51
48
  auto *inputChannel = audioBus_->getChannel(channel)->getData();
52
49
 
53
- for (int i = 0; i < framesToProcess; i++) {
54
- outputChannel[processedFrames + i] = inputChannel[i];
55
- }
50
+ memcpy(outputChannel + processedFrames, inputChannel, framesToProcess * sizeof(float));
56
51
  }
57
52
 
58
53
  processedFrames += framesToProcess;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-api",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "description": "react-native-audio-api provides system for controlling audio in React Native environment compatible with Web Audio API specification",
5
5
  "bin": {
6
6
  "setup-rn-audio-api-web": "./scripts/setup-rn-audio-api-web.js"