作者
- 尼古拉斯·博雷尔 (nikolasborrel@gmail.com)
- 安德烈亚斯·霍特加德·丹尼尔森 (andreashd@gmail.com)
Notus 是一种特定领域的语言,用于以函数式编程的高级声明式风格表达音乐结构。 这些音乐结构由诸如音符和休止符等基本概念,诸如转调和节奏缩放等转换音乐对象的操作,以及诸如并行和顺序组合等组合音乐对象以形成更复杂对象的操作组成。 从这些简单的基础开始,可以很容易地开发出更丰富的音乐创意。
Notus 的灵感来自使用 Haskell 实现的 Euterpea 项目。
在 Notus 中,音乐结构的表示使用 Music 数据类型完成,语义(或解释)通过将 Music 转换为 Performance 数据类型来完成,由此可以将音乐导出到 MIDI、CSound 或其他底层表示(目前仅提供 MIDI 导出)。
让我们从音高类型开始,它由 Pitch
和定义音高的 Octave
组成
public enum PitchClass {
case c, d, e, f, g, a, b,
cf, df, ef, ff, gf, af, bf,
cs, ds, es, fs, gs, aas, bs,
cff, dff, eff, fff, gff, aff, bff,
css, dss, ess, fss, gss, ass, bss
}
public typealias Octave = UInt8
public typealias Pitch = (PitchClass, Octave)
PitchClass
数据类型声明了 35 个音高类名称中的所有 12 个半音。 例如,“音乐会 A 调”的概念在上述设计中表示为 (.a, 4)
。 音乐结构的表示使用 Music
类型完成
public enum Music {
case prim(Primitive)
indirect case stack(Music, Music)
indirect case seq(Music, Music)
indirect case modify(Control, Music)
}
public enum Primitive {
case note(Dur, Pitch)
case noteAttr(Dur, Pitch, [NoteAttribute])
case rest(Dur)
case none
}
public enum Control {
case tempo(Bpm)
case volume(Vol)
case transpose(RelativePitch)
case staff(Staff)
case player(PlayerName)
case interpret([PhraseAttribute])
}
public typealias PlayerName = String
note
由持续时间和音高定义,而 rest
仅具有持续时间。 持续时间定义了全音符 (.wn
)、二分音符 (.hn
) 等。 为了避免您脆弱的手指受到伤害,Notus 语言上洒了一些语法糖,如下所示:O(.qn, (.a, 4))
表示音高为 A、八度音阶为 4 的四分音符,而 R(.wn)
表示全音符休止符。
有了这些构建块,我们可以构建更复杂的音乐结构,如下所示
m1 ++ m2
是 m1
和 m2
的**顺序组合**,即 m1
和 m2
依次播放。m1 |=| m2
是 m1
和 m2
的**并行组合**,即 m1
和 m2
同时播放。tempo(Bpm) => music
设置 music
的节奏 Bpm
。volume(Vol) => music
设置 music
的音量 Vol
。transpose(RelativePitch) => music
以半音为单位,将 music
转换一个间隔 RelativePitch
。staff(Staff) => music
将 music
放置在谱号 Staff
中。interpret([PhraseAttribute]) => music
设置与 music
的主观解释相关的各种属性,例如力度和发音。下面显示了一个使用这些构造器的简单示例
let dMajor: Notes = notes((.d, 4), (.fs, 4), (.a, 4))
let gMinor: Notes = notes((.d, 4), (.g, 4), (.bf, 4))
let dMajorMelody1: Music =
O(.en, (.d, 5)) ++ O(.en, (.d, 5)) ++ O(.en, (.d, 5))
++ O(.en, (.e, 5))
++ O(.en, (.fs, 5)) ++ O(.en, (.fs, 5)) ++ O(.qn, (.fs, 5))
let dMajorMelody2: Music =
O(.en, (.e, 5)) ++ O(.en, (.d, 5)) ++ O(.en, (.e, 5))
++ O(.en, (.fs, 5))
++ R(.qn) ++ O(.qn, (.d, 5))
let melody = dMajorMelody1 ++ dMajorMelody2
let harmony = dMajor.stack(.wn) ++ gMinor.stack(.hn) ++ dMajor.stack(.hn)
let music = melody |=| harmony
let upm = try UserPatchMap([(0, .ch0, .acousticGrandPiano)])
let piece = .staff(0) => .tempo(60) => music
**Notus** 框架分为 2 个部分
**NotusDemoHost** 是一个 iOS 演示项目,展示了一些 Notus 音乐示例。
以下文件是开始了解 Notus 奇怪的递归世界的良好起点
├── Sources
│ └── Notus
│ ├── MusicExamples
│ │ ├── Canine
│ │ ├── Chameleon.swift
│ │ ├── DrumSolo.swift
│ │ ├── James.swift
│ ├── MusicNotation.swift: The Notus domain-specific language
│ ├── NotusIO
│ │ ├── Midi
│ │ │ ├── MidiToMusic.swift: Convert from Midi to Music
│ │ │ ├── MusicToMidi.swift: Convert from Music to Midi
│ │ └── Playback
│ │ └── MidiPlayer.swift: Playback Midi
│ └── Performance
│ ├── MusicToPerformance.swift: conversion from Music to Performance
│ ├── Performance.swift: defines constructs for describing music as `Performance`, the most low-level representation available in Notus. This representation can then be transformed from `Performance` to Midi or other formats.
│ ├── UserPatchMap.swift: defines mappings between `Staff`, `Channel` and `Instrument`
│ ├── Players
│ │ ├── DefaultPlayer.swift: simple player handling (some) interpretations
│ │ └── FancyPlayer.swift: more advanced player handling dynamics, articulations and tempos
Swift 包管理器是一个用于管理 Swift 代码分发的工具。 它与 Swift 构建系统集成,以自动执行下载、编译和链接依赖项的过程。
设置好 Swift 包后,将 Notus 添加为依赖项的方法是将包 URL 添加到您的 Package.swift 中。
Swift 5
dependencies: [
.package(url: "https://github.com/cremo-music/Notus.git", from: "1.0.0")
]
有许多示例展示了如何使用 Notus。 有两种方法可以运行 NotusDemoHost 项目
打开 NotusDemoHost 的工作区,名为 NotusDemoHost.xcworkspace
,位于 NotusDemoHost
文件夹内,然后在 iOS 设备上简单地运行该项目。
要在 iOS 上运行并获得正确的 MIDI 声音,您需要一个音色库。 我们推荐 Airfont 340,您可以从这里下载。 该文件应像往常一样放置在 Resources
文件夹中。
打开终端并转到 NotusDemoHost
文件夹。 要运行
swift run
在 MacOS 上,这将在 Mac 的 Documents 文件夹中生成许多 MIDI 示例文件。 在 Linux 上,它将以 Notus 语言打印一个示例。
这是一个开源项目,如果您有兴趣贡献,请联系我们!
请参阅 LICENSE