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 +8 -3
- package/android/src/main/cpp/audioapi/android/core/AudioPlayer.cpp +1 -0
- package/common/cpp/audioapi/core/effects/ConvolverNode.cpp +4 -10
- package/common/cpp/audioapi/core/effects/ConvolverNode.h +0 -4
- package/ios/audioapi/ios/core/IOSAudioPlayer.h +16 -2
- package/ios/audioapi/ios/core/IOSAudioPlayer.mm +90 -24
- package/package.json +1 -1
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
|
-
|
|
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
|
-
:
|
|
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
|
-
|
|
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.
|
|
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"
|