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 > Double-buffered audio playback

#58962 - SittingDuck - Thu Oct 27, 2005 5:07 pm

What's wrong with this?

Code:
#include "gba.h"
#include "dispcnt.h"
#include "interrupt.h"
#include "core_asm.h"

void DoVBlank(void);

extern s8 testTone[];
s8* pos;

///// AUDIO BUFFERS /////

// adjust this to make the sound sound just right!
#define ABUFSIZE 736
s8 abuffers[2][ABUFSIZE];
u32 abuf = 0;

int main(void){
  pos = &testTone[0];

  memcpy32(&abuffers, pos, (ABUFSIZE*2));
  pos += (ABUFSIZE*2);

  // set interrupt routine
  IntrTable[0] = DoVBlank;

  // enable video mode 4
  REG_DISPCNT = DCNT_MODE_4 | DCNT_BG2_ENABLE;
  memset16(MEM_VRAM, 0xFFFF, 240*160);
  PALETTE256[255] = 0x1F;

  // initialize interrupt service routine
  int_init;
  // enable all interrupts
  REG_IME = 1;
  // request VBlank interrupt
  REG_IE |= INT_VBLANK;
  REG_DISPSTAT |= DSTAT_INT_V;

  // set up sound output
  REG_SOUNDCNT_L = 0;
  REG_SOUNDCNT_H = SNDCH_VOLUME_3 | SNDCH_A2LEFT | SNDCH_A2RIGHT;
  REG_SOUNDCNT_X = SNDCX_ON;

  // set up DMA
  REG_DMA1SAD = (u32) &abuffers[0];
  REG_DMA1DAD = (u32) &REG_SGFIFOA;
  REG_DMA1CNT_L = 0;
  REG_DMA1CNT_H = DMA_DST_FIXED | DMA_SRC_INC | DMA_REPEAT | DMA_WORD | DMA_START_FIFO | DMA_ENABLE;

  // set up timer for 44.1kHz
  REG_TM0CNT_L = 0xFE82;  // is this right?
  REG_TM0CNT_H = TM_CYCLES_1 | TM_ENABLE;

  while(1);

  return 0;
}

void DoVBlank(void){
  void* buffer;
  u16 old;

  // we have to turn the DMA off momentarily for the source address
  // change to take effect. Store the original settings first and
  // put them back afterwards.

  if(abuf == 1){
    old = REG_DMA1CNT_H;                 // store old DMA settings
    REG_DMA1CNT_H = 0;                   // stop DMA
    REG_DMA1SAD = (u32) &abuffers[0];    // set DMA source
    NOP;
    REG_DMA1CNT_H = old;                 // start DMA
    abuf = 0;                 
    buffer = &abuffers[1];
  } else {
    abuf = 1;
    buffer = &abuffers[0];
  }

  memcpy32(buffer, pos, ABUFSIZE);  // refill buffer
  pos += ABUFSIZE;
  if(((u32)pos-(u32)&testTone) > 44100) pos = (s8*)&testTone;
  PALETTE256[255]++;                // debug purposes - shows it's running
}


It works OK apart from clicks and crackles. I took the frequency and the buffer size from the list of perfect buffer sizes at http://deku.gbadev.org/program/sound1.html so I don't see how it's still clicking. The source data, testTone, is 44160 samples long so that it is divisible by the buffer size. I have put a sample of the sound output here.

Compiler: DevKitArm
Emulator: VisualBoyAdvance

#58979 - SittingDuck - Thu Oct 27, 2005 8:33 pm

Oh, and here's the binary: here.

#58982 - tepples - Thu Oct 27, 2005 8:44 pm

Quote:
Code:
  REG_TM0CNT_L = 0xFE82;  // is this right?

This line sets the playback delay to 382. This number is not a factor of 280896, the number of cycles per vblank. If you're switching buffers in a vblank ISR, then you need to use a compatible rate, such as one computed by this program. The closest vblank-friendly sample rate to 44100 Hz is 42048.16 Hz.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#58984 - SittingDuck - Thu Oct 27, 2005 8:59 pm

It's still doing the same thing, but at a lower pitch. :-(. But I think I did it wrong again. I'll keep on trying...

#58985 - DekuTree64 - Thu Oct 27, 2005 9:15 pm

Huh, yeah, it looks like that last sample rate in my list doesn't work after all. Sorry about that, I'll remove it from the list.

The one above it, 42048Hz with a buffer size of 704 samples, should work. The timer value would be 0xFE71, which is 65536 - 16777216 / 42048.16.

Also, you said the sample's length was 44160, which is indeed a multiple of your old buffer size and would work, but this line:
Code:
  if(((u32)pos-(u32)&testTone) > 44100) pos = (s8*)&testTone;

isn't checking against that number. Plus it should be doing >= instead of just >, since after copying the last chunk, the counter would be exactly equal to the length, so it wouldn't reset, and would read off the end of the sample next time.

But then using the new buffer size of 704 samples, 44160 won't line up anymore... Looks like the closest integer multiples of 704 are 43648 and 44352.

Hope that helps :)
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku

#59033 - SittingDuck - Fri Oct 28, 2005 7:51 am

Thanks very much. :-D

#59034 - SittingDuck - Fri Oct 28, 2005 8:11 am

IT WORKS! I could kiss you! OK, I won't go that far.

^_^