1 /* player.c - simple audio file player implementation
2  *
3  * This version uses PortAudio (http://www.portaudio.com) for audio
4  * output.
5  *
6  * Copyright 2010 Petteri Hintsanen <petterih@iki.fi>
7  *
8  * This file is part of abx.
9  *
10  * abx is free software: you can redistribute it and/or modify it
11  * under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * abx is distributed in the hope that it will be useful, but WITHOUT
16  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
18  * License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with abx.  If not, see <http://www.gnu.org/licenses/>.
22  */
23 
24 #include "player.h"
25 #include "soundfile.h"
26 #include <assert.h>
27 #include <glib.h>
28 #include <pthread.h>
29 #include <semaphore.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 /* Ring buffer size, in kiB. */
35 static const int BUFFER_SIZE = 512;
36 /* Fill this portion of buffer before playback.  This value must be
37  * between 0 and 1. */
38 static const float PREFILL = 0.2;
39 /* Begin to fill buffer after this portion of buffer has been
40  * consumed.  This is intended to keep PortAudio stream callback from
41  * signalling spaces semaphore all the time.  SLACK value must be
42  * between 0 and 1, though not too close to zero to prevent buffer
43  * underruns. */
44 static const float SLACK = 0.5;
45 
46 pthread_mutex_t audio_init_count_mutex = PTHREAD_MUTEX_INITIALIZER;
47 static unsigned int audio_init_count;
48 
49 /*
50  * Message structure for passing requests to the controller thread.
51  * These should not be built in user code; API functions (see
52  * player.h) should be used instead.
53  */
54 typedef struct {
55     enum {
56         CMD_PAUSE,
57         CMD_PLAY,
58         CMD_SEEK,
59         CMD_STOP,
60         CMD_TERM
61     } command;
62     double offset;
63     int whence;
64     sem_t *sem;
65 } Message;
66 
67 /*
68  * Player handle type.  This struct should not be accessed directly
69  * from user code.  See init_player.
70  */
71 struct Player {
72     /* Ring buffer.  We always keep one vacant slot, so that it is
73      * relatively easy to distinguish between empty (in == out) and
74      * full (in + 1 == out) buffer. */
75     struct {
76         float *begin;
77         float *end;
78         float *in;
79         float *out;
80         int bufsize;
81         int nframes;
82         sem_t prefill;
83         sem_t space;
84         int prefill_sem_ok;
85         int space_sem_ok;
86     } buffer;
87 
88     /* reader thread */
89     struct {
90         /* thread status/control values */
91         enum {
92             READER_RESUME,
93             READER_RUNNING,
94             READER_STOP,
95             READER_STOPPED,
96             READER_TERM
97         } control;
98         /* condition variable and mutex for thread control */
99         pthread_cond_t control_cond;
100         pthread_mutex_t control_cond_mutex;
101 
102         pthread_t thread_id;
103         int thread_ok;
104         sem_t sem;
105         sem_t notify;
106         int sem_ok;
107         int notify_ok;
108 
109         Sound_file *sndfile;
110         Metadata metadata;
111         char is_eof;
112     } reader;
113 
114     /* controller thread */
115     struct {
116         pthread_t thread_id;
117         pthread_t thread_ok;
118         GAsyncQueue *messages;
119     } controller;
120 
121     /* current playback status */
122     Player_state state;
123     /* location at the most recent seek, in secods */
124     double origin;
125     /* frames played after the most recent seek */
126     int nplayed;
127     /* PortAudio output stream */
128     PaStream *stream;
129 };
130 
131 /*
132  * Reader thread.
133  *
134  * This thread implements the producer part of the producer-consumer
135  * model by reading blocks of audio data from the sound file into the
136  * ring buffer.  Some notes:
137  *
138  * - Thread blocks on space semaphore, and expects that the semaphore
139  *   will be signaled at some point.
140  *
141  * - Thread reads the sound file until EOF is encountered, in which
142  *   case it sets is_eof to nonzero and waits on control_cond.
143  *
144  * - Thread signals sem before waiting.
145  *
146  * - Thread signals sem and resets is_eof to zero when awakened from
147  *   wait on control_cond.
148  *
149  * - After waking up from wait on control_cond, and filling PREFILL *
150  *   BUFFER_SIZE portion of the buffer (or hitting EOF), the thread
151  *   signals notify.
152  */
153 static void *
reader_main(void * arg)154 reader_main(void *arg)
155 {
156     Player *player;
157     int nchannels;
158     int notify;
159 
160     player = (Player *) arg;
161     nchannels = player->reader.metadata.channels; /* for prettier
162                                                    * offsetting */
163 
164     /* g_debug("starting reader thread, reader.control: %d",  */
165     /*         player->reader.control); */
166 
167     for (;;) {
168         pthread_mutex_lock(&player->reader.control_cond_mutex);
169         while (player->reader.control == READER_STOP
170                || player->reader.control == READER_TERM
171                || player->reader.is_eof) {
172             if (player->reader.control == READER_TERM) {
173                 /* g_debug("terminating reader thread"); */
174                 pthread_exit(NULL);
175             }
176 
177             if (player->reader.control == READER_STOP) {
178                 sem_post(&player->reader.sem);
179             }
180 
181             player->reader.control = READER_STOPPED;
182             /* g_debug("reader stopped"); */
183             pthread_cond_wait(&player->reader.control_cond,
184                               &player->reader.control_cond_mutex);
185             /* g_debug("reader resumed"); */
186             assert(player->reader.control == READER_RESUME
187                    || player->reader.control == READER_TERM);
188             sem_post(&player->reader.sem);
189             player->reader.is_eof = 0;
190         }
191 
192         if (player->reader.control == READER_RESUME) notify = 1;
193         else notify = 0;
194 
195         player->reader.control = READER_RUNNING;
196         pthread_mutex_unlock(&player->reader.control_cond_mutex);
197         /* g_debug("reader waiting for space"); */
198         sem_wait(&player->buffer.space);
199 
200         /* Reader stop/termination request could occur while waiting
201          * for buffer space. */
202         if (player->reader.control == READER_STOP
203             || player->reader.control == READER_TERM) continue;
204 
205         /* read in data */
206         for (;;) {
207             int nitems;
208             int nread;
209             if ((player->buffer.in == player->buffer.end
210                  && player->buffer.out == player->buffer.begin)
211                 || player->buffer.in + nchannels == player->buffer.out) {
212                 /* full buffer */
213                 /* g_debug("full buffer"); */
214                 if (notify) {
215                     /* g_debug("notifying prefill"); */
216                     sem_post(&player->reader.notify);
217                 }
218                 break;
219             }
220 
221             if (player->buffer.in == player->buffer.end) {
222                 /* rewind the ring buffer */
223                 /* g_debug("rewound the ring buffer"); */
224                 player->buffer.in = player->buffer.begin;
225             }
226 
227             if (player->buffer.in >= player->buffer.out) {
228                 nitems = ((player->buffer.end
229                            - player->buffer.in) / nchannels);
230             } else {
231                 nitems = ((player->buffer.out -
232                            player->buffer.in) / nchannels) - 1;
233             }
234 
235             assert(nitems > 0);
236 
237             /* g_debug("reading %d frames, %d frames in buffer, " */
238             /*         "buffer begin = %p, in = %p, out = %p, end = %p", */
239             /*         nitems, player->buffer.nframes, */
240             /*         (void *) player->buffer.begin, */
241             /*         (void *) player->buffer.in, */
242             /*         (void *) player->buffer.out, */
243             /*         (void *) player->buffer.end); */
244 
245             nread = read_pcm_data(player->reader.sndfile,
246                                   player->buffer.in, nitems);
247 
248             /* g_debug("read %d frames", nread); */
249 
250             player->buffer.nframes += nread;
251             player->buffer.in += (nread * nchannels);
252             assert(player->buffer.nframes <= player->buffer.bufsize);
253 
254             if (nread != nitems) {
255                 /* EOF */
256                 /* g_debug("eof"); */
257                 player->reader.is_eof = 1;
258                 if (notify) {
259                     sem_post(&player->reader.notify);
260                     notify = 0;
261                 }
262                 break;
263             }
264 
265             if (notify && player->buffer.nframes
266                 > PREFILL * player->buffer.bufsize) {
267                 /* g_debug("read %d frames, notifying prefill", nframes); */
268                 sem_post(&player->reader.notify);
269                 notify = 0;
270             }
271         }
272     }
273 }
274 
275 /*
276  * Stop reader thread.  Playback must be stopped, since this function
277  * can garble the input buffer.
278  *
279  * Return 0 on success, or 1 if reader was already stopped.
280  */
281 static int
stop_reader_thread(Player * player)282 stop_reader_thread(Player *player)
283 {
284     /* g_debug("stopping reader"); */
285     /* g_debug("locking reader mutex"); */
286     pthread_mutex_lock(&player->reader.control_cond_mutex);
287     assert(player->reader.control != READER_STOP);
288     if (player->reader.control == READER_STOPPED) {
289         /* g_debug("reader is already waiting"); */
290         pthread_mutex_unlock(&player->reader.control_cond_mutex);
291         /* g_debug("released reader mutex"); */
292         return 1;
293     } else {
294         int space;
295         /* g_debug("telling reader to stop"); */
296         player->reader.control = READER_STOP;
297         sem_getvalue(&player->buffer.space, &space);
298         assert(space == 0 || space == 1);
299         if (space == 0) {
300             /* g_debug("signaling spaces in case reader " */
301             /*         "is waiting"); */
302             sem_post(&player->buffer.space);
303         }
304         pthread_mutex_unlock(&player->reader.control_cond_mutex);
305         /* g_debug("released reader mutex"); */
306         /* g_debug("waiting for reader to stop"); */
307         sem_wait(&player->reader.sem);
308         /* g_debug("reader is now sleeping"); */
309         return 0;
310     }
311 }
312 
313 /*
314  * Resume reader thread.  Block until the input buffer has been filled
315  * with at least PREFILL * BUFFER_SIZE new frames, or EOF has been
316  * encountered.
317  *
318  * Return 0 on success, or 1 if reader was already running.
319  */
320 static int
resume_reader_thread(Player * player)321 resume_reader_thread(Player *player)
322 {
323     /* g_debug("resuming reader"); */
324     /* g_debug("locking reader mutex"); */
325     pthread_mutex_lock(&player->reader.control_cond_mutex);
326     assert(player->reader.control != READER_RESUME);
327     if (player->reader.control == READER_RUNNING) {
328         /* g_debug("reader is already running"); */
329         pthread_mutex_unlock(&player->reader.control_cond_mutex);
330         /* g_debug("released reader mutex"); */
331         return 1;
332     } else {
333         /* g_debug("telling reader to resume"); */
334         player->reader.control = READER_RESUME;
335         pthread_cond_signal(&player->reader.control_cond);
336         pthread_mutex_unlock(&player->reader.control_cond_mutex);
337         /* g_debug("released reader mutex"); */
338         /* g_debug("waiting for reader to wake up"); */
339         sem_wait(&player->reader.sem);
340         /* g_debug("reader is now running"); */
341         /* g_debug("waiting for prefill"); */
342         sem_wait(&player->reader.notify);
343         return 0;
344     }
345 }
346 
347 /*
348  * Terminate reader thread.  Playback must be stopped, since this
349  * function can garble the input buffer.
350  */
351 static void
terminate_reader_thread(Player * player)352 terminate_reader_thread(Player *player)
353 {
354     int space;
355     assert(player->reader.control != READER_TERM);
356     /* g_debug("terminating reader"); */
357     /* g_debug("locking reader mutex"); */
358     pthread_mutex_lock(&player->reader.control_cond_mutex);
359     /* g_debug("telling reader to terminate"); */
360     player->reader.control = READER_TERM;
361     sem_getvalue(&player->buffer.space, &space);
362     assert(space == 0 || space == 1);
363     if (space == 0) {
364         /* g_debug("signaling spaces in case reader is waiting"); */
365         sem_post(&player->buffer.space);
366     }
367     pthread_cond_signal(&player->reader.control_cond);
368     pthread_mutex_unlock(&player->reader.control_cond_mutex);
369     /* g_debug("waiting for reader to terminate"); */
370     pthread_join(player->reader.thread_id, NULL);
371     /* g_debug("reader has been terminated"); */
372 }
373 
374 /*
375  * Seek player.  Semantics are as in fseek, offset is in seconds.  If
376  * playback is in progress, it is continued from the new location.
377  * Return the new location, or -1 on error.
378  */
379 static double
seek(Player * player,double offset,int whence)380 seek(Player *player, double offset, int whence)
381 {
382     int space;
383     int state;
384     double loc;
385 
386     /* g_debug("seeking to %f, whence %d", offset, whence); */
387 
388     /* stop playback */
389     state = player->state.playback;
390     Pa_StopStream(player->stream);
391     stop_reader_thread(player);
392 
393     /* reset state */
394     switch (whence) {
395     case SEEK_SET:
396         loc = offset;
397         break;
398 
399     case SEEK_CUR:
400         loc = player->state.location + offset;
401         break;
402 
403     case SEEK_END:
404         loc = player->reader.metadata.duration + offset;
405         break;
406     }
407 
408     if (loc < 0 || loc > player->reader.metadata.duration) {
409         return -1;
410     }
411 
412     player->state.location = loc;
413     player->origin = loc;
414     player->nplayed = 0;
415     player->buffer.in = player->buffer.out = player->buffer.begin;
416     player->buffer.nframes = 0;
417     if (seek_sound_file(player->reader.sndfile, loc, SEEK_SET) == -1) {
418         return -1;
419     }
420 
421     sem_getvalue(&player->buffer.space, &space);
422     assert(space == 0 || space == 1);
423     if (space == 0) sem_post(&player->buffer.space);
424 
425     resume_reader_thread(player);
426     /* resume playback if it was in progress */
427     if (state == PLAYING) {
428         Pa_StartStream(player->stream);
429         player->state.playback = PLAYING;
430     }
431 
432     return loc;
433 }
434 
435 /*
436  * Controller thread.
437  *
438  * This thread polls an asynchronous message queue for command
439  * requests (stop playback, start playback, seek, etc.) and services
440  * them in the order or arrival.  This way multiple threads can access
441  * the same player in a serialized manner by calling the "API"
442  * functions.  A message can contain a semaphore reference, which is
443  * signalled after the corresponding command has been executed by the
444  * controller (in case the calling code wants to synchronize with the
445  * player).
446  */
447 static void *
controller_main(void * arg)448 controller_main(void *arg)
449 {
450     Player *player = (Player *) arg;
451     Message *msg;
452     int term = 0;
453 
454     for (;;) {
455         msg = (Message *) g_async_queue_pop(player->controller.messages);
456         /* g_debug("servicing new request %d", msg->command); */
457         switch (msg->command) {
458         case CMD_PAUSE:
459             switch (player->state.playback) {
460             case PLAYING:
461                 Pa_StopStream(player->stream);
462                 player->state.playback = PAUSED;
463                 break;
464             case PAUSED:
465                 Pa_StartStream(player->stream);
466                 player->state.playback = PLAYING;
467                 break;
468             default:
469                 break;
470             }
471             break;
472         case CMD_PLAY:
473             Pa_StartStream(player->stream);
474             player->state.playback = PLAYING;
475             break;
476         case CMD_SEEK:
477             seek(player, msg->offset, msg->whence);
478             break;
479         case CMD_STOP:
480             Pa_StopStream(player->stream);
481             player->state.playback = STOPPED;
482             break;
483         case CMD_TERM:
484             if (player->stream) {
485                 Pa_StopStream(player->stream);
486                 if (Pa_CloseStream(player->stream) != paNoError) {
487                     g_warning("failed to close playback stream");
488                 }
489                 player->stream = NULL;
490             }
491             if (player->reader.thread_ok) {
492                 terminate_reader_thread(player);
493             }
494             term = 1;
495             break;
496         }
497         /* g_debug("request handled"); */
498         if (msg->sem) sem_post(msg->sem);
499         g_free(msg);
500         if (term) {
501             /* g_debug("terminating controller thread"); */
502             pthread_exit(NULL);
503         }
504     }
505 
506     return NULL;
507 }
508 
509 /*
510  * Stream callback for PortAudio.  Fill the audio output buffer from
511  * the ring buffer, and signal space so that the reader thread can
512  * fill the ring buffer.
513  *
514  * Return PaContinue so that PortAudio will keep on playback, or
515  * PaComplete if EOF has been reached.
516  */
517 static int
stream_callback(const void * input,void * output,unsigned long nframes,const PaStreamCallbackTimeInfo * timeinfo,PaStreamCallbackFlags statusflags,void * userdata)518 stream_callback(const void *input, void *output,
519                 unsigned long nframes,
520                 const PaStreamCallbackTimeInfo *timeinfo,
521                 PaStreamCallbackFlags statusflags,
522                 void *userdata)
523 {
524     Player *player = (Player *) userdata;
525     char eof = 0;
526     int nchannels = player->reader.metadata.channels;
527     float *outbuf = (float *) output;
528 
529     player->nplayed += nframes;
530     while (nframes > 0) {
531         size_t i;
532         if (player->buffer.out == player->buffer.end) {
533             /* rewind the ring buffer */
534             /* g_debug("rewound the ring buffer"); */
535             player->buffer.out = player->buffer.begin;
536         }
537 
538         if (player->buffer.out == player->buffer.in) {
539             /* buffer underflow or eof, output silence */
540             if (!player->reader.is_eof) {
541                 g_warning("buffer underflow");
542             } else {
543                 /* g_debug("eof"); */
544                 eof = 1;
545             }
546             while (nframes > 0) {
547                 for (i = 0; i < nchannels; i++) {
548                     *outbuf++ = -1.0f;
549                 }
550                 nframes--;
551             }
552         } else {
553             for (i = 0; i < nchannels; i++) {
554                 *outbuf++ = *player->buffer.out++;
555             }
556             player->buffer.nframes--;
557             nframes--;
558         }
559     }
560 
561     /* update current playback location */
562     player->state.location = (player->origin
563                               + (1.0 * player->nplayed
564                                  / player->reader.metadata.rate));
565     /* g_debug("location %f", player->state.location); */
566 
567     if (player->buffer.nframes < SLACK * player->buffer.bufsize) {
568         int space;
569         sem_getvalue(&player->buffer.space, &space);
570         assert(space == 0 || space == 1);
571         if (space == 0) {
572             sem_post(&player->buffer.space);
573         }
574     }
575 
576     if (!eof) return paContinue;
577     else return paComplete;
578 }
579 
580 /*
581  * Stream finished callback for PortAudio.
582  */
583 static void
stream_finished_callback(void * userdata)584 stream_finished_callback(void *userdata)
585 {
586     Player *player = (Player *) userdata;
587     player->state.playback = STOPPED;
588 }
589 
590 /*
591  * Scan for default PortAudio host API output device.  Return the
592  * device index, or -1 if no device was found.
593  */
594 static PaDeviceIndex
scan_audio_output(void)595 scan_audio_output(void)
596 {
597     const PaDeviceInfo *devinfo;
598     PaDeviceIndex i;
599     PaDeviceIndex outdev = -1;
600     PaDeviceIndex ndevices;
601 
602     ndevices = Pa_GetDeviceCount();
603     if (ndevices < 0) {
604         g_warning("can't find available audio devices: %s",
605                   Pa_GetErrorText(ndevices));
606     }
607     for (i = 0; i < ndevices; i++) {
608         devinfo = Pa_GetDeviceInfo(i);
609         if (!devinfo) continue;
610         if (i == Pa_GetHostApiInfo
611             (devinfo->hostApi)->defaultOutputDevice) {
612             outdev = i;
613             /* g_debug("found default host api output device index %d", */
614             /*         outdev); */
615             break;
616         }
617     }
618     return outdev;
619 }
620 
621 /*
622  * Initialize a new player for given sound file.  Use the given
623  * PortAudio device for audio output, or scan for default host API
624  * output device if outdev = -1.
625  *
626  * Return handle to the new player, or NULL on error.
627  */
628 Player *
init_player(const char * filename,PaDeviceIndex outdev)629 init_player(const char *filename, PaDeviceIndex outdev)
630 {
631     Player *player;
632     PaError pa_rval;
633     size_t nbytes;
634     PaStreamParameters strparams;
635 
636     /* init player structure */
637     player = g_malloc(sizeof(Player));
638     player->stream = NULL;
639     player->reader.sndfile = NULL;
640     player->reader.sem_ok = player->reader.thread_ok = 0;
641     player->reader.is_eof = 0;
642     pthread_mutex_init(&player->reader.control_cond_mutex, NULL);
643     pthread_cond_init(&player->reader.control_cond, NULL);
644     player->controller.messages = NULL;
645     player->controller.thread_ok = 0;
646     player->buffer.begin = player->buffer.end = NULL;
647     player->buffer.in = player->buffer.out = NULL;
648     player->buffer.space_sem_ok = 0;
649     player->state.location = 0;
650     player->origin = 0;
651 
652     /* initialize audio */
653     pthread_mutex_lock(&audio_init_count_mutex);
654     if (audio_init_count == 0) {
655         pa_rval = Pa_Initialize();
656         /* scan for default device */
657 
658         if (pa_rval != paNoError) {
659             g_warning("can't initialize audio subsystem: %s",
660                       Pa_GetErrorText(pa_rval));
661             close_player(player);
662             pthread_mutex_unlock(&audio_init_count_mutex);
663             return NULL;
664         }
665 
666     }
667     audio_init_count++;
668     pthread_mutex_unlock(&audio_init_count_mutex);
669 
670     /* scan for default output device, if needed */
671     if (outdev == -1) outdev = scan_audio_output();
672     if (outdev == -1) {
673         g_warning("can't find default output audio device");
674         close_player(player);
675         return NULL;
676     }
677 
678     /* open sound file */
679     if ((player->reader.sndfile = open_sound_file(filename))) {
680         player->reader.metadata = get_metadata(player->reader.sndfile);
681     } else {
682         g_warning("can't open file '%s'", filename);
683         close_player(player);
684         return NULL;
685     }
686 
687     /* open playback stream */
688     memset(&strparams, 0, sizeof(PaStreamParameters));
689     strparams.channelCount = player->reader.metadata.channels;
690     strparams.device = outdev;
691     strparams.sampleFormat = paFloat32;
692     strparams.suggestedLatency =
693         Pa_GetDeviceInfo(outdev)->defaultHighOutputLatency;
694     strparams.hostApiSpecificStreamInfo = NULL;
695     pa_rval = Pa_OpenStream(&player->stream, NULL, &strparams,
696                             player->reader.metadata.rate, 0,
697                             paNoFlag, stream_callback,
698                             player);
699     if (pa_rval != paNoError) {
700         g_warning("can't open audio stream for playback: %s",
701                   Pa_GetErrorText(pa_rval));
702         player->stream = NULL;
703         close_player(player);
704         return NULL;
705     }
706     Pa_SetStreamFinishedCallback(player->stream, stream_finished_callback);
707 
708     /* calculate buffer size */
709     nbytes = 1024 * BUFFER_SIZE;
710     nbytes -= nbytes % (player->reader.metadata.channels
711                         * sizeof(float));
712     player->buffer.bufsize = nbytes / sizeof(float);
713     player->buffer.begin = (float *) g_malloc(nbytes);
714     player->buffer.end = player->buffer.begin + player->buffer.bufsize;
715     player->buffer.in = player->buffer.out = player->buffer.begin;
716     player->buffer.nframes = 0;
717     /* g_debug("allocated %d bytes of memory for sample buffer", nbytes); */
718 
719     /* initialize semaphores */
720     player->buffer.space_sem_ok =
721         (sem_init(&player->buffer.space, 0, 1) == 0);
722     player->reader.sem_ok =
723         (sem_init(&player->reader.sem, 0, 0) == 0);
724     player->reader.notify_ok =
725         (sem_init(&player->reader.notify, 0, 0) == 0);
726     if (!player->buffer.space_sem_ok
727         || !player->reader.sem_ok
728         || !player->reader.notify_ok) {
729         close_player(player);
730         return NULL;
731     }
732 
733     /* create threads */
734     if (!g_thread_supported()) g_thread_init(NULL);
735 
736     player->controller.messages = g_async_queue_new();
737     player->controller.thread_ok =
738         (pthread_create(&player->controller.thread_id, NULL,
739                         controller_main, player) == 0);
740     if (!player->controller.thread_ok) {
741         g_warning("can't create controller thread");
742         close_player(player);
743         return NULL;
744     }
745 
746     player->reader.control = READER_STOP;
747     player->reader.thread_ok =
748         (pthread_create(&player->reader.thread_id, NULL,
749                         reader_main, player) == 0);
750     if (!player->reader.thread_ok) {
751         g_warning("can't create reader thread");
752         close_player(player);
753         return NULL;
754     }
755     /* g_debug("waiting for reader"); */
756     sem_wait(&player->reader.sem);
757 
758     /* g_debug("player %p ok", (void *) player); */
759     return player;
760 }
761 
762 /*
763  * Close player and free allocated resources.
764  */
765 void
close_player(Player * player)766 close_player(Player *player)
767 {
768     Message *msg;
769     assert(player);
770 
771     /* g_debug("closing player %p", (void *) player); */
772 
773     if (player->controller.thread_ok) {
774         /* terminate threads */
775         msg = g_malloc(sizeof(Message));
776         msg->command = CMD_TERM;
777         msg->sem = NULL;
778         g_async_queue_push(player->controller.messages, msg);
779         pthread_join(player->controller.thread_id, NULL);
780     }
781 
782     if (player->reader.sndfile &&
783         close_sound_file(player->reader.sndfile) != 0) {
784         g_warning("failed to close sound file");
785     }
786 
787     pthread_mutex_lock(&audio_init_count_mutex);
788     audio_init_count--;
789     if (audio_init_count == 0) {
790         if (Pa_Terminate() != paNoError) {
791             g_warning("failed to shut down audio subsystem");
792         }
793     }
794     pthread_mutex_unlock(&audio_init_count_mutex);
795 
796     if (player->buffer.begin) g_free(player->buffer.begin);
797     pthread_mutex_destroy(&player->reader.control_cond_mutex);
798     pthread_cond_destroy(&player->reader.control_cond);
799     if (player->buffer.space_sem_ok) sem_destroy(&player->buffer.space);
800     if (player->reader.sem_ok) sem_destroy(&player->reader.sem);
801 
802     g_free(player);
803     /* g_debug("closed player %p", (void *) player); */
804 }
805 
806 /*
807  * Return sound file metadata from player.
808  */
809 Metadata
get_player_metadata(Player * player)810 get_player_metadata(Player *player)
811 {
812     assert(player);
813     return player->reader.metadata;
814 }
815 
816 /*
817  * Return the current playback state.
818  */
819 Player_state
get_player_state(Player * player)820 get_player_state(Player *player)
821 {
822     assert(player);
823     return player->state;
824 }
825 
826 /*
827  * Start playback.  If sem is non-NULL, it will be signalled after
828  * playback has been started.
829  */
830 void
start_player(Player * player,sem_t * sem)831 start_player(Player *player, sem_t *sem)
832 {
833     Message *msg;
834     assert(player);
835     msg = g_malloc(sizeof(Message));
836     msg->command = CMD_PLAY;
837     msg->sem = sem;
838     g_async_queue_push(player->controller.messages, msg);
839 }
840 
841 /*
842  * Stop playback.  If sem is non-NULL, it will be signalled after
843  * playback has been stopped.
844  */
845 void
stop_player(Player * player,sem_t * sem)846 stop_player(Player *player, sem_t *sem)
847 {
848     Message *msg;
849     assert(player);
850     msg = g_malloc(sizeof(Message));
851     msg->command = CMD_STOP;
852     msg->sem = sem;
853     g_async_queue_push(player->controller.messages, msg);
854 }
855 
856 /*
857  * Pause or resume playback.  If sem is not NULL,
858  * it will be signalled after seeking.
859  */
860 void
pause_or_resume_player(Player * player,sem_t * sem)861 pause_or_resume_player(Player *player, sem_t *sem)
862 {
863     Message *msg;
864     assert(player);
865     msg = g_malloc(sizeof(Message));
866     msg->command = CMD_PAUSE;
867     msg->sem = sem;
868     g_async_queue_push(player->controller.messages, msg);
869 }
870 
871 /*
872  * Seek player using seek function (which see).  If sem is not NULL,
873  * it will be signalled after seeking.
874  */
875 void
seek_player(Player * player,double offset,int whence,sem_t * sem)876 seek_player(Player *player, double offset, int whence, sem_t *sem)
877 {
878     Message *msg;
879     assert(player);
880     msg = g_malloc(sizeof(Message));
881     msg->command = CMD_SEEK;
882     msg->offset = offset;
883     msg->whence = whence;
884     msg->sem = sem;
885     g_async_queue_push(player->controller.messages, msg);
886 }
887