Skip to content

Commit

Permalink
feat: locker + audio node manager wip
Browse files Browse the repository at this point in the history
  • Loading branch information
michalsek committed Nov 22, 2024
1 parent fb4c245 commit 8afa7b9
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 11 deletions.
4 changes: 2 additions & 2 deletions apps/common-app/src/examples/DrumMachine/DrumMachine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const defaultPreset = 'Empty';

function setupPlayer(audioCtx: AudioContext) {
const kick = new Kick(audioCtx);
// const clap = new Clap(audioCtx);
const clap = new Clap(audioCtx);
const hiHat = new HiHat(audioCtx);

const playNote = (name: InstrumentName, time: number) => {
Expand All @@ -31,7 +31,7 @@ function setupPlayer(audioCtx: AudioContext) {
kick.play(time);
break;
case 'clap':
// clap.play(time);
clap.play(time);
break;
case 'hi-hat':
hiHat.play(time);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ void AudioDestinationNode::renderAudio(AudioBus *destinationBus, int32_t numFram
destinationBus->copy(processedBus);
}

destinationBus->normalize();
// destinationBus->normalize();
destinationBus->zero();

currentSampleFrame_ += numFrames;
}
Expand Down
20 changes: 16 additions & 4 deletions packages/react-native-audio-api/common/cpp/core/AudioNode.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "AudioBus.h"
#include "AudioNode.h"
#include "AudioNodeManager.h"
#include "BaseAudioContext.h"

namespace audioapi {
Expand Down Expand Up @@ -34,11 +35,19 @@ std::string AudioNode::getChannelInterpretation() const {
}

void AudioNode::connect(const std::shared_ptr<AudioNode> &node) {
context_->getNodeManager()->addPendingConnection(shared_from_this(), node, AudioNodeManager::ConnectionType::CONNECT);
}

void AudioNode::connectNode(std::shared_ptr<AudioNode> &node) {
outputNodes_.push_back(node);
node->inputNodes_.push_back(shared_from_this());
}

void AudioNode::disconnect(const std::shared_ptr<AudioNode> &node) {
context_->getNodeManager()->addPendingConnection(shared_from_this(), node, AudioNodeManager::ConnectionType::DISCONNECT);
}

void AudioNode::disconnectNode(std::shared_ptr<AudioNode> &node) {
outputNodes_.erase(
std::remove(outputNodes_.begin(), outputNodes_.end(), node),
outputNodes_.end());
Expand All @@ -51,6 +60,10 @@ void AudioNode::disconnect(const std::shared_ptr<AudioNode> &node) {
}
}

bool AudioNode::isInitialized() const {
return isInitialized_;
}


std::string AudioNode::toString(ChannelCountMode mode) {
switch (mode) {
Expand All @@ -77,7 +90,6 @@ std::string AudioNode::toString(ChannelInterpretation interpretation) {
}

AudioBus* AudioNode::processAudio(AudioBus* outputBus, int framesToProcess) {
debugName_;
if (!isInitialized_) {
return outputBus;
}
Expand Down Expand Up @@ -115,12 +127,12 @@ AudioBus* AudioNode::processAudio(AudioBus* outputBus, int framesToProcess) {
return processingBus;
}

for (auto it = inputNodes_.begin(); it != inputNodes_.end(); it += 1) {
for (auto it = inputNodes_.begin(); it != inputNodes_.end(); ++it) {
// Process first connected node, it can be directly connected to the processingBus,
// resulting in one less summing operation.q
// resulting in one less summing operation.
if (it == inputNodes_.begin()) {
it->get()->processAudio(processingBus, framesToProcess);
} else if (it->get()) {
} else {
// Enforce the summing to be done using the internal bus.
AudioBus* inputBus = it->get()->processAudio(0, framesToProcess);
if (inputBus) {
Expand Down
9 changes: 8 additions & 1 deletion packages/react-native-audio-api/common/cpp/core/AudioNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <memory>
#include <string>
#include <vector>
#include "AudioNode.h"
#include "Constants.h"

namespace audioapi {
Expand All @@ -22,7 +23,11 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
void connect(const std::shared_ptr<AudioNode> &node);
void disconnect(const std::shared_ptr<AudioNode> &node);

bool isInitialized() const;

protected:
friend class AudioNodeManager;

enum class ChannelCountMode { MAX, CLAMPED_MAX, EXPLICIT };
enum class ChannelInterpretation { SPEAKERS, DISCRETE };

Expand All @@ -47,10 +52,12 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
std::vector<std::shared_ptr<AudioNode>> inputNodes_ = {};
std::vector<std::shared_ptr<AudioNode>> outputNodes_ = {};


void cleanup();
AudioBus* processAudio(AudioBus* outputBus, int framesToProcess);
virtual void processNode(AudioBus* processingBus, int framesToProcess) = 0;

void connectNode(std::shared_ptr<AudioNode> &node);
void disconnectNode(std::shared_ptr<AudioNode> &node);
};

} // namespace audioapi
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

#include "Locker.hpp"
#include "AudioNode.h"
#include "AudioNodeManager.h"

namespace audioapi {

AudioNodeManager::AudioNodeManager() {}

AudioNodeManager::~AudioNodeManager() {
audioNodesToConnect_.clear();
audioNodesToDelete_.clear();
}

void AudioNodeManager::addPendingConnection(std::shared_ptr<AudioNode> from, std::shared_ptr<AudioNode> to, ConnectionType type) {
Locker lock(getGraphLock());

audioNodesToConnect_.push_back(std::make_tuple(from, to, type));
}

void AudioNodeManager::setNodeToDelete(std::shared_ptr<AudioNode> node) {
Locker lock(getGraphLock());

audioNodesToDelete_.push_back(node);
}

void AudioNodeManager::preProcessGraph() {
if (!Locker::tryLock(getGraphLock())) {
return;
}
}

void AudioNodeManager::postProcessGraph() {
if (!Locker::tryLock(getGraphLock())) {
return;
}
}

std::mutex& AudioNodeManager::getGraphLock() {
return graphLock_;
}

void AudioNodeManager::settlePendingConnections() {
for (auto& connection : audioNodesToConnect_) {
std::shared_ptr<AudioNode> from = std::get<0>(connection);
std::shared_ptr<AudioNode> to = std::get<1>(connection);
ConnectionType type = std::get<2>(connection);

if (type == ConnectionType::CONNECT) {
from->connectNode(to);
} else {
from->disconnectNode(to);
}
}

audioNodesToConnect_.clear();
}

void AudioNodeManager::settlePendingDeletions() {
audioNodesToDelete_.clear();
}

} // namespace audioapi
36 changes: 36 additions & 0 deletions packages/react-native-audio-api/common/cpp/core/AudioNodeManager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once

#include <mutex>
#include <tuple>
#include <memory>
#include <vector>

namespace audioapi {

class AudioNode;

class AudioNodeManager {
public:
enum class ConnectionType { CONNECT, DISCONNECT };
AudioNodeManager();
~AudioNodeManager();

void preProcessGraph();
void postProcessGraph();
void addPendingConnection(std::shared_ptr<AudioNode> from, std::shared_ptr<AudioNode> to, ConnectionType type);

void setNodeToDelete(std::shared_ptr<AudioNode> node);

std::mutex& getGraphLock();

private:
std::mutex graphLock_;

std::vector<std::tuple<std::shared_ptr<AudioNode>, std::shared_ptr<AudioNode>, ConnectionType>> audioNodesToConnect_;
std::vector<std::shared_ptr<AudioNode>> audioNodesToDelete_;

void settlePendingConnections();
void settlePendingDeletions();
};

} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "OscillatorNode.h"
#include "StereoPannerNode.h"
#include "BiquadFilterNode.h"
#include "AudioNodeManager.h"
#include "AudioDestinationNode.h"
#include "AudioBufferSourceNode.h"

Expand All @@ -28,8 +29,9 @@ BaseAudioContext::BaseAudioContext() {
sampleRate_ = audioPlayer_->getSampleRate();
bufferSizeInFrames_ = audioPlayer_->getBufferSizeInFrames();

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

std::string BaseAudioContext::getState() {
Expand Down Expand Up @@ -93,6 +95,10 @@ std::function<void(AudioBus*, int)> BaseAudioContext::renderAudio() {
};
}

AudioNodeManager* BaseAudioContext::getNodeManager() {
return nodeManager_.get();
}

std::string BaseAudioContext::toString(State state) {
switch (state) {
case State::SUSPENDED:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class StereoPannerNode;
class BiquadFilterNode;
class AudioDestinationNode;
class AudioBufferSourceNode;
class AudioNodeManager;

#ifdef ANDROID
class AudioPlayer;
Expand All @@ -41,6 +42,8 @@ class BaseAudioContext {
static std::shared_ptr<AudioBuffer> createBuffer(int numberOfChannels, int length, int sampleRate);
std::function<void(AudioBus *, int)> renderAudio();

AudioNodeManager* getNodeManager();

protected:
enum class State { SUSPENDED, RUNNING, CLOSED };
static std::string toString(State state);
Expand All @@ -55,6 +58,7 @@ class BaseAudioContext {
State state_ = State::RUNNING;
int sampleRate_;
int bufferSizeInFrames_;
std::shared_ptr<AudioNodeManager> nodeManager_;
};

} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,6 @@ void BiquadFilterNode::applyFilter() {
}

void BiquadFilterNode::processNode(AudioBus* processingBus, int framesToProcess) {
printf("BiquadFilterNode::processNode\n");
resetCoefficients();
applyFilter();

Expand All @@ -371,7 +370,7 @@ void BiquadFilterNode::processNode(AudioBus* processingBus, int framesToProcess)
float a1 = a1_;
float a2 = a2_;

for (int i = 0; i < processingBus->getNumberOfChannels(); i += 1) {
for (int i = 0; i < framesToProcess; i += 1) {
float input = (*processingBus->getChannel(0))[i];
float output = b0 * input + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;

Expand Down
47 changes: 47 additions & 0 deletions packages/react-native-audio-api/common/cpp/utils/Locker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

#include <mutex>

namespace audioapi {

// Small easy interface to manage locking
class Locker {
public:
Locker(): lockPtr_(0) {}
Locker(std::mutex& lockPtr): lockPtr_(&lockPtr) {
lock();
}

~Locker() {
unlock();
}

explicit operator bool() const { return !!lockPtr_; }

void lock() {
if (lockPtr_) {
lockPtr_->lock();
}
}

void unlock() {
if (lockPtr_) {
lockPtr_->unlock();
}
}

static Locker tryLock(std::mutex& lock) {
Locker result = Locker();

if (lock.try_lock()) {
result.lockPtr_ = &lock;
}

return result;
}

private:
std::mutex* lockPtr_;
};

} // namespace audioapi

0 comments on commit 8afa7b9

Please sign in to comment.