掌上游戲機開發指南GBA探索日記(9)(轉)

post0發表於2007-08-12
掌上游戲機開發指南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/,如需轉載,請註明出處,否則將追究法律責任。

相關文章