2 * Copyright (c) 2006-2011 Ed Schouten <ed@80386.nl>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * @file audio_output_alsa.c
28 * @brief ALSA audio output driver.
33 #include <alsa/asoundlib.h>
35 #include "audio_file.h"
36 #include "audio_output.h"
41 * @brief Handle to the audio device obtained from ALSA.
43 static snd_pcm_t
*devhnd
;
45 * @brief the amount of channels that is currently used for playback.
47 static unsigned int channels
= 0;
49 * @brief the sample rate that is currently used for playback.
51 static unsigned int srate
= 0;
54 * @brief Handle to the ALSA mixer.
56 static snd_mixer_t
*mixhnd
= NULL
;
58 * @brief ALSA mixer element for the audio device.
60 static snd_mixer_elem_t
*elem
;
61 #endif /* BUILD_VOLUME */
64 * @brief Alter the audio output parameters of the audio output device.
67 audio_output_apply_hwparams(void)
69 snd_pcm_hw_params_t
*devparam
;
71 snd_pcm_hw_params_alloca(&devparam
);
73 /* Acquire the standard parameters */
74 if (snd_pcm_hw_params_any(devhnd
, devparam
) != 0)
77 /* Set the access method - XXX: mmap */
78 if (snd_pcm_hw_params_set_access(devhnd
, devparam
,
79 SND_PCM_ACCESS_RW_INTERLEAVED
) != 0)
83 if (snd_pcm_hw_params_set_format(devhnd
, devparam
,
84 SND_PCM_FORMAT_S16
) != 0)
87 if (snd_pcm_hw_params_set_rate_near(devhnd
, devparam
, &srate
, NULL
) != 0)
90 if (snd_pcm_hw_params_set_channels(devhnd
, devparam
, channels
) != 0)
93 /* Drain current data and make sure we aren't underrun */
94 snd_pcm_drain(devhnd
);
97 if (snd_pcm_hw_params(devhnd
, devparam
) != 0)
105 * @brief Open the ALSA mixer device, making it possible to adjust the
109 audio_output_volume_open(void)
111 snd_mixer_selem_id_t
*mixer
;
113 /* Open mixer device */
114 if (snd_mixer_open(&mixhnd
, 0) != 0)
116 if (snd_mixer_attach(mixhnd
,
117 config_getopt("audio.output.alsa.device")) != 0)
119 if (snd_mixer_selem_register(mixhnd
, NULL
, NULL
) < 0)
121 if (snd_mixer_load(mixhnd
) < 0)
124 /* Obtain mixer element */
125 snd_mixer_selem_id_alloca(&mixer
);
126 snd_mixer_selem_id_set_name(mixer
, config_getopt("audio.output.alsa.mixer"));
128 elem
= snd_mixer_find_selem(mixhnd
, mixer
);
132 snd_mixer_close(mixhnd
);
135 #endif /* BUILD_VOLUME */
138 audio_output_open(void)
140 /* Open the device */
141 if (snd_pcm_open(&devhnd
, config_getopt("audio.output.alsa.device"),
142 SND_PCM_STREAM_PLAYBACK
, 0) != 0)
146 audio_output_volume_open();
147 #endif /* BUILD_VOLUME */
151 g_printerr("%s\n", _("Cannot open the audio device."));
156 audio_output_play(struct audio_file
*fd
)
159 snd_pcm_sframes_t ret
, len
, done
= 0;
161 if ((len
= audio_file_read(fd
, buf
, sizeof buf
/ sizeof(int16_t))) == 0)
164 if (fd
->channels
!= channels
|| fd
->srate
!= srate
) {
165 /* Apply the new values */
166 channels
= fd
->channels
;
168 if (audio_output_apply_hwparams() != 0) {
169 gui_msgbar_warn(_("Sample rate or amount of channels not supported."));
170 /* Invalidate the old settings */
176 /* ALSA measures in sample lengths */
179 /* Our complex error handling for snd_pcm_writei() */
181 ret
= snd_pcm_writei(devhnd
, buf
+ (done
* fd
->channels
), len
- done
);
183 /* Buffer underrun. Try again. */
184 if (snd_pcm_prepare(devhnd
) != 0)
187 } else if (ret
<= 0) {
188 /* Some other strange error. */
199 audio_output_close(void)
201 snd_pcm_close(devhnd
);
204 snd_mixer_close(mixhnd
);
205 #endif /* BUILD_VOLUME */
210 * @brief Adjust the audio output by a certain percentage and return the
214 audio_output_volume_adjust(int n
)
223 /* Allow other applications to change mixer */
224 snd_mixer_handle_events(mixhnd
);
226 /* Obtain volume range boundaries */
227 if (snd_mixer_selem_get_playback_volume_range(elem
, &min
, &max
) != 0)
232 /* Convert delta from percent to steps */
233 delta
= (n
* (max
- min
)) / 100;
235 delta
= n
> 0 ? 1 : -1;
237 /* Obtain current volume */
238 if (snd_mixer_selem_get_playback_volume(elem
,
239 SND_MIXER_SCHN_FRONT_LEFT
, &left
) != 0)
241 if (snd_mixer_selem_get_playback_volume(elem
,
242 SND_MIXER_SCHN_FRONT_RIGHT
, &right
) != 0)
246 left
= CLAMP(left
, min
, max
);
247 right
= right
+ delta
;
248 right
= CLAMP(right
, min
, max
);
251 if (snd_mixer_selem_set_playback_volume(elem
,
252 SND_MIXER_SCHN_FRONT_LEFT
, left
) != 0)
254 if (snd_mixer_selem_set_playback_volume(elem
,
255 SND_MIXER_SCHN_FRONT_RIGHT
, right
) != 0)
258 /* Convert volume back to a percentage */
259 return (((left
+ right
) / 2 - min
) * 100) / (max
- min
);
263 audio_output_volume_up(void)
265 return audio_output_volume_adjust(4);
269 audio_output_volume_down(void)
271 return audio_output_volume_adjust(-4);
273 #endif /* BUILD_VOLUME */