From: notaz <notasas@gmail.com>
Date: Wed, 5 Aug 2009 22:29:46 +0000 (+0000)
Subject: oss code refactoring, sound for frame unlim mode
X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=dd59a74b2061298e73c13ddf1be7287cca3ebc74;p=libpicofe.git

oss code refactoring, sound for frame unlim mode

git-svn-id: file:///home/notaz/opt/svn/PicoDrive/platform@735 be3aeb3a-fb24-0410-a615-afba39da0efa
---

diff --git a/base_readme.txt b/base_readme.txt
index 0298f6d..c6757ea 100644
--- a/base_readme.txt
+++ b/base_readme.txt
@@ -430,9 +430,6 @@ problems in some rare cases. Try disabling this if your game has problems.
 
 @@3. "Disable frame limiter"
 This allows games to run faster then 50/60fps, useful for benchmarking.
-#ifdef GP2X
-You need to disable sound for this to work properly.
-#endif
 
 #ifdef GP2X
 @@3. "Use ARM940 core for sound"
diff --git a/gp2x/emu.c b/gp2x/emu.c
index 7e60c2b..acc3881 100644
--- a/gp2x/emu.c
+++ b/gp2x/emu.c
@@ -590,12 +590,16 @@ void plat_update_volume(int has_changed, int is_up)
 
 static void updateSound(int len)
 {
+	len <<= 1;
 	if (PicoOpt & POPT_EN_STEREO)
 		len <<= 1;
 
+	if ((currentConfig.EmuOpt & EOPT_NO_FRMLIMIT) && !sndout_oss_can_write(len))
+		return;
+
 	/* avoid writing audio when lagging behind to prevent audio lag */
 	if (PicoSkipFrame != 2)
-		sndout_oss_write(PsndOut, len<<1);
+		sndout_oss_write(PsndOut, len);
 }
 
 void pemu_sound_start(void)
@@ -609,31 +613,40 @@ void pemu_sound_start(void)
 	{
 		int is_stereo = (PicoOpt & POPT_EN_STEREO) ? 1 : 0;
 		int target_fps = Pico.m.pal ? 50 : 60;
-		int snd_excess_add;
+		int frame_samples, snd_excess_add;
 		gp2x_soc_t soc;
 
+		soc = soc_detect();
+
 		#define SOUND_RERATE_FLAGS (POPT_EN_FM|POPT_EN_PSG|POPT_EN_STEREO|POPT_EXT_FM|POPT_EN_MCD_CDDA)
 		if (PsndRate != PsndRate_old || Pico.m.pal != pal_old || ((PicoOpt & POPT_EXT_FM) && crashed_940) ||
 				((PicoOpt ^ PicoOpt_old) & SOUND_RERATE_FLAGS)) {
 			PsndRerate(Pico.m.frame_count ? 1 : 0);
 		}
-		snd_excess_add = ((PsndRate - PsndLen * target_fps)<<16) / target_fps;
-		printf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n",
-			PsndRate, PsndLen, snd_excess_add, is_stereo, Pico.m.pal);
-		sndout_oss_start(PsndRate, 16, is_stereo);
-		sndout_oss_setvol(currentConfig.volume, currentConfig.volume);
-		PicoWriteSound = updateSound;
-		plat_update_volume(0, 0);
+
 		memset(sndBuffer, 0, sizeof(sndBuffer));
 		PsndOut = sndBuffer;
+		PicoWriteSound = updateSound;
 		PsndRate_old = PsndRate;
 		PicoOpt_old  = PicoOpt;
 		pal_old = Pico.m.pal;
+		plat_update_volume(0, 0);
+
+		frame_samples = PsndLen;
+		snd_excess_add = ((PsndRate - PsndLen * target_fps)<<16) / target_fps;
+		if (snd_excess_add != 0)
+			frame_samples++;
+		if (soc == SOCID_POLLUX)
+			frame_samples *= 2;	/* force larger buffer */
+
+		printf("starting audio: %i len: %i (ex: %04x) stereo: %i, pal: %i\n",
+			PsndRate, PsndLen, snd_excess_add, is_stereo, Pico.m.pal);
+		sndout_oss_setvol(currentConfig.volume, currentConfig.volume);
+		sndout_oss_start(PsndRate, frame_samples, is_stereo);
 
 		/* Wiz's sound hardware needs more prebuffer */
-		soc = soc_detect();
 		if (soc == SOCID_POLLUX)
-			updateSound(PsndLen);
+			updateSound(frame_samples);
 	}
 }
 
diff --git a/linux/sndout_oss.c b/linux/sndout_oss.c
index e2d0bde..ccadbba 100644
--- a/linux/sndout_oss.c
+++ b/linux/sndout_oss.c
@@ -10,6 +10,8 @@
 #include "sndout_oss.h"
 
 static int sounddev = -1, mixerdev = -1;
+static int can_write_safe;
+
 
 int sndout_oss_init(void)
 {
@@ -23,18 +25,21 @@ int sndout_oss_init(void)
 	return 0;
 }
 
-
-int sndout_oss_start(int rate, int bits, int stereo)
+int sndout_oss_start(int rate, int frame_samples, int stereo)
 {
-	static int s_oldrate = 0, s_oldbits = 0, s_oldstereo = 0;
-	int frag = 0, bsize, buffers, ret;
+	static int s_oldrate = 0, s_old_fsamples = 0, s_oldstereo = 0;
+	int frag, bsize, bits, ret;
 
 	// if no settings change, we don't need to do anything,
 	// since audio is never stopped
-	if (rate == s_oldrate && s_oldbits == bits && s_oldstereo == stereo)
+	if (rate == s_oldrate && s_old_fsamples == frame_samples && s_oldstereo == stereo)
 		return 0;
 
-	if (sounddev >= 0) close(sounddev);
+	if (sounddev >= 0) {
+		ioctl(sounddev, SOUND_PCM_SYNC, 0);
+		close(sounddev);
+	}
+
 	sounddev = open("/dev/dsp", O_WRONLY|O_ASYNC);
 	if (sounddev == -1)
 	{
@@ -42,42 +47,67 @@ int sndout_oss_start(int rate, int bits, int stereo)
 		return -1;
 	}
 
-	// calculate buffer size
-	// this is tuned for GP2X
-	buffers = 16;
-	bsize = rate / 32;
-	if (rate > 22050) { bsize*=4; buffers*=2; }
-	while ((bsize>>=1)) frag++;
-	frag |= buffers<<16; // 16 buffers
-	ret = ioctl(sounddev, SNDCTL_DSP_SETFRAGMENT, &frag);
-	if (ret) perror("SNDCTL_DSP_SETFRAGMENT failed");
+	// calculate buffer size. We one to fit 1 frame worth of sound data.
+	// Also ignore mono because both GP2X and Wiz mixes mono to stereo anyway.
+	bsize = frame_samples << 2;
+
+	for (frag = 0; bsize; bsize >>= 1, frag++)
+		;
 
-	ret  = ioctl(sounddev, SNDCTL_DSP_STEREO, &stereo);
-	ret |= ioctl(sounddev, SNDCTL_DSP_SETFMT, &bits);
-	ret |= ioctl(sounddev, SNDCTL_DSP_SPEED,  &rate);
-	if (ret) printf("failed to set audio format\n");
+	frag |= 16 << 16;	// fragment count
+	ret = ioctl(sounddev, SNDCTL_DSP_SETFRAGMENT, &frag);
+	if (ret < 0)
+		perror("SNDCTL_DSP_SETFRAGMENT failed");
+
+	bits = 16;
+	ret = ioctl(sounddev, SNDCTL_DSP_STEREO, &stereo);
+	if (ret == 0)
+		ret = ioctl(sounddev, SNDCTL_DSP_SETFMT, &bits);
+	if (ret == 0)
+		ret = ioctl(sounddev, SNDCTL_DSP_SPEED, &rate);
+	if (ret < 0)
+		perror("failed to set audio format");
+
+	// not sure if this is still needed (avoiding driver bugs?)
 	usleep(192*1024);
 
-	printf("gp2x_set_sound: %i/%ibit/%s, %i buffers of %i bytes\n",
-		rate, bits, stereo?"stereo":"mono", frag>>16, 1<<(frag&0xffff));
+	printf("sndout_oss_start: %d/%dbit/%s, %d buffers of %i bytes\n",
+		rate, bits, stereo ? "stereo" : "mono", frag >> 16, 1 << (frag & 0xffff));
 
-	s_oldrate = rate; s_oldbits = bits; s_oldstereo = stereo;
+	s_oldrate = rate; s_old_fsamples = frame_samples; s_oldstereo = stereo;
+	can_write_safe = 0;
 	return 0;
 }
 
-
 int sndout_oss_write(const void *buff, int len)
 {
 	return write(sounddev, buff, len);
 }
 
+int sndout_oss_can_write(int bytes)
+{
+	audio_buf_info bi;
+	int ret;
+
+#ifdef __GP2X__
+	// note: SNDCTL_DSP_GETOSPACE crashes F100 kernel for some reason
+	// if called too early, so we work around here
+	if (can_write_safe++ < 8)
+		return 1;
+#endif
+	ret = ioctl(sounddev, SNDCTL_DSP_GETOSPACE, &bi);
+	if (ret < 0)
+		return 1;
+
+	// have enough bytes to write + 4 extra frags
+	return bi.bytes - bi.fragsize * 4 >= bytes ? 1 : 0;
+}
 
 void sndout_oss_sync(void)
 {
 	ioctl(sounddev, SOUND_PCM_SYNC, 0);
 }
 
-
 void sndout_oss_setvol(int l, int r)
 {
 	if (mixerdev < 0) return;
@@ -87,7 +117,6 @@ void sndout_oss_setvol(int l, int r)
  	ioctl(mixerdev, SOUND_MIXER_WRITE_PCM, &l); /*SOUND_MIXER_WRITE_VOLUME*/
 }
 
-
 void sndout_oss_exit(void)
 {
 	if (sounddev >= 0) close(sounddev); sounddev = -1;
diff --git a/linux/sndout_oss.h b/linux/sndout_oss.h
index d6f285b..d5a2610 100644
--- a/linux/sndout_oss.h
+++ b/linux/sndout_oss.h
@@ -1,6 +1,7 @@
 int  sndout_oss_init(void);
-int  sndout_oss_start(int rate, int bits, int stereo);
+int  sndout_oss_start(int rate, int frame_samples, int stereo);
 int  sndout_oss_write(const void *buff, int len);
+int  sndout_oss_can_write(int bytes);
 void sndout_oss_sync(void);
 void sndout_oss_setvol(int l, int r);
 void sndout_oss_exit(void);