gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

Audio > How do i play an audio sample which constantly loops

#23593 - gbawiz - Thu Jul 15, 2004 10:24 pm

Hello all,

Can anyone suggest a suitable method for playing an audio sample loop over and over again.
I would like to be able to play an audio sample of size 2Kbytes in a continous loop at 22KHZ, how can i do this.

My past efforts at playing sample loops seems to be hampered by a strange clicking sound.

When a sample is playing and it reaches it's end, what is the best method of detecting and reseting the sample?

Cheers!
Gbawiz

#23595 - dagamer34 - Fri Jul 16, 2004 12:03 am

Use a timer to count the time passed and when it is done, simply "play" the sample again.
_________________
Little kids and Playstation 2's don't mix. :(

#23619 - gbawiz - Fri Jul 16, 2004 9:24 am

dagamer34 wrote:
Use a timer to count the time passed and when it is done, simply "play" the sample again.


Yeah, I see what you mean about using the timer to count.
Ok, how do you "play" the sample "again"

Thanks

#23624 - poslundc - Fri Jul 16, 2004 12:13 pm

How did you play the sample in the first place?

Do that again.

Dan.

#23688 - gbawiz - Sun Jul 18, 2004 10:39 pm

poslundc wrote:
How did you play the sample in the first place?

Do that again.

Dan.



Im trying to get a fresh perspective on this without going down the same road, because the method which I'm using doesn't seem to work properly.

Ok I will give a little clue, I think there is a problem with the DMA switching in my attempts, but would like to hear how others would "play a continuous sample loop"


Last edited by gbawiz on Mon Jul 19, 2004 2:42 pm; edited 1 time in total

#23690 - dagamer34 - Sun Jul 18, 2004 11:23 pm

gbawiz wrote:
poslundc wrote:
How did you play the sample in the first place?

Do that again.

Dan.


Oh what genius!!

Im trying to get a fresh perspective on this without going down the same road, because the method which I'm using doesn't seem to work properly.

Ok I will give a little clue, I think there is a problem with the DMA switching in my attempts, but would like to hear how others would "play a continuous sample loop"


Yea, do what poslundc said. A little tip though. IF you are using a timer with DMA to keep track of your samples, make sure that you set the timer counting register and the DMA control register to zero before you set them back up again, otherwise you will hear clicking every time you play the sample.

I'm wondering, if you didn't know how to "play" a sample again for looping, how did you stop it in the first place?
_________________
Little kids and Playstation 2's don't mix. :(

#23691 - poslundc - Sun Jul 18, 2004 11:40 pm

gbawiz wrote:
Oh what genius!!

Im trying to get a fresh perspective on this without going down the same road, because the method which I'm using doesn't seem to work properly.

Ok I will give a little clue, I think there is a problem with the DMA switching in my attempts, but would like to hear how others would "play a continuous sample loop"


I was perfectly serious. In my mixer, I restart a sample pretty much the same way I start it.

It occurs to me that perhaps you aren't using a correct framework. A normal mixer runs constantly, switching from buffer to buffer, even if no sound is being played. So starting, stopping or looping a sample is pretty much trivial. You just say what sound the channel is pointing to, and reset the position to the beginning of the sound. The mixer should take care of the rest.

Dan.

#23692 - DekuTree64 - Sun Jul 18, 2004 11:44 pm

How long is the sample? The length needs to be a multiple of 16 because the sound DMA actually transfers 16 bytes into the FIFO, not 4, as it seems like it would. The FIFO registers are just the way of sending data into the real buffers. I haven't tested this, but you could probably write to them 4 times consecutively using the CPU and they would play just the same as when the DMA does it.

Also untested is what actually happens when the length isn't a multiple of 16. I would assume that because the DMA plays forever unless you stop it, that on the last time it gets triggered before your timer interrupt to restart it, it would just read past the end of the sample until it got to the next 16 byte boundary and get whatever data happened to be there, which would most likely not fit smoothly with the end of the sample, and cause a click.

To fix the problem, just copy the value of the last sample until you get to a multiple of 16, and hopefully it won't cause enough of a delay to still make a click when it restarts.

EDIT: Dan posted while I was typing. He is right that a mixer is what you really need, my solution is just a quick fix because writing a mixer takes a while.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#23714 - gbawiz - Mon Jul 19, 2004 2:04 pm

Hello guys,

Thanks for the assistance :)

This is something which i have been trying to figure out for quite some time.
I have used a method which works like this:

1. setup dma transfer & counters
2. timer 2 counts the number of samples being played by counting the overflows from timer 1 (which is outputing the sample rate).
3. when timer 2 overloads after the size of my buffer then it will generate an interrupt.

The interrupt:
This will stop the sample by flipping the enable bit of the DMA.
The read and write buffer addresses are swapped and the new sample is mixed into the write buffer.
the DMA transfer is restarted and reads from the read buffer.

--end interrupt ----

4. the whole process continues.

#23715 - JL - Mon Jul 19, 2004 2:13 pm

I posted a small example application (to gbadev.org) that does exactly what is requested (play a looping sound without audible clicks using double buffering and which is timed on VBLANK interrupt). It compiles with devkitadv and is written in C/C++ with a considerable amount of comments.

I haven't seen it up yet, but I guess it will be soon. If anyone is interested in it and can't wait, then drop me a line and I'll send it to you directly.

Cheers,
Niels

#23716 - poslundc - Mon Jul 19, 2004 2:22 pm

gbawiz wrote:
1. setup dma transfer & counters
2. timer 2 counts the number of samples being played by counting the overflows from timer 1 (which is outputing the sample rate).
3. when timer 2 overloads after the size of my buffer then it will generate an interrupt.

The interrupt:
This will stop the sample by flipping the enable bit of the DMA.
The read and write buffer addresses are swapped and the new sample is mixed into the write buffer.
the DMA transfer is restarted and reads from the read buffer.

--end interrupt ----

4. the whole process continues.


You need to remove the sound player from the act of playing a sample. The sound player should be running constantly in the background, examining a global memory location to see if there is a sound to play. If so, then it feeds the sound into your buffer, if not, it feeds zeros into the buffer. Once you have this system set up, you can start, stop, or loop a sound just by changing what is in that memory location. Your program will be much easier to debug once you have a proper sound engine in place.

Dan.

#23717 - gbawiz - Mon Jul 19, 2004 2:41 pm

Thanks Dan :)

I was wondering, do you think that the 'calling of the ISR' can cause a delay which could then appear as a spike or click?

#23720 - poslundc - Mon Jul 19, 2004 4:42 pm

If your player is running at 16 KHz, that would give your ISR exactly 1024 cycles in which to switch the DMA over to the next buffer before a sample must be played. Overhead to enter the ISR shouldn't be more than 50 cycles or so, and on the off chance your ISR is preempted by, say, an HBlank interrupt then that's another 250 cycles (if you are using multiple interrupts). Even in the worst-case scenario you should still have plenty of time to swap the DMA over to your other buffer.

Dan.

#23722 - SimonB - Mon Jul 19, 2004 5:12 pm

JL's example code can be found on the main site now...

http://gbadev.org/index.php?ID=389

Simon

#23750 - dagamer34 - Tue Jul 20, 2004 10:03 pm

That's a really good tutorial. I wish I knew how to make an audio mixer though.
_________________
Little kids and Playstation 2's don't mix. :(

#23770 - gbawiz - Wed Jul 21, 2004 8:26 am

SimonB wrote:
JL's example code can be found on the main site now...

http://gbadev.org/index.php?ID=389

Simon


I have looked at the source code for the example and have figured out how it works.

The big nuisance is being able to choose the correct buffer size and sample frequency and the table provided is quite invaluable.

Thanks for providing this JL :)

Another question which i have is with regards to choosing a proper sample buffer size & frequency.

I have a simple 50hz sine wave which has been sampled at exactly 22050hz.

I have set the REG_TM0D equal to (0xffff - 761) which should provide a frequency playback of 22050hz.

My buffer size is 22050 / 59.72727272 (approx 368 bytes)

However there is still some clicking noizes.

Is this frequency of 22050 supported?

Many thanks
Gbawiz

#23791 - tepples - Wed Jul 21, 2004 5:20 pm

Sample buffers have to be an exact multiple of 16 bytes in size. The sample playback rate 16777216 Hz / 761 = 22046.3 Hz is supported, but if you use it, you'll have to switch buffers on a timer interrupt rather than on vblank. To find those few rates compatible with switching buffers on vblank, use this tool.

In practice, you'll find that many commercial games use 18157.16 Hz or 21024.08 Hz.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#23964 - jdxpolygon - Sun Jul 25, 2004 3:59 pm

Hi

I haven't really had a chance to look at the source code example yet, I will do shortly, but before that I thought I'd add some of what I've discovered from trying to produce a click-free sound mixer.

Originally I was working with a sample rate of 16384, because I'd been lead to believe that I had to use a power of 2 sample rate to tie in best with the clock frequency and produce a clean sound. But obviously 16384Hz gave me a lot of clicking. Then I posted here a while ago stating my problems, and I was given the table of suitable sample rates, which at first seemed to work in fixing my mixer (done in assembly by the way), but I think at the time, the two sound buffers I had in memory were not being altered, I'd put in a very short test waveform (a triangular wave) into both buffers (same in each), and once I started using a recommended frequency value, it played very clearly, but once I switched the mixing function back on, it started to sound a bit crackly again - and from recording the output from VBA and analysing it a bit, it seemed like rather than random noise in my mixer output every 60th of a second, it was a tiny little snippet from somewhere else in (what would have been) the correct output, and I presumed that possibly for some reason the start of each 60th of a second of mixed sound seemed to be the equivalent samples from the other sound buffer - and obviously, beforehand, when the buffers were identical, this didn't create a problem. I'm still not entirely sure what happens (and it's a while since I worked on it, I tend to go and work on another element of my project when I get stuck), but I hope looking at the source provided on this site will help.

J

#23971 - JL - Sun Jul 25, 2004 7:51 pm

Hi jdxpolygon,

My sample doesn't really 'mix', it just plays one sample using 2 buffers over and over again. Mixing is necessary when you play more than one sample through the same DAC. Your problem sounds like:

-Either you're not copying enough (or all?) data in time into your buffers, causing the click every once in a while. If so, look at my sample, it should clear things up. Try putting your triangular wave in my sample and see if it reproduces your click (I hope it doesn't :-) ).

-If you're really mixing, then maybe your clipping doesn't work as expected? It's easy to mess things up once you're casting data from a short to a char or something like that.

The way I programmed my mod player was to first start with getting the sample to play continuously without clicking and then using that to play 'tracked' samples. Then I added more channels by building a mixer on top of the looping sound example.

I hope that puts you in the right direction.
Cheers,
Niels

#24150 - Kayamon - Wed Jul 28, 2004 4:00 pm

Just an extra note -

Make sure your sample buffers are 4-byte aligned. I was getting clicks for quite a while until I figured this one out.