put example files to dedicated dir
[monster.git] / player_alsa_pcm.c
blobbff91c45f438fc7867378444a38e28239b2e269d
1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
2 /*****************************************************************************
4 * Player capable to send PCM sequence over ALSA
5 * This file is part of monster
7 * Copyright (C) 2006,2007 Nedko Arnaudov <nedko@arnaudov.name>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; version 2 of the License
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22 *****************************************************************************/
24 #include <alsa/asoundlib.h>
25 #include <pthread.h>
27 #define DISABLE_DEBUG_OUTPUT
29 #include "player.h"
30 #include "log.h"
31 #include "media_source.h"
32 #include "types.h"
33 #include "wav.h"
34 #include "player_alsa_pcm.h"
35 #include "conf.h"
37 #define FEED_THREAD_STATE_INITIAL 0
38 #define FEED_THREAD_STATE_ARMED 1
39 #define FEED_THREAD_STATE_PLAY 2
40 #define FEED_THREAD_STATE_STOP_REQUESTED 3
41 #define FEED_THREAD_STATE_STOPPED 4
43 struct player_alsa_pcm
45 struct player virtual;
47 struct media_source * media_source_ptr;
48 int eof;
49 eod_t eod;
50 unsigned int eod_context;
52 snd_pcm_t * playback_handle;
54 unsigned int rt_prio_alsa_pcm_feed;
56 pthread_t feed_thread;
58 pthread_mutex_t feed_mutex;
59 pthread_cond_t feed_cond;
60 unsigned int feed_thread_state;
63 int
64 playback_callback(struct player_alsa_pcm * player_ptr)
66 int ret;
67 void * buffer_ptr;
69 //DEBUG_OUT("playback callback called");
71 if (player_ptr->eof == 0)
73 player_ptr->eod(player_ptr->eod_context);
74 player_ptr->eof = 1;
77 ret = media_source_get_frame_data(player_ptr->media_source_ptr, &buffer_ptr);
78 if (ret < 0)
80 ERROR_OUT("media_source_get_frame_data() failed.");
81 return -1;
84 if (ret == 1 && player_ptr->eof == -1)
86 player_ptr->eof = 0;
89 ret = snd_pcm_writei(player_ptr->playback_handle, buffer_ptr, WAV_DATA_CHUNK);
90 if (ret < 0)
92 ERROR_OUT("write failed (%s)", snd_strerror(ret));
93 return -1;
96 if (ret != WAV_DATA_CHUNK)
98 ERROR_OUT("write accepted less data (%d)", ret);
99 return -1;
102 media_source_next_frame(player_ptr->media_source_ptr);
104 return 0;
107 void dump_state(const char * msg, struct player_alsa_pcm * player_ptr)
109 snd_pcm_state_t state;
111 state = snd_pcm_state(player_ptr->playback_handle);
113 switch (state)
115 case SND_PCM_STATE_OPEN:
116 DEBUG_OUT("%s: Open", msg);
117 return;
118 case SND_PCM_STATE_SETUP:
119 DEBUG_OUT("%s: Setup installed", msg);
120 return;
121 case SND_PCM_STATE_PREPARED:
122 DEBUG_OUT("%s: Ready to start", msg);
123 return;
124 case SND_PCM_STATE_RUNNING:
125 DEBUG_OUT("%s: Running", msg);
126 return;
127 case SND_PCM_STATE_XRUN:
128 DEBUG_OUT("%s: Stopped: underrun (playback) or overrun (capture) detected", msg);
129 return;
130 case SND_PCM_STATE_DRAINING:
131 DEBUG_OUT("%s: Draining: running (playback) or stopped (capture)", msg);
132 return;
133 case SND_PCM_STATE_PAUSED:
134 DEBUG_OUT("%s: Paused", msg);
135 return;
136 case SND_PCM_STATE_SUSPENDED:
137 DEBUG_OUT("%s: Hardware is suspended", msg);
138 return;
139 case SND_PCM_STATE_DISCONNECTED:
140 DEBUG_OUT("%s: Hardware is disconnected ", msg);
141 return;
144 DEBUG_OUT("%s: Unknown ALSA pcm state %u", msg, (unsigned int)state);
147 #define player_ptr ((struct player_alsa_pcm *)context)
149 void * player_alsa_pcm_feed(void * context)
151 int ret;
152 snd_pcm_sframes_t frames_to_deliver;
154 DEBUG_OUT("player_alsa_pcm_feed thread started");
157 struct sched_param param;
158 int policy;
160 pthread_getschedparam(pthread_self(), &policy, &param);
161 param.sched_priority = player_ptr->rt_prio_alsa_pcm_feed;
162 policy = SCHED_FIFO;
163 pthread_setschedparam(pthread_self(), policy, &param);
166 /* mark that we are ready */
167 pthread_mutex_lock(&player_ptr->feed_mutex);
169 player_ptr->feed_thread_state = FEED_THREAD_STATE_ARMED;
171 ret = pthread_cond_signal(&player_ptr->feed_cond);
172 if (ret != 0)
174 ERROR_OUT("Failed to signal feed cond (%d)", ret);
175 pthread_mutex_unlock(&player_ptr->feed_mutex);
176 goto exit;
179 pthread_mutex_unlock(&player_ptr->feed_mutex);
181 /* wait for play command */
182 pthread_mutex_lock(&player_ptr->feed_mutex);
184 while (player_ptr->feed_thread_state == FEED_THREAD_STATE_ARMED)
186 pthread_cond_wait(&player_ptr->feed_cond, &player_ptr->feed_mutex);
189 if (player_ptr->feed_thread_state != FEED_THREAD_STATE_PLAY)
191 goto exit_locked;
194 pthread_mutex_unlock(&player_ptr->feed_mutex);
196 dump_state("Before loop", player_ptr);
198 loop:
199 /* wait till the interface is ready for data, or 1 second has elapsed. */
200 if ((ret = snd_pcm_wait(player_ptr->playback_handle, 1000)) < 0)
202 if (errno == 0)
204 /* othe thread stopped the player */
205 goto exit;
208 ERROR_OUT("poll failed (%i: %s)", errno, strerror(errno));
209 goto exit;
212 //DEBUG_OUT("snd_pcm_wait() returned %u", (unsigned int)ret);
214 pthread_mutex_lock(&player_ptr->feed_mutex);
216 if (player_ptr->feed_thread_state != FEED_THREAD_STATE_PLAY)
218 goto exit_locked;
221 pthread_mutex_unlock(&player_ptr->feed_mutex);
223 /* find out how much space is available for playback data */
227 if ((frames_to_deliver = snd_pcm_avail_update(player_ptr->playback_handle)) < 0)
229 if (frames_to_deliver == -EPIPE)
231 ERROR_OUT("an xrun occured");
232 goto exit;
234 else
236 ERROR_OUT("unknown ALSA avail update return value (%d)",
237 (int)frames_to_deliver);
238 goto exit;
242 while (frames_to_deliver < WAV_DATA_CHUNK);
244 /* deliver the data */
246 if (playback_callback(player_ptr) < 0)
248 ERROR_OUT("playback callback failed");
249 goto exit;
252 if (player_ptr->eof == 1)
254 goto exit;
257 goto loop;
259 exit:
260 pthread_mutex_lock(&player_ptr->feed_mutex);
262 exit_locked:
263 player_ptr->feed_thread_state = FEED_THREAD_STATE_STOPPED;
265 ret = pthread_cond_signal(&player_ptr->feed_cond);
266 if (ret != 0)
268 ERROR_OUT("Failed to signal feed cond (%d)", ret);
271 pthread_mutex_unlock(&player_ptr->feed_mutex);
273 DEBUG_OUT("player_alsa_pcm_feed thread stopped");
274 return NULL;
277 #undef player_ptr
279 #define player_ptr ((struct player_alsa_pcm *)virtual_ptr)
281 void
282 player_alsa_pcm_destroy(struct player * virtual_ptr)
284 int ret;
286 DEBUG_OUT("player_alsa_pcm_destroy");
288 if (player_ptr->playback_handle != NULL)
290 /* signal thread that it must stop */
291 pthread_mutex_lock(&player_ptr->feed_mutex);
292 if (player_ptr->feed_thread_state != FEED_THREAD_STATE_STOPPED)
294 player_ptr->feed_thread_state = FEED_THREAD_STATE_STOP_REQUESTED;
296 ret = pthread_cond_signal(&player_ptr->feed_cond);
297 if (ret != 0)
299 ERROR_OUT("Failed to signal feed cond (%d)", ret);
300 pthread_mutex_unlock(&player_ptr->feed_mutex);
302 else
304 pthread_mutex_unlock(&player_ptr->feed_mutex);
306 snd_pcm_drain(player_ptr->playback_handle);
308 pthread_join(player_ptr->feed_thread, NULL);
311 else
313 pthread_mutex_unlock(&player_ptr->feed_mutex);
316 ret = pthread_cond_destroy(&player_ptr->feed_cond);
317 if (ret != 0)
319 ERROR_OUT("Failed to destroy feed cond (%d)", ret);
322 ret = pthread_mutex_destroy(&player_ptr->feed_mutex);
323 if (ret != 0)
325 ERROR_OUT("Failed to destroy feed mutex (%d)", ret);
328 snd_pcm_close(player_ptr->playback_handle);
331 free(player_ptr);
334 int player_alsa_pcm_start_prepare(struct player * virtual_ptr)
336 snd_pcm_hw_params_t *hw_params;
337 snd_pcm_sw_params_t *sw_params;
338 int ret;
340 DEBUG_OUT("player_alsa_pcm_start_prepare");
342 NOTICE_OUT("Audio: Using priority %i for feed thread", player_ptr->rt_prio_alsa_pcm_feed);
344 if ((ret = snd_pcm_open(&player_ptr->playback_handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
346 ERROR_OUT("cannot open default audio device (%s)", snd_strerror(ret));
347 ret = -1;
348 goto exit;
351 if ((ret = snd_pcm_hw_params_malloc(&hw_params)) < 0)
353 ERROR_OUT("cannot allocate hardware parameter structure (%s)", snd_strerror(ret));
354 ret = -1;
355 goto exit_close_pcm;
358 if ((ret = snd_pcm_hw_params_any(player_ptr->playback_handle, hw_params)) < 0)
360 ERROR_OUT("cannot initialize hardware parameter structure (%s)", snd_strerror(ret));
361 snd_pcm_hw_params_free(hw_params);
362 ret = -1;
363 goto exit_close_pcm;
366 if ((ret = snd_pcm_hw_params_set_access(player_ptr->playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
368 ERROR_OUT("cannot set access type (%s)", snd_strerror(ret));
369 snd_pcm_hw_params_free(hw_params);
370 ret = -1;
371 goto exit_close_pcm;
374 if ((ret = snd_pcm_hw_params_set_format(player_ptr->playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0)
376 ERROR_OUT("cannot set sample format (%s)", snd_strerror(ret));
377 snd_pcm_hw_params_free(hw_params);
378 ret = -1;
379 goto exit_close_pcm;
382 if ((ret = snd_pcm_hw_params_set_rate(player_ptr->playback_handle, hw_params, 48000, 0)) < 0)
384 ERROR_OUT("cannot set sample rate (%s)", snd_strerror(ret));
385 snd_pcm_hw_params_free(hw_params);
386 ret = -1;
387 goto exit_close_pcm;
390 if ((ret = snd_pcm_hw_params_set_channels(player_ptr->playback_handle, hw_params, 2)) < 0)
392 ERROR_OUT("cannot set channel count (%s)", snd_strerror(ret));
393 snd_pcm_hw_params_free(hw_params);
394 ret = -1;
395 goto exit_close_pcm;
398 //dump_state("Before snd_pcm_hw_params", player_ptr);
400 if ((ret = snd_pcm_hw_params(player_ptr->playback_handle, hw_params)) < 0)
402 ERROR_OUT("cannot set parameters (%s)", snd_strerror(ret));
403 snd_pcm_hw_params_free(hw_params);
404 ret = -1;
405 goto exit_close_pcm;
408 //dump_state("After snd_pcm_hw_params", player_ptr);
410 snd_pcm_hw_params_free(hw_params);
412 /* tell ALSA to wake us up whenever 4096 or more frames
413 of playback data can be delivered. Also, tell
414 ALSA that we'll start the device ourselves.
417 if ((ret = snd_pcm_sw_params_malloc(&sw_params)) < 0)
419 ERROR_OUT("cannot allocate software parameters structure (%s)", snd_strerror(ret));
420 ret = -1;
421 goto exit_close_pcm;
423 if ((ret = snd_pcm_sw_params_current(player_ptr->playback_handle, sw_params)) < 0)
425 ERROR_OUT("cannot initialize software parameters structure (%s)", snd_strerror(ret));
426 ret = -1;
427 goto exit_close_pcm;
429 if ((ret = snd_pcm_sw_params_set_avail_min(player_ptr->playback_handle, sw_params, 4096)) < 0)
431 ERROR_OUT("cannot set minimum available count (%s)", snd_strerror(ret));
432 ret = -1;
433 goto exit_close_pcm;
435 if ((ret = snd_pcm_sw_params_set_start_threshold(player_ptr->playback_handle, sw_params, 0U)) < 0)
437 ERROR_OUT("cannot set start mode (%s)", snd_strerror(ret));
438 ret = -1;
439 goto exit_close_pcm;
442 //dump_state("Before snd_pcm_sw_params", player_ptr);
444 if ((ret = snd_pcm_sw_params(player_ptr->playback_handle, sw_params)) < 0)
446 ERROR_OUT("cannot set software parameters (%s)", snd_strerror(ret));
447 ret = -1;
448 goto exit_close_pcm;
451 //dump_state("Before prepare", player_ptr);
453 /* the interface will interrupt the kernel every 4096 frames, and ALSA
454 will wake up this program very soon after that.
457 /* if ((ret = snd_pcm_prepare(player_ptr->playback_handle)) < 0) */
458 /* { */
459 /* ERROR_OUT("cannot prepare audio interface for use (%s)", snd_strerror(ret)); */
460 /* ret = -1; */
461 /* goto exit_close_pcm; */
462 /* } */
464 ret = pthread_mutex_init(&player_ptr->feed_mutex, NULL);
465 if (ret != 0)
467 ERROR_OUT("Failed to init feed mutex (%d)", ret);
468 ret = -1;
469 goto exit_close_pcm;
472 ret = pthread_cond_init(&player_ptr->feed_cond, NULL);
473 if (ret != 0)
475 ERROR_OUT("Failed to init feed cond (%d)", ret);
476 ret = -1;
477 goto exit_destroy_mutex;
480 player_ptr->feed_thread_state = FEED_THREAD_STATE_INITIAL;
482 ret = pthread_create(&player_ptr->feed_thread, NULL, player_alsa_pcm_feed, player_ptr);
483 if (ret != 0)
485 ERROR_OUT("Failed to start feed thread (%d)", ret);
486 ret = -1;
487 goto exit_destroy_cond;
490 /* Wait feed thread to fill kernel buffers */
491 DEBUG_OUT("Waiting feed thread to fill kernel buffers...");
492 pthread_mutex_lock(&player_ptr->feed_mutex);
493 while (player_ptr->feed_thread_state == FEED_THREAD_STATE_INITIAL)
495 pthread_cond_wait(&player_ptr->feed_cond, &player_ptr->feed_mutex);
498 if (player_ptr->feed_thread_state != FEED_THREAD_STATE_ARMED)
500 ERROR_OUT("Feed thread failed to prefill kernel buffers");
501 pthread_mutex_unlock(&player_ptr->feed_mutex);
502 goto exit_join;
505 pthread_mutex_unlock(&player_ptr->feed_mutex);
507 ret = 0;
508 goto exit;
510 exit_join:
511 /* signal thread that it must stop */
512 pthread_mutex_lock(&player_ptr->feed_mutex);
513 if (player_ptr->feed_thread_state != FEED_THREAD_STATE_STOPPED)
515 player_ptr->feed_thread_state = FEED_THREAD_STATE_STOP_REQUESTED;
517 ret = pthread_cond_signal(&player_ptr->feed_cond);
518 if (ret != 0)
520 ERROR_OUT("Failed to signal feed cond (%d)", ret);
521 pthread_mutex_unlock(&player_ptr->feed_mutex);
522 goto exit_destroy_cond;
525 pthread_mutex_unlock(&player_ptr->feed_mutex);
527 ret = pthread_join(player_ptr->feed_thread, NULL);
528 if (ret != 0)
530 ERROR_OUT("Failed to join feed thread (%d)", ret);
533 exit_destroy_cond:
534 ret = pthread_cond_destroy(&player_ptr->feed_cond);
535 if (ret != 0)
537 ERROR_OUT("Failed to destroy feed cond (%d)", ret);
540 exit_destroy_mutex:
541 ret = pthread_mutex_destroy(&player_ptr->feed_mutex);
542 if (ret != 0)
544 ERROR_OUT("Failed to destroy feed mutex (%d)", ret);
547 exit_close_pcm:
548 snd_pcm_close(player_ptr->playback_handle);
549 player_ptr->playback_handle = NULL;
551 exit:
552 return ret;
555 int player_alsa_pcm_start(struct player * virtual_ptr)
557 int ret;
559 DEBUG_OUT("player_alsa_pcm_start");
561 /* signal thread that it must start play */
563 pthread_mutex_lock(&player_ptr->feed_mutex);
565 if (player_ptr->feed_thread_state != FEED_THREAD_STATE_ARMED)
567 ERROR_OUT("Feed thread is not armed");
568 ret = -1;
569 goto unlock;
572 player_ptr->feed_thread_state = FEED_THREAD_STATE_PLAY;
574 ret = pthread_cond_signal(&player_ptr->feed_cond);
575 if (ret != 0)
577 ERROR_OUT("Failed to signal feed cond (%d)", ret);
578 ret = -1;
579 goto unlock;
582 ret = 0;
584 unlock:
585 pthread_mutex_unlock(&player_ptr->feed_mutex);
587 return ret;
590 #undef player_ptr
593 player_alsa_pcm(
594 struct media_source * media_source_ptr,
595 eod_t eod,
596 unsigned int eod_context,
597 struct player ** player_ptr_ptr)
599 int ret;
600 struct player_alsa_pcm * player_ptr;
601 unsigned int format;
602 unsigned int rt_prio_alsa_pcm_feed;
604 DEBUG_OUT("player_alsa_pcm");
606 ret = media_source_format_query(media_source_ptr, &format);
607 if (ret < 0)
609 return -1;
612 if (format != MEDIA_FORMAT_PCM_16_STEREO_48)
614 ERROR_OUT("player_alsa_pcm cannot handle format %u", format);
615 return -1;
618 ret = conf_file_get_uint("/conf/realtime_priorities/alsa_pcm_feed", &rt_prio_alsa_pcm_feed);
619 if (ret != 0)
621 ERROR_OUT("failed to read from configuration what realtime priority to use for ALSA PCM feed thread");
622 return -1;
625 player_ptr = malloc(sizeof(struct player_alsa_pcm));
626 if (player_ptr == NULL)
628 ERROR_OUT("malloc() failed.");
629 return -1;
632 player_ptr->virtual.destroy = player_alsa_pcm_destroy;
633 player_ptr->virtual.start_prepare = player_alsa_pcm_start_prepare;
634 player_ptr->virtual.start = player_alsa_pcm_start;
636 player_ptr->media_source_ptr = media_source_ptr;
638 player_ptr->eod = eod;
639 player_ptr->eod_context = eod_context;
640 player_ptr->eof = -1;
642 player_ptr->rt_prio_alsa_pcm_feed = rt_prio_alsa_pcm_feed;
644 player_ptr->playback_handle = NULL;
646 strcpy(player_ptr->virtual.name, "ALSA PCM");
648 *player_ptr_ptr = &player_ptr->virtual;
650 return 0;