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)
参数 name
、type
、subtype
和 nvoices
用于为 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);
除了 open
和 close
回调之外,它们可以为 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_index
、file_mode
和 seq_mode
由 OSS 音序器初始化。app_index
是应用程序索引,对于每个打开 OSS 音序器的应用程序都是唯一的。file_mode
是指示文件操作模式的位标志。有关其含义,请参见 seq_oss.h
。seq_mode
是音序器操作模式。在当前版本中,仅使用 SND_OSSSEQ_MODE_SYNTH
。
接下来的两个字段 addr
和 private_data
必须由合成驱动程序在 open 回调中填充。addr
包含分配给此设备的 ALSA 音序器端口的地址。如果驱动程序为 private_data
分配了内存,则必须在 close 回调中自行释放。
最后一个字段 event_passing
指示如何转换音符开/关事件。在 PROCESS_EVENTS
模式下,音符 255 被视为速度变化,并且将键压力事件传递到端口。在 PASS_EVENTS
模式下,所有音符开/关事件都将传递到端口,而无需修改。PROCESS_KEYPRESS
模式检查高于 128 的音符,并将其视为键压力事件(主要用于 Emu8000 驱动程序)。
打开回调¶
open
在每次应用程序使用 OSS 音序器打开此设备时调用。这不能为 NULL。通常,open 回调执行以下过程
分配私有数据记录。
创建 ALSA 音序器端口。
在
arg->addr
上设置新的端口地址。在
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_PRIVATE
和 SEQ_VOLUME
事件)作为 SND_SEQ_OSS_PRIVATE 事件类型传递。OSS 音序器会传递这些 8 字节的数据包,不做任何修改。底层驱动程序应适当地处理这些事件。
MIDI 设备接口¶
由于 OSS 仿真通过接收来自 ALSA 音序器的声明自动探测 ALSA MIDI 音序器端口的创建和删除,因此 MIDI 设备无需像合成器设备那样显式注册。但是,注册到 ALSA 音序器的 MIDI port_info 必须包含组名称 SND_SEQ_GROUP_DEVICE
和功能位 CAP_READ
或 CAP_WRITE
。此外,还必须定义订阅功能,CAP_SUBS_READ
或 CAP_SUBS_WRITE
。如果这些条件不满足,该端口将不会注册为 OSS 音序器 MIDI 设备。
通过 MIDI 设备发送的事件在 OSS 音序器中解析,并转换为相应的 ALSA 音序器事件。来自 MIDI 音序器的输入也由 OSS 音序器转换为 MIDI 字节事件。这只是 seq_midi 模块的反向工作方式。
已知问题 / 待办事项¶
尚未实现通过 ALSA 乐器层加载补丁的功能。