1 /* playback.c - high-level audio playback implementation
2  *
3  * Copyright 2010 Petteri Hintsanen <petterih@iki.fi>
4  *
5  * This file is part of abx.
6  *
7  * abx is free software: you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * abx is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
15  * License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with abx.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "playback.h"
22 #include "player.h"
23 #include <assert.h>
24 #include <stdio.h>
25 #include <glib.h>
26 
27 static Player *player_a;        /* Player for the first sample. */
28 static Player *player_b;        /* Player for the second sample. */
29 static Player *current_player;  /* Current player, if playback is in
30                                  * progress. */
31 static sem_t semaphore;         /* Semaphore for synchronizing with
32                                  * the players. */
33 
34 /*
35  * Initialize playback using sample files a and b.  Samples must have
36  * equal duration.
37  *
38  * Return
39  *   0 on success,
40  *   1 if playback has been already initialized,
41  *   2 if player for a could not be initialized,
42  *   3 if player for b could not be initialized, or
43  *   4 if the samples have different duration.
44  */
45 int
init_playback(const char * a,const char * b,PaDeviceIndex outdev)46 init_playback(const char *a, const char *b, PaDeviceIndex outdev)
47 {
48     Metadata ma;
49     Metadata mb;
50     Player *pa;
51     Player *pb;
52 
53     if (player_a) {
54         assert(player_b);
55         return 1;
56     }
57 
58     if (!(pa = init_player(a, outdev))) {
59         g_warning("can't initialize player for '%s'", a);
60         return 2;
61     }
62 
63     if (!(pb = init_player(b, outdev))) {
64         g_warning("can't initialize player for '%s'", b);
65         close_player(pa);
66         return 3;
67     }
68 
69     ma = get_player_metadata(pa);
70     mb = get_player_metadata(pb);
71     if (ma.duration != mb.duration) {
72         g_warning("samples '%s' and '%s' have different duration", a, b);
73         close_player(pa);
74         close_player(pb);
75         return 4;
76     }
77 
78     player_a = pa;
79     player_b = pb;
80     sem_init(&semaphore, 0, 0);
81     return 0;
82 }
83 
84 /*
85  * Close playback and release resources.
86  */
87 void
close_playback(void)88 close_playback(void)
89 {
90     close_player(player_a);
91     close_player(player_b);
92     player_a = player_b = current_player = NULL;
93     sem_destroy(&semaphore);
94 }
95 
96 /*
97  * Fill in given metadata structures from initialized players.
98  */
99 void
get_metadatas(Metadata * a,Metadata * b)100 get_metadatas(Metadata *a, Metadata *b)
101 {
102     assert(a && b);
103     *a = get_player_metadata(player_a);
104     *b = get_player_metadata(player_b);
105 }
106 
107 /*
108  * Get the current playback state and store it to *state.  Return the
109  * current sample id, or -1 if no playback is in progress.
110  */
111 int
get_playback_state(Player_state * state)112 get_playback_state(Player_state *state)
113 {
114     assert(state);
115     if (!current_player) {
116         return -1;
117     } else if (current_player == player_a) {
118         *state = get_player_state(player_a);
119         return 0;
120     } else {
121         *state = get_player_state(player_b);
122         return 1;
123     }
124 }
125 
126 /*
127  * Play sample from the given location (in seconds).  This function
128  * blocks until the playback has been started.
129  */
130 void
start_playback(int sample,double location)131 start_playback(int sample, double location)
132 {
133     Player *pl;
134     /* g_debug("starting playback of sample %d from %f", sample, location); */
135     if (sample == 0) pl = player_a;
136     else pl = player_b;
137     stop_playback();
138     seek_player(pl, location, SEEK_SET, NULL);
139     start_player(pl, &semaphore);
140     current_player = pl;
141     sem_wait(&semaphore);
142 }
143 
144 /*
145  * Stop playback.  This function blocks until the playback has been
146  * stopped.
147  */
148 void
stop_playback(void)149 stop_playback(void)
150 {
151     if (current_player == NULL) return;
152     stop_player(current_player, &semaphore);
153     sem_wait(&semaphore);
154     current_player = NULL;
155 }
156 
157 /*
158  * Pause or resume playback.  This function blocks until the playback
159  * request has been serviced by the controller thread.
160  *
161  * Return 0 if the playback was paused, 1 if the playback was resumed,
162  * or 2 if there is no current player or playback has been stopped.
163  */
164 int
pause_or_resume_playback(void)165 pause_or_resume_playback(void)
166 {
167     Player_state state;
168     if (current_player == NULL) return 2;
169     pause_or_resume_player(current_player, &semaphore);
170     sem_wait(&semaphore);
171     get_playback_state(&state);
172     switch (state.playback) {
173     case PAUSED:
174         return 0;
175     case PLAYING:
176         return 1;
177     case STOPPED:
178         return 2;
179     };
180     return 2;
181 }
182 
183 /*
184  * Reposition current player to location (in seconds, measured from
185  * the beginning).  This function blocks until the seek has been
186  * completed.
187  */
188 void
seek_playback(double offset)189 seek_playback(double offset)
190 {
191     if (current_player == NULL) return;
192     seek_player(current_player, offset, SEEK_SET, &semaphore);
193     sem_wait(&semaphore);
194 }
195