How to output sound from MIDI keyboard using Tone.js, Microsoft GS wavetable synth, or anything but using an oscillator

22 Views Asked by At

I can produce sound with oscillator, as shown in the code, to approximate a kind of electric piano sound, but I want to use a real piano sound, perhaps using Microsoft GS wavetable synth. How can I code it?

function enableMIDI(element) {
  const onMIDISuccess = (midiAccess) => {
    for (var input of midiAccess.inputs.values()) input.onmidimessage = getMIDIMessage;
  }
  const getMIDIMessage = message => {
    const [command, note, velocity] = message.data;
    switch (command) {
      case 144: // on
        if (velocity > 0) {
          const event = new CustomEvent('noteon', { detail: { note, velocity } });
          element.dispatchEvent(event);
        }
        break;
      case 128: // off
        const event = new CustomEvent('noteoff', { detail: { note } });
        element.dispatchEvent(event);
        break;
    }
  }
  if (navigator.requestMIDIAccess) {
    navigator.requestMIDIAccess().then(onMIDISuccess, () => console.log('Could not access your MIDI devices.'));
  }
}

/* audio.js */
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const masterGain = audioCtx.createGain();
masterGain.connect(audioCtx.destination);

const maxPolyphony = 7;

function createPianoOscillators(freq) {
    const oscillators = [];
    const baseFrequency = freq;

    for (let i = 1; i <= 20; i++) {
        const oscNode = audioCtx.createOscillator();
        oscNode.type = 'sine'; // Change the oscillator type to 'triangle'
        oscNode.frequency.value = baseFrequency * i;

        const gainNode = audioCtx.createGain();
        gainNode.gain.value = 1 / Math.pow(i, 1.5*i^2.3); // Adjust the gain for each harmonic

        oscNode.connect(gainNode);
        oscillators.push({ oscNode, gainNode });
    }

    const masterGainNode = audioCtx.createGain();
    masterGainNode.gain.value = 0.2;

    oscillators.forEach(({ oscNode, gainNode }) => {
        gainNode.connect(masterGainNode);
        oscNode.start(0);
    });

    masterGainNode.connect(masterGain);

    return { oscillators, masterGainNode };
}

const notemap = new Map();

function noteon(key) {
    if (!key.classList.contains('keydown') && notemap.size < maxPolyphony) {
        const { oscillators, masterGainNode } = createPianoOscillators(key.dataset.freq);
        
        // Add an envelope to simulate piano sound with a shorter attack time
        masterGainNode.gain.setValueAtTime(0, audioCtx.currentTime);
        masterGainNode.gain.linearRampToValueAtTime(0.086, audioCtx.currentTime + 0.0001); // Shorten the attack time
        
        // Increase the sustain duration
        masterGainNode.gain.linearRampToValueAtTime(0.1, audioCtx.currentTime + 0.3);

        key.classList.add('keydown');
        notemap.set(key.name, { oscillators, masterGainNode });
    }
}

function noteoff(key) {
    key.classList.remove('keydown');
    const { oscillators, masterGainNode } = notemap.get(key.name);

    if (oscillators) {
        const releaseTime = audioCtx.currentTime + 0.06; // Adjust release time

        masterGainNode.gain.setValueAtTime(masterGainNode.gain.value, audioCtx.currentTime);
        masterGainNode.gain.linearRampToValueAtTime(0.01, releaseTime);

        oscillators.forEach(({ oscNode }) => {
            oscNode.stop(releaseTime);
        });
    }

    notemap.delete(key.name);
}

/* init */
enableMIDI(midi);

midi.addEventListener('noteon', (event) => {
  const note = midi.elements[`midi_${event.detail.note}`];
  note.style.setProperty('--v', event.detail.velocity);
  noteon(note, [{ freq: note.dataset.freq }]);
});

midi.addEventListener('noteoff', (event) => {
  const note = midi.elements[`midi_${event.detail.note}`];
  noteoff(note);
});

midi.addEventListener('submit', event => event.preventDefault());

const keys = midi.querySelectorAll('button');
keys.forEach(key => {
  key.addEventListener('pointerdown', event => {
    noteon(event.target, [{ freq: event.target.dataset.freq }]);
  });
  key.addEventListener('pointerup', event => { noteoff(event.target); });
  key.addEventListener('pointerleave', event => { noteoff(event.target); });
});

Any help appreciated!

I tried to search up how to use Microsoft GS wavetable synth, tone.js, and so far I have no luck.

0

There are 0 best solutions below