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

Thank you so much for virtual-audio-graph #408

Open
chr15m opened this issue Mar 27, 2023 · 1 comment
Open

Thank you so much for virtual-audio-graph #408

chr15m opened this issue Mar 27, 2023 · 1 comment

Comments

@chr15m
Copy link

chr15m commented Mar 27, 2023

Hello @benji6 sorry to open a ticket but I couldn't figure out another way to contact you. I just wanted to say thank you very much for virtual-audio-graph, it's really wonderful! 🙏

@vincerubinetti
Copy link

vincerubinetti commented Jan 26, 2025

I second this. So far this library has been a life-saver. It would've been a nightmare to use the raw imperative web audio api for the app I'm building.

I'm making a very simple DAW in the browser for recording audio tracks. I was worried that the library wouldn't conveniently handle buffer sources, e.g. recalling graph.update when a volume knob updates would restart the buffer or something like that.

But it turns out, as long as you update a bufferSource node "in-place", i.e. with the same id, it will update perfectly.


One thing that the library could use is more examples, so here's an example of what I'm doing. I wasn't sure this would work just reading the docs, but luckily it did.

/* UI looks roughly like this:

⏺️ ▶️    0:13 ---⚫-------------- 5:12     🔈 ----⚫-- 

Track 1: --∿∿∿∿∿∿∿∿∿---∿∿∿---
Track 2: ----∿∿∿----∿--------
etc.

user can play/pause/record/seek around like normal DAW (e.g. Audacity)
*/

// heavily reduced example
const ReactComponent = () => {
  // state tied to play button
  const [playing, setPlaying] = useState(false);

  // state tied to a volume knob
  const [volume, setVolume] = useState(1);

  // current time, in seconds (updating very frequently when playing)
  const [time, setTime] = useState(0);

  // mark when playing started or time jumped (only updated on user gesture)
  const [mark, setMark] = useState({ time: 0, timestamp: 0 });
  // when setting, set time to current "time" and timestamp to current performance.now()

  // how many times a second to update time variable, just for UI smoothness
  const fps = 60;

  // (from any react hooks util library)
  useInterval(
    () => {
      // tick time forward when playing
      setTime(mark.time + (window.performance.now() - mark.timestamp) / 1000);
      // do based on offset from mark, instead of naive += 1/fps which would drift over time
    },
    playing ? 1000 / fps : null,
  );

  // when vars that would affect graph change...
  useEffect(() => {
    const track1 = playing
      ? // add node only if playing
        {
          // tie node id to track # and stable mark timestamp
          [`track-1-${mark.timestamp}`]: bufferSource("gain", {
            buffer: track1buffer,
            offsetTime: mark.time,
          }),
        }
      : {};
    // ...track 2, etc...

    // update graph
    graph.update({
      ...track1,
      gain: gain("output", { gain: volume }),
    });
  }, [track1buffer, playing, mark, volume]);

  return <></>;
};

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