Fixing the sound lag on mame4all-pi

Fine tuning Mame4all_pi (2015-12) for vertical displays

A guide by Maarten Spoek

In my previous blog entry, I thought I fixed the sound lag with mame4all-pi, but that wasn’t true somehow the next morning… duh…
So I had to dive into the sound buffer. It seemed to me that the 60fps was not really 60fps, or not the same 60fps the alsa worker thread was running.
Maybe sometimes it was and sometimes it wasn’t.
Anyway, the only solution to fix this and not wanting to rewrite the whole sound buffer, is to under run the sound buffer, which will crackle the sound sometimes.

diff --git a/src/rpi/video.cpp b/src/rpi/video.cpp
index 7e6655d..e0a07f0 100644
--- a/src/rpi/video.cpp
+++ b/src/rpi/video.cpp
@@ -432,28 +440,37 @@ int osd_set_display(int width,int height,int depth,int attributes,int orientatio
 
 	vsync_frame_rate = video_fps;
 
-	if (video_sync)
+	if (video_sync) // you need to enable this in the mame.cfg ...
 	{
 		TICKER a,b;
 		float rate;

 
 		/* wait some time to let everything stabilize */
-		for (i = 0;i < 60;i++)
-		{
-			vsync();
-			a = ticker();
-		}

+		usleep(1000000); // for (i = 0;i < 100000;i++) ; 
-		/* small delay for really really fast machines */
-		for (i = 0;i < 100000;i++) ; 
-		a = ticker(); 
-  
-		vsync(); 
- 		b = ticker(); 
-  
-		rate = ((float)TICKS_PER_SEC)/(b-a); 
+		rate = (float)video_fps * 0.998; // underrun the sound   
-		logerror("target frame rate = %ffps, video frame rate = %3.2fHz\n",video_fps,rate); 
+		logerror("target frame rate = %dfps, video frame rate = %3.2fHz\n",video_fps,rate);
    		/* don't allow more than 8% difference between target and actual frame rate */
  		while (rate > video_fps * 108 / 100)
@@ -463,7 +480,7 @@ int osd_set_display(int width,int height,int depth,int attributes,int orientatio
 		{
 			osd_close_display();
 			logerror("-vsync option cannot be used with this display mode:\n"
-						"video refresh frequency = %dHz, target frame rate = %ffps\n",
+						"video refresh frequency = %dHz, target frame rate = %dfps\n",
 						(int)(TICKS_PER_SEC/(b-a)),video_fps);
 			return 0;
 		}
@@ -772,7 +789,7 @@ void osd_update_video_and_audio(struct osd_bitmap *bitmap)
 		{12,0,0,0,0,0,0,0,0,0,0,0 }
 	};
 	int i;
-	static int showfps,showfpstemp;
+	static int showfps = 0,showfpstemp = 0;
 	TICKER curr;
 	static TICKER prev_measure=0,this_frame_base,prev;
 	static int speed = 100;
@@ -825,12 +842,12 @@ void osd_update_video_and_audio(struct osd_bitmap *bitmap)
 			profiler_mark(PROFILER_IDLE);
 			if (video_sync)
 			{
-				static TICKER last;
+				static TICKER last = ticker();
 				do
 				{
-					vsync();
+					usleep(20); // vsync();
 					curr = ticker();
-				} while (TICKS_PER_SEC / (curr - last) > video_fps * 11 /10);
+				} while (TICKS_PER_SEC / (curr - last) >= video_fps);
 				last = curr;
 			}
 			else

So in addition to under running the sound buffer, I wrote some code to repeat the last sample when under running, instead of blanking it with zeros. This way, there is no cracking sound.

diff --git a/src/rpi/sound.cpp b/src/rpi/sound.cpp
index 7b74942..4366c16 100644
--- a/src/rpi/sound.cpp
+++ b/src/rpi/sound.cpp
@@ -123,13 +123,15 @@ void osd_stop_audio_stream(void)
 }
 
 #define min(a, b) ((a) period_size_bytes / alsa->period_size_frames;
+	UINT8 lastsample[MAXBYTESPERSAMPLE] = {0,0,0,0};
 	UINT8 *buf = (UINT8 *)calloc(1, alsa->period_size_bytes);
 	if (!buf)
 	{
@@ -153,21 +155,31 @@ static void alsa_worker_thread(void *data)
 		if(avail period_size_bytes)
 		{
 			slock_unlock(alsa->fifo_lock);
-			fifo_size = 0;
+			buf_size = 0;
 		}
 		else
 		{
-			fifo_size = min(alsa->period_size_bytes, avail);
-			if(fifo_size > alsa->period_size_bytes)
-				fifo_size = alsa->period_size_bytes;
-			fifo_read(alsa->buffer, buf, fifo_size);
+			buf_size = min(alsa->period_size_bytes, avail);
+			fifo_read(alsa->buffer, buf, buf_size);
 			scond_signal(alsa->cond);
 			slock_unlock(alsa->fifo_lock);
 		}
 	    
-		// If underrun, fill rest with silence.
- 		if(alsa->period_size_bytes != fifo_size) {
-			memset(buf + fifo_size, 0, alsa->period_size_bytes - fifo_size);
+		// If underrun, fill rest with last value (a silence will give crackle).
+ 		if ((buf_size >= bytes) && (bytes <= MAXBYTESPERSAMPLE)) {
+	 		// Read last sample in case of future underrun
+	 		for (int i=0; i<bytes; i++) {
+	 			lastsample[i] = buf[buf_size - bytes + i];
+	 		}
+	 	} else if(buf_size == 0) {
+	 		// now fill underrun with the value
+	 		for (int j=0; jperiod_size_bytes - buf_size;) {
+		 		// write last sample
+		 		for (int i=0; iperiod_size_bytes*3);
+		tempbuf=calloc(1, alsa->period_size_bytes*4);
 		snd_pcm_writei (alsa->pcm, tempbuf, 2 * alsa->period_size_frames);
 		free(tempbuf);
 	}
-
+	
 	alsa->fifo_lock = slock_new();
 	alsa->cond_lock = slock_new();
 	alsa->cond = scond_new();

Now when running my video screen @60Hz the sound is pretty much perfect.

Fixing the sound lag on mame4all-pi

Patching Mame4all-pi / Advancemenu

Fine tuning Advmenu 2.08 & Mame4all_pi (2015-12) for vertical displays

A guide by Maarten Spoek

[UPDATE January 18th, 2016: Sound is still lagging… strange… it was running well, and now the problem is back………]

[UPDATE January 19th, 2016: Hopefully I fixed it…]

Currently I have a Raspberry PI 2 with a vertical 3:4 display running Ms Pac-Man at 10-15% cpu. Here is how I did it.
PS, if you run on a horizontal oriented display, there are some useful changes in the Mame4all-pi code for you as well.

I use Mame4all-pi and Advancemenu 2.08
I followed the normal instructions you can find on the internet, where you rotate the screen via the mame.cfg (rol=1). But I wasn’t satisfied with the setup out of the box. I had the following issues:

  • Neo Geo was not rotating all of the content.
  • CPU was always around 98%
  • Sound was lagging behind.
  • Raspbian boot messages where still rotated.

So the simplest thing to do was using the /boot/config.txt from Raspbian to rotate the screen. My screen is a 4:3 1280×1024 LCD rotated counter clockwise.

Change /boot/config.txt as super-user by adding these lines

# rotate counter clock-wise (use 3 for clock-wise)
display_rotate=1

# disable the ugly rainbow boot screen 
disable_splash=1

# give extra memory to the gpu for rotating screen (minimum should be 32)
gpu_mem=256

 

Then change back the rotation of Mame4all_pi and Advancemenu.

~/.advance/advmenu.rc

display_orientation 

mame.cfg

rol=no
ror=no

 

Ok, now booting is nice and readable, but performance isĀ terrible. It seems rotating the screen via this option is pretty slow on the Raspberry PI. And I ran into other problems with the Advancemenu.

The following issues had to be conquered:

  • Sound was cracking up.
  • Advancemenu was showing a 4:3 display in a 3:4 display, so it was not using the complete display and it was very stretched out.

Lowering the resolution solved the sound cracking up, but then the sound was starting to lag again.
Here is how you lower the resolution. It is also a good idea to set the frame rate at 60Hz for any resolution. Use either 640×480 or 800×600. Higher is also possible.

/boot/config.txt

# uncomment to force a specific HDMI mode (this will force VGA)
hdmi_group=2
# 4 = 640x480@60  9 = 800x600@60
hdmi_mode=4
# 1 to force a DVI mode rather than HDMI.
hdmi_drive=1

 

Advancemenu wasn’t able to produce a 3:4 display, whatever setting I tried. Which was very strange, because the advcfg was able to set up a 3:4 display. So I started digging into the source. The 4:3 display was hard coded into the source!
For now I patched the files to the aspect ratio of 3:4. In the future, perhaps, I will implement a better solution, which detects the aspect ratio of the display first. Which is easy, because there is already code which remembers the screen resolution before switching any graphics mode.

So here is how I changed it.

diff --git a/advancemenu-2.8/advance/menu/menu.cc b/advancemenu-2.8-spoek/advance/menu/menu.cc
index bbad165..8ce70c5 100644
--- a/advancemenu-2.8/advance/menu/menu.cc
+++ b/advancemenu-2.8-spoek/advance/menu/menu.cc
@@ -745,6 +745,7 @@ static void run_background_wait(config_state& rs, const resource& sound, bool si
 
 static int run_menu_user(config_state& rs, bool flipxy, menu_array& gc, sort_item_func* category_extract, bool silent, string over_msg)
 {
+flipxy = !flipxy;
 	int coln; // number of columns
 	int rown; // number of rows
 
diff --git a/advancemenu-2.8/advance/menu/text.cc b/advancemenu-2.8-spoek/advance/menu/text.cc
index 87b48ea..c8b1c1b 100644
--- a/advancemenu-2.8/advance/menu/text.cc
+++ b/advancemenu-2.8-spoek/advance/menu/text.cc
@@ -412,7 +412,7 @@ static bool int_mode_find(bool& mode_found, unsigned index, adv_crtc_container&
 	// generate an exact mode with clock
 	if (int_has_generate) {
 		adv_crtc crtc;
-		err = generate_find_interpolate(&crtc, int_mode_size, int_mode_size*3/4, 70, &int_monitor, &int_interpolate, video_mode_generate_driver_flags(VIDEO_DRIVER_FLAGS_MODE_GRAPH_MASK, 0), GENERATE_ADJUST_EXACT | GENERATE_ADJUST_VCLOCK);
+		err = generate_find_interpolate(&crtc, int_mode_size, int_mode_size*4/3, 60, &int_monitor, &int_interpolate, video_mode_generate_driver_flags(VIDEO_DRIVER_FLAGS_MODE_GRAPH_MASK, 0), GENERATE_ADJUST_EXACT | GENERATE_ADJUST_VCLOCK);
 		if (err == 0) {
 			if (crtc_clock_check(&int_monitor, &crtc)) {
 				adv_mode mode;
@@ -791,7 +791,7 @@ bool int_enable(int fontx, int fonty, const string& font, unsigned orientation)
 	if (fontx >= 5 && fontx <= 200)
  		font_size_x = video_size_x() / fonts;
  	else 
-		font_size_x = font_size_y * video_size_x() * 3 / video_size_y() / 4; 
+		font_size_x = font_size_y * video_size_x() * 4 / video_size_y() / 3 ;
    	// load the font
  	int_font = 0; 
@@ -934,8 +934,8 @@ void cell_pos_t::compute_size(unsigned* rx, unsigned* ry, const adv_bitmap* bit
  		aspecty = 1;
  	}   
-	aspectx *= 3 * video_size_x(); 
-	aspecty *= 4 * video_size_y(); 
+	aspectx *= 4 * video_size_x(); 
+	aspecty *= 3 * video_size_y();
    	if (aspectx * real_dy > aspecty * real_dx) {
 		*rx = real_dx;

 

To Mame4all_pi, I made several changes:

  • Proper “vsync”, boosting CPU performance.
  • Removed sound lag
  • Fixed orientation on several games from Cave
  • Added option to adjust pixel aspect ratio, so you stretch the screen a bit. Useful for horizontal oriented games.
  • Added option to fill the whole screen, if it falls within a certain aspect ratio, compared to your screen. So Donkey Kong is filling your 3:4 display, as well as Ms Pac-Man, etc.

Changes to Mame4all_pi source.

Fixed orientation on several games from Cave

diff --git a/src/drivers/cave.cpp b/src/drivers/cave.cpp
index d2149f4..89adc50 100644
--- a/src/drivers/cave.cpp
+++ b/src/drivers/cave.cpp
@@ -1291,7 +1291,7 @@ void init_uopoko(void)
 ***************************************************************************/
 
 GAME( 1997, ddonpach, 0, ddonpach, dfeveron, ddonpach, ROT90_16BIT, "Atlus/Cave",                  "Dodonpachi (Japan)"     )
-GAME( 1998, dfeveron, 0, dfeveron, dfeveron, dfeveron, ROT270_16BIT, "Cave (Nihon System license)", "Dangun Feveron (Japan)" )
-GAME( 1998, esprade,  0, esprade,  dfeveron, esprade,  ROT270_16BIT, "Atlus/Cave",                  "ESP Ra.De. (Japan)"     )
+GAME( 1998, dfeveron, 0, dfeveron, dfeveron, dfeveron, ROT90_16BIT, "Cave (Nihon System license)", "Dangun Feveron (Japan)" )
+GAME( 1998, esprade,  0, esprade,  dfeveron, esprade,  ROT90_16BIT, "Atlus/Cave",                  "ESP Ra.De. (Japan)"     )
 GAME( 1998, uopoko,   0, uopoko,   dfeveron, uopoko,   ROT0_16BIT,   "Cave (Jaleco license)",       "Uo Poko (Japan)"        )
-GAME( 1999, guwange,  0, guwange,  guwange,  esprade,  ROT270_16BIT, "Atlus/Cave",                  "Guwange (Japan)"        )
+GAME( 1999, guwange,  0, guwange,  guwange,  esprade,  ROT90_16BIT, "Atlus/Cave",                  "Guwange (Japan)"        )

Added option to adjust pixel aspect ratio, so you stretch the screen a bit. Useful for horizontal oriented games.
Added option to fill the whole screen, if it falls within a certain aspect ratio, compared to your screen. So Donkey Kong is filling your 3:4 display, as well as Ms Pac-Man, etc.

diff --git a/src/mame.h b/src/mame.h
index 0662052..aaca7c6 100644
--- a/src/mame.h
+++ b/src/mame.h
@@ -105,6 +105,10 @@ struct GameOptions {
 	int display_border;
 	int display_smooth_stretch;
 	int display_effect;
+
+	/* spoek */
+	float pixel_aspect_ratio; /* for calculating the real screen ratio, like pixel_aspect_ratio * display_width / display_height */
+	float ratio_threshold; /* when ratio difference between game and screen is below this threshold, we just fill the complete screen */
 };
 
 extern struct GameOptions options;
diff --git a/src/rpi/config.cpp b/src/rpi/config.cpp
index 3edf2c7..d4728ad 100644
--- a/src/rpi/config.cpp
+++ b/src/rpi/config.cpp
@@ -296,7 +296,7 @@ void init_inpdir(void)
 
 void parse_cmdline (int argc, char **argv, int game_index)
 {
-	static float f_beam, f_flicker;
+	static float f_beam, f_flicker, f_pixelratio, f_threshold;
 	char *resolution;
 	char *joyname;
 	char tmpres[10];
@@ -485,6 +485,15 @@ void parse_cmdline (int argc, char **argv, int game_index)
 	options.display_smooth_stretch = get_bool ("config", "display_smooth_stretch", NULL, 1);
 	options.display_effect = get_int ("config", "display_effect", NULL, 0);
 
+	f_pixelratio = get_float  ("config", "pixel_aspect_ratio",      NULL, 1.0);
+	if (f_pixelratio > 2.0) f_pixelratio = 2.0;
+	if (f_pixelratio < 0.5) f_pixelratio = 0.5;
+	options.pixel_aspect_ratio = !(options.rol ^ options.ror) ? f_pixelratio : 1.0 / f_pixelratio;
+
+	f_threshold = get_float  ("config", "ratio_threshold",      NULL, 1.0);
+	if (f_threshold < 1.0) f_threshold = 1.0 / f_threshold; +	options.ratio_threshold = f_threshold; +  	kiosk_mode = get_bool("config", "kioskmode", NULL, 0);    	close_config_file(); diff --git a/src/rpi/minimal.cpp b/src/rpi/minimal.cpp index e4c9dab..45363fd 100644 --- a/src/rpi/minimal.cpp +++ b/src/rpi/minimal.cpp @@ -302,16 +302,23 @@ void gp2x_set_video_mode(struct osd_bitmap *bitmap, int bpp,int width,int height    	 	// Work out the position and size on the display  	 	display_ratio = (float)display_width/(float)display_height; -	 	game_ratio = (float)width/(float)height; +	 	game_ratio = (float)width/(float)height*options.pixel_aspect_ratio;  	   		display_x = sx = display_adj_width;  		display_y = sy = display_adj_height;   -	 	if (game_ratio>display_ratio) 
-			sy = (float)display_adj_width/(float)game_ratio;
-	 	else 
-			sx = (float)display_adj_height*(float)game_ratio;
-	 
+		float ratio_diff = game_ratio / display_ratio;
+		if (ratio_diff < 1.0)  +			ratio_diff = 1.0 / ratio_diff; + +		// we only adjust for game ratio if more then % diff +		if (ratio_diff > options.ratio_threshold) { 
+	 		if (game_ratio>display_ratio) 
+				sy = (float)display_adj_width/(float)game_ratio;
+	 		else 
+				sx = (float)display_adj_height*(float)game_ratio;
+	 	}
+
 		// Centre bitmap on screen
 	 	display_x = (display_x - sx) / 2;
 	 	display_y = (display_y - sy) / 2;

Proper “vsync”, boosting CPU performance. It is not really vsync, because the display driver used has no option for it (I think… well it is not implemented in this source), but we time it correctly now.
Removed sound lag by measuring exactly 60Hz and not 66Hz like it was… Sound buffers are calculated to be exactly a multitude of the fps, so we have to time it exact as well…

diff --git a/src/rpi/video.cpp b/src/rpi/video.cpp
index 7e6655d..55387f2 100644
--- a/src/rpi/video.cpp
+++ b/src/rpi/video.cpp
@@ -358,7 +358,7 @@ Returns 0 on success.
 */
 int osd_create_display(int width,int height,int depth,int fps,int attributes,int orientation)
 {
-	logerror("width %d, height %d\n", width,height);
+	logerror("width %d, height %d, depth %d, fps %d\n", width,height,depth,fps);
 
 	video_depth = depth;
 	video_fps = fps;
@@ -432,7 +432,8 @@ int osd_set_display(int width,int height,int depth,int attributes,int orientatio
 
 	vsync_frame_rate = video_fps;
 
-	if (video_sync)
+
+	if (0 & video_sync) // disable this, just isn't working...
 	{
 		TICKER a,b;
 		float rate;
@@ -830,7 +831,7 @@ void osd_update_video_and_audio(struct osd_bitmap *bitmap)
 				{
 					vsync();
 					curr = ticker();
-				} while (TICKS_PER_SEC / (curr - last) > video_fps * 11 /10);
+				} while (TICKS_PER_SEC / (curr - last) > video_fps);
 				last = curr;
 			}
 			else

 

For Mame4all_pi to use this code changes, we change the mame.cfg

diff --git a/mame.cfg.template b/mame.cfg.template
index c33f321..454647f 100644
--- a/mame.cfg.template
+++ b/mame.cfg.template
@@ -16,8 +16,8 @@ flipy=no
 samplerate=44100
 volume=0
 cheat=no
-vector_width=640
-vector_height=480
+vector_width=480
+vector_height=640
 kioskmode=no
 force_stereo=no
 # Anti-alias the display?
@@ -25,6 +25,14 @@ display_smooth_stretch=yes
 display_border=0
 # display effect postprocessing: 0 none, 1 scanlines
 display_effect=0
+# turn on vsync waiting
+vsync=yes
+waitvsync=yes
+dirty=yes
+# make pixels a bit taller
+pixel_aspect_ratio=0.85
+# fill screen when game is within aspect ratio limits (75% < ar < 125%)
+ratio_threshold=1.25
 
 [directory]
 inp=inp
Patching Mame4all-pi / Advancemenu