ALSA 上的 OSS 音序器模拟

版权所有 (c) 1998,1999 Takashi Iwai

版本 0.1.8; 1999 年 11 月 16 日

描述

此目录包含 ALSA 上的 OSS 音序器模拟驱动程序。请注意,此程序仍处于开发状态。

它的作用 - 它提供了 OSS 音序器的模拟,通过 /dev/sequencer/dev/music 设备进行访问。如果准备好适当的 ALSA 音序器,则大多数使用 OSS 的应用程序都可以运行。

此驱动程序模拟以下功能

  • 普通音序器和 MIDI 事件

    它们被转换为 ALSA 音序器事件,并发送到相应的端口。

  • 定时器事件

    定时器不能通过 ioctl 选择。控制速率固定为 100,无论 HZ 如何。也就是说,即使在 Alpha 系统上,一个节拍也始终是 1/100 秒。基本速率和速度可以在 /dev/music 中更改。

  • 补丁加载

    它纯粹取决于合成驱动程序是否支持,因为补丁加载是通过回调到合成驱动程序实现的。

  • I/O 控制

    大多数控制都被接受。一些控制取决于合成驱动程序,甚至取决于原始的 OSS。

此外,您还可以找到以下高级功能

  • 更好的队列机制

    事件在处理之前会排队。

  • 多个应用程序

    您可以同时运行两个或多个应用程序(即使对于 OSS 音序器)!但是,每个 MIDI 设备都是独占的 - 也就是说,如果某个 MIDI 设备被某个应用程序打开一次,则其他应用程序无法使用它。合成设备没有这种限制。

  • 实时事件处理

    事件可以实时处理,而无需使用越界 ioctl。要切换到实时模式,请发送 ABSTIME 0 事件。随后的事件将以实时方式处理,而无需排队。要关闭实时模式,请发送 RELTIME 0 事件。

  • /proc 接口

    可以通过 /proc/asound/seq/oss 随时显示应用程序和设备的状态。在以后的版本中,配置也将通过 /proc 接口更改。

安装

运行配置脚本,同时启用音序器支持 (--with-sequencer=yes) 和 OSS 模拟 (--with-oss=yes) 选项。将创建一个模块 snd-seq-oss.o。如果您的声卡合成模块支持 OSS 模拟(到目前为止,只有 Emu8000 驱动程序),则将自动加载此模块。否则,您需要手动加载此模块。

开始时,此模块会探测所有已连接到音序器的 MIDI 端口。之后,端口的创建和删除由 ALSA 音序器的公告机制监视。

可以在 proc 接口中找到可用的合成器和 MIDI 设备。运行 cat /proc/asound/seq/oss,然后检查设备。例如,如果您使用 AWE64 卡,您会看到如下内容

OSS sequencer emulation version 0.1.8
ALSA client number 63
ALSA receiver port 0

Number of applications: 0

Number of synth devices: 1
synth 0: [EMU8000]
  type 0x1 : subtype 0x20 : voices 32
  capabilities : ioctl enabled / load_patch enabled

Number of MIDI devices: 3
midi 0: [Emu8000 Port-0] ALSA port 65:0
  capability write / opened none

midi 1: [Emu8000 Port-1] ALSA port 65:1
  capability write / opened none

midi 2: [0: MPU-401 (UART)] ALSA port 64:0
  capability read/write / opened none

请注意,设备编号可能与 /proc/asound/oss-devices 的信息或原始 OSS 驱动程序的编号不同。使用 /proc/asound/seq/oss 中列出的设备编号,通过 OSS 音序器模拟进行播放。

使用合成器设备

运行您喜欢的程序。我已经测试了 playmidi-2.4、awemidi-0.4.3、gmod-3.1 和 xmp-1.1.5。您还可以通过 /dev/sequencer 加载样本,例如 sfxload。

如果低级驱动程序支持对合成设备的多重访问(如 Emu8000 驱动程序),则允许同时运行两个或多个应用程序。

使用 MIDI 设备

到目前为止,只测试了 MIDI 输出。MIDI 输入根本没有检查,但希望它能工作。使用 /proc/asound/seq/oss 中列出的设备编号。请注意,这些数字大多与 /proc/asound/oss-devices 中的列表不同。

模块选项

以下模块选项可用

maxqlen

指定最大读取/写入队列长度。此队列是 OSS 音序器私有的,因此它独立于 ALSA 音序器的队列长度。默认值为 1024。

seq_oss_debug

指定调试级别,并接受零(= 无调试消息)或正整数。默认值为 0。

队列机制

OSS 音序器模拟使用 ALSA 优先级队列。来自 /dev/sequencer 的事件被处理并放入模块选项指定的队列中。

来自 /dev/sequencer 的所有事件都会在开始时进行解析。定时事件也会在此刻进行解析,以便事件可以实时处理。发送事件 ABSTIME 0 将操作模式切换到实时模式,发送事件 RELTIME 0 将其关闭。在实时模式下,所有事件都会立即分派。

排队的事件在 ALSA 音序器调度程序调度的时间之后分派到相应的 ALSA 音序器端口。

如果写入队列已满,则应用程序将在阻塞模式下休眠,直到一定数量(默认情况下为一半)为空。还实现了与写入时间同步。

来自 MIDI 设备或回声事件的输入存储在读取 FIFO 队列中。如果应用程序在阻塞模式下读取 /dev/sequencer,则该进程将被唤醒。

合成器设备的接口

注册

要注册 OSS 合成器设备,请使用 snd_seq_oss_synth_register() 函数

int snd_seq_oss_synth_register(char *name, int type, int subtype, int nvoices,
        snd_seq_oss_callback_t *oper, void *private_data)

参数 nametypesubtypenvoices 用于为 ioctl 创建适当的 synth_info 结构。返回值是此设备的索引号。必须记住此索引以进行注销。如果注册失败,将返回 -errno。

要释放此设备,请调用 snd_seq_oss_synth_unregister() 函数

int snd_seq_oss_synth_unregister(int index)

其中 index 是注册函数返回的索引号。

回调

OSS 合成器设备具有样本下载和 ioctl(如样本重置)的功能。在 OSS 模拟中,这些特殊功能是通过使用回调实现的。注册参数 oper 用于指定这些回调。必须定义以下回调函数

snd_seq_oss_callback_t:
 int (*open)(snd_seq_oss_arg_t *p, void *closure);
 int (*close)(snd_seq_oss_arg_t *p);
 int (*ioctl)(snd_seq_oss_arg_t *p, unsigned int cmd, unsigned long arg);
 int (*load_patch)(snd_seq_oss_arg_t *p, int format, const char *buf, int offs, int count);
 int (*reset)(snd_seq_oss_arg_t *p);

除了 openclose 回调之外,它们可以为 NULL。

每个回调函数都将参数类型 snd_seq_oss_arg_t 作为第一个参数。

struct snd_seq_oss_arg_t {
    int app_index;
    int file_mode;
    int seq_mode;
    snd_seq_addr_t addr;
    void *private_data;
    int event_passing;
};

前三个字段,app_indexfile_modeseq_mode 由 OSS 音序器初始化。app_index 是应用程序索引,对于每个打开 OSS 音序器的应用程序都是唯一的。file_mode 是指示文件操作模式的位标志。有关其含义,请参见 seq_oss.hseq_mode 是音序器操作模式。在当前版本中,仅使用 SND_OSSSEQ_MODE_SYNTH

接下来的两个字段 addrprivate_data 必须由合成驱动程序在 open 回调中填充。addr 包含分配给此设备的 ALSA 音序器端口的地址。如果驱动程序为 private_data 分配了内存,则必须在 close 回调中自行释放。

最后一个字段 event_passing 指示如何转换音符开/关事件。在 PROCESS_EVENTS 模式下,音符 255 被视为速度变化,并且将键压力事件传递到端口。在 PASS_EVENTS 模式下,所有音符开/关事件都将传递到端口,而无需修改。PROCESS_KEYPRESS 模式检查高于 128 的音符,并将其视为键压力事件(主要用于 Emu8000 驱动程序)。

打开回调

open 在每次应用程序使用 OSS 音序器打开此设备时调用。这不能为 NULL。通常,open 回调执行以下过程

  1. 分配私有数据记录。

  2. 创建 ALSA 音序器端口。

  3. arg->addr 上设置新的端口地址。

  4. arg->private_data 上设置私有数据记录指针。

请注意,此合成端口的 port_info 中的类型位标志不能包含 TYPE_MIDI_GENERIC 位。相反,应使用 TYPE_SPECIFIC。此外,也不应包括 CAP_SUBSCRIPTION 位。这对于将其与其他普通 MIDI 设备区分开来是必要的。如果打开过程成功,则返回零。否则,返回 -errno。

Ioctl 回调

当音序器接收到特定于设备的 ioctl 时,会调用 ioctl 回调函数。此回调函数应处理以下两个 ioctl:

IOCTL_SEQ_RESET_SAMPLES

重置内存中的所有采样 -- 返回 0

IOCTL_SYNTH_MEMAVL

返回可用内存大小

FM_4OP_ENABLE

通常可以忽略

其他的 ioctl 在音序器内部处理,不会传递给底层驱动程序。

Load_Patch 回调函数

load_patch 回调函数用于下载采样数据。此回调函数必须读取用户空间的数据并将其传输到每个设备。如果成功,返回 0;如果失败,返回 -errno。format 参数是 patch_info 记录中的补丁键。buf 是存储 patch_info 记录的用户空间指针。offs 可以忽略。count 是此采样数据的总大小。

Close 回调函数

当应用程序关闭此设备时,会调用 close 回调函数。如果在 open 回调函数中分配了任何私有数据,则必须在 close 回调函数中释放。还应该在此处删除 ALSA 端口。此回调函数不能为空。

Reset 回调函数

当音序器设备被应用程序重置或关闭时,会调用 reset 回调函数。此回调函数应立即关闭相关端口上的声音,并初始化端口的状态。如果未定义此回调函数,OSS 音序器会向端口发送一个 HEARTBEAT 事件。

事件

大多数事件由音序器处理,并转换为相应的 ALSA 音序器事件,以便每个合成器设备都可以通过 ALSA 音序器端口的 input_event 回调接收。驱动程序应实现以下 ALSA 事件:

ALSA 事件

原始 OSS 事件

NOTEON

SEQ_NOTEON, MIDI_NOTEON

NOTE

SEQ_NOTEOFF, MIDI_NOTEOFF

KEYPRESS

MIDI_KEY_PRESSURE

CHANPRESS

SEQ_AFTERTOUCH, MIDI_CHN_PRESSURE

PGMCHANGE

SEQ_PGMCHANGE, MIDI_PGM_CHANGE

PITCHBEND

SEQ_CONTROLLER(CTRL_PITCH_BENDER), MIDI_PITCH_BEND

CONTROLLER

MIDI_CTL_CHANGE, SEQ_BALANCE (使用 CTL_PAN)

CONTROL14

SEQ_CONTROLLER

REGPARAM

SEQ_CONTROLLER(CTRL_PITCH_BENDER_RANGE)

SYSEX

SEQ_SYSEX

大多数这些行为可以通过 Emu8000 底层驱动程序中包含的 MIDI 仿真驱动程序来实现。在未来版本中,此模块将是独立的。

一些 OSS 事件(SEQ_PRIVATESEQ_VOLUME 事件)作为 SND_SEQ_OSS_PRIVATE 事件类型传递。OSS 音序器会传递这些 8 字节的数据包,不做任何修改。底层驱动程序应适当地处理这些事件。

MIDI 设备接口

由于 OSS 仿真通过接收来自 ALSA 音序器的声明自动探测 ALSA MIDI 音序器端口的创建和删除,因此 MIDI 设备无需像合成器设备那样显式注册。但是,注册到 ALSA 音序器的 MIDI port_info 必须包含组名称 SND_SEQ_GROUP_DEVICE 和功能位 CAP_READCAP_WRITE。此外,还必须定义订阅功能,CAP_SUBS_READCAP_SUBS_WRITE。如果这些条件不满足,该端口将不会注册为 OSS 音序器 MIDI 设备。

通过 MIDI 设备发送的事件在 OSS 音序器中解析,并转换为相应的 ALSA 音序器事件。来自 MIDI 音序器的输入也由 OSS 音序器转换为 MIDI 字节事件。这只是 seq_midi 模块的反向工作方式。

已知问题 / 待办事项

  • 尚未实现通过 ALSA 乐器层加载补丁的功能。