■ MML(Music Macro Language) 연주기를 만드는 방법을 보여준다.
▶ Cargo.toml
1 2 3 4 5 6 7 8 9 10 11 |
[package] name = "test_project" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] hound = "3.4.0" |
▶ wav_writer.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
use std::f32::consts; use std::fs; use std::io; use hound; const SAMPLE_RATE : f32 = 44100.0; #[derive(Debug)] pub struct Note { pub note : i32, pub length : i32 } pub fn write(file_path : &str, note_vector : Vec<Note>, bpm : f32) { let wac_spec : hound::WavSpec = hound::WavSpec { channels : 1, sample_rate : SAMPLE_RATE as u32, bits_per_sample : 16, sample_format : hound::SampleFormat::Int }; let mut wav_writer : hound::WavWriter<io::BufWriter<fs::File>> = hound::WavWriter::create(file_path, wac_spec).unwrap(); for note in note_vector.into_iter() { let length : u32 = (4.0 / note.length as f32 * (60.0 / bpm) * SAMPLE_RATE) as u32; let tone : f32 = if note.note >= 0 { 440.0 * 2.0f32.powf((note.note - 69) as f32 / 12.0) } else { 0.0 }; write_tone(&mut wav_writer, tone, length); } } fn write_tone<W>(wav_writer : &mut hound::WavWriter<W>, tone : f32, length : u32) where W: io::Write + io::Seek { for t in 0..length { let a : f32 = t as f32 / SAMPLE_RATE; let v : f32 = (a * tone * 2.0 * consts::PI).sin(); wav_writer.write_sample((v * i16::MAX as f32) as i16).unwrap(); } } |
▶ mml_parser.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
use std::str; use crate::wav_writer::Note; pub fn parse(source_string : String) -> Vec<Note> { let mut note_vector : Vec<Note> = vec![]; let mut octave : i32 = 5; let mut length : i32 = 4; let mut chars : str::Chars = source_string.chars(); while let Some(character) = chars.next() { match character { 'a'..='g' => { let note : i32 = match character { 'c' => 0, 'd' => 2, 'e' => 4, 'f' => 5, 'g' => 7, 'a' => 9, 'b' => 11, _ => 0 }; let target_note : i32 = note + octave * 12; note_vector.push(Note { note : target_note, length }); }, 'r' => { note_vector.push(Note { note : -1, length }); }, 'o' => { let v : char = chars.next().expect("o 다음에 숫자를 지정해주시기 바랍니다."); let o : i32 = v as i32 - '0' as i32; if o >= 0 && o < 9 { octave = o; } }, 'l' => { let v : char = chars.next().expect("l 다음에 숫자를 지정해주시기 바랍니다."); let l : i32 = v as i32 - '0' as i32; if l >= 1 && l <= 9 { length = l; } }, _ => {}, }; } return note_vector; } |
▶ main.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
mod mml_parser; mod wav_writer; fn main() { let bpm : f32 = 120.0; // 산토끼 let mml_string1 : String = format! ( "{}{}", "o4 l4 g l8 ee g e l4 cd l8 edce l4 gs", "o5 l8 c o4 l8 g o5 l8 c o4 g o5 c o4 g l4 e g l8 d fed l4 c" ); let note_vector1 : Vec<wav_writer::Note> = mml_parser::parse(mml_string1); wav_writer::write("d:/rabbit.wav", note_vector1, bpm); // 작은별 let mml_string2 : String = format! ( "{}{}{}", "o5 l4 ccgg aal2g l4 ffee ddl2c", "l4 ggff eel2d l4 ggff eel2d", "l4 ccgg aal2g l4 ffee ddl2c" ); let note_vector2 : Vec<wav_writer::Note> = mml_parser::parse(mml_string2); wav_writer::write("d:/twinkle_star.wav", note_vector2, bpm); } |