Rust で音のうなりを体感する

## うなりとは
音叉を叩くとポワンポワンってなるヤツです。理科の授業でやりましたね。[うなり - Wikipedia](https://ja.wikipedia.org/wiki/%E3%81%86%E3%81%AA%E3%82%8A) ページの「うなり」の音声サンプルを再現してみたいと思います。Rust で。

## コード
8bit ステレオの PCM データは`buf: Vec<u8>`としたとき、`buf[0]`が左の音、`buf[1]`が右の音、`buf[2]`が左、`buf[3]`が右…という構造になっています。この`buf`に正弦波を符号化して 0～255 の値を詰め込んでいけばよいわけです。

PCM データさえ準備できれば、あとは PulseAudio or Windows の API を叩くだけなので丸っとコードを載せます。Linux の場合`libpulse-binding`という crate があるのでそれを使用します。Windows の場合は`winapi`crate で `waveOut***`関数を使います。
**※実行すると音が鳴ります。スピーカーで聞かないと「うなり」は分かりません。**

```toml:Cargo.toml
[package]
name = "beat"
version = "0.1.0"
authors = ["benki"]
edition = "2018"

[target.x86_64-unknown-linux-gnu.dependencies]
libpulse-binding = { version = "2.16", default-features = false }
libpulse-simple-binding = { version = "2.16", default-features = false }

[target.x86_64-pc-windows-msvc.dependencies]
winapi = { version = "0.3", features = ["mmsystem", "mmeapi"] }
```

```rust:main.rs
use std::f64::consts::PI;

// サンプリングレート。大きいほど音が良い
const SAMPLING_RATE: u32 = 44100; // 44.1kHz
// 周波数。大きいほど音が高い
const FREQUENCY: u32 = 440; // ラ♪

enum Sound {
    Left,
    Right,
    Both,
    Blank,
}

fn main() {
    // PCM データの準備
    let mut buf: Vec<u8> = [
        // 左のスピーカーから4秒音を出す
        (Sound::Left, 4),
        // 1秒無音
        (Sound::Blank, 1),
        // 右のスピーカーから4秒音を出す
        (Sound::Right, 4),
        // 1秒無音
        (Sound::Blank, 1),
        // 両方のスピーカーから音を出す
        // 左右のスピーカーで少しだけ周波数が違うので
        // 「うなり」が聴こえる（はず）
        (Sound::Both, 4),
    ]
    .iter()
    .flat_map(sampling)
    .collect();

    #[cfg(target_os = "linux")]
    beat_on_linux(&buf);

    #[cfg(target_os = "windows")]
    beat_on_windows(&mut buf);
}

fn sampling((sound, sec): &(Sound, u32)) -> Vec<u8> {
    (0..SAMPLING_RATE * 2 * sec)
        .map(|n| {
            let (i, frequency) = match (sound, n % 2) {
                // 左のスピーカーから出す音は周波数を 2 下げる
                // 右の音は 2 上げる
                (Sound::Left, 0) => (n / 2, FREQUENCY - 2),
                (Sound::Right, 1) => ((n - 1) / 2, FREQUENCY + 2),
                (Sound::Both, 0) => (n / 2, FREQUENCY - 2),
                (Sound::Both, 1) => ((n - 1) / 2, FREQUENCY + 2),
                (_, _) => (0, 0),
            };
            (((i as f64 * 2.0 * frequency as f64 / SAMPLING_RATE as f64 * PI).sin() + 1.0) * 128.0)
                as u8
        })
        .collect()
}

#[cfg(target_os = "linux")]
fn beat_on_linux(buf: &[u8]) {
    use libpulse_binding::{sample, stream::Direction};
    use libpulse_simple_binding::Simple;

    let spec = sample::Spec {
        format: sample::Format::U8, // 8bit 量子化
        channels: 2,                // ステレオ
        rate: SAMPLING_RATE,
    };

    let pasimple = Simple::new(
        None,                // Use the default server
        "BeatApp",           // Our application’s name
        Direction::Playback, // We want a playback stream
        None,                // Use the default device
        "Beat",              // Description of our stream
        &spec,               // Our sample format
        None,                // Use default channel map
        None,                // Use default buffering attributes
    )
    .unwrap();

    pasimple.write(buf).unwrap();
    pasimple.drain().unwrap();
}

#[cfg(target_os = "windows")]
fn beat_on_windows(buf: &mut [u8]) {
    use std::mem;
    use std::thread;
    use std::time::Duration;
    use winapi::{
        shared::mmreg::{WAVEFORMATEX, WAVE_FORMAT_PCM},
        um::{
            mmeapi::{
                waveOutClose, waveOutOpen, waveOutPrepareHeader, waveOutReset,
                waveOutUnprepareHeader, waveOutWrite,
            },
            mmsystem::{CALLBACK_NULL, HWAVEOUT, WAVEHDR, WAVE_MAPPER},
        },
    };

    unsafe {
        // 以下諸々の初期化処理
        let mut wfe = init::<WAVEFORMATEX>();
        wfe.wFormatTag = WAVE_FORMAT_PCM;
        wfe.nChannels = 2; // ステレオ
        wfe.nSamplesPerSec = SAMPLING_RATE;
        wfe.nAvgBytesPerSec = SAMPLING_RATE;
        wfe.wBitsPerSample = 8; // 量子化ビット数
        wfe.nBlockAlign = wfe.nChannels * wfe.wBitsPerSample / 8;

        // デバイスを開く。エラーで無ければ MMSYSERR_NOERROR が返る
        let mut h_wave = init::<HWAVEOUT>();
        waveOutOpen(&mut h_wave, WAVE_MAPPER, &wfe, 0, 0, CALLBACK_NULL);

        let mut whdr = init::<WAVEHDR>();
        whdr.lpData = buf.as_mut_ptr() as *mut i8;
        whdr.dwBufferLength = buf.len() as u32;
        whdr.dwFlags = 0;
        whdr.dwLoops = 1;
        waveOutPrepareHeader(h_wave, &mut whdr, size_of::<WAVEHDR>());
        
        // 音を出力
        waveOutWrite(h_wave, &mut whdr, size_of::<WAVEHDR>());
        thread::sleep(Duration::from_secs(15));
        
        // 終了処理
        waveOutReset(h_wave);
        waveOutUnprepareHeader(h_wave, &mut whdr, size_of::<WAVEHDR>());
        waveOutClose(h_wave);
    }
    // ヘルパー関数
    unsafe fn init<T>() -> T {
        mem::MaybeUninit::<T>::uninit().assume_init()
    }
    fn size_of<T>() -> u32 {
        mem::size_of::<T>() as u32
    }
}
```
周波数をいろいろ変えて音の高さを変えてみたり、サンプリングレートを変えて音質の変化を感じてみるのもおもしろいかもしれません。人間の可聴域は 20Hz〜20000Hz らしいですが、私の環境（PC と耳の性能）では 200〜13000Hz ぐらいの範囲しか聞こえませんでした。