Linux MIDI:简要概述,第 4 部分
在我们 Linux MIDI 软件巡览的这一部分中,我们将了解一些实验性的 MIDI 音乐创作环境。我将这类软件分为命令行和基于 GUI 的应用程序。无论界面如何,用户都需要运用高于平均水平的脑力才能从该软件中获得最佳效果。考虑到这一警告,让我们首先了解一些为实验性 MIDI 音乐家设计的基于语言的环境。
Craig Stuart Sapp 的 Improv 专为面向实验性 MIDI 音乐家的交互式表演环境而设计。该系统目前以软件包形式提供,其中包括一个针对 MIDI I/O 优化的 C++ 类库,以及一组演示这些类的功能和特性的示例。
Improv 控制主机和外部合成器之间的实时 MIDI 通信。在一个典型的程序中,计算机接收来自合成器的 MIDI 输入,立即以某种预编程的方式更改该输入,并将更改后的数据流发送到指定的 MIDI 输出端口。一些 Improv 示例让计算机产生 MIDI 输出流,该输出流可以被外部键盘更改,从而为与程序进行音乐“对话”创造有趣的可能性。
以下小程序反转了 MIDI 键盘的音符编号,范围为 0-127。因此,如果演奏者向上演奏 C-D-E 音阶,即向上两个全音,Improv 程序会将音阶转换为 C-Bb-Ab,即向下两个全音,同时进行。启用 MIDI Thru 后,正常音轨和更改后的音轨会同时发出声音。该代码来自 Improv 软件包中提供的一组示例。为了简洁起见,我编辑了 Craig 的注释,但完整的列表可在网上 此处 获取。
// An Improv program that inverts the incoming MIDI note numbers. // // Header for synthesizer I/O programming #include "synthImprov.h" /*------------- beginning of improvization algorithms ------------*/ /*- ------------- maintenance algorithms ------------------------*/ // description -- Put a description of the program and how to use it here. void description(void) { cout << "plays music backwards" << endl; } // initialization -- Put items here which need to be initialized at the // beginning of the program. void initialization(void) { } // finishup -- Put items here which need to be taken care of when the // program is finished. void finishup(void) { } /*-------------------- main loop algorithms -----------------------------*/ // mainloopalgorithms -- this function is called by the improv interface // continuously while the program is running. The global variable t_time // which stores the current time is set just before this function is // called and remains constant while in this functions. MidiMessage m; void mainloopalgorithms(void) { while (synth.getNoteCount() > 0) { m = synth.extractNote(); synth.send(m.p0(), 127 - m.p1(), m.p2()); } } /*-------------------- triggered algorithms -----------------------------*/ // keyboardchar -- Put commands here which will be executed when a key is // pressed on the computer keyboard. void keyboardchar(int key) { } /*------------------ end improvization algorithms -----------------------*/
此示例的主循环执行传入 MIDI 音符编号的简单反转,并将结果发送到 MIDI 输出端口。该示例相当简单,但其意义在于它的实时操作,通过这种操作,反转似乎与输入同时发生。
henontune 示例是我最喜欢的 Improv 功能演示之一。当 henontune 运行时,它会生成一个由 Henon 混沌映射函数生成的旋律。传入的 MIDI 音符开和力度值控制映射函数的参数,从而产生一个新的音符流,该音符流变为循环的、进入混沌序列或只是停止。henontune 的代码列表太长,无法在此处打印,但可以在网上 此处 阅读。
Improv 可用于创建您自己的 MIDI 编程环境,包括非实时应用程序。该程序的作者不仅为典型的 MIDI 合成器,还为 Radio Baton 和 Adams Stick 提供了控制流程。该软件包的文档非常出色,它会告诉您创建自己的 Improv 程序所需了解的一切。
总有一天,我将专门用一个专栏来介绍使用 Java 编程语言制作的众多有趣的音乐应用程序,其中之一就是优秀的 jMusic 音频/MIDI 编程环境。与 Improv 类似,jMusic 软件包提供了一个类库以及一组有用的示例,以演示库函数和功能。jMusic 的 API 还将 Java 的图形处理能力借用到其资源中,让程序员有机会为他的音乐编码工作创建有吸引力的 GUI。
jMusic 的开发投入了大量工作。由此产生的环境是一个强大的跨平台资源,可用于音乐创作、分析和非实时演奏。jMusic 可用于创建实时 MIDI 音符流,也可以生成标准 MIDI 文件。您可以使用 JavaSound 进行音频输出转换,也可以使用 MidiShare 环境为 jMusic 提供灵活的 MIDI I/O,以便将输出路由到外部合成器,包括软合成器。
以下示例来自 jMusic 在线教程,演示了一个基本程序,该程序创建了一个标准 MIDI 文件作为其输出。该示例很简单,只包含一个音符,但它确实表明了典型的 jMusic 程序是如何组织的。
// First, access the jMusic classes: import jm.JMC; import jm.music.data.*; import jm.util.*; /* * This is the simplest jMusic program of all. * The equivalent to a programming language's 'Hello World' */ public final class Bing implements JMC{ public static void main(String[] args){ //create a middle C minim (half note) Note n = new Note(C4, MINIM); //create a phrase Phrase phr = new Phrase(); //put the note inside the phrase phr.addNote(n); //pack the phrase into a part Part p = new Part(); p.addPhrase(phr); //pack the part into a score titled 'Bing' Score s = new Score("Bing"); s.addPart(p); //write the score as a MIDI file to disk Write.midi(s, "Bing.mid"); } }
使用以下命令编译此代码javac Bing.java会生成一个名为 Bing.class 的文件。运行java Bing会生成一个 Type 1 标准 MIDI 文件,然后可以由任何 MIDI 播放器或音序器播放,包括 TiMidity、playmidi、MusE 和 Rosegarden4。jMusic 教程包括以下版本的程序,该程序已准备好最终输出到 Sun 的 AU 格式的音频文件。jMusic 应用程序通常首先创建一个 MIDI 流,该流要么作为标准 MIDI 文件输出,要么交给 JavaSound 进行音频转换。
import jm.JMC; import jm.music.data.*; import jm.util.*; import jm.audio.*; public final class SonOfBing implements JMC{ public static void main(String[] args){ Score score = new Score(new Part(new Phrase(new Note(C4, MINIM)))); Write.midi(score); Instrument inst = new SawtoothInst(44100); Write.au(score, inst); } }
使用以下命令编译此代码javac SonOfBing.java会生成 SonOfBing.class 文件。运行java SonOfBing会打开对话框,首先保存 MIDI 输出,然后保存 AU 文件。
下一个示例是由 jMusic 开发人员 Andrew Brown 编写的更复杂的程序。该程序名为 NIAM--N Is After M--以纪念 David Zicarelli 编写的著名 M 音乐软件。同样,代码列表太长,无法在此处包含,但可以从 jMusic 网站的“应用程序”页面获取源代码(请参阅“资源”)。图 1 中的屏幕截图很好地展示了 jMusic 的 GUI 功能。
最多可以定义四个部分,每个部分都有图 1 中看到的所有参数的单独设置。输出可以保存为 MIDI 文件,并且尽管它本质上是非实时的,而且外观简单。NIAM 是一个功能强大的小程序。在 jMusic 网站上可以找到许多其他基于 jMusic 的应用程序,展示了诸如通过元胞自动机生成旋律、音乐理论辅助以及将标准乐谱转换为 MIDI 文件等功能。
jMusic 的开发人员费尽心思为新用户提供了大量的文档和教程材料。jMusic 网站还提供了指向关于 Java 及其音乐和声音功能的一般信息的指针,并为计算机音乐和相关主题的其他相关材料提供了指导。如果您已经了解 Java,您应该能够立即开始使用 jMusic,但即使是 Java 新手,也可以通过其网站提供的出色帮助快速编写自己的 jMusic 工具和应用程序。
Albert Graef 博士将他的 Q 编程语言描述为“一种基于项重写的函数式编程语言”。根据文档,Q 程序是“方程的集合……用作重写规则来简化表达式”。如果此描述对您来说有点晦涩难懂,请不要害怕,这位好医生提供了出色的文档和许多实际示例,通过这些文档和示例,您可以快速学习并欣赏 Q 语言的强大功能。
Q 软件包本身包含一些基本的 MIDI 例程,但添加 Q-Midi 模块极大地扩展了这些功能。在 Q-Midi 模块中,MIDI 事件表示为符号数据,这应该有助于制定用于操作和处理 MIDI 消息和序列的程序。Q-Midi 支持实时 MIDI I/O 以及标准 MIDI 文件的加载、编辑和播放,这要归功于它基于 GRAME 团队的 MidiShare,这是一个用于跨平台便携式 MIDI 编程的出色 C 库。
以下代码摘自 Graef 博士的 Q-Midi PDF 介绍。它是 Q-Midi 在算法作曲中的应用的示例,该算法作曲基于 18 世纪作曲家 Johann Kirnberger 设计的使用骰子作曲的方法。这种方法在 Kirnberger 之前就已存在,但他的技术非常适合通过 Q-Midi 表达。
/* import Q-Midi functions */ include midi; /* read in MIDI files from a directory named "midi" */ def M = map load (glob "midi/*.mid"); /* create a map for file selection via the dice */ /* the numbers are the MIDI file fragments, */ /* i.e., 70.mid, 10.mid, 42.mid, etc. */ def T = map (map pred) [// part A [ 70, 10, 42, 62, 44, 72], [ 34, 24, 6, 8, 56, 30], [ 68, 50, 60, 36, 40, 4], [ 18, 46, 2, 12, 79, 28], [ 32, 14, 52, 16, 48, 22], [ 58, 26, 66, 38, 54, 64], // part B [ 80, 20, 82, 43, 78, 69], [ 11, 77, 3, 41, 84, 63], [ 59, 65, 9, 45, 29, 7], [ 35, 5, 83, 17, 76, 47], [ 74, 27, 67, 37, 61, 19], [ 13, 71, 1, 49, 57, 31], [ 21, 15, 53, 73, 51, 81], [ 33, 39, 25, 23, 75, 55]]; /* define and roll the dice */ dice N = listof die (I in nums 1 N); die = random div 1000 mod 6; /* make selections from map table */ polonaise D = foldl seq [] (A++A++B++A1) where P = map (M!) (zipwith (!) T D), A = take 6 P, B = drop 6 P, A1 = drop 2 A; /* create sequences */ seq S1 S2 = S2 if null S1; = S1++map (shift DT) S2 where (DT,_) = last S1; shift DT (T,MSG) = (T+DT,MSG); /* play the results of 14 throws */ kirnberger = play (polonaise (dice 14));
Kirnberger 的方法使用掷骰子的结果来确定 84 个旋律片段(midi 目录中的文件)中的哪一个将被组合起来以创建一种称为波兰舞曲的舞蹈形式的乐句。片段 A 长 6 小节,片段 B 长 8 小节。骰子决定组合哪些片段,代码的最后一行播放结果。
目前,Kirnberger 代码没有 GUI,但图 2 说明了 Q 与 KDE/Qt 结合使用的可能性。基本的 MIDI 文件播放器代码作为 Q 源代码的示例包含在内,但 QMidiPlayer 是一个单独的源软件包,需要完整安装 Q、Q-Midi 模块和最新版本的 KDE/Qt。
它的主页告诉我们,Common Music (CM) 是
一个面向对象的音乐作曲环境。它通过将音乐结构的高级表示转换为各种用于声音合成和显示的控制协议来产生声音。Common Music 定义了一个广泛的作曲工具库和一个 API,作曲家可以通过该 API 轻松修改和扩展系统。
Common Music 的输出类型包括为 Csound 和 Common Lisp Music 声音合成语言、Common Music Notation 系统和 MIDI 文件格式化的乐谱。它也可以通过 MidiShare 实时渲染。
以下代码在听觉上模拟著名的 Sierpinski 三角形,创建了一个自相似主题的纹理
;;; 8. Defining a recursive process (sierpinski) with five input args. ;;; sprout's second arg is the time to start the sprouted object. ;;; (define (sierpinski knum ints dur amp depth) (let ((len (length ints))) (process for i in ints for k = (transpose knum i) output (new midi :time (now) :duration dur :amplitude amp :keynum k) when (> depth 1) ;; sprout a process on output note sprout (sierpinski (transpose k 12) ints (/ dur len) amp (- depth 1)) wait dur))) (events (sierpinski 'a0 '(0 7 5 ) 3 .5 4) "test.mid")
此片段位于 etc/examples/intro.cm 文件中,该文件是简短 CM 程序的汇编,介绍了该语言的许多有用的基本功能。通过利用 MidiShare 系统,您只需更改即可实时渲染片段test.mid为midi.port。当然,您需要一个支持 MidiShare 的合成器来连接,例如使用以下选项启动的 Fluidsynth 音色库播放器
fluidsynth --midi-driver=midishare --audio-driver=jack 8mbgmsfx.sf2
您还需要将 CM 连接到合成器。图 3 显示了 CM 通过 MidiShare msconnect 实用程序连接到 QSynth(Fluidsynth 的 GUI)。在此安排中,可以看到 Fluidsynth 正在使用 JACK 音频驱动程序运行,因此出现了 QJackCtl。
作为一个基于语言的环境,Common Music 的开发最适合在支持 Lisp 的文本编辑器中进行,例如 Emacs 或 X/Emacs。Macintosh 的 Common Music 用户长期以来一直享受着名为 Plotter 的图形工具的使用,该工具最近已移植到 Linux。一个新的渲染 GUI(图 4)已添加到 Common Music CVS 源代码中,这应该可以更轻松地将 CM 输出定向到其各种目标,包括 Plotter,当然还有 MIDI。
Common Music 已经开发多年,其作者 Rick Taube 为新用户和经验丰富的用户提供了出色的文档和教程。最近,Rick 出版了一本关于使用计算机进行音乐创作的杰出著作,Notes From The Metalevel(请参阅下面的“资源”)。他自然而然地选择 Common Music 作为演示语言。该系统包含在本书随附的 CD 中,因此新用户可以立即开始开发和测试他们自己的 CM 代码。
历史悠久的 Csound 音乐和声音编程语言长期以来一直支持 MIDI 输入,作为 Csound 乐器播放或通过 MIDI 设备播放 Csound 乐器的 MIDI 文件,通过外部硬件——键盘、音序器、风控制器。主要归功于开发人员 Gabriel Maldonado 的工作,Csound 也是一种用于实时 MIDI 输出的有用编程语言。
通过外部 MIDI 设备播放 Csound 乐器(Csound 术语中的 orc)是一个相当简单的过程。以下 orc 文件准备了一个 MIDI 敏感乐器
instr 1 inum notnum ; receive MIDI note number iamp ampmidi inum*50 ; use it to set amplitude scaling kfreq cpsmidib ; convert MIDI note number to Hertz frequency value a1 oscil iamp,kfreq,1 ; three detuned oscillators, each using a different waveform (see score below) a2 oscil iamp,kfreq*1.003,2 a3 oscil iamp,kfreq*.997,3 asig = a1+a2+a3 ; audio signal equals sum of three oscillators kenv linenr 1,.07,.11,.01 ; MIDI-controlled envelope for scaling output out asig*kenv ; output equals oscillators shaped by envelope endin
请注意,一旦 MIDI 数据被 Csound 捕获,它就可以在乐器设计中用于任何目的。
此代码为该乐器提供了 Csound 乐谱文件,即 Csound 术语中的 sco
f1 0 8192 10 .1 0 .2 0 0 .4 0 0 0 0 .8 ; function tables for oscillator waveforms f2 0 8192 10 1 0 .9 0 0 .7 0 0 0 .4 f3 0 8192 10 .5 0 .6 0 0 .3 0 0 0 .9 f0 10000 ; placeholder to activate instrument for 10000 seconds e ; end score
在经典的 Csound 中,您可以像这样运行此 orc/sco
csound -o devaudio -M /dev/midi -dm6
其中 -o 确定输出目标,-M 选择 MIDI 输入设备,-dm6 设置图形和消息级别。乐器、乐谱和启动选项都可以滚动到单个 CSD 文件中,这种格式将 Csound 的必要组件统一为一个方便的形式。
设计 Csound 乐器涉及称为操作码的组件的定义和连接。Csound 操作码可以是振荡器 (oscil)、包络发生器 (linenr, linseg)、数学运算符 (=)、MIDI 捕获函数 (ampmid, cpsmidib) 或数百个其他功能和功能,这些功能和功能以 Csound 操作码的形式提供。
以下 CSD 格式的 Csound 代码演示了 moscil 操作码,这是一个 MIDI 输出函数。乐器 instr 1 传输一个大调音阶,作为一系列 MIDI 音符消息,从给定的音符编号 48 开始,并根据 linseg 操作码描述的包络曲线上升。
<CsoundSynthesizer> <CsOptions> ;;; The -Q flag selects a MIDI output port. ;;; An audio output device is required. -Q0 -o devaudio -dm6 </CsOptions> <CsInstruments> sr=44100 ; audio sampling rate, behaves as a ; tempo control for MIDI output kr=44100 ; signal control rate (equals sr ; for MIDI output best results) ksmps=1 ; samples per control period nchnls=1 ; number of audio channels instr 1 ival = 48 kchn = 0 knum linseg ival,1,ival+2,1,ival+4,1,ival+5,1,ival+7,1,ival+9,1, ival+11,1,ival+12 kdur = .8 kpause = .2 moscil kchn,knum,44,kdur,kpause ; There is no audio output stage. endin </CsInstruments> <CsScore> i1 0 8 e </CsScore> </CsoundSynthesizer>
linseg 包络发生器创建一个多级包络,其中每个步骤对应一个音阶度数。当使用以下命令运行此文件时csound moscil-test.csd,C 大调音阶将由任何在 -Q 选项选择的 MIDI 接口上接收数据的乐器播放。
Csound 有许多专用于 MIDI 消息的接收、传输和更改的操作码。可能性令人着迷:同时 MIDI 输入/输出,给定两个物理端口;MIDI 控制合成参数;具有组合音频/MIDI 输出的乐器;甚至通过 Csound 基于 FLTK 的 GUI 操作码进行 MIDI 控制。有了 Csound,您的想象力就是极限。
在我的下一专栏中,我将完成这次 MIDI 巡览,并介绍一些基于 GUI 的实验性 MIDI 应用程序,包括 Tim Thompson 的 KeyKit、Jeffrey Putnam 的 Grammidity 和来自 GRAME 的 Elody,MidiShare 的开发者。下个月见!
Dave Phillips 是一位音乐家、教师和作家,居住在俄亥俄州芬德利。自 1995 年首次接触 Linux 以来,他一直是 Linux 音频社区的活跃成员。他是 The Book of Linux Music & Sound 的作者,以及 Linux Journal 中的许多文章的作者。