最近の個人開発について

-- Views

March 13, 26

スライド概要

Roppongi.rb #40

profile-image

日常と情報科学関連の話題を中心に話してます。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

最近の個人開発について Roppongi.rb #40 Toshio Maki

2.

自己紹介 • Toshio Maki(@Kirika̲K2) • RubyとRailsは15年ぐらいやってます。

3.

作ってるもの • MIDoRi(MIDi on Ruby Interpreter) • https://github.com/kirikak2/midori • プログラマブルMIDIオーケストレー ションツール

4.

モチベーション • 電子楽器(の収集と演奏)を趣味としているので、自宅にあるMIDI デバイスを色々繋いで、プログラマブルに操作したかった

5.

メイン画面 • メイン画面 • BPM制御とか • タップしたらBPM調整してく れる

6.

Pads • パッド • タッチしたら、設定したRubyブ ロックが発火する(予定) • MIDIデバイスにnote onすると音 が鳴る

7.

MIDI Devices • 接続したMIDIのステータスを表示

8.

スクリプト選択画面 • SDカードに登録したRubyスクリ プトを選択して実行 • まだ未実装

9.

ログ • 通常はUSBシリアルコンソールか らログを取得するのですが、 USB-MIDIで潰しているので、ロ グは画面に出してます • 普段は別の開発ボード上で

10.

Freenove-ESP32-S3開発ボード • 本気の場合はこっちを使う。 USBが2個あるので、片方USBMIDIにして、片方USBシリアル コンソールに出来る • オプションで切り替えて、両方 でビルドできるようになってる

11.

先行事例 • midiglue • ノードをつなげてmidiデバイスのデータを 加工とか出来るデバイス • FPGAでmidiデバイスも色々繋げられる • クラウドファンディングで500個だけ作ら れて、現在入手困難。

12.

midiglueとの比較 接続可能デバイス Midori midiglue MIDI-DIN(1個) MIDI-DIN(2個) USB-MIDI(1個) USB-MIDI(1個) CV/GATE(2個) タッチパネルディスプレイ 専用ノブ2個 専用ボタン4個 操作方法 Picoruby 専用エディタでのノードプログラミング ノード自体をC++で組める 入手困難度 簡単 (M5stack Core3で動作) 困難 外部操作方法

13.

picoruby-midi • picorubyのmrbgemとして開発中 • 元々PRK̲Firmware用に作っていたMIDI実装だったが、今回 の開発に合わせて全部実装を入れ替えた

14.

主要mrbgemたち • picoruby-sam2695 • • picoruby-usb̲midi • • M5stack用のMIDIデバイスを使用するためのmrbgem M5stack用のUSB-MIDIデバイスを使用するためのmrbgem Picoruby-ui • midoriで使用するUIを定義したmrbgem(配置場所模索中)

15.

いくつかの主要コンポーネント • MIDI::Device • • sam2695やusb̲midiのMIDI出力を透過的に扱うためクラス MIDI::Input • 同様にMIDI入力を透過的に扱うためのクラス

16.

その他ヘルパー系 • MIDI::MML • • Music Macro Languageを定義するクラス MIDI::MML::Player • MMLを再生してMIDI::Deviceに流すためのクラス

17.
[beta]
require 'midi'
require 'ui'
require 'usb_midi'
require 'sam2695'
sam = SAM2695.new(17, 18)
device = MIDI::Device.new(sam)
# デバイス接続待ち
sleep_ms(100) until USB_MIDI.instance.connected?
device2 = MIDI.usb_device
# GM Program Numbers
GM_TRUMPET
= 56
GM_FRENCH_HORN
= 60
GM_TUBA
= 58
GM_TIMPANI
= 47
GM_STRING_ENSEMBLE = 48
# 音色設定
def setup_promenade_instruments(device)
device.program_change(GM_TRUMPET, channel: 0)
device.program_change(GM_FRENCH_HORN, channel: 1)
device.program_change(GM_TUBA, channel: 2)
device.program_change(GM_STRING_ENSEMBLE, channel: 3)
device.program_change(GM_TIMPANI, channel: 4)
device.volume(100, channel: 0)
device.volume(85, channel: 1)
device.volume(95, channel: 2)
device.volume(75, channel: 3)
device.volume(90, channel: 4)
device.pan(64, channel: 0)
device.pan(44, channel: 1)
device.pan(64, channel: 2)
device.pan(84, channel: 3)
device.pan(64, channel: 4)
end

# チャンネル0: メインメロディ(トランペット)
# MIDIから正確に抽出した音符
melody1 = MIDI::MML::Sequence.new(<<-MML, channel: 0, velocity: 110)
o4 l4
g f a+ >c8 f8 d c8 f8 d< a+ >c< g f
g f a+ >c8 f8 d c8 f8 d< a+ >c< g f
f g d f8 g8 c g8 a8 f
>f d c8< a+8 f f g d f8 g8
d+ a+8 >c8< g+
>g+ f d+8 c+8< g+ g+ a+ g+ a+8 >c8 d+8
MML
# チャンネル1: ハーモニー(ホルン)
harmony1 = MIDI::MML::Sequence.new(<<-MML, channel: 1, velocity: 90)
o4 l4
r1 d c d
a a a a+ g g c c
r1
>f d c8< r8 r2
r1
g+ f d+8 r8 r2
d+2. a+ g+ d+
>c8 d+8< a+8 g+ f8 g+8
MML
# チャンネル2: ベースライン(チューバ)
bass1 = MIDI::MML::Sequence.new(<<-MML, channel: 2, velocity: 100)
o2 l4
r1 g a g
f d f a+ g c e f
r1
f a+ g f
r1
g+ >c+< a+ g+
<f+2 f f+ f+>
f+ f8 d+8 c+ d+8 f8 g+ a+ g+
MML

18.

# ===== 再生 ===== setup_promenade_instruments(device2) Kernel.sleep_ms(100) players = [ MIDI::MML::Player.new(device2, melody1, loop: true), MIDI::MML::Player.new(device2, harmony1, loop: true), MIDI::MML::Player.new(device2, bass1, loop: true), MIDI::MML::Player.new(device2, strings1, loop: true), MIDI::MML::Player.new(device2, timpani1, loop: true), ] bpm = 92 UI.on(:bpm_change) {|event| bpm = event[:bpm] } input2 = MIDI::Input.new(device2) #input2.on(:note_on) do |event| # UI.log(event[:note]) #end #input2.on(:note_off) do |event| # UI.log(event[:note]) #end # BPM 92(原曲のAllegro giusto) on_loop = Proc.new{ UI.process } on_error = Proc.new{|message| UI.log(message) } bpm_source = Proc.new{ bpm } MIDI.bpm_loop(92, output: device2, subdivisions: 24, send_start: false, on_loop: on_loop, on_error: on_error, bpm_source: bpm_source) do |c| players.each { |p| p.tick(c) } end

19.

デモ

20.

難しいところ • タスクの優先度設定 • • MIDI入力(ハードウェア割り込み)、タッチパネル操作(ハード ウェア割り込み)、Picoruby、イベントキュー処理などのバック グラウンドタスクが多いので、優先度設定をミスるとフリーズし たりする 複数タスクによって同時に共有メモリにアクセスすることがあるの で、タイミングによってCore吐いて死ぬケースがある

21.

ボトルネックになってた 課題が解決できたのが大きい • M5stackのmicroSDカードとディスプレイとPSRAM(外部 RAM)はSPI通信で同時にアクセスできないと思っていた。 • この3つは今回のプロダクトを作る上で絶対に必要な3要素だった • Claude codeに解決策を模索させまくってたら、同時に使う方法を 見つけてきた。

22.

まだできてないところ • microSDに複数のRubyファイル置いて、任意のスクリプトを実行 • • UI.padのイベント制御の実装 • • クリーンアップ処理が課題 これはやるだけ。優先度の問題 External BPMとInternal BPMをSyncさせる仕組み • 強引にやれば出来るんだけど、ガジェットとしての心地よさも追求したい

23.

これからやりたいこと • BLE-MIDI対応 • ユーザの関心と、機器制御の部分をうまく分離してDSLぽくやりたい • Picorubyでエラーが発生した時に、何が問題なのか分からないのを なんとかしたい。(何故かログにもでない) • USB mass storageクラスを実装して、SDカードの中身を透過的に 扱いたかったり(毎回書き換えのためにSDカード抜き差しするのめ んどい)