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