原本用AVAudioPlayer來播放一些音效感覺很方便,
可是要播放即時的音訊串流就沒輒了.
請見 Technical Q&A QA1634
好吧~那就自立自強捲起袖子… 趕緊google阿!
不過google大神給的答案實在很朦朧,
勉勉強強找到的殘破程式碼也有看沒有懂.
幸好天無絕人之路 :)
Apress出的iPhone cool projects這本書第六章有寫捏,
又有source code可以run,實在"揪甘心"阿.
範例程式:下載
如果剛點開範例程式,跟我一樣有頭暈眼花的症狀.
那不用擔心,沒有這麼難,看完下面解釋說不定就懂了.
從架構上來講,一共分成三個部分:
1. AudioPlayer跟AudioRequest可以視作一個大項
主要負責下達URLrequest,並將收到的data回傳給AudioStream
另外就是擔任AudioStream的委託(delegate)去使用AudioQueue
可是要播放即時的音訊串流就沒輒了.
請見 Technical Q&A QA1634
好吧~那就自立自強捲起袖子… 趕緊google阿!
不過google大神給的答案實在很朦朧,
勉勉強強找到的殘破程式碼也有看沒有懂.
幸好天無絕人之路 :)
Apress出的iPhone cool projects這本書第六章有寫捏,
又有source code可以run,實在"揪甘心"阿.
|
官方網站 |
範例程式:下載
如果剛點開範例程式,跟我一樣有頭暈眼花的症狀.
那不用擔心,沒有這麼難,看完下面解釋說不定就懂了.
從架構上來講,一共分成三個部分:
1. AudioPlayer跟AudioRequest可以視作一個大項
主要負責下達URLrequest,並將收到的data回傳給AudioStream
另外就是擔任AudioStream的委託(delegate)去使用AudioQueue
2. AudioStream有三件主要的事情
* 將一收到的data進行parse並得到StreamID
* 有了StreamID之後會分成 propertyCallback與packetCallback
先講propertyCallback, 其目的顧名思義就是要辨識Audio的屬性
就想像成一段資料收進來,需要貼上標籤,註明格式,頻率等等
* packetCallback會將收進來的stream分成數個packet
然後請委託(delegate)幫忙執行AudioQueue的程式部分
3. AudioQueue會接受來自委託(delegate)的幾件事情
* AudioQueueNewOutput會產生一個新的playback audio queue物件
* AudioQueuePlay會開始播放AudioQueue裡頭的音訊
* 上述兩項是延續自propertyCallback的部份,在packetCallback這邊
因為有一整段stream的大小,所以透過AudioQueueAllocateBuffer給定
適當的buffer size後,使用AudioQueueEnqueueBuffer一直塞buffer
到audio queue就會播出我們要的串流音訊啦!
neilmix[dot]com[slash]book[slash]etude[dot]mp3
嗯…一切自我感覺良好,沒有問題
換成linear PCM格式的wav source…結果是"大崩壞"!
追了很久發現有一個叫packetDescription的structure資料全都是null,
難怪在memory copy時都會造成crash.
所以精華來啦~
1. 這邊mp3的audio stream是VBR格式,也就是說每個packet大小可能不一,
因此packetDescription會特別記錄這些資訊.
linear PCM的wav是CBR格式自然沒有這些資料,
所以簡單的方法就是把packetDescriptions copy這段註解掉.
2. 由於CBR格式中的bit rate是固定關係, AudioQueueEnqueueBuffer後兩個
參數為0,跟NULL.
3. 總結來講,AudioQueue service是精隨.只要搞清楚流程,一邊餵進Audio基本資料
另一邊allocate好audio queue把data依序塞進去,輕鬆播放串流音訊非難事!
參考資料: iOS Reference Library
zonble
cocoaChina
stackoverflow (synthesize with CoreAudio)
stackoverflow (iPhone combine audio files)
歐~差點忘了,
在iOS4的simulator環境下跑,開始的時候都會頓一下
log會出現"AddRunningClient starting device on non-zero client count"訊息
雖然不會造成什麼大問題,不過這樣卡卡真的有點討厭就是了
網路上一片無解阿~討論串
* 將一收到的data進行parse並得到StreamID
* 有了StreamID之後會分成 propertyCallback與packetCallback
先講propertyCallback, 其目的顧名思義就是要辨識Audio的屬性
就想像成一段資料收進來,需要貼上標籤,註明格式,頻率等等
* packetCallback會將收進來的stream分成數個packet
然後請委託(delegate)幫忙執行AudioQueue的程式部分
3. AudioQueue會接受來自委託(delegate)的幾件事情
* AudioQueueNewOutput會產生一個新的playback audio queue物件
* AudioQueuePlay會開始播放AudioQueue裡頭的音訊
* 上述兩項是延續自propertyCallback的部份,在packetCallback這邊
因為有一整段stream的大小,所以透過AudioQueueAllocateBuffer給定
適當的buffer size後,使用AudioQueueEnqueueBuffer一直塞buffer
到audio queue就會播出我們要的串流音訊啦!
NOTE: 2011/01/10
這個範例被我拿來改用之後發現有memory allocation的問題
我原以為是我改壞了 才造成memory allocation不斷飆升的情況
最近花了一點時間修改一下 發現問題就出在AudioQueueAllocateBuffer
這個function不應該在data每次進來都做一次
如果好好看過Apple提供的playback示意圖
AudioQueueAllocateBuffer只會在AudioQueue宣告出來後指定好
講是這樣講啦~
實際測試了一下書中範例給的mp3neilmix[dot]com[slash]book[slash]etude[dot]mp3
嗯…一切自我感覺良好,沒有問題
換成linear PCM格式的wav source…結果是"大崩壞"!
追了很久發現有一個叫packetDescription的structure資料全都是null,
難怪在memory copy時都會造成crash.
所以精華來啦~
1. 這邊mp3的audio stream是VBR格式,也就是說每個packet大小可能不一,
因此packetDescription會特別記錄這些資訊.
linear PCM的wav是CBR格式自然沒有這些資料,
所以簡單的方法就是把packetDescriptions copy這段註解掉.
1 2 3 4 5 | memcpy(outBufferRef->mAudioData, data.bytes, data.length); outBufferRef->mAudioDataByteSize = data.length; //memcpy(outBufferRef->mPacketDescriptions, packetDescriptions, sizeof(AudioStreamPacketDescription) * packetCount); outBufferRef->mPacketDescriptionCount = packetCount; |
2. 由於CBR格式中的bit rate是固定關係, AudioQueueEnqueueBuffer後兩個
參數為0,跟NULL.
3. 總結來講,AudioQueue service是精隨.只要搞清楚流程,一邊餵進Audio基本資料
另一邊allocate好audio queue把data依序塞進去,輕鬆播放串流音訊非難事!
參考資料: iOS Reference Library
zonble
cocoaChina
stackoverflow (synthesize with CoreAudio)
stackoverflow (iPhone combine audio files)
歐~差點忘了,
在iOS4的simulator環境下跑,開始的時候都會頓一下
log會出現"AddRunningClient starting device on non-zero client count"訊息
雖然不會造成什麼大問題,不過這樣卡卡真的有點討厭就是了
網路上一片無解阿~討論串