Tonejs / Tone.js

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

Midi output example #840

Open apiel opened 3 years ago

apiel commented 3 years ago

The feature you'd like Would be great to have an example combining midi output and Tone.Sampler or Tone.Synth.

Additional context From my understanding, Tone.Transport.scheduleRepeat callback trigger a little bit before the moment we want to play a note with Sampler or Synth, so the AudioContext can ajust the timing. Or when if we include the code to send a midi message to an output device, the note we will be played directly. I think this issue show the problem: https://github.com/Tonejs/Tone.js/issues/196 tambien proposed a solution but when I try to implement it, the result are not accurate.

Here would be a start of example:

import * as Tone from 'tone';

let midi;
if (navigator.requestMIDIAccess) {
    navigator.requestMIDIAccess({}).then((midiAccess) => midi = midiAccess, console.error);
} else {
    alert('No MIDI support in your browser.');
}

function midiSend(msg, time) {
    midi.outputs.forEach((output) => output.send(msg, time));
}

const sampler = new Tone.Sampler({ urls: {  ["C4"]: "file.wav" }}).toDestination();
Tone.Transport.scheduleRepeat(function(time){
    // const timingOffset = (Tone.now()- time) * 1000;
    const timingOffset = (performance.now()/1000) - Tone.now();
    midiSend([0x90, 60, 100], timingOffset);
    midiSend([0x80, 60, 0], timingOffset+200);
    // sampler.triggerAttackRelease("C4", "2n", Tone.now());
    sampler.triggerAttackRelease("C4", "2n", time);
}, "1n");

Using midi devices is a very common thing in electronic music and such example would be really great.

apiel commented 3 years ago

I actually wonder if the sync is not better like this:

const audioContext = new AudioContext();
const timingOffset = (performance.now()/1000) - audioContext.currentTime;

https://cartoonbeats.com/how-to-sync-web-audio-api-and-web-midi/