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.