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