2009-10-09 Chris Toshok <toshok@ximian.com>
[moon.git] / src / audio-alsa.cpp
bloba4489198bc4355c0e2f578e0a3d5105e268b2df0
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * audio-alsa.cpp:
5 * Contact:
6 * Moonlight List (moonlight-list@lists.ximian.com)
8 * Copyright 2008 Novell, Inc. (http://www.novell.com)
10 * See the LICENSE file included with the distribution for details.
13 #include <config.h>
15 #include <dlfcn.h>
17 #include "audio-alsa.h"
18 #include "runtime.h"
19 #include "clock.h"
20 #include "debug.h"
22 typedef int (dyn_snd_pcm_open) (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
23 typedef int (dyn_snd_pcm_close) (snd_pcm_t *pcm);
24 typedef int (dyn_snd_pcm_get_params) (snd_pcm_t *pcm, snd_pcm_uframes_t *buffer_size, snd_pcm_uframes_t *period_size);
25 typedef int (dyn_snd_pcm_poll_descriptors_count) (snd_pcm_t *pcm);
26 typedef int (dyn_snd_pcm_poll_descriptors) (snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
27 typedef int (dyn_snd_output_stdio_attach) (snd_output_t **outputp, FILE *fp, int _close);
28 typedef int (dyn_snd_pcm_hw_params_malloc) (snd_pcm_hw_params_t **ptr);
29 typedef int (dyn_snd_pcm_hw_params_any) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
30 typedef int (dyn_snd_pcm_hw_params_dump) (snd_pcm_hw_params_t *params, snd_output_t *out);
31 typedef int (dyn_snd_pcm_hw_params_set_rate_resample) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
32 typedef int (dyn_snd_pcm_hw_params_test_access) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access);
33 typedef int (dyn_snd_pcm_hw_params_set_access) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access);
34 typedef int (dyn_snd_pcm_hw_params_set_format) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val);
35 typedef int (dyn_snd_pcm_hw_params_set_channels) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
36 typedef int (dyn_snd_pcm_hw_params_set_rate_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
37 typedef int (dyn_snd_pcm_hw_params_set_buffer_time_near) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
38 typedef int (dyn_snd_pcm_hw_params) (snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
39 typedef int (dyn_snd_pcm_hw_params_can_pause) (const snd_pcm_hw_params_t *params);
40 typedef void (dyn_snd_pcm_hw_params_free) (snd_pcm_hw_params_t *obj);
41 typedef snd_pcm_state_t (dyn_snd_pcm_state) (snd_pcm_t *pcm);
42 typedef const char * (dyn_snd_pcm_state_name) (const snd_pcm_state_t state);
43 typedef int (dyn_snd_pcm_drop) (snd_pcm_t *pcm);
44 typedef snd_pcm_sframes_t (dyn_snd_pcm_writei) (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
45 typedef int (dyn_snd_pcm_mmap_begin) (snd_pcm_t *pcm, const snd_pcm_channel_area_t **areas, snd_pcm_uframes_t *offset, snd_pcm_uframes_t *frames);
46 typedef snd_pcm_sframes_t (dyn_snd_pcm_mmap_commit) (snd_pcm_t *pcm, snd_pcm_uframes_t offset, snd_pcm_uframes_t frames);
47 typedef int (dyn_snd_pcm_prepare) (snd_pcm_t *pcm);
48 typedef int (dyn_snd_pcm_resume) (snd_pcm_t *pcm);
49 typedef snd_pcm_sframes_t (dyn_snd_pcm_avail_update) (snd_pcm_t *pcm);
50 typedef int (dyn_snd_pcm_start) (snd_pcm_t *pcm);
51 typedef int (dyn_snd_pcm_delay) (snd_pcm_t *pcm, snd_pcm_sframes_t *delayp);
52 typedef const char * (dyn_snd_strerror) (int errnum);
53 typedef const char * (dyn_snd_asoundlib_version) (void);
55 dyn_snd_pcm_open * d_snd_pcm_open = NULL;
56 dyn_snd_pcm_close * d_snd_pcm_close = NULL;
57 dyn_snd_pcm_get_params * d_snd_pcm_get_params = NULL;
58 dyn_snd_pcm_poll_descriptors_count * d_snd_pcm_poll_descriptors_count = NULL;
59 dyn_snd_pcm_poll_descriptors * d_snd_pcm_poll_descriptors = NULL;
60 dyn_snd_output_stdio_attach * d_snd_output_stdio_attach = NULL;
61 dyn_snd_strerror * d_snd_strerror = NULL;
62 dyn_snd_pcm_hw_params_malloc * d_snd_pcm_hw_params_malloc = NULL;
63 dyn_snd_pcm_hw_params_any * d_snd_pcm_hw_params_any = NULL;
64 dyn_snd_pcm_hw_params_dump * d_snd_pcm_hw_params_dump = NULL;
65 dyn_snd_pcm_hw_params_set_rate_resample * d_snd_pcm_hw_params_set_rate_resample = NULL;
66 dyn_snd_pcm_hw_params_test_access * d_snd_pcm_hw_params_test_access = NULL;
67 dyn_snd_pcm_hw_params_set_access * d_snd_pcm_hw_params_set_access = NULL;
68 dyn_snd_pcm_hw_params_set_format * d_snd_pcm_hw_params_set_format = NULL;
69 dyn_snd_pcm_hw_params_set_channels * d_snd_pcm_hw_params_set_channels = NULL;
70 dyn_snd_pcm_hw_params_set_rate_near * d_snd_pcm_hw_params_set_rate_near = NULL;
71 dyn_snd_pcm_hw_params_set_buffer_time_near * d_snd_pcm_hw_params_set_buffer_time_near = NULL;
72 dyn_snd_pcm_hw_params * d_snd_pcm_hw_params = NULL;
73 dyn_snd_pcm_hw_params_can_pause * d_snd_pcm_hw_params_can_pause = NULL;
74 dyn_snd_pcm_hw_params_free * d_snd_pcm_hw_params_free = NULL;
75 dyn_snd_pcm_state * d_snd_pcm_state = NULL;
76 dyn_snd_pcm_state_name * d_snd_pcm_state_name = NULL;
77 dyn_snd_pcm_drop * d_snd_pcm_drop = NULL;
78 dyn_snd_pcm_writei * d_snd_pcm_writei = NULL;
79 dyn_snd_pcm_mmap_begin * d_snd_pcm_mmap_begin = NULL;
80 dyn_snd_pcm_mmap_commit * d_snd_pcm_mmap_commit = NULL;
81 dyn_snd_pcm_prepare * d_snd_pcm_prepare = NULL;
82 dyn_snd_pcm_resume * d_snd_pcm_resume = NULL;
83 dyn_snd_pcm_avail_update * d_snd_pcm_avail_update = NULL;
84 dyn_snd_pcm_start * d_snd_pcm_start = NULL;
85 dyn_snd_pcm_delay * d_snd_pcm_delay = NULL;
86 dyn_snd_asoundlib_version * d_snd_asoundlib_version = NULL;
88 #define snd_pcm_open d_snd_pcm_open
89 #define snd_pcm_close d_snd_pcm_close
90 #define snd_pcm_get_params d_snd_pcm_get_params
91 #define snd_pcm_poll_descriptors_count d_snd_pcm_poll_descriptors_count
92 #define snd_pcm_poll_descriptors d_snd_pcm_poll_descriptors
93 #define snd_output_stdio_attach d_snd_output_stdio_attach
94 #define snd_strerror d_snd_strerror
95 #define snd_pcm_hw_params_malloc d_snd_pcm_hw_params_malloc
96 #define snd_pcm_hw_params_any d_snd_pcm_hw_params_any
97 #define snd_pcm_hw_params_dump d_snd_pcm_hw_params_dump
98 #define snd_pcm_hw_params_set_rate_resample d_snd_pcm_hw_params_set_rate_resample
99 #define snd_pcm_hw_params_test_access d_snd_pcm_hw_params_test_access
100 #define snd_pcm_hw_params_test_access d_snd_pcm_hw_params_test_access
101 #define snd_pcm_hw_params_set_access d_snd_pcm_hw_params_set_access
102 #define snd_pcm_hw_params_set_format d_snd_pcm_hw_params_set_format
103 #define snd_pcm_hw_params_set_channels d_snd_pcm_hw_params_set_channels
104 #define snd_pcm_hw_params_set_rate_near d_snd_pcm_hw_params_set_rate_near
105 #define snd_pcm_hw_params_set_buffer_time_near d_snd_pcm_hw_params_set_buffer_time_near
106 #define snd_pcm_hw_params d_snd_pcm_hw_params
107 #define snd_pcm_hw_params_can_pause d_snd_pcm_hw_params_can_pause
108 #define snd_pcm_hw_params_free d_snd_pcm_hw_params_free
109 #define snd_pcm_state d_snd_pcm_state
110 #define snd_pcm_state_name d_snd_pcm_state_name
111 #define snd_pcm_drop d_snd_pcm_drop
112 #define snd_pcm_writei d_snd_pcm_writei
113 #define snd_pcm_mmap_begin d_snd_pcm_mmap_begin
114 #define snd_pcm_mmap_commit d_snd_pcm_mmap_commit
115 #define snd_pcm_prepare d_snd_pcm_prepare
116 #define snd_pcm_resume d_snd_pcm_resume
117 #define snd_pcm_avail_update d_snd_pcm_avail_update
118 #define snd_pcm_start d_snd_pcm_start
119 #define snd_pcm_delay d_snd_pcm_delay
120 #define snd_asoundlib_version d_snd_asoundlib_version
123 * AlsaSource
126 AlsaSource::AlsaSource (AlsaPlayer *player, MediaPlayer *mplayer, AudioStream *stream)
127 : AudioSource (player, mplayer, stream), mutex (true)
129 LOG_ALSA ("AlsaSource::AlsaSource (%p, %p)\n", player, stream);
131 this->player = player;
133 pcm = NULL;
134 period_size = 0;
135 buffer_size = 0;
137 mmap = false;
138 udfs = NULL;
139 ndfs = 0;
141 started = false;
142 drop_pending = false;
143 initialized = false;
146 AlsaSource::~AlsaSource ()
148 LOG_ALSA ("AlsaSource::~AlsaSource ()\n");
150 CloseAlsa ();
153 bool
154 AlsaSource::InitializeInternal ()
156 LOG_AUDIO ("AlsaSource::InitializeInternal ()\n");
157 // this is a no-op, initialization is done when needed.
158 return true;
161 bool
162 AlsaSource::InitializeAlsa ()
164 bool res = false;
165 int result;
166 AudioStream *stream = NULL;
168 LOG_AUDIO ("AlsaSource::InitializeAlsa (%p) initialized: %i\n", this, initialized);
170 mutex.Lock ();
172 if (initialized) {
173 result = true;
174 goto cleanup;
177 stream = GetStreamReffed ();
179 if (stream == NULL) {
180 // Shouldn't really happen, but handle this case anyway.
181 LOG_AUDIO ("AlsaSource::Initialize (): trying to initialize an audio device, but there's no audio to play.\n");
182 goto cleanup;
185 // Open a pcm device
186 result = snd_pcm_open (&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0 /*SND_PCM_NONBLOCK*/);
187 if (result != 0) {
188 LOG_AUDIO ("AlsaSource::Initialize (): cannot open audio device: %s\n", snd_strerror (result));
189 pcm = NULL;
190 goto cleanup;
193 // Configure the hardware
194 if (!SetupHW ()) {
195 LOG_AUDIO ("AlsaSource::Initialize (): could not configure hardware for audio playback\n");
196 Close ();
197 goto cleanup;
200 result = snd_pcm_get_params (pcm, &buffer_size, &period_size);
201 if (result != 0) {
202 LOG_AUDIO ("AlsaSource::Initialize (): error while getting parameters: %s\n", snd_strerror (result));
203 Close ();
204 goto cleanup;
207 // Get the file descriptors to poll on
208 ndfs = snd_pcm_poll_descriptors_count (pcm);
209 if (ndfs <= 0) {
210 LOG_AUDIO ("AlsaSource::Initialize(): Unable to initialize audio for playback (could not get poll descriptor count).\n");
211 Close ();
212 goto cleanup;
215 udfs = (pollfd *) g_malloc0 (sizeof (pollfd) * ndfs);
216 if (snd_pcm_poll_descriptors (pcm, udfs, ndfs) < 0) {
217 LOG_AUDIO ("AlsaSource::Initialize (): Unable to initialize audio for playback (could not get poll descriptors).\n");
218 Close ();
219 goto cleanup;
222 LOG_AUDIO ("AlsaSource::Initialize (%p): Succeeded. Buffer size: %lu, period size: %lu\n", this, buffer_size, period_size);
224 res = true;
225 initialized = true;
227 cleanup:
229 mutex.Unlock ();
231 if (stream)
232 stream->unref ();
234 return res;
237 bool
238 AlsaSource::SetupHW ()
240 bool result = false;
241 bool rw_available = false;
242 bool mmap_available = false;
243 #if DEBUG
244 bool debug = debug_flags & RUNTIME_DEBUG_AUDIO;
245 #else
246 bool debug = false;
247 #endif
249 snd_pcm_hw_params_t *params = NULL;
250 snd_output_t *output = NULL;
251 guint32 buffer_time = 100000; // request 0.1 seconds of buffer time.
252 int err = 0;
253 int dir = 0;
254 unsigned int rate = GetSampleRate ();
255 unsigned int actual_rate = rate;
256 guint32 channels = GetChannels ();
258 if (debug) {
259 err = snd_output_stdio_attach (&output, stdout, 0);
260 if (err < 0)
261 LOG_AUDIO ("AlsaSource::SetupHW (): Could not create alsa output: %s\n", snd_strerror (err));
264 err = snd_pcm_hw_params_malloc (&params);
265 if (err < 0) {
266 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (malloc): %s\n", snd_strerror (err));
267 return false;
270 // choose all parameters
271 err = snd_pcm_hw_params_any (pcm, params);
272 if (err < 0) {
273 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (no configurations available): %s\n", snd_strerror (err));
274 goto cleanup;
277 if (debug && output != NULL) {
278 LOG_AUDIO ("AlsaSource::SetupHW (): hw configurations:\n");
279 snd_pcm_hw_params_dump (params, output);
282 // enable software resampling
283 err = snd_pcm_hw_params_set_rate_resample (pcm, params, 1);
284 if (err < 0) {
285 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (could not enable resampling): %s\n", snd_strerror (err));
286 goto cleanup;
289 // test for available transfer modes
290 if (!(moonlight_flags & RUNTIME_INIT_AUDIO_ALSA_MMAP)) {
291 err = snd_pcm_hw_params_test_access (pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED);
292 if (err < 0) {
293 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup: RW access mode not supported (%s).\n", snd_strerror (err));
294 } else {
295 rw_available = true;
298 if (!(moonlight_flags & RUNTIME_INIT_AUDIO_ALSA_RW)) {
299 err = snd_pcm_hw_params_test_access (pcm, params, SND_PCM_ACCESS_MMAP_INTERLEAVED);
300 if (err < 0) {
301 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup: MMAP access mode not supported (%s).\n", snd_strerror (err));
302 } else {
303 mmap_available = true;
306 if (mmap_available) {
307 mmap = true;
308 } else if (rw_available) {
309 mmap = false;
310 } else {
311 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed, no available access mode\n");
312 goto cleanup;
315 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup: using %s access mode.\n", mmap ? "MMAP" : "RW");
317 // set transfer mode (mmap or rw in our case)
318 err = snd_pcm_hw_params_set_access (pcm, params, mmap ? SND_PCM_ACCESS_MMAP_INTERLEAVED : SND_PCM_ACCESS_RW_INTERLEAVED);
319 if (err < 0) {
320 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (access type not available for playback): %s\n", snd_strerror (err));
321 goto cleanup;
324 // set audio format
325 switch (GetInputBytesPerSample ()) {
326 case 2: // 16 bit audio
327 err = snd_pcm_hw_params_set_format (pcm, params, SND_PCM_FORMAT_S16);
328 SetOutputBytesPerSample (2);
329 break;
330 case 3: // 24 bit audio
331 // write as 32 bit audio, this is a lot easier to write to than 24 bit.
332 err = snd_pcm_hw_params_set_format (pcm, params, SND_PCM_FORMAT_S32);
333 SetOutputBytesPerSample (4);
334 break;
335 default:
336 LOG_AUDIO ("AlsaSource::SetupHW (): Invalid input bytes per sample, expected 2 or 3, got %i\n", GetInputBytesPerSample ());
337 goto cleanup;
340 if (err < 0) {
341 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (sample format not available for playback): %s\n", snd_strerror (err));
342 goto cleanup;
345 // set channel count
346 err = snd_pcm_hw_params_set_channels (pcm, params, channels);
347 if (err < 0) {
348 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (channels count %i not available for playback): %s\n", channels, snd_strerror (err));
349 goto cleanup;
352 // set sample rate
353 err = snd_pcm_hw_params_set_rate_near (pcm, params, &actual_rate, 0);
354 if (err < 0) {
355 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (sample rate %i Hz not available for playback): %s\n", rate, snd_strerror (err));
356 goto cleanup;
357 } else if (actual_rate != rate) {
358 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (sample rate %i Hz not available for playback, only got %i Hz).\n", rate, actual_rate);
359 goto cleanup;
362 // set the buffer time
363 err = snd_pcm_hw_params_set_buffer_time_near (pcm, params, &buffer_time, &dir);
364 if (err < 0) {
365 LOG_AUDIO ("AudioNode::SetupHW (): Audio HW setup failed (unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror (err));
366 goto cleanup;
369 // write the parameters to device
370 err = snd_pcm_hw_params (pcm, params);
371 if (err < 0) {
372 LOG_AUDIO ("AlsaSource::SetupHW (): Audio HW setup failed (unable to set hw params for playback: %s)\n", snd_strerror (err));
373 if (debug && output != NULL) {
374 LOG_AUDIO ("AlsaSource::SetupHW (): current hw configurations:\n");
375 snd_pcm_hw_params_dump (params, output);
377 goto cleanup;
380 if (debug) {
381 LOG_AUDIO ("AlsaSource::SetupHW (): hardware pause support: %s\n", snd_pcm_hw_params_can_pause (params) == 0 ? "no" : "yes");
382 LOG_AUDIO ("AlsaSource::SetupHW (): succeeded\n");
383 if (output != NULL)
384 snd_pcm_hw_params_dump (params, output);
387 result = true;
389 cleanup:
390 snd_pcm_hw_params_free (params);
392 return result;
395 void
396 AlsaSource::StateChanged (AudioState old_state)
398 if (GetState () == AudioPlaying)
399 InitializeAlsa ();
400 player->UpdatePollList ();
403 void
404 AlsaSource::Played ()
406 InitializeAlsa ();
407 player->UpdatePollList ();
410 void
411 AlsaSource::Paused ()
413 mutex.Lock ();
414 drop_pending = true;
415 mutex.Unlock ();
418 void
419 AlsaSource::Stopped ()
421 CloseAlsa ();
422 mutex.Lock ();
423 drop_pending = true;
424 mutex.Unlock ();
427 void
428 AlsaSource::DropAlsa ()
430 int err;
432 LOG_ALSA ("AlsaSource::DropAlsa ()\n");
434 mutex.Lock ();
435 drop_pending = false;
437 if (pcm != NULL && snd_pcm_state (pcm) == SND_PCM_STATE_RUNNING) {
438 err = snd_pcm_drop (pcm);
439 if (err < 0)
440 LOG_AUDIO ("AlsaSource::DropAlsa (): Could not stop/drain pcm: %s\n", snd_strerror (err));
442 mutex.Unlock ();
445 void
446 AlsaSource::CloseInternal ()
448 CloseAlsa ();
451 void
452 AlsaSource::CloseAlsa ()
454 mutex.Lock ();
455 if (pcm != NULL) {
456 snd_pcm_close (pcm);
457 pcm = NULL;
460 g_free (udfs);
461 udfs = NULL;
463 initialized = false;
464 started = false;
465 mutex.Unlock ();
468 bool
469 AlsaSource::WriteRW ()
471 snd_pcm_sframes_t avail;
472 snd_pcm_sframes_t commitres = 0;
473 guint32 frames;
474 void *buffer;
475 int err = 0;
477 if (GetState () != AudioPlaying) {
478 LOG_ALSA ("AlsaSource::WriteRW (): trying to write when we're not playing (state: %i)\n", GetState ());
479 return false;
482 if (!PreparePcm (&avail))
483 return false;
485 LOG_ALSA ("AlsaSource::WriteRW (): entering play loop, avail: %lld, sample size: %i\n", (gint64) avail, (int) period_size);
487 buffer = g_malloc (avail * GetOutputBytesPerFrame ());
489 frames = Write (buffer, (guint32) avail);
491 mutex.Lock ();
492 if (initialized)
493 commitres = snd_pcm_writei (pcm, buffer, frames);
494 mutex.Unlock ();
496 g_free (buffer);
498 LOG_ALSA ("AlsaSource::WriteRW (): played %i samples, of %i available samples, result: %i.\n", (int) frames, (int) avail, (int) commitres);
500 if (commitres < 0 || (snd_pcm_uframes_t) commitres != frames) {
501 if (commitres == -EAGAIN)
502 LOG_AUDIO ("AlsaSource::WriteRW (): not enough space for all the data\n");
503 if (!XrunRecovery (commitres >= 0 ? -EPIPE : commitres)) {
504 LOG_AUDIO ("AudioPlayer: could not write audio data: %s, commitres: %li, frames: %u\n", snd_strerror(err), commitres, frames);
505 return false;
507 started = false;
510 return frames != 0;
513 bool
514 AlsaSource::WriteMmap ()
516 snd_pcm_channel_area_t *areas = NULL;
517 snd_pcm_uframes_t offset = 0;
518 snd_pcm_uframes_t frames;
519 snd_pcm_sframes_t available_samples;
520 snd_pcm_sframes_t commitres = 0;
521 guint32 channels = GetChannels ();
522 int err = 0;
523 AudioData *data [channels + 1];
525 if (GetState () != AudioPlaying) {
526 LOG_ALSA ("AlsaSource::WriteMmap (): trying to write when we're not playing (state: %i)\n", GetState ());
527 return false;
530 if (!PreparePcm (&available_samples))
531 return false;
533 if (GetFlag (AudioEnded)) {
534 Underflowed ();
535 return false;
538 LOG_ALSA_EX ("AlsaSource::WriteMmap (): entering play loop, avail: %lld, sample size: %i\n", (gint64) available_samples, (int) period_size);
540 frames = available_samples;
542 mutex.Lock ();
543 if (!initialized)
544 goto cleanup;
546 err = snd_pcm_mmap_begin (pcm, (const snd_pcm_channel_area_t** ) &areas, &offset, &frames);
547 if (err < 0) {
548 if (!XrunRecovery (err)) {
549 LOG_AUDIO ("AudioPlayer: could not get mmapped memory: %s\n", snd_strerror (err));
550 goto cleanup;
552 started = false;
555 LOG_ALSA_EX ("AlsaSource::WriteMmap (): can write %lu frames, avail: %lu\n", frames, available_samples);
557 for (guint32 channel = 0; channel < channels; channel++) {
558 data [channel] = (AudioData *) g_malloc (sizeof (AudioData));
559 // pointer to the first sample to write to
560 data [channel]->dest = ((gint8 *) areas [channel].addr) + (areas [channel].first / 8) + offset * areas [channel].step / 8;
561 // distance (in bytes) between samples
562 data [channel]->distance = areas [channel].step / 8;
564 data [channels] = NULL;
566 frames = WriteFull (data, frames);
568 for (guint32 channel = 0; channel < channels; channel++) {
569 g_free (data [channel]);
572 commitres = snd_pcm_mmap_commit (pcm, offset, frames);
574 LOG_ALSA_EX ("AlsaSource::WriteMmap (): played %i samples, of %i available samples, result: %i.\n", (int) frames, (int) 0, (int) commitres);
576 if (commitres < 0 || (snd_pcm_uframes_t) commitres != frames) {
577 if (!XrunRecovery (commitres >= 0 ? -EPIPE : commitres)) {
578 LOG_AUDIO ("AudioPlayer: could not commit mmapped memory: %s\n", snd_strerror(err));
579 commitres = 0; // so that we end up returning false
580 goto cleanup;
582 started = false;
585 cleanup:
587 mutex.Unlock ();
589 return commitres > 0;
592 bool
593 AlsaSource::WriteAlsa ()
595 if (mmap)
596 return WriteMmap ();
597 else
598 return WriteRW ();
601 bool
602 AlsaSource::XrunRecovery (int err)
604 switch (err) {
605 case -EPIPE: // under-run
606 Underflowed ();
607 mutex.Lock ();
608 if (initialized) {
609 err = snd_pcm_prepare (pcm);
610 if (err < 0) {
611 LOG_AUDIO ("AlsaPlayer: Can't recover from underrun, prepare failed: %s.\n", snd_strerror (err));
613 } else {
614 LOG_AUDIO ("AlsaPlayer: Can't recover from underrun, pcm has been closed.\n");
616 mutex.Unlock ();
617 break;
618 case -ESTRPIPE:
619 mutex.Lock ();
620 if (initialized) {
621 while ((err = snd_pcm_resume (pcm)) == -EAGAIN) {
622 LOG_AUDIO ("XrunRecovery: waiting for resume\n");
623 sleep (1); // wait until the suspend flag is released
625 if (err < 0) {
626 err = snd_pcm_prepare (pcm);
627 if (err < 0) {
628 LOG_AUDIO ("AlsaPlayer: Can't recover from suspend, prepare failed: %s.\n", snd_strerror (err));
631 } else {
632 LOG_AUDIO ("AlsaPlayer: Can't recover from suspend, pcm has been closed.\n");
634 mutex.Unlock ();
635 break;
636 default:
637 LOG_AUDIO ("AlsaPlayer: Can't recover from underrun: %s\n", snd_strerror (err));
638 break;
641 return err >= 0;
644 bool
645 AlsaSource::PreparePcm (snd_pcm_sframes_t *avail)
647 int err = 0;
648 bool closed = false;
649 snd_pcm_state_t state;
651 mutex.Lock ();
652 if (initialized) {
653 state = snd_pcm_state (pcm);
654 } else {
655 LOG_ALSA ("AlsaSource::PreparePcm (): pcm has been closed.\n");
656 closed = true;
658 mutex.Unlock ();
660 if (closed)
661 return false;
663 switch (state) {
664 case SND_PCM_STATE_XRUN:
665 LOG_ALSA ("AlsaSource::PreparePcm (): SND_PCM_STATE_XRUN.\n");
667 if (!XrunRecovery (-EPIPE))
668 return false;
670 started = false;
671 break;
672 case SND_PCM_STATE_SUSPENDED:
673 if (!XrunRecovery (-ESTRPIPE))
674 return false;
675 break;
676 case SND_PCM_STATE_SETUP:
677 if (!XrunRecovery (-EPIPE))
678 return false;
680 started = false;
681 break;
682 case SND_PCM_STATE_RUNNING:
683 started = true; // We might have gotten started automatically after writing a certain number of samples.
684 case SND_PCM_STATE_PREPARED:
685 break;
686 case SND_PCM_STATE_PAUSED:
687 case SND_PCM_STATE_DRAINING:
688 default:
689 LOG_ALSA ("AlsaSource::PreparePcm (): state: %s (prepare failed)\n", snd_pcm_state_name (state));
690 return false;
693 err = 0;
694 mutex.Lock ();
695 if (initialized) {
696 *avail = snd_pcm_avail_update (pcm);
697 } else {
698 closed = true;
700 mutex.Unlock ();
702 if (closed)
703 return false;
705 if (*avail < 0) {
706 if (!XrunRecovery (*avail))
707 return false;
709 started = false;
710 return false;
713 if ((snd_pcm_uframes_t) *avail < period_size) {
714 if (!started) {
715 LOG_ALSA ("AlsaSource::PreparePcm (): starting pcm (period size: %li, available: %li)\n", period_size, *avail);
717 mutex.Lock ();
718 if (initialized) {
719 err = snd_pcm_start (pcm);
720 } else {
721 closed = true;
723 mutex.Unlock ();
725 if (closed)
726 return false;
728 if (err < 0) {
729 LOG_AUDIO ("AlsaPlayer: Could not start pcm: %s\n", snd_strerror (err));
730 return false;
732 started = true;
733 } else {
734 return false;
736 return false;
739 LOG_ALSA ("AlsaSource::PreparePcm (): Prepared, avail: %li, started: %i\n", *avail, (int) started);
741 return true;
744 guint64
745 AlsaSource::GetDelayInternal ()
747 snd_pcm_sframes_t delay;
748 int err;
749 guint64 result;
750 bool update_error = false;
751 bool not_initialized = false;
753 mutex.Lock ();
754 if (!initialized) {
755 not_initialized = true;
756 } else {
757 err = snd_pcm_avail_update (pcm);
759 if (err < 0) {
760 LOG_AUDIO ("AlsaSource::GetDelayInternal (): Could not update delay (%s)\n", snd_strerror (err));
761 update_error = true;
762 } else {
763 err = snd_pcm_delay (pcm, &delay);
766 mutex.Unlock ();
768 if (not_initialized) {
769 LOG_AUDIO ("AlsaSource::GetDelayInternal (): pcm has been closed.\n");
770 return G_MAXUINT64;
773 if (update_error)
774 return G_MAXUINT64;
776 if (err < 0) {
777 LOG_AUDIO ("AlsaSource::GetDelayInternal (): Could not get delay (%s)\n", snd_strerror (err));
778 result = G_MAXUINT64;
779 } else if (delay < 0) {
780 LOG_AUDIO ("AlsaSource::GetDelayInternal (): Got negative delay (%li)\n", delay);
781 result = G_MAXUINT64;
782 } else {
783 result = (guint64) TIMESPANTICKS_IN_SECOND * (guint64) delay / (guint64) GetSampleRate ();
786 return result;
790 * AlsaPlayer
793 AlsaPlayer::AlsaPlayer ()
795 LOG_ALSA ("AlsaPlayer::AlsaPlayer ()\n");
797 audio_thread = NULL;
798 shutdown = false;
799 update_poll_pending = true;
800 udfs = NULL;
801 ndfs = 0;
802 fds [0] = -1;
803 fds [1] = -1;
806 AlsaPlayer::~AlsaPlayer ()
808 LOG_ALSA ("AlsaPlayer::~AlsaPlayer ()\n");
811 bool
812 AlsaPlayer::Initialize ()
814 bool result;
816 LOG_ALSA ("AlsaPlayer::Initialize ()\n");
818 // Create our spipe
819 if (pipe (fds) != 0) {
820 LOG_AUDIO ("AlsaPlayer::Initialize (): Unable to create pipe (%s).\n", strerror (errno));
821 return false;
824 // Make the writer pipe non-blocking.
825 fcntl (fds [1], F_SETFL, fcntl (fds [1], F_GETFL) | O_NONBLOCK);
827 // Create the audio thread
828 audio_thread = (pthread_t *) g_malloc (sizeof (pthread_t));
829 result = pthread_create (audio_thread, NULL, Loop, this);
830 if (result != 0) {
831 LOG_AUDIO ("AlsaPlayer::Initialize (): could not create audio thread (error code: %i = '%s').\n", result, strerror (result));
832 g_free (audio_thread);
833 audio_thread = NULL;
834 return false;
837 LOG_ALSA ("AlsaPlayer::Initialize (): the audio player has been initialized.\n");
839 return true;
842 static int is_alsa_usable = 0; // 0 = not tested, 1 = tested, usable, 2 = tested, not usable
843 static void *libalsa = NULL;
845 bool
846 AlsaPlayer::IsInstalled ()
848 bool result = false;
849 const char *version;
851 switch (is_alsa_usable) {
852 case 0:
853 libalsa = dlopen ("libasound.so.2", RTLD_LAZY);
854 if (libalsa == NULL) {
855 is_alsa_usable = 2;
856 return false;
858 result = true;
860 result &= NULL != (d_snd_pcm_open = (dyn_snd_pcm_open *) dlsym (libalsa, "snd_pcm_open"));
861 result &= NULL != (d_snd_pcm_close = (dyn_snd_pcm_close *) dlsym (libalsa, "snd_pcm_close"));
862 result &= NULL != (d_snd_pcm_get_params = (dyn_snd_pcm_get_params *) dlsym (libalsa, "snd_pcm_get_params"));
863 result &= NULL != (d_snd_pcm_poll_descriptors_count = (dyn_snd_pcm_poll_descriptors_count *) dlsym (libalsa, "snd_pcm_poll_descriptors_count"));
864 result &= NULL != (d_snd_pcm_poll_descriptors = (dyn_snd_pcm_poll_descriptors *) dlsym (libalsa, "snd_pcm_poll_descriptors"));
865 result &= NULL != (d_snd_output_stdio_attach = (dyn_snd_output_stdio_attach *) dlsym (libalsa, "snd_output_stdio_attach"));
866 result &= NULL != (d_snd_pcm_hw_params_malloc = (dyn_snd_pcm_hw_params_malloc *) dlsym (libalsa, "snd_pcm_hw_params_malloc"));
867 result &= NULL != (d_snd_pcm_hw_params_any = (dyn_snd_pcm_hw_params_any *) dlsym (libalsa, "snd_pcm_hw_params_any"));
868 result &= NULL != (d_snd_pcm_hw_params_dump = (dyn_snd_pcm_hw_params_dump *) dlsym (libalsa, "snd_pcm_hw_params_dump"));
869 result &= NULL != (d_snd_pcm_hw_params_set_rate_resample = (dyn_snd_pcm_hw_params_set_rate_resample *) dlsym (libalsa, "snd_pcm_hw_params_set_rate_resample"));
870 result &= NULL != (d_snd_pcm_hw_params_test_access = (dyn_snd_pcm_hw_params_test_access *) dlsym (libalsa, "snd_pcm_hw_params_test_access"));
871 result &= NULL != (d_snd_pcm_hw_params_set_access = (dyn_snd_pcm_hw_params_set_access *) dlsym (libalsa, "snd_pcm_hw_params_set_access"));
872 result &= NULL != (d_snd_pcm_hw_params_set_format = (dyn_snd_pcm_hw_params_set_format *) dlsym (libalsa, "snd_pcm_hw_params_set_format"));
873 result &= NULL != (d_snd_pcm_hw_params_set_channels = (dyn_snd_pcm_hw_params_set_channels *) dlsym (libalsa, "snd_pcm_hw_params_set_channels"));
874 result &= NULL != (d_snd_pcm_hw_params_set_rate_near = (dyn_snd_pcm_hw_params_set_rate_near *) dlsym (libalsa, "snd_pcm_hw_params_set_rate_near"));
875 result &= NULL != (d_snd_pcm_hw_params_set_buffer_time_near = (dyn_snd_pcm_hw_params_set_buffer_time_near *) dlsym (libalsa, "snd_pcm_hw_params_set_buffer_time_near"));
876 result &= NULL != (d_snd_pcm_hw_params = (dyn_snd_pcm_hw_params *) dlsym (libalsa, "snd_pcm_hw_params"));
877 result &= NULL != (d_snd_pcm_hw_params_can_pause = (dyn_snd_pcm_hw_params_can_pause *) dlsym (libalsa, "snd_pcm_hw_params_can_pause"));
878 result &= NULL != (d_snd_pcm_hw_params_free = (dyn_snd_pcm_hw_params_free *) dlsym (libalsa, "snd_pcm_hw_params_free"));
879 result &= NULL != (d_snd_pcm_state = (dyn_snd_pcm_state *) dlsym (libalsa, "snd_pcm_state"));
880 result &= NULL != (d_snd_pcm_state_name = (dyn_snd_pcm_state_name *) dlsym (libalsa, "snd_pcm_state_name"));
881 result &= NULL != (d_snd_pcm_drop = (dyn_snd_pcm_drop *) dlsym (libalsa, "snd_pcm_drop"));
882 result &= NULL != (d_snd_pcm_writei = (dyn_snd_pcm_writei *) dlsym (libalsa, "snd_pcm_writei"));
883 result &= NULL != (d_snd_pcm_mmap_begin = (dyn_snd_pcm_mmap_begin *) dlsym (libalsa, "snd_pcm_mmap_begin"));
884 result &= NULL != (d_snd_pcm_mmap_commit = (dyn_snd_pcm_mmap_commit *) dlsym (libalsa, "snd_pcm_mmap_commit"));
885 result &= NULL != (d_snd_pcm_prepare = (dyn_snd_pcm_prepare *) dlsym (libalsa, "snd_pcm_prepare"));
886 result &= NULL != (d_snd_pcm_resume = (dyn_snd_pcm_resume *) dlsym (libalsa, "snd_pcm_resume"));
887 result &= NULL != (d_snd_pcm_avail_update = (dyn_snd_pcm_avail_update *) dlsym (libalsa, "snd_pcm_avail_update"));
888 result &= NULL != (d_snd_pcm_start = (dyn_snd_pcm_start *) dlsym (libalsa, "snd_pcm_start"));
889 result &= NULL != (d_snd_pcm_delay = (dyn_snd_pcm_delay *) dlsym (libalsa, "snd_pcm_delay"));
890 result &= NULL != (d_snd_asoundlib_version = (dyn_snd_asoundlib_version *) dlsym (libalsa, "snd_asoundlib_version"));
891 result &= NULL != (d_snd_strerror = (dyn_snd_strerror *) dlsym (libalsa, "snd_strerror"));
893 if (d_snd_asoundlib_version != NULL) {
894 version = d_snd_asoundlib_version ();
895 LOG_AUDIO ("AlsaPlayer: Found alsa/asound version: '%s'\n", version);
898 if (!result)
899 LOG_AUDIO ("AlsaPlayer: Failed to load one or more required functions in libasound.so.");
901 is_alsa_usable = result ? 1 : 2;
902 return result;
903 case 1:
904 return true;
905 default:
906 return false;
909 return true;
912 void
913 AlsaPlayer::AddInternal (AudioSource *source)
915 update_poll_pending = true;
916 WakeUp ();
919 void
920 AlsaPlayer::RemoveInternal (AudioSource *source)
922 update_poll_pending = true;
923 WakeUp ();
926 void
927 AlsaPlayer::PrepareShutdownInternal ()
929 int result = 0;
931 LOG_ALSA ("AlsaPlayer::PrepareShutdownInternal ().\n");
933 // Wait for the audio thread to finish
934 shutdown = true;
935 if (audio_thread != NULL) {
936 WakeUp ();
937 result = pthread_join (*audio_thread, NULL);
938 if (result != 0) {
939 LOG_AUDIO ("AudioPlayer::Shutdown (): failed to join the audio thread (error code: %i).\n", result);
940 } else {
941 // Only free the thread if we could join it.
942 g_free (audio_thread);
944 audio_thread = NULL;
948 void
949 AlsaPlayer::FinishShutdownInternal ()
951 LOG_ALSA ("AlsaPlayer::FinishShutdownInternal ().\n");
953 if (fds [0] != -1) {
954 close (fds [0]);
955 fds [0] = -1;
957 if (fds [1] != -1) {
958 close (fds [1]);
959 fds [1] = -1;
962 g_free (udfs);
963 udfs = NULL;
964 ndfs = 0;
967 void
968 AlsaPlayer::UpdatePollList ()
970 update_poll_pending = true;
971 WakeUp ();
974 AudioSource *
975 AlsaPlayer::CreateNode (MediaPlayer *mplayer, AudioStream *stream)
977 return new AlsaSource (this, mplayer, stream);
980 void*
981 AlsaPlayer::Loop (void *data)
983 ((AlsaPlayer *) data)->Loop ();
984 return NULL;
987 void
988 AlsaPlayer::Loop ()
990 AlsaSource *source = NULL;
992 bool played_something;
993 int result;
994 int buffer;
995 int current;
997 LOG_ALSA ("AlsaPlayer: entering audio loop.\n");
999 while (!shutdown) {
1000 sources.StartEnumeration ();
1002 played_something = false;
1003 while ((source = (AlsaSource *) sources.GetNext (false)) != NULL) {
1004 if (source->GetState () == AudioPlaying) {
1005 if (source->WriteAlsa ())
1006 played_something = true;
1007 } else if (source->IsDropPending ()) {
1008 source->DropAlsa ();
1010 source->unref ();
1013 if (played_something)
1014 continue;
1016 // None of the audio nodes in the list played anything
1017 // (or there are no audio nodes), so wait for something
1018 // to happen. We handle spurious wakeups correctly, so
1019 // there is no find out exactly what happened.
1021 while (!shutdown && update_poll_pending) {
1023 * We need to update the list of file descriptors we poll on
1024 * to only include audio nodes which are playing.
1026 update_poll_pending = false;
1028 ndfs = 1;
1029 current = 1;
1030 sources.StartEnumeration ();
1031 while ((source = (AlsaSource *) sources.GetNext (true)) != NULL) {
1032 ndfs += source->ndfs;
1033 source->unref ();
1036 g_free (udfs);
1037 udfs = (pollfd*) g_malloc0 (sizeof (pollfd) * ndfs);
1038 udfs [0].fd = fds [0];
1039 udfs [0].events = POLLIN;
1041 sources.StartEnumeration ();
1042 while (!update_poll_pending && (source = (AlsaSource *) sources.GetNext (true)) != NULL) {
1043 if (current + source->ndfs > ndfs) {
1044 // the list of sources changed.
1045 update_poll_pending = true;
1046 } else {
1047 memcpy (&udfs [current], source->udfs, source->ndfs * sizeof (pollfd));
1048 current += source->ndfs;
1050 source->unref ();
1053 if (current != ndfs) {
1054 // The list of sources changed
1055 update_poll_pending = true;
1059 do {
1060 udfs [0].events = POLLIN;
1061 udfs [0].revents = 0;
1063 LOG_ALSA_EX ("AlsaPlayer::Loop (): polling... ndfs: %i\n", ndfs);
1065 result = poll (udfs, ndfs, 10000); // Have a timeout of 10 seconds, just in case something goes wrong.
1067 LOG_ALSA_EX ("AlsaPlayer::Loop (): poll result: %i, fd: %i, fd [0].revents: %i, errno: %i, err: %s, ndfs = %i, shutdown: %i\n",
1068 result, udfs [0].fd, (int) udfs [0].revents, errno, strerror (errno), ndfs, shutdown);
1070 if (result == 0) { // Timed out
1071 LOG_ALSA_EX ("AlsaPlayer::Loop (): poll timed out.\n");
1072 } else if (result < 0) { // Some error poll exit condition
1073 // Doesn't matter what happened (happens quite often due to interrupts)
1074 LOG_ALSA_EX ("AlsaPlayer::Loop (): poll failed: %i (%s)\n", errno, strerror (errno));
1075 } else { // Something woke up the poll
1076 if (udfs [0].revents & POLLIN) {
1077 // We were asked to wake up by the audio player
1078 // Read whatever was written into the pipe so that the pipe doesn't fill up.
1079 read (udfs [0].fd, &buffer, sizeof (int));
1080 LOG_ALSA_EX ("AlsaPlayer::Loop (): woken up by ourselves.\n");
1081 } else {
1082 // Something happened on any of the audio streams
1085 } while (result == -1 && errno == EINTR);
1088 LOG_ALSA ("AlsaPlayer: exiting audio loop.\n");
1091 void
1092 AlsaPlayer::WakeUp ()
1094 int result;
1096 LOG_ALSA_EX ("AlsaPlayer::WakeUp ().\n");
1098 // Write until something has been written.
1099 do {
1100 result = write (fds [1], "c", 1);
1101 } while (result == 0);
1103 if (result == -1)
1104 LOG_AUDIO ("AlsaPlayer::WakeUp (): Could not wake up audio thread: %s\n", strerror (errno));
1106 LOG_ALSA_EX ("AlsaPlayer::WakeUp (): thread should now wake up (or have woken up already).\n");