こんにちは。新卒エンジニアのhansprocsです!
この度2024年8月に行われた大阪Ruby会議04に参加してきました。 とても面白いテーマが多かったですね...業務では触れることのできなかったものも多くRubyのポテンシャルはまだまだあると感じました。
大阪Ruby会議に参加した話については以下記事をご覧ください!
さて、カンファレンスに参加しただけでは知識が身につかないので、早速手を動かしてみました。
大阪Ruby会議で紹介されたtree-sitter-rbs
のようにRustとRubyを繋げたgemを作っています。
(RustとRubyって並べてみると文字の感じも似ていていいですね)
どんなもの?
audirubyを実行すると以下のようなUI画面が開きます。
Start Capture
でオーディオの入力を検知し始める形となっています。
入出力の制御は開発中ですが、ひとまずRust側でオーディオの検知ができていることをログとして出しています。
画面にも出ている通り、周波数やコードを検知してチューナーに近いものを見せる予定です。
$ ruby lib/audiruby.rb Rust library loaded successfully Ruby: Calling start_audio_capture Ruby: Calling Rust method _rust_start_audio_capture Rust: start_audio_capture called Ruby: start_audio_capture result: "Audio capture started"
なぜRust?
というところで、RustでGemを作る話を聞いて自分で触らずにはいられなくなり、実際にgemの雛形を作ってみました。
私はギターのエフェクターが好きで、前から自分でRubyやJavaScriptを使ってギターの音の入出力を制御してみたいと思っていました。 しかし、少し触ってみた感じだとRubyやJavaScriptの処理速度ではどうしてもレイテンシが生まれてしまい、半分諦めの状態でした。
そこで今回の大阪Ruby会議でRustを使ってRubyのgemが開発できるということを聞き、速く処理して欲しいものはRustに任せ、UIなどはRubyに任せることで今までやってみたかったことが実現できるのではないか、と思いました。
C言語の方が歴史が深く情報が多いかもしれませんが、言語自体のハードルとしてはRustの方が私の目を惹きましたね。
実装
まずは以下が今回作ったgemの雛形になります。
RubyでAudioを制御したい、というところでaudiruby
と名づけました。
まだRust側でオーディオのリアルタイムキャプチャーができていることをRubyが検知しているだけのものになります。
ライブラリー
RubyでRustを呼び出すためにrutie
を、GUIを見せるためtk
を採用しています。
コード
Rustのコードを呼び出すに、以下のようなことをしています。
AudioProcessor._rust_start_audio_capture
のようにオブジェクトに持たせて呼び出している形ですね。
def process(input) puts "Ruby: Calling process" begin result = _rust_process(input) puts "Ruby: process result: #{result.inspect}" result rescue => e puts "Ruby: Error in process: #{e.message}" nil end end def _rust_start_audio_capture puts "Ruby: Calling Rust method _rust_start_audio_capture" # Rustのコードからできたオブジェクトを呼び出す AudioProcessor._rust_start_audio_capture rescue => e puts "Error calling Rust method: #{e.message}" puts e.backtrace raise end
Rustでは以上の実装のためにlib.rs
でこのような実装をしています。
大阪Ruby会議のスピーチでもあった話ですが、RString
のようにRustでRubyの型を制御することに非常に苦労しました。
メモリ管理に厳しいRustの特徴の関係で、Rubyと繋がりを持たせることが難しかったですね。
fn ruby_process(input: RString) -> AnyObject { println!("Rust: process called with input: {:?}", input); match input.map_err(|e| VM::raise_ex(e)) { Ok(s) => audio_processor::AudioProcessor::process(s), Err(_) => RString::new_utf8("Error processing input").into() } } fn ruby_start_audio_capture() -> AnyObject { println!("Rust: start_audio_capture called"); audio_processor::AudioProcessor::start_audio_capture() }
audirubyのこれから
まずはgemとして公開できる最低限の要件として、ギターのチューナーを作るところから始めてみたいと思います。
rust-ttf
ライブラリーを使ってプーリエ変換した値を元に、現在弾いている音やコードを検知し、Ruby側に渡す。
Rubyでは渡ってきたものをUIで出力してみたいと思っています。
さらに、エフェクターまで実装できたら夢のようなアプリケーションの誕生ですね!