如何向LV2插件(atom)发送MIDI消息——基于Lilv迷你宿主
使用Lilv向LV2插件发送MIDI消息(音符开示例)
前提准备
首先确保你已经包含了必要的头文件,包括Lilv核心、LV2 Atom和MIDI相关的定义:
#include <lilv/lilv.h> #include <lv2/atom/atom.h> #include <lv2/atom/util.h> #include <lv2/midi/midi.h>
还要确认你的项目链接了Lilv库,并且已经初始化了Lilv世界、加载了目标LV2插件、创建了插件实例。
核心实现步骤
1. 定位插件的MIDI输入端口
LV2中MIDI事件通过atom:Sequence类型的端口传输,所以首先要找到插件的输入Atom Sequence端口:
// 假设你已经有lilv_plugin* plugin和lilv_instance* instance实例 const LilvPort* midi_in_port = NULL; LilvIter* ports = lilv_plugin_get_ports(plugin); while (lilv_iter_is_valid(ports)) { const LilvPort* port = lilv_iter_get(ports); // 筛选输入类型的Atom Sequence端口 if (lilv_port_is_input(port) && lilv_port_is_a(plugin, port, lilv_atom_sequence_uri)) { midi_in_port = port; break; } ports = lilv_iter_next(ports); } lilv_iter_free(ports); if (!midi_in_port) { fprintf(stderr, "找不到插件的MIDI输入端口\n"); return -1; }
2. 创建并初始化Atom Sequence缓冲区
Atom Sequence是LV2中事件流的容器,我们需要分配缓冲区并初始化:
// 分配足够的缓冲区空间(可根据实际需求调整大小) const uint32_t buffer_size = 4096; LV2_Atom_Sequence* seq = (LV2_Atom_Sequence*)malloc(buffer_size); if (!seq) { fprintf(stderr, "内存分配失败\n"); return -1; } // 初始化Sequence,设置容器类型和总容量 lv2_atom_sequence_init(seq, buffer_size, lilv_atom_sequence_type_uri);
3. 构建并添加音符开事件
LV2 MIDI事件的格式和标准MIDI一致,音符开消息的结构是0x90 + 音高 + 力度,我们把它打包进Atom Sequence:
// 事件的时间戳(基于采样帧,这里示例在第0帧触发) const uint32_t frame_time = 0; // 音符开:状态字节0x90,音高60(中C),力度100 uint8_t midi_note_on[] = {0x90, 60, 100}; const size_t msg_len = sizeof(midi_note_on); // 向Sequence中追加事件 LV2_Atom_Event* event = lv2_atom_sequence_append( seq, buffer_size, frame_time, lilv_midi_event_type_uri, msg_len ); if (event) { // 将MIDI数据复制到事件的body区域 memcpy(LV2_ATOM_BODY(&event->body), midi_note_on, msg_len); } else { fprintf(stderr, "无法添加MIDI事件到Sequence\n"); free(seq); return -1; }
4. 发送事件并运行插件
把填充好的Atom Sequence写入插件的MIDI输入端口缓冲区,然后运行插件处理事件:
// 获取插件端口的缓冲区指针 LilvPortBuffer* port_buf = lilv_instance_get_port_buffer(instance, midi_in_port); if (!port_buf) { fprintf(stderr, "无法获取端口缓冲区\n"); free(seq); return -1; } // 将Sequence复制到端口缓冲区 memcpy(port_buf, seq, lv2_atom_total_size(&seq->atom)); // 运行插件,处理事件(这里示例运行1024帧) lilv_instance_run(instance, 1024, 0, 0, NULL);
5. 资源清理
别忘了释放分配的内存:
free(seq);
额外注意事项
- 音符关消息可以用
0x80 + 音高 + 0,或者0x90 + 音高 + 0(两种都符合LV2 MIDI规范) - 时间戳
frame_time是相对于当前run周期起始帧的偏移量,需要根据宿主的播放进度动态调整 - 确保Lilv世界已经正确初始化,并且插件实例的音频端口也已经正确配置(否则插件可能无法正常输出音频)
- 如果需要批量发送多个事件,重复调用
lv2_atom_sequence_append即可,注意不要超过缓冲区大小
内容的提问来源于stack exchange,提问作者NumberSigmaTafKsee
相关产品推荐
相关产品推荐

