1

I am trying to build a web page where I can play the tones using a Bluetooth MIDI controller. I intend to use up to 4 different controllers at once but select only one at a time using the dropdown that lists all the available input MIDI controllers. In the code I am using, even though I can set the MIDI input device, sending the command from any other connected device (but not selected in the dropdown) also activates the tones.

I am not able to figure it out. https://codepen.io/wittywit/pen/NWMeQjr?

    import * as Tone from 'https://cdn.skypack.dev/tone@v14.7.58';
        let reverb = new Tone.JCReverb(0).toDestination();
        let delay = new Tone.FeedbackDelay(0);

        const synth = new Tone.Synth().chain(delay, reverb).toDestination();
const now = Tone.now();
const notes = ['A3','A#3','B3','C4','C#4','D4','D#4','E4','F4','F#4'];
const keyboardNotesMap = {
  'q': 'A3',
  'w': 'A#3',
  'e': 'B3',
  'r': 'C4',
  't': 'C#4',
  'y': 'D4',
  'u': 'D#4',
  'i': 'E4',
  'o': 'F4',
  'p': 'F#4'
};
const midiNotesMap = {
  '57': 'A3',
  '58': 'A#3',
  '59': 'B3',
  '60': 'C4',
  '61': 'C#4',
  '62': 'D4',
  '63': 'D#4',
  '64': 'E4',
  '65': 'F4',
  '66': 'F#4'
};
let pianoElement;
let keyElements;

function getUpDOM() {
  const pianoElement = document.getElementById('piano');
  notes.forEach((note) => {
    const noteDiv =  document.createElement("div");
    noteDiv.textContent = note;
    noteDiv.className = 'note';
    noteDiv.setAttribute('data', note);
    piano.appendChild(noteDiv);
  });

  keyElements = document.getElementsByClassName('note');

}

function keyAction() {
  for (let i = 0; i < keyElements.length; i++) {
    keyElements[i].onmousedown = async function playNote(key) {
      const note = keyElements[i].getAttribute('data');
      synth.triggerAttackRelease(note, "8n");
    };
  };
}

function startUp() {
  document.querySelector('button.start')
      .addEventListener('click', async () => {
    await Tone.start();

    synth.triggerAttackRelease("C4", "8n", now)
    synth.triggerAttackRelease("E4", "8n", now + 0.5)
    synth.triggerAttackRelease("G4", "8n", now + 1)
  });
}

function keyUpHandler(event) {
  for (let i = 0; i < keyElements.length; i++) {
     keyElements[i].classList.remove('active');
  }
}

function keyDownHandler(event) {
console.log(event);
 
  if (event.key && !keyboardNotesMap[event.key]) {
    return;
  }
  
  if (event.midiKey && !midiNotesMap[event.midiKey]) {
    return;
  }
  
   const key = keyboardNotesMap[event.key] || midiNotesMap[event.midiKey];
    console.log('key', key);
    synth.triggerAttackRelease(key, "8n");
    let element;
    for (let i = 0; i < keyElements.length; i++) {
      if( keyElements[i].getAttribute('data') === keyboardNotesMap[event.key] ) {
        element = keyElements[i];
        element.classList.add('active');
      }
    }

}

function keyboardPress() {
  document.addEventListener('keydown', keyDownHandler, false);
  document.addEventListener('keyup', keyUpHandler, false);
}

function delayListner() {
  const delayElement = document.querySelector('.controls .delay');

delayElement.addEventListener('change', (event) => {
    delay = new Tone.FeedbackDelay(event.target.value);
});
}

startUp();
getUpDOM();
keyAction();
keyboardPress();
delayListner();

const addMidiListSelect = (midiInput) => {
    const select = document.querySelector('.midi select')
    var opt = document.createElement('option');
    opt.value = midiInput.value;
    opt.innerHTML = midiInput.name;
    select.appendChild(opt);
}

function parseMidiMessage(message) {
  return {
    command: message.data[0] >> 4,
    channel: message.data[0] & 0xf,
    note: message.data[1],
    velocity: message.data[2] / 127
  }
}

navigator.requestMIDIAccess()
  .then(function(access) {
     console.log('midi access');
     // Get lists of available MIDI controllers
     const inputs = access.inputs;
     const outputs = access.outputs;

     inputs.forEach((input) => {
       addMidiListSelect({ name: input.name, value: input.value });
       // console.log(input.name); /* inherited property from MIDIPort */
       console.log(input);
       input.onmidimessage = function(message) {
          const { command, note } = parseMidiMessage(message);
  
        if(command === 9) {
          console.log(note);
          keyDownHandler({ midiKey: note })
        }
         if(command === 8) {
          console.log(note);
          keyUpHandler({ midiKey: note })
        }
       }
     });
     access.onstatechange = function(e) {

       // Print information about the (dis)connected MIDI controller
       console.log(e.port.name, e.port.manufacturer, e.port.state);
     };
 });

        

Codepen contains the code that I am using.

0 Answers0