#音乐 #语言 #

lmml

LMML音乐宏语言

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日

194音频

每月下载 31
2 crates 中使用

MIT/Apache

37KB
686

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 Major
  • [ace] - A Minor
  • [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兼容的格式处理音符。

※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、32bit-float进行波形合成。

各个crate

lmmlcrate

包含LMML的AST和波形合成处理的crate。

lmml-parsercrate

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-clicrate

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

安装方法

cargo install lmml-cli

使用方法

播放文件。

lmml load ファイル

启动交互模式。

lmml repl

依赖项

~1–33MB
~443K SLoC