更新

Web Midi APIのテスト[midiio.html]

1. Arduino Davinci(Leonardo互換)でMidiデバイス(Keyboard / Synthesizer)を作る


#include "MIDIUSB.h" #include "PitchToNote.h" #include "pitchToFrequency.h" #define BUZZ_PIN 13 #define N 8 const uint8_t pins[N]={2, 3, 4, 5, 6, 7, 8, 9}; const byte pitches[N]={pitchC3, pitchD3, pitchE3, pitchF3, pitchG3, pitchA3, pitchB3, pitchC4}; uint8_t notesTime[N]; uint8_t buttonState=0x00, lastState=0x00; uint8_t intensity; void setup(){ Serial.begin(115200); for(int i=0; i<N; i++) pinMode(pins[i], INPUT_PULLUP); } void loop(){ // Midi Keyboard for(int i=0; i<N; i++) if(digitalRead(pins[i])==LOW){ bitWrite(buttonState, i, 1); delay(50);} else bitWrite(buttonState, i, 0); int val=analogRead(A0); intensity = (uint8_t)map(val, 0,1023, 0,127); for(int i=0; i<N; i++){ if(bitRead(buttonState, i) != bitRead(lastState, i)){ if(bitRead(buttonState, i)){ bitWrite(lastState, i, 1); noteOnSend(0, pitches[i], intensity); MidiUSB.flush(); }else{ bitWrite(lastState, i, 0); noteOffSend(0, pitches[i], 0); MidiUSB.flush(); } } } // Midi Synthesizer midiEventPacket_t rx=MidiUSB.read(); switch(rx.header){ case 0: break; //No pending events case 0x9: noteOn (rx.byte1&0xF, rx.byte2, rx.byte3); //channel, control, value break; case 0x8: noteOff(rx.byte1&0xF, rx.byte2, rx.byte3); //channel, control, value break; case 0xB: controlChange(rx.byte1&0xF, rx.byte2, rx.byte3); //channel, control, value break; case 0xF: if(rx.byte1==0xf4) pinMode(rx.byte2, OUTPUT); else if(rx.byte1==0xf5) digitalWrite(rx.byte2, rx.byte3); break; default: Serial.print("Unhandled MIDI message: "); Serial.print(rx.header, HEX); Serial.print("-"); Serial.print(rx.byte1, HEX); Serial.print("-"); Serial.print(rx.byte2, HEX); Serial.print("-"); Serial.println(rx.byte3, HEX); } } void controlChangeSend(byte channel, byte control, byte value){ midiEventPacket_t event ={ 0x0B, // event type (0x0B = control change) 0xB0|channel, // event type combined with the channel control, // control number (0-119) value // control value (0-127) }; MidiUSB.sendMIDI(event); } void noteOnSend(byte channel, byte pitch, byte velocity){ midiEventPacket_t noteOn ={ 0x09, // event type (0x09=note on) 0x90|channel, // note-on combined with the channel(0-15) pitch, // note number (48 = middle C) velocity // velocity (64=normal, 127=fastest) }; MidiUSB.sendMIDI(noteOn); } void noteOffSend(byte channel, byte pitch, byte velocity){ midiEventPacket_t noteOff = { 0x08, // event type (0x08=note off) 0x80|channel, // note-off combined with the channel(0-15) pitch, // note number (48 = middle C) velocity // velocity (64=normal, 127=fastest) }; MidiUSB.sendMIDI(noteOff); } const char* pitch_name(byte pitch) { static const char* names[] = {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; return names[pitch % 12]; } int pitch_octave(byte pitch){ return (pitch/12)-1; } void noteOn(byte channel, byte pitch, byte velocity){ tone(BUZZ_PIN, pitchFrequency[pitch]); Serial.print("Note On: "); Serial.print(pitch_name(pitch)); Serial.print(pitch_octave(pitch)); Serial.print(", channel="); Serial.print(channel); Serial.print(", velocity="); Serial.println(velocity); } void noteOff(byte channel, byte pitch, byte velocity){ noTone(BUZZ_PIN); Serial.print("Note Off: "); Serial.print(pitch_name(pitch)); Serial.print(pitch_octave(pitch)); Serial.print(", channel="); Serial.print(channel); Serial.print(", velocity="); Serial.println(velocity); } void controlChange(byte channel, byte control, byte value){ Serial.print("Control change: control="); Serial.print(control); Serial.print(", value="); Serial.print(value); Serial.print(", channel="); Serial.println(channel); } pitchToFrequency.h: #pragma once // Conversion from MIDI pitch codes to frequencies in Hz static const float pitchFrequency[] = { 8.176, 8.662, 9.177, 9.723, 10.301, 10.913, 11.562, 12.250, 12.978, 13.750, 14.568, 15.434, 16.352, 17.324, 18.354, 19.445, 20.602, 21.827, 23.125, 24.500, 25.957, 27.500, 29.135, 30.868, 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55.000, 58.270, 61.735, 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110.000, 116.541, 123.471, 130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220.000, 233.082, 246.942, 261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440.000, 466.164, 493.883, 523.251, 554.365, 587.330, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880.000, 932.328, 987.767, 1046.502, 1108.731, 1174.659, 1244.508, 1318.510, 1396.913, 1479.978, 1567.982, 1661.219, 1760.000, 1864.655, 1975.533, 2093.005, 2217.461, 2349.318, 2489.016, 2637.020, 2793.826, 2959.955, 3135.963, 3322.438, 3520.000, 3729.310, 3951.066, 4186.009, 4434.922, 4698.636, 4978.032, 5274.041, 5587.652, 5919.911, 6271.927, 6644.875, 7040.000, 7458.620, 7902.133, 8372.018, 8869.844, 9397.273, 9956.063, 10548.082, 11175.303, 11839.822, 12543.854, };

2. ChromeでMidiデバイス(Keyboard / Synthesizer)を作る[midiio.html]

midiio.html: <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>Web Midi API</title> <script> var ctx=new AudioContext(), osc={}, gain, midiInputs=[], midiOutputs=[]; var gainNode=ctx.createGain(); gainNode.connect(ctx.destination); window.onload=function(){ if(navigator.requestMIDIAccess) navigator.requestMIDIAccess().then(onMIDIsuccess, onMIDIfailure); else console.log("No MIDI support present in your browser."); } function $(id){ return document.getElementById(id); } function onMIDIfailure(){ console.error('No access to your midi devices.'); } function onMIDIsuccess(midi){ var inputIterator = midi.inputs.values(); for(var o=inputIterator.next(); !o.done; o=inputIterator.next()) midiInputs.push(o.value); for(var i=0; i<midiInputs.length; i++) $("MIDIIN").appendChild(new Option(midiInputs[i].name)); onChangeInputPort(); var outputIterator = midi.outputs.values(); for(var o=outputIterator.next(); !o.done; o=outputIterator.next()) midiOutputs.push(o.value); for(var i=0; i<midiOutputs.length; i++) $("MIDIOUT").appendChild(new Option(midiOutputs[i].name)); } function onChangeInputPort(){ var port=midiInputs[$("MIDIIN").selectedIndex]; if(port) port.onmidimessage=onMIDIMessage; } function onMIDIMessage(mes){ var freq =NoteToFreq(mes.data[1]); var gain =mes.data[2]/128; if(mes.data[0]===144 && mes.data[2]>0) playNote(freq, gain); if(mes.data[0]===128 || mes.data[2]===0)stopNote(freq); } function NoteToFreq(note){ return Math.pow(2, ((note-69)/12))*440; } function playNote(freq, gain){ osc[freq] = ctx.createOscillator(); osc[freq].connect(gainNode); osc[freq].start(ctx.currentTime); osc[freq].frequency.value = freq; gainNode.gain.value = gain; } function stopNote(freq){ osc[freq].stop(ctx.currentTime); osc[freq].disconnect(); } function sendNote(note){ var port=midiOutputs[$("MIDIOUT").selectedIndex]; var noteOnMessage = [0x90, note, 0x7f]; port.send(noteOnMessage); // send immediately port.send([0x80, note, 0x40], window.performance.now() + 500.0); // note off, middle C, release velocity=64, timestamp=now+500ms } </script> </head> <body> <h2>Midi Keyboard/Synth</h2> MIDI IN: <select id=MIDIIN onchange="onChangeInputPort()"></select>---&gt; WebMidi Synthesizer<br><br> MIDI OUT: <select id=MIDIOUT></select> &lt;--- <button onclick=sendNote(60)>ド</button> <button onclick=sendNote(62)>レ</button> <button onclick=sendNote(64)>ミ</button> <button onclick=sendNote(65)>ファ</button> <button onclick=sendNote(67)>ソ</button> </body> </html>
koyama@hirosaki-u.ac.jp