ああああ、Linux Kernelのドキュメント読み飽きたんじゃー、ということで、おもちゃ出てきたし遊ぶ。
ようやく:Rust でポケットミクを動かしてみた
- やっぱり物が動くのはよい。
- low level/ raw level デバッグに大切。
参考資料
下記サイトの内容をベースに検討しました。ただ、まだ声を変更できてないので、もうちょっと検討したいなあ……
なにをしたかったのか
大掃除したら出てきた、「ポケットミク」を、RUSTつかって動かしてみようかなーと
alsaで認識しているIDなどを確認。
accountコマンドをつかって確認したところ、NSX-39だから、"20:0" でよさそう。
[kmtr@localhost rust-alsa]$ sudo LANG=C aconnect -l
client 0: 'System' [type=kernel]
0 'Timer '
1 'Announce '
client 14: 'Midi Through' [type=kernel]
0 'Midi Through Port-0'
client 16: 'Ensoniq AudioPCI' [type=kernel,card=0]
0 'ES1371 '
client 20: 'NSX-39' [type=kernel,card=1]
0 'NSX-39 MIDI 1 '
[kmtr@localhost rust-alsa]$
rustでmidiを操作するためのライブラリを組み込む(ALSA)
Cargo.toml に、alsaとclapを追加。
[package]
name = "rust-alsa"
version = "0.1.0"
authors = ["kmtr"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
alsa = "0.4.2"
clap = "2.20"
alsa経由でポケットミクから音を出す
rustのalsa wrapper片手にあーでもない、こーでもないでこうなりました。とりあえず、あー、と音が出るのは確認。
extern crate alsa;
use std::error;
use alsa::seq;
use std::ffi::CString;
use std::thread::sleep;
use std::time::Duration;
const DEFAULT_NAME: &str = "RUSTMIKU";
const MIKU_ID: i32 = 20;
const MIKU_PORT: i32 = 0;
fn note( s: &alsa::Seq, evt : alsa::seq::EventType, channel : u8, note: u8, velocity : u8, s_port:i32)
{
let o_note = alsa::seq::EvNote {
channel,
note,
velocity,
off_velocity: 0,
duration: 0
};
let mut o_event = alsa::seq::Event::new( evt , &o_note );
o_event.set_direct();
o_event.set_source( s_port );
o_event.set_dest( alsa::seq::Addr { client: MIKU_ID, port: MIKU_PORT } );
s.event_output( &mut o_event ) ;
s.drain_output();
}
fn sing() -> Result<alsa::Seq, Box<dyn error::Error> > {
// snd_seq_open()
let miku = alsa::Seq::open( None, Some(alsa::Direction::Playback), false ) ?;
// snd_seq_create_simple_port()
let cstr = CString::new(DEFAULT_NAME)?;
let client_port = miku.create_simple_port(&cstr,
alsa::seq::PortCap::READ,
alsa::seq::PortType::MIDI_GENERIC | alsa::seq::PortType::APPLICATION )?;
// subscribe_port()
let client_id = miku.client_id()?;
let subs = seq::PortSubscribe::empty()?;
subs.set_dest (seq::Addr { client: MIKU_ID, port: MIKU_PORT } );
subs.set_sender(seq::Addr { client: client_id, port: client_port });
miku.subscribe_port(&subs)?;
// Note on
note ( &miku, alsa::seq::EventType::Noteon, 0, 60, 64, client_port );
// Wait
sleep ( Duration::from_millis(1000) );
// Note off
note ( &miku, alsa::seq::EventType::Noteoff, 0, 60, 64, client_port );
Ok(miku)
}
fn main() {
sing();
}
debugに役立ったのは、strace
最初、音がでなくて、なんでだろーと悩んでました。
この時、debugに役に立ったのは、strace
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
ioctl(3, SNDRV_SEQ_IOCTL_PVERSION, 0x7ffe4a9ffeb8) = 0
ioctl(3, SNDRV_SEQ_IOCTL_CLIENT_ID, 0x7ffe4a9ffebc) = 0
ioctl(3, SNDRV_SEQ_IOCTL_RUNNING_MODE, 0x7ffe4a9ffec0) = 0
ioctl(3, SNDRV_SEQ_IOCTL_CREATE_PORT, 0x7ffe4aa00260) = 0
ioctl(3, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, 0x560006f2b5b0) = 0
write(3, "\5\0\0\375\0\0\0\0\0\0\0\0\0\0\24\0\0<@\0\0\0\0\0\0\0\0\0", 28) = -1 EINVAL (Invalid argument)
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffe4aa00340) = 0
write(3, "\5\0\0\375\0\0\0\0\0\0\0\0\0\0\24\0\0<@\0\0\0\0\0\0\0\0\0\7\0\0\375"..., 56) = -1 EINVAL (Invalid argument)
close(3) = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
munmap(0x7f57fe537000, 8192) = 0
exit_group(0) = ?
+++ exited with 0 +++
ん?なんか処理できてなーい。で、
note ( &miku, alsa::seq::EventType::Note, 0, 60, 64, client_port );
ではなく、
note ( &miku, alsa::seq::EventType::Noteon, 0, 60, 64, client_port );
というのに気が付いたと。
fcntl(3, F_SETFD, FD_CLOEXEC) = 0
ioctl(3, SNDRV_SEQ_IOCTL_PVERSION, 0x7ffc72b00148) = 0
ioctl(3, SNDRV_SEQ_IOCTL_CLIENT_ID, 0x7ffc72b0014c) = 0
ioctl(3, SNDRV_SEQ_IOCTL_RUNNING_MODE, 0x7ffc72b00150) = 0
ioctl(3, SNDRV_SEQ_IOCTL_CREATE_PORT, 0x7ffc72b004f0) = 0
ioctl(3, SNDRV_SEQ_IOCTL_SUBSCRIBE_PORT, 0x556f313ee5b0) = 0
write(3, "\6\0\0\375\0\0\0\0\0\0\0\0\0\0\24\0\0<@\0\0\0\0\0\0\0\0\0", 28) = 28
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc72b005d0) = 0
write(3, "\7\0\0\375\0\0\0\0\0\0\0\0\0\0\24\0\0<@\0\0\0\0\0\0\0\0\0", 28) = 28
close(3) = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
munmap(0x7f59d2ff5000, 8192) = 0
exit_group(0)
やっぱり、raw level/low levelでのデバッグって大切ですね!!
TODO
sysexを使って、発話するものを変えたいなあ…
以上になります。