Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feedback loops do not work properly (no output w/ no warnings in the console) #1302

Open
ascpixi opened this issue Jan 1, 2025 · 2 comments

Comments

@ascpixi
Copy link

ascpixi commented Jan 1, 2025

Describe the bug
When forming a loop via audio effects, the result will always be silent, no matter the node. For example, this graph:
image

...will always produce silence, and only silence.

To Reproduce

codesandbox.io sandbox: https://codesandbox.io/p/sandbox/tone-js-feedback-loop-issue-9xq3gk
Pure HTML/JS version:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>tone.js issue repro</title>
    <script src="http://unpkg.com/tone"></script>
  </head>

  <body>
    <input type="checkbox" id="cbBypass"> bypass <br>
    <button onclick="start()">start</button>

    <script>
      async function start() {
        await Tone.start();

        /** @type {boolean} */
        const shouldBypass = document.querySelector("#cbBypass").checked;

        const synth = new Tone.Synth();

        const inNode = new Tone.Gain();
        const reverbNode = new Tone.Reverb({
          decay: 3.0,
          wet: 0.8
        });

        const pitchShiftNode = new Tone.PitchShift(12);
        const feedbackNode = new Tone.Gain(0.75);
        const wetMixNode = new Tone.Gain(0.5);
        const dryMixNode = new Tone.Gain(0.5);
        const outNode = new Tone.Gain(1);
        
        synth.connect(inNode);

        if (!shouldBypass) {
          inNode.connect(reverbNode);
          reverbNode.connect(pitchShiftNode);
          pitchShiftNode.connect(feedbackNode);
          feedbackNode.connect(reverbNode);
          reverbNode.connect(outNode);
        } else {
          inNode.connect(reverbNode);
          reverbNode.connect(outNode);
        }

        outNode.toDestination();

        const tick = () => {
          synth.triggerAttackRelease("C4", 0.25);
        };

        setInterval(tick, 1000);
        tick();
      }
    </script>
  </body>

</html>

Expected behavior
The output should either emit sound, or should report warnings in the console if this operation isn't supported.

What I've tried
I've tried using fan, as well as specifying 0, 0 in calls to connect (e.g. this._reverbNode.connect(this._wetMixNode, 0, 0);), but to no avail. When disconnecting any node in the loop, the chain emits sound again, so I'm guessing this is an issue specific to feedback loops.

@chrisguttandin
Copy link
Contributor

Hi @ascpixi, I believe what you ran into is that feedback loops without a delay get muted by design. It would otherwise be a bit like an infinite loop in programming.

It's defined by the Web Audio API specification:

If nodes contains cycles, mute all the AudioNodes that are part of this cycle, and remove them from nodes.

https://webaudio.github.io/web-audio-api/#rendering-loop (4.2.7.)

I'm afraid there is not much that Tone.js can do about it.

@ascpixi
Copy link
Author

ascpixi commented Jan 5, 2025

Ah, that's a shame. I'm guessing the only way around this is to implement a set number of iterations by duplicating the nodes. To be fair - I was also led astray by the FeedbackEffect class, haha...

I do think there should be some kind of warning for this case, though. A lot of audio effects are based on feedback loops (the one in the example is the Eno/Lanois shimmer reverb), so I'm guessing other programmers could easily fall into the same exact trap.

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

No branches or pull requests

2 participants