728x90
728x170
■ MML(Music Macro Language) 연주기를 만드는 방법을 보여준다.
▶ Cargo.toml
[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
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
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
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);
}
728x90
그리드형(광고전용)