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.

C/C++ > Clicks and pops when playing sound

#1236 - Vortex - Wed Jan 15, 2003 9:48 pm

I am working on a small sound program, based on the www.belogic.com sample code. Sometimes when a sound sample is played there is a clicking sound. From my Windows sound card programming experience I can conclude that at least one byte of sample data is being lost during the DMA transfer. The symptom is not consistent and it may be a problem with the emulator (VBA) or there is a high-priority interrupt which in some way causes data lost.

Your input will be appreciated.

Thanks


Code:

#include "gba.h"
#include "screenmode.h"
#include "keypad.h"

#include "sound21.c"
#include "sound25.c"

#define FIFO_A (0x040000A0)
#define FIFO_B (0x040000A4)

void InterruptProcess(void) __attribute__ ((section(".iwram"))); //the interrupt handler from crt0.s

void InterruptProcess(void)
{
   //sample finished!,stop Direct sound
   REG_TM0CNT_H  = 0; //disable timer 0
   REG_TM1CNT_H  = 0; //disable timer 1

   REG_DMA1CNT_H = 0; //stop DMA

   //clear the interrupt(s)
   REG_IF |= REG_IF;
}

void PlaySample( const u32* sound_data, u16 num_samples, u32 playbackFreq )
{
   const u32 cpuFreq = 16777216lu;

   //play a mono sound at 16khz
   //uses timer 0 as sampling rate source
   //uses timer 1 to count the samples played in order to stop the sound

   // Direct Sound Channel A
   REG_DMA1SAD   = (unsigned long) sound_data; //dma1 source
   REG_DMA1DAD   = FIFO_A; //write to FIFO A address
   REG_DMA1CNT_H = 0xb600;   //dma control: DMA enabled+ start on FIFO+32bit+repeat+increment source&dest
   REG_DMA1CNT_L = 0;

   REG_TM1CNT_L = 0xFFFF - num_samples; //0xffff-the number of samples to play
   REG_TM1CNT_H = 0xC4;   //enable timer1 + irq and cascade from timer 0

   REG_IE = 0x10; //enable irq for timer 1
   REG_IME = 1;   //master enable interrupts

   //Formula for playback frequency is: 0xFFFF-round(cpuFreq/playbackFreq)

   REG_TM0CNT_L = 0xFFFF - (cpuFreq/playbackFreq);
   REG_TM0CNT_H = 0x0080; //enable timer0
}

struct SountCntH
{
   int Ch1to4OutputRatio          : 2;
   int DSndAOutRatio              : 1;
   int DSndBOutRatio              : 1;
   int unused                     : 4;

   int EnableDSndAToRightSpeaker  : 1;
   int EnableDSndAToLeftSpeaker   : 1;
   int DSndASampleRateTimer       : 1;
   int DSndAFIFOReset             : 1;

   int EnableDSndBToRightSpeaker  : 1;
   int EnableDSndBToLeftSpeaker   : 1;
   int DSndBSampleRateTimer       : 1;
   int DSndBFIFOReset             : 1;

};

void AgbMain (void)
{
   u32 playbackFreq = 16384;

   // u32 SoundCntXMask = 0x080C0;

   struct SountCntH snd_ctrl;

   snd_ctrl.Ch1to4OutputRatio          = 0;
   snd_ctrl.DSndAOutRatio              = 1;   // 100%
   snd_ctrl.DSndBOutRatio              = 0;   
   snd_ctrl.unused                     = 0;

   snd_ctrl.EnableDSndAToRightSpeaker  = 1;
   snd_ctrl.EnableDSndAToLeftSpeaker   = 1;
   snd_ctrl.DSndASampleRateTimer       = 0;   //Timer 0
   snd_ctrl.DSndAFIFOReset             = 1;

   snd_ctrl.EnableDSndBToRightSpeaker  = 0;
   snd_ctrl.EnableDSndBToLeftSpeaker   = 0;
   snd_ctrl.DSndBSampleRateTimer       = 0;   // Timer 0
   snd_ctrl.DSndBFIFOReset             = 0;

   //play a mono sound at 16khz
   //uses timer 0 as sampling rate source
   //uses timer 1 to count the samples played in order to stop the sound

   // REG_SOUNDCNT_H = 0x0b0F; //enable DS A&B + fifo reset + use timer0 + max volume to L and R
   // REG_SOUNDCNT_H = SoundCntXMask | ENABLE_DSA_TO_LEFT | ENABLE_DSB_TO_RIGHT;

   REG_SOUNDCNT_H = * (u16*) &snd_ctrl;
   REG_SOUNDCNT_X = 0x0080; //turn sound chip on

   SetMode( MODE_3 | BG2_ENABLE );

   while(1)
   {
      if( REG_TM1CNT_H == 0 )
      {
         if(!(*KEYS & KEY_A))
         {
            while(!(*KEYS & KEY_A));

            PlaySample((const u32*) SOUND25_DATA, SOUND25_LENGTH, playbackFreq );
         }
         else if(!(*KEYS & KEY_B))
         {
            while(!(*KEYS & KEY_B));

            PlaySample((const u32*) SOUND21_DATA, SOUND21_LENGTH, playbackFreq );
         }
      }

      if(!(*KEYS & KEY_UP))
      {
         while(!(*KEYS & KEY_UP));
         playbackFreq *= 2;
      }
      else if(!(*KEYS & KEY_DOWN))
      {
         while(!(*KEYS & KEY_DOWN));
         playbackFreq /= 2;
      }
   }
}

#1244 - Splam - Wed Jan 15, 2003 10:26 pm

None of the emulators are quite accurate enough to presume any sound clicking wouldn't be due to them but having said that a straight "stream" like this shouldn't cause as many problems as using a buffer (as would be needed for a mod player) because the streaming will just keep doing dma copys into the FIFO until the timers run their length and the interrupt happens.

This code may well work perfectly on hardware and fiddling with it to work on an emu would just break the hardware version.

The only thing I can see that would maybe cause clicks on vboy is in the interrupt code you could try moving the Stop DMA to be the 1st thing it does. Like I say though, if this stuff works on hardware you're only going to break it messing with it ;)

To sum up, emulators aren't perfect, you have to put up with popping sound, glitching smooth scrolls and other problems :(