夏休みの自由研究「RustでCR戦姫絶唱シンフォギアの大当たりの音を作る」

# 本記事について

仕事もひと段落し、Rustの勉強中にRustでサウンドが作れるということで、以前からやってみたかった夏休みの自由研究
「戦姫絶唱シンフォギアが永遠に当たる気分を作る」というテーマでやってみました。


https://www.amazon.co.jp/gp/product/B09WQT2DQD/ref=dbs_a_def_rwt_hsch_vapi_tkin_p1_i0

## 参考動画

https://www.youtube.com/watch?v=NjfQ4rwCMqc

こちらの二つを掛け合わせてできました。

とても楽しかったです。

## 補足

シンフォギアライブ当選された方おめでとうございます！！

https://www.symphogear-xv.com/live2020-2022/

是非とも11月20日はみなさんの「絶唱」楽しみにしております。

うらやましいなぁ。。。

## ソースコード

このコードに関する解説はぜひ本をお買い求めください。

追加のクレート

```toml
hound = "3.4.0"
```

main.rs

```rust
use hound;
const SAMPLE_RATE: f32 = 44100.0;

fn main() {
    let spec = hound::WavSpec {
        channels: 1,
        sample_rate: SAMPLE_RATE as u32,
        bits_per_sample: 32,
        sample_format: hound::SampleFormat::Float,
    };
    let mut fw = hound::WavWriter::create("saw.wav", spec).unwrap();
    let mut wav: Vec<f32> = vec![];
    let bpm = 120;
    wav.extend(sawtooth_wave(67, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(74, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(75, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(75, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(75, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(75, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(75, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(67, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(67, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(67, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(67, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(67, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(67, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(74, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(74, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(74, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(74, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(74, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(74, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(70, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(70, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(70, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(70, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(70, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(70, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 16), 0.4));
    wav.extend(sawtooth_wave(72, calc_len(bpm, 4), 0.4));

    for v in wav.into_iter() {
        fw.write_sample(v).unwrap();
        println!("{}", v);
    }
}

fn noteno_to_hz(no: i32) -> f32 {
    440.0 * 2.0f32.powf((no - 69) as f32 / 12.0)
}

fn calc_len(bpm: usize, n: usize) -> usize {
    let base_len = (60.0 / bpm as f32) * SAMPLE_RATE;
    ((4.0 / n as f32) * base_len) as usize
}

fn sawtooth_wave(noteno: i32, len: usize, gain: f32) -> Vec<f32> {
    let tone = noteno_to_hz(noteno);
    let form_samples = SAMPLE_RATE / tone;
    let mut wav: Vec<f32> = vec![0.0; len];
    for i in 0..len {
        let pif = (i as f32 / form_samples) % 1.0;
        wav[i] = pif * 2.0 - 1.0;
    }
    wav.into_iter().map(|v| (v * gain) as f32).collect()
}

```