掌上游戲機開發指南GBA探索日記(9)(轉)
掌上游戲機開發指南GBA探索日記(9)(轉)[@more@] 這一節我們討論如何正常結束聲音播放的問題。不要覺得這是個小問題,當初這個小問題難倒了我們所有研究GBA的愛好者。我總覺得這一點似乎是GBA設計上的缺陷,但是最後我們可以透過使用VBlank中斷還解決這個問題。本篇結束的時候,我還會給出完整聲音播放的程式碼。這樣,你甚至不必瞭解聲音播放的細節就可以實現聲音的播放功能了。 由於聲音播放使用的DMA不能根據WORD COUNT來控制傳輸的資料量,同時又不能在傳輸完畢的時候產生DMA中斷,所以我們無法知道什麼時候DMA把我們的音訊資料傳輸完畢。這樣,我們必須自己另外加一計時器,來計算什麼時候聲音播放結束。當然,你可以使用Timer來做,但是另外再設定個Timer似乎就麻煩了點,而且Timer的精度過高,也不好計算長時間的聲音播放。依照官方開發包中的做法,我們就拿VBlank中斷作為聲音播放的計時器。 VBlank中斷是在硬體掃描螢幕V方向掃描線的時候產生的硬體中斷。它是嚴格的每秒59.78次(接近60次/s) 下面我們來看看這段修改後的播放聲音的程式碼。需要注意拿他和前一節的播放程式碼來比較一下,看看他們之間有什麼不同。 這個PlayDirectSoundA的播發函式多了一個引數u32 time.它就是用來控制聲音結束的時間引數。它的單位是秒(對於一般的聲音播放來說,秒單位應該是足夠精確了)。 soundATime = time * 59.727; 這條程式碼是將實際播放時間的秒數轉換成一共要產生VBlank的中斷數。前面說過,我們將使用VBlank來判斷我們的聲音是否播放結束。而VBlank中斷的頻率是59.727Hz,也就是說一秒中要產生59.727次。比如說如果我們的這段聲音要播放5秒鐘,那麼播放過程中一共要產生5*59.727次VBlank中斷。這樣,我們只要使用一個計數器,每次VBlank中斷產生的時候計數器自加1,當它等於5*59.727的時候,那麼聲音也就應該結束了。 其它的程式碼就差不多和前一節的PlayDirectSoundA差不多了。 ////////////////////////////////////PlayDirectSoundA///////////////////////////////////// // time是播發時間,單位是秒數 void PlayDirectSoundA(u8 *sound, u16 sampleRate, u32 length,u32 time) { //Stop any previous sample soundAPlaying = 0; *(vu16 *)REG_TM0CNT_H = 0; *(vu16 *)REG_TM0CNT_L = 0; *(vu32 *)REG_DMA1SAD = 0; *(vu16 *)REG_DMA1CNT_H = 0; *(vu32 *)REG_DMA1DAD = 0; //Output DirectSound A to right channel *(vu16 *)REG_SOUNDCNT_H |= DSOUND_A_RIGHT_CHANNEL| DSOUND_A_TIMER_0 | DSOUND_A_LEFT_CHANNEL | DSOUND_A_FIFO_RESET | DSOUND_A_OUTPUT_FULL; //Enable all sound *(vu16 *)REG_SOUNDCNT_X |= SOUND_MASTER_ENABLE; //DMA1 Source Addresss *(vu32 *)REG_DMA1SAD = (u32)sound; //Set sound A's current sound soundA = sound; //Set the length, looping, etc soundALength = length; // 講time秒轉換成V-Blank中斷次數,V-Blank中斷是59.727Hz soundATime = time * 59.727; soundACurrent = 0; soundASampleRate = sampleRate; //DMA1 Destination Address (REG_SGFIFOA) *(vu32 *)REG_DMA1DAD = 0x40000A0; //Write 32 bits into 0x040000A0 (REG_SGFIF0A) every VSync *(vu32 *)REG_DMA1CNT = DMA_DEST_FIXED | DMA_REPEATE | DMA_32 | DMA_TIMEING_SYNC_TO_DISPLAY | DMA_ENABLE; //Sample Rate *(vu16 *)REG_TM0CNT_L = 65536 - (16777216/sampleRate); //Enable the timer *(vu16 *)REG_TM0CNT_H = TIMER_ENABLE | TIMER_IRQ; //The sound is playing soundAPlaying = 1; } 在這裡我們增加了一個函式UpdateDirectSoundA.它的作用是判斷Direct Sound A是否播放完畢的函式.它的程式碼很簡單.每呼叫它一次,當前計數器soundACurrent自加1,同時判斷是否播放完畢. /////////////////////////////////////UpdateDirectSoundA//////////////////////////////// void UpdateDirectSoundA(void) { // if now is not playing sound A,return if(!soundAPlaying) return; // Increase the Current timer soundACurrent +=1; // If sound A play time is up,close the sound A if(soundACurrent >= soundATime) CloseDirectSoundA(); } 需要注意的是,這個函式的呼叫必須是放在VBlank中斷的響應函式里.比如像下面一樣,把它的呼叫放在Interrupt中處理VBlank的部分裡.這樣我們才能保證它是每1/59.727秒被呼叫一次. 關於中斷的部分,請看看前面的<>有關中斷的部分. void Interrupts(void) { u16 Int_Flag; REG_IME = 0x00; // Disable interrupts Int_Flag = REG_IF; // Read the interrupt flags if((REG_IF & 1) == 1) { UpdateDirectSoundA(); } REG_IF = Int_Flag; // Write back the interrupt flags REG_IME = 1; // Re-Enable interrups } 好了.這個如何控制聲音播放結束的問題總算解決了.其實我們也可以使用Timer用同樣的原理來控制聲音播放的結束,不過至少我覺得Timer的中斷產生頻率太快,完全沒有必要.下面我將把我播放聲音的全部程式碼公佈出來,你可以將它複製到libsound.c和libsound.h,然後就可以使用了. 隨便在結束本節之前再說幾句,我們關於GBA的基礎部分也就結束.但是我們對於GBA開發的探索還沒有結束.後面的GBA探索日記將介紹一些實際遊戲開發過程中需要的技術,比如漢字顯示,Tile模式下寫點等問題.不過這些問題就和我們一直走的官方開發包沒有關係(無論是日本人還是老美根本不會去考慮漢字顯示的問題),但是這些技術在實際開發過程中是很有用的.特別是漢字顯示和Tile下寫點這兩個問題,是很多朋友都在做的,現在應該說是比較成熟的,所以在後面的兩篇日記裡,我也將它們公佈出來,希望對大家有所幫助. //************************************* // 檔名: libsound.h // GBA中Direct Sound處理函式或宏 // tangl_99 建立於2003.2.7 //************************************* #ifndef _SOUND_H #define _SOUND_H //////////////////////////Necessary Includes///////////////////////////// //////////////////////////////Definitions///////////////////////////////// #define BIT00 1 #define BIT01 2 #define BIT02 4 #define BIT03 8 #define BIT04 16 #define BIT05 32 #define BIT06 64 #define BIT07 128 #define BIT08 256 #define BIT09 512 #define BIT10 1024 #define BIT11 2048 #define BIT12 4096 #define BIT13 8192 #define BIT14 16384 #define BIT15 32768 #define TIMER_CASCADE BIT02 #define TIMER_IRQ BIT06 #define TIMER_ENABLE BIT07 #define DMA_ENABLE 0x80000000 #define DMA_INTERUPT_ENABLE 0x40000000 #define DMA_TIMEING_IMMEDIATE 0x00000000 #define DMA_TIMEING_VBLANK 0x10000000 #define DMA_TIMEING_HBLANK 0x20000000 #define DMA_TIMEING_SYNC_TO_DISPLAY 0x30000000 #define DMA_16 0x00000000 #define DMA_32 0x04000000 #define DMA_REPEATE 0x02000000 #define DMA_SOURCE_INCREMENT 0x00000000 #define DMA_SOURCE_DECREMENT 0x00800000 #define DMA_SOURCE_FIXED 0x01000000 #define DMA_DEST_INCREMENT 0x00000000 #define DMA_DEST_DECREMENT 0x00200000 #define DMA_DEST_FIXED 0x00400000 #define DMA_DEST_RELOAD 0x00600000
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/8225414/viewspace-951688/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 掌上游戲機開發指南GBA探索日記(10)(轉)
- GBA開發C語言內功補習(轉)C語言
- 開發日誌9
- Switch等掌上游戲機記憶體不夠用咋辦?宏旺半導體給你支招兒記憶體
- Three.js開發指南(9):建立動畫和移動相機JS動畫
- App Annie&IDC:預測掌上游戲機將流失更多的市場份額APP
- Forte for Java開發指南 (轉)Java
- 開發日記10
- ESMTP身份驗證機制探索手記 (轉)
- 安卓APP開發日記1——名為Another的日記APP開發安卓APP
- 小辣椒開發日記
- 安卓開發日記57安卓
- 安卓開發日記56安卓
- 安卓開發日記55安卓
- 安卓開發日記26安卓
- 安卓開發日記25安卓
- 安卓開發日記24安卓
- 安卓開發日記28安卓
- 安卓開發日記27安卓
- 安卓開發日記4安卓
- 安卓開發日記13安卓
- 安卓開發日記14安卓
- 安卓開發日記12安卓
- 安卓開發日記17安卓
- 安卓開發日記16安卓
- 安卓開發日記15安卓
- 安卓開發日記19安卓
- 安卓開發日記18安卓
- 安卓開發日記46安卓
- 安卓開發日記45安卓
- 安卓開發日記47安卓
- 探索外包專案開發的管理(轉)
- 權威支援: WebSphere Application Server 日誌記錄開發人員指南WebAPPServer
- 掌趣電競系統開發技術分析,掌趣電競原始碼設計原始碼
- 5.13安卓開發日記34安卓
- 5.9安卓開發日記31安卓
- WPF學習日記9
- 【HTML CSS】筆記9日HTMLCSS筆記