react-native-audio-api 0.12.0 → 0.13.0-nightly-a3c8bc2-20260422

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/README.md CHANGED
@@ -28,9 +28,6 @@ check out the [Getting Started](https://docs.swmansion.com/react-native-audio-ap
28
28
  - **DynamicCompressorNode 〽️**<br />
29
29
  Reduce the volume of loud sounds and boost quieter nodes to balance the audio signal, avoid clipping or distorted sounds
30
30
 
31
- - **Audio tag 🏷️**<br />
32
- Simple ability to play and buffer audio, with all of the most commonly used functions, same as on the web, without the need to create and manipulate an audio graph.
33
-
34
31
  - **MIDI support 🎸**<br />
35
32
  Complementary lib for react-native-audio-api, that will allow to communicate with MIDI devices or read/write MIDI files.
36
33
 
@@ -40,6 +37,14 @@ check out the [Getting Started](https://docs.swmansion.com/react-native-audio-ap
40
37
  - **Noise Cancellation 🦇**<br />
41
38
  System-based active noise and echo cancellation support
42
39
 
40
+ ### <a href="https://github.com/software-mansion/react-native-audio-api/releases/tag/0.12.0"><img src="https://img.shields.io/badge/Released_in-0.12.0-green" /></a>
41
+
42
+ - **Audio tag 🏷️**<br />
43
+ Simple ability to play and buffer audio, with all of the most commonly used functions, same as on the web, without the need to create and manipulate an audio graph.
44
+
45
+ - **Recording rotation 🎤**<br />
46
+ Ability to chunk your recording into smaller files, increasing resilience to unpredictable events.
47
+
43
48
  ### <a href="https://github.com/software-mansion/react-native-audio-api/releases/tag/0.11.0"><img src="https://img.shields.io/badge/Released_in-0.11.0-green" /></a>
44
49
 
45
50
  - **Recording to file 📼**<br />
@@ -31,6 +31,7 @@ bool AudioPlayer::openAudioStream() {
31
31
  ->setPerformanceMode(PerformanceMode::None)
32
32
  ->setChannelCount(channelCount_)
33
33
  ->setSampleRateConversionQuality(SampleRateConversionQuality::Medium)
34
+ ->setFramesPerDataCallback(RENDER_QUANTUM_SIZE)
34
35
  ->setDataCallback(this)
35
36
  ->setSampleRate(static_cast<int>(sampleRate_))
36
37
  ->setErrorCallback(this);
@@ -88,21 +88,15 @@ void ConvolverNode::onInputDisabled() {
88
88
  }
89
89
  }
90
90
 
91
- std::shared_ptr<DSPAudioBuffer> ConvolverNode::processInputs(
92
- const std::shared_ptr<DSPAudioBuffer> &outputBuffer,
93
- int framesToProcess,
94
- bool checkIsAlreadyProcessed) {
95
- if (internalBufferIndex_ < framesToProcess) {
96
- return AudioNode::processInputs(outputBuffer, RENDER_QUANTUM_SIZE, false);
97
- }
98
- return AudioNode::processInputs(outputBuffer, 0, false);
99
- }
100
-
101
91
  // processing pipeline: processingBuffer -> intermediateBuffer_ -> audioBuffer_ (mixing
102
92
  // with intermediateBuffer_)
103
93
  std::shared_ptr<DSPAudioBuffer> ConvolverNode::processNode(
104
94
  const std::shared_ptr<DSPAudioBuffer> &processingBuffer,
105
95
  int framesToProcess) {
96
+ if (processingBuffer->getSize() != RENDER_QUANTUM_SIZE) {
97
+ printf(
98
+ "[AUDIOAPI WARN] convolver requires 128 buffer size for each render quantum, otherwise quality of convolution is very poor\n");
99
+ }
106
100
  if (signalledToStop_) {
107
101
  if (remainingSegments_ > 0) {
108
102
  remainingSegments_--;
@@ -41,10 +41,6 @@ class ConvolverNode : public AudioNode {
41
41
  int framesToProcess) override;
42
42
 
43
43
  private:
44
- std::shared_ptr<DSPAudioBuffer> processInputs(
45
- const std::shared_ptr<DSPAudioBuffer> &outputBuffer,
46
- int framesToProcess,
47
- bool checkIsAlreadyProcessed) override;
48
44
  void onInputDisabled() override;
49
45
  const float gainCalibrationSampleRate_;
50
46
  size_t remainingSegments_;
@@ -4,11 +4,14 @@
4
4
  #import <NativeAudioPlayer.h>
5
5
  #else // when compiled as C++
6
6
  typedef struct objc_object NativeAudioPlayer;
7
+ typedef struct objc_object AudioBufferList;
7
8
  #endif // __OBJC__
8
9
 
9
10
  #include <audioapi/utils/AudioBuffer.hpp>
10
- #include <functional>
11
11
 
12
+ #include <atomic>
13
+ #include <cstddef>
14
+ #include <functional>
12
15
  namespace audioapi {
13
16
 
14
17
  class AudioContext;
@@ -29,12 +32,23 @@ class IOSAudioPlayer {
29
32
 
30
33
  bool isRunning() const;
31
34
 
32
- protected:
35
+ private:
36
+ void clearPendingSaved();
37
+ /// Audio-thread only. Always pulls the graph in steps of RENDER_QUANTUM_SIZE; if the system
38
+ /// buffer size is not a multiple of 128, the unused tail of the last quantum is kept (max 128
39
+ /// frames) and played at the start of the next callback.
40
+ void deliverOutputBuffers(AudioBufferList *outputData, int numFrames);
41
+
33
42
  std::shared_ptr<DSPAudioBuffer> audioBuffer_;
34
43
  NativeAudioPlayer *audioPlayer_;
35
44
  std::function<void(std::shared_ptr<DSPAudioBuffer>, int)> renderAudio_;
36
45
  int channelCount_;
37
46
  std::atomic<bool> isRunning_;
47
+ /// Set from main thread on start/resume; consumed on audio thread to drop stale pending audio.
48
+ std::atomic<bool> flushOverflowNextPull_{false};
49
+ /// Frames valid at the front of each `pendingSaved_[ch]` (0 … RENDER_QUANTUM_SIZE).
50
+ int pendingSavedCount_{0};
51
+ DSPAudioBuffer pendingSaved_;
38
52
  };
39
53
 
40
54
  } // namespace audioapi
@@ -1,10 +1,11 @@
1
1
  #import <AVFoundation/AVFoundation.h>
2
2
 
3
+ #include <algorithm>
4
+ #include <cstring>
5
+
3
6
  #include <audioapi/core/utils/Constants.h>
4
- #include <audioapi/dsp/VectorMath.h>
5
7
  #include <audioapi/ios/core/IOSAudioPlayer.h>
6
8
  #include <audioapi/ios/system/AudioEngine.h>
7
- #include <audioapi/utils/AudioArray.hpp>
8
9
  #include <audioapi/utils/AudioBuffer.hpp>
9
10
 
10
11
  namespace audioapi {
@@ -13,35 +14,20 @@ IOSAudioPlayer::IOSAudioPlayer(
13
14
  const std::function<void(std::shared_ptr<DSPAudioBuffer>, int)> &renderAudio,
14
15
  float sampleRate,
15
16
  int channelCount)
16
- : renderAudio_(renderAudio), channelCount_(channelCount), audioBuffer_(0), isRunning_(false)
17
+ : audioBuffer_(nullptr),
18
+ audioPlayer_(nullptr),
19
+ renderAudio_(renderAudio),
20
+ channelCount_(channelCount),
21
+ isRunning_(false),
22
+ pendingSaved_(RENDER_QUANTUM_SIZE, channelCount_, sampleRate)
17
23
  {
18
24
  RenderAudioBlock renderAudioBlock = ^(AudioBufferList *outputData, int numFrames) {
19
- int processedFrames = 0;
20
-
21
- while (processedFrames < numFrames) {
22
- int framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE);
23
-
24
- if (isRunning_.load(std::memory_order_acquire)) {
25
- renderAudio_(audioBuffer_, framesToProcess);
26
- } else {
27
- audioBuffer_->zero();
28
- }
29
-
30
- for (size_t channel = 0; channel < channelCount_; channel += 1) {
31
- float *outputChannel = (float *)outputData->mBuffers[channel].mData;
32
-
33
- audioBuffer_->getChannel(channel)->copyTo(
34
- outputChannel, 0, processedFrames, framesToProcess);
35
- }
36
-
37
- processedFrames += framesToProcess;
38
- }
25
+ deliverOutputBuffers(outputData, numFrames);
39
26
  };
40
27
 
41
28
  audioPlayer_ = [[NativeAudioPlayer alloc] initWithRenderAudio:renderAudioBlock
42
29
  sampleRate:sampleRate
43
30
  channelCount:channelCount_];
44
-
45
31
  audioBuffer_ = std::make_shared<DSPAudioBuffer>(RENDER_QUANTUM_SIZE, channelCount_, sampleRate);
46
32
  }
47
33
 
@@ -50,6 +36,80 @@ IOSAudioPlayer::~IOSAudioPlayer()
50
36
  cleanup();
51
37
  }
52
38
 
39
+ void IOSAudioPlayer::clearPendingSaved()
40
+ {
41
+ pendingSavedCount_ = 0;
42
+ pendingSaved_.zero();
43
+ }
44
+
45
+ void IOSAudioPlayer::deliverOutputBuffers(AudioBufferList *outputData, int numFrames)
46
+ {
47
+ // If requested, clear any saved overflow before continuing normal rendering.
48
+ if (flushOverflowNextPull_.exchange(false, std::memory_order_acq_rel)) {
49
+ clearPendingSaved();
50
+ }
51
+
52
+ // if not running, set output to 0
53
+ if (!isRunning_.load(std::memory_order_acquire)) {
54
+ for (int channel = 0; channel < channelCount_; ++channel) {
55
+ auto *outputChannel = static_cast<float *>(outputData->mBuffers[channel].mData);
56
+ std::memset(outputChannel, 0, static_cast<size_t>(numFrames) * sizeof(float));
57
+ }
58
+ return;
59
+ }
60
+
61
+ int outPos = 0;
62
+ while (outPos < numFrames) {
63
+ const int need = numFrames - outPos;
64
+
65
+ if (pendingSavedCount_ > 0) {
66
+ const int fromPending = std::min(need, pendingSavedCount_);
67
+
68
+ // populate output with pendingSaved
69
+ for (int ch = 0; ch < channelCount_; ++ch) {
70
+ float *dst = static_cast<float *>(outputData->mBuffers[ch].mData) + outPos;
71
+ const float *src = pendingSaved_[ch].begin();
72
+ std::memcpy(dst, src, fromPending * sizeof(float));
73
+
74
+ // move the remaining samples to the beginning of the pendingSaved buffer
75
+ const int remain = pendingSavedCount_ - fromPending;
76
+ if (remain > 0) {
77
+ float *buf = pendingSaved_[ch].begin();
78
+ std::memmove(buf, buf + fromPending, remain * sizeof(float));
79
+ }
80
+ }
81
+
82
+ pendingSavedCount_ -= fromPending;
83
+ outPos += fromPending;
84
+ continue;
85
+ }
86
+
87
+ renderAudio_(audioBuffer_, RENDER_QUANTUM_SIZE);
88
+
89
+ // normal rendering - take RENDER_QUANTUM_SIZE frames from the graph and copy to output
90
+ const int stillNeed = numFrames - outPos;
91
+ if (stillNeed >= RENDER_QUANTUM_SIZE) {
92
+ for (int ch = 0; ch < channelCount_; ++ch) {
93
+ auto *src = (*audioBuffer_)[ch].begin();
94
+ float *dst = static_cast<float *>(outputData->mBuffers[ch].mData) + outPos;
95
+ std::memcpy(dst, src, RENDER_QUANTUM_SIZE * sizeof(float));
96
+ }
97
+ outPos += RENDER_QUANTUM_SIZE;
98
+ } else {
99
+ // when output will be sliced, copy the remaining frames to pendingSaved
100
+ const int tail = RENDER_QUANTUM_SIZE - stillNeed;
101
+ for (int ch = 0; ch < channelCount_; ++ch) {
102
+ auto *src = (*audioBuffer_)[ch].begin();
103
+ float *dst = static_cast<float *>(outputData->mBuffers[ch].mData) + outPos;
104
+ std::memcpy(dst, src, stillNeed * sizeof(float));
105
+ }
106
+ pendingSaved_.copy(*audioBuffer_, stillNeed, 0, tail);
107
+ pendingSavedCount_ = tail;
108
+ outPos += stillNeed;
109
+ }
110
+ }
111
+ }
112
+
53
113
  bool IOSAudioPlayer::start()
54
114
  {
55
115
  if (isRunning()) {
@@ -57,6 +117,9 @@ bool IOSAudioPlayer::start()
57
117
  }
58
118
 
59
119
  bool success = [audioPlayer_ start];
120
+ if (success) {
121
+ flushOverflowNextPull_.store(true, std::memory_order_release);
122
+ }
60
123
  isRunning_.store(success, std::memory_order_release);
61
124
  return success;
62
125
  }
@@ -74,6 +137,9 @@ bool IOSAudioPlayer::resume()
74
137
  }
75
138
 
76
139
  bool success = [audioPlayer_ resume];
140
+ if (success) {
141
+ flushOverflowNextPull_.store(true, std::memory_order_release);
142
+ }
77
143
  isRunning_.store(success, std::memory_order_release);
78
144
  return success;
79
145
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-audio-api",
3
- "version": "0.12.0",
3
+ "version": "0.13.0-nightly-a3c8bc2-20260422",
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"