MIDI 包概述

introduction简要介绍了 Java Sound API 的 MIDI 功能。接下来的讨论提供了 Java Sound API 的 MIDI 体系结构的更详细介绍,可通过javax.sound.midi包对其进行访问。 MIDI 本身的一些基本功能(作为复习或介绍)将在上下文中放置 Java Sound API 的 MIDI 功能。然后,它 continue 讨论 Java Sound API 的 MIDI 方法,以为后续各节中说明的编程任务做准备。 MIDI API 的以下讨论分为两个主要区域:数据和设备。

MIDI 刷新器:连线和文件

乐器数字interface(MIDI)标准定义了电子音乐设备(例如电子键盘乐器和个人计算机)的通信协议。 MIDI 数据可以在 site 演奏时通过特殊的电缆传输,也可以存储在标准类型的文件中以供以后播放或编辑。

本节将回顾一些 MIDI 基础知识,而不涉及 Java Sound API。讨论的 Object 是让熟悉 MIDI 的 Reader 复习一下,并为不熟悉 MIDI 的 Reader 提供简短的介绍,为后续对 Java Sound API 的 MIDI 包的讨论提供背景。如果您对 MIDI 有深入的了解,则可以安全地跳过此部分。在编写大量 MIDI 应用程序之前,不熟悉 MIDI 的程序员可能需要比本教程中包含的 MIDI 更完整的描述。请参阅《完整 MIDI 1.0 详细规范》,该规范仅可从http://www.midi.org进行硬拷贝(尽管您可能会在网上找到措辞或摘要的版本)。

MIDI 既是硬件规范,又是软件规范。要了解 MIDI 的设计,它有助于了解其历史。 MIDI 最初设计用于在诸如合成器之类的电子键盘乐器之间传递音乐事件,例如按键按下。被称为音序器的硬件设备存储了音符序列,这些音符序列可以控制合成器,从而可以录制音乐表演并随后进行播放。后来,开发了将 MIDI 乐器连接到计算机 String 行端口的硬件interface,从而使音序器可以用软件实现。最近,计算机声卡已结合了用于 MIDI I/O 和合成音乐声的硬件。如今,许多 MIDI 用户仅处理声卡,而从未连接到外部 MIDI 设备。 CPU 的速度已经足够快,合成器也可以用软件实现。声卡仅用于音频 I/O,在某些应用程序中用于与外部 MIDI 设备进行通信。

MIDI 规范的简短硬件部分规定了 MIDI 电缆的引脚分配以及这些电缆所插入的插孔。这部分不需要我们关注。因为最初需要硬件的设备(例如音序器和合成器)现在可以在软件中实现,所以大多数程序员了解 MIDI 硬件设备的唯一原因可能仅仅是理解 MIDI 中的隐喻。但是,外部 MIDI 硬件设备对于某些重要的音乐应用程序仍然是必不可少的,因此 Java Sound API 支持 MIDI 数据的 Importing 和输出。

MIDI 规范的软件部分非常广泛。这部分涉及 MIDI 数据的结构以及诸如合成器之类的设备应如何响应该数据。重要的是要理解 MIDI 数据可以排序。这种 Double 重性反映了完整 MIDI 1.0 详细规范的两个不同部分:

  • MIDI 1.0

  • 标准 MIDI 文件

我们将通过检查 MIDI 规范这两部分的 Object 来解释流和排序的含义。

以 MIDI Wire 协议流式传输数据

MIDI 规范的这两部分中的第一部分描述了非正式地称为“ MIDI 线协议”的内容。 MIDI 有线协议(原始的 MIDI 协议)基于 MIDI 数据通过 MIDI 电缆(“电线”)发送的假设。电缆将数字数据从一个 MIDI 设备传输到另一个。每个 MIDI 设备可能是乐器或类似设备,也可能是配备了 MIDI 功能的声卡或 MIDI 到 String 行端口interface的通用计算机。

由 MIDI 有线协议定义的 MIDI 数据被组织成消息。不同类型的消息由消息中的第一个字节(称为* status byte )来区分。 (状态字节是唯一将最高位设置为 1 的字节.)消息中状态字节后面的字节称为数据字节*。某些 MIDI 消息(称为* channel *消息)具有一个状态字节,该状态字节包含用于指定通道消息类型的四位和用于指定通道号的另外四位。因此,有 16 个 MIDI 通道。可以将接收 MIDI 消息的设备设置为对所有或其中一个虚拟通道上的通道消息作出响应。通常,每个 MIDI 通道(不应与音频通道混淆)都用于发送不同乐器的音符。例如,两个常用通道消息是“音符开”和“音符关”,它们分别启动音符发声然后停止。这两个消息各自占用两个数据字节:第一个指定音符的音高,第二个指定音符的“力度”(假设键盘乐器正在弹奏音符,则按下或释放键的速度)。

MIDI 有线协议定义了 MIDI 数据的流模型。该协议的主要 Feature 是 MIDI 数据的字节是实时传递的,也就是说,它们是流式传输的。数据本身不包含时序信息。每个事件在收到时都会进行处理,并假定它在正确的时间到达。如果音符是由 site 音乐人生成的,则该模型很好,但是如果您想存储这些音符以供以后播放,或者您想不实时地合成它们,则该模型是不够的。当您意识到 MIDI 最初是为演奏而设计的,这是可以理解的,它可以作为键盘音乐家控制多个合成器的一种方式,早在许多音乐家使用计算机的时代。 (该规范的第一版于 1984 年发布.)

标准 MIDI 文件中的 Sequences 数据

MIDI 规范的“标准 MIDI 文件”部分解决了 MIDI 线协议中的时序限制。标准 MIDI 文件是包含 MIDI * events *的数字文件。事件只是 MIDI 线协议中定义的 MIDI 消息,但带有指定事件时间的附加信息。 (还有一些与 MIDI 有线协议消息不对应的事件,我们将在下一节中看到.)附加的时序信息是一系列字节,指示何时执行消息所描述的操作。换句话说,标准 MIDI 文件不仅指定要演奏的音符,而且还指定何时演奏每个音符。有点像乐谱。

标准 MIDI 文件中的信息称为* sequence 。标准 MIDI 文件包含一个或多个 tracks *。每个音轨通常都包含如果由 site 音乐家演奏的音乐,则单个乐器会演奏的音符。音序器是一种软件或硬件设备,可以读取音序并在适当的时间传送其中包含的 MIDI 消息。音序器有点像乐团指挥:它具有所有音符的信息,包括音符的时间,并且可以告诉其他实体何时执行音符。

Java Sound API 的 MIDI 数据表示

现在,我们已经草绘了 MIDI 规范用于流式处理和排序音乐数据的方法,现在让我们研究一下 Java Sound API 如何表示该数据。

MIDI Messages

MidiMessage是代表“原始” MIDI 消息的抽象类。 “原始” MIDI 消息通常是由 MIDI 有线协议定义的消息。它也可以是标准 MIDI 文件规范定义的事件之一,但是没有事件的定时信息。原始 MIDI 消息分为三类,在 Java Sound API 中由这三个相应的MidiMessage子类表示:

  • ShortMessages是最常见的消息,并且在状态字节之后最多具有两个数据字节。Channels 消息(例如“注意打开”和“注解 关闭”)都是短消息,与其他一些消息一样。

  • SysexMessages包含*系统专有的 MIDI 消息。它们可能有很多字节,并且通常包含特定于制造商的指令。

  • MetaMessages出现在 MIDI 文件中,但不在 MIDI 有线协议中。元消息包含诸如歌词或速度设置之类的数据,这些数据可能对音序器有用,但对于合成器而言通常毫无意义。

MIDI Events

如我们所见,标准 MIDI 文件包含事件,这些事件是“原始” MIDI 消息的包装器以及定时信息。 Java Sound API 的MidiEvent类的实例表示一个事件,例如可能存储在标准 MIDI 文件中。

MidiEvent的 API 包含用于设置和获取事件的计时值的方法。还有一种方法可以检索其嵌入的原始 MIDI 消息,该消息是MidiMessage子类的实例,下面将进行讨论。 (仅在构造MidiEvent时可以设置嵌入的原始 MIDI 消息.)

序列和曲目

如前所述,标准 MIDI 文件存储安排在轨道中的事件。通常,该文件代表一种音乐作品,并且通常,每首曲目代表一个部分,例如单个乐器演奏者可能已经演奏过。乐器演奏者演奏的每个音符至少由两个事件表示:“音符打开”开始音符,“音符关闭”结束它。音轨可能还包含与笔记不对应的事件,例如元事件(上面已提到)。

Java Sound API 按照三部分的层次结构组织 MIDI 数据:

TrackMidiEvents的集合,而SequenceTracks的集合。此层次结构反映了“标准 MIDI 文件”规范的文件,音轨和事件。 (注意:这是包含和所有权的层次结构;就继承而言,这不是*类层次结构.这三个类中的每一个都直接从java.lang.Object继承.)

可以从 MIDI 文件中读取 Sequences,也可以从头开始创建 Sequences,然后通过将Tracks添加到Sequence(或删除它们)来进行编辑。类似地,可以将MidiEvents添加到序列中的轨道或从中删除。

MIDI 设备的 Java Sound API 表示形式

上一节介绍了如何在 Java Sound API 中表示 MIDI 消息。但是,MIDI 信息并不存在。它们通常是从一台设备发送到另一台设备的。使用 Java Sound API 的程序可以从头开始生成 MIDI 消息,但是更常见的是,这些消息是由软件设备(例如音序器)创建的,或者是通过 MIDIImporting 端口从计算机外部接收的。这样的设备通常将这些消息发送到另一个设备,例如合成器或 MIDI 输出端口。

MidiDevice interface

在外部 MIDI 硬件设备的世界中,许多设备可以将 MIDI 消息发送到其他设备,也可以从其他设备接收消息。同样,在 Java Sound API 中,实现MidiDeviceinterface的软件对象可以发送和接收消息。这样的对象可以完全用软件实现,也可以用作声卡 MIDI 功能等硬件的interface。基本的MidiDeviceinterface提供 MIDIImporting 或输出端口通常所需的所有功能。但是,合成器和定序器分别进一步实现MidiDevice的子interface之一:SynthesizerSequencer

MidiDeviceinterface包括用于打开和关闭设备的 API。它还包括一个称为MidiDevice.Info的内部类,该类提供设备的文本描述,包括其名称,供应商和版本。如果您已阅读本教程的音频采样部分,则该 API 听起来可能很熟悉,因为它的设计类似于javax.sampled.Mixerinterface的设计,该interface代表一个音频设备,并且具有类似的内部类Mixer.Info

发送器和接收器

大多数 MIDI 设备都能够发送MidiMessages,接收它们或同时接收这两者。设备发送数据的方式是通过它“拥有”的一个或多个发送器对象。类似地,设备接收数据的方式是通过其一个或多个接收器对象。发送器对象实现Transmitterinterface,接收器对象实现Receiverinterface。

每个 Launcher 一次只能连接到一个接收器,反之亦然。同时将其 MIDI 消息发送到其他多个设备的设备通过具有多个发送器来实现此 Object,每个发送器都连接到不同设备的接收器。同样,一次可以同时从多个来源接收 MIDI 消息的设备必须通过多个接收器进行接收。

Sequencers

音序器是一种用于catch和播放 MIDI 事件序列的设备。它具有发送器,因为它通常会将序列中存储的 MIDI 消息发送到另一个设备,例如合成器或 MIDI 输出端口。它还具有接收器,因为它可以catch MIDI 消息并按 Sequences 存储它们。 MidiDeviceSequencer为其superinterface添加了用于基本 MIDI 音序操作的方法。音序器可以从 MIDI 文件加载音序,查询和设置音序的速度,并将其他设备与其同步。当定序器处理某些类型的事件时,应用程序可以注册要通知的对象。

Synthesizers

A Synthesizer是用于产生声音的设备。它是javax.sound.midi包中唯一产生音频数据的对象。合成器设备控制一组 MIDI 通道对象-通常为 16 个,因为 MIDI 规范要求 16 个 MIDI 通道。这些 MIDI 通道对象是实现MidiChannelinterface的类的实例,该interface的方法表示 MIDI 规范的“通道语音消息”和“通道 Pattern 消息”。

应用程序可以通过直接调用合成器的 MIDI 通道对象的方法来生成声音。不过,更常见的是,合成器会响应发送到一个或多个接收器的消息来生成声音。例如,这些消息可能是通过音序器或 MIDIImporting 端口发送的。合成器根据事件中指定的 MIDI 通道号,解析其接收者收到的每条消息,并通常向其MidiChannel对象之一分配一个相应的命令(例如noteOncontrolChange)。

MidiChannel使用这些消息中的音符信息来合成音乐。例如,一条noteOn消息指定了音符的音高和“力度”(音量)。但是,笔记信息不足。合成器还需要有关如何为每个音符创建音频 signal 的精确说明。这些指令用Instrument表示。每个Instrument通常都模拟不同的现实乐器或声音效果。 Instruments可能是合成器的预设,也可能是从音库文件中加载的。在合成器中,按库号(可以认为是行)和程序号(列)排列Instruments

本节提供了理解 MIDI 数据的背景,并且介绍了 Java Sound API 中与 MIDI 相关的一些重要interface和类。随后的部分说明如何在应用程序中访问和使用这些对象。