I am wondering how to integrate integrate Webmidi.js, p5.js, and my Akai mPK49 midi controller? This is for a final project for school.
Reading through the Webmidi.js Basics, I was able to get the program to "listed" to my midi controller. Even though the p5 website was throwing a "TypeError: Cannot read properties of undefined (reading 'channels')" error, the note names were printing in the console. I then tried to see if I could play midi notes in my DAW Logic Pro X. I noticed that both logic and the P5.js/Webmidi.js program were both acknowledging that I was playing keys on the controller.
I went on to the next step to add a basic synth and to try to play an actual note. The console stopped showing that I was playing notes and only showed the Akai ports and that Logic Pro was an input and output.
Additionally, I commented out new lines of code for the synth and the console still would not show the midi notes in the console. So I am a bit stumped. Here is a link to the P5.js sketch. Any help is appreciated!
function setup() {
createCanvas(400, 400);
WebMidi
.enable()
//.then(() => console.log("WebMidi enabled!"))
.then(onEnabled)
.catch(err => alert(err));
}
function draw() {
background(220);
}
function onEnabled() {
// Inputs
WebMidi.inputs.forEach(input => console.log(input.manufacturer, input.name));
// Outputs
WebMidi.outputs.forEach(output => console.log(output.manufacturer, output.name));
const myInput = WebMidi.getInputByName("Akai MPK 49");
const mySynth = myInput.channels[1]; // <-- the MIDI channel (10)
myInput.addListener("noteon", e => {
console.log(e.note.identifier);
})
mySynth.addListener("noteon", e => {
console.log(e.note.identifier, e.message.channel);
})
let output = WebMidi.outputs[0];
let channel = output.channels[1];
channel.playNote("C3");
}
The following works on my system. p5.TriOsc() is used to play the notes with this technique. You will need to change the name for your device; you don't need the manufacturer, only the device name. You will also need to add the following script to index.html:
<script src="https://cdn.jsdelivr.net/npm/webmidi@latest/dist/iife/webmidi.iife.js"></script>
function setup() {
createCanvas(200, 200);
osc = new p5.TriOsc(); // Need this to play the notes
env = new p5.Envelope();
WebMidi.enable()
.then(() => console.log("WebMidi enabled!"))
.then(onEnabled)
.catch((err) => alert(err));
}
function draw() {
background(220);
}
function playSound(midiVal) {
osc.start();
freq = midiToFreq(midiVal);
osc.freq(freq);
env.ramp(osc, 0, 1.0, 0);
}
function onEnabled() {
// Inputs
WebMidi.inputs.forEach((input) =>
console.log(input.manufacturer, input.name)
);
// Outputs
WebMidi.outputs.forEach((output) =>
console.log(output.manufacturer, output.name)
);
const myInput = WebMidi.getInputByName("Q Mini");
myInput.addListener("noteon", (e) => {
console.log(e.note.identifier);
console.log("value =", e.message.data[1]);
playSound(e.message.data[1]);
});
}
Alternate Method:
The following is an alternate method which does not require any additional code to be added to index.html or a 'listener' to be requested. Its shortcoming is that onmidimessage is reported twice and therefore the note is played twice. To work around this issue a counter is added and reset to zero after the first iteration, thereby playing the note only once.
let counter = 0;
function setup() {
createCanvas(100, 100);
osc = new p5.TriOsc(); // Need this to play the notes
env = new p5.Envelope();
}
function playSound(midiVal) {
osc.start();
freq = midiToFreq(midiVal);
osc.freq(freq);
env.ramp(osc, 0, 1.0, 0);
}
navigator.requestMIDIAccess().then((midiAccess) => {
console.log(midiAccess.inputs);
Array.from(midiAccess.inputs).forEach((input) => {
input[1].onmidimessage = (msg) => {
if (counter == 0) {
console.log("value =", msg.data[1]);
playSound(msg.data[1]);
}
counter++;
if (counter > 1) counter = 0;
};
});
});
function draw() {
background(220);
}