#23379 - JL - Sun Jul 11, 2004 10:57 am
Hi guys,
I have a soundplayer routine that gets called every VSYNC, each call writes new sample data into the buffer that is going to be played. On each call, I stop the DMA and timer, swap the buffers and enable DMA and timer.
Now, when I play a 'zero-filled' sample (=no sound). I still hear a (although very distant) click. I suspect the switching on/off of the DMA and timer are causing this. Is there a way to avoid this?
Do I have to use an interrupt to get 'spot-on' vsync timing (I'm now waiting for it in a loop)? Or is there something else I'm missing?
Cheers,
Niels
#23381 - poslundc - Sun Jul 11, 2004 1:42 pm
You shouldn't ever touch the timer that the sound chip is using as its pulse. That is what is probably causing your click.
Dan.
#23394 - JL - Sun Jul 11, 2004 9:26 pm
Hi Dan,
Thanks for your reply, however, when I comment out stopping the timer the noise still is there. But it doesn't change anything so you're right about not touching the timers. I guess, stopping both the DMA and timer comes from a 'play one single wav' example that I used as starting point.
The clicking (thumping more, really) is very distant but I checked some games and they don't have the clicking so it must be something in my code....
Below, I added the code that is called every vblank. I'm switching the buffers first before copying data into them. Maybe that's the problem (but then, if they're zero-filled that should matter).
Does anyone see any odd things in my code?
Once 'we' get this fixed I intend to put up the sources for download as I can't really find a simple example doing continuous sound on the GBA and most people seem to have trouble with this.
Grtz,
Niels
Code: |
void ModPlayer::replay()
{
// stop the sound:
// REG_TM0CNT = 0;
//flip to disable
REG_DMA1CNT ^= ENABLE_DMA;
// Set the buffer that was filled out previously and restart the DMA
REG_DMA1SAD = (u32)(buf1Playing ? buf2 : buf1);
//flip to enable
REG_DMA1CNT ^= ENABLE_DMA;
// REG_TM0CNT = TIMER_ENABLED;
SampleCounter = 0;
// swap
buf1Playing = buf1Playing ? false : true;
if (buf1Playing)
{
for (int i=0;i<BUFSIZE;i++)
{
buf2[i] = zeroSound ? 0 : waver[SamplePosition];
SamplePosition++;
if (SamplePosition >= SampleLength)
{
SamplePosition = 0;
}
}
}
else
{
for (int i=0;i<BUFSIZE;i++)
{
buf1[i] = zeroSound ? 0 : waver[SamplePosition];
SamplePosition++;
if (SamplePosition >= SampleLength)
{
SamplePosition = 0;
}
}
}
if(!(KEYS & KEY_START) &&
(prevKeyPress & KEY_START)
)
{
zeroSound = zeroSound ? false: true;
SamplePosition = 0;
}
if(!(KEYS & KEY_A) &&
(prevKeyPress & KEY_A)
)
{
waver = (signed char*)waver1;
SampleLength = 6612;
SamplePosition = 0;
}
if(!(KEYS & KEY_B) &&
(prevKeyPress & KEY_B)
)
{
waver = (signed char*)waver2;
SampleLength = 242420;
SamplePosition = 0;
}
prevKeyPress = KEYS;
SampleCounter++;
}
|
#23397 - gb_feedback - Sun Jul 11, 2004 10:31 pm
Might be worth asking what BUFSIZE is...
_________________
http://www.bookreader.co.uk/
#23398 - poslundc - Sun Jul 11, 2004 10:40 pm
Nothing seems glaringly incorrect about it. Are you certain you are
- Calling it at the right time (ie. at the beginning of VBlank, preferably with an interrupt)
- Have the timer set to the correct frequency to match your buffer size
These are two good sources of error to produce the kind of effect you are describing.
Dan.
#23403 - DekuTree64 - Mon Jul 12, 2004 2:29 am
It's probably a slight timing error. There are only a few frequencies that time out perfectly to restart the DMA on VBlank. Tepples made a tool for finding them, which can be found at http://www.pineight.com/gba/samplerates/. The most commonly used is 18157Hz with a buffer size of 304 bytes.
I would suggest using that for now, but it is possible to play at any frequency without clicks using a timer interrupt. It is best to keep the mixing synchronized to VBlank though, because it takes a lot of time and could cause problems if it was happening at different places every frame. Then, instead of using a 'double buffer', you get more of a 'circular buffer', and just mix a frame's worth of samples into it on VBlank, and let the timer wrap it back around to the start whenever it hits the end. You do end up running into the end of the buffer half way through mixing sometimes, but you can just mix in two batches if needed.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#23413 - JL - Mon Jul 12, 2004 10:21 am
Thanks for the hints, I'll try them asap.
As for the buffersize, it shouldn't really matter should it? Assuming that the buffers are large enough to accomodate for more than what reasonably can be played in each timeslot and they're totally filled out with zero's then no click should be hearable.
I'm waiting for the vblank to occur, I might change that into an interrupt routine just to see if that makes any difference.
I'll keep you guys posted!
Niels
Just for completeness sake, here's my 'init' function.
Code: |
static int BUFSIZE=132;
void ModPlayer::replayWav(unsigned char* wav1, unsigned char* wav2)
{
// Enable and reset Direct Sound channel A, at full volume, using
// timer 0 to determine frequency
REG_SOUNDCNT_H = SND_OUTPUT_RATIO_100 |
DSA_OUTPUT_RATIO_100 |
DSA_OUTPUT_TO_BOTH |
DSA_TIMER0 |
DSA_FIFO_RESET;
waver1 = wav1;
waver2 = wav2;
//enable all sound
REG_SOUNDCNT_X = SND_ENABLED;
// Start the DMA transfer.
waver = (signed char*) waver1;
REG_DMA1DAD = (u32)REG_FIFO_A;
//write 32 bits into destination every vblank
REG_DMA1CNT = ENABLE_DMA | START_ON_FIFO_EMPTY | WORD_DMA | DMA_REPEAT | DEST_REG_SAME;
// Set the timer to overflow at the appropriate frequency and start it
// From forum.gbadev.org:
//
// timer CPU freq mix freq buf size
// 63408 = 65536 - (16777216 / 7884), 132
REG_TM0D = 63408;
// Set the length of the sample and start from zero.
SampleLength = 6612;
SamplePosition = 0;
samples = 16777216 / 7884; // (=2128)
// Fill out buf2
for (int i=0;i<BUFSIZE;i++)
{
buf2[i] = waver[SamplePosition];
SamplePosition++;
if (SamplePosition >= SampleLength)
{
SamplePosition = 0;
}
}
// Fill out buf1
for (int i=0;i<BUFSIZE;i++)
{
buf1[i] = waver[SamplePosition];
SamplePosition++;
if (SamplePosition >= SampleLength)
{
SamplePosition = 0;
}
}
SampleCounter = 0;
// We start playing from buf2
buf1Playing = false;
REG_DMA1SAD = (u32)buf2;
//enable the timer
REG_TM0CNT = TIMER_ENABLED;
}
|
#23415 - gb_feedback - Mon Jul 12, 2004 12:02 pm
Quote: |
As for the buffersize, it shouldn't really matter should it? |
I think the point is that the dma transfers 16 bytes at a time, so your buffer length needs to be a multiple of 16, else you can't have an integral number of dma transfers per vblank.
_________________
http://www.bookreader.co.uk/
#23416 - JL - Mon Jul 12, 2004 12:37 pm
Hi gb_feedback,
How come the DMA transfers 16 bytes at a time?
I specify WORD_DMA in the DMA setup, so I'd expect 32 bits (or 4 bytes if you will) to be transfered each DMA.
Did I completely overlook something?
Thnx,
Niels
#23417 - poslundc - Mon Jul 12, 2004 12:38 pm
gb_feedback wrote: |
Quote: | As for the buffersize, it shouldn't really matter should it? |
I think the point is that the dma transfers 16 bytes at a time, so your buffer length needs to be a multiple of 16, else you can't have an integral number of dma transfers per vblank. |
O_o
#1 DMA transfers either 16 or 32 bits at a time, not 16 bytes.
#2 the buffer needs to be a multiple of 4, not 16... since 4 bytes is how many samples the DMA transfers into the FIFO when it is activated
#3 The greater impacting factor on buffer size, as DT64 pointed out, is the frequency at which both your mixer and the sound chip run at.
Dan.
#23418 - gb_feedback - Mon Jul 12, 2004 1:52 pm
If the DMA is in sound FIFO transfer mode 4 words of data are transferred. The DMA WORD COUNT is ignored.
_________________
http://www.bookreader.co.uk/
#23419 - poslundc - Mon Jul 12, 2004 2:24 pm
Okay...
Let's try it this way. The sound FIFO is 4 bytes (1 word) long. Where are the remaining twelve bytes (3 words) transferred to, exactly?
Dan.
#23421 - gb_feedback - Mon Jul 12, 2004 2:40 pm
Well. The sound FIFO *input register* is 32 bits wide. The FIFO is 8 words long. When it empties so that there is room for 4 words, a DMA of 4 words is initiated.
_________________
http://www.bookreader.co.uk/
#23424 - poslundc - Mon Jul 12, 2004 3:28 pm
Ah, interesting! This is not mentioned on most of the GBA audio pages. So then would multiple str statments to the FIFO register be able to fill up to 16 bytes of data in an interrupt-driven mixer? If so, that could vastly change my strategy for mixer/player writing.
Dan.
#23425 - gb_feedback - Mon Jul 12, 2004 3:38 pm
Quote: |
So then would multiple str statments to the FIFO register be able to fill up to 16 bytes of data in an interrupt-driven mixer? |
Logically yes, although I've never tried it. It certainly should cut down on interrupt overhead. I have to admit that I never got on with interrupt driven mixers. Mine kept locking up, and then I figured out just how much time gets lost in all those interrupts. If my mixer had worked first time, I'd still be doing that way I guess. But you must be using assembler, which I've always been too lazy to learn (on this processor). That's got to be a big help!
_________________
http://www.bookreader.co.uk/