Changing Clock Sometimes Breaks Sound


jrd

Still Fresh
Joined
Oct 30, 2005
Messages
23
Hi everyone,

I'm changing FCLK with the following code:

Code:
#define SYSTEM_CLOCK 2457600

tmp = (new_clock*1000000) / SYSTEM_CLOCK;
tmp = (tmp-8)<<8;
gp2x_memregs[0x910>>1] = tmp | 4;
while(gp2x_memregs[0x902>>1] & 1);

This works, except that sometimes it causes the sound thread to lock on the next write() to "/dev/dsp". I have tried the code from the LCD tweaker and it does exactly the same thing.

No problem, I thought, I'll just shut down the sound before changing the clock and reinitialise it afterwards. This fixed the crash problem, but now the sound is sometimes at the wrong frequency, is silent or generally corrupt after the clock has been changed. Here's the code I'm using to change it: (I'm using ryleh's minimal lib BTW)

Code:
#define SYSTEM_CLOCK 2457600

void CPU_SetClock(unsigned int new_clock)
{
	if (new_clock != curr_clock)
	{
		unsigned int tmp, store;
		int re_init_audio = 0;
		
		curr_clock = new_clock;
		tmp = (new_clock*1000000) / SYSTEM_CLOCK;
		tmp = (tmp-8)<<8;
		
		// make sure sound interrupt doesn't happen just after the clock has been changed
		store = gp2x_sound_pausei;
		gp2x_sound_pausei = 1;
		
		if (gp2x_dev[3])
		{
			close(gp2x_dev[3]);
			gp2x_dev[3] = 0;
			re_init_audio = 1;
		}
		
		gp2x_memregs[0x910>>1] = tmp | 4;
		
		while(gp2x_memregs[0x902>>1] & 1);
		
		if (re_init_audio)
		{
			int rate = 44100, bits = 16, stereo = 1, frag = 0x80000|9;
			
			gp2x_dev[3] = open("/dev/dsp", O_WRONLY);
			gp2x_sound_volume(100,100);
			ioctl(gp2x_dev[3], SNDCTL_DSP_SPEED,  &rate);
			ioctl(gp2x_dev[3], SNDCTL_DSP_SETFMT, &bits);
			ioctl(gp2x_dev[3], SNDCTL_DSP_STEREO, &stereo);
			ioctl(gp2x_dev[3], SNDCTL_DSP_SETFRAGMENT, &frag);
		}
		
		gp2x_sound_pausei = store;
	}
}

I've tried re-initialising the sound several times (even every iteration of the audio thread) or adding delays between setting the clock and opening "/dev/dsp" but to no avail.

Any help, suggestions or ideas would be really appreciated!

Thanks,
James.
 
The first person who helps me fix this get a free copy of the finished game! :)
 
I think he tries to dynamicly ajust the clockrate. Tune it up when you in a heavier part of the game and tune it down when there isn't much happening.

Is your clock changing routine correct? And you might want to turn off interrupts while you change the clock:
Code:
void SetClock(unsigned int MHZ)
{
#ifdef GP2X
  unsigned int v;
  unsigned int mdiv,pdiv=3,scale=0;
  MHZ*=1000000;
  mdiv=(MHZ*pdiv)/SYS_CLK_FREQ;

  mdiv=((mdiv-8)<<8) & 0xff00;
  pdiv=((pdiv-2)<<2) & 0xfc;
  scale&=3;
  v = mdiv | pdiv | scale;
  
  unsigned int l = memregs32[0x808>>2];// Get interupt flags
  memregs32[0x808>>2] = 0xFF8FFFE7;   //Turn off interrupts
  memregs16[0x910>>1]=v;			  //Set frequentie
  while(memregs16[0x0902>>1] & 1);	//Wait for the frequentie to be ajused
  memregs32[0x808>>2] = l;			//Turn on interrupts
#endif
}
 
Franxis posted on Sep 12 2006 at 12:12 AM said:
change the cpu clock before initialising the sound ? :p :D

I can't really avoid that unfortunately.

Parkydr posted on Sep 12 2006 at 08:23 AM said:
The obvious solution is not to change the clock :) What are you trying to do?

There's a power saving mode that clocks down the CPU after a minute to reduce battery usage. Also, the user can change the clock speed in the menu whilst the game is running.

Daid posted on Sep 12 2006 at 10:47 AM said:
Is your clock changing routine correct?

I'm pretty sure that the bit that actually changes the clock is correct - it certainly seems to have the desired effect.

Daid posted on Sep 12 2006 at 10:47 AM said:
And you might want to turn off interrupts while you change the clock:

The code as posted stopped the sound, but it didn't resume at all when the interrupts were restored - Linux probably doesn't like it when you hack the interrupt registers directly.

I tried adding the interrupt stop/start lines to my original code but I still get the same problem (sound is sometimes a bit broken after a clock change).

What I don't understand is what the difference is between re-initialising it after a clock change and initialising it on startup (which works fine).

I'm thinking about handling the interrupts directly, rather than letting Linux do it. Does anyone have any experience with this? Will doing this fix the problem?
 
Last edited by a moderator:
Back
Top