1 /* libmpdclient
2 (c) 2003-2019 The Music Player Daemon Project
3 This project's homepage is: http://www.musicpd.org
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 - Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 - Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15
16 - Neither the name of the Music Player Daemon nor the names of its
17 contributors may be used to endorse or promote products derived from
18 this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
24 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <mpd/status.h>
34 #include <mpd/pair.h>
35 #include <mpd/audio_format.h>
36 #include "iaf.h"
37
38 #include <assert.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 /**
43 * Information about MPD's current status.
44 */
45 struct mpd_status {
46 /** 0-100, or MPD_STATUS_NO_VOLUME when there is no volume support */
47 int volume;
48
49 /** Queue repeat mode enabled? */
50 bool repeat;
51
52 /** Random mode enabled? */
53 bool random;
54
55 /** Single song mode enabled? */
56 enum mpd_single_state single;
57
58 /** Song consume mode enabled? */
59 bool consume;
60
61 /** Number of songs in the queue */
62 unsigned queue_length;
63
64 /**
65 * Queue version, use this to determine when the playlist has
66 * changed.
67 */
68 unsigned queue_version;
69
70 /** MPD's current playback state */
71 enum mpd_state state;
72
73 /** crossfade setting in seconds */
74 unsigned crossfade;
75
76 /** Mixramp threshold in dB */
77 float mixrampdb;
78
79 /** Mixramp extra delay in seconds */
80 float mixrampdelay;
81
82 /**
83 * If a song is currently selected (always the case when state
84 * is PLAY or PAUSE), this is the position of the currently
85 * playing song in the queue, beginning with 0.
86 */
87 int song_pos;
88
89 /** Song ID of the currently selected song */
90 int song_id;
91
92 /** The same as song_pos, but for the next song to be played */
93 int next_song_pos;
94
95 /** Song ID of the next song to be played */
96 int next_song_id;
97
98 /**
99 * Time in seconds that have elapsed in the currently
100 * playing/paused song.
101 */
102 unsigned elapsed_time;
103
104 /**
105 * Time in milliseconds that have elapsed in the currently
106 * playing/paused song.
107 */
108 unsigned elapsed_ms;
109
110 /** length in seconds of the currently playing/paused song */
111 unsigned total_time;
112
113 /** current bit rate in kbps */
114 unsigned kbit_rate;
115
116 /** the current audio format */
117 struct mpd_audio_format audio_format;
118
119 /** non-zero if MPD is updating, 0 otherwise */
120 unsigned update_id;
121
122 /** the name of the current partition */
123 char *partition;
124
125 /** error message */
126 char *error;
127 };
128
129 struct mpd_status *
mpd_status_begin(void)130 mpd_status_begin(void)
131 {
132 struct mpd_status *status = malloc(sizeof(*status));
133 if (status == NULL)
134 return NULL;
135
136 status->volume = -1;
137 status->repeat = false;
138 status->random = false;
139 status->single = MPD_SINGLE_OFF;
140 status->consume = false;
141 status->queue_version = 0;
142 status->queue_length = 0;
143 status->state = MPD_STATE_UNKNOWN;
144 status->song_pos = -1;
145 status->song_id = -1;
146 status->next_song_pos = -1;
147 status->next_song_id = -1;
148 status->elapsed_time = 0;
149 status->elapsed_ms = 0;
150 status->total_time = 0;
151 status->kbit_rate = 0;
152 memset(&status->audio_format, 0, sizeof(status->audio_format));
153 status->crossfade = 0;
154 status->mixrampdb = 100.0;
155 status->mixrampdelay = -1.0;
156 status->partition = NULL;
157 status->error = NULL;
158 status->update_id = 0;
159
160 return status;
161 }
162
163 /**
164 * Parses the fractional part of the "elapsed" response line. Up to
165 * three digits are parsed.
166 */
167 static unsigned
parse_ms(const char * p)168 parse_ms(const char *p)
169 {
170 unsigned ms;
171
172 if (*p >= '0' && *p <= '9')
173 ms = 100 * (*p++ - '0');
174 else
175 return 0;
176
177 if (*p >= '0' && *p <= '9')
178 ms += 10 * (*p - '0');
179 else
180 return ms;
181
182 if (*p >= '0' && *p <= '9')
183 ms += *p - '0';
184
185 return ms;
186 }
187
188 static enum mpd_state
parse_mpd_state(const char * p)189 parse_mpd_state(const char *p)
190 {
191 if (strcmp(p, "play") == 0)
192 return MPD_STATE_PLAY;
193 else if (strcmp(p, "stop") == 0)
194 return MPD_STATE_STOP;
195 else if (strcmp(p, "pause") == 0)
196 return MPD_STATE_PAUSE;
197 else
198 return MPD_STATE_UNKNOWN;
199 }
200
201 static enum mpd_single_state
parse_mpd_single_state(const char * p)202 parse_mpd_single_state(const char *p)
203 {
204 if (strcmp(p, "0") == 0)
205 return MPD_SINGLE_OFF;
206 else if (strcmp(p, "1") == 0)
207 return MPD_SINGLE_ON;
208 else if (strcmp(p, "oneshot") == 0)
209 return MPD_SINGLE_ONESHOT;
210 else
211 return MPD_SINGLE_UNKNOWN;
212 }
213
214 void
mpd_status_feed(struct mpd_status * status,const struct mpd_pair * pair)215 mpd_status_feed(struct mpd_status *status, const struct mpd_pair *pair)
216 {
217 assert(status != NULL);
218 assert(pair != NULL);
219
220 if (strcmp(pair->name, "volume") == 0)
221 status->volume = atoi(pair->value);
222 else if (strcmp(pair->name, "repeat") == 0)
223 status->repeat = !!atoi(pair->value);
224 else if (strcmp(pair->name, "random") == 0)
225 status->random = !!atoi(pair->value);
226 else if (strcmp(pair->name, "single") == 0)
227 status->single = parse_mpd_single_state(pair->value);
228 else if (strcmp(pair->name, "consume") == 0)
229 status->consume = !!atoi(pair->value);
230 else if (strcmp(pair->name, "playlist") == 0)
231 status->queue_version = strtoul(pair->value, NULL, 10);
232 else if (strcmp(pair->name, "playlistlength") == 0)
233 status->queue_length = atoi(pair->value);
234 else if (strcmp(pair->name, "bitrate") == 0)
235 status->kbit_rate = atoi(pair->value);
236 else if (strcmp(pair->name, "state") == 0)
237 status->state = parse_mpd_state(pair->value);
238 else if (strcmp(pair->name, "song") == 0)
239 status->song_pos = atoi(pair->value);
240 else if (strcmp(pair->name, "songid") == 0)
241 status->song_id = atoi(pair->value);
242 else if (strcmp(pair->name, "nextsong") == 0)
243 status->next_song_pos = atoi(pair->value);
244 else if (strcmp(pair->name, "nextsongid") == 0)
245 status->next_song_id = atoi(pair->value);
246 else if (strcmp(pair->name, "time") == 0) {
247 char *endptr;
248
249 status->elapsed_time = strtoul(pair->value, &endptr, 10);
250 if (*endptr == ':')
251 status->total_time = strtoul(endptr + 1, NULL, 10);
252
253 if (status->elapsed_ms == 0)
254 status->elapsed_ms = status->elapsed_time * 1000;
255 } else if (strcmp(pair->name, "elapsed") == 0) {
256 char *endptr;
257
258 status->elapsed_ms = strtoul(pair->value, &endptr, 10) * 1000;
259 if (*endptr == '.')
260 status->elapsed_ms += parse_ms(endptr + 1);
261
262 if (status->elapsed_time == 0)
263 status->elapsed_time = status->elapsed_ms / 1000;
264 } else if (strcmp(pair->name, "partition") == 0) {
265 free(status->partition);
266 status->partition = strdup(pair->value);
267 } else if (strcmp(pair->name, "error") == 0) {
268 free(status->error);
269 status->error = strdup(pair->value);
270 } else if (strcmp(pair->name, "xfade") == 0)
271 status->crossfade = atoi(pair->value);
272 else if (strcmp(pair->name, "mixrampdb") == 0)
273 status->mixrampdb = atof(pair->value);
274 else if (strcmp(pair->name, "mixrampdelay") == 0)
275 status->mixrampdelay = atof(pair->value);
276 else if (strcmp(pair->name, "updating_db") == 0)
277 status->update_id = atoi(pair->value);
278 else if (strcmp(pair->name, "audio") == 0)
279 mpd_parse_audio_format(&status->audio_format, pair->value);
280 }
281
mpd_status_free(struct mpd_status * status)282 void mpd_status_free(struct mpd_status * status)
283 {
284 assert(status != NULL);
285
286 free(status->partition);
287 free(status->error);
288 free(status);
289 }
290
mpd_status_get_volume(const struct mpd_status * status)291 int mpd_status_get_volume(const struct mpd_status *status)
292 {
293 assert(status != NULL);
294
295 return status->volume;
296 }
297
298 bool
mpd_status_get_repeat(const struct mpd_status * status)299 mpd_status_get_repeat(const struct mpd_status *status)
300 {
301 assert(status != NULL);
302
303 return status->repeat;
304 }
305
306 bool
mpd_status_get_random(const struct mpd_status * status)307 mpd_status_get_random(const struct mpd_status *status)
308 {
309 assert(status != NULL);
310
311 return status->random;
312 }
313
314 enum mpd_single_state
mpd_status_get_single_state(const struct mpd_status * status)315 mpd_status_get_single_state(const struct mpd_status *status)
316 {
317 assert(status != NULL);
318
319 return status->single;
320 }
321
322 bool
mpd_status_get_single(const struct mpd_status * status)323 mpd_status_get_single(const struct mpd_status *status)
324 {
325 assert(status != NULL);
326
327 return status->single == MPD_SINGLE_ONESHOT ||
328 status->single == MPD_SINGLE_ON;
329 }
330
331 bool
mpd_status_get_consume(const struct mpd_status * status)332 mpd_status_get_consume(const struct mpd_status *status)
333 {
334 assert(status != NULL);
335
336 return status->consume;
337 }
338
339 unsigned
mpd_status_get_queue_length(const struct mpd_status * status)340 mpd_status_get_queue_length(const struct mpd_status *status)
341 {
342 assert(status != NULL);
343
344 return status->queue_length;
345 }
346
347 unsigned
mpd_status_get_queue_version(const struct mpd_status * status)348 mpd_status_get_queue_version(const struct mpd_status *status)
349 {
350 assert(status != NULL);
351
352 return status->queue_version;
353 }
354
355 enum mpd_state
mpd_status_get_state(const struct mpd_status * status)356 mpd_status_get_state(const struct mpd_status *status)
357 {
358 assert(status != NULL);
359
360 return status->state;
361 }
362
363 unsigned
mpd_status_get_crossfade(const struct mpd_status * status)364 mpd_status_get_crossfade(const struct mpd_status *status)
365 {
366 assert(status != NULL);
367
368 return status->crossfade;
369 }
370
371 float
mpd_status_get_mixrampdb(const struct mpd_status * status)372 mpd_status_get_mixrampdb(const struct mpd_status *status)
373 {
374 assert(status != NULL);
375
376 return status->mixrampdb;
377 }
378
379 float
mpd_status_get_mixrampdelay(const struct mpd_status * status)380 mpd_status_get_mixrampdelay(const struct mpd_status *status)
381 {
382 assert(status != NULL);
383
384 return status->mixrampdelay;
385 }
386
387 int
mpd_status_get_song_pos(const struct mpd_status * status)388 mpd_status_get_song_pos(const struct mpd_status *status)
389 {
390 assert(status != NULL);
391
392 return status->song_pos;
393 }
394
395 int
mpd_status_get_song_id(const struct mpd_status * status)396 mpd_status_get_song_id(const struct mpd_status *status)
397 {
398 assert(status != NULL);
399
400 return status->song_id;
401 }
402
403 int
mpd_status_get_next_song_pos(const struct mpd_status * status)404 mpd_status_get_next_song_pos(const struct mpd_status *status)
405 {
406 assert(status != NULL);
407
408 return status->next_song_pos;
409 }
410
411 int
mpd_status_get_next_song_id(const struct mpd_status * status)412 mpd_status_get_next_song_id(const struct mpd_status *status)
413 {
414 return status->next_song_id;
415 }
416
417 unsigned
mpd_status_get_elapsed_time(const struct mpd_status * status)418 mpd_status_get_elapsed_time(const struct mpd_status *status)
419 {
420 assert(status != NULL);
421
422 return status->elapsed_time;
423 }
424
425 unsigned
mpd_status_get_elapsed_ms(const struct mpd_status * status)426 mpd_status_get_elapsed_ms(const struct mpd_status *status)
427 {
428 assert(status != NULL);
429
430 return status->elapsed_ms;
431 }
432
433 unsigned
mpd_status_get_total_time(const struct mpd_status * status)434 mpd_status_get_total_time(const struct mpd_status *status)
435 {
436 assert(status != NULL);
437
438 return status->total_time;
439 }
440
441 unsigned
mpd_status_get_kbit_rate(const struct mpd_status * status)442 mpd_status_get_kbit_rate(const struct mpd_status *status)
443 {
444 assert(status != NULL);
445
446 return status->kbit_rate;
447 }
448
449 const struct mpd_audio_format *
mpd_status_get_audio_format(const struct mpd_status * status)450 mpd_status_get_audio_format(const struct mpd_status *status)
451 {
452 assert(status != NULL);
453
454 return !mpd_audio_format_is_empty(&status->audio_format)
455 ? &status->audio_format
456 : NULL;
457 }
458
459 unsigned
mpd_status_get_update_id(const struct mpd_status * status)460 mpd_status_get_update_id(const struct mpd_status *status)
461 {
462 assert(status != NULL);
463
464 return status->update_id;
465 }
466
467 const char *
mpd_status_get_partition(const struct mpd_status * status)468 mpd_status_get_partition(const struct mpd_status *status)
469 {
470 assert(status != NULL);
471
472 return status->partition;
473 }
474
475 const char *
mpd_status_get_error(const struct mpd_status * status)476 mpd_status_get_error(const struct mpd_status *status)
477 {
478 assert(status != NULL);
479
480 return status->error;
481 }
482