Skip to content

Conversation

@maciejmakowski2003
Copy link
Collaborator

@maciejmakowski2003 maciejmakowski2003 commented Dec 29, 2025

Closes #

⚠️ Breaking changes ⚠️

Introduced changes

  • Enhanced communication between JS thread and Audio thread. All node's modifications from JS thread are scheduled on CrossThreadEventScheduler wrapper to be executed on Audio thread. HostObject plays a ShadowAudioNode role - it stores a copy of node's state.

Read: Simply read HostObject state
Write: Schedule modification and modify Shadow State

Lock free approach!!!

TODO:

  • AudioNode creation guide
  • test AudioPlayer on Android

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

Comment on lines 13 to 20
bool AudioEventScheduler::scheduleEvent(std::function<void(BaseAudioContext &)> &&event) noexcept {
if (context_->isRunning()) {
return eventScheduler_.scheduleEvent(std::move(event));
} else {
event(*context_);
return true;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

It should be fine but one thing is missing. We drain the queue at the start of renderAudio callback so here look at this sequence
TA - audio thread
TJ - JS thread

  1. TA - drains the queue
  2. TJ - pushes E1 to queue (ctx running so pushes to queue)
  3. TJ - stops context
  4. TA - processes last frame
  5. TJ - pushes E2 to queue (ctx not running imediately executes)

We get inconsistent state because our operations are not executed in proper order that depends on the order of their schedule. So E1 should be executed before E2 for example these two events might depend on each other like connect disconnect or something like allowing these two things is not alright or even worse we can get race condition if last frames is processed after the flag is set to true

And draining queue on stop can cause a race cond on the other hand. So this is major problem that needs to be solved.

}

bool AudioPlayer::start() {
if (mStream_) {
Copy link
Contributor

Choose a reason for hiding this comment

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

we can get rid of all implicit stream to bool casts in this pr

Comment on lines 123 to 124
for (int i = 0; i < framesToProcess; i++) {
for (int channel = 0; channel < channelCount_; channel += 1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
for (int i = 0; i < framesToProcess; i++) {
for (int channel = 0; channel < channelCount_; channel += 1) {
for (int i = 0; i < framesToProcess; i++) {
for (int channel = 0; channel < channelCount_; channel++) {

consistency

audioBufferSourceNode->setLoop(value.getBool());
auto loop = value.getBool();

auto event = [audioBufferSourceNode, loop](BaseAudioContext &context) {
Copy link
Contributor

Choose a reason for hiding this comment

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

do we need reference to context here and in all of the lambdas that they do not use context inside it? if not change sfinae definition of a function so more types can be passed in event scheduler

: nodeManager_(std::make_shared<AudioNodeManager>()),
: sampleRate_ {sampleRate},
nodeManager_(std::make_shared<AudioNodeManager>()),
audioEventScheduler_(std::make_unique<CrossThreadEventScheduler<BaseAudioContext>>(1024)),
Copy link
Contributor

Choose a reason for hiding this comment

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

magic number, put it in some constant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants