博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
掌上游戏机开发指南GBA探索日记(9)(转)
阅读量:2450 次
发布时间:2019-05-10

本文共 5368 字,大约阅读时间需要 17 分钟。

掌上游戏机开发指南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/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/8225414/viewspace-951688/

你可能感兴趣的文章
javascript指南_JavaScript指南
查看>>
web容器 ejb容器_容器实用指南
查看>>
盲人编程_帮助盲人学习编码
查看>>
flexbox算法实现_如何使用Flexbox实现水平滚动
查看>>
github 生产环境_如何在GitHub上提高生产力
查看>>
github 和git_Google编码文档:Git和GitHub
查看>>
summit_Chrome Dev Summit 2018的亮点
查看>>
git 初始化git存储库_什么不保存到Git存储库中
查看>>
node.js编写网页_为Node.js编写可扩展架构
查看>>
mysql 不显示消息错误_如何编写不吸的错误消息
查看>>
动态瑜伽 静态瑜伽 初学者_瑜伽与编程有什么关系?
查看>>
css grid_如何使用CSS Grid创建图片库
查看>>
kms服务器管理_如何使用Google Cloud KMS保护和管理机密
查看>>
firebase使用_使用Firebase进行物联网原型设计:如何事半功倍
查看>>
--eval mongo_这就是为什么您的read-eval-print-loop如此惊人的原因
查看>>
初学react实现路由跳转_如何使用React构建模因制作者:初学者指南
查看>>
串行测试 并行测试_如何通过CircleCI测试并行性增加构建时间
查看>>
soa面向服务体系结构_服务和面向微服务的体系结构简介
查看>>
heroku服务器_如何在Heroku上使用Express服务器部署React应用
查看>>
垂死病中惊坐起_我如何开始恢复垂死的软件团队的过程
查看>>