player.c 6.37 KB
Newer Older
Warren Dukes's avatar
Warren Dukes committed
1
/* the Music Player Daemon (MPD)
2
 * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
Warren Dukes's avatar
Warren Dukes committed
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * This project's homepage is: http://www.musicpd.org
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "player.h"
Eric Wong's avatar
Eric Wong committed
20
#include "path.h"
Warren Dukes's avatar
Warren Dukes committed
21 22 23
#include "command.h"
#include "log.h"
#include "playerData.h"
Max Kellermann's avatar
Max Kellermann committed
24
#include "ack.h"
25
#include "os_compat.h"
26
#include "main_notify.h"
Warren Dukes's avatar
Warren Dukes committed
27

Eric Wong's avatar
Eric Wong committed
28 29
static void playerCloseAudio(void);

30
static void wakeup_player(void)
31
{
32
	notify_signal(&pc.notify);
33
	wait_main_task();
34
}
35

36
static void * player_task(mpd_unused void *arg)
37
{
38
	notify_enter(&pc.notify);
39

40
	while (1) {
41
		if (pc.play) {
42 43
			decode();
			continue; /* decode() calls wakeup_main_task */
44 45 46 47 48 49 50
		} else if (pc.stop) {
			pc.stop = 0;
		} else if (pc.seek) {
			pc.seek = 0;
		} else if (pc.pause) {
			pc.pause = 0;
		} else if (pc.closeAudio) {
51
			closeAudioDevice();
52 53 54 55 56 57 58
			pc.closeAudio = 0;
		} else if (pc.lockQueue) {
			pc.queueLockState = PLAYER_QUEUE_LOCKED;
			pc.lockQueue = 0;
		} else if (pc.unlockQueue) {
			pc.queueLockState = PLAYER_QUEUE_UNLOCKED;
			pc.unlockQueue = 0;
59
		} else {
60
			notify_wait(&pc.notify);
61
			continue;
62
		}
63 64
		/* we did something, tell the main task about it */
		wakeup_main_task();
Warren Dukes's avatar
Warren Dukes committed
65
	}
66
	return NULL;
Warren Dukes's avatar
Warren Dukes committed
67 68
}

69
void playerInit(void)
70 71 72
{
	pthread_attr_t attr;
	pthread_t player_thread;
Warren Dukes's avatar
Warren Dukes committed
73

74 75
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
76
	if (pthread_create(&player_thread, &attr, player_task, NULL))
77
		FATAL("Failed to spawn player task: %s\n", strerror(errno));
Warren Dukes's avatar
Warren Dukes committed
78 79
}

80
int playerWait(int fd)
81 82 83 84 85 86 87 88 89
{
	if (playerStop(fd) < 0)
		return -1;

	playerCloseAudio();

	return 0;
}

90
static void set_current_song(Song *song)
91
{
92
	pc.fileTime = song->tag ? song->tag->time : 0;
93
	pc.next_song = song;
94 95
}

96
int playerPlay(int fd, Song * song)
Avuton Olrich's avatar
Avuton Olrich committed
97
{
98
	if (playerStop(fd) < 0)
Avuton Olrich's avatar
Avuton Olrich committed
99
		return -1;
Warren Dukes's avatar
Warren Dukes committed
100

101
	set_current_song(song);
Warren Dukes's avatar
Warren Dukes committed
102

103
	pc.play = 1;
104
	/* FIXME: _nb() variant is probably wrong here, and everywhere... */
105
	do { notify_signal(&pc.notify); } while (pc.play);
Avuton Olrich's avatar
Avuton Olrich committed
106

Warren Dukes's avatar
Warren Dukes committed
107 108 109
	return 0;
}

110
int playerStop(mpd_unused int fd)
Avuton Olrich's avatar
Avuton Olrich committed
111
{
112 113 114
	if (pc.state != PLAYER_STATE_STOP) {
		pc.stop = 1;
		do { wakeup_player(); } while (pc.stop);
Warren Dukes's avatar
Warren Dukes committed
115 116
	}

117
	pc.queueState = PLAYER_QUEUE_BLANK;
Warren Dukes's avatar
Warren Dukes committed
118 119 120 121 122
	playerQueueUnlock();

	return 0;
}

123
void playerKill(void) /* deprecated */
Avuton Olrich's avatar
Avuton Olrich committed
124
{
125
	playerPause(STDERR_FILENO);
Warren Dukes's avatar
Warren Dukes committed
126 127
}

128
int playerPause(mpd_unused int fd)
Avuton Olrich's avatar
Avuton Olrich committed
129
{
130 131 132
	if (pc.state != PLAYER_STATE_STOP) {
		pc.pause = 1;
		do { wakeup_player(); } while (pc.pause);
Warren Dukes's avatar
Warren Dukes committed
133 134
	}

135 136 137
	return 0;
}

Max Kellermann's avatar
Max Kellermann committed
138
int playerSetPause(int fd, int pause_flag)
Avuton Olrich's avatar
Avuton Olrich committed
139
{
140
	switch (pc.state) {
141
	case PLAYER_STATE_PLAY:
Max Kellermann's avatar
Max Kellermann committed
142
		if (pause_flag)
143
			playerPause(fd);
144 145
		break;
	case PLAYER_STATE_PAUSE:
Max Kellermann's avatar
Max Kellermann committed
146
		if (!pause_flag)
147
			playerPause(fd);
148 149 150
		break;
	}

Warren Dukes's avatar
Warren Dukes committed
151 152 153
	return 0;
}

Eric Wong's avatar
Eric Wong committed
154
int getPlayerElapsedTime(void)
Avuton Olrich's avatar
Avuton Olrich committed
155
{
156
	return (int)(pc.elapsedTime + 0.5);
Warren Dukes's avatar
Warren Dukes committed
157 158
}

Eric Wong's avatar
Eric Wong committed
159
unsigned long getPlayerBitRate(void)
Avuton Olrich's avatar
Avuton Olrich committed
160
{
161
	return pc.bitRate;
Warren Dukes's avatar
Warren Dukes committed
162 163
}

Eric Wong's avatar
Eric Wong committed
164
int getPlayerTotalTime(void)
Avuton Olrich's avatar
Avuton Olrich committed
165
{
166
	return (int)(pc.totalTime + 0.5);
Warren Dukes's avatar
Warren Dukes committed
167 168
}

Eric Wong's avatar
Eric Wong committed
169
int getPlayerState(void)
Avuton Olrich's avatar
Avuton Olrich committed
170
{
171
	return pc.state;
Warren Dukes's avatar
Warren Dukes committed
172 173
}

Eric Wong's avatar
Eric Wong committed
174
void clearPlayerError(void)
Avuton Olrich's avatar
Avuton Olrich committed
175
{
176
	pc.error = 0;
Warren Dukes's avatar
Warren Dukes committed
177 178
}

Eric Wong's avatar
Eric Wong committed
179
int getPlayerError(void)
Avuton Olrich's avatar
Avuton Olrich committed
180
{
181
	return pc.error;
Warren Dukes's avatar
Warren Dukes committed
182 183
}

Eric Wong's avatar
Eric Wong committed
184
char *getPlayerErrorStr(void)
Avuton Olrich's avatar
Avuton Olrich committed
185
{
186 187 188 189 190
	/* static OK here, only one user in main task */
	static char error[MPD_PATH_MAX + 64]; /* still too much */
	static const size_t errorlen = sizeof(error);
	char path_max_tmp[MPD_PATH_MAX];
	*error = '\0'; /* likely */
191

192
	switch (pc.error) {
Warren Dukes's avatar
Warren Dukes committed
193
	case PLAYER_ERROR_FILENOTFOUND:
Avuton Olrich's avatar
Avuton Olrich committed
194
		snprintf(error, errorlen,
Avuton Olrich's avatar
Avuton Olrich committed
195
			 "file \"%s\" does not exist or is inaccessible",
196
			 get_song_url(path_max_tmp, pc.errored_song));
197
		break;
Warren Dukes's avatar
Warren Dukes committed
198
	case PLAYER_ERROR_FILE:
Avuton Olrich's avatar
Avuton Olrich committed
199
		snprintf(error, errorlen, "problems decoding \"%s\"",
200
			 get_song_url(path_max_tmp, pc.errored_song));
201
		break;
Warren Dukes's avatar
Warren Dukes committed
202
	case PLAYER_ERROR_AUDIO:
203
		strcpy(error, "problems opening audio device");
204
		break;
Warren Dukes's avatar
Warren Dukes committed
205
	case PLAYER_ERROR_SYSTEM:
206
		strcpy(error, "system error occured");
207
		break;
Warren Dukes's avatar
Warren Dukes committed
208
	case PLAYER_ERROR_UNKTYPE:
209
		snprintf(error, errorlen, "file type of \"%s\" is unknown",
210
			 get_song_url(path_max_tmp, pc.errored_song));
Warren Dukes's avatar
Warren Dukes committed
211
	}
212
	return *error ? error : NULL;
Warren Dukes's avatar
Warren Dukes committed
213 214
}

Eric Wong's avatar
Eric Wong committed
215
static void playerCloseAudio(void)
Avuton Olrich's avatar
Avuton Olrich committed
216
{
217 218
	if (playerStop(STDERR_FILENO) < 0)
		return;
219 220
	pc.closeAudio = 1;
	do { wakeup_player(); } while (pc.closeAudio);
Warren Dukes's avatar
Warren Dukes committed
221 222
}

Avuton Olrich's avatar
Avuton Olrich committed
223 224
int queueSong(Song * song)
{
225 226 227
	if (pc.queueState == PLAYER_QUEUE_BLANK) {
		set_current_song(song);
		pc.queueState = PLAYER_QUEUE_FULL;
Warren Dukes's avatar
Warren Dukes committed
228 229 230 231 232 233
		return 0;
	}

	return -1;
}

Eric Wong's avatar
Eric Wong committed
234
int getPlayerQueueState(void)
Avuton Olrich's avatar
Avuton Olrich committed
235
{
236
	return pc.queueState;
Warren Dukes's avatar
Warren Dukes committed
237 238
}

Avuton Olrich's avatar
Avuton Olrich committed
239 240
void setQueueState(int queueState)
{
241
	pc.queueState = queueState;
242
	notify_signal(&pc.notify);
Warren Dukes's avatar
Warren Dukes committed
243 244
}

Eric Wong's avatar
Eric Wong committed
245
void playerQueueLock(void)
Avuton Olrich's avatar
Avuton Olrich committed
246
{
247 248 249
	if (pc.queueLockState == PLAYER_QUEUE_UNLOCKED) {
		pc.lockQueue = 1;
		do { wakeup_player(); } while (pc.lockQueue);
Warren Dukes's avatar
Warren Dukes committed
250 251 252
	}
}

Eric Wong's avatar
Eric Wong committed
253
void playerQueueUnlock(void)
Avuton Olrich's avatar
Avuton Olrich committed
254
{
255 256 257
	if (pc.queueLockState == PLAYER_QUEUE_LOCKED) {
		pc.unlockQueue = 1;
		do { wakeup_player(); } while (pc.unlockQueue);
Warren Dukes's avatar
Warren Dukes committed
258 259 260
	}
}

Max Kellermann's avatar
Max Kellermann committed
261
int playerSeek(int fd, Song * song, float seek_time)
Avuton Olrich's avatar
Avuton Olrich committed
262
{
Max Kellermann's avatar
Max Kellermann committed
263 264
	assert(song != NULL);

265
	if (pc.state == PLAYER_STATE_STOP) {
266
		commandError(fd, ACK_ERROR_PLAYER_SYNC,
267
			     "player not currently playing");
Warren Dukes's avatar
Warren Dukes committed
268 269 270
		return -1;
	}

271
	if (pc.next_song != song)
272
		set_current_song(song);
Warren Dukes's avatar
Warren Dukes committed
273

274 275 276
	if (pc.error == PLAYER_ERROR_NOERROR) {
		pc.seekWhere = seek_time;
		pc.seek = 1;
277
		/* FIXME: _nb() is probably wrong here, too */
278
		do { notify_signal(&pc.notify); } while (pc.seek);
Warren Dukes's avatar
Warren Dukes committed
279 280 281 282 283
	}

	return 0;
}

Eric Wong's avatar
Eric Wong committed
284
float getPlayerCrossFade(void)
Avuton Olrich's avatar
Avuton Olrich committed
285
{
286
	return pc.crossFade;
Warren Dukes's avatar
Warren Dukes committed
287 288
}

Avuton Olrich's avatar
Avuton Olrich committed
289 290 291 292
void setPlayerCrossFade(float crossFadeInSeconds)
{
	if (crossFadeInSeconds < 0)
		crossFadeInSeconds = 0;
293
	pc.crossFade = crossFadeInSeconds;
Warren Dukes's avatar
Warren Dukes committed
294 295
}

Avuton Olrich's avatar
Avuton Olrich committed
296 297 298
void setPlayerSoftwareVolume(int volume)
{
	volume = (volume > 1000) ? 1000 : (volume < 0 ? 0 : volume);
299
	pc.softwareVolume = volume;
Warren Dukes's avatar
Warren Dukes committed
300 301
}

Eric Wong's avatar
Eric Wong committed
302
double getPlayerTotalPlayTime(void)
Avuton Olrich's avatar
Avuton Olrich committed
303
{
304
	return pc.totalPlayTime;
Warren Dukes's avatar
Warren Dukes committed
305
}
306

Eric Wong's avatar
Eric Wong committed
307
unsigned int getPlayerSampleRate(void)
Avuton Olrich's avatar
Avuton Olrich committed
308
{
309
	return pc.sampleRate;
310 311
}

Eric Wong's avatar
Eric Wong committed
312
int getPlayerBits(void)
Avuton Olrich's avatar
Avuton Olrich committed
313
{
314
	return pc.bits;
315 316
}

Eric Wong's avatar
Eric Wong committed
317
int getPlayerChannels(void)
Avuton Olrich's avatar
Avuton Olrich committed
318
{
319
	return pc.channels;
320
}
321

322
/* this actually creates a dupe of the current metadata */
Eric Wong's avatar
Eric Wong committed
323
Song *playerCurrentDecodeSong(void)
Avuton Olrich's avatar
Avuton Olrich committed
324
{
325
	return NULL;
326
}