Tonejs / Tone.js

A Web Audio framework for making interactive music in the browser.
https://tonejs.github.io
MIT License
13.4k stars 976 forks source link

How about a Players.startAll() method? #1042

Closed stevebarakat closed 2 years ago

stevebarakat commented 2 years ago

Players.startAll()

There's currently a Players.stopAll() method. Why no Players.startAll()? It seems so obvious, I'm surprised it doesn't already exist. For example, I'm making an audio mixer that has 12 channels, playing 12 audio files simultaneously. I'm able to start all the track simultaneously using multiple Tone.Player methods but can't get it working with Tone.Players.

import * as Tone from "tone";
import { initialTracks as tracks } from "./tracks";
import "./App.css";

function App() {
  // create track buffers to ensure audio files are loaded
  let buffers = {};
  let trackNum = 1;
  let playerNum = 1;
  for (let i = 0; i < tracks.length; i++) {
    buffers["track" + trackNum] = tracks[i].path;
    trackNum++;
  }

  // build channel strips
  let channels = {};
  for (let i = 0; i < tracks.length; i++) {
    channels[tracks[i].slug] = new Tone.Channel(
      tracks[i].gain,
      tracks[i].pan
    ).toDestination();
  }

  new Tone.ToneAudioBuffers(buffers, () => console.log("loaded"));

  // **** THIS WAY WORKS ***** //

  // const player1 = new Tone.Player(buffers.track1).chain(channels.drumsRight);
  // const player2 = new Tone.Player(buffers.track2).chain(channels.drumsLeft);
  // const player3 = new Tone.Player(buffers.track3).chain(channels.triggers);
  // const player4 = new Tone.Player(buffers.track4).chain(channels.bass);
  // const player5 = new Tone.Player(buffers.track5).chain(channels.keys);
  // const player6 = new Tone.Player(buffers.track6).chain(channels.gtr1);
  // const player7 = new Tone.Player(buffers.track7).chain(channels.gtr2);
  // const player8 = new Tone.Player(buffers.track8).chain(channels.synth1);
  // const player9 = new Tone.Player(buffers.track9).chain(channels.synth2);
  // const player10 = new Tone.Player(buffers.track10).chain(channels.siren);
  // const player11 = new Tone.Player(buffers.track11).chain(channels.voxFx);
  // const player12 = new Tone.Player(buffers.track12).chain(channels.leadVox);

  // function playTrack() {
  //   player1.start();
  //   player2.start();
  //   player3.start();
  //   player4.start();
  //   player5.start();
  //   player6.start();
  //   player7.start();
  //   player8.start();
  //   player9.start();
  //   player10.start();
  //   player11.start();
  //   player12.start();
  // }

  // *** THIS DOES NOT *** //

  let player = {};
  for (let i = 0; i < tracks.length; i++) {
    player["buffers" + playerNum] = buffers["track" + playerNum];
    playerNum++;
  }

  const players = new Tone.Players(player);

  console.log("playerName:", players.player.name); // player

  players.player("player").start(); // Uncaught Error: No Player with the name player exists on this object

  return (
    <div>
      <button onClick={() => console.log(players)}>click me</button>
      <div className="mixer">
        {tracks.map((track) => (
          <input
            key={track.slug}
            type="range"
            orient="vertical"
            className="fader"
            min="-90"
            max="20"
            step="1"
            onChange={(e) => {
              channels[track.slug].set({ volume: e.target.value });
            }}
          />
        ))}
      </div>
    </div>
  );
}

export default App;
aMadReason commented 2 years ago

+1

stevebarakat commented 2 years ago

Now that I have a better understanding of the use case for Tone.Players, I understand why there is no Players.startAll() method. Tone.Players is an API for making sampler instruments, for example:

const players = new Tone.Players({
    url:{
        "c0": "../samples/c0.wav",
        "c#0": "../samples/c#0.wav",
        "d0": "../samples/d0.wav",
        "d#0": "../samples/d#0.wav",
        "e0": "../samples/e0.wav",
        //...
    }
})

So you obviously wouldn't want to start playing every note on the instrument simultaneously.

In the case of making an audio mixer with multiple tracks that need to start simultaneously, use the sync method. https://tonejs.github.io/docs/14.7.77/Player#sync

aMadReason commented 1 year ago

That makes sense!

I'm using it for a simple audio player for a text game (I want to apply effects to tracks on the fly e.g. sound from inside a room and outside it behind a door).

I also wanted it to play looped tracks and then other tracks at random intervals depending on a chance paramater (like the random creepy sounds in minecraft)

I've coded a simple implementation for my use case which was easy enough to implement using Tone and it's effects, filters and players. It makes sense not to have the playAll method based on what you've outlined above :)