Exemple #1
0
struct song *
playlist_check_translate_song(struct song *song, const char *base_uri,
			      bool secure)
{
	if (song_in_database(song))
		/* already ok */
		return song;

	const char *uri = song->uri;

	if (uri_has_scheme(uri)) {
		if (uri_supported_scheme(uri))
			/* valid remote song */
			return song;
		else {
			/* unsupported remote song */
			song_free(song);
			return NULL;
		}
	}

	if (base_uri != NULL && strcmp(base_uri, ".") == 0)
		/* g_path_get_dirname() returns "." when there is no
		   directory name in the given path; clear that now,
		   because it would break the database lookup
		   functions */
		base_uri = NULL;

	if (g_path_is_absolute(uri)) {
		/* XXX fs_charset vs utf8? */
		const char *suffix = map_to_relative_path(uri);
		assert(suffix != NULL);

		if (suffix != uri)
			uri = suffix;
		else if (!secure) {
			/* local files must be relative to the music
			   directory when "secure" is enabled */
			song_free(song);
			return NULL;
		}

		base_uri = NULL;
	}

	char *allocated = NULL;
	if (base_uri != NULL)
		uri = allocated = g_build_filename(base_uri, uri, NULL);

	struct song *dest = playlist_check_load_song(song, uri, secure);
	song_free(song);
	g_free(allocated);
	return dest;
}
Exemple #2
0
/**
 * After the decoder has been started asynchronously, wait for the
 * "START" command to finish.  The decoder may not be initialized yet,
 * i.e. there is no audio_format information yet.
 *
 * The player lock is not held.
 */
static bool
player_wait_for_decoder(struct player *player)
{
	struct player_control *pc = player->pc;
	struct decoder_control *dc = player->dc;

	assert(player->queued || pc->command == PLAYER_COMMAND_SEEK);
	assert(pc->next_song != NULL);

	player->queued = false;

	GError *error = dc_lock_get_error(dc);
	if (error != NULL) {
		player_lock(pc);
		pc_set_error(pc, PLAYER_ERROR_DECODER, error);

		song_free(pc->next_song);
		pc->next_song = NULL;

		player_unlock(pc);

		return false;
	}

	if (player->song != NULL)
		song_free(player->song);

	player->song = pc->next_song;
	player->elapsed_time = 0.0;

	/* set the "starting" flag, which will be cleared by
	   player_check_decoder_startup() */
	player->decoder_starting = true;

	player_lock(pc);

	/* update player_control's song information */
	pc->total_time = song_get_duration(pc->next_song);
	pc->bit_rate = 0;
	audio_format_clear(&pc->audio_format);

	/* clear the queued song */
	pc->next_song = NULL;

	player_unlock(pc);

	/* call syncPlaylistWithQueue() in the main thread */
	event_pipe_emit(PIPE_EVENT_PLAYLIST);

	return true;
}
Exemple #3
0
static struct song *
apply_song_metadata(struct song *dest, const struct song *src)
{
	struct song *tmp;

	assert(dest != NULL);
	assert(src != NULL);

	if (src->tag == NULL && src->start_ms == 0 && src->end_ms == 0)
		return dest;

	if (song_in_database(dest)) {
		char *path_fs = map_song_fs(dest);
		if (path_fs == NULL)
			return dest;

		tmp = song_file_new(path_fs, NULL);
		g_free(path_fs);

		merge_song_metadata(tmp, dest, src);
	} else {
		tmp = song_file_new(dest->uri, NULL);
		merge_song_metadata(tmp, dest, src);
		song_free(dest);
	}

	return tmp;
}
Exemple #4
0
struct song *
song_file_load(const char *path, struct directory *parent)
{
	struct song *song;
	bool ret;

	assert((parent == NULL) == g_path_is_absolute(path));
	assert(!uri_has_scheme(path));
	assert(strchr(path, '\n') == NULL);

	song = song_file_new(path, parent);

	//in archive ?
	if (parent != NULL && parent->device == DEVICE_INARCHIVE) {
		ret = song_file_update_inarchive(song);
	} else {
		ret = song_file_update(song);
	}
	if (!ret) {
		song_free(song);
		return NULL;
	}

	return song;
}
Exemple #5
0
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
			 struct playlist *dest, struct player_control *pc,
			 bool secure)
{
	enum playlist_result result;
	struct song *song;
	char *base_uri = uri != NULL ? g_path_get_dirname(uri) : NULL;

	while ((song = playlist_plugin_read(source)) != NULL) {
		song = playlist_check_translate_song(song, base_uri, secure);
		if (song == NULL)
			continue;

		result = playlist_append_song(dest, pc, song, NULL);
		if (result != PLAYLIST_RESULT_SUCCESS) {
			if (!song_in_database(song))
				song_free(song);
			g_free(base_uri);
			return result;
		}
	}

	g_free(base_uri);

	return PLAYLIST_RESULT_SUCCESS;
}
static void
asx_end_element(G_GNUC_UNUSED GMarkupParseContext *context,
		const gchar *element_name,
		gpointer user_data, G_GNUC_UNUSED GError **error)
{
	struct asx_parser *parser = user_data;

	switch (parser->state) {
	case ROOT:
		break;

	case ENTRY:
		if (g_ascii_strcasecmp(element_name, "entry") == 0) {
			if (strcmp(parser->song->uri, "asx:") != 0)
				parser->songs = g_slist_prepend(parser->songs,
								parser->song);
			else
				song_free(parser->song);

			parser->state = ROOT;
		} else
			parser->tag = TAG_NUM_OF_ITEM_TYPES;

		break;
	}
}
static void
song_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
	struct song *song = data;

	song_free(song);
}
Exemple #8
0
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
                         unsigned start_index, unsigned end_index,
                         struct playlist *dest, struct player_control *pc,
                         bool secure)
{
    enum playlist_result result;
    struct song *song;
    char *base_uri = uri != NULL ? g_path_get_dirname(uri) : NULL;

    for (unsigned i = 0;
    i < end_index && (song = playlist_plugin_read(source)) != NULL;
    ++i) {
        if (i < start_index) {
            /* skip songs before the start index */
            song_free(song);
            continue;
        }

        song = playlist_check_translate_song(song, base_uri, secure);
        if (song == NULL)
            continue;

#ifdef ENABLE_DESPOTIFY
// Not the right place to do this...
        if (strcmp(g_uri_parse_scheme(song->uri), "spt") == 0) {
            g_debug("Despotify update info : %s\n", song->uri);
            struct song *song_new;
            song_new = despotify_update_song(song);
            if (song_new != NULL) {
                song = song_new;
            }
        }
#endif

        result = playlist_append_song(dest, pc, song, NULL);
        song_free(song);
        if (result != PLAYLIST_RESULT_SUCCESS) {
            g_free(base_uri);
            return result;
        }
    }

    g_free(base_uri);

    return PLAYLIST_RESULT_SUCCESS;
}
Exemple #9
0
void
pc_free(struct player_control *pc)
{
	if (pc->next_song != NULL)
		song_free(pc->next_song);

	g_cond_free(pc->cond);
	g_mutex_free(pc->mutex);
	g_free(pc);
}
static void
asx_parser_destroy(gpointer data)
{
	struct asx_parser *parser = data;

	if (parser->state >= ENTRY)
		song_free(parser->song);

	g_slist_foreach(parser->songs, song_free_callback, NULL);
	g_slist_free(parser->songs);
}
static void
xspf_parser_destroy(gpointer data)
{
	struct xspf_parser *parser = data;

	if (parser->state >= TRACK && parser->song != NULL)
		song_free(parser->song);

	g_slist_foreach(parser->songs, song_free_callback, NULL);
	g_slist_free(parser->songs);
}
static void
delete_song(struct directory *dir, struct song *del)
{
	/* first, prevent traversers in main task from getting this */
	songvec_delete(&dir->songs, del);

	/* now take it out of the playlist (in the main_task) */
	update_remove_song(del);

	/* finally, all possible references gone, free it */
	song_free(del);
}
static void
asx_start_element(G_GNUC_UNUSED GMarkupParseContext *context,
		  const gchar *element_name,
		  const gchar **attribute_names,
		  const gchar **attribute_values,
		  gpointer user_data, G_GNUC_UNUSED GError **error)
{
	struct asx_parser *parser = user_data;

	switch (parser->state) {
	case ROOT:
		if (g_ascii_strcasecmp(element_name, "entry") == 0) {
			parser->state = ENTRY;
			parser->song = song_remote_new("asx:");
			parser->tag = TAG_NUM_OF_ITEM_TYPES;
		}

		break;

	case ENTRY:
		if (g_ascii_strcasecmp(element_name, "ref") == 0) {
			const gchar *href = get_attribute(attribute_names,
							  attribute_values,
							  "href");
			if (href != NULL) {
				/* create new song object, and copy
				   the existing tag over; we cannot
				   replace the existing song's URI,
				   because that attribute is
				   immutable */
				struct song *song = song_remote_new(href);

				if (parser->song != NULL) {
					song->tag = parser->song->tag;
					parser->song->tag = NULL;
					song_free(parser->song);
				}

				parser->song = song;
			}
		} else if (g_ascii_strcasecmp(element_name, "author") == 0)
			/* is that correct?  or should it be COMPOSER
			   or PERFORMER? */
			parser->tag = TAG_ARTIST;
		else if (g_ascii_strcasecmp(element_name, "title") == 0)
			parser->tag = TAG_TITLE;

		break;
	}
}
void
queue_clear(struct queue *queue)
{
	for (unsigned i = 0; i < queue->length; i++) {
		struct queue_item *item = &queue->items[i];

		if (!song_in_database(item->song))
			song_free(item->song);

		queue->id_to_position[item->id] = -1;
	}

	queue->length = 0;
}
void
directory_free(struct directory *directory)
{
	for (unsigned i = 0; i < directory->songs.nr; ++i)
		song_free(directory->songs.base[i]);

	for (unsigned i = 0; i < directory->children.nr; ++i)
		directory_free(directory->children.base[i]);

	dirvec_destroy(&directory->children);
	songvec_destroy(&directory->songs);
	g_free(directory);
	/* this resets last dir returned */
	/*directory_get_path(NULL); */
}
Exemple #16
0
void
directory_free(struct directory *directory)
{
	playlist_vector_deinit(&directory->playlists);

	struct song *song, *ns;
	directory_for_each_song_safe(song, ns, directory)
		song_free(song);

	struct directory *child, *n;
	directory_for_each_child_safe(child, n, directory)
		directory_free(child);

	g_free(directory);
	/* this resets last dir returned */
	/*directory_get_path(NULL); */
}
Exemple #17
0
/* Destroy play list */
void plist_free( plist_t *pl )
{
	if (pl != NULL)
	{
		if (pl->m_list != NULL)
		{
			int i;
			
			plist_lock(pl);
			for ( i = 0; i < pl->m_len; i ++ )
				song_free(pl->m_list[i]);
			free(pl->m_list);
			plist_unlock(pl);
		}
		
		pthread_mutex_destroy(&pl->m_mutex);
		free(pl);
	}
} /* End of 'plist_free' function */
Exemple #18
0
static struct song *
apply_song_metadata(struct song *dest, const struct song *src)
{
	struct song *tmp;

	assert(dest != NULL);
	assert(src != NULL);

	if (src->tag == NULL && src->start_ms == 0 && src->end_ms == 0)
		return dest;

	if (song_in_database(dest)) {
		char *path_fs = map_song_fs(dest);
		if (path_fs == NULL)
			return dest;

		char *path_utf8 = fs_charset_to_utf8(path_fs);
		if (path_utf8 != NULL)
			g_free(path_fs);
		else
			path_utf8 = path_fs;

		tmp = song_file_new(path_utf8, NULL);
		g_free(path_utf8);

		merge_song_metadata(tmp, dest, src);
	} else {
		tmp = song_file_new(dest->uri, NULL);
		merge_song_metadata(tmp, dest, src);
	}

	if (dest->tag != NULL && dest->tag->time > 0 &&
	    src->start_ms > 0 && src->end_ms == 0 &&
	    src->start_ms / 1000 < (unsigned)dest->tag->time)
		/* the range is open-ended, and the playlist plugin
		   did not know the total length of the song file
		   (e.g. last track on a CUE file); fix it up here */
		tmp->tag->time = dest->tag->time - src->start_ms / 1000;

	song_free(dest);
	return tmp;
}
Exemple #19
0
enum playlist_result
spl_append_uri(const char *url, const char *utf8file)
{
	struct song *song;

	if (uri_has_scheme(url)) {
		enum playlist_result ret;

		song = song_remote_new(url);
		ret = spl_append_song(utf8file, song);
		song_free(song);
		return ret;
	} else {
		song = db_get_song(url);
		if (song == NULL)
			return PLAYLIST_RESULT_NO_SUCH_SONG;

		return spl_append_song(utf8file, song);
	}
}
Exemple #20
0
bool
pc_seek(struct player_control *pc, struct song *song, float seek_time)
{
	assert(song != NULL);

	player_lock(pc);

	if (pc->next_song != NULL)
		song_free(pc->next_song);

	pc->next_song = song;
	pc->seek_where = seek_time;
	player_command_locked(pc, PLAYER_COMMAND_SEEK);
	player_unlock(pc);

	assert(pc->next_song == NULL);

	idle_add(IDLE_PLAYER);

	return true;
}
Exemple #21
0
bool
spl_append_uri(const char *url, const char *utf8file, GError **error_r)
{
    struct song *song;

    if (uri_has_scheme(url)) {
        song = song_remote_new(url);
        bool success = spl_append_song(utf8file, song, error_r);
        song_free(song);
        return success;
    } else {
        song = db_get_song(url);
        if (song == NULL) {
            g_set_error_literal(error_r, playlist_quark(),
                                PLAYLIST_RESULT_NO_SUCH_SONG,
                                "No such song");
            return false;
        }

        return spl_append_song(utf8file, song, error_r);
    }
}
void
queue_delete(struct queue *queue, unsigned position)
{
	struct song *song;
	unsigned id, order;

	assert(position < queue->length);

	song = queue_get(queue, position);
	if (!song_in_database(song))
		song_free(song);

	id = queue_position_to_id(queue, position);
	order = queue_position_to_order(queue, position);

	--queue->length;

	/* release the song id */

	queue->id_to_position[id] = -1;

	/* delete song from songs array */

	for (unsigned i = position; i < queue->length; i++)
		queue_move_song_to(queue, i + 1, i);

	/* delete the entry from the order array */

	for (unsigned i = order; i < queue->length; i++)
		queue->order[i] = queue->order[i + 1];

	/* readjust values in the order array */

	for (unsigned i = 0; i < queue->length; i++)
		if (queue->order[i] > position)
			--queue->order[i];
}
Exemple #23
0
struct song *
song_load(FILE *fp, struct directory *parent, const char *uri,
	  GString *buffer, GError **error_r)
{
	struct song *song = parent != NULL
		? song_file_new(uri, parent)
		: song_remote_new(uri);
	char *line, *colon;
	enum tag_type type;
	const char *value;

	while ((line = read_text_line(fp, buffer)) != NULL &&
	       strcmp(line, SONG_END) != 0) {
		colon = strchr(line, ':');
		if (colon == NULL || colon == line) {
			if (song->tag != NULL)
				tag_end_add(song->tag);
			song_free(song);

			g_set_error(error_r, song_save_quark(), 0,
				    "unknown line in db: %s", line);
			return NULL;
		}

		*colon++ = 0;
		value = g_strchug(colon);

		if ((type = tag_name_parse(line)) != TAG_NUM_OF_ITEM_TYPES) {
			if (!song->tag) {
				song->tag = tag_new();
				tag_begin_add(song->tag);
			}

			tag_add_item(song->tag, type, value);
		} else if (strcmp(line, "Time") == 0) {
			if (!song->tag) {
				song->tag = tag_new();
				tag_begin_add(song->tag);
			}

			song->tag->time = atoi(value);
		} else if (strcmp(line, SONG_MTIME) == 0) {
			song->mtime = atoi(value);
		} else if (strcmp(line, "Range") == 0) {
			char *endptr;

			song->start_ms = strtoul(value, &endptr, 10);
			if (*endptr == '-')
				song->end_ms = strtoul(endptr + 1, NULL, 10);
		} else {
			if (song->tag != NULL)
				tag_end_add(song->tag);
			song_free(song);

			g_set_error(error_r, song_save_quark(), 0,
				    "unknown line in db: %s", line);
			return NULL;
		}
	}

	if (song->tag != NULL)
		tag_end_add(song->tag);

	return song;
}
Exemple #24
0
struct song *
playlist_check_translate_song(struct song *song, const char *base_uri,
			      bool secure)
{
	struct song *dest;

	if (song_in_database(song))
		/* already ok */
		return song;

	char *uri = song->uri;

	if (uri_has_scheme(uri)) {
		if (uri_supported_scheme(uri))
			/* valid remote song */
			return song;
		else {
			/* unsupported remote song */
			song_free(song);
			return NULL;
		}
	}

	if (base_uri != NULL && strcmp(base_uri, ".") == 0)
		/* g_path_get_dirname() returns "." when there is no
		   directory name in the given path; clear that now,
		   because it would break the database lookup
		   functions */
		base_uri = NULL;

	if (g_path_is_absolute(uri)) {
		/* XXX fs_charset vs utf8? */
		const char *prefix = mapper_get_music_directory();

		if (prefix != NULL && g_str_has_prefix(uri, prefix) &&
		    uri[strlen(prefix)] == '/')
			uri += strlen(prefix) + 1;
		else if (!secure) {
			/* local files must be relative to the music
			   directory when "secure" is enabled */
			song_free(song);
			return NULL;
		}

		base_uri = NULL;
	}

	if (base_uri != NULL)
		uri = g_build_filename(base_uri, uri, NULL);
	else
		uri = g_strdup(uri);

	if (uri_has_scheme(uri)) {
		dest = song_remote_new(uri);
		g_free(uri);
	} else if (g_path_is_absolute(uri) && secure) {
		dest = song_file_load(uri, NULL);
		if (dest == NULL) {
			song_free(song);
			return NULL;
		}
	} else {
		dest = db_get_song(uri);
		g_free(uri);
		if (dest == NULL) {
			/* not found in database */
			song_free(song);
			return dest;
		}
	}

	dest = apply_song_metadata(dest, song);
	song_free(song);

	return dest;
}
Exemple #25
0
struct song *
playlist_check_translate_song(struct song *song, const char *base_uri)
{
	struct song *dest;

	if (song_in_database(song))
		/* already ok */
		return song;

	char *uri = song->uri;

	if (uri_has_scheme(uri)) {
		if (uri_supported_scheme(uri))
			/* valid remote song */
			return song;
		else {
			/* unsupported remote song */
			song_free(song);
			return NULL;
		}
	}

	if (g_path_is_absolute(uri)) {
		/* XXX fs_charset vs utf8? */
		char *prefix = base_uri != NULL
			? map_uri_fs(base_uri)
			: map_directory_fs(db_get_root());

		if (prefix == NULL || !g_str_has_prefix(uri, prefix) ||
		    uri[strlen(prefix)] != '/') {
			/* local files must be relative to the music
			   directory */
			g_free(prefix);
			song_free(song);
			return NULL;
		}

		uri += strlen(prefix) + 1;
		g_free(prefix);
	}

	if (base_uri != NULL)
		uri = g_build_filename(base_uri, uri, NULL);
	else
		uri = g_strdup(uri);

	if (uri_has_scheme(base_uri)) {
		dest = song_remote_new(uri);
		g_free(uri);
	} else {
		dest = db_get_song(uri);
		g_free(uri);
		if (dest == NULL) {
			/* not found in database */
			song_free(song);
			return dest;
		}
	}

	dest = apply_song_metadata(dest, song);
	song_free(song);

	return dest;
}
Exemple #26
0
/* Remove selected songs from play list */
void plist_rem( plist_t *pl )
{
	int start, end, i, cur;
	assert(pl);

	/* Get real selection bounds */
	PLIST_GET_SEL(pl, start, end);
	if (start >= pl->m_len)
		start = pl->m_len - 1;
	if (end >= pl->m_len)
		end = pl->m_len - 1;
	if (start < 0)
		start = 0;
	if (end < 0)
		end = 0;

	/* Check if we have anything to delete */
	if (!pl->m_len)
		return;

	/* Store undo information */
	if (player_store_undo)
	{
		struct tag_undo_list_item_t *undo;
		struct tag_undo_list_rem_t *data;
		int j;
		song_metadata_t metadata_empty = SONG_METADATA_EMPTY;
		
		undo = (struct tag_undo_list_item_t *)malloc(sizeof(*undo));
		undo->m_type = UNDO_REM;
		undo->m_next = undo->m_prev = NULL;
		data = &undo->m_data.m_rem;
		data->m_num_files = end - start + 1;
		data->m_start_pos = start;
		data->m_files = (struct song_name *)malloc(sizeof(struct song_name) * data->m_num_files);
		for ( i = start, j = 0; i <= end; i ++, j ++ )
		{
			song_t *s = pl->m_list[i];
			struct song_name *sn = &data->m_files[j];
			if (s->m_filename)
			{
				sn->m_filename = strdup(s->m_filename);
				sn->m_fullname = NULL;
			}
			else
			{
				sn->m_fullname = strdup(s->m_fullname);
				sn->m_filename = NULL;
			}

			song_metadata_t *metadata = &sn->m_metadata;
			(*metadata) = metadata_empty;
			metadata->m_start_time = s->m_start_time;
			metadata->m_end_time = s->m_end_time;
			metadata->m_len = s->m_len;
			if (s->m_default_title)
				metadata->m_title = strdup(s->m_default_title);
		}
		undo_add(player_ul, undo);
	}

	/* Stop currently playing song if it is inside area being removed */
	if (pl->m_cur_song >= start && pl->m_cur_song <= end)
	{
		player_end_play(TRUE);
	}

	/* Unlock play list */
	plist_lock(pl);

	/* Free memory */
	for ( i = start; i <= end; i ++ )
		song_free(pl->m_list[i]);

	/* Shift songs list and reallocate memory */
	memmove(&pl->m_list[start], &pl->m_list[end + 1],
			(pl->m_len - end - 1) * sizeof(*pl->m_list));
	pl->m_len -= (end - start + 1);
	if (pl->m_len)
		pl->m_list = (song_t **)realloc(pl->m_list, 
				pl->m_len * sizeof(*pl->m_list));
	else
	{
		free(pl->m_list);
		pl->m_list = NULL;
	}

	/* Fix cursor */
	plist_move(pl, start, FALSE);
	pl->m_sel_start = pl->m_sel_end;
	if (pl->m_cur_song > end)
		pl->m_cur_song -= (end - start + 1);

	/* Unlock play list */
	plist_unlock(pl);

	pmng_hook(player_pmng, "playlist");
} /* End of 'plist_rem' function */
Exemple #27
0
/*
 * The main loop of the player thread, during playback.  This is
 * basically a state machine, which multiplexes data between the
 * decoder thread and the output threads.
 */
static void do_play(struct player_control *pc, struct decoder_control *dc)
{
	struct player player = {
		.pc = pc,
		.dc = dc,
		.buffering = true,
		.decoder_starting = false,
		.paused = false,
		.queued = true,
		.output_open = false,
		.song = NULL,
		.xfade = XFADE_UNKNOWN,
		.cross_fading = false,
		.cross_fade_chunks = 0,
		.cross_fade_tag = NULL,
		.elapsed_time = 0.0,
	};

	player_unlock(pc);

	player.pipe = music_pipe_new();

	player_dc_start(&player, player.pipe);
	if (!player_wait_for_decoder(&player)) {
		assert(player.song == NULL);

		player_dc_stop(&player);
		player_command_finished(pc);
		music_pipe_free(player.pipe);
		event_pipe_emit(PIPE_EVENT_PLAYLIST);
		player_lock(pc);
		return;
	}

	player_lock(pc);
	pc->state = PLAYER_STATE_PLAY;

	if (pc->command == PLAYER_COMMAND_SEEK)
		player.elapsed_time = pc->seek_where;

	player_command_finished_locked(pc);

	while (true) {
		player_process_command(&player);
		if (pc->command == PLAYER_COMMAND_STOP ||
		    pc->command == PLAYER_COMMAND_EXIT ||
		    pc->command == PLAYER_COMMAND_CLOSE_AUDIO) {
			player_unlock(pc);
			audio_output_all_cancel();
			break;
		}

		player_unlock(pc);

		if (player.buffering) {
			/* buffering at the start of the song - wait
			   until the buffer is large enough, to
			   prevent stuttering on slow machines */

			if (music_pipe_size(player.pipe) < pc->buffered_before_play &&
			    !decoder_lock_is_idle(dc)) {
				/* not enough decoded buffer space yet */

				if (!player.paused &&
				    player.output_open &&
				    audio_output_all_check() < 4 &&
				    !player_send_silence(&player))
					break;

				decoder_lock(dc);
				/* XXX race condition: check decoder again */
				player_wait_decoder(pc, dc);
				decoder_unlock(dc);
				player_lock(pc);
				continue;
			} else {
				/* buffering is complete */
				player.buffering = false;
			}
		}

		if (player.decoder_starting) {
			/* wait until the decoder is initialized completely */

			if (!player_check_decoder_startup(&player))
				break;

			player_lock(pc);
			continue;
		}

#ifndef NDEBUG
		/*
		music_pipe_check_format(&play_audio_format,
					player.next_song_chunk,
					&dc->out_audio_format);
		*/
#endif

		if (decoder_lock_is_idle(dc) && player.queued &&
		    dc->pipe == player.pipe) {
			/* the decoder has finished the current song;
			   make it decode the next song */

			assert(dc->pipe == NULL || dc->pipe == player.pipe);

			player_dc_start(&player, music_pipe_new());
		}

		if (/* no cross-fading if MPD is going to pause at the
		       end of the current song */
		    !pc->border_pause &&
		    player_dc_at_next_song(&player) &&
		    player.xfade == XFADE_UNKNOWN &&
		    !decoder_lock_is_starting(dc)) {
			/* enable cross fading in this song?  if yes,
			   calculate how many chunks will be required
			   for it */
			player.cross_fade_chunks =
				cross_fade_calc(pc->cross_fade_seconds, dc->total_time,
						pc->mixramp_db,
						pc->mixramp_delay_seconds,
						dc->replay_gain_db,
						dc->replay_gain_prev_db,
						dc->mixramp_start,
						dc->mixramp_prev_end,
						&dc->out_audio_format,
						&player.play_audio_format,
						music_buffer_size(player_buffer) -
						pc->buffered_before_play);
			if (player.cross_fade_chunks > 0) {
				player.xfade = XFADE_ENABLED;
				player.cross_fading = false;
			} else
				/* cross fading is disabled or the
				   next song is too short */
				player.xfade = XFADE_DISABLED;
		}

		if (player.paused) {
			player_lock(pc);

			if (pc->command == PLAYER_COMMAND_NONE)
				player_wait(pc);
			continue;
		} else if (!music_pipe_empty(player.pipe)) {
			/* at least one music chunk is ready - send it
			   to the audio output */

			play_next_chunk(&player);
		} else if (audio_output_all_check() > 0) {
			/* not enough data from decoder, but the
			   output thread is still busy, so it's
			   okay */

			/* XXX synchronize in a better way */
			g_usleep(10000);
		} else if (player_dc_at_next_song(&player)) {
			/* at the beginning of a new song */

			if (!player_song_border(&player))
				break;
		} else if (decoder_lock_is_idle(dc)) {
			/* check the size of the pipe again, because
			   the decoder thread may have added something
			   since we last checked */
			if (music_pipe_empty(player.pipe)) {
				/* wait for the hardware to finish
				   playback */
				audio_output_all_drain();
				break;
			}
		} else if (player.output_open) {
			/* the decoder is too busy and hasn't provided
			   new PCM data in time: send silence (if the
			   output pipe is empty) */
			if (!player_send_silence(&player))
				break;
		}

		player_lock(pc);
	}

	player_dc_stop(&player);

	music_pipe_clear(player.pipe, player_buffer);
	music_pipe_free(player.pipe);

	if (player.cross_fade_tag != NULL)
		tag_free(player.cross_fade_tag);

	if (player.song != NULL)
		song_free(player.song);

	player_lock(pc);

	if (player.queued) {
		assert(pc->next_song != NULL);
		song_free(pc->next_song);
		pc->next_song = NULL;
	}

	pc->state = PLAYER_STATE_STOP;

	player_unlock(pc);

	event_pipe_emit(PIPE_EVENT_PLAYLIST);

	player_lock(pc);
}

static gpointer
player_task(gpointer arg)
{
	struct player_control *pc = arg;

	struct decoder_control *dc = dc_new(pc->cond);
	decoder_thread_start(dc);

	player_buffer = music_buffer_new(pc->buffer_chunks);

	player_lock(pc);

	while (1) {
		switch (pc->command) {
		case PLAYER_COMMAND_SEEK:
		case PLAYER_COMMAND_QUEUE:
			assert(pc->next_song != NULL);

			do_play(pc, dc);
			break;

		case PLAYER_COMMAND_STOP:
			player_unlock(pc);
			audio_output_all_cancel();
			player_lock(pc);

			/* fall through */

		case PLAYER_COMMAND_PAUSE:
			if (pc->next_song != NULL) {
				song_free(pc->next_song);
				pc->next_song = NULL;
			}

			player_command_finished_locked(pc);
			break;

		case PLAYER_COMMAND_CLOSE_AUDIO:
			player_unlock(pc);

			audio_output_all_release();

			player_lock(pc);
			player_command_finished_locked(pc);

#ifndef NDEBUG
			/* in the DEBUG build, check for leaked
			   music_chunk objects by freeing the
			   music_buffer */
			music_buffer_free(player_buffer);
			player_buffer = music_buffer_new(pc->buffer_chunks);
#endif

			break;

		case PLAYER_COMMAND_UPDATE_AUDIO:
			player_unlock(pc);
			audio_output_all_enable_disable();
			player_lock(pc);
			player_command_finished_locked(pc);
			break;

		case PLAYER_COMMAND_EXIT:
			player_unlock(pc);

			dc_quit(dc);
			dc_free(dc);
			audio_output_all_close();
			music_buffer_free(player_buffer);

			player_command_finished(pc);
			return NULL;

		case PLAYER_COMMAND_CANCEL:
			if (pc->next_song != NULL) {
				song_free(pc->next_song);
				pc->next_song = NULL;
			}

			player_command_finished_locked(pc);
			break;

		case PLAYER_COMMAND_REFRESH:
			/* no-op when not playing */
			player_command_finished_locked(pc);
			break;

		case PLAYER_COMMAND_NONE:
			player_wait(pc);
			break;
		}
	}
}

void
player_create(struct player_control *pc)
{
	assert(pc->thread == NULL);

	GError *e = NULL;
	pc->thread = g_thread_create(player_task, pc, true, &e);
	if (pc->thread == NULL)
		MPD_ERROR("Failed to spawn player task: %s", e->message);
}
Exemple #28
0
/**
 * Player lock must be held before calling.
 */
static void player_process_command(struct player *player)
{
	struct player_control *pc = player->pc;
	G_GNUC_UNUSED struct decoder_control *dc = player->dc;

	switch (pc->command) {
	case PLAYER_COMMAND_NONE:
	case PLAYER_COMMAND_STOP:
	case PLAYER_COMMAND_EXIT:
	case PLAYER_COMMAND_CLOSE_AUDIO:
		break;

	case PLAYER_COMMAND_UPDATE_AUDIO:
		player_unlock(pc);
		audio_output_all_enable_disable();
		player_lock(pc);
		player_command_finished_locked(pc);
		break;

	case PLAYER_COMMAND_QUEUE:
		assert(pc->next_song != NULL);
		assert(!player->queued);
		assert(!player_dc_at_next_song(player));

		player->queued = true;
		player_command_finished_locked(pc);
		break;

	case PLAYER_COMMAND_PAUSE:
		player_unlock(pc);

		player->paused = !player->paused;
		if (player->paused) {
			audio_output_all_pause();
			player_lock(pc);

			pc->state = PLAYER_STATE_PAUSE;
		} else if (!audio_format_defined(&player->play_audio_format)) {
			/* the decoder hasn't provided an audio format
			   yet - don't open the audio device yet */
			player_lock(pc);

			pc->state = PLAYER_STATE_PLAY;
		} else {
			player_open_output(player);

			player_lock(pc);
		}

		player_command_finished_locked(pc);
		break;

	case PLAYER_COMMAND_SEEK:
		player_unlock(pc);
		player_seek_decoder(player);
		player_lock(pc);
		break;

	case PLAYER_COMMAND_CANCEL:
		if (pc->next_song == NULL) {
			/* the cancel request arrived too late, we're
			   already playing the queued song...  stop
			   everything now */
			pc->command = PLAYER_COMMAND_STOP;
			return;
		}

		if (player_dc_at_next_song(player)) {
			/* the decoder is already decoding the song -
			   stop it and reset the position */
			player_unlock(pc);
			player_dc_stop(player);
			player_lock(pc);
		}

		song_free(pc->next_song);
		pc->next_song = NULL;
		player->queued = false;
		player_command_finished_locked(pc);
		break;

	case PLAYER_COMMAND_REFRESH:
		if (player->output_open && !player->paused) {
			player_unlock(pc);
			audio_output_all_check();
			player_lock(pc);
		}

		pc->elapsed_time = audio_output_all_get_elapsed_time();
		if (pc->elapsed_time < 0.0)
			pc->elapsed_time = player->elapsed_time;

		player_command_finished_locked(pc);
		break;
	}
}
Exemple #29
0
/**
 * This is the handler for the #PLAYER_COMMAND_SEEK command.
 *
 * The player lock is not held.
 */
static bool player_seek_decoder(struct player *player)
{
	struct player_control *pc = player->pc;
	struct song *song = pc->next_song;
	struct decoder_control *dc = player->dc;

	assert(pc->next_song != NULL);

	const unsigned start_ms = song->start_ms;

	if (!decoder_lock_is_current_song(dc, song)) {
		/* the decoder is already decoding the "next" song -
		   stop it and start the previous song again */

		player_dc_stop(player);

		/* clear music chunks which might still reside in the
		   pipe */
		music_pipe_clear(player->pipe, player_buffer);

		/* re-start the decoder */
		player_dc_start(player, player->pipe);
		if (!player_wait_for_decoder(player)) {
			/* decoder failure */
			player_command_finished(pc);
			return false;
		}
	} else {
		if (!player_dc_at_current_song(player)) {
			/* the decoder is already decoding the "next" song,
			   but it is the same song file; exchange the pipe */
			music_pipe_clear(player->pipe, player_buffer);
			music_pipe_free(player->pipe);
			player->pipe = dc->pipe;
		}

		song_free(pc->next_song);
		pc->next_song = NULL;
		player->queued = false;
	}

	/* wait for the decoder to complete initialization */

	while (player->decoder_starting) {
		if (!player_check_decoder_startup(player)) {
			/* decoder failure */
			player_command_finished(pc);
			return false;
		}
	}

	/* send the SEEK command */

	double where = pc->seek_where;
	if (where > pc->total_time)
		where = pc->total_time - 0.1;
	if (where < 0.0)
		where = 0.0;

	if (!dc_seek(dc, where + start_ms / 1000.0)) {
		/* decoder failure */
		player_command_finished(pc);
		return false;
	}

	player->elapsed_time = where;

	player_command_finished(pc);

	player->xfade = XFADE_UNKNOWN;

	/* re-fill the buffer after seeking */
	player->buffering = true;

	audio_output_all_cancel();

	return true;
}
Exemple #30
0
int main(int argc, char **argv)
{
	const char *uri;
	struct input_stream *is = NULL;
	bool success;
	GError *error = NULL;
	struct playlist_provider *playlist;
	struct song *song;

	if (argc != 3) {
		g_printerr("Usage: dump_playlist CONFIG URI\n");
		return 1;
	}

	uri = argv[2];

	/* initialize GLib */

	g_thread_init(NULL);
	g_log_set_default_handler(my_log_func, NULL);

	/* initialize MPD */

	config_global_init();
	success = config_read_file(argv[1], &error);
	if (!success) {
		g_printerr("%s\n", error->message);
		g_error_free(error);
		return 1;
	}

	io_thread_init();
	if (!io_thread_start(&error)) {
		g_warning("%s", error->message);
		g_error_free(error);
		return EXIT_FAILURE;
	}

	if (!input_stream_global_init(&error)) {
		g_warning("%s", error->message);
		g_error_free(error);
		return 2;
	}

	playlist_list_global_init();
	decoder_plugin_init_all();

	/* open the playlist */

	GMutex *mutex = g_mutex_new();
	GCond *cond = g_cond_new();

	playlist = playlist_list_open_uri(uri, mutex, cond);
	if (playlist == NULL) {
		/* open the stream and wait until it becomes ready */

		is = input_stream_open(uri, mutex, cond, &error);
		if (is == NULL) {
			if (error != NULL) {
				g_warning("%s", error->message);
				g_error_free(error);
			} else
				g_printerr("input_stream_open() failed\n");
			return 2;
		}

		input_stream_lock_wait_ready(is);

		/* open the playlist */

		playlist = playlist_list_open_stream(is, uri);
		if (playlist == NULL) {
			input_stream_close(is);
			g_printerr("Failed to open playlist\n");
			return 2;
		}
	}

	/* dump the playlist */

	while ((song = playlist_plugin_read(playlist)) != NULL) {
		g_print("%s\n", song->uri);

		if (song->end_ms > 0)
			g_print("range: %u:%02u..%u:%02u\n",
				song->start_ms / 60000,
				(song->start_ms / 1000) % 60,
				song->end_ms / 60000,
				(song->end_ms / 1000) % 60);
		else if (song->start_ms > 0)
			g_print("range: %u:%02u..\n",
				song->start_ms / 60000,
				(song->start_ms / 1000) % 60);

		if (song->tag != NULL)
			tag_save(stdout, song->tag);

		song_free(song);
	}

	/* deinitialize everything */

	playlist_plugin_close(playlist);
	if (is != NULL)
		input_stream_close(is);

	g_cond_free(cond);
	g_mutex_free(mutex);

	decoder_plugin_deinit_all();
	playlist_list_global_finish();
	input_stream_global_finish();
	io_thread_deinit();
	config_global_finish();

	return 0;
}