GBA探索日记(9)(声音播放2)
这一节我们讨论如何正常结束声音播放的问题.不要觉得这是个小问题,当初这个小问题难倒了我们所有研究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秒被调用一次.
关于中断的部分,请看看前面的<<GBA探索日记>>有关中断的部分.
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
#define
RIGHT_VOLUME(n) n
#define
LEFT_VOLUME(n) n<<4
#define
SOUND_1_RIGHT_CHANNEL BIT08
#define
SOUND_2_RIGHT_CHANNEL BIT09
#define
SOUND_3_RIGHT_CHANNEL BIT10
#define
SOUND_4_RIGHT_CHANNEL BIT11
#define
SOUND_1_LEFT_CHANNEL BIT12
#define
SOUND_2_LEFT_CHANNEL BIT13
#define
SOUND_3_LEFT_CHANNEL BIT14
#define
SOUND_4_LEFT_CHANNEL BIT15
//SGCNT0_H
#define SOUND_OUTPUT_1_4 0
#define
SOUND_OUTPUT_1_2 BIT00
#define
SOUND_OUTPUT_FULL BIT01
#define
DSOUND_A_OUTPUT_HALF 0
#define
DSOUND_A_OUTPUT_FULL BIT02
#define
DSOUND_B_OUTPUT_HALF 0
#define
DSOUND_B_OUTPUT_FULL BIT03
#define
DSOUND_A_RIGHT_CHANNEL BIT08
#define
DSOUND_A_LEFT_CHANNEL BIT09
#define
DSOUND_A_TIMER_0 0
#define
DSOUND_A_TIMER_1 BIT10
#define
DSOUND_A_FIFO_RESET BIT11
#define
DSOUND_B_RIGHT_CHANNEL BIT12
#define
DSOUND_B_LEFT_CHANNEL BIT13
#define
DSOUND_B_TIMER_0 0
#define
DSOUND_B_TIMER_1 BIT14
#define
DSOUND_B_FIFO_RESET BIT15
//SGCNT1
#define
SOUND_1_ENABLE BIT00
#define
SOUND_2_ENABLE BIT01
#define
SOUND_3_ENABLE BIT02
#define
SOUND_4_ENABLE BIT03
#define
SOUND_MASTER_ENABLE BIT07
//SG10_L
#define
S10L_NUM_SWEEP_SHIFTS(n) n
#define
S10L_SWEEP_INCREASE 0
#define
S10L_SWEEP_DECREASE BIT03
#define
S10L_SWEEP_CHANGE_FREQ(n) n<<4
//SG10_H
#define
S10H_SOUND_LENGTH(n) n
#define
S10H_WAVEFORM_CYCLE_12 0
#define
S10H_WAVEFORM_CYCLE_25 BIT06
#define
S10H_WAVEFORM_CYCLE_50 BIT07
#define
S10H_WAVEFROM_CYCLE_75 BIT06
| BIT07
#define
S10H_ENVELOPE_STEPS(n) n<<8
#define
S10H_ENVELOPE_INCREASE 0
#define
S10H_ENVELOPE_DECREASE BIT11
#define
S10H_ENVELOPE_START_VALUE(n) n<<12
//SG20
#define
S20_SOUND_LENGTH(n) n
#define
S20_WAVEFORM_CYCLE_12 0
#define
S20_WAVEFORM_CYCLE_25 BIT06
#define
S20_WAVEFORM_CYCLE_50 BIT07
#define
S20_WAVEFROM_CYCLE_75 BIT06
| BIT07
#define
S20_ENVELOPE_STEPS(n) n<<8
#define
S20_ENVELOPE_INCREASE 0
#define
S20_ENVELOPE_DECREASE BIT11
#define
S20_ENVELOPE_START_VALUE(n) n<<12
//SG21
#define
S21_FREQUENCY(n) n
#define
S21_CONTINOUS 0
#define
S21_COUNTER BIT14
#define
S21_RESTART BIT15
//SG30_L
#define
S30L_32_STEP_WAVEFORM 0
#define
S30L_64_STEP_WAVEFORM BIT05
#define
S30L_RAM_BANK_0 0
#define
S30L_RAM_BANK_1 BIT06
#define
S30L_ENABLE BIT07
//SG30_H
#define
S30H_SOUND_LENGTH(n) n
#define
S30H_OUTPUT_MUTE 0
#define
S30H_OUTPUT_FULL BIT13
#define S30H_OUTPUT_1_2 BIT14
#define
S30H_OUTPUT_1_4 BIT13 | BIT14
#define
S30H_OUTPUT_3_4 BIT15
//SG31
#define
S31_FREQUENCY(n) n
#define
S31_CONTINOUS 0
#define
S31_COUNTER BIT14
#define
S31_RESTART BIT15
//SG40
#define
S40_SOUND_LENGTH(n) n
#define
S40_ENVELOPE_STEPS(n) n<<8
#define
S40_ENVELOPE_INCREASE 0
#define
S40_ENVELOPE_DECREASE BIT11
#define
S40_ENVELOPE_START_VALUE(n) n<<12
//SG41
#define
S41_DIVIDE_FREQ_2 0
#define
S41_DIVIDE_FREQ_1 BIT00
#define
S41_DIVIDE_FREQ_1_2 BIT01
#define
S41_DIVIDE_FREQ_1_3 BIT00
| BIT01
#define
S41_DIVIDE_FREQ_1_4 BIT02
#define
S41_DIVIDE_FREQ_1_5 BIT00
| BIT02
#define
S41_DIVIDE_FREQ_1_6 BIT01
| BIT02
#define
S41_DIVIDE_FREQ_1_7 BIT00
| BIT01 | BIT02
#define S41_POLY_STEPS_15 0
#define
S41_POLY_STEPS_7 BIT03
#define
S41_POLY_SHIFT_FREQ(n) n<<4
#define
S41_CONTINOUS 0
#define
S41_COUNTER BIT14
#define
S41_RESTART BIT15
#define
REG_SGCNT0_L 0x4000080
#define
REG_SGCNT0_H 0x4000082
#define
REG_SGCNT1
0x4000084
extern u8
*soundA;
//Sound A sample
extern u8
soundALoop; //Sound
A loop parameter
extern u8
soundAPlaying;
//Sound A status
extern u32
soundACurrent; //Sound
A current location
extern u32 soundALength;
//Sound A sample length
extern u32
soundATime; //Sound
A sample time
extern u16
soundASampleRate;
//Sound A sample rate
extern u8
*soundB;
//Sound B sample
extern u8
soundBLoop; //Sound
B loop parameter
extern u8
soundBPlaying;
//Sound B status
extern u32
soundBLength;
//Sound B sample length
extern u32
soundBCurrent; //Sound
B current location
extern u32
soundBTime; //Sound
B sample time
extern u16
soundBSampleRate;
//Sound B sample rate
////////////////////////////////Function
Prototypes//////////////////////////////
void
PlaySound(u8 *sound, u16 sampleRate, u32 length,u32 time);
void
UpdateDirectSound(void);
void
PlayDirectSoundA(u8 *sound, u16 sampleRate, u32 length, u32 time);
void
PlayDirectSoundB(u8 *sound, u16 sampleRate, u32 length, u32 time);
void
UpdateDirectSoundA(void);
void
UpdateDirectSoundB(void);
void
CloseDirectSoundA(void);
void
CloseDirectSoundB(void);
void
StopDirectSoundA(void);
void
StopDirectSoundB(void);
void
ResumeDirectSoundA(void);
void
ResumeDirectSoundB(void);
#endif
//*************************************
// 文件名:libsound.c
// GBA中Direct Sound处理函数或宏
// tangl_99 创建于2003.2.7
//*************************************
#include
<agb.h>
#include
"libsound.h"
/////////////////////////////////////Globals/////////////////////////////////////
u8
*soundA;
//Sound A sample
u8 soundALoop; //Sound
A loop parameter
u8
soundAPlaying;
//Sound A status
u32
soundACurrent; //Sound
A current location
u32
soundALength;
//Sound A sample length
u32
soundATime; //Sound A sample time
u16
soundASampleRate;
//Sound A sample rate
u8
*soundB;
//Sound B sample
u8 soundBLoop; //Sound
B loop parameter
u8
soundBPlaying;
//Sound B status
u32
soundBLength;
//Sound B sample length
u32
soundBCurrent; //Sound
B current location
u32
soundBTime; //Sound B sample time
u16
soundBSampleRate;
//Sound B sample rate
////////////////////////////////////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;
}
////////////////////////////////////PlayDirectSoundB/////////////////////////////////////
void
PlayDirectSoundB(u8 *sound, u16 sampleRate, u32 length,u32 time)
{
//Stop
any previous sample
soundBPlaying
= 0;
*(vu16
*)REG_TM1CNT_H = 0;
*(vu16
*)REG_TM1CNT_L = 0;
*(vu32
*)REG_DMA2SAD = 0;
*(vu16
*)REG_DMA2CNT_H = 0;
*(vu32
*)REG_DMA2DAD = 0;
//Output
DirectSound B to right channel
*(vu16
*)REG_SOUNDCNT_H |= DSOUND_B_RIGHT_CHANNEL | DSOUND_B_TIMER_1|
DSOUND_B_LEFT_CHANNEL | DSOUND_B_FIFO_RESET | DSOUND_B_OUTPUT_FULL;
//Enable
all sound
*(vu16
*)REG_SOUNDCNT_X |= SOUND_MASTER_ENABLE;
//DMA1
Source Addresss
*(vu32
*)REG_DMA2SAD = (u32)sound;
//Set
sound B's current sound
soundB
= sound;
//Set
the length and looping
soundBLength
= length;
// 讲time秒转换成V-Blank中断次数,V-Blank中断是59.727Hz
soundBTime
= time * 59.727;
soundBCurrent
= 0;
soundBSampleRate
= sampleRate;
soundBPlaying
= 1;
//DMA1
Destination Address (REG_SGFIFOB)
*(vu32
*)REG_DMA2DAD = 0x40000A4;
//Write
32 bits into 0x040000A4 (REG_SGFIF0B) every VSync
*(vu32 *)REG_DMA2CNT = DMA_DEST_FIXED | DMA_REPEATE | DMA_32 |
DMA_TIMEING_SYNC_TO_DISPLAY | DMA_ENABLE;
//Sample
Rate
*(vu16
*)REG_TM1CNT_L = 65536 - (16777216/sampleRate);
//Enable
the timer
*(vu16
*)REG_TM1CNT_H = TIMER_ENABLE | TIMER_IRQ;
}
/////////////////////////////////////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();
}
/////////////////////////////////////UpdateDirectSoundB////////////////////////////////
void
UpdateDirectSoundB(void)
{
// if
now is not playing sound B,return
if(!soundBPlaying)
return;
soundBCurrent
+=1;
// If
sound B play time is up,close the sound B
if(soundBCurrent
>= soundBTime)
CloseDirectSoundB();
}
void
PlaySound(u8 *sound, u16 sampleRate, u32 length, u32 time)
{
PlayDirectSoundA(sound,sampleRate,length,time);
PlayDirectSoundB(sound,sampleRate,length,time);
}
void
UpdateDirectSound()
{
UpdateDirectSoundA();
UpdateDirectSoundB();
}
void
CloseDirectSoundA(void)
{
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;
}
void
CloseDirectSoundB(void)
{
soundBPlaying
= 0;
*(vu16
*)REG_TM1CNT_H = 0;
*(vu16
*)REG_TM1CNT_L = 0;
*(vu32
*)REG_DMA2SAD = 0;
*(vu16
*)REG_DMA2CNT_H = 0;
*(vu32
*)REG_DMA2DAD = 0;
}
void
StopDirectSoundB(void)
{
CloseDirectSoundB();
// 用soundBCurrent来记录播放的时间,Resume的时候根据soundBCurrent/soundBtime,来判断sound
data的入口
}
void
StopDirectSoundA(void)
{
CloseDirectSoundA();
// 用soundACurrent来记录播放的时间,Resume的时候根据soundACurrent/soundAtime,来判断sound
data的入口
}
void
ResumeDirectSoundA(void)
{
//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)(soundA+soundACurrent*soundALength/soundATime);
//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/soundASampleRate);
//Enable
the timer
*(vu16
*)REG_TM0CNT_H = TIMER_ENABLE | TIMER_IRQ;
//The
sound is playing
soundAPlaying
= 1;
}
void
ResumeDirectSoundB(void)
{
//Output
DirectSound B to right channel
*(vu16
*)REG_SOUNDCNT_H |= DSOUND_B_RIGHT_CHANNEL | DSOUND_B_TIMER_1|
DSOUND_B_LEFT_CHANNEL | DSOUND_B_FIFO_RESET | DSOUND_B_OUTPUT_FULL;
//Enable
all sound
*(vu16
*)REG_SOUNDCNT_X |= SOUND_MASTER_ENABLE;
//DMA1
Source Addresss 需要计算重新开始的地址
*(vu32
*)REG_DMA2SAD = (u32)(soundB+soundBCurrent*soundBLength/soundBTime);
//DMA1
Destination Address (REG_SGFIFOB)
*(vu32
*)REG_DMA2DAD = 0x40000A4;
//Write
32 bits into 0x040000A4 (REG_SGFIF0B) every VSync
*(vu32 *)REG_DMA2CNT = DMA_DEST_FIXED | DMA_REPEATE | DMA_32 |
DMA_TIMEING_SYNC_TO_DISPLAY | DMA_ENABLE;
//Sample
Rate
*(vu16
*)REG_TM1CNT_L = 65536 - (16777216/soundBSampleRate);
//Enable
the timer
*(vu16
*)REG_TM1CNT_H = TIMER_ENABLE | TIMER_IRQ;
soundBPlaying
= 1;
}