#include <alsa/asoundlib.h>
#include "quakedef.h"
+#include "snd_main.h"
-static int snd_inited;
static snd_pcm_uframes_t buffer_size;
static const char *pcmname = NULL;
qboolean SNDDMA_Init (void)
{
- int err, i;
- int bps = -1, stereo = -1;
- unsigned int rate = 0;
+ int err, i, j;
+ int width;
+ int channels;
+ unsigned int rate;
snd_pcm_hw_params_t *hw;
snd_pcm_sw_params_t *sw;
- snd_pcm_uframes_t frag_size;
+ snd_pcm_uframes_t frag_size;
snd_pcm_hw_params_alloca (&hw);
snd_pcm_sw_params_alloca (&sw);
-// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm <devicename> selects which pcm device to us, default is "default"
- if ((i=COM_CheckParm("-sndpcm"))!=0)
- pcmname=com_argv[i+1];
- if (!pcmname)
- pcmname = "default";
-
// COMMANDLINEOPTION: Linux ALSA Sound: -sndbits <number> sets sound precision to 8 or 16 bit (email me if you want others added)
+ width = 2;
if ((i=COM_CheckParm("-sndbits")) != 0)
{
- bps = atoi(com_argv[i+1]);
- if (bps != 16 && bps != 8)
- {
- Con_Printf("Error: invalid sample bits: %d\n", bps);
- return false;
- }
+ j = atoi(com_argv[i+1]);
+ if (j == 16 || j == 8)
+ width = j / 8;
+ else
+ Con_Printf("Error: invalid sample bits: %d\n", j);
}
// COMMANDLINEOPTION: Linux ALSA Sound: -sndspeed <hz> chooses 44100 hz, 22100 hz, or 11025 hz sound output rate
+ rate = 44100;
if ((i=COM_CheckParm("-sndspeed")) != 0)
{
- rate = atoi(com_argv[i+1]);
- if (rate!=44100 && rate!=22050 && rate!=11025)
- {
+ j = atoi(com_argv[i+1]);
+ if (j >= 1)
+ rate = j;
+ else
Con_Printf("Error: invalid sample rate: %d\n", rate);
- return false;
- }
}
+ for (channels = 8;channels >= 1;channels--)
+ {
+ if ((channels & 1) && channels != 1)
+ continue;
// COMMANDLINEOPTION: Linux ALSA Sound: -sndmono sets sound output to mono
- if ((i=COM_CheckParm("-sndmono")) != 0)
- stereo=0;
+ if ((i=COM_CheckParm("-sndmono")) != 0)
+ if (channels != 1)
+ continue;
// COMMANDLINEOPTION: Linux ALSA Sound: -sndstereo sets sound output to stereo
- if ((i=COM_CheckParm("-sndstereo")) != 0)
- stereo=1;
-
- err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK,
- SND_PCM_NONBLOCK);
- if (0 > err) {
- Sys_Printf ("Error: audio open error: %s\n", snd_strerror (err));
- return 0;
- }
- Sys_Printf ("ALSA: Using PCM %s.\n", pcmname);
-
- err = snd_pcm_hw_params_any (pcm, hw);
- if (0 > err) {
- Sys_Printf ("ALSA: error setting hw_params_any. %s\n",
- snd_strerror (err));
- goto error;
- }
+ if ((i=COM_CheckParm("-sndstereo")) != 0)
+ if (channels != 2)
+ continue;
- err = snd_pcm_hw_params_set_access (pcm, hw,
- SND_PCM_ACCESS_MMAP_INTERLEAVED);
- if (0 > err) {
- Sys_Printf ("ALSA: Failure to set noninterleaved PCM access. %s\n"
- "Note: Interleaved is not supported\n",
- snd_strerror (err));
- goto error;
- }
+// COMMANDLINEOPTION: Linux ALSA Sound: -sndpcm <devicename> selects which pcm device to us, default is "default"
+ if (channels == 8)
+ pcmname = "surround71";
+ else if (channels == 6)
+ pcmname = "surround51";
+ else if (channels == 4)
+ pcmname = "surround40";
+ else
+ pcmname = "default";
+ if ((i=COM_CheckParm("-sndpcm"))!=0)
+ pcmname = com_argv[i+1];
+
+ Con_Printf ("ALSA: Trying PCM %s.\n", pcmname);
+
+ err = snd_pcm_open (&pcm, pcmname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
+ if (0 > err)
+ {
+ Con_Printf ("Error: audio open error: %s\n", snd_strerror (err));
+ continue;
+ }
- switch (bps) {
- case -1:
- err = snd_pcm_hw_params_set_format (pcm, hw,
- SND_PCM_FORMAT_S16);
- if (0 <= err) {
- bps = 16;
- } else if (0 <= (err = snd_pcm_hw_params_set_format (pcm, hw,
- SND_PCM_FORMAT_U8))) {
- bps = 8;
- } else {
- Sys_Printf ("ALSA: no useable formats. %s\n",
- snd_strerror (err));
- goto error;
- }
- break;
- case 8:
- case 16:
- err = snd_pcm_hw_params_set_format (pcm, hw, bps == 8 ?
- SND_PCM_FORMAT_U8 :
- SND_PCM_FORMAT_S16);
- if (0 > err) {
- Sys_Printf ("ALSA: no usable formats. %s\n",
- snd_strerror (err));
- goto error;
- }
- break;
- default:
- Sys_Printf ("ALSA: desired format not supported\n");
- goto error;
- }
+ err = snd_pcm_hw_params_any (pcm, hw);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: error setting hw_params_any. %s\n", snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
- switch (stereo) {
- case -1:
- err = snd_pcm_hw_params_set_channels (pcm, hw, 2);
- if (0 <= err) {
- stereo = 1;
- } else if (0 <= (err = snd_pcm_hw_params_set_channels (pcm, hw,
- 1))) {
- stereo = 0;
- } else {
- Sys_Printf ("ALSA: no usable channels. %s\n",
- snd_strerror (err));
- goto error;
- }
- break;
- case 0:
- case 1:
- err = snd_pcm_hw_params_set_channels (pcm, hw, stereo ? 2 : 1);
- if (0 > err) {
- Sys_Printf ("ALSA: no usable channels. %s\n",
- snd_strerror (err));
- goto error;
- }
- break;
- default:
- Sys_Printf ("ALSA: desired channels not supported\n");
- goto error;
- }
+ err = snd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: Failure to set interleaved mmap PCM access. %s\n", snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
- switch (rate) {
- case 0:
- rate = 44100;
- err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
- if (0 <= err) {
- frag_size = 32 * bps;
- } else {
- rate = 22050;
- err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
- if (0 <= err) {
- frag_size = 16 * bps;
- } else {
- rate = 11025;
- err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate,
- 0);
- if (0 <= err) {
- frag_size = 8 * bps;
- } else {
- Sys_Printf ("ALSA: no usable rates. %s\n",
- snd_strerror (err));
- goto error;
- }
- }
- }
- break;
- case 11025:
- case 22050:
- case 44100:
- err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
- if (0 > err) {
- Sys_Printf ("ALSA: desired rate %i not supported. %s\n", rate,
- snd_strerror (err));
- goto error;
- }
- frag_size = 8 * bps * rate / 11025;
- break;
- default:
- Sys_Printf ("ALSA: desired rate %i not supported.\n", rate);
- goto error;
- }
+ err = snd_pcm_hw_params_set_format (pcm, hw, width == 1 ? SND_PCM_FORMAT_U8 : SND_PCM_FORMAT_S16);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: desired bits %i not supported by driver. %s\n", width * 8, snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
- err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0);
- if (0 > err) {
- Sys_Printf ("ALSA: unable to set period size near %i. %s\n",
- (int) frag_size, snd_strerror (err));
- goto error;
- }
- err = snd_pcm_hw_params (pcm, hw);
- if (0 > err) {
- Sys_Printf ("ALSA: unable to install hw params: %s\n",
- snd_strerror (err));
- goto error;
- }
- err = snd_pcm_sw_params_current (pcm, sw);
- if (0 > err) {
- Sys_Printf ("ALSA: unable to determine current sw params. %s\n",
- snd_strerror (err));
- goto error;
- }
- err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);
- if (0 > err) {
- Sys_Printf ("ALSA: unable to set playback threshold. %s\n",
- snd_strerror (err));
- goto error;
- }
- err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);
- if (0 > err) {
- Sys_Printf ("ALSA: unable to set playback stop threshold. %s\n",
- snd_strerror (err));
- goto error;
- }
- err = snd_pcm_sw_params (pcm, sw);
- if (0 > err) {
- Sys_Printf ("ALSA: unable to install sw params. %s\n",
- snd_strerror (err));
- goto error;
- }
+ err = snd_pcm_hw_params_set_channels (pcm, hw, channels);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: no usable channels. %s\n", snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
- shm->format.channels = stereo + 1;
- shm->samplepos = 0;
- shm->format.width = bps / 8;
+ err = snd_pcm_hw_params_set_rate_near (pcm, hw, &rate, 0);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: desired rate %i not supported by driver. %s\n", rate, snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
- err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size);
- if (0 > err) {
- Sys_Printf ("ALSA: unable to get buffer size. %s\n",
- snd_strerror (err));
- goto error;
- }
+ frag_size = 64 * width * rate / 11025;
+ err = snd_pcm_hw_params_set_period_size_near (pcm, hw, &frag_size, 0);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: unable to set period size near %i. %s\n", (int) frag_size, snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
+ err = snd_pcm_hw_params (pcm, hw);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: unable to install hw params: %s\n", snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
+ err = snd_pcm_sw_params_current (pcm, sw);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: unable to determine current sw params. %s\n", snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
+ err = snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: unable to set playback threshold. %s\n", snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
+ err = snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: unable to set playback stop threshold. %s\n", snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
+ err = snd_pcm_sw_params (pcm, sw);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: unable to install sw params. %s\n", snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
- shm->samples = buffer_size * shm->format.channels; // mono samples in buffer
- shm->format.speed = rate;
- SNDDMA_GetDMAPos (); // sets shm->buffer
+ err = snd_pcm_hw_params_get_buffer_size (hw, &buffer_size);
+ if (0 > err)
+ {
+ Con_Printf ("ALSA: unable to get buffer size. %s\n", snd_strerror (err));
+ snd_pcm_close (pcm);
+ continue;
+ }
- snd_inited = 1;
- return true;
+ memset( (void*) shm, 0, sizeof(*shm) );
+ shm->format.channels = channels;
+ shm->format.width = width;
+ shm->format.speed = rate;
+ shm->samplepos = 0;
+ shm->sampleframes = buffer_size;
+ shm->samples = shm->sampleframes * shm->format.channels;
+ SNDDMA_GetDMAPos (); // sets shm->buffer
-error:
- snd_pcm_close (pcm);
+ return true;
+ }
return false;
}
{
const snd_pcm_channel_area_t *areas;
snd_pcm_uframes_t offset;
- snd_pcm_uframes_t nframes = shm->samples/shm->format.channels;
+ snd_pcm_uframes_t nframes = shm->sampleframes;
- if (!snd_inited)
+ if (!shm)
return 0;
snd_pcm_avail_update (pcm);
offset *= shm->format.channels;
nframes *= shm->format.channels;
shm->samplepos = offset;
- shm->buffer = areas->addr;
+ shm->buffer = (unsigned char *)areas->addr;
return shm->samplepos;
}
void SNDDMA_Shutdown (void)
{
- if (snd_inited) {
- snd_pcm_close (pcm);
- snd_inited = 0;
- }
+ snd_pcm_close (pcm);
}
/*