Aros/开发者/文档/库/CAMD
要创建两个程序之间的链接。两者都打开 camd.library,并使用 CreateMidi() 创建一个节点。现在要进行通信,只需创建一个指向命名位置的链接。AddMidiLink。第二个程序执行相同的操作,只是它使用自己的节点和接收器。现在程序 A 发送的每个 MIDI 事件都将被程序 B 接收。此外,任何其他寻找连接位置的程序都会看到“uniquename”作为选项。他们可以发送到它或从它接收。
获得精确播放的一种方法是将每个事件时间转换为从音乐开始的偏移量。然后在播放时,发送所有时间戳早于或等于“现在”的事件,然后休眠到 Music_Start_Time + 下一个事件的偏移量。
USB 将自动轮询并找到所有 MIDI 设备
原始的 Commodore Amiga MIDI 驱动程序 (CAMD) 是一个用于 AmigaOS 的共享库,它为 MIDI 数据提供了一个通用设备驱动程序,这样应用程序可以实时地相互共享 MIDI 数据,并以独立于设备的方式与 MIDI 硬件接口。AmigaOS 自身在 3.1 版本发布之前并不支持 MIDI,直到 Roger Dannenberg 的 CAMD 库被采用为标准 MIDI API。Commodore 版本的 CAMD 还包含一个内置驱动程序,用于 Amiga 串行端口。阅读更多 这里
早期使用 AROS 版本作为基础,但最终在 2012 年左右,当前版本的 camd 是 41.1(2016 年 11 月 26 日),这是一个大部分闭源的重写版本,最新版本带有 src 的版本在 50.8
A Camd 库用 src 重写 并且是兼容的,始于 2000 年代初。
首选项->环境是同步的连接。有两种同步
- 由 MIDI 时钟控制(在环境窗口中简称为“时钟”)
- 由 MIDI 时间码控制(简称为“MTC”)
MIDI 时钟仅同步两个 MIDI 设备之间的节奏,而它们何时开始或停止则独立,而 MIDI 时间码还同步时间点。MTC 主要用于与特殊时间表同步,例如在 PowerPoint、视频或幻灯片演示中。
BarsnPipes 本身中的音序器需要 MIDI 输入和输出工具。这些工具目前在板载版本中缺失。我们需要提到的驱动程序作为 Poseidon-USB 系统和 CAMD(Commodore Amiga MIDI 驱动程序)系统之间的桥梁。新的 camd.library 没有内置的串行端口驱动程序。只需使用 mmp/port 0 代替。它也应该比旧的内部驱动程序更稳定。
只将你使用的驱动程序复制到 devs:midi/ 中。不要在 devs:midi/ 中放置多个针对相同硬件的驱动程序。例如,不要同时拥有 mmp 和 uaemidi(它们都使用串行端口),例如 out.0、out.1、out.2、in.0、in.1 等。不再存在。链接将具有 <drivername>.out.0、<drivername>.out.1、<drivername>.in.0 等名称。代替。
CAMD 的 MIDI 播放始终需要输出集群的名称。一开始几乎总是“out.0”,并且许多程序仅针对该值进行了硬编码。CAMD 文档指出,我们要求用户提供他们想要的输出,并在每个应用程序的基础上保存它。
不幸的是,现代 PC 硬件不支持 MIDI 的传输速度:31.25 kBaud 通过串行端口。除此之外,还需要一个所谓的 MIDI 接口,它将串行信号转换为 MIDI。AMIGA 上的串行传输非常简单,因为那里的传输速度是可编程的。
Link Links Application ----------> Cluster ----------> Application <---------- Application Node Node
每个节点都是硬件、接口或应用程序
标签
MIDI_Name (STRPTR) name of the node, usually the program name MIDI_SignalTask (struct Task *) Task to be signaled, defaults to current task MIDI_RecvHook (struct Hook *) the hook to be called when new messages arrive MIDI_PartHook (struct Hook *) the hook to call when linkages are added or removed MIDI_RecvSignal (int8) the signal to send when messages arrive MIDI_PartSignal (int8) the signal to send when linkages are added or removed MIDI_MsgQueue (uint32) the desired size of incoming message queue MIDI_SysExSize (uint32) the desired byte size of the System Exclusive buffer MIDI_TimeStamp (uint32*) pointer to the desired MIDI time stamp source. MIDI_ErrFilter (uint16) the desired error filter for this node. see camd.h MIDI_ClientType (uint16) the desired client type for this node. see camd.h MIDI_Image (struct Image *) Image (suggested 32X32) for this node.
链接是在节点之间创建的
标签
MLINK_Name (STRPTR) name for this link MLINK_Location (STRPTR) Cluster to connect to, Case sensitive MLINK_ChannelMask (uint16) Mask of which MIDI channels to listen to, defaults to ~0 MLINK_EventMask (uint16) Mask of which types of MIDI events to listen for, defaults to ~0 MLINK_UserData (CPTR) User defined MLINK_Comment (STRPTR) highest priority link will comment the cluster MLINK_PortID (uint8) Value to copy to any msgs arriving through this link MLINK_Private (BOOL) if TRUE, link requests to be hidden MLINK_Priority (int8) priority of this MidiLink MLINK_SysExFilter (uint32) data is 3 1 byte SysEx ID's to filter with MLINK_SysExFilterX (uint32) data is one 3 Byte SysEx ID to filter with MLINK_Parse (BOOL) If true, CAMD will parse incoming stream into MIDI Messages MLINK_ErrorCode, (uint32*) points to an error code buffer
来自不同应用程序或接口(输入、输出或两者)的链接的汇合被称为集群。它允许来自一个链接的消息自动到达其他链接。
Link Links Application ----------> Cluster ----------> Application <---------- Application
您可以始终使用 CAMD 进行任务间通信。大多数 MIDI 消息不超过三个字节,但系统独占消息可以更长,在合理的范围内。所有消息都在命名端口之间传递
如果将多个流发送到同一个目的地,它们在接收时将被简单地混合在一起。接收器可以从它关心的任何地方知道每个消息来自哪里。您也可以从给定端口发送多个流,每个目的地都会收到消息的副本。因此,混合(合并)很容易,多播也很容易。
/* MidiMsg struct */ mm_Msg mm_Time mm_Status mm_Data1 mm_Data2 mm_Port mm_Data
GetMidi PutMidi
低 4 位包含通道号,如果 MIDI 消息在与设置的位不匹配的通道上,则不使用消息
MIDI 想要什么
note on/off program change pitch bend controller change MSB controller change LSB controller change Boolean switch controller change single byte controller parameter change undefined controllers mode change messages channel after touch polyphonic after touch system real-time messages (MIDI clock, MTC Quarter Frame) system common messages (Start, Stop, etc) system exclusive messages
MIDI 系统
MIDI 分发系统基于应用程序之间“链接”(称为 MidiLinks)的概念。每个应用程序或硬件驱动程序都可以建立与其他应用程序或硬件驱动程序的链接。可以建立多个链接到单个源,以便多个应用程序可以看到从硬件端口或应用程序输出输出的 MIDI 流。类似地,多个应用程序可以将 MIDI 流发送到单个硬件端口或应用程序输入。能够让一个应用程序将数据发送到另一个应用程序允许 MIDI 流的“管道化”,例如将交互式作曲程序连接到音序器并同时运行两者。
请注意,没有要求发送的数据实际上是有效的音乐数据——一对应用程序可以建立一个私有链接,并相互通信任何它们想要的东西,只要它遵循 MIDI 的语法规则。但是,建议将此类链接隐藏在用户面前(使用一个使链接私有的特殊位),因为最终会有一款“补丁编辑器”,它将允许用户在应用程序不知情的情况下操作链接。
每个 MIDI 应用程序都必须创建一个 MidiNode。此结构用作传入和传出消息的中央调度点,并保存所有特定于应用程序的信息,包括
-- location and size of input buffers. -- the name of the application -- the icon to be used for the application in the patch editor. -- the address of the task to signal when messages are received, and the signal bit to use.
MIDI 消息
每个发送或接收的 MIDI 消息都包含在 MidiMsg 结构中。此 8 字节结构包含时间戳、实际 MIDI 字节(最多 3 个)和链接号(以便具有多个输入链接的应用程序可以确定哪个链接接收了消息)。请注意,由于消息很小,因此在传输 MIDI 数据时会复制整个消息,而不是传递指针。
如何接收 MIDI 数据
MIDI 应用程序可以是基于任务的或基于回调的。基于任务的应用程序使用信号来等待传入的 MIDI 数据。收到信号后,应用程序可以调用 GetMidi() 来实际查看接收到的内容。所有传入的消息都被排队,并且有一个单独的队列用于系统独占消息(这可能很长)。每个传入的 MIDI 事件都被打上时间戳,并用来自其接收链接的链接号(由应用程序设置)标记。
有些人质疑一项任务是否能以足够快的速度响应传入的 MIDI 数据以满足专业的定时精度标准。我们的实验已经确定一个高优先级任务(例如,30 或更多)可以满足这些要求,即使磁盘驱动器正在运行。
但是,如果应用程序处理 MIDI 的速度非常快,则使用回调可能更好。回调发生在发送者的上下文中,因此最好快速执行,以免减慢发送任务。(请注意,发送者始终是一个任务,而不是一个中断,因为实际的硬件驱动程序通过任务进行服务)回调通过标准 Hook 结构调用。使用回调可以避免任务切换的开销,并且可以提高整体性能。
如何发送 MIDI 数据
发送 MIDI 数据非常简单,主要是填写 MidiMsg 结构并调用 PutMidi()。请注意,如果接收缓冲区已满,则该函数将失败,而不是等待接收缓冲区清空。
系统独占
对于那些不熟悉 MIDI 的人来说,系统独占消息(简称 SysEx)是 MIDI 规范中的一种“逃生门”,它允许开发人员定义自己的消息。与其他限制为 3 个字节或更少的 MIDI 事件不同,SysEx 消息可以是任何长度。在 CAMD 中,SysEx 消息通过将消息的标头(前三个字节)作为 MidiMsg 放入常规接收队列,并将完整的消息放入单独的缓冲区来处理。接收者可以查看前三个字节,并决定是否要通过调用 GetSysEx() 来读取其余部分,或者通过调用 SkipSysEx() 将其丢弃;
Sending SysEx is done by calling the function PutSysEx().
过滤器
为了减少系统负载,可以过滤 MIDI 数据,以便只有有用的数据显示在应用程序的输入缓冲区中。每个 MidiLink 都有一个过滤器位集,可以允许忽略传入消息(不放入接收队列)。第一组过滤器位对应于 16 个 MIDI 通道。(对于那些不熟悉 MIDI 的人来说,第一个 MIDI 字节的低 nibble 包含通道号)。如果传入的 MIDI 消息位于与过滤器字中设置的位之一不对应的通道上,则该消息将被跳过。第二个过滤器基于事件类型,CAMD 将其分为 14 类
-- note on/off -- program change -- pitch bend -- controller change MSB -- controller change LSB -- controller change boolean switch -- controller change single byte -- controller parameter change -- undefined controllers -- mode change messages -- channel after touch -- polyphonic after touch -- system real-time messages (MIDI clock, MTC Quarter Frame) -- system common messages (Start, Stop, etc) -- system exclusive messages
此外,还存在一个针对 SysEx 消息的特殊过滤系统,允许根据 SysEx 标头字节之后的第一个字节或前三个字节过滤它们。如果仅使用第一个字节,则可以指定三个不同的过滤器。如果使用前三个字节,则只能指定一个过滤器。
建立链接
MidiLinks 的优点之一是,即使另一个应用程序或硬件驱动程序尚未加载,也可以建立它们。这是因为 MidiLink 不直接连接到另一个应用程序,而是连接到一个称为“集线器”的 MidiLinks 的“汇合点”或“会合点”。每个集线器都以名称引用。例如,如果我建立一个到“foo”集线器的输出链接,而其他人建立一个到同一个集线器的输入链接,那么我的应用程序发送到该链接的任何数据都将被该另一个应用程序接收。如果第三个应用程序创建到“foo”的输入链接,那么它也将接收数据,而如果另一个应用程序创建到“foo”的输出链接,那么它的 MIDI 数据将与我的数据合并,并分发到所有输入链接。
所以集线器的规则如下
-- The first attempt to link to a cluster creates the cluster, and the last link to leave deletes it. -- Each sender link to a cluster is merged with all the other senders. -- Each receiver link to a cluster gets a copy of what all the other receivers get.
此外,集线器还有一些其他属性
参与者:库函数 MidiLinkConnected() 可用于检查集线器以查看是否有任何相反类型的链接。例如,发送者可以检查是否有人在监听,或者他们是否只是在对真空说话。类似地,接收者可以检查是否有任何发送者。此外,您可以请求在集线器中的参与者发生变化时通知您(通过信号)。此功能主要由库本身中的硬件接口使用——它允许在没有应用程序使用驱动程序时关闭驱动程序(并释放硬件资源)。
集线器注释:为了构建选择集线器的用户界面,每个到集线器的链接都可以指定一个最多 34 个字符的“注释”,描述该集线器实际上是什么。但是,由于每个集线器只能有一个注释,因此第一个链接的注释是使用的注释。
集线器模型的优点之一是,应用程序可以按任何顺序启动并仍然可以工作。以下是一些关于应用程序如何处理链接的建议
- 应用程序应允许用户查看现有集线器的列表(CAMD 可以提供),或允许用户键入新的集线器名称。[问题:用户界面中应该使用“集线器”一词吗?]
- 应用程序应将当前链接保存在应用程序的“设置”中,或嵌入到文档或性能文件中(可能使用 IFF 块)。当应用程序重新启动(或加载该性能或任何其他操作)时,应用程序应自动建立指定的链接。
如果每个应用程序都这样做,那么用户可以轻松地设置与上次相同的链接配置,即使他们以不同的顺序启动应用程序。即使应用程序是通过脚本调用的,应用程序网络也可以自动出现。
(最终我们将需要一个“性能管理器”,它可以“快照”整个应用程序网络,允许用户稍后返回该“状态”。这可以使用 ARexx 完成。)
MIDI 时间戳
每个传入的 MIDI 消息都可能被加盖时间戳,但是,您必须提供一个计时信息源(推荐的方法是使用 RealTime.library,但是许多其他时间戳源是可能的)。
您可以告诉 CAMD 使用特定计时源。MidiNode 包含一个指向器(类型为 LONG *),它可以指向时间戳的来源。每当收到 MidiMsg 时,指向该指针的 longword 就会被用作当前时间,并被复制到 MidiMsg 中。通常,您想要做的是将此指针指向一个正在不断更新的 longword。请注意,以这种方式,您的应用程序可以以任何它想要的格式拥有时间戳,因为 CAMD 从不查看时间戳字段,一旦它被设置。
一个重要的点是时间戳是在将消息放入接收者的缓冲区时设置的。在第一个 MIDI 状态字节的中断时间对消息进行时间戳将是不错的选择,但这将使集线器模型的分布变得不可能。希望获得最终精度的应用程序可能应该调整时间戳以补偿 MidiMsg 的长度。
与硬件接口
CAMD 保持一个硬件驱动程序列表,这些驱动程序用于访问串行端口、DSP 板或任何其他硬件接口。一个首选项文件 (ENV:sys/midi.prefs) 用于指示哪些硬件驱动程序应加载以及哪些端口应激活。请注意,使用文件通知,CAMD 可以随时更改此设置,即使应用程序正在运行。
硬件驱动程序位于 DEVS:midi 目录中。它们可以由第三方开发人员创建,并且非常简单。
CAMD 为每个输入 MIDI 流维护一个任务。该任务负责从硬件读取字节,将它们解析为 MidiMsgs,并将它们发送到集线器。集线器名称在首选项文件中按端口指定。
示例
[edit | edit source]#include <stdio.h> #include <proto/exec.h> #include <proto/camd.h> #include <midi/camd.h> #define TABSIZE 4 struct Library *CamdBase=NULL; #ifndef GetMidiLinkAttrs ULONG GetMidiLinkAttrs(struct MidiLink *ml, Tag tag, ...){ return GetMidiLinkAttrsA(ml, (struct TagItem *)&tag ); } #endif #ifndef GetMidiAttrs ULONG GetMidiAttrs(struct MidiNode *ml, Tag tag, ...){ return GetMidiAttrsA(ml, (struct TagItem *)&tag ); } #endif struct MidiLink *GetMidiLinkFromOwnerNode(struct MinNode *node){ struct MidiLink dummy; return (struct MidiLink *)((char *)((char *)(node)-((char *)&dummy.ml_OwnerNode-(char *)&dummy))); } void printSpaces(int level){ int lokke; for(lokke=0;lokke<level*TABSIZE;lokke++){ printf(" "); } } void printLink_brancheNodes(struct MidiLink *midilink,int level,int maxlevel); void printLink_brancheClusters(struct MidiLink *midilink,int level,int maxlevel); void printCluster(struct MidiCluster *cluster,int level,int maxlevel); void printNode(struct MidiNode *midinode,int level,int maxlevel){ char *nodename=NULL; struct MinNode *node; if(level==maxlevel) return; GetMidiAttrs(midinode,MIDI_Name,(IPTR)&nodename,TAG_END); printSpaces(level); printf( "%p, -%s-\n", midinode, nodename ); if(level+1==maxlevel) return; if( ! (IsListEmpty((struct List *)&midinode->mi_OutLinks))){ printSpaces(level); printf(" -OutLinks:\n"); node=midinode->mi_OutLinks.mlh_Head; while(node->mln_Succ!=NULL){ printLink_brancheClusters(GetMidiLinkFromOwnerNode(node),level+1,maxlevel); node=node->mln_Succ; } } if( ! (IsListEmpty((struct List *)&midinode->mi_InLinks))){ printSpaces(level); printf(" -InLinks:\n"); node=midinode->mi_InLinks.mlh_Head; while(node->mln_Succ!=NULL){ printLink_brancheClusters(GetMidiLinkFromOwnerNode(node),level+1,maxlevel); node=node->mln_Succ; } } } BOOL printLink(struct MidiLink *midilink){ char *linkname=NULL; if(midilink->ml_Node.ln_Type==NT_USER-MLTYPE_Receiver || midilink->ml_Node.ln_Type==NT_USER-MLTYPE_Sender){ GetMidiLinkAttrs(midilink,MLINK_Name,(IPTR)&linkname,TAG_END); printf( "%p, -%s-\n", midilink, linkname ); return TRUE; } printf("%p, <driverdata> (private)\n",midilink); return FALSE; } void printLink_brancheNodes(struct MidiLink *midilink,int level,int maxlevel){ struct MidiNode *midinode; if(level==maxlevel) return; printSpaces(level); if(printLink(midilink)==TRUE){ midinode=midilink->ml_MidiNode; printSpaces(level); printf(" -Owner (MidiNode): \n"); printNode(midinode,level+1,maxlevel); } } void printLink_brancheClusters(struct MidiLink *midilink,int level,int maxlevel){ if(level==maxlevel) return; printSpaces(level); printLink(midilink); if(level+1==maxlevel) return; printSpaces(level); printf(" -Cluster: \n"); printCluster(midilink->ml_Location,level+1,maxlevel); } void printCluster(struct MidiCluster *cluster,int level,int maxlevel){ struct MidiLink *midilink; if(level==maxlevel) return; printSpaces(level); printf("clustername: -%s-\n",cluster->mcl_Node.ln_Name); if(level+1==maxlevel) return; if(!(IsListEmpty(&cluster->mcl_Receivers))){ printSpaces(level); printf(" "); printf("-Receiver links:\n"); midilink=(struct MidiLink *)cluster->mcl_Receivers.lh_Head; while(midilink->ml_Node.ln_Succ!=NULL){ printLink_brancheNodes(midilink,level+1,maxlevel); midilink=(struct MidiLink *)midilink->ml_Node.ln_Succ; } } if(!(IsListEmpty(&cluster->mcl_Senders))){ printSpaces(level); printf(" "); printf("-Sender links:\n"); midilink=(struct MidiLink *)cluster->mcl_Senders.lh_Head; while(midilink->ml_Node.ln_Succ!=NULL){ printLink_brancheNodes(midilink,level+1,maxlevel); midilink=(struct MidiLink *)midilink->ml_Node.ln_Succ; } } } int main(){ APTR lock; struct MidiCluster *cluster; CamdBase=OpenLibrary("camd.library",40L); if(CamdBase!=NULL){ lock=LockCAMD(CD_Linkages); cluster=NextCluster(NULL); if(cluster==NULL){ printf("No clusters available.\n"); }else{ printf("-Clusters:\n\n"); do{ printCluster(cluster,1,6); printf("\n"); cluster=NextCluster(cluster); }while(cluster!=NULL); } UnlockCAMD(lock); CloseLibrary(CamdBase); }else{ printf("Could not open at least V40 of camd.library.\n"); return 1; } return 0; }
#include <proto/camd.h> #include <proto/dos.h> #include <proto/exec.h> #include <midi/camd.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char **argv) { struct MidiNode *ournode; struct MidiLink *to; MidiMsg mmsg; if((ournode = Camd->CreateMidi( MIDI_MsgQueue, 2048L, MIDI_SysExSize, 10000L, MIDI_Name, argv[0], TAG_END))) { if((to = Camd->AddMidiLink(ournode, MLTYPE_Sender, MLINK_Location, argv[1], TAG_END))) { mmsg.mm_Status = MS_Start; mmsg.mm_Data1 = 0; mmsg.mm_Data2 = 0; Camd->PutMidi(to, mmsg.mm_Msg); Camd->RemoveMidiLink(to); } Camd->DeleteMidi(ournode); } CloseLibrary(CamdBase); return 0; }
为 camd.library 创建一个 midi 驱动程序
[edit | edit source]作为其在 init 例程中的第一个操作,Camd.library 搜索 Devs:Midi/ 中的所有文件,并加载所有可能的 midi 设备驱动程序。为此,驱动程序当然必须放置在 Devs:Midi 目录中。
midi 设备驱动程序的中心点是 MidiDeviceData 结构,它必须包含在您的二进制文件中,在某个地方。Camd.library 搜索您的文件以定位该结构。为此,您需要遵循一些规则,让 Camd.library 识别该驱动程序为合法。
该结构如下所示(取自 Include/devs/camddevices.h)
struct MidiDeviceData { ULONG Magic; char *Name; char *IDString; UWORD Version; UWORD Revision; BOOL (ASM *Init)( REG(a6) APTR SysBase ); void (*Expunge)(void); struct MidiPortData *(ASM *OpenPort)( REG(a3) struct MidiDeviceData *data, REG(d0) LONG portnum, REG(a0) ULONG (* ASM transmitfunc)(APTR REG(a2) userdata), REG(a1) void (* ASM receivefunc)(UWORD REG(d0) input,APTR REG(a2) userdata), REG(a2) APTR userdata ); void (ASM *ClosePort)( REG(a3) struct MidiDeviceData *data, REG(d0) LONG portnum ); UBYTE NPorts; UBYTE Flags; };
- Magic - 这个必须包含 MDD_MAGIC 常量,它也位于 Include/devs/midi/camddevices.h 中。
- Name 必须包含您文件的名称。例如,如果驱动程序文件的名称为“Pinnacle-Fiji”,则“Name”也必须是指向包含“Pinnacle-Fiji”的字符串的指针。如果“Name”与文件的名称不匹配,则该驱动程序将不会加载。(但是,如果“Flags”为 0,则这不是必需的,稍后将详细介绍)。
- IDString 指向描述您驱动程序的字符串。
- Version 16 位无符号整数,包含驱动程序的版本。
- Revision 16 位无符号整数,包含驱动程序的修订版。
Init
Must contain a pointer to a legal function. This is the first function to be called after the driver is loaded into memory. The function will be called with sysbase as its only argument and it must return FALSE if a necessary initialisation failed, and otherwise TRUE. If FALSE is returned, the 'Expunge'-function will not be called, so any resources already allocated before the failure will have to be freed from within the function, before returning. The 'Init'-function is normally called from the Camd.library's 'Init'-function, but due to some, hmm, bad design in AROS/Camd.library; you shouldn't be too sure the Forbid-state is not broken, so please call Forbid()/Permit() if that is needed.
In this function you may also set the 'Expunge', 'OpenPort', 'ClosePort' and 'NPorts' attributes if you want to. If they were not set before, you must set them now. This feature might be of use if you don't know how many ports are available before running. (As an example, if you want to make drivers for Turtle Beachs' pinnacle and fiji, instead of making two drivers, you just check if the device is pinnacle or fiji, and set 'NPorts' one or two higher for pinnacle than you would if it were a fiji, since pinnacle and fiji are very similar, apart from the fact that pinnacle has a built-in midi-synth and possibly an add-on synth-card.)
Expunge 此函数在驱动程序从内存中卸载之前被调用。它通常从 Camd.libraries Expunge 例程调用,但不要相信 Forbid() 状态不会被破坏。
OpenPort
指向从 Camd.library 首次调用时调用的函数的指针,当应用程序想要使用驱动程序中的特定端口时。它提供以下参数
- “data” 如果您不想在驱动程序中使用任何全局数据,则可以使用此数据。它指向您的 MidiDeviceData 结构。
- “portnum” 要使用的端口号。
- “transmitfunc” 当有 MIDI 数据等待通过端口发送时,您调用的函数的指针。重要说明:“transmitfunc” 实际上是单线程的,即,具有相同“userdata” 的两个访问者同时访问将不起作用。调用此函数时可能会禁用中断。有关“transmitfunc” 的更多信息,请参见下文。
- “receiverfunc” 当来自您要分发到 Camd.library 的端口的 MIDI 数据时,您调用的函数的指针。“receiverfunc” 也是单线程的,因此一次只有一个访问者具有相同的“userdata”(虽然不必指出)。调用时不能禁用中断,因为它会发出信号给任务。“input” 参数是要发送到 camd 的 MIDI 字节。它不是消息,只是一个字节。如果设置了第 8 位,则告诉 Camd.library 端口上存在溢出。如果您的 MIDI 设备使用运行状态来降低数据流(几乎总是这样),则不必担心,因为 Camd.library 中的 receiverfunc 会处理运行状态。
- “userdata” 指向此端口的唯一数据的指针,在调用由“transmitfunc” 和“receiverfunc” 指向的函数时必须提供。
关于“transmitfunc” 和“receiverfunc” 参数。它们始终指向相同的函数,也可以在 Init 函数中提供。但是,这是 Camd.library 从一开始就被设计的方式,可能不会改变。尽管如此,在驱动程序中保留两个指向“transmitfunc” 和“receiverfunc” 的指针是安全的。有关示例,请参阅调试驱动程序源代码。最重要的是,它们都可以从中断中调用。
ClosePort 指向当最后一个使用您的端口的应用程序告诉 Camd.library 它不再需要该端口时调用的函数的指针。
NPorts 驱动程序提供的端口数量。可以在编译时直接设置,也可以在 Init 函数中设置。如果“Flags” 中的第 0 位未设置,则“NPorts” 必须在编译时直接设置(“Flags” 中的第 0 位仅适用于旧的驱动程序格式,如果您正在阅读此文档文件,则可能不想使用它)。
Flags 目前只使用一个标志,即第 0 位。不要设置任何其他标志。如果未设置第 0 位,camd.library 就会知道这是一个旧类型的驱动程序。但是,该模式不适合新的 camd.library,因此请设置此标志。
“OpenPort” 应该返回指向 MidiPortData 结构的指针。如果端口无法打开,则返回 NULL。
MidiPortData 结构如下所示
struct MidiPortData { void (* ASM ActivateXmit)(ULONG REG(d0) portnum); };
“ActivateXmit” 包含指向驱动程序中函数的指针,该函数从 Camd.library 调用,每当 Camd.library 有一些要分发的 MIDI 数据时。换句话说,当 Camd.library 调用此函数时,就该尽快调用“transmit” 函数来获取 mididata。以下是一个示例,说明如何执行此操作
while((data=(transmitfunc)(UserData[portnum-1])!=0x100) SendDataToPort(portnum-1,data);
(这里,“UserData” 是“usedrata” 的数组。“userdata” 在调用“OpenPort” 时提供。)
如您所见,可能存在多个字节等待提取(实际上这是常见情况),当没有更多数据要提取时,“transmitfunc” 返回 0x100。
与原始驱动程序格式不同,即使没有更多数据要提取,调用“transmitfunc” 也不会造成任何损害。(这是旧驱动程序格式和新驱动程序格式之间最重要的变化:你怎么可能知道对于所有可能的情况,没有更多数据要提取呢?)
还要注意,“ActivateXmit” 可能会被调用,即使没有更多数据要提取,或者您当前正在使用另一个任务或中断提取数据。但幸运的是,“ActivateXmit” 只有在您返回后才会再次被调用,因此它对于每个端口都是单线程的。
请注意,“transmitfunc” 返回使用运行状态优化的数据,因此,如果您发送的数据不会通过 MIDI 电缆直接传输,或者您发送数据的目标以某种方式无法处理运行状态,则必须手动跟踪运行状态。但是,如果是这种情况,最好编写一个正常的 Camd.library 应用程序,该应用程序创建一个新的集群,您通过挂钩接收数据,然后再次将其分发到您想要的位置。然后您将获得所有状态字节。(使用 Camd.library 的应用程序无法区分正常驱动程序和这里描述的驱动程序技术。)
/* Copyright � 1995-2001, The AROS Development Team. All rights reserved. $Id$ Desc: Lang: English */ /********************************************************************************* Not a very good example at all. But at least it should prove that AROS is able to use camd-drivers. -Kjetil M. Compiling it up is easy. Its just like any other AROS-program, showed by this makefile: " debugdriver: debugdriver.o gcc -nostartfiles -nostdlib -Xlinker -i ../../lib/startup.o debugdriver.o -o debugdriver -L../../lib -larossupport -lamiga -larosc -larosm debugdriver.o: debugdriver.c makefile gcc debugdriver.c -c -I../../Include -Wall " ***********************************************************************************/ #include <proto/exec.h> #include <exec/types.h> #include <midi/camddevices.h> #include <libcore/compiler.h> #define NUMPORTS 4 struct ExecBase *SysBase; int main(void){ /* A camd mididriver is not supposed to be run directly, so we return an error. */ return -1; } /* Prototypes */ extern void kprintf(char *bla,...); BOOL ASM Init(REG(a6) APTR sysbase); void Expunge(void); SAVEDS ASM struct MidiPortData *OpenPort( REG(a3) struct MidiDeviceData *data, REG(d0) LONG portnum, REG(a0) ULONG (* ASM transmitfunc)(APTR REG(a2) userdata), REG(a1) void (* ASM recievefunc)(UWORD REG(d0) input,APTR REG(a2) userdata), REG(a2) APTR userdata ); ASM void ClosePort( REG(a3) struct MidiDeviceData *data, REG(d0) LONG portnum ); /* End prototypes */ /*********************************************************************** The mididevicedata struct. Note. It doesn't have to be declared with the const qualifier, since NPorts may be set at Init. You should set the name-field to the same as the filename, that might be a demand... ***********************************************************************/ const struct MidiDeviceData mididevicedata={ MDD_Magic, "debugdriver", "Debugdriver V41.0 (c) 2001 AROS - The AROS Research OS", 41, 0, Init, Expunge, OpenPort, ClosePort, NUMPORTS, 1 }; /**************************************************************** We only store sysbase, thats all we need in this example. Otherwise, you may want to open libraries, set number of ports, obtain interrupts, etc. ***************************************************************/ SAVEDS ASM BOOL Init(REG(a6) APTR sysbase){ SysBase=sysbase; return TRUE; } /**************************************************************** Nothing to do here. Normally, you may want to free memory, close some libraries, free some interrupts, etc. *****************************************************************/ void Expunge(void){ return; } ULONG (ASM *TransmitFunc)(REG(a2) APTR userdata); APTR UserData[NUMPORTS]; /**************************************************************** Normally, you may want to start an interrupt, or signal another task, or send a message to a port, that calls the transmit-function. But for this small example, sending the signal directly via kprintf is excactly what we want to do. ****************************************************************/ SAVEDS ASM void ActivateXmit(REG(a2) APTR userdata,ULONG REG(d0) portnum){ ULONG data; for(;;){ data=(TransmitFunc)(userdata); if(data==0x100) return; kprintf("Debugdriver has received: %lx at port %ld\n",data,portnum); } } struct MidiPortData midiportdata={ ActivateXmit }; /**************************************************************** This one is called whenever a program that has opened camd.library wants to use your services. ****************************************************************/ SAVEDS ASM struct MidiPortData *OpenPort( REG(a3) struct MidiDeviceData *data, REG(d0) LONG portnum, REG(a0) ULONG (* ASM transmitfunc)(APTR REG(a2) userdata), REG(a1) void (* ASM recieverfunc)(UWORD REG(d0) input,APTR REG(a2) userdata), REG(a2) APTR userdata ){ /* We haven't got any receiver function, so we don't bother about storing the receiverfunc variable. */ TransmitFunc=transmitfunc; UserData[portnum-1]=userdata; return &midiportdata; } /**************************************************************** Nothing to do here. Normally, you may want to free memory, mark the port not to be in use anymore, delete a task, etc. *****************************************************************/ ASM void ClosePort( REG(a3) struct MidiDeviceData *data, REG(d0) LONG portnum ){ return; }
APTR LockCAMD(ULONG locktype) void UnlockCAMD(APTR lock) struct MidiNode *CreateMidiA(struct TagItem *tags) void DeleteMidi(struct MidiNode *midinode) BOOL SetMidiAttrsA(struct MidiNode *midinode, struct TagItem *tags) ULONG GetMidiAttrsA(struct MidiNode *midinode, struct TagItem *tags) struct MidiNode *NextMidi(struct MidiNode *midinode) struct MidiNode *FindMidi(STRPTR name) void FlushMidi(struct MidiNode *midinode) struct MidiLink *AddMidiLinkA(struct MidiNode *midinode, LONG type, struct TagItem *tags) void RemoveMidiLink(struct MidiLink *midilink) BOOL SetMidiLinkAttrsA(struct MidiLink *midilink, struct TagItem *tags) ULONG GetMidiLinkAttrsA(struct MidiLink *midilink, struct TagItem *tags) struct MidiLink *NextClusterLink(struct MidiCluster *cluster, struct MidiLink *midilink, LONG type) struct MidiLink *NextMidiLink(struct MidiNode *midinode, struct MidiLink *midilink, LONG type) BOOL MidiLinkConnected(struct MidiLink *midilink) struct MidiCluster *NextCluster(struct MidiCluster *last) struct MidiCluster *FindCluster(STRPTR name) void PutMidi(struct MidiLink *link, ULONG msg) BOOL GetMidi(struct MidiNode *midinode, MidiMsg *msg) BOOL WaitMidi(struct MidiNode *midinode, MidiMsg *msg) void PutSysEx(struct MidiLink *midilink, UBYTE *buffer) ULONG GetSysEx(struct MidiNode *midinode, UBYTE *Buf, ULONG len) ULONG QuerySysEx(struct MidiNode *midinode) void SkipSysEx(struct MidiNode *midinode) UBYTE GetMidiErr(struct MidiNode *midinode) WORD MidiMsgType(MidiMsg *msg) WORD MidiMsgLen(ULONG msg) void ParseMidi(struct MidiLink *midilink, UBYTE *buffer, ULONG length) struct MidiDeviceData *OpenMidiDevice(UBYTE *name) void CloseMidiDevice(struct MidiDeviceData *mididevicedata) LONG RethinkCAMD() void StartClusterNotify(struct ClusterNotifyNode *cn) void EndClusterNotify(struct ClusterNotifyNode *cn) APTR GoodPutMidi(struct MidiLink *midilink, ULONG msg, ULONG maxbuff) BOOL Midi2Driver(APTR driverdata, ULONG msg, ULONG maxbuff)
AddMidiLinkA() CloseMidiDevice() CreateMidiA() DeleteMidi() EndClusterNotify() FindCluster() FindMidi() FlushMidi() GetMidi() GetMidiAttrsA() GetMidiErr() GetMidiLinkAttrsA() GetSysEx() GoodPutMidi() LockCAMD() Midi2Driver() MidiLinkConnected() MidiMsgLen() MidiMsgType() NextCluster() NextClusterLink() NextMidi() NextMidiLink() OpenMidiDevice() ParseMidi() PutMidi() PutMidiMsg() PutSysEx() QuerySysEx() RemoveMidiLink() RethinkCAMD() SetMidiAttrsA() SetMidiLinkAttrsA() SkipSysEx() StartClusterNotify() UnlockCAMD() WaitMidi()
AddMidiLinkA()
概要
struct MidiLink * AddMidiLinkA( struct MidiNode * midinode, LONG type, struct TagItem * tags ); struct MidiLink * AddMidiLink( struct MidiNode * midinode, LONG type, TAG tag, ... );
函数
将一个 midilink 添加到一个 midinode 中。输入
midinode - 所有者。type - MLTYPE_Receiver 或 MLTYPE_Sender 标签 - 提供给 SetMidiLinkAttrs 的标签值。另见 CreateMidiA() SetMidiLinkAttrsA()
CloseMidiDevice()
概要
void CloseMidiDevice( struct MidiDeviceData * mididevicedata );
函数
提醒我稍后在此处填写内容。另见 OpenMidiDevice()
CreateMidiA()
概要
struct MidiNode * CreateMidiA( struct TagItem * tags ); struct MidiNode * CreateMidi( TAG tag, ... );
输入
标签 - 提供给 SetMidiAttrs 的标签值。如果失败,则为 NULL。
DeleteMidi()
概要
void DeleteMidi( struct MidiNode * midinode );
函数
首先删除附加到 midinode 的所有 midilink,然后释放所有缓冲区,最后释放自身。
EndClusterNotify()
概要
void EndClusterNotify( struct ClusterNotifyNode * cn );
函数
void EndClusterNotify(struct ClusterNotifyNode *) 输入
指向先前添加的 ClusterNotifyNode 的指针。结果
void 备注
不要使用未添加的 ClusterNotifyNode 调用。错误
无。另见 StartClusterNotify(),
FindCluster()
概要
struct MidiCluster * FindCluster(STRPTR name );
函数
从 camd 的内部 midicluster 列表中查找一个 midicluster。输入
name - 要查找的集群的名称。结果
如果找不到集群,则为 NULL。备注
- 在调用之前必须锁定 CL_Linkages。另见 FindMidi()
FindMidi()
概要
struct MidiNode * FindMidi( STRPTR name );
函数
查找名称为“name” 的 midinode。输入
name - 要查找的 midinode 的名称。结果
如果找不到该名称的 midinode,则为 NULL;如果成功,则为指向 midinode 的指针。备注 CL_Linkages 必须被锁定。
FlushMidi()
概要
void FlushMidi( struct MidiNode * midinode );
函数
提醒我稍后在此处填写内容。错误
未经测试。另见 GetMidi() GetSysEx()
GetMidi()
概要
BOOL GetMidi( struct MidiNode * midinode, MidiMsg * msg );
函数
从 midinode 的缓冲区中获取一条消息。输入
midinode - 指向 midinode msg - 消息将从内部缓冲区中删除并复制到 msg 中。结果
如果复制了消息,则为 TRUE;如果缓冲区为空,则为 FALSE。另见 WaitMidi()
GetMidiAttrsA()
概要
ULONG GetMidiAttrsA( struct MidiNode * midinode, struct TagItem * tags ); ULONG GetMidiAttrs( struct MidiNode * midinode, TAG tag, ... );
备注
如果您不是 midinode 的所有者,则应在调用之前锁定 Camd,以确保它不会消失。另见 SetMidiAttrsA(),
GetMidiErr()
概要
UBYTE GetMidiErr( struct MidiNode * midinode );
函数
获取 midinode 的当前错误状态。输入
midinode - 指向 midinode 结果
如果一切正常,则为 0;否则为非 0。另见 GetMidi() WaitMidi()
GetMidiLinkAttrsA()
概要
ULONG GetMidiLinkAttrsA( struct MidiLink * midilink, struct TagItem * tags ); ULONG GetMidiLinkAttrs( struct MidiLink * midilink, TAG tag, ... );
函数
提醒我稍后在此处填写内容。备注
如果您不是 midilink 的所有者,则应在调用之前锁定 Camd,以确保它不会消失。如果您知道它不会消失,则锁定没有意义。另见 SetMidiLinkAttrsA()
GetSysEx()
概要
ULONG GetSysEx( struct MidiNode * midinode, UBYTE * Buf, ULONG len );
函数
提醒我稍后在此处填写内容。另见 SkipSysEx() QuerySysEx()
GoodPutMidi()
概要
APTR GoodPutMidi( struct MidiLink * midilink, ULONG msg, ULONG maxbuff );
函数
这是一个私有函数,可能很快就会过时。请勿使用。结果
如果成功,则为 NULL;如果失败,则为 driverdata。另见 PutMidi() PutMidiMsg() Midi2Driver()
LockCAMD()
概要
APTR LockCAMD( ULONG locktype );
函数
锁定 camd 中的内部列表。您必须稍后调用 UnlockCAMD。输入
locktype - 只有 CD_Linkages 是合法的。结果 APTR,发送到 UnlockCAMD
Midi2Driver()
概要
BOOL Midi2Driver( APTR driverdata, ULONG msg, ULONG maxbuff );
函数
这是一个私有函数,可能很快就会过时。请勿使用。结果
如果 max(buffer,maxbuffer) 足够大以容纳消息,则为 TRUE;否则为 FALSE。另见 PutMidi() GoodPutMidi() PutMidiMsg()
MidiLinkConnected()
概要
BOOL MidiLinkConnected( struct MidiLink * midilink );
函数
如果 midilink 是发送方,则在集群没有接收方时返回 FALSE。如果 midilink 是接收方,则在集群没有发送方时返回 FALSE。否则返回 TRUE。输入 midilink - 我们要检查的 midilink 的指针。
MidiMsgLen()
概要
WORD MidiMsgLen( ULONG msg );
函数
返回 midimessage 的长度。sysex 消息导致长度为零。输入 msg - 消息。
MidiMsgType()
概要
WORD MidiMsgType( MidiMsg * msg );
函数
返回消息的类型(参见 <midi/camd.h>)。sysex 消息返回 -1。输入
msg - midimessage。NextCluster()
概要
struct MidiCluster * NextCluster( struct MidiCluster * last );
函数
在 camd 的集群列表中查找下一个集群。输入 last - 要开始搜索的集群。结果 列表中的下一个集群,如果“last” 为 NULL,则为第一个。
#include <stdio.h> #include <proto/exec.h> #include <proto/camd.h> #include <midi/camd.h> int main(){ APTR lock; struct MidiCluster *cluster; struct Library *CamdBase=OpenLibrary("camd.library",0L); if(CamdBase!=NULL){ lock=LockCAMD(CD_Linkages); cluster=NextCluster(NULL); if(cluster==NULL){ printf("No clusters available.\n"); }else{ do{ printf("clustername: -%s-\n",cluster->mcl_Node.ln_Name); cluster=NextCluster(cluster); }while(cluster!=NULL); } UnlockCAMD(lock); CloseLibrary(CamdBase); }else{ printf("Could not open camd.library.\n"); return 1; } return 0; }
备注
- CL_Linkages 必须被锁定。
- 通常,程序想要使用此函数来查找可用的
clusters a user can choose from. It is then recommended to also let the user have the possibility to write in the name of a new cluster, so that camd can make new clusters automatically to be used for communication between various applications without having hardware-drivers etc. interfere with the datastreams. Applications do not need to make special concerns about how cluster works or what they contain; that is all managed by camd.
另见 NextMidiLink() NextMidi() FindCluster()
NextClusterLink()
概要
struct MidiLink * NextClusterLink( struct MidiCluster * cluster, struct MidiLink * midilink, LONG type );
函数
在 midicluster 中查找指定类型的下一个 midilink。输入
cluster - 指向 midilink 所属的 midicluster 的指针。midilink - 指向要从其开始搜索的 midilink 的指针。type - MLTYPE_Receiver 或 MLTYPE_Sender 结果
返回指定类型的下一个 MidiLink,如果它是列表中的最后一个,则返回 NULL。如果 midilink 为 NULL,则返回第一个。备注 CL_Linkages 必须被锁定。
NextMidi()
概要
struct MidiNode * NextMidi( struct MidiNode * midinode );
函数
返回 midinode 列表中的下一个 midinode,如果 midinode 是最后一个,则返回 NULL。输入
midinode - 要从其开始搜索的 midinode。如果为 NULL,则
returns the first midinode in the list.
备注 CL_Linkages 必须被锁定。
NextMidiLink()
概要
struct MidiLink * NextMidiLink( struct MidiNode * midinode, struct MidiLink * midilink, LONG type );
函数
返回属于 midinode 的指定类型的下一个 MidiLink。或者,如果 midilink 是最后一个,则返回 NULL。如果 midilink 为 NULL,则返回第一个。输入 type - MLTYPE_Sender 或 MLTYPE_Receiver。
备注 CL_Linkages 必须被锁定。
OpenMidiDevice()
概要
struct MidiDeviceData * OpenMidiDevice( UBYTE * name );
函数
另见 CloseMidiDevice()
ParseMidi()
概要
void ParseMidi( struct MidiLink * midilink, UBYTE * buffer, ULONG length );
函数
将 midibuffer 放入 midilink 的集群 midilink midinode 和硬件中。为了帮助理解它的作用,以下宏使 PutMidi 使用 ParseMidi 而不是调用 camd.library 的 PutMidi 函数来处理小端 CPU
#define PutMidi(midilink,message) ParseMidi((midilink),&(message),MidiMsgLen(message))
(但是请不要使用此宏,因为它不兼容大端,而 PutMidi 比 ParseMidi 更快)
备注
如果使用 PutMidi 和 PutSysEx 比使用 ParseMidi 更方便,请这样做。ParseMidi 比 PutMidi 和 PutSysEx 更重。
调用 AddMidiLinkA 或 SetMidiLinkAttrsA 时,必须设置 MLINK_Parse。
另见 PutMidi() PutSysEx()
PutMidi()
概要
void PutMidi( struct MidiLink * link, ULONG msg );
函数
将 MIDI 消息发送到硬件和属于 MIDI 连接集群的所有发送方链接。仅在硬件发送缓冲区已满时等待,然后反复尝试直到消息发送成功。否则,函数将立即返回。输入
link - 指向要发送到的 MIDI 连接的指针。
msg - 要发送的完整消息。消息不能超过
than 3 bytes, so it fits fine in a ULONG integer. See NOTES to see how a message is built up.
备注
发送非法消息可能会导致严重后果。如果您出于某种原因不完全确定您的消息是否合法,您可以执行以下测试
if((msg>>24)<0x80 || (msg>>24)==0xf0 || (msg>>24)==0xf7 || (msg>>16&0xff)>0x7f || (msg>>8&0xff)>0x7f){ debug("Warning, illegal midimessage: %x\n",msg); }else{ PutMidi(midilink,msg); }
另请参见 PutMidiMsg()
PutMidiMsg()
概要
PutMidiMsg( link, msg);
函数
调用 PutMidi((midilink),(msg)->mm_Msg) 注意,实现为宏。
PutSysEx()
概要
void PutSysEx( struct MidiLink * midilink, UBYTE * buffer );
函数
分发 SysEx 消息。首先将消息发送到硬件和连接到 MIDI 连接集群的所有 MIDI 节点,然后等待将完整消息发送到硬件(如果有)。如果 MIDI 节点的 SysEx 缓冲区太小而无法容纳该消息,则该消息将不会被发送。如果缓冲区足够大,但空间不足,则会将 SysEx 溢出错误设置为节点。无论发送缓冲区大小如何,消息都会发送到硬件。输入
midilink - 指向链接的指针。buffer - 要发送的消息,必须以 0xf0 开头,以 0xf7 结尾。
No bytes higher than 0x7f are allowed in the message.
另请参见 GetSysEx()
QuerySysEx()
概要
ULONG QuerySysEx( struct MidiNode * midinode );
函数
返回当前 SysEx 消息中剩余的字节数。输入
midinode - 指向 MIDI 节点的指针。结果 SysEx 消息中剩余的字节数。如果从 GetMidi() 读取的最后一条消息不是 SysEx 消息,则返回 0。错误已测试。另请参见 SkipSysEx() GetSysEx()
RemoveMidiLink()
概要
void RemoveMidiLink( struct MidiLink * midilink );
函数
从系统中移除并释放 MIDI 连接。输入 midilink - 指向要移除的 MIDI 连接的指针。
RethinkCAMD()
概要
LONG RethinkCAMD();
函数
使 CAMD 重新加载 MIDI 首选项。结果成功返回 0。错误未实现。
SetMidiAttrsA()
概要
BOOL SetMidiAttrsA( struct MidiNode * midinode, struct TagItem * tags ); BOOL SetMidiAttrs( struct MidiNode * midinode, TAG tag, ... );
输入
tagList -- 指向描述播放器的标签数组
attributes or NULL.
标签
MIDI_Name (STRPTR) -- The name of the midinode; default is NULL or a pointer to a string. MIDI_SignalTask (struct Task *) -- Task to signal whenever a midimessage is arriving to the node; default is the task of the caller of this function. (FindTask(NULL)) MIDI_RecvHook (struct Hook *) -- Function to call whenever a midimessage is arriving to the node. You should get the midimessage as the first argument in the function, however, that has not yet been implemented. Default is NULL. MIDI_PartHook (struct Hook *) -- Don't really know what this one is for. Have to check amigos-autodocs. It does not currently do anything. MIDI_RecvSignal (BYTE) -- Signal bit to use when signalling a task whenever a midimessage is arriving at the node, or -1 to disable signalling. Default is -1. MIDI_PartSignal (BYTE) -- Signal bit to use when signalling a task when..... Default is -1. MIDI_MsgQueue (ULONG) -- Number of messages the messagequeue is able to hold. MIDI_TimeStamp (ULONG *) -- Pointer to an ULONG value which value is copied directly into the timestamp attribute in midimessages whenever a new message is received at the node. MIDI_ErrFilter (UBYTE) -- Filters out the errors you don't want to see. MIDI_ClientType (UWORD) -- What sort of application you that owns this node. MIDI_Image (struct Image *) -- Pointer to an image representing this node. MIDI_ErrorCode (ULONG *) -- Pointer to an ULONG which will be set if something went wrong.
结果
如果一切顺利,则为 TRUE,否则为 FALSE。如果提供,则将错误代码放入 MIDI_ErrorCode 标签指向的 ULONG 中。
备注
- 如果 MIDI 节点不属于您自己,请锁定
camd to ensure it wont go away.
- 虽然您可以修改属于
others, please avoid it, its normally "non of your buziness", and may lead to crashes and other "unexpected" behaviors. However, if you have full control of the owner of the midinode (f.ex when both you and the owner belongs to the same probram and you are absolutely shure you know what you are doing), there is no problem.
另请参见 GetMidiAttrsA()
SetMidiLinkAttrsA()
概要
BOOL SetMidiLinkAttrsA( struct MidiLink * midilink, struct TagItem * tags ); BOOL SetMidiLinkAttrs( struct MidiLink * midilink, TAG tag, ... );
函数
提醒我稍后在此处填写内容。备注
- 如果 MIDI 连接不属于您自己,请锁定
camd to ensure it wont go away.
- 虽然您可以修改属于
others, please avoid it, its normally "non of your buziness", and may lead to crashes and other "unexpected" behaviours. However, if you have full control of the owner of the midilink (f.ex when both you and the owner belongs to the same probram and you are absolutely shure you know what you are doing), there is no problem.
- 警告!如果另一个任务已锁定 CAMD 并正在等待
for you to finish, there will be a deadlock if you try to change priority or change/set cluster.
另请参见 GetMidiLinkAttrsA()
SkipSysEx()
概要
void SkipSysEx( struct MidiNode * midinode );
函数
提醒我以后在这里填写内容。另请参见 QuerySysEx() GetSysEx()
StartClusterNotify()
概要
void StartClusterNotify( struct ClusterNotifyNode * cn );
函数
void StartClusterNotify(struct ClusterNotifyNode *cn) 输入
指向已初始化的 ClusterNotifyNode 结构的指针。结果 void
struct ClusterNotifyNode cnn; cnn.cnn_Task=IExec->FindTask(NULL); cnn.cnn_SigBit=IExec->AllocSignal(-1); StartClusterNotify(&cnn); somewhere down the line... Wait(1L<<cnn.cnn_SigBit) printf("Cluster Changes have happened\n");
备注
ClusterNotifyNode 结构必须保持有效直到 EndClusterNotify();只会发出添加和删除集群的信号,不会发出内部状态变化的信号。另请参见 EndClusterNotify()
UnlockCAMD()
概要
void UnlockCAMD( APTR lock );
函数
解锁 CAMD 中的内部列表。输入
从 LockCAMD 收到的指针。结果
要发送到 UnlockCAMD 的 APTR。另请参见 LockCAMD()
WaitMidi()
概要
BOOL WaitMidi( struct MidiNode * midinode, MidiMsg * msg );
函数
等待节点收到新消息,并将消息复制到 msg。输入
msg - 指向将复制消息的 MIDI 消息的指针。结果
如果收到或已收到新消息,则返回 TRUE;如果 MIDI 节点上出现错误,则返回 FALSE。另请参见 GetMidi()
* * CAMD Driver for Poseidon USB camdusbmidi.class * * Copyright 2006 Chris Hodges * ************************************************************************ * Date | Change *----------------------------------------------------------------------- * 04-Feb-2006 : Initial ************************************************************************ ;output DEVS:midi/poseidonusb include "AmigaLVOs.s" include "Macros.lnk" include "exec/types.i" include "exec/execbase.i" ; for FlushDevice() include "exec/macros.i" include "exec/ports.i" include "utility/hooks.i" include "midi/camddevices.i" _LVOusbCAMDOpenPort equ -108 _LVOusbCAMDClosePort equ -114 Version equ 1 Revision equ 2 Ports equ 16 DEBUG_DETAIL set 0 STRUCTURE CAMDAdapter,0 APTR ca_ActivateFunc BOOL ca_IsOpen STRUCT ca_CAMDRXFunc,h_SIZEOF STRUCT ca_CAMDTXFunc,IS_SIZE ULONG ca_PortNum APTR ca_TXFunc APTR ca_RXFunc APTR ca_UserData APTR ca_TXBuffer ULONG ca_TXBufSize ULONG ca_TXWritePos ULONG ca_TXReadPos APTR ca_MsgPort LABEL CAMDAdapter_SIZE **************************************************************** * * Standard MIDI Device driver header * **************************************************************** ; code at start of file in case anyone tries to execute us as a program FalseStart moveq #-1,d0 rts MDD ; struct MidiDeviceData dc.l MDD_Magic ; mdd_Magic dc.l Name ; mdd_Name dc.l IDString ; mdd_IDString dc.w Version ; mdd_Version dc.w Revision ; mdd_Revision dc.l Init ; mdd_Init dc.l Expunge ; mdd_Expunge dc.l OpenPort ; mdd_OpenPort dc.l ClosePort ; mdd_ClosePort dc.b Ports ; mdd_NPorts dc.b 0 ; mdd_Flags ; 123456789012 Name dc.b 'poseidonusb',0 dc.b '3456789012345678901',0 ; 32 bytes IDString dc.b '$VER: Poseidon USB camdusbmidi.class driver 1.1 (21-Feb-06)',0 dc.b 'Copyright 2006 Chris Hodges',0 even **************************************************************** * * MidiDeviceData Functions * **************************************************************** **************************************************************** * * Init * * FUNCTION * Gets called by CAMD after being LoadSeg'ed. * * INPUTS * None * * RESULTS * TRUE if successful, FALSE on failure. * **************************************************************** Init PUTMSG 10,<"Init!"> PUSHM a6/a0/a1/d1 move.l 4.w,a6 move.l a6,SysBase lea .classname(pc),a1 moveq.l #0,d0 CALL OpenLibrary move.l d0,USBClsBase bne.s .good lea .classname2(pc),a1 moveq.l #0,d0 CALL OpenLibrary move.l d0,USBClsBase .good POPM rts .classname2 dc.b 'USB/' .classname dc.b 'camdusbmidi.class',0 even **************************************************************** * * Expunge * * FUNCTION * Gets called by CAMD immediately before being * UnLoadSeg'ed. * * INPUTS * None * * RESULTS * None * **************************************************************** Expunge PUTMSG 10,<"Expunge!"> PUSHM a6/a1/d0/d1 move.l SysBase(pc),a6 move.l USBClsBase(pc),a1 CALL CloseLibrary clr.l USBClsBase POPM rts **************************************************************** * * SendToCAMD * * Called by CallHookA() from camdusbmidi.class with Buffer address in a2 * and size in a1. * **************************************************************** SendToCAMD PUTMSG 10,<"SendToCAMD %08lx, buf=%08lx">,a0,a2 PUSHM a2-a4/d2 moveq.l #0,d2 move.l a2,a3 ; object move.l h_Data(a0),a0 move.b (a3)+,d2 PUTMSG 10,<"size=%08ld">,d2 move.l ca_UserData(a0),a2 move.l ca_RXFunc(a0),a4 .loop moveq.l #0,d0 move.b (a3)+,d0 jsr (a4) subq.l #1,d2 bgt.s .loop POPM PUTMSG 10,<"done"> rts **************************************************************** * * GetFromCAMD * * Called by Cause() from ActivateXmit. * **************************************************************** GetFromCAMD PUTMSG 10,<"GetFromCAMD %08lx">,a1 PUSHM a2-a6/d2/d3 move.l a1,a3 move.l ca_UserData(a3),a2 move.l ca_TXBuffer(a3),a5 move.l ca_TXFunc(a3),a4 move.l ca_TXBufSize(a3),d3 move.l ca_TXWritePos(a3),d2 subq.l #1,d3 .loop jsr (a4) PUTMSG 10,<"Byte... %lx %ld">,d0,d1 move.b d0,(a5,d2.l) addq.l #1,d2 and.l d3,d2 cmp.l ca_TXReadPos(a3),d2 beq.s .oops ; this will lose data! move.l d2,ca_TXWritePos(a3) tst.b d1 beq.s .loop .oops move.l ca_MsgPort(a3),d0 ; now inform usb driver beq.s .noport move.l d0,a0 move.l MP_SIGTASK(a0),a1 moveq.l #1,d0 moveq.l #0,d1 move.b MP_SIGBIT(a0),d1 lsl.l d1,d0 move.l SysBase(pc),a6 CALL Signal .noport POPM PUTMSG 10,<"Done"> rts **************************************************************** * * OpenPort * * FUNCTION * Open a MIDI port. * * INPUTS * D0.b - Port number (should always be 0 for this driver) * A0 - Xmit function * A1 - Recv function * A2 - Data * * RESULT * D0 - pointer to MidiPortData structure. * **************************************************************** OpenPort PUTMSG 10,<"OpenPort %ld, Xmit=%08lx,Recv=%08lx,Data=%08lx">,d0,a0,a1,a2 PUSHM a6/a0-a3/d1/d2 move.l USBClsBase(pc),a6 moveq.l #0,d2 move.b d0,d2 move.l d2,d0 lea Name(pc),a3 CALL usbCAMDOpenPort lea CAMDPortBases(pc),a0 move.l d0,(a0,d2.l*4) beq.s .toobad move.l d0,a0 lea ActivateXmit0(pc,d2.l*4),a1 move.l a1,ca_ActivateFunc(a0) move.l d0,ca_CAMDRXFunc+h_Data(a0) lea SendToCAMD(pc),a1 move.l a1,ca_CAMDRXFunc+h_Entry(a0) move.b #NT_INTERRUPT,ca_CAMDTXFunc+LN_TYPE(a0) clr.b ca_CAMDTXFunc+LN_PRI(a0) move.l d0,ca_CAMDTXFunc+IS_DATA(a0) lea GetFromCAMD(pc),a1 move.l a1,ca_CAMDTXFunc+IS_CODE(a0) st ca_IsOpen(a0) .toobad PUTMSG 10,<"Result=%08lx">,d0 POPM rts **************************************************************** * * ClosePort * * FUNCTION * Close a MIDI port. * * INPUTS * D0.b - Port number (always 0 for this driver). * A1 - USBID * * RESULT * None * **************************************************************** ClosePort PUSHM a6/a0/a1/d1 move.l USBClsBase(pc),a6 and.l #$ff,d0 lea Name(pc),a1 CALL usbCAMDClosePort POPM rts IFNE DEBUG_DETAIL kprintf PUSHM a2-a3/a6 lea .putchr(pc),a2 move.l 4.w,a6 move.l a6,a3 CALL RawDoFmt POPM rts .putchr dc.w $CD4B,$4EAE,$FDFC,$CD4B,$4E75 ENDC **************************************************************** * * ActivateXmit * * Called by CAMD with Midi Data in a0 (or a2?) * BUG: Actually, the Midi Data is not in any register. This sucks * **************************************************************** ActivateXmit0 moveq.l #0,d0 bra.s ActivateXmit moveq.l #1,d0 bra.s ActivateXmit moveq.l #2,d0 bra.s ActivateXmit moveq.l #3,d0 bra.s ActivateXmit moveq.l #4,d0 bra.s ActivateXmit moveq.l #5,d0 bra.s ActivateXmit moveq.l #6,d0 bra.s ActivateXmit moveq.l #7,d0 bra.s ActivateXmit moveq.l #8,d0 bra.s ActivateXmit moveq.l #9,d0 bra.s ActivateXmit moveq.l #10,d0 bra.s ActivateXmit moveq.l #11,d0 bra.s ActivateXmit moveq.l #12,d0 bra.s ActivateXmit moveq.l #13,d0 bra.s ActivateXmit moveq.l #14,d0 bra.s ActivateXmit moveq.l #15,d0 ; bra.s ActivateXmit ActivateXmit PUTMSG 10,<"ActivateXmit Port %ld">,d0 PUSHM a6 move.l SysBase(pc),a6 move.l CAMDPortBases(pc,d0.l*4),a0 lea ca_CAMDTXFunc(a0),a1 CALL Cause POPM rts USBClsBase ds.l 1 SysBase ds.l 1 CAMDPortBases ds.l 16 END