diff --git a/README.md b/README.md index 1d2fcfb..8cb9747 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,43 @@ # テキスト音楽サクラ(Rust版) -「ドレミ」や「cde」のテキストをMIDIファイルに変換するコンパイラです。 -手軽に音楽を作成できるツールです。 +「ドレミ」や「cde」のテキストをMIDIファイルに変換するコンパイラです。手軽に音楽を作成できるツールです。 +Rustで作られておりマルチプラットフォーム(macOS/Windows/Linux/WebAssembly)で動作します。 ## どこまで作ったか -だいぶ、いろいろな楽譜を再生できます。 - -- 使えるコマンドの一覧が[こちら](src/command.md)にあります。 - ブラウザで手軽に音楽を再生できる[ピコサクラ](https://sakuramml.com/go.php?15)を実装しました。 +- サクラv2と比べると機能は少ないですが、使えるコマンドの一覧が[こちら](src/command.md)にあります。 - 内部構造についての説明が[こちら](dev_memo.md)にあります。 -## 実装予定だが未実装 +### 実装予定だが未実装 - & タイ Slur(type[,value,range]) タイ記号"&"の異音程(スラー)の動作を変更する。type=0:グリッサンド/1:ベンド/2:ゲート/3:アルペジオ ### 未実装で実装予定なし -- onCycle系の連続書き込み命令 (あまり使わない?) -- FOR IF WHILE FUNCTION (別途スクリプト言語からMMLを動的に生成する方が実用的) +- onCycle命令 +- FOR IF WHILE FUNCTION (別途スクリプト言語からMMLを動的に生成する方が実用的かと) + +# インストールについて + +Web版が「[こちら(ピコサクラ)](https://sakuramml.com/go.php?15)」です。 +ブラウザ上で手軽にMIDIファイルを再生できます。 + +ダウンロードして使いたい場合、コマンドライン版が使えます。releaseより各OSのバイナリをダウンロードしてください。 # 使い方 +## コマンドライン版の使い方 + +楽譜情報をテキストに記述します。例えば「test.mml」というファイルに記述します。 +それを、"test.mml"を"test.mid"に変換するには、コマンドラインで以下のようにコマンドを実行します。 + +``` +$ sakuramml test.mml +``` + +## 基本的な使い方 + ``` 音階4 ドレミファソラシ↑ド↓シラソファミレド o4 cdefgab>cc< @@ -132,6 +148,7 @@ v127 c ( c ( c (( c )) c ) c ) c ``` `ceg` `dfa` `egb` `ceg` +「ドミソ」「レファラ」「ミソシ」「ドミソ」 ``` ### 先行指定とCCやPBの連続書き込み @@ -173,5 +190,5 @@ P1 ## 参考 - サクラ(Rust版)のコマンド一覧 --- [command.md](src/command.md) -- サクラv2のコマンド一覧 --- https://sakuramml.com/doc/command/index.htm + - サクラ(v2版)のコマンド一覧 --- https://sakuramml.com/doc/command/index.htm diff --git a/src/lexer.rs b/src/lexer.rs index 509ada5..1cde733 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -33,8 +33,8 @@ pub fn lex(song: &mut Song, src: &str, lineno: isize) -> Vec { 't' => result.push(read_timing(&mut cur, song)), // @ 発音タイミングの指定 (例 t-1) / t.Random=n 'y' => result.push(read_cc(&mut cur, song)), // @ コントロールチェンジの指定 (例 y1,100) 範囲:0-127 / y1.onTime(low,high,len) // uppwer command - 'A'..='Z' | '_' => result.push(read_upper_command(&mut cur, song, ch)), - '#' => result.push(read_upper_command(&mut cur, song, ch)), + 'A'..='Z' | '_' => result.push(read_upper_command(&mut cur, song)), + '#' => result.push(read_upper_command(&mut cur, song)), // flag '@' => result.push(read_voice(&mut cur, song)), // @ 音色の指定 範囲:1-128 '>' => result.push(Token::new_value(TokenType::OctaveRel, 1)), // @ 音階を1つ上げる @@ -87,39 +87,19 @@ pub fn lex(song: &mut Song, src: &str, lineno: isize) -> Vec { } /// read Upper case commands -fn read_upper_command(cur: &mut TokenCursor, song: &mut Song, ch: char) -> Token { - cur.prev(); // back 1char - let cur_pos = cur.index; - let mut cmd = cur.get_word(); - - // macro define? - if ch == '#' { - cur.skip_space(); - if cur.eq_char('=') { // DEFINE MACRO - cur.index = cur_pos; - return read_def_str(cur, song); - } - } - - // variables? - match song.variables.get(&cmd) { - Some(sval) => { - cur.skip_space(); - // set varable? - if cur.eq_char('=') { - cur.index = cur_pos; - return read_def_str(cur, song); - } - // get variable - return read_variables(cur, song, &cmd, sval.clone()); - }, +fn read_upper_command(cur: &mut TokenCursor, song: &mut Song) -> Token { + cur.prev(); // back 1char + let tmp_pos = cur.index; + match read_let_variable(cur, song) { + Some(res) => return res, None => {}, - }; - + } + cur.index = tmp_pos; + let mut cmd = cur.get_word(); // Systemの場合は"."に続く if cmd == "System" || cmd == "SYSTEM" { - if cur.eq_char('.') { cur.next(); cmd.push('.'); } cmd = "System".to_string(); // convert "SYSTEM" to "System" + if cur.eq_char('.') { cur.next(); cmd.push('.'); } cmd.push_str(&cur.get_word()); } @@ -255,6 +235,36 @@ fn read_upper_command(cur: &mut TokenCursor, song: &mut Song, ch: char) -> Token return Token::new_empty(&cmd); } +fn read_let_variable(cur: &mut TokenCursor, song: &mut Song) -> Option { + let cur_pos = cur.index; + let ch = cur.peek_n(0); + let cmd = cur.get_word(); + + // macro define? + if ch == '#' { + cur.skip_space(); + if cur.eq_char('=') { // DEFINE MACRO + cur.index = cur_pos; + return Some(read_def_str(cur, song)); + } + } + + // variables? + match song.variables.get(&cmd) { + Some(sval) => { + cur.skip_space(); + // set varable? + if cur.eq_char('=') { + cur.index = cur_pos; + return Some(read_def_str(cur, song)); + } + // get variable + return Some(read_variables(cur, song, &cmd, sval.clone())); + }, + None => {}, + }; + None +} fn read_variables(cur: &mut TokenCursor, song: &mut Song, name: &str, sval: SValue) -> Token { match sval {