react-native-audio-api 0.5.1 → 0.5.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.
@@ -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,20 +144,25 @@
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
  }
158
167
  }
159
168
 
@@ -166,7 +175,11 @@
166
175
  [self.notificationCenter addObserver:self
167
176
  selector:@selector(handleEngineConfigurationChange:)
168
177
  name:AVAudioEngineConfigurationChangeNotification
169
- object:nil];
178
+ object:self];
179
+ [self.notificationCenter addObserver:self
180
+ selector:@selector(handleInterruption:)
181
+ name:AVAudioSessionInterruptionNotification
182
+ object:self];
170
183
  }
171
184
 
172
185
  - (void)connectAudioEngine
@@ -182,18 +195,50 @@
182
195
  NSError *error = nil;
183
196
 
184
197
  if (![self.audioEngine startAndReturnError:&error]) {
185
- NSLog(@"Error starting audio engine: %@", [error localizedDescription]);
198
+ NSLog(@"Error starting audio engine: %@", [error debugDescription]);
186
199
  }
187
200
  }
188
201
  }
189
202
 
190
203
  - (void)handleEngineConfigurationChange:(NSNotification *)notification
191
204
  {
192
- if (!self.isRunning) {
205
+ if (!self.isRunning || self.isInterrupted) {
206
+ self.configurationChanged = true;
193
207
  return;
194
208
  }
195
209
 
196
- [self connectAudioEngine];
210
+ dispatch_async(dispatch_get_main_queue(), ^{
211
+ [self connectAudioEngine];
212
+ });
213
+ }
214
+
215
+ - (void)handleInterruption:(NSNotification *)notification
216
+ {
217
+ NSError *error;
218
+ UInt8 type = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] intValue];
219
+ UInt8 option = [[notification.userInfo valueForKey:AVAudioSessionInterruptionOptionKey] intValue];
220
+
221
+ if (type == AVAudioSessionInterruptionTypeBegan) {
222
+ self.isInterrupted = true;
223
+ return;
224
+ }
225
+
226
+ if (type != AVAudioSessionInterruptionTypeEnded || option != AVAudioSessionInterruptionOptionShouldResume) {
227
+ return;
228
+ }
229
+
230
+ bool success = [self.audioSession setActive:true error:&error];
231
+
232
+ if (!success) {
233
+ NSLog(@"ERror: %@", [error debugDescription]);
234
+ return;
235
+ }
236
+
237
+ if (self.configurationChanged && self.isRunning) {
238
+ dispatch_async(dispatch_get_main_queue(), ^{
239
+ [self connectAudioEngine];
240
+ });
241
+ }
197
242
  }
198
243
 
199
244
  @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.2",
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"