8个版本 (5个破坏性版本)

0.5.1 2024年5月12日
0.5.0 2023年11月17日
0.4.1 2023年11月17日
0.3.0 2023年11月17日
0.0.0 2023年11月8日

#580 in 音频

每月 40 次下载
lmml-cli 中使用

MIT/Apache

56KB
867

LMML

GitHub Actions Workflow Status

LMML (LMML Music Macro Language) 是MML的一种方言。

演奏示例

https://github.com/yuma140902/lmml/assets/23431077/dfad8777-ade5-4591-8804-a3968a2e14ea

; Bad Apple!! feat. nomico

t320 l4

@0 v15
<ab>cd e.r8ag e.r8<a.r8> edc<b>
<ab>cd e.r8dc <bab>c <bagb>
<ab>cd e.r8ag e.r8<a.r8> edc<b>
<ab>cd e.r8dc <b.r8>c.r8 d.r8e.r8

@1 v10
<ab>cd e.r8ag e.r8<a.r8> edc<b>
<ab>cd e.r8dc <bab>c <bagb>
<ab>cd e.r8ag e.r8<a.r8> edc<b>
<ab>cd e.r8dc <b.r8>c.r8 d.r8e.r8

@3 v25
gaed e.r8de gaed e.r8
dedc <bga.r8 gab>c de<a.r8>
egga ede.r8 dega ede.r8
dedc <bga.r8 gab>c de<a.r8>

@4 v30
egga ede.r8 dega ede.r8
dedc <bga.r8 gab>c de<a.r8>
egga ede.r8 dega ede.r8
ab>c<b age.r8 dedc <bga.r8>

@0 v15
egga ede.r8 dega ede.r8
dedc <bga.r8 gab>c de<a.r8>
egga ede.r8 dega ede.r8
dedc <bga.r8 gab>c de<a.r8>
egga ede.r8 dega ede.r8
dedc <bga.r8 gab>c de<a.r8>
egga ede.r8 dega ede.r8
ab>c<b age.r8 dedc <bga.r8

LMML教程

演奏方法

要演奏LMML代码,最简单的方法是使用lmml-cli的对话模式。

cargo install lmml-cli

安装lmml-cli,

lmml repl

启动对话模式。

语法

LMML有几个命令。这些命令的集合构成了LMML程序。

要了解严格的语法,请参阅下面的“LMML语言的语法”。

音符命令

CB的字母分别表示从C到B的音。加上+则升半音,加上-则降半音。

在音符命令之后可以跟一个表示音长的数字。四分音符为4,八分音符为8等。注意,数字越大,音长越短。

如果省略数字,则使用最后执行的L命令的值。如果没有L命令,则使用4。

此外,可以在数字后面加上..表示附点音符。

示例

  • c4 - C的四分音符
  • c+8 - C#的八分音符
  • d4. - D的附点四分音符
  • d+4. - D#的附点四分音符

休止符命令

R表示休止符。与音符命令一样,可以加上表示长度的数字和.

和弦

通过用[]包围音符来表示和弦。音符应按从根音到音高的顺序书写。

示例

  • [ceg] - C大调
  • [ace] - Am
  • [ga+df] - Gm7
; カノン進行 C/G - G - Am/E - Em - F/C - C - F/C - G/D
@4t40 [gce][gbd][eac][egb][cfa][ceg][cfa][dgb]

https://github.com/yuma140902/lmml/assets/23431077/9f5b72ed-5317-4cdf-bf3a-d028d05c7cd5

L命令

设置音符的长度。如果没有在音符命令后面加上数字,则使用此命令的值。有关详细信息,请参阅下面的“LMML语言的详细规格”。

O命令

设置八度音的值。八度音的初始值是4,值越大,音越高。八度音4的A是440Hz。

>命令

增加八度音的值。

<命令

减少八度音的值。

N命令

使用MIDI音符号(0~127)指定音符。

※LMML不依赖于MIDI,但内部使用MIDI兼容的格式处理音符。

V命令

设置音量。初始值为20,值越大,音越大。100为0dB,0为-∞ dB。

T命令

设置节奏。值表示一分钟内四分音符响起的次数。初始值为120。

@命令

使用0~4的数字设置波形。默认值为0。

数字 波形
0 锯齿波
1 矩形波
2 脉冲波
3 三角波
4 正弦波

:命令

LMML有16个通道,编号为0~15,可以同时演奏。

写成如:0的形式,则后续命令将对通道0起作用。

示例

https://github.com/yuma140902/lmml/assets/23431077/6f76011c-c638-4741-a067-14986da3178d

; 少女さとり ~ 3rd eye (東方地霊殿より)
; (最初の2小節のみ)

:0 @3 v20 t60 o4
:1 @3 v20 t60 o3
:2 @3 v30 t60 o2
:3 @4 v50 t60 o2
:4 @4 v20 t60 o4
:5 @4 v40 t60 o1

:0 l8
<b4.>c+16d16f+ <b>df+g<b>c+gf+2

:1 l2
f+1 gf+

:2 l2
f+1 gf+

:3 l32
crrrrrrr rrrrrrrr [d+g]rrrrr[d+g]r rrrr[d+g]rrr
<<[bg+f]rrrrrrr rrrr[bg+f]rrr >[g+f]<rrrrrrr rrrrrrrr>>

:4 l2
[bf+]1[bg][bf+]

:5 l2
[bf+]1[bg][bf+]

LMML语言语法

<lmml>        := <command>*
<command>     := <note-cmd>
               | <chord>
               | <n-cmd>
               | <rest-cmd>
               | <set-octave>
               | <set-length>
               | <set-volume>
               | <set-tempo>
               | <set-wave>
               | <set-channel>
               | <inc-octave>
               | <dec-octave>
<note-cmd>    := <note-char> <modifier>? <number>? <dot>?
<note-char>   := 'C' | 'D' | 'E' | 'F' | 'G' | 'A' | 'B'
               | 'c' | 'd' | 'e' | 'f' | 'g' | 'a' | 'b'
<modifier>    := '+' | '-'
<rest-cmd>    := 'R' <number>? <dot>? | 'r' <number>? <dot>?
<chord>       := '[' (<note-char> <modifier>?)+ ']' <number>? <dot>?
<dot>         := '.'
<n-cmd>       := 'N' <number> | 'n' <number>
<set-ocatve>  := 'O' <number> | 'o' <number>
<set-length>  := 'L' <number> <dot>? | 'l' <number> <dot>?
<set-volume>  := 'V' <number> | 'v' <number>
<set-tempo>   := 'T' <number> | 't' <number>
<set-wave>    := '@' <number>
<set-channel> := ':' <number>
<inc-octave>  := '>'
<dec-octave>  := '<'
<number>      := <digit>+
<digit>       := '0' | '1' | '2' | '3' | '4'
               | '5' | '6' | '7' | '8' | '9'

基本上不区分大小写。空格或换行将被忽略。

;开头的行将被视为注释并忽略。

LMML语言的详细规格

长度指定

音符命令、休止符命令、L命令省略数字或.时的行为如下表所示。预计行为因人而异的部分以粗体显示。

最后L命令的数字 .是否存在 音符・休止符命令的数字 .是否存在 长度
L命令 N/A 省略 四分音符
L命令 N/A 省略 附点四分音符
L命令 N/A n n分音符
L命令 N/A n 附点n分音符
m 省略 m分音符
m 省略 附点m分音符
m n n分音符
m n 附点n分音符
m 省略 附点m分音符
m 省略 附点m分音符
m n n分音符
m n 附点n分音符

音高

音符命令转换为频率的过程分为两步。

首先将其转换为称为音符编号的内部形式。$n$是音符命令的类型、$m$是升号、降号等指定、$o$是当前的八度。

base = if n == C then 0
       if n == D then 2
       if n == E then 4
       if n == F then 5
       if n == G then 7
       if n == A then 9
       if n == B then 11

modifier = if m == '+' then  1
           if m == '-' then -1
           else              0

N = base + modifier + ((o + 1) * 12)

然后将音符编号$N$转换为频率$f$ [Hz]。

f = 440 \times 2 ^ { \frac{N - 69}{12} }

音量衰减

一个音符在响起的瞬间音量最大,然后逐渐减小。具体来说,如果使用V命令指定的音量为v_0、音符长度为T、音符开始响起的经过时间为t,则t时刻的音量v(t)为

v(t) = \frac{v_0 (T-t)}{T}

目前还没有改变衰减方式的方法。

数值限制

八度值

上限和下限都不存在,但预计在-1到9的范围内使用。另外,由于O命令只能指定非负值,因此如果想要指定负八度,请使用<命令。

音量值

下限是0,上限没有限制。但是,由于100对应0dB,因此指定比100更大的值可能会导致音质受损。0时音量完全无声。

N命令的值

下限是0,上限没有限制。但是,预计在0到127的范围内使用。

节奏

下限是1,上限没有限制。

通道号

下限是0,上限是15。

LMML实现的详细规格

精度

波形合成使用44.1kHz、32位浮点数进行。

各个模块

lmml模块

包含LMML的AST、时间线数据结构和波形合成处理的模块。

lmml-parser模块

LMML的解析器。

use lmml::{LmmlAst, LmmlCommand, NoteChar, NoteModifier};
use lmml_parser::parse_lmml;

pub fn main() {
    let ast = parse_lmml("t80 c+ d e8. r8").unwrap();

    assert_eq!(ast, LmmlAst(vec![
        LmmlCommand::SetTempo(80),
        LmmlCommand::Note {
            note: NoteChar::C,
            modifier: NoteModifier::Sharp,
            length: None,
            is_tied: false,
        },
        LmmlCommand::Note {
            note: NoteChar::D,
            modifier: NoteModifier::Natural,
            length: None,
            is_tied: false,
        },
        LmmlCommand::Note {
            note: NoteChar::E,
            modifier: NoteModifier::Natural,
            length: Some(8),
            is_tied: true,
        },
        Lmmlcommand::Rest { 
            length: Some(8) 
            is_tied: false,
        },
    ]));
}

lmml-cli模块

用于交互式演奏LMML或将其转换为其他格式的命令行工具。

安装方法

cargo install lmml-cli

使用方法

播放文件。

lmml load ファイル

启动对话模式。

lmml repl

依赖项

~2-30MB
~461K SLoC