Example #1
0
enum decoder_command
decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
	    const struct tag *tag)
{
	enum decoder_command cmd;

	assert(dc.state == DECODE_STATE_DECODE);
	assert(tag != NULL);

	/* save the tag */

	if (decoder->decoder_tag != NULL)
		tag_free(decoder->decoder_tag);
	decoder->decoder_tag = tag_dup(tag);

	/* check for a new stream tag */

	update_stream_tag(decoder, is);

	/* send tag to music pipe */

	if (decoder->stream_tag != NULL) {
		/* merge with tag from input stream */
		struct tag *merged;

		merged = tag_merge(decoder->stream_tag, decoder->decoder_tag);
		cmd = do_send_tag(decoder, is, merged);
		tag_free(merged);
	} else
		/* send only the decoder tag */
		cmd = do_send_tag(decoder, is, tag);

	return cmd;
}
Example #2
0
static void
decoder_run_song(struct decoder_control *dc,
		 const struct song *song, const char *uri)
{
	struct decoder decoder = {
		.dc = dc,
	};
	int ret;

	decoder.timestamp = 0.0;
	decoder.seeking = false;
	decoder.song_tag = song->tag != NULL && song_is_file(song)
		? tag_dup(song->tag) : NULL;
	decoder.stream_tag = NULL;
	decoder.decoder_tag = NULL;
	decoder.chunk = NULL;

	dc->state = DECODE_STATE_START;

	decoder_command_finished_locked(dc);

	pcm_convert_init(&decoder.conv_state);

	ret = song_is_file(song)
		? decoder_run_file(&decoder, uri)
		: decoder_run_stream(&decoder, uri);

	decoder_unlock(dc);

	pcm_convert_deinit(&decoder.conv_state);

	/* flush the last chunk */

	if (decoder.chunk != NULL)
		decoder_flush_chunk(&decoder);

	if (decoder.song_tag != NULL)
		tag_free(decoder.song_tag);

	if (decoder.stream_tag != NULL)
		tag_free(decoder.stream_tag);

	if (decoder.decoder_tag != NULL)
		tag_free(decoder.decoder_tag);

	decoder_lock(dc);

	dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR;
}
Example #3
0
int main(int argc, char **argv)
{
	const char *decoder_name, *path;
	const struct decoder_plugin *plugin;
	struct tag *tag;
	bool empty;

	if (argc != 3) {
		g_printerr("Usage: read_tags DECODER FILE\n");
		return 1;
	}

	decoder_name = argv[1];
	path = argv[2];

	input_stream_global_init();
	decoder_plugin_init_all();

	plugin = decoder_plugin_from_name(decoder_name);
	if (plugin == NULL) {
		g_printerr("No such decoder: %s\n", decoder_name);
		return 1;
	}

	tag = decoder_plugin_tag_dup(plugin, path);
	decoder_plugin_deinit_all();
	input_stream_global_finish();
	if (tag == NULL) {
		g_printerr("Failed to read tags\n");
		return 1;
	}

	print_tag(tag);

	empty = tag_is_empty(tag);
	tag_free(tag);

	if (empty) {
		tag = tag_ape_load(path);
		if (tag == NULL)
			tag = tag_id3_load(path);
		if (tag != NULL) {
			print_tag(tag);
			tag_free(tag);
		}
	}

	return 0;
}
Example #4
0
struct tag *
tag_merge_replace(struct tag *base, struct tag *add)
{
	if (add == NULL)
		return base;

	if (base == NULL)
		return add;

	struct tag *tag = tag_merge(base, add);
	tag_free(base);
	tag_free(add);

	return tag;
}
Example #5
0
static struct tag *
oggflac_tag_dup(const char *file)
{
	struct tag *ret = NULL;
	FLAC__Metadata_Iterator *it;
	FLAC__StreamMetadata *block;
	FLAC__Metadata_Chain *chain = FLAC__metadata_chain_new();

	if (!(FLAC__metadata_chain_read_ogg(chain, file)))
		goto out;
	it = FLAC__metadata_iterator_new();
	FLAC__metadata_iterator_init(it, chain);

	ret = tag_new();
	do {
		if (!(block = FLAC__metadata_iterator_get_block(it)))
			break;

		flac_tag_apply_metadata(ret, NULL, block);
	} while (FLAC__metadata_iterator_next(it));
	FLAC__metadata_iterator_delete(it);

	if (!tag_is_defined(ret)) {
		tag_free(ret);
		ret = NULL;
	}

out:
	FLAC__metadata_chain_delete(chain);
	return ret;
}
Example #6
0
File: tag_id3.c Project: azuwis/mpd
struct tag *tag_id3_import(struct id3_tag * tag)
{
	struct tag *ret = tag_new();

	getID3Info(tag, ID3_FRAME_ARTIST, TAG_ITEM_ARTIST, ret);
	getID3Info(tag, ID3_FRAME_ALBUM_ARTIST,
		   TAG_ITEM_ALBUM_ARTIST, ret);
	getID3Info(tag, ID3_FRAME_ALBUM_ARTIST_SORT,
		   TAG_ITEM_ALBUM_ARTIST, ret);
	getID3Info(tag, ID3_FRAME_TITLE, TAG_ITEM_TITLE, ret);
	getID3Info(tag, ID3_FRAME_ALBUM, TAG_ITEM_ALBUM, ret);
	getID3Info(tag, ID3_FRAME_TRACK, TAG_ITEM_TRACK, ret);
	getID3Info(tag, ID3_FRAME_YEAR, TAG_ITEM_DATE, ret);
	getID3Info(tag, ID3_FRAME_GENRE, TAG_ITEM_GENRE, ret);
	getID3Info(tag, ID3_FRAME_COMPOSER, TAG_ITEM_COMPOSER, ret);
	getID3Info(tag, ID3_FRAME_PERFORMER, TAG_ITEM_PERFORMER, ret);
	getID3Info(tag, ID3_FRAME_COMMENT, TAG_ITEM_COMMENT, ret);
	getID3Info(tag, ID3_FRAME_DISC, TAG_ITEM_DISC, ret);

	tag_id3_import_musicbrainz(ret, tag);

	if (tag_is_empty(ret)) {
		tag_free(ret);
		ret = NULL;
	}

	return ret;
}
Example #7
0
void
song_free(struct song *song)
{
	if (song->tag)
		tag_free(song->tag);
	g_free(song);
}
Example #8
0
bool
song_file_update_inarchive(struct song *song)
{
	const char *suffix;
	const struct decoder_plugin *plugin;

	assert(song_is_file(song));

	/* check if there's a suffix and a plugin */

	suffix = uri_get_suffix(song->uri);
	if (suffix == NULL)
		return false;

	plugin = decoder_plugin_from_suffix(suffix, false);
	if (plugin == NULL)
		return false;

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

	//accept every file that has music suffix
	//because we don't support tag reading through
	//input streams
	song->tag = tag_new();

	return true;
}
Example #9
0
struct tag *tag_id3_import(struct id3_tag * tag)
{
	struct tag *ret = tag_new();

	tag_id3_import_text(ret, tag, ID3_FRAME_ARTIST, TAG_ARTIST);
	tag_id3_import_text(ret, tag, ID3_FRAME_ALBUM_ARTIST,
			    TAG_ALBUM_ARTIST);
	tag_id3_import_text(ret, tag, ID3_FRAME_ARTIST_SORT,
			    TAG_ARTIST_SORT);
	tag_id3_import_text(ret, tag, ID3_FRAME_ALBUM_ARTIST_SORT,
			    TAG_ALBUM_ARTIST_SORT);
	tag_id3_import_text(ret, tag, ID3_FRAME_TITLE, TAG_TITLE);
	tag_id3_import_text(ret, tag, ID3_FRAME_ALBUM, TAG_ALBUM);
	tag_id3_import_text(ret, tag, ID3_FRAME_TRACK, TAG_TRACK);
	tag_id3_import_text(ret, tag, ID3_FRAME_YEAR, TAG_DATE);
	tag_id3_import_text(ret, tag, ID3_FRAME_GENRE, TAG_GENRE);
	tag_id3_import_text(ret, tag, ID3_FRAME_COMPOSER, TAG_COMPOSER);
	tag_id3_import_text(ret, tag, "TPE3", TAG_PERFORMER);
	tag_id3_import_text(ret, tag, "TPE4", TAG_PERFORMER);
	tag_id3_import_comment(ret, tag, ID3_FRAME_COMMENT, TAG_COMMENT);
	tag_id3_import_text(ret, tag, ID3_FRAME_DISC, TAG_DISC);

	tag_id3_import_musicbrainz(ret, tag);
	tag_id3_import_ufid(ret, tag);

	if (tag_is_empty(ret)) {
		tag_free(ret);
		ret = NULL;
	}

	return ret;
}
Example #10
0
void
flac_data_deinit(struct flac_data *data)
{
	pcm_buffer_deinit(&data->buffer);

	if (data->tag != NULL)
		tag_free(data->tag);
}
static int
dump_input_stream(struct input_stream *is)
{
	GError *error = NULL;
	char buffer[4096];
	size_t num_read;
	ssize_t num_written;

	/* wait until the stream becomes ready */

	while (!is->ready) {
		int ret = input_stream_buffer(is, &error);
		if (ret < 0) {
			/* error */
			g_warning("%s", error->message);
			g_error_free(error);
			return 2;
		}

		if (ret == 0)
			/* nothing was buffered - wait */
			g_usleep(10000);
	}

	/* print meta data */

	if (is->mime != NULL)
		g_printerr("MIME type: %s\n", is->mime);

	/* read data and tags from the stream */

	while (!input_stream_eof(is)) {
		struct tag *tag = input_stream_tag(is);
		if (tag != NULL) {
			g_printerr("Received a tag:\n");
			tag_save(stderr, tag);
			tag_free(tag);
		}

		num_read = input_stream_read(is, buffer, sizeof(buffer),
					     &error);
		if (num_read == 0) {
			if (error != NULL) {
				g_warning("%s", error->message);
				g_error_free(error);
			}

			break;
		}

		num_written = write(1, buffer, num_read);
		if (num_written <= 0)
			break;
	}

	return 0;
}
Example #12
0
enum decoder_command
decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
	    const struct tag *tag)
{
	G_GNUC_UNUSED const struct decoder_control *dc = decoder->dc;
	enum decoder_command cmd;

	assert(dc->state == DECODE_STATE_DECODE);
	assert(dc->pipe != NULL);
	assert(tag != NULL);

	/* save the tag */

	if (decoder->decoder_tag != NULL)
		tag_free(decoder->decoder_tag);
	decoder->decoder_tag = tag_dup(tag);

	/* check for a new stream tag */

	update_stream_tag(decoder, is);

	/* check if we're seeking */

	if (decoder_prepare_initial_seek(decoder))
		/* during initial seek, no music chunk must be created
		   until seeking is finished; skip the rest of the
		   function here */
		return DECODE_COMMAND_SEEK;

	/* send tag to music pipe */

	if (decoder->stream_tag != NULL) {
		/* merge with tag from input stream */
		struct tag *merged;

		merged = tag_merge(decoder->stream_tag, decoder->decoder_tag);
		cmd = do_send_tag(decoder, is, merged);
		tag_free(merged);
	} else
		/* send only the decoder tag */
		cmd = do_send_tag(decoder, is, tag);

	return cmd;
}
Example #13
0
	/*}}}*/
};
static int	psize = sizeof (parseable) / sizeof (parseable[0]);

tag_t *
tag_alloc (void) /*{{{*/
{
	tag_t	*t;
	
	if (t = (tag_t *) malloc (sizeof (tag_t))) {
		t -> name = xmlBufferCreate ();
		t -> cname = NULL;
		t -> hash = 0;
		t -> type = NULL;
		t -> topt = NULL;
		t -> value = xmlBufferCreate ();
		t -> parm = NULL;
		t -> used = false;
		t -> next = NULL;
		if ((! t -> name) || (! t -> value))
			t = tag_free (t);
	}
	return t;
}/*}}}*/
tag_t *
tag_free (tag_t *t) /*{{{*/
{
	if (t) {
		if (t -> name)
			xmlBufferFree (t -> name);
		if (t -> cname)
			free (t -> cname);
		if (t -> type)
			free (t -> type);
		if (t -> value)
			xmlBufferFree (t -> value);
		if (t -> parm)
			var_free_all (t -> parm);
		free (t);
	}
	return NULL;
}/*}}}*/
tag_t *
tag_free_all (tag_t *t) /*{{{*/
{
	tag_t	*tmp;
	
	while (tmp = t) {
		t = t -> next;
		tag_free (tmp);
	}
	return NULL;
}/*}}}*/
Example #14
0
static void
vorbis_send_comments(struct decoder *decoder, struct input_stream *is,
		     char **comments)
{
	struct tag *tag = vorbis_comments_to_tag(comments);
	if (!tag)
		return;

	decoder_tag(decoder, is, tag);
	tag_free(tag);
}
Example #15
0
static void
mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
{
	struct mp3_data data;
	GError *error = NULL;
	struct tag *tag = NULL;
	struct audio_format audio_format;

	if (!mp3_open(input_stream, &data, decoder, &tag)) {
		if (decoder_get_command(decoder) == DECODE_COMMAND_NONE)
			g_warning
			    ("Input does not appear to be a mp3 bit stream.\n");
		return;
	}

	if (!audio_format_init_checked(&audio_format,
				       data.frame.header.samplerate,
				       SAMPLE_FORMAT_S24_P32,
				       MAD_NCHANNELS(&data.frame.header),
				       &error)) {
		g_warning("%s", error->message);
		g_error_free(error);

		if (tag != NULL)
			tag_free(tag);
		mp3_data_finish(&data);
		return;
	}

	decoder_initialized(decoder, &audio_format,
			    data.input_stream->seekable, data.total_time);

	if (tag != NULL) {
		decoder_tag(decoder, input_stream, tag);
		tag_free(tag);
	}

	while (mp3_read(&data)) ;

	mp3_data_finish(&data);
}
Example #16
0
static void
input_despotify_close(struct input_stream *is)
{
	struct input_despotify *ctx = (struct input_despotify *)is;

	if (ctx->tag != NULL)
		tag_free(ctx->tag);

	mpd_despotify_unregister_callback(callback);
	despotify_free_track(ctx->track);
	input_stream_deinit(&ctx->base);
	g_free(ctx);
}
Example #17
0
struct tag *
vorbis_comments_to_tag(char **comments)
{
	struct tag *tag = tag_new();
	vorbis_comments_scan(comments, &add_tag_handler, tag);

	if (tag_is_empty(tag)) {
		tag_free(tag);
		tag = NULL;
	}

	return tag;
}
Example #18
0
File: song.c Project: azuwis/mpd
bool
song_file_update(struct song *song)
{
	const char *suffix;
	char *path_fs;
	const struct decoder_plugin *plugin;
	struct stat st;

	assert(song_is_file(song));

	/* check if there's a suffix and a plugin */

	suffix = uri_get_suffix(song->url);
	if (suffix == NULL)
		return false;

	plugin = decoder_plugin_from_suffix(suffix, false);
	if (plugin == NULL)
		return false;

	path_fs = map_song_fs(song);
	if (path_fs == NULL)
		return false;

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

	if (stat(path_fs, &st) < 0 || !S_ISREG(st.st_mode)) {
		g_free(path_fs);
		return false;
	}

	song->mtime = st.st_mtime;

	do {
		song->tag = plugin->tag_dup(path_fs);
		if (song->tag != NULL)
			break;

		plugin = decoder_plugin_from_suffix(suffix, true);
	} while (plugin != NULL);

	if (song->tag != NULL && tag_is_empty(song->tag))
		song->tag = tag_fallback(path_fs, song->tag);

	g_free(path_fs);
	return song->tag != NULL;
}
Example #19
0
static void
flac_decoder_loop(struct flac_data *data, FLAC__StreamDecoder *flac_dec,
		  FLAC__uint64 t_start, FLAC__uint64 t_end)
{
	struct decoder *decoder = data->decoder;
	enum decoder_command cmd;

	data->first_frame = t_start;

	while (true) {
		if (data->tag != NULL && !tag_is_empty(data->tag)) {
			cmd = decoder_tag(data->decoder, data->input_stream,
					  data->tag);
			tag_free(data->tag);
			data->tag = tag_new();
		} else
			cmd = decoder_get_command(decoder);

		if (cmd == DECODE_COMMAND_SEEK) {
			FLAC__uint64 seek_sample = t_start +
				decoder_seek_where(decoder) *
				data->audio_format.sample_rate;
			if (seek_sample >= t_start &&
			    (t_end == 0 || seek_sample <= t_end) &&
			    FLAC__stream_decoder_seek_absolute(flac_dec, seek_sample)) {
				data->next_frame = seek_sample;
				data->position = 0;
				decoder_command_finished(decoder);
			} else
				decoder_seek_error(decoder);
		} else if (cmd == DECODE_COMMAND_STOP ||
			   FLAC__stream_decoder_get_state(flac_dec) == FLAC__STREAM_DECODER_END_OF_STREAM)
			break;

		if (t_end != 0 && data->next_frame >= t_end)
			/* end of this sub track */
			break;

		if (!FLAC__stream_decoder_process_single(flac_dec)) {
			cmd = decoder_get_command(decoder);
			if (cmd != DECODE_COMMAND_SEEK)
				break;
		}
	}

	if (cmd != DECODE_COMMAND_STOP) {
		flacPrintErroredState(FLAC__stream_decoder_get_state(flac_dec));
		FLAC__stream_decoder_finish(flac_dec);
	}
}
Example #20
0
/**
 * The decoder plugin failed to load any tags: fall back to the APE or
 * ID3 tag loader.
 */
static struct tag *
tag_fallback(const char *path, struct tag *tag)
{
	struct tag *fallback = tag_load_fallback(path);

	if (fallback != NULL) {
		/* tag was successfully loaded: copy the song
		   duration, and destroy the old (empty) tag */
		fallback->time = tag->time;
		tag_free(tag);
		return fallback;
	} else
		/* no APE/ID3 tag found: return the empty tag */
		return tag;
}
Example #21
0
static bool
mp3_open(struct input_stream *is, struct mp3_data *data,
	 struct decoder *decoder, struct tag **tag)
{
	mp3_data_init(data, decoder, is);
	*tag = NULL;
	if (!mp3_decode_first_frame(data, tag)) {
		mp3_data_finish(data);
		if (tag && *tag)
			tag_free(*tag);
		return false;
	}

	return true;
}
Example #22
0
File: screen.c Project: xorg62/wmfs
void
screen_free(void)
{
     struct screen *s;

     while(!SLIST_EMPTY(&W->h.screen))
     {
          s = SLIST_FIRST(&W->h.screen);
          SLIST_REMOVE_HEAD(&W->h.screen, next);

          infobar_free(s);
          tag_free(s);
          free(s);
     }
}
Example #23
0
static void
copy_icy_tag(struct input_curl *c)
{
	struct tag *tag = icy_tag(&c->icy_metadata);

	if (tag == NULL)
		return;

	if (c->tag != NULL)
		tag_free(c->tag);

	if (c->meta_name != NULL && !tag_has_type(tag, TAG_NAME))
		tag_add_item(tag, TAG_NAME, c->meta_name);

	c->tag = tag;
}
Example #24
0
/**
 * Frees this stream (but not the input_stream struct itself).
 */
static void
input_curl_free(struct input_curl *c)
{
	if (c->tag != NULL)
		tag_free(c->tag);
	g_free(c->meta_name);

	input_curl_easy_free(c);

	if (c->multi != NULL)
		curl_multi_cleanup(c->multi);

	g_queue_free(c->buffers);

	g_free(c->url);
	input_stream_deinit(&c->base);
	g_free(c);
}
Example #25
0
static bool
update_stream_tag(struct decoder *decoder, struct input_stream *is)
{
	struct tag *tag;

	if (is == NULL)
		return false;

	tag = input_stream_tag(is);
	if (tag == NULL)
		return false;

	if (decoder->stream_tag != NULL)
		tag_free(decoder->stream_tag);

	decoder->stream_tag = tag;
	return true;
}
Example #26
0
tag_t *
tag_alloc (void) /*{{{*/
{
	tag_t	*t;
	
	if (t = (tag_t *) malloc (sizeof (tag_t))) {
		t -> name = xmlBufferCreate ();
		t -> cname = NULL;
		t -> hash = 0;
		t -> type = NULL;
		t -> topt = NULL;
		t -> value = xmlBufferCreate ();
		t -> parm = NULL;
		t -> used = false;
		t -> next = NULL;
		if ((! t -> name) || (! t -> value))
			t = tag_free (t);
	}
	return t;
}/*}}}*/
static void
update_song_tag(struct song *song, const struct tag *new_tag)
{
	if (song_is_file(song))
		/* don't update tags of local files, only remote
		   streams may change tags dynamically */
		return;

	struct tag *old_tag = song->tag;
	song->tag = tag_dup(new_tag);

	if (old_tag != NULL)
		tag_free(old_tag);

	/* the main thread will update the playlist version when he
	   receives this event */
	event_pipe_emit(PIPE_EVENT_TAG);

	/* notify all clients that the tag of the current song has
	   changed */
	idle_add(IDLE_PLAYER);
}
Example #28
0
static bool
update_stream_tag(struct decoder *decoder, struct input_stream *is)
{
	struct tag *tag;

	tag = is != NULL
		? input_stream_tag(is)
		: NULL;
	if (tag == NULL) {
		tag = decoder->song_tag;
		if (tag == NULL)
			return false;

		/* no stream tag present - submit the song tag
		   instead */
		decoder->song_tag = NULL;
	}

	if (decoder->stream_tag != NULL)
		tag_free(decoder->stream_tag);

	decoder->stream_tag = tag;
	return true;
}
Example #29
0
static void
mp3_decode(struct decoder *decoder, struct input_stream *input_stream)
{
	struct mp3_data data;
	struct tag *tag = NULL;
	struct replay_gain_info *replay_gain_info = NULL;
	struct audio_format audio_format;

	if (!mp3_open(input_stream, &data, decoder, &tag, &replay_gain_info)) {
		if (decoder_get_command(decoder) == DECODE_COMMAND_NONE)
			g_warning
			    ("Input does not appear to be a mp3 bit stream.\n");
		return;
	}

	mp3_audio_format(&data, &audio_format);

	decoder_initialized(decoder, &audio_format,
			    data.input_stream->seekable, data.total_time);

	if (tag != NULL) {
		decoder_tag(decoder, input_stream, tag);
		tag_free(tag);
	}

	while (mp3_read(&data, &replay_gain_info)) ;

	if (replay_gain_info)
		replay_gain_info_free(replay_gain_info);

	if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK &&
	    data.mute_frame == MUTEFRAME_SEEK)
		decoder_command_finished(decoder);

	mp3_data_finish(&data);
}
Example #30
0
static void
decoder_run_song(struct decoder_control *dc,
		 const struct song *song, const char *uri)
{
	struct decoder decoder = {
		.dc = dc,
		.initial_seek_pending = dc->start_ms > 0,
		.initial_seek_running = false,
	};
	int ret;

	decoder.timestamp = 0.0;
	decoder.seeking = false;
	decoder.song_tag = song->tag != NULL && song_is_file(song)
		? tag_dup(song->tag) : NULL;
	decoder.stream_tag = NULL;
	decoder.decoder_tag = NULL;
	decoder.chunk = NULL;

	dc->state = DECODE_STATE_START;

	decoder_command_finished_locked(dc);

	pcm_convert_init(&decoder.conv_state);

	ret = song_is_file(song)
		? decoder_run_file(&decoder, uri)
		: decoder_run_stream(&decoder, uri);

	decoder_unlock(dc);

	pcm_convert_deinit(&decoder.conv_state);

	/* flush the last chunk */

	if (decoder.chunk != NULL)
		decoder_flush_chunk(&decoder);

	if (decoder.song_tag != NULL)
		tag_free(decoder.song_tag);

	if (decoder.stream_tag != NULL)
		tag_free(decoder.stream_tag);

	if (decoder.decoder_tag != NULL)
		tag_free(decoder.decoder_tag);

	decoder_lock(dc);

	dc->state = ret ? DECODE_STATE_STOP : DECODE_STATE_ERROR;
}

static void
decoder_run(struct decoder_control *dc)
{
	const struct song *song = dc->song;
	char *uri;

	assert(song != NULL);

	if (song_is_file(song))
		uri = map_song_fs(song);
	else
		uri = song_get_uri(song);

	if (uri == NULL) {
		dc->state = DECODE_STATE_ERROR;
		decoder_command_finished_locked(dc);
		return;
	}

	decoder_run_song(dc, song, uri);
	g_free(uri);

}

static gpointer
decoder_task(gpointer arg)
{
	struct decoder_control *dc = arg;

	decoder_lock(dc);

	do {
		assert(dc->state == DECODE_STATE_STOP ||
		       dc->state == DECODE_STATE_ERROR);

		switch (dc->command) {
		case DECODE_COMMAND_START:
			dc_mixramp_start(dc, NULL);
			dc_mixramp_prev_end(dc, dc->mixramp_end);
			dc->mixramp_end = NULL; /* Don't free, it's copied above. */
			dc->replay_gain_prev_db = dc->replay_gain_db;
			dc->replay_gain_db = 0;

                        /* fall through */

		case DECODE_COMMAND_SEEK:
			decoder_run(dc);
			break;

		case DECODE_COMMAND_STOP:
			decoder_command_finished_locked(dc);
			break;

		case DECODE_COMMAND_NONE:
			decoder_wait(dc);
			break;
		}
	} while (dc->command != DECODE_COMMAND_NONE || !dc->quit);

	decoder_unlock(dc);

	return NULL;
}

void
decoder_thread_start(struct decoder_control *dc)
{
	GError *e = NULL;

	assert(dc->thread == NULL);

	dc->quit = false;

	dc->thread = g_thread_create(decoder_task, dc, true, &e);
	if (dc->thread == NULL)
		MPD_ERROR("Failed to spawn decoder task: %s", e->message);
}