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

ああああ、Linux Kernelのドキュメント読み飽きたんじゃー、ということで、おもちゃ出てきたし遊ぶ。

# ようやく：Rust でポケットミクを動かしてみた

- やっぱり物が動くのはよい。
- low level/ raw level デバッグに大切。

# 参考資料
下記サイトの内容をベースに検討しました。ただ、まだ声を変更できてないので、もうちょっと検討したいなあ……

- [Linuxビンボ道～明日がALSAプログラム編](http://www.nurs.or.jp/~sug/soft/binbo/binbo5.htm)
- [ポケット・ミク（NSX-39）のSysExメモ](http://mergecells.hatenablog.com/entry/2016/10/04/231818#%E3%81%AA%E3%81%9C%E3%83%9D%E3%82%B1%E3%83%9F%E3%82%AF%E3%81%A7SysEx%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%AA%E3%81%AE%E3%81%8B)

# なにをしたかったのか
大掃除したら出てきた、「ポケットミク」を、RUSTつかって動かしてみようかなーと

# alsaで認識しているIDなどを確認。

accountコマンドをつかって確認したところ、NSX-39だから、"20:0" でよさそう。

```shell
[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を追加。

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

```rust: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を使って、発話するものを変えたいなあ…

以上になります。