Skip to content

feat: Graph refactor chapter II#991

Open
poneciak57 wants to merge 65 commits into
mainfrom
feat/graph_refactor_2
Open

feat: Graph refactor chapter II#991
poneciak57 wants to merge 65 commits into
mainfrom
feat/graph_refactor_2

Conversation

@poneciak57

@poneciak57 poneciak57 commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Closes RNAA-433

⚠️ Breaking changes ⚠️

Introduced changes

Checklist

  • Linked relevant issue
  • Updated relevant documentation
  • Added/Conducted relevant tests
  • Performed self-review of the code
  • Updated Web Audio API coverage
  • Added support for web
  • Updated old arch android spec file

@maciejmakowski2003 maciejmakowski2003 force-pushed the feat/graph_refactor_2 branch 2 times, most recently from 62ae703 to 71f1fd0 Compare March 31, 2026 06:37
@mdydek mdydek marked this pull request as ready for review May 7, 2026 09:34
@mdydek mdydek added the feature New feature label May 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just checking in
is it possible to call start and stop concurently (eg at the same time)?

@poneciak57 poneciak57 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overally it looks really great. One global issue is lack of high level documentation. It would be great to write/generate some high level design doc on top of that or keep existing ones in sync with the code

But as i said overally good job 👍

Comment on lines +35 to +36
delay_ring::bufferOperation(
delayBuffer, audioBuffer_, framesToProcess, writeIndex, delay_ring::BufferAction::WRITE);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why u have Action type instead of having two methods write/read?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because it shares a lot of code under the hood, but it would be hard to split it into two separate functions and put common code elsewhere, this way we can easily control what is shared and what is not

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot address this architecture choice discussion

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hope he will not empty out my wallet for this question ....

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eh...
I would challenge the assumption that minimizing code duplication should be the primary goal.

Read and write are distinct operations with different semantics. The fact that they share some implementation details doesn't necessarily mean they should be exposed through a single function with an Action enum.

I'd generally prefer separate read() and write() APIs and keep any shared logic internal. A slightly larger implementation is often a reasonable trade-off for a cleaner and more explicit interface.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make sure this file is compiled with -O3 -ffast-math

Comment thread packages/react-native-audio-api/common/cpp/audioapi/jsi/JsiPromise.h Outdated
Comment on lines +38 to +65
auto drainA = [&] {
AGEvent ev;
while (eventReceiver_.try_receive(ev) == ResponseStatus::SUCCESS) {
if (ev) {
ev(audioGraph, *disposer_);
}
}
};

// Steady-state: drain any A events queued since the last cycle.
drainA();

// For every pending orphan on Channel B: ensure Channel A's receive
// cursor has reached the barrier the GC thread snapshotted at orphan
// enqueue time. Until that barrier is met, every A event that the
// orphan happens-after must still be in flight; we drain A again to
// catch up. Only then do we consume and apply the orphan.
while (const OrphanEnvelope *front = gcEventReceiver_.try_peek()) {
if (eventReceiver_.rcvCursor() < front->barrier) {
drainA();
if (eventReceiver_.rcvCursor() < front->barrier) {
// Producer is mid-send on Channel A; the event will arrive
// imminently. Defer this orphan to the next processEvents()
// tick rather than burning audio-thread cycles spinning.
break;
}
}
OrphanEnvelope consumed;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this "vector-clock-like" aproach is great, but the logic seems to be split between few places. It would be nice to maybe make some wrapper on top of the SPSC to handle that scenario

@maciejmakowski2003 maciejmakowski2003 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a few more remarks

Comment thread packages/react-native-audio-api/common/cpp/audioapi/utils/SpscChannel.hpp Outdated
Comment thread packages/react-native-audio-api/common/cpp/audioapi/utils/SpscChannel.hpp Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants