2016-05-16 25 views
0

根據蘋果的文檔,它說:如何正確使用MIDIReadProc?

因爲你MIDIReadProc回調是從單獨的線程調用, 使用 回調提供的數據時,要注意的同步問題。

這是否意味着,使用@synchronize做安全線程阻塞?

或者這是否意味着同步計時問題可能會發生?

我正在嘗試讀取midi文件,並使用MIDIReadProc觸發基於MIDI事件的軟件合成器的音符/音符。我需要這是非常可靠和完美的時間。現在,我注意到,當我使用這些midi事件並將音頻寫入緩衝區(全部由MIDIReadProc完成)時,時序非常渺茫,聽起來沒有任何問題。所以我想知道,從MIDIReadProc中消耗midi事件的「正確」方式是什麼?

另外,是MIDIReadProc是從MIDI文件中消耗MIDI事件的唯一選擇嗎?

就設置可以被我的合成器直接消耗的虛擬端點而言,還有其他選擇嗎?如果是這樣,那究竟是如何工作的?

回答

0

如果你想這格式的功能是midiReadProc

void midiReadProc(const MIDIPacketList *packetList, 
       void* readProcRefCon, 
       void* srcConnRefCon) 
{ 
    MIDIPacket *packet = (MIDIPacket*)packetList->packet; 

    int count = packetList->numPackets; 
    for (int k=0; k<count; k++) { 
     Byte midiStatus = packet->data[0]; 
     Byte midiChannel= midiStatus & 0x0F; 
     Byte midiCommand = midiStatus >> 4; 

    //parse MIDI messages, extract relevant information and pass it to the controller 
    //controller must be visible from the midiReadProc 
    }  
packet = MIDIPacketNext(packet); 
} 

的MIDI客戶端已經在controller聲明,解釋MIDI事件被存儲到controller從MIDI回調,讀取的每個音頻渲染週期上的audioRenderCallback()。通過這種方式,您可以最小化音頻緩衝區的時間不精確度,您可以在AudioUnit設置期間協商音頻緩衝區的長度,使其與系統允許的一樣短。

控制器可以是你定義一個@interface myMidiSynthController : NSViewController,由MIDI信道的矩陣和的預先確定的最大複音每通道,其中諸如接口元件,相位累加器爲每個活動的語音等相關數據, AudioComponentInstance等...根據midiReadProc()輸入調整控制器大小是錯誤的。 RAM現在很便宜。

我正在使用這種MIDI回調來處理來自MIDI設備的現場輸入。關於MIDI文件的播放,如果您想要處理任意複雜的流或文件,您可能會遇到意外。 MIDI標準本身 具有計時功能,其工作與MIDI硬件一樣好。一旦你將整個文件讀入內存,你可以將你的數據轉換成任何你想要的,並使用自己的代碼來控制聲音合成。
請注意不要使用阻塞音頻渲染線程的任何代碼(即在audioRenderCallback()之內),或者對其進行內存管理。

+0

但是您沒有提及如何「將它傳遞給控制器​​」,以便它可以在特殊的midiReadProc高優先級線程之外處理。顯然,我需要將這些數據包存儲到主線程可以使用並轉換爲音頻的數據結構中。如何正確地做到這一點?理想情況下,我想要一個多維數組,因此我可以執行數據包[midiChannel] [currentChannelPacketIndex] = *數據包...但我不知道如何分配給定通道上的總共多少個數據包,以便我可以正確分配該存儲的內存。 – patrick

+0

@patrick,你的假設「主線程可以消耗某些東西並轉化爲音頻」或者似乎有措辭問題,或者不完全確切。只要在實時渲染線程上完成音頻製作/消費,主線程就只關注GUI更新,這是時間選擇最有效的選擇。關於將MIDI生成的數據傳遞給控制器​​,如果您只是將其傳遞給控制器​​,您仍然需要將數據包解碼爲渲染線程將理解的內容。你的問題相當廣泛,我會盡快調整我的答案。 – user3078414

+0

「關於播放MIDI文件,如果您想處理任意複雜的流或文件,您可能會遇到意外。」 - 那是我的問題。我正嘗試傳輸一個185bpm的midi文件,其中最快的音符是第16個,這聽起來像是一場絕對的災難.. – patrick

0

您可以使用AVAudioEngine.musicSequence並準備您的音頻單位圖。然後使用MusicSequence API加載您的GM文件。像這樣,你不需要自己做時間。注意到目前爲止我還沒有做到這一點,但我理解它應該像這樣工作。

實例化合成器音頻單元后,將其附加並連接到AVAudioEngine圖表。

這是否意味着,使用@synchronize做安全線程阻塞?

與你所說的相反:你當然不應該鎖定實時線程。如果資源已被鎖定,@synchronized指令將被鎖定。你可以考慮使用無鎖隊列來實時線程。另見Four common mistakes in audio development

如果您必須使用CoreMIDI和MIDIReadProc,則可以通過在回調中調用MusicDeviceMIDIEvent來將MIDI命令發送到合成器音頻單元。