Skip to content

[audio] added signal node#33223

Closed
igorkorsukov wants to merge 1 commit intomusescore:masterfrom
igorkorsukov:w/audio/ctx_step20
Closed

[audio] added signal node#33223
igorkorsukov wants to merge 1 commit intomusescore:masterfrom
igorkorsukov:w/audio/ctx_step20

Conversation

@igorkorsukov
Copy link
Copy Markdown
Contributor

No description provided.

Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

📝 Walkthrough

Walkthrough

This change introduces a new SignalNode class that centralizes audio signal state management and silence detection within the audio engine. The MixerChannel and Mixer classes are refactored to delegate signal handling to this node instead of managing it directly. The MixerChannel constructor signature is updated to remove the OutputSpec parameter, with initialization deferred to a separate init() method. The factory and context code are adjusted to accommodate the new initialization sequence. Audio output peak tracking and silence computation are relocated from distributed peak-scanning logic to dedicated node operations.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning No pull request description was provided by the author, but the description template requires context about the changes and completion of submission checklist items. Add a description explaining the motivation for introducing SignalNode, its purpose in the audio engine refactoring, and check off applicable template items.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title '[audio] added signal node' accurately describes the main change, which involves introducing a new SignalNode class and integrating it throughout the audio engine.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/framework/audio/engine/internal/mixerchannel.cpp (1)

222-232: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Unguarded m_signalNode->isSilent() calls in processing logic.

Lines 223 and 228 call m_signalNode->isSilent() without null checks, while m_sourceNode on line 222 is properly guarded. This is inconsistent with the pattern at line 257.

🛡️ Proposed fix using consistent pattern
     if (m_sourceNode) {
-        if (!m_params.muted || !m_signalNode->isSilent()) {
+        bool isSilent = m_signalNode ? m_signalNode->isSilent() : true;
+        if (!m_params.muted || !isSilent) {
             m_sourceNode->process(buffer, samplesPerChannel);
         }
     }

-    if (m_params.muted && m_signalNode->isSilent()) {
+    bool isSilent = m_signalNode ? m_signalNode->isSilent() : true;
+    if (m_params.muted && isSilent) {
         std::fill(buffer, buffer + samplesPerChannel * audioChannelCount, 0.f);
         setNoAudioSignal();
         return;
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/framework/audio/engine/internal/mixerchannel.cpp` around lines 222 - 232,
The code calls m_signalNode->isSilent() without null checks (while m_sourceNode
is guarded), causing potential null derefs; update the logic in the processing
block to check m_signalNode before calling isSilent()—for example, guard both
uses of m_signalNode->isSilent() with a nullptr check (e.g., if (m_signalNode &&
!m_params.muted && !m_signalNode->isSilent()) for the process decision and if
(m_params.muted && (!m_signalNode || m_signalNode->isSilent())) before zeroing
the buffer), and keep calls to setNoAudioSignal() and m_sourceNode->process(...)
unchanged otherwise so behavior remains consistent with the pattern used later
around line 257.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/framework/audio/engine/internal/mixerchannel.cpp`:
- Around line 153-166: The three methods audioSignalChanges(),
setNoAudioSignal(), and notifyAboutAudioSignalChanges() access m_signalNode
without null checks which can lead to crashes; either ensure init() is always
called from the MixerChannel constructor (so m_signalNode is guaranteed
initialized, referencing init() and m_signalNode) or add defensive guards inside
each method (e.g., check m_signalNode and return a safe default for
audioSignalChanges() and no-op for setNoAudioSignal() and
notifyAboutAudioSignalChanges()), keeping behavior consistent with isSilent()
which uses m_signalNode ? ... : true.
- Around line 207-210: Add a null check around the signal node before entering
the chain-processing block: only set m_chainProcessing = true and call
m_signalNode->process(buffer, samplesPerChannel) if m_signalNode != nullptr, and
ensure m_chainProcessing is reset to false in all paths (i.e., do not set it
true unless you will later set it false). Update the code in mixerchannel.cpp
around the existing m_chainProcessing / m_signalNode usage so the audio thread
will skip processing when m_signalNode is null and will not leave
m_chainProcessing stuck true.
- Around line 185-190: m_signalNode and m_controlNode are accessed without null
checks while m_sourceNode is guarded; update the code around the setOutputSpec
calls to perform the same defensive null-check pattern used for m_sourceNode by
wrapping m_signalNode->setOutputSpec(spec) and
m_controlNode->setOutputSpec(spec) in if (m_signalNode) { ... } and if
(m_controlNode) { ... } respectively so all three nodes are only dereferenced
when non-null; keep the existing call to m_sourceNode->setOutputSpec(spec)
unchanged.

---

Outside diff comments:
In `@src/framework/audio/engine/internal/mixerchannel.cpp`:
- Around line 222-232: The code calls m_signalNode->isSilent() without null
checks (while m_sourceNode is guarded), causing potential null derefs; update
the logic in the processing block to check m_signalNode before calling
isSilent()—for example, guard both uses of m_signalNode->isSilent() with a
nullptr check (e.g., if (m_signalNode && !m_params.muted &&
!m_signalNode->isSilent()) for the process decision and if (m_params.muted &&
(!m_signalNode || m_signalNode->isSilent())) before zeroing the buffer), and
keep calls to setNoAudioSignal() and m_sourceNode->process(...) unchanged
otherwise so behavior remains consistent with the pattern used later around line
257.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6ff73973-5d0e-49db-b992-29ec732468b2

📥 Commits

Reviewing files that changed from the base of the PR and between 5d344bc and cfd9fca.

📒 Files selected for processing (10)
  • src/framework/audio/engine/CMakeLists.txt
  • src/framework/audio/engine/internal/audiocontext.cpp
  • src/framework/audio/engine/internal/audiofactory.cpp
  • src/framework/audio/engine/internal/mixer.cpp
  • src/framework/audio/engine/internal/mixer.h
  • src/framework/audio/engine/internal/mixerchannel.cpp
  • src/framework/audio/engine/internal/mixerchannel.h
  • src/framework/audio/engine/internal/nodes/audiooutputnode.h
  • src/framework/audio/engine/internal/nodes/signalnode.cpp
  • src/framework/audio/engine/internal/nodes/signalnode.h
💤 Files with no reviewable changes (1)
  • src/framework/audio/engine/internal/nodes/audiooutputnode.h

Comment on lines 153 to 166
AudioSignalChanges MixerChannel::audioSignalChanges() const
{
return m_audioSignalNotifier.audioSignalChanges;
return m_signalNode->audioSignalChanges();
}

void MixerChannel::setNoAudioSignal()
{
m_signalNode->setNoAudioSignal();
}

void MixerChannel::notifyAboutAudioSignalChanges()
{
m_signalNode->notifyAboutAudioSignalChanges();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Inconsistent null checking for m_signalNode risks null pointer dereference.

These methods access m_signalNode without null checks, but isSilent() at line 257 does check: m_signalNode ? m_signalNode->isSilent() : true. If any of these methods is called before init(), or if the initialization order changes, the code will crash.

Either add defensive null checks for consistency, or ensure init() is called from the constructor to guarantee the invariant.

🛡️ Proposed fix to add null guards
 AudioSignalChanges MixerChannel::audioSignalChanges() const
 {
-    return m_signalNode->audioSignalChanges();
+    return m_signalNode ? m_signalNode->audioSignalChanges() : AudioSignalChanges();
 }

 void MixerChannel::setNoAudioSignal()
 {
-    m_signalNode->setNoAudioSignal();
+    if (m_signalNode) {
+        m_signalNode->setNoAudioSignal();
+    }
 }

 void MixerChannel::notifyAboutAudioSignalChanges()
 {
-    m_signalNode->notifyAboutAudioSignalChanges();
+    if (m_signalNode) {
+        m_signalNode->notifyAboutAudioSignalChanges();
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
AudioSignalChanges MixerChannel::audioSignalChanges() const
{
return m_audioSignalNotifier.audioSignalChanges;
return m_signalNode->audioSignalChanges();
}
void MixerChannel::setNoAudioSignal()
{
m_signalNode->setNoAudioSignal();
}
void MixerChannel::notifyAboutAudioSignalChanges()
{
m_signalNode->notifyAboutAudioSignalChanges();
}
AudioSignalChanges MixerChannel::audioSignalChanges() const
{
return m_signalNode ? m_signalNode->audioSignalChanges() : AudioSignalChanges();
}
void MixerChannel::setNoAudioSignal()
{
if (m_signalNode) {
m_signalNode->setNoAudioSignal();
}
}
void MixerChannel::notifyAboutAudioSignalChanges()
{
if (m_signalNode) {
m_signalNode->notifyAboutAudioSignalChanges();
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/framework/audio/engine/internal/mixerchannel.cpp` around lines 153 - 166,
The three methods audioSignalChanges(), setNoAudioSignal(), and
notifyAboutAudioSignalChanges() access m_signalNode without null checks which
can lead to crashes; either ensure init() is always called from the MixerChannel
constructor (so m_signalNode is guaranteed initialized, referencing init() and
m_signalNode) or add defensive guards inside each method (e.g., check
m_signalNode and return a safe default for audioSignalChanges() and no-op for
setNoAudioSignal() and notifyAboutAudioSignalChanges()), keeping behavior
consistent with isSilent() which uses m_signalNode ? ... : true.

Comment on lines +185 to 190
m_signalNode->setOutputSpec(spec);
m_controlNode->setOutputSpec(spec);

if (m_sourceNode) {
m_sourceNode->setOutputSpec(spec);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Same null-check inconsistency for m_signalNode and m_controlNode.

Lines 185-186 access m_signalNode and m_controlNode without null checks, while m_sourceNode on line 188 is properly guarded. Apply the same defensive pattern here.

🛡️ Proposed fix
 void MixerChannel::onOutputSpecChanged(const OutputSpec& spec)
 {
     ONLY_AUDIO_ENGINE_THREAD;

-    m_signalNode->setOutputSpec(spec);
-    m_controlNode->setOutputSpec(spec);
+    if (m_signalNode) {
+        m_signalNode->setOutputSpec(spec);
+    }
+    if (m_controlNode) {
+        m_controlNode->setOutputSpec(spec);
+    }

     if (m_sourceNode) {
         m_sourceNode->setOutputSpec(spec);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
m_signalNode->setOutputSpec(spec);
m_controlNode->setOutputSpec(spec);
if (m_sourceNode) {
m_sourceNode->setOutputSpec(spec);
}
if (m_signalNode) {
m_signalNode->setOutputSpec(spec);
}
if (m_controlNode) {
m_controlNode->setOutputSpec(spec);
}
if (m_sourceNode) {
m_sourceNode->setOutputSpec(spec);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/framework/audio/engine/internal/mixerchannel.cpp` around lines 185 - 190,
m_signalNode and m_controlNode are accessed without null checks while
m_sourceNode is guarded; update the code around the setOutputSpec calls to
perform the same defensive null-check pattern used for m_sourceNode by wrapping
m_signalNode->setOutputSpec(spec) and m_controlNode->setOutputSpec(spec) in if
(m_signalNode) { ... } and if (m_controlNode) { ... } respectively so all three
nodes are only dereferenced when non-null; keep the existing call to
m_sourceNode->setOutputSpec(spec) unchanged.

Comment on lines +207 to +210
if (!m_chainProcessing) {
m_chainProcessing = true;
m_signalNode->process(buffer, samplesPerChannel);
m_chainProcessing = false;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing null check for m_signalNode in audio processing path.

m_signalNode->process() is called without a null guard. Since this runs on the audio thread (ONLY_AUDIO_PROC_THREAD), a crash here could cause audio dropouts or system instability.

🛡️ Proposed fix
     if (!m_chainProcessing) {
         m_chainProcessing = true;
-        m_signalNode->process(buffer, samplesPerChannel);
+        if (m_signalNode) {
+            m_signalNode->process(buffer, samplesPerChannel);
+        }
         m_chainProcessing = false;
     } else {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!m_chainProcessing) {
m_chainProcessing = true;
m_signalNode->process(buffer, samplesPerChannel);
m_chainProcessing = false;
if (!m_chainProcessing) {
m_chainProcessing = true;
if (m_signalNode) {
m_signalNode->process(buffer, samplesPerChannel);
}
m_chainProcessing = false;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/framework/audio/engine/internal/mixerchannel.cpp` around lines 207 - 210,
Add a null check around the signal node before entering the chain-processing
block: only set m_chainProcessing = true and call m_signalNode->process(buffer,
samplesPerChannel) if m_signalNode != nullptr, and ensure m_chainProcessing is
reset to false in all paths (i.e., do not set it true unless you will later set
it false). Update the code in mixerchannel.cpp around the existing
m_chainProcessing / m_signalNode usage so the audio thread will skip processing
when m_signalNode is null and will not leave m_chainProcessing stuck true.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants