Skip to content

Commit

Permalink
feat: something is working
Browse files Browse the repository at this point in the history
  • Loading branch information
michalsek committed Nov 25, 2024
1 parent 2172775 commit cc1ad11
Show file tree
Hide file tree
Showing 17 changed files with 195 additions and 97 deletions.
8 changes: 4 additions & 4 deletions apps/common-app/src/examples/DrumMachine/DrumMachine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ 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) => {
switch (name) {
case 'kick':
kick.play(time);
break;
case 'clap':
clap.play(time);
break;
// case 'clap':
// clap.play(time);
// break;
case 'hi-hat':
hiHat.play(time);
break;
Expand Down
1 change: 0 additions & 1 deletion apps/common-app/src/examples/Oscillator/Oscillator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ const Oscillator: FC = () => {
const handlePlayPause = () => {
if (isPlaying) {
oscillatorRef.current?.stop(0);
oscillatorRef.current = null;
} else {
setup();
oscillatorRef.current?.start(0);
Expand Down
21 changes: 12 additions & 9 deletions apps/common-app/src/examples/SharedUtils/soundEngines/HiHat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,29 @@ class HiHat implements SoundEngine {
const oscillator = this.audioContext.createOscillator();
oscillator.type = 'square';
oscillator.frequency.value = this.tone * ratio;

const bandpassFilter = this.audioContext.createBiquadFilter();
const highpassFilter = this.audioContext.createBiquadFilter();
const gain = this.audioContext.createGain();
// const gain = this.audioContext.createGain();

bandpassFilter.type = 'bandpass';
bandpassFilter.frequency.value = this.bandpassFilterFrequency;

highpassFilter.type = 'highpass';
highpassFilter.frequency.value = this.highpassFilterFrequency;

gain.gain.setValueAtTime(0.0001, time);
gain.gain.exponentialRampToValueAtTime(this.volume, time + 0.02);
gain.gain.exponentialRampToValueAtTime(this.volume * 0.33, time + 0.03);
gain.gain.exponentialRampToValueAtTime(this.volume * 0.0001, time + 0.3);
gain.gain.setValueAtTime(0, time + 0.3 + 0.001);
// gain.gain.setValueAtTime(0.0001, time);
// gain.gain.exponentialRampToValueAtTime(this.volume, time + 0.02);
// gain.gain.exponentialRampToValueAtTime(this.volume * 0.33, time + 0.03);
// gain.gain.exponentialRampToValueAtTime(this.volume * 0.0001, time + 0.3);
// gain.gain.setValueAtTime(0, time + 0.3 + 0.001);

oscillator.connect(bandpassFilter);
bandpassFilter.connect(highpassFilter);
highpassFilter.connect(gain);
gain.connect(this.audioContext.destination!);
// bandpassFilter.connect(highpassFilter);
// highpassFilter.connect(gain);
// gain.connect(this.audioContext.destination!);

bandpassFilter.connect(this.audioContext.destination);
oscillator.start(time);
oscillator.stop(time + this.decay);
});
Expand Down
4 changes: 2 additions & 2 deletions apps/fabric-example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2063,8 +2063,8 @@ SPEC CHECKSUMS:
RNReanimated: 77242c6d67416988a2fd9f5cf574bb3e60016362
RNScreens: e389d6a6a66a4f0d3662924ecae803073ccce8ec
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
Yoga: f8ec45ce98bba1bc93dd28f2ee37215180e6d2b6
Yoga: 1d66db49f38fd9e576a1d7c3b081e46ab4c28b9e

PODFILE CHECKSUM: 75ad38075e71875257a2590065853ea6a608b897

COCOAPODS: 1.15.2
COCOAPODS: 1.16.2
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ void AudioBufferSourceNode::processNode(AudioBus* processingBus, int framesToPro
double time = context_->getCurrentTime();

// No audio data to fill, zero the output and return.
if (!isPlaying_ || !buffer_ || buffer_->getLength() == 0) {
if (!isPlaying() || !buffer_ || buffer_->getLength() == 0) {
processingBus->zero();
return;
}
Expand All @@ -51,7 +51,7 @@ void AudioBufferSourceNode::processNode(AudioBus* processingBus, int framesToPro
processingBus->copy(buffer_->bus_.get());

if (!loop_) {
isPlaying_ = false;
playbackState_ = PlaybackState::FINISHED;
}

return;
Expand All @@ -70,7 +70,7 @@ void AudioBufferSourceNode::processNode(AudioBus* processingBus, int framesToPro
}

if (bufferIndex_ + framesToCopy == buffer_->getLength() - 1) {
isPlaying_ = false;
playbackState_ = PlaybackState::FINISHED;
bufferIndex_ = 0;
}

Expand All @@ -82,7 +82,8 @@ void AudioBufferSourceNode::processNode(AudioBus* processingBus, int framesToPro
// If we don't loop the buffer, copy it once and zero the remaining processing bus frames.
processingBus->copy(buffer_->bus_.get());
processingBus->zero(buffer_->getLength(), framesToProcess - buffer_->getLength());
isPlaying_ = false;
playbackState_ = PlaybackState::FINISHED;

return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ double AudioDestinationNode::getCurrentTime() const {
}

void AudioDestinationNode::renderAudio(AudioBus *destinationBus, int32_t numFrames) {
printf("connected nodes: %d\n", inputNodes_.size());

if (!isInitialized_) {
return;
}
Expand Down
84 changes: 68 additions & 16 deletions packages/react-native-audio-api/common/cpp/core/AudioNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,28 +42,22 @@ void AudioNode::connect(const std::shared_ptr<AudioNode> &node) {

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

node->onInputConnected(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());
node->onInputDisconnected(this);

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

if (auto sharedThis = shared_from_this()) {
node->inputNodes_.erase(
std::remove(
node->inputNodes_.begin(), node->inputNodes_.end(), sharedThis),
node->inputNodes_.end());
if (position != outputNodes_.end()) {
outputNodes_.erase(position);
}
numberOfConnections_ -= 1;
node->numberOfConnections_ -= 1;
}

bool AudioNode::isInitialized() const {
Expand All @@ -76,10 +70,18 @@ bool AudioNode::isEnabled() const {

void AudioNode::enable() {
isEnabled_ = true;

for (auto it = outputNodes_.begin(); it != outputNodes_.end(); ++it) {
it->get()->onInputEnabled();
}
}

void AudioNode::disable() {
isEnabled_ = false;

for (auto it = outputNodes_.begin(); it != outputNodes_.end(); ++it) {
it->get()->onInputDisabled();
}
}

std::string AudioNode::toString(ChannelCountMode mode) {
Expand Down Expand Up @@ -123,7 +125,7 @@ AudioBus* AudioNode::processAudio(AudioBus* outputBus, int framesToProcess) {
bool canUseOutputBus = outputBus != 0 && inputNodes_.size() < 2 && outputNodes_.size() < 2;

if (isAlreadyProcessed) {
// If it was already processed in the rendering quantum,return it.
// If it was already processed in the rendering quantum, return it.
return audioBus_.get();
}

Expand Down Expand Up @@ -152,10 +154,14 @@ AudioBus* AudioNode::processAudio(AudioBus* outputBus, int framesToProcess) {
// Process first connected node, it can be directly connected to the processingBus,
// resulting in one less summing operation.
if (it == inputNodes_.begin()) {
it->get()->processAudio(processingBus, framesToProcess);
AudioBus* inputBus = (*it)->processAudio(processingBus, framesToProcess);

if (inputBus != processingBus) {
processingBus->sum(inputBus);
}
} else {
// Enforce the summing to be done using the internal bus.
AudioBus* inputBus = it->get()->processAudio(0, framesToProcess);
AudioBus* inputBus = (*it)->processAudio(0, framesToProcess);
if (inputBus) {
processingBus->sum(inputBus);
}
Expand All @@ -173,4 +179,50 @@ void AudioNode::cleanup() {
inputNodes_.clear();
}

void AudioNode::onInputEnabled() {
numberOfEnabledInputNodes_ += 1;

if (!isEnabled()) {
enable();
}
}

void AudioNode::onInputDisabled() {
numberOfEnabledInputNodes_ -= 1;

if (isEnabled() && numberOfEnabledInputNodes_ == 0) {
disable();
}
}

void AudioNode::onInputConnected(AudioNode *node) {
inputNodes_.push_back(node);

if (node->isEnabled()) {
onInputEnabled();
}
}

void AudioNode::onInputDisconnected(AudioNode *node) {
auto position = std::find(inputNodes_.begin(), inputNodes_.end(), node);

if (position == inputNodes_.end()) {
return;
}

inputNodes_.erase(position);

if (inputNodes_.size() > 0 || outputNodes_.size() == 0) {
return;
}

if (isEnabled()) {
node->onInputDisabled();
}

for (auto outputNode : outputNodes_) {
disconnectNode(outputNode);
}
}

} // namespace audioapi
22 changes: 15 additions & 7 deletions packages/react-native-audio-api/common/cpp/core/AudioNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
BaseAudioContext *context_;
std::shared_ptr<AudioBus> audioBus_;

int channelCount_ = CHANNEL_COUNT;

int numberOfInputs_ = 1;
int numberOfOutputs_ = 1;
int channelCount_ = CHANNEL_COUNT;
int numberOfConnections_ = 0;
int numberOfEnabledInputNodes_ = 0;


bool isInitialized_ = false;
bool isEnabled_ = true;

Expand All @@ -51,7 +54,7 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {
ChannelInterpretation channelInterpretation_ =
ChannelInterpretation::SPEAKERS;

std::vector<std::shared_ptr<AudioNode>> inputNodes_ = {};
std::vector<AudioNode*> inputNodes_ = {};
std::vector<std::shared_ptr<AudioNode>> outputNodes_ = {};

private:
Expand All @@ -64,6 +67,11 @@ class AudioNode : public std::enable_shared_from_this<AudioNode> {

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

void onInputEnabled();
void onInputDisabled();
void onInputConnected(AudioNode *node);
void onInputDisconnected(AudioNode *node);
};

} // namespace audioapi
Expand All @@ -90,9 +98,9 @@ We use shared pointers in audio node manager to keep track of all source nodes
when audio source node finished playing it:
- disables itself and tells all output nodes that it has been disabled
- each node up to destination, checks their input nodes and if was its only active input node, it disables itself.
- source node tells audio node manager to dereference it (only if it is the last reference to the source node)
- audio manager in pre-process or post-process will remove the reference
- deletion of the node will dereference all connected nodes, resulting in destroy'ing them if they are not referenced from JS side
- source node tells audio node manager to dereference it (only if it is the last reference to the source node).
- audio manager in pre-process or post-process will remove the reference.
- audio manager in pre-process or post-process will also have to check for source nodes with only one reference and delete them if already stopped.
- deletion of the node will dereference all connected nodes, resulting in destroy'ing them if they are not referenced from JS side.
*/
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,19 @@ void AudioNodeManager::preProcessGraph() {
return;
}

settlePendingConnections();
settlePendingDeletions();
settlePendingConnections();
removeFinishedSourceNodes();
}

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

settlePendingConnections();
settlePendingDeletions();
settlePendingConnections();
removeFinishedSourceNodes();
}

std::mutex& AudioNodeManager::getGraphLock() {
Expand All @@ -72,4 +74,21 @@ void AudioNodeManager::settlePendingDeletions() {
audioNodesToDelete_.clear();
}

void AudioNodeManager::removeFinishedSourceNodes() {
for (auto it = sourceNodes_.begin(); it != sourceNodes_.end();) {
auto currentNode = it->get();
// Release the source node if use count is equal to 1 (this vector)
if (!currentNode->isEnabled() && it->use_count() == 1) {

for (auto& outputNode : currentNode->outputNodes_) {
currentNode->disconnectNode(outputNode);
}

it = sourceNodes_.erase(it);
} else {
++it;
}
}
}

} // namespace audioapi
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ class AudioNodeManager {
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_;
std::vector<std::shared_ptr<AudioNode>> sourceNodes_;
std::vector<std::shared_ptr<AudioNode>> audioNodesToDelete_;
std::vector<std::tuple<std::shared_ptr<AudioNode>, std::shared_ptr<AudioNode>, ConnectionType>> audioNodesToConnect_;

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

} // namespace audioapi
Loading

0 comments on commit cc1ad11

Please sign in to comment.