]> de.git.xonotic.org Git - xonotic/darkplaces.git/blobdiff - snd_alsa.c
implemented 7.1 audio, only works with SDL (attempted ALSA support but ALSA doesn...
[xonotic/darkplaces.git] / snd_alsa.c
index 95310d84f2e413d9f60aaa9be6cd8859f9f40f3c..4328a603d5ba71c25e8b6675c53f58523a01584e 100644 (file)
@@ -39,232 +39,177 @@ static snd_pcm_t   *pcm;
 
 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;
+               if ((i=COM_CheckParm("-sndstereo")) != 0)
+                       if (channels != 2)
+                               continue;
 
-       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));
-               return 0;
-       }
-       Con_Printf ("ALSA: Using PCM %s.\n", pcmname);
+// 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;
+               }
 
-       err = snd_pcm_hw_params_any (pcm, hw);
-       if (0 > err) {
-               Con_Printf ("ALSA: error setting hw_params_any. %s\n",
-                                       snd_strerror (err));
-               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;
+               }
 
-       err = snd_pcm_hw_params_set_access (pcm, hw,
-                                                                                 SND_PCM_ACCESS_MMAP_INTERLEAVED);
-       if (0 > err) {
-               Con_Printf ("ALSA: Failure to set noninterleaved PCM access. %s\n"
-                                       "Note: Interleaved is not supported\n",
-                                       snd_strerror (err));
-               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 (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 {
-                               Con_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) {
-                               Con_Printf ("ALSA: no usable formats. %s\n",
-                                                       snd_strerror (err));
-                               goto error;
-                       }
-                       break;
-               default:
-                       Con_Printf ("ALSA: desired format not supported\n");
-                       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;
+               }
 
-       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 {
-                               Con_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) {
-                               Con_Printf ("ALSA: no usable channels. %s\n",
-                                                       snd_strerror (err));
-                               goto error;
-                       }
-                       break;
-               default:
-                       Con_Printf ("ALSA: desired channels not supported\n");
-                       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;
+               }
 
-       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 {
-                                               Con_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) {
-                               Con_Printf ("ALSA: desired rate %i not supported. %s\n", rate,
-                                                       snd_strerror (err));
-                               goto error;
-                       }
-                       frag_size = 8 * bps * rate / 11025;
-                       break;
-               default:
-                       Con_Printf ("ALSA: desired rate %i not supported.\n", rate);
-                       goto error;
-       }
+               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_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));
-               goto error;
-       }
-       err = snd_pcm_hw_params (pcm, hw);
-       if (0 > err) {
-               Con_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) {
-               Con_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) {
-               Con_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) {
-               Con_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) {
-               Con_Printf ("ALSA: unable to install sw params. %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->format.channels = stereo + 1;
-       shm->samplepos = 0;
-       shm->format.width = bps / 8;
+               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;
+               }
 
-       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));
-               goto error;
+               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
+
+               snd_inited = 1;
+               return true;
        }
-
-       shm->samples = buffer_size * shm->format.channels;              // mono samples in buffer
-       shm->format.speed = rate;
-       SNDDMA_GetDMAPos ();            // sets shm->buffer
-
-       snd_inited = 1;
-       return true;
-
-error:
-       snd_pcm_close (pcm);
        return false;
 }
 
@@ -272,7 +217,7 @@ int SNDDMA_GetDMAPos (void)
 {
        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)
                return 0;