첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
------------------------------------------------------------------------------------------------------------------------------------------------------
728x90
728x170

■ MML(Music Macro Language) 연주기를 만드는 방법을 보여준다.

test_project.zip
0.00MB

▶ 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
그리드형(광고전용)
Posted by icodebroker
,