groovyでmidi演奏

MIDIライブラリ。CORBAとともに削除してほしい、モジュラーJREの登場後はぜひとも読み込まれてほしくないAPIナンバーワン

http://journal.mycom.co.jp/news/2009/04/22/018/index.html

不要No1がCORBAなのは同意だけど、midiを同列にするとは。こんな楽しいものを削除しようとするとは。。。

midi APIjavaで使うのは面倒だけど、groovyからつかうとかなり簡略化されます。
以下は、さくらさくら(の一部)を演奏するプログラム:

import javax.sound.midi.*

// compose sequence

ticks = 16
// compose utils
def addNote(track, pitch, start, duration=ticks, velocity=64) {
  // pitch: C=60, D=62, E=64, F=65, G=67, A=69, B=71, ...
  // start: absolute time
  // duration: 1/4 = ticks
  // velocity: 0 <= soft < 64 < hard <= 127
  // returns: end time
  message = new ShortMessage()
  message.setMessage(ShortMessage.NOTE_ON, pitch, velocity)
  track.add(new MidiEvent(message, start))
  message = new ShortMessage()
  message.setMessage(ShortMessage.NOTE_OFF, pitch, velocity)
  track.add(new MidiEvent(message, start + duration))
  return start + duration
}
def instChange(track, instNo=0) {
  // instNo: 0-127, default 0
  message = new ShortMessage()
  message.setMessage(ShortMessage.PROGRAM_CHANGE, 0, instNo, 0)
  track.add(new MidiEvent(message, 0))
}

C1=60; D1=62; E1=64; F1=65; G1=67; A1=69; B1=71; C2=72
d1=ticks*4; d2=ticks*2; d4=ticks; d8=ticks/2 as int

score = [
  [A1, d4], [A1, d4], [B1, d2],
  [A1, d4], [A1, d4], [B1, d2],
  [A1, d4], [B1, d4], [C2, d4], [B1, d4],
  [A1, d4], [B1, d8], [A1, d8], [F1, d2],
]

// edit track
seq = new Sequence(Sequence.PPQ, ticks)
track = seq.createTrack()
instChange(track, 107)
t = 0
score.each { pitch, duration ->
  t = addNote(track, pitch, t, duration)
}


// play 
seqr = MidiSystem.sequencer
seqr.tempoInBPM = 72.0
seqr.sequence = seq
seqr.open()

seqr.start()
while (seqr.isRunning()) Thread.sleep(1000)
seqr.close()

groovyだと、mainクラスや変数型や例外処理が不要、getter/setter経由のアクセス、デフォルト変数やクロージャなどでだいぶすっきりかけます。groovyの大きな難点は int/intがBigDecimalになってしまい、intにキャストする必要がある点だろうか。

上記コードではinstは107と適当に設定してます。inst番号と種類の一覧は以下のようなコードで出せるかも

import javax.sound.midi.*

seqr = MidiSystem.sequencer
seqr.open()

// list insts
synth = MidiSystem.synthesizer
println synth.deviceInfo.name
sb = synth.getDefaultSoundbank()
if (sb) {
  println sb.name
  sb.instruments.each {println "${it.patch.program} : ${it.name}" }
}
seqr.close()

midiは初心者にもとりつきやすい機能だと思うんですけどね。

追記: さくらさくら完全化