Recommendation from Lennart Poettering: SDL-1.2
authorSam Lantinga <slouken@libsdl.org>
Mon, 19 Oct 2009 02:23:21 +0000
branchSDL-1.2
changeset 4357 a10dac5858fe
parent 4356 ab2dfac9d5c1
child 4358 df306a61a61d
Recommendation from Lennart Poettering: In ALSA_OpenAudio(): instead of setting period_size+n_periods OR buffer_size I'd recommend copying the hwparams stuff before you do this, then first try period_size+n_periods, and then apply it with snd_pcm_hw_params() and check if that works. If it didn't you should take the copy of hwparams and try setting buffer_size and apply that via snd_pcm_hw_params() and check if that worked. And if that failed too, then take the copy and don't apply neither period nor buffer settings and see if that works.
src/audio/alsa/SDL_alsa_audio.c
--- a/src/audio/alsa/SDL_alsa_audio.c	Sun Oct 18 23:18:28 2009 +0000
+++ b/src/audio/alsa/SDL_alsa_audio.c	Mon Oct 19 02:23:21 2009 +0000
@@ -65,6 +65,7 @@
 static const char *(*SDL_NAME(snd_strerror))(int errnum);
 static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
 static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void);
+static void (*SDL_NAME(snd_pcm_hw_params_copy))(snd_pcm_hw_params_t *dst, const snd_pcm_hw_params_t *src);
 static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
 static int (*SDL_NAME(snd_pcm_hw_params_set_access))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access);
 static int (*SDL_NAME(snd_pcm_hw_params_set_format))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val);
@@ -101,6 +102,7 @@
 	{ "snd_strerror",	(void**)(char*)&SDL_NAME(snd_strerror)		},
 	{ "snd_pcm_hw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof)		},
 	{ "snd_pcm_sw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof)		},
+	{ "snd_pcm_hw_params_copy",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_copy)		},
 	{ "snd_pcm_hw_params_any",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_any)		},
 	{ "snd_pcm_hw_params_set_access",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_access)		},
 	{ "snd_pcm_hw_params_set_format",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_format)		},
@@ -365,16 +367,116 @@
 	}
 }
 
+static int ALSA_finalize_hardware(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *hwparams, int override)
+{
+	int status;
+	snd_pcm_uframes_t bufsize;
+
+	/* "set" the hardware with the desired parameters */
+	status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
+	if ( status < 0 ) {
+		return(-1);
+	}
+
+	/* Get samples for the actual buffer size */
+	status = SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
+	if ( status < 0 ) {
+		return(-1);
+	}
+	if ( !override && bufsize != spec->samples * 2 ) {
+		return(-1);
+	}
+
+	/* FIXME: Is this safe to do? */
+	spec->samples = bufsize / 2;
+
+	/* This is useful for debugging */
+	if ( getenv("SDL_AUDIO_ALSA_DEBUG") ) {
+		snd_pcm_sframes_t persize = 0;
+		unsigned int periods = 0;
+
+		SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, NULL);
+		SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, NULL);
+
+		fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
+	}
+	return(0);
+}
+
+static int ALSA_set_period_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params)
+{
+	const char *env;
+	int status;
+	int override = 0;
+	snd_pcm_hw_params_t *hwparams;
+	snd_pcm_uframes_t frames;
+	unsigned int periods;
+
+	/* Copy the hardware parameters for this setup */
+	snd_pcm_hw_params_alloca(&hwparams);
+	SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
+
+	env = getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
+	if ( env ) {
+		override = SDL_strol(env);
+		if ( override == 0 ) {
+			return(-1);
+		}
+	}
+
+	frames = spec->samples;
+	status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
+	if ( status < 0 ) {
+		return(-1);
+	}
+
+	periods = 2;
+	status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
+	if ( status < 0 ) {
+		return(-1);
+	}
+
+	return ALSA_finalize_hardware(this, spec, hwparams, override);
+}
+
+static int ALSA_set_buffer_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params)
+{
+	const char *env;
+	int status;
+	int override = 0;
+	snd_pcm_hw_params_t *hwparams;
+	snd_pcm_uframes_t frames;
+
+	/* Copy the hardware parameters for this setup */
+	snd_pcm_hw_params_alloca(&hwparams);
+	SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
+
+	env = getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
+	if ( env ) {
+		override = SDL_strol(env);
+		if ( override == 0 ) {
+			return(-1);
+		}
+	}
+
+	frames = spec->samples * 2;
+	status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
+	if ( status < 0 ) {
+		return(-1);
+	}
+
+	return ALSA_finalize_hardware(this, spec, hwparams, override);
+}
+
 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
 {
 	int                  status;
 	snd_pcm_hw_params_t *hwparams;
 	snd_pcm_sw_params_t *swparams;
 	snd_pcm_format_t     format;
-	snd_pcm_uframes_t    frames;
 	unsigned int         rate;
-	unsigned int         periods;
 	unsigned int 	     channels;
+	snd_pcm_uframes_t    bufsize;
 	Uint16               test_format;
 
 	/* Open the audio device */
@@ -469,53 +571,14 @@
 	spec->freq = rate;
 
 	/* Set the buffer size, in samples */
-	if (getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE")) {
-		frames = spec->samples;
-		status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
-		if ( status < 0 ) {
-			SDL_SetError("Couldn't set period size: %s", SDL_NAME(snd_strerror)(status));
-			ALSA_CloseAudio(this);
-			return(-1);
-		}
-
-		spec->samples = frames;
-
-		periods = 2;
-		status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
-		if ( status < 0 ) {
-			SDL_SetError("Couldn't set period count: %s", SDL_NAME(snd_strerror)(status));
+	if ( ALSA_set_period_size(this, spec, hwparams) < 0 &&
+	     ALSA_set_buffer_size(this, spec, hwparams) < 0 ) {
+		/* Failed to set buffer size, try to just use the defaults */
+		if ( ALSA_finalize_hardware(this, spec, hwparams, 1) < 0 ) {
+			SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
 			ALSA_CloseAudio(this);
 			return(-1);
 		}
-	} else {
-		frames = spec->samples * 2;
-		status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
-		if ( status < 0 ) {
-			SDL_SetError("Couldn't set buffer size: %s", SDL_NAME(snd_strerror)(status));
-			ALSA_CloseAudio(this);
-			return(-1);
-		}
-	}
-
-	/* "set" the hardware with the desired parameters */
-	status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
-	if ( status < 0 ) {
-		SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
-		ALSA_CloseAudio(this);
-		return(-1);
-	}
-
-	/* This is useful for debugging */
-	if (getenv("SDL_AUDIO_ALSA_DEBUG_PERIOD_SIZE")) {
-		snd_pcm_uframes_t bufsize;
-		snd_pcm_sframes_t persize;
-		unsigned int periods; int dir;
-
-		SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
-		SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, &dir);
-		SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, &dir);
-
-		fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
 	}
 
 	/* Set the software parameters */