2010-09-22 11 views
9

Jakie są prawidłowe sposoby inicjowania (przydzielania pamięci) i zwalniania (zwalniania) listy AudioBufferList za pomocą 3 AudioBufferów? (Jestem świadomy, że może istnieć więcej niż jeden sposób robienia tego.)iPhone: inicjacja i wydanie AudioBufferList

Chciałbym użyć tych 3 buforów, aby odczytać sekwencyjne części pliku audio i odtwarzać je za pomocą Audio Unit.

Odpowiedz

15

Oto jak to zrobić:

AudioBufferList * 
AllocateABL(UInt32 channelsPerFrame, UInt32 bytesPerFrame, bool interleaved, UInt32 capacityFrames) 
{ 
    AudioBufferList *bufferList = NULL; 

    UInt32 numBuffers = interleaved ? 1 : channelsPerFrame; 
    UInt32 channelsPerBuffer = interleaved ? channelsPerFrame : 1; 

    bufferList = static_cast<AudioBufferList *>(calloc(1, offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * numBuffers))); 

    bufferList->mNumberBuffers = numBuffers; 

    for(UInt32 bufferIndex = 0; bufferIndex < bufferList->mNumberBuffers; ++bufferIndex) { 
     bufferList->mBuffers[bufferIndex].mData = static_cast<void *>(calloc(capacityFrames, bytesPerFrame)); 
     bufferList->mBuffers[bufferIndex].mDataByteSize = capacityFrames * bytesPerFrame; 
     bufferList->mBuffers[bufferIndex].mNumberChannels = channelsPerBuffer; 
    } 

    return bufferList; 
} 
11

Przede wszystkim, myślę, że faktycznie chcesz 3 AudioBufferLists, a nie jednego AudioBufferList z 3 członami AudioBuffer. AudioBuffer reprezentuje pojedynczy kanał danych, więc jeśli masz 3 stereofoniczne pliki audio, powinieneś umieścić je w 3 AudioBufferListach, z których każda ma 2 AudioBuffers, jeden bufor dla lewego kanału i jeden dla prawego. Twój kod będzie następnie przetwarzał każdą listę (i dane jej kanału) osobno, i możesz przechowywać listy w NSArray lub coś w tym stylu.

Z technicznego punktu widzenia nie ma powodu, dla którego nie można mieć pojedynczej listy buforów z 3 przeplecionymi kanałami audio (co oznacza, że ​​zarówno prawy kanał lewy & jest przechowywany w pojedynczym buforze danych), ale jest to sprzeczne z konwencjonalnym wykorzystaniem API i będzie nieco mylące.

W każdym razie ta część CoreAudio API jest bardziej C-ish niż Objective-C-ish, więc używałbyś malloc/free zamiast alloc/release. Kod wyglądałby tak:

#define kNumChannels 2 
AudioBufferList *bufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) * kNumChannels); 
bufferList->mNumberBuffers = kNumChannels; // 2 for stereo, 1 for mono 
for(int i = 0; i < 2; i++) { 
    int numSamples = 123456; // Number of sample frames in the buffer 
    bufferList->mBuffers[i].mNumberChannels = 1; 
    bufferList->mBuffers[i].mDataByteSize = numSamples * sizeof(Float32); 
    bufferList->mBuffers[i].mData = (Float32*)malloc(sizeof(Float32) * numSamples); 
} 

// Do stuff... 

for(int i = 0; i < 2; i++) { 
    free(bufferList->mBuffers[i].mData); 
} 
free(bufferList); 

Powyższy kod zakłada, że ​​dane są wczytywane jako zmiennoprzecinkowe. Jeśli nie robisz żadnego specjalnego przetwarzania plików, bardziej efektywnie jest je czytać jako SInt16 (surowe dane PCM), ponieważ iPhone nie ma jednostki FPU.

Ponadto, jeśli nie używasz list poza pojedynczą metodą, bardziej sensowne jest przydzielanie ich na stosie zamiast stosu przez deklarowanie go jako zwykłego obiektu, a nie wskaźnika. Nadal musisz malloc() faktycznego członka mData AudioBuffer, ale przynajmniej nie musisz martwić się o free() samego faktycznego AudioBufferList.

+0

Dzięki. Więc jeśli mam przeplecione kanały audio, powinienem utworzyć 3 osobne AudioBufferListy z jednym AudioBufferem, prawda? Ale jaki jest sens używania AudioBufferLists? Jeśli dobrze zrozumiem, co mówisz, lepiej będzie dla 3 AudioBufferów (i bez AudioBufferLists) - przynajmniej w tym przypadku. –

+1

Tak, to byłoby poprawne. Celem korzystania z AudioBufferLists jest ułatwienie zarządzania danymi wielokanałowymi, ponieważ zwykle dane z przeplotem są prawdziwym problemem przy wykonywaniu operacji DSP. O wiele lepiej jest mieć oddzielone kanały stereo z każdym kanałem we własnym buforze. Wyobraź sobie, że pracujesz z sygnałem stereo 4.1 - wtedy masz jeden bufor z 5 kanałami z przeplotem! Niezbyt przyjemnie się z tym pracuje. –

+1

Ah, ale nie powiedziałem, żeby całkowicie nie używać AudioBufferList. Choć może wydawać się głupio przekazać pojedynczy AudioBuffer wewnątrz AudioBufferList, są one znacznie łatwiejsze do przekazania w interfejsie API CoreAudio. Poza tym sama struktura AudioBufferList nie narzuca dużego nakładu pamięci. –