LoginSignup
2
1

More than 3 years have passed since last update.

Rust つかって、ポケットミクを動かしてみる。

Last updated at Posted at 2020-05-04

ああああ、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を追加。

Cargo.toml
[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片手にあーでもない、こーでもないでこうなりました。とりあえず、あー、と音が出るのは確認。

src/main.rs
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を使って、発話するものを変えたいなあ…

以上になります。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1