4 * This work is licensed under the terms of any of these licenses
6 * - GNU GPL, version 2 or later.
7 * - GNU LGPL, version 2.1 or later.
9 * See the COPYING file in the top-level directory.
13 #include <alsa/asoundlib.h>
16 #include "sndout_alsa.h"
18 #define PFX "sndout_alsa: "
20 static snd_pcm_t *handle;
21 static snd_pcm_uframes_t buffer_size, period_size;
22 static void *silent_period;
23 static unsigned int channels;
24 static int failure_counter;
26 int sndout_alsa_init(void)
30 ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
37 int sndout_alsa_start(int rate_, int stereo)
39 snd_pcm_hw_params_t *hwparams = NULL;
40 unsigned int rate = rate_;
44 samples = rate * 40 / 1000;
45 for (shift = 8; (1 << shift) < samples; shift++)
47 period_size = 1 << shift;
48 buffer_size = 8 * period_size;
50 snd_pcm_hw_params_alloca(&hwparams);
52 ret = snd_pcm_hw_params_any(handle, hwparams);
53 ret |= snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
54 ret |= snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE);
55 ret |= snd_pcm_hw_params_set_channels(handle, hwparams, stereo ? 2 : 1);
56 ret |= snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, 0);
57 ret |= snd_pcm_hw_params_set_buffer_size_near(handle, hwparams, &buffer_size);
58 ret |= snd_pcm_hw_params_set_period_size_near(handle, hwparams, &period_size, NULL);
61 fprintf(stderr, PFX "failed to set hwparams\n");
65 ret = snd_pcm_hw_params(handle, hwparams);
67 fprintf(stderr, PFX "failed to apply hwparams: %d\n", ret);
71 snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
72 snd_pcm_hw_params_get_period_size(hwparams, &period_size, NULL);
73 snd_pcm_hw_params_get_channels(hwparams, &channels);
75 silent_period = realloc(silent_period, period_size * 2 * channels);
76 if (silent_period != NULL)
77 memset(silent_period, 0, period_size * 2 * channels);
79 ret = snd_pcm_prepare(handle);
81 fprintf(stderr, PFX "snd_pcm_prepare failed: %d\n", ret);
85 ret = snd_pcm_start(handle);
87 fprintf(stderr, PFX "snd_pcm_start failed: %d\n", ret);
96 // to flush out redirected logs
102 void sndout_alsa_stop(void)
104 int ret = snd_pcm_drop(handle);
106 fprintf(stderr, PFX "snd_pcm_drop failed: %d\n", ret);
109 void sndout_alsa_wait(void)
111 snd_pcm_sframes_t left;
115 left = snd_pcm_avail(handle);
116 if (left < 0 || left >= buffer_size / 2)
123 int sndout_alsa_write_nb(const void *samples, int len)
125 snd_pcm_sframes_t left;
132 left = snd_pcm_avail(handle);
133 if (left >= 0 && left < len)
136 ret = snd_pcm_writei(handle, samples, len);
138 ret = snd_pcm_recover(handle, ret, 1);
139 if (ret != 0 && failure_counter++ < 5)
140 fprintf(stderr, PFX "snd_pcm_recover: %d\n", ret);
143 snd_pcm_writei(handle, silent_period, period_size);
144 snd_pcm_writei(handle, samples, len);
150 void sndout_alsa_exit(void)
152 snd_pcm_close(handle);