Web MIDI API W3C

Musical Instrument Digital Interface (MIDI) protocol是乐器(instruments),控制器(controllers)和电脑(computers)之间的的通信协议
MIDI不直接发送音频信号,而是发送event messages,包含了musical notes,controller signals(volume,vibrato,panning,cues,clock signals)来设置tempo等。

Web MIDI API 将不会实现高级的概念,比如sequencing,不直接支持 MIDI文件(MIDI播放器可以使用Web MIDI API创建)

requestMIDIAccess()

The feature name for requestMIDIAccess() is midi

partial interface Navigator {
  [SecureContext] 
  Promise <MIDIAccess> requestMIDIAccess(optional MIDIOptions options = {});
};

MIDIOptions Dictionary

dictionary MIDIOptions {
  boolean sysex;
  boolean software;
};

MIDIInputMap Interface

[SecureContext, Exposed=Window] interface MIDIInputMap {
  readonly maplike <DOMString, MIDIInput>;
};

This type is used to represent all the currently available MIDI input ports.

// to tell how many entries there are:
var numberOfMIDIInputs = inputs.size;

// add each of the ports to a <select> box
inputs.forEach( function( port, key ) {
  var opt = document.createElement("option");
  opt.text = port.name;
  document.getElementById("inputportselector").add(opt);
});

MIDIOutputMap Interface

[SecureContext, Exposed=Window] interface MIDIOutputMap {
  readonly maplike <DOMString, MIDIOutput>;
};

This type is used to represent all the currently available MIDI output ports. This enables:

// to tell how many entries there are:
var numberOfMIDIOutputs = outputs.size;

// add each of the ports to a <select> box
outputs.forEach( function( port, key ) {
  var opt = document.createElement("option");
  opt.text = port.name;
  document.getElementById("outputportselector").add(opt);
});

MIDIAccess Interface

provides the methods to list MIDI input and output devices, and obtain access to an individual device

[SecureContext, Exposed=Window] interface MIDIAccess: EventTarget {
  readonly attribute MIDIInputMap inputs;
  readonly attribute MIDIOutputMap outputs;
  attribute EventHandler onstatechange;
  readonly attribute boolean sysexEnabled;
};
属性 描述
inputs MIDI输入
outputs MIDI输出
onstatechange 新的端口连接,或者已有的端口变化
sysexEnabled

MIDIPort Interface

This interface represents a MIDI input or output port.

[SecureContext, Exposed=Window] interface MIDIPort: EventTarget {
  readonly attribute DOMString id;
  readonly attribute DOMString? manufacturer;
  readonly attribute DOMString? name;
  readonly attribute MIDIPortType type;
  readonly attribute DOMString? version;
  readonly attribute MIDIPortDeviceState state;
  readonly attribute MIDIPortConnectionState connection;
  attribute EventHandler onstatechange;
  Promise <MIDIPort> open();
  Promise <MIDIPort> close();
};
属性 描述
id A unique ID of the port
manufacturer 端口的生产商
name 端口的名称
type 输入(input)或输出(output)的标志
version
state disconnected 或 connected
connection open 或 close 或 pending
onstatechange
open 打开端口
close 关闭端口

MIDIInput Interface

[SecureContext, Exposed=Window] interface MIDIInput: MIDIPort {
  attribute EventHandler onmidimessage;
};

MIDIOutput Interface

[SecureContext, Exposed=Window] interface MIDIOutput : MIDIPort {
  void send(sequence<octet> data, optional DOMHighResTimeStamp timestamp = 0);
  void clear();
};

MIDIMessageEvent Interface

[SecureContext, Exposed=Window]
interface MIDIMessageEvent : Event {
  constructor(DOMString type, optional MIDIMessageEventInit eventInitDict = {});
  readonly attribute Uint8Array data;
};

例子

获得midi接口

var midi = null;  // global MIDIAccess object

function onMIDISuccess( midiAccess ) {//如果连接midi设备成功
  console.log( "MIDI ready!" );
  midi = midiAccess;  // store in the global (in real usage, would probably keep in an object instance)
}

function onMIDIFailure(msg) {//如果连接midi设备失败
  console.log( "Failed to get MIDI access - " + msg );
}

navigator.requestMIDIAccess().then( onMIDISuccess, onMIDIFailure );

获得midi的输入和输出端口

function listInputsAndOutputs( midiAccess ) {
  for (var entry of midiAccess.inputs) {
    var input = entry[1];
    console.log( "Input port [type:'" + input.type + "'] id:'" + input.id +
      "' manufacturer:'" + input.manufacturer + "' name:'" + input.name +
      "' version:'" + input.version + "'" );
  }

  for (var entry of midiAccess.outputs) {
    var output = entry[1];
    console.log( "Output port [type:'" + output.type + "'] id:'" + output.id +
      "' manufacturer:'" + output.manufacturer + "' name:'" + output.name +
      "' version:'" + output.version + "'" );
  }
}

操作MIDI的输入

function onMIDIMessage( event ) {
  var str = "MIDI message received at timestamp " + event.timestamp + "[" + event.data.length + " bytes]: ";
  for (var i=0; i<event.data.length; i++) {
    str += "0x" + event.data[i].toString(16) + " ";
  }
  console.log( str );
}

function startLoggingMIDIInput( midiAccess, indexOfPort ) {
  midiAccess.inputs.forEach( function(entry) {entry.onmidimessage = onMIDIMessage;});
}

操作MIDI的输出

function sendMiddleC( midiAccess, portID ) {
  var noteOnMessage = [0x90, 60, 0x7f];    // note on, middle C, full velocity
  var output = midiAccess.outputs.get(portID);
  output.send( noteOnMessage );  //omitting the timestamp means send immediately.
  output.send( [0x80, 60, 0x40], window.performance.now() + 1000.0 ); // Inlined array creation- note off, middle C,
                                                                      // release velocity = 64, timestamp = now + 1000ms.
}

var context=null;   // the Web Audio "context" object
var midiAccess=null;  // the MIDIAccess object.
var oscillator=null;  // the single oscillator
var envelope=null;    // the envelope for the single oscillator
var attack=0.05;      // attack speed
var release=0.05;   // release speed
var portamento=0.05;  // portamento/glide speed
var activeNotes = []; // the stack of actively-pressed keys

window.addEventListener('load', function() {
  // patch up prefixes
  window.AudioContext=window.AudioContext||window.webkitAudioContext;

  context = new AudioContext();
  if (navigator.requestMIDIAccess)
    navigator.requestMIDIAccess().then( onMIDIInit, onMIDIReject );
  else
    alert("No MIDI support present in your browser.  You're gonna have a bad time.")

  // set up the basic oscillator chain, muted to begin with.
  oscillator = context.createOscillator();
  oscillator.frequency.setValueAtTime(110, 0);
  envelope = context.createGain();
  oscillator.connect(envelope);
  envelope.connect(context.destination);
  envelope.gain.value = 0.0;  // Mute the sound
  oscillator.start(0);  // Go ahead and start up the oscillator
} );

function onMIDIInit(midi) {
  midiAccess = midi;

  var haveAtLeastOneDevice=false;
  var inputs=midiAccess.inputs.values();
  for ( var input = inputs.next(); input && !input.done; input = inputs.next()) {
    input.value.onmidimessage = MIDIMessageEventHandler;
    haveAtLeastOneDevice = true;
  }
  if (!haveAtLeastOneDevice)
    alert("No MIDI input devices present.  You're gonna have a bad time.");
}

function onMIDIReject(err) {
  alert("The MIDI system failed to start.  You're gonna have a bad time.");
}

function MIDIMessageEventHandler(event) {
  // Mask off the lower nibble (MIDI channel, which we don't care about)
  switch (event.data[0] & 0xf0) {
    case 0x90:
      if (event.data[2]!=0) {  // if velocity != 0, this is a note-on message
        noteOn(event.data[1]);
        return;
      }
      // if velocity == 0, fall thru: it's a note-off.  MIDI's weird, y'all.
    case 0x80:
      noteOff(event.data[1]);
      return;
  }
}

function frequencyFromNoteNumber( note ) {
  return 440 * Math.pow(2,(note-69)/12);
}

function noteOn(noteNumber) {
  activeNotes.push( noteNumber );
  oscillator.frequency.cancelScheduledValues(0);
  oscillator.frequency.setTargetAtTime( frequencyFromNoteNumber(noteNumber), 0, portamento );
  envelope.gain.cancelScheduledValues(0);
  envelope.gain.setTargetAtTime(1.0, 0, attack);
}

function noteOff(noteNumber) {
  var position = activeNotes.indexOf(noteNumber);
  if (position!=-1) {
    activeNotes.splice(position,1);
  }
  if (activeNotes.length==0) {  // shut off the envelope
    envelope.gain.cancelScheduledValues(0);
    envelope.gain.setTargetAtTime(0.0, 0, release );
  } else {
    oscillator.frequency.cancelScheduledValues(0);
    oscillator.frequency.setTargetAtTime( frequencyFromNoteNumber(activeNotes[activeNotes.length-1]), 0, portamento );
  }
}

参考:
https://webaudio.github.io/web-midi-api/#midiaccess-interface

发布了1794 篇原创文章 · 获赞 582 · 访问量 154万+

猜你喜欢

转载自blog.csdn.net/claroja/article/details/104165454
w3c