2025-02-16
どういうふうに設計するかでウンウン悩んでいたけど以下のような感じでいきたい
code: ruby
groovebox = Groovebox.new
synth1 = Synthesizer.new
synth1.harmonics = 1.0, 1.0], [2.0, 0.4
synth1.osillator.waveform = :sawtooth
synth1.envelope.attack = 0.01
synth1.envelope.decay = 0.1
synth1.envelope.sustain = 0.7
synth1.envelope.release = 0.5
synth1.effectors.add(Effector::Distortion.new)
synth1.effectors.add(Effector::Reverb.new)
groovebox.add_track synth1
synth2 = Synthesizer.new #snip groovebox.add_track synth2
compressor = Effector::compressor.new
groovebox.add_effector compressor
DRb.start_service
seq = DRbObject.new_with_uri('druby:://localhost:8787')
groovebox.set_sequencer seq
Thread.new do
handle_midi_signal(groovebox)
end
grooveboxの中にトラックを生やして、一度に出せる音を増やしていくようなイメージ。今はシンセ部分の実装に集中してるからシンセ部分のコードは多め。将来的にはgroovebox内にミキサーを司る係が生えてきてよしなになるはず。
シーケンサー、
handle_midi_signalの中身は自分で書くようなイメージ
例えば今のgroovebox-rubyでは以下のようになっている
code: ruby
def handle_midi_signals(synthesizer, sequencer_player, config)
white_keys = 0, 2, 4, 5, 7, 9, 11 # C, D, E, F, G, A, B loop do
# 白鍵のパッドを光らせる
if white_keys.include?(midi_note % 12)
midi_output.puts(0x90, midi_note, 120)
else
midi_output.puts(0x80, midi_note, 0)
end
end
midi_input.gets.each do |message|
# TODO: 254を受信出来なくなったらエラーを出して終了させる
next if data0 == 254 # Active Sensing を無視 when 0x90 # Note On
if velocity > 0
frequency = 440.0 * (2 ** ((midi_note - 69) / 12.0))
synthesizer.note_on(midi_note, frequency)
end
end
when 0x80 # Note Off or Note On with velocity 0
synthesizer.note_off(midi_note)
end
when 0xB0 # Control Change
# LPF / HPF
cutoff_change = value == 127 ? 10 : -10
if control == 71
synthesizer.vcf.low_pass_cutoff += cutoff_change
elsif control == 72
synthesizer.vcf.high_pass_cutoff += cutoff_change
end
# ADSR
case control
when 73 # Attack
new_attack = 0.01 + (value / 127.0) * 2.49
synthesizer.envelope.attack = new_attack
when 74 # Decay
new_decay = 0.01 + (value / 127.0) * 2.49
synthesizer.envelope.decay = new_decay
when 75 # Sustain
new_sustain = value / 127.0
synthesizer.envelope.sustain = new_sustain
when 76 # Release
new_release = 0.01 + (value / 127.0) * 2.49
synthesizer.envelope.release = new_release
end
if control == config'start' && value == 127 Thread.new do
sequencer_player.play
end
elsif control == config'stop' && value == 127 sequencer_player.stop
end
end
end
end
end
最初はyamlの中にMIDIの信号ごとに辞書的に定義していたけど、最近はもうベタ書きでいいやという感じになっている。一貫性がないのであとで直したい。
自分でハードをつくるとなるとこのあたりのMIDI信号のルールセットは自分で決められるんだけど、今はソフトウェアの部分だけなので書きたい人が自由に設定できるようになる予定。
あとこのgroovebox-rubyの開発に使っているMIDIデバイスがAbleton Moveでこれ自体がGrooveboxなのでそこはちょっとおもろい。曲を作ろうと思うとパッドやロータリーエンコーダーなどが結構な数が必要でそういうのはDAWで使う想定のMIDIコントローラーだと地味に足りないことがある。MIDIキーボードだと充分そろっていることもあるけど、鍵盤な分デカいのだよな。