Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor/general context refactoring #276

Merged
merged 4 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1656,7 +1656,7 @@ PODS:
- React-logger (= 0.76.5)
- React-perflogger (= 0.76.5)
- React-utils (= 0.76.5)
- RNAudioAPI (0.3.2):
- RNAudioAPI (0.4.0):
- DoubleConversion
- glog
- hermes-engine
Expand Down Expand Up @@ -2152,7 +2152,7 @@ SPEC CHECKSUMS:
React-utils: f584a494ac233c7857bab176416b0c49cb4037ba
ReactCodegen: 1f59af46efc9351f27046d90d9ceb5e99385f623
ReactCommon: 5809a8ee421b7219221a475b78180f8f34b5c5ec
RNAudioAPI: 0672a736922266bbb418cb6481d32204e8136988
RNAudioAPI: 8a98b4d1149f55f3815919576d50520fbab125f9
RNGestureHandler: e1dcb274c17ca0680a04d7ff357e35e37c384185
RNReanimated: 270e2df37d3a18e8270a9c461d9f90a374a97d04
RNScreens: 351f431ef2a042a1887d4d90e1c1024b8ae9d123
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,14 @@ AudioPlayer::AudioPlayer(
->openStream(mStream_);

mBus_ = std::make_shared<AudioBus>(
getSampleRate(), getBufferSizeInFrames(), CHANNEL_COUNT);
getSampleRate(), RENDER_QUANTUM_SIZE, CHANNEL_COUNT);
isInitialized_ = true;
}

int AudioPlayer::getSampleRate() const {
return mStream_->getSampleRate();
}

int AudioPlayer::getBufferSizeInFrames() const {
return mStream_->getBufferSizeInFrames();
}

void AudioPlayer::start() {
if (mStream_) {
mStream_->requestStart();
Expand All @@ -59,14 +55,22 @@ DataCallbackResult AudioPlayer::onAudioReady(
}

auto buffer = static_cast<float *>(audioData);
renderAudio_(mBus_.get(), numFrames);
int processedFrames = 0;

// TODO: optimize this with SIMD?
for (int32_t i = 0; i < numFrames; i += 1) {
for (int channel = 0; channel < CHANNEL_COUNT; channel += 1) {
buffer[i * CHANNEL_COUNT + channel] =
mBus_->getChannel(channel)->getData()[i];
while (processedFrames < numFrames) {
int framesToProcess =
std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE);
renderAudio_(mBus_.get(), framesToProcess);

// TODO: optimize this with SIMD?
for (int i = 0; i < framesToProcess; i++) {
for (int channel = 0; channel < CHANNEL_COUNT; channel += 1) {
buffer[(processedFrames + i) * CHANNEL_COUNT + channel] =
mBus_->getChannel(channel)->getData()[i];
}
}

processedFrames += framesToProcess;
}

return DataCallbackResult::Continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ class AudioPlayer : public AudioStreamDataCallback {
explicit AudioPlayer(const std::function<void(AudioBus *, int)> &renderAudio);

[[nodiscard]] int getSampleRate() const;
[[nodiscard]] int getBufferSizeInFrames() const;
void start();
void stop();

Expand Down
26 changes: 26 additions & 0 deletions packages/react-native-audio-api/common/cpp/core/AudioContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,42 @@
#endif

#include "AudioContext.h"
#include "AudioDestinationNode.h"

namespace audioapi {

AudioContext::AudioContext() : BaseAudioContext() {
#ifdef ANDROID
audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio());
#else
audioPlayer_ = std::make_shared<IOSAudioPlayer>(this->renderAudio());
#endif
sampleRate_ = audioPlayer_->getSampleRate();

audioPlayer_->start();
}

AudioContext::~AudioContext() {
if (isRunning()) {
return;
}

close();
}

void AudioContext::close() {
state_ = ContextState::CLOSED;
audioPlayer_->stop();
}

std::function<void(AudioBus *, int)> AudioContext::renderAudio() {
if (!isRunning()) {
return [](AudioBus *, int) {};
}

return [this](AudioBus *data, int frames) {
destination_->renderAudio(data, frames);
};
}

} // namespace audioapi
15 changes: 15 additions & 0 deletions packages/react-native-audio-api/common/cpp/core/AudioContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,27 @@
#include "BaseAudioContext.h"

namespace audioapi {
#ifdef ANDROID
class AudioPlayer;
#else
class IOSAudioPlayer;
#endif

class AudioContext : public BaseAudioContext {
public:
AudioContext();
~AudioContext() override;

void close();

std::function<void(AudioBus *, int)> renderAudio();

private:
#ifdef ANDROID
std::shared_ptr<AudioPlayer> audioPlayer_;
#else
std::shared_ptr<IOSAudioPlayer> audioPlayer_;
#endif
};

} // namespace audioapi
12 changes: 9 additions & 3 deletions packages/react-native-audio-api/common/cpp/core/AudioNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ namespace audioapi {

AudioNode::AudioNode(BaseAudioContext *context) : context_(context) {
audioBus_ = std::make_shared<AudioBus>(
context->getSampleRate(),
context->getBufferSizeInFrames(),
channelCount_);
context->getSampleRate(), RENDER_QUANTUM_SIZE, channelCount_);
}

AudioNode::~AudioNode() {
Expand Down Expand Up @@ -207,6 +205,10 @@ void AudioNode::onInputDisabled() {
}

void AudioNode::onInputConnected(AudioNode *node) {
if (!isInitialized_) {
return;
}

inputNodes_.push_back(node);

if (node->isEnabled()) {
Expand All @@ -215,6 +217,10 @@ void AudioNode::onInputConnected(AudioNode *node) {
}

void AudioNode::onInputDisconnected(AudioNode *node) {
if (!isInitialized_) {
return;
}

auto position = std::find(inputNodes_.begin(), inputNodes_.end(), node);

if (position != inputNodes_.end()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,3 @@
#ifdef ANDROID
#include "AudioPlayer.h"
#else
#include "IOSAudioPlayer.h"
#endif

#include "BaseAudioContext.h"

#include "AnalyserNode.h"
Expand All @@ -22,31 +16,13 @@

namespace audioapi {

BaseAudioContext::BaseAudioContext() {
#ifdef ANDROID
audioPlayer_ = std::make_shared<AudioPlayer>(this->renderAudio());
#else
audioPlayer_ = std::make_shared<IOSAudioPlayer>(this->renderAudio());
#endif

audioDecoder_ = std::make_shared<AudioDecoder>(audioPlayer_->getSampleRate());

sampleRate_ = audioPlayer_->getSampleRate();
bufferSizeInFrames_ = audioPlayer_->getBufferSizeInFrames();
BaseAudioContext::BaseAudioContext() : sampleRate_(DEFAULT_SAMPLE_RATE) {
audioDecoder_ = std::make_shared<AudioDecoder>(sampleRate_);

nodeManager_ = std::make_shared<AudioNodeManager>();
destination_ = std::make_shared<AudioDestinationNode>(this);
}

BaseAudioContext::~BaseAudioContext() {
if (isRunning()) {
return;
}

state_ = ContextState::CLOSED;
audioPlayer_->stop();
}

std::string BaseAudioContext::getState() {
return BaseAudioContext::toString(state_);
}
Expand All @@ -55,10 +31,6 @@ int BaseAudioContext::getSampleRate() const {
return sampleRate_;
}

int BaseAudioContext::getBufferSizeInFrames() const {
return bufferSizeInFrames_;
}

std::size_t BaseAudioContext::getCurrentSampleFrame() const {
return destination_->getCurrentSampleFrame();
}
Expand Down Expand Up @@ -117,16 +89,6 @@ std::shared_ptr<AudioBuffer> BaseAudioContext::decodeAudioDataSource(
return std::make_shared<AudioBuffer>(audioBus);
}

std::function<void(AudioBus *, int)> BaseAudioContext::renderAudio() {
if (!isRunning()) {
return [](AudioBus *, int) {};
}

return [this](AudioBus *data, int frames) {
destination_->renderAudio(data, frames);
};
}

AudioNodeManager *BaseAudioContext::getNodeManager() {
return nodeManager_.get();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,14 @@ class AudioBufferSourceNode;
class AudioDecoder;
class AnalyserNode;

#ifdef ANDROID
class AudioPlayer;
#else
class IOSAudioPlayer;
#endif

class BaseAudioContext {
public:
BaseAudioContext();
~BaseAudioContext();
virtual ~BaseAudioContext() = default;

std::string getState();
[[nodiscard]] int getSampleRate() const;
[[nodiscard]] double getCurrentTime() const;
[[nodiscard]] int getBufferSizeInFrames() const;
[[nodiscard]] std::size_t getCurrentSampleFrame() const;
std::shared_ptr<AudioDestinationNode> getDestination();

Expand All @@ -58,7 +51,6 @@ class BaseAudioContext {

std::shared_ptr<AudioBuffer> decodeAudioDataSource(const std::string &path);
std::shared_ptr<PeriodicWave> getBasicWaveForm(OscillatorType type);
std::function<void(AudioBus *, int)> renderAudio();
AudioNodeManager *getNodeManager();
[[nodiscard]] bool isRunning() const;
[[nodiscard]] bool isClosed() const;
Expand All @@ -68,14 +60,7 @@ class BaseAudioContext {
std::shared_ptr<AudioDestinationNode> destination_;
std::shared_ptr<AudioDecoder> audioDecoder_;

#ifdef ANDROID
std::shared_ptr<AudioPlayer> audioPlayer_;
#else
std::shared_ptr<IOSAudioPlayer> audioPlayer_;
#endif

int sampleRate_;
int bufferSizeInFrames_;
ContextState state_ = ContextState::RUNNING;
std::shared_ptr<AudioNodeManager> nodeManager_;

Expand Down
5 changes: 3 additions & 2 deletions packages/react-native-audio-api/common/cpp/core/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@
// https://webaudio.github.io/web-audio-api/

namespace audioapi {
constexpr int SAMPLE_RATE = 48000;
constexpr int DEFAULT_SAMPLE_RATE = 48000;
constexpr int RENDER_QUANTUM_SIZE = 128;
constexpr int CHANNEL_COUNT = 2;

constexpr float MOST_POSITIVE_SINGLE_FLOAT = static_cast<float>(std::numeric_limits<float>::max());
constexpr float MOST_NEGATIVE_SINGLE_FLOAT = static_cast<float>(std::numeric_limits<float>::lowest());

constexpr float NYQUIST_FREQUENCY = SAMPLE_RATE / 2.0;
constexpr float NYQUIST_FREQUENCY = DEFAULT_SAMPLE_RATE / 2.0;
static float MAX_DETUNE = 1200 * std::log2(MOST_POSITIVE_SINGLE_FLOAT);
constexpr float MAX_GAIN = MOST_POSITIVE_SINGLE_FLOAT;
constexpr float MAX_PAN = 1.0;
Expand Down
2 changes: 0 additions & 2 deletions packages/react-native-audio-api/ios/core/AudioPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ typedef void (^RenderAudioBlock)(AudioBufferList *outputBuffer, int numFrames);

- (int)getSampleRate;

- (int)getBufferSizeInFrames;

- (void)start;

- (void)stop;
Expand Down
15 changes: 0 additions & 15 deletions packages/react-native-audio-api/ios/core/AudioPlayer.m
Original file line number Diff line number Diff line change
Expand Up @@ -52,21 +52,6 @@ - (int)getSampleRate
return [self.audioSession sampleRate];
}

- (int)getBufferSizeInFrames
{
// Note: might be important in the future.
// For some reason audioSession.IOBufferDuration is always 0.01, which for sample rate of 48k
// gives exactly 480 frames, while at the same time frameCount requested by AVAudioSourceEngine
// might vary f.e. between 555-560.
// preferredIOBufferDuration seems to be double the value (resulting in 960 frames),
// which is safer to base our internal AudioBus sizes.
// Buut no documentation => no guarantee :)
// If something is crackling when it should play silence, start here 📻
double maxBufferDuration =
fmax(0.02, fmax(self.audioSession.IOBufferDuration, self.audioSession.preferredIOBufferDuration));
return (int)(maxBufferDuration * self.audioSession.sampleRate + 1);
}

- (void)start
{
[self.audioEngine attachNode:self.sourceNode];
Expand Down
1 change: 0 additions & 1 deletion packages/react-native-audio-api/ios/core/IOSAudioPlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ class IOSAudioPlayer {
~IOSAudioPlayer();

int getSampleRate() const;
int getBufferSizeInFrames() const;

void start();
void stop();
Expand Down
27 changes: 17 additions & 10 deletions packages/react-native-audio-api/ios/core/IOSAudioPlayer.mm
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,30 @@
IOSAudioPlayer::IOSAudioPlayer(const std::function<void(AudioBus *, int)> &renderAudio)
: renderAudio_(renderAudio), audioBus_(0)
{
audioBus_ = new AudioBus(getSampleRate(), RENDER_QUANTUM_SIZE, CHANNEL_COUNT);

RenderAudioBlock renderAudioBlock = ^(AudioBufferList *outputData, int numFrames) {
renderAudio_(audioBus_, numFrames);
int processedFrames = 0;

while (processedFrames < numFrames) {
int framesToProcess = std::min(numFrames - processedFrames, RENDER_QUANTUM_SIZE);
renderAudio_(audioBus_, framesToProcess);

// TODO: optimize this with SIMD?
for (int channel = 0; channel < CHANNEL_COUNT; channel += 1) {
float *outputChannel = (float *)outputData->mBuffers[channel].mData;
auto *inputChannel = audioBus_->getChannel(channel)->getData();

for (int i = 0; i < outputData->mNumberBuffers; i += 1) {
float *outputBuffer = (float *)outputData->mBuffers[i].mData;
for (int i = 0; i < framesToProcess; i++) {
outputChannel[processedFrames + i] = inputChannel[i];
}
}

memcpy(outputBuffer, audioBus_->getChannel(i)->getData(), sizeof(float) * numFrames);
processedFrames += framesToProcess;
}
};

audioPlayer_ = [[AudioPlayer alloc] initWithRenderAudioBlock:renderAudioBlock];
audioBus_ = new AudioBus(getSampleRate(), getBufferSizeInFrames(), CHANNEL_COUNT);
}

IOSAudioPlayer::~IOSAudioPlayer()
Expand Down Expand Up @@ -50,9 +62,4 @@
return [audioPlayer_ getSampleRate];
}

int IOSAudioPlayer::getBufferSizeInFrames() const
{
return [audioPlayer_ getBufferSizeInFrames];
}

} // namespace audioapi
Loading