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