首页>
技术资讯>
详情

掌上游戏机开发指南 GBA探索日记(8)

2016-05-28 来源:CloudBest 阅读量: 169
关键词: 程序设计


  有了前几篇关于中断和Timer,DMA传输的介绍后,现在我们可以进入GBA声音播放的部分了.GBA一共有6个声道,从Sound 1-4加上两个Direct Sound A(and)B.GBA中对声音的播放应该是最困难的部分.至少我是这么认为,因为这里面涉及到太多的技术细节.所以我在这里只能告诉大家怎么在GBA中播放声音,至于内部的细节,我就无可奉告了.
  
  如果你手上有任天堂的官方开发包AgbLib3.0,那么看看AgbLib3.0中有关声音的播放部分.任天堂公司在官方开发包里提供了个mediaPlayer2000来实现GBA声音的播放.那个mediaPlayer2000是个十分复杂的东西,需要配置很复杂的ini配置文件.光是看配置ini文件的帮助文件都够我们看几天了.为此,在官方开发包里面还专门附加了mediaPlyaer2000的帮助部分.不过最大的问题是,它的mediaPlayer2000播放声音的库文件无法在一般的GCC下在编译.或许是我所得到的官方开发包不完整(只有个AgbLib,GCC是另外自己搭配的),总之,那个mediaPlayer2000是不能用了.
  
  前面的<>都是以官方开发包为基础而写的,内容是十分贴近AgbLib.但是这一节由于官方开发包里的mediaPlayer2000无法使用,所以我们就必须另开辟一条道路来走了.还好,这条路我已经走通了,大家也可以顺着这个方向前进.
  
  这一部分关于声音播放的内容你也可以跳过前面的理论讲解.在后面我会直接把所有的声音播放的代码完整得公布在下一篇的探索日记中.如果你觉得麻烦,也可以直接看后面怎么使用这些播放库函数就可以了.
  
  我们首先看看这张有关声道的内部结构图:
  

 

  这里我们看到,Sound 1-4跟Direct Sound A和B有着不同的播放渠道.Direct Sound播放是需要通过DMA实现声音数据的传输.而传输速度就必须由Timer来控制.这也就是为什么我要在前面几节讲解DMA和Timer了.
  
  我们这里主要看Direct Sound的声音播放.因为它的功能是最强大的.而Sound 1-4各个播放的声音数据都不同,而且使用起来很复杂,所以这里不讲了,如果你有兴趣,可以参考官方开发包.
  
  Direct Sounds A and B 播放的是8位线性的音频数据
  
  它们的数据入口就是两个寄存器SGFIFOA(B)_L和SGFIFOA(B)_H
  
 

  每个Direct Sound一次可以接受4个字节的数据.
  
  然后我们看看声音播放的控制寄存器
  

  很复杂吧.其实我已经不只一次地给出这样的寄存器图表了.这些图片都是官方开发包里面的.它的说明都是很齐全的.不过我还是要说几句.
  
  Direct Sound的播放一定要和Timer0或者1,DMA联系在一起.其中
  
  Timer Selection for Direct Sound A或者B
  
  就是指定该Direct Sound所使用的Timer了.而Timer只能是Timer 1或者Timer 0.
  
  Output of Direct Sound A或者B
  
  是选择每个Direct Sound左右声道的使用.其实这个选择没有多大用处.我试过,无论你选哪个,在GBA上播放的效果都差不多.所以我们一般都是把每个Direct Sound左右声道都开起.
  
  Direct Sound FIFO A(B) Clear and Sequencer Reset
  
  一般是初始化Direct Sound时候使用.FIFO是指先进先出接口.
  
  下面让我们来做个实例来看看具体要做的步骤.
  
  首先,我们要有个转换工具wav2gbac.exe. 它可以把我们电脑上的*.wav文件转换成一个*.h头文件.但是得注意,你的*.wav文件最好是8位音频,因为GBA上的Direct Sound只能播放8位的音频.即使8位以上的wav文件,但是播放出来还是只有8位的效果.
  
  wav2gbac.exe这个工具可以在www.gbadev.org上下载.是个DOS下的程序.
  
  我们在dos提示符下输入:
  
  wav2gbac xxx.wav xxx.h
  
  这样它就把xxx.wav音频数据转换到xxx.h里的一个大数组里了.
  
  好了,我们已经得到转换后的音频数据.下面我们来看看如何写我们的声音播放函数.
  
  void PlayDirectSoundA(u8 *sound, u16 sampleRate, u32 length)
  
  {
  
  //Stop any previous sample
  
  *(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;
  
  //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;
  
  }
  
  这是个播放Direct Sound A声道的播放函数.
  
  首先我们来看函数的参数,u8 *sound,u16 sampleRate,u32 length这些都是我们的声音转换工具wav2gbac.exe自动创建了的.你只要简单地把声音文件xxx.h的参数传递过来就可以了.
  
  在播放声音之前,我们得先重至一次所需要的一切资源.这就是下面的代码完成的功能:
  
  //Stop any prev

热门推荐 查看更多