static const struct audio_format * autoconvert_filter_open(struct filter *_filter, struct audio_format *in_audio_format, GError **error_r) { struct autoconvert_filter *filter = (struct autoconvert_filter *)_filter; const struct audio_format *out_audio_format; assert(audio_format_valid(in_audio_format)); /* open the "real" filter */ filter->in_audio_format = *in_audio_format; out_audio_format = filter_open(filter->filter, &filter->in_audio_format, error_r); if (out_audio_format == NULL) return NULL; /* need to convert? */ if (!audio_format_equals(&filter->in_audio_format, in_audio_format)) { /* yes - create a convert_filter */ struct audio_format audio_format2 = *in_audio_format; const struct audio_format *audio_format3; filter->convert = filter_new(&convert_filter_plugin, NULL, error_r); if (filter->convert == NULL) { filter_close(filter->filter); return NULL; } audio_format3 = filter_open(filter->convert, &audio_format2, error_r); if (audio_format3 == NULL) { filter_free(filter->convert); filter_close(filter->filter); return NULL; } assert(audio_format_equals(&audio_format2, in_audio_format)); convert_filter_set(filter->convert, &filter->in_audio_format); } else /* no */ filter->convert = NULL; return out_audio_format; }
void decoder_initialized(G_GNUC_UNUSED struct decoder * decoder, const struct audio_format *audio_format, bool seekable, float total_time) { assert(dc.state == DECODE_STATE_START); assert(decoder != NULL); assert(decoder->stream_tag == NULL); assert(decoder->decoder_tag == NULL); assert(!decoder->seeking); assert(audio_format != NULL); assert(audio_format_defined(audio_format)); assert(audio_format_valid(audio_format)); dc.in_audio_format = *audio_format; getOutputAudioFormat(audio_format, &dc.out_audio_format); dc.seekable = seekable; dc.total_time = total_time; dc.state = DECODE_STATE_DECODE; notify_signal(&pc.notify); g_debug("audio_format=%u:%u:%u, seekable=%s", dc.in_audio_format.sample_rate, dc.in_audio_format.bits, dc.in_audio_format.channels, seekable ? "true" : "false"); if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) g_debug("converting to %u:%u:%u", dc.out_audio_format.sample_rate, dc.out_audio_format.bits, dc.out_audio_format.channels); }
static const struct audio_format * chain_open_child(struct filter *filter, const struct audio_format *prev_audio_format, GError **error_r) { struct audio_format conv_audio_format = *prev_audio_format; const struct audio_format *next_audio_format; next_audio_format = filter_open(filter, &conv_audio_format, error_r); if (next_audio_format == NULL) return NULL; if (!audio_format_equals(&conv_audio_format, prev_audio_format)) { struct audio_format_string s; filter_close(filter); g_set_error(error_r, filter_quark(), 0, "Audio format not supported by filter '%s': %s", filter->plugin->name, audio_format_to_string(prev_audio_format, &s)); return NULL; } return next_audio_format; }
unsigned cross_fade_calc(float duration, float total_time, float mixramp_db, float mixramp_delay, float replay_gain_db, float replay_gain_prev_db, char *mixramp_start, char *mixramp_prev_end, const struct audio_format *af, const struct audio_format *old_format, unsigned max_chunks) { unsigned int chunks = 0; float chunks_f; float mixramp_overlap; if (duration < 0 || duration >= total_time || /* we can't crossfade when the audio formats are different */ !audio_format_equals(af, old_format)) return 0; assert(duration >= 0); assert(audio_format_valid(af)); chunks_f = (float)audio_format_time_to_size(af) / (float)CHUNK_SIZE; if (isnan(mixramp_delay) || !(mixramp_start) || !(mixramp_prev_end)) { chunks = (chunks_f * duration + 0.5); } else { /* Calculate mixramp overlap. */ mixramp_overlap = mixramp_interpolate(mixramp_start, mixramp_db - replay_gain_db) + mixramp_interpolate(mixramp_prev_end, mixramp_db - replay_gain_prev_db); if (!isnan(mixramp_overlap) && (mixramp_delay <= mixramp_overlap)) { chunks = (chunks_f * (mixramp_overlap - mixramp_delay)); g_debug("will overlap %d chunks, %fs", chunks, mixramp_overlap - mixramp_delay); } } if (chunks > max_chunks) { chunks = max_chunks; g_warning("audio_buffer_size too small for computed MixRamp overlap"); } return chunks; }
void decoder_initialized(struct decoder *decoder, const struct audio_format *audio_format, bool seekable, float total_time) { struct decoder_control *dc = decoder->dc; struct audio_format_string af_string; assert(dc->state == DECODE_STATE_START); assert(dc->pipe != NULL); assert(decoder != NULL); assert(decoder->stream_tag == NULL); assert(decoder->decoder_tag == NULL); assert(!decoder->seeking); assert(audio_format != NULL); assert(audio_format_defined(audio_format)); assert(audio_format_valid(audio_format)); dc->in_audio_format = *audio_format; getOutputAudioFormat(audio_format, &dc->out_audio_format); dc->seekable = seekable; dc->total_time = total_time; decoder_lock(dc); dc->state = DECODE_STATE_DECODE; decoder_unlock(dc); player_lock_signal(); g_debug("audio_format=%s, seekable=%s", audio_format_to_string(&dc->in_audio_format, &af_string), seekable ? "true" : "false"); if (!audio_format_equals(&dc->in_audio_format, &dc->out_audio_format)) g_debug("converting to %s", audio_format_to_string(&dc->out_audio_format, &af_string)); }
static void ao_open(struct audio_output *ao) { bool success; GError *error = NULL; const struct audio_format *filter_audio_format; struct audio_format_string af_string; assert(!ao->open); assert(ao->pipe != NULL); assert(ao->chunk == NULL); assert(audio_format_valid(&ao->in_audio_format)); if (ao->fail_timer != NULL) { /* this can only happen when this output thread fails while audio_output_open() is run in the player thread */ g_timer_destroy(ao->fail_timer); ao->fail_timer = NULL; } /* enable the device (just in case the last enable has failed) */ if (!ao_enable(ao)) /* still no luck */ return; /* open the filter */ filter_audio_format = ao_filter_open(ao, &ao->in_audio_format, &error); if (filter_audio_format == NULL) { g_warning("Failed to open filter for \"%s\" [%s]: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); ao->fail_timer = g_timer_new(); return; } assert(audio_format_valid(filter_audio_format)); ao->out_audio_format = *filter_audio_format; audio_format_mask_apply(&ao->out_audio_format, &ao->config_audio_format); g_mutex_unlock(ao->mutex); success = ao_plugin_open(ao->plugin, ao->data, &ao->out_audio_format, &error); g_mutex_lock(ao->mutex); assert(!ao->open); if (!success) { g_warning("Failed to open \"%s\" [%s]: %s", ao->name, ao->plugin->name, error->message); g_error_free(error); ao_filter_close(ao); ao->fail_timer = g_timer_new(); return; } convert_filter_set(ao->convert_filter, &ao->out_audio_format); ao->open = true; g_debug("opened plugin=%s name=\"%s\" " "audio_format=%s", ao->plugin->name, ao->name, audio_format_to_string(&ao->out_audio_format, &af_string)); if (!audio_format_equals(&ao->in_audio_format, &ao->out_audio_format)) g_debug("converting from %s", audio_format_to_string(&ao->in_audio_format, &af_string)); }
enum decoder_command decoder_data(struct decoder *decoder, struct input_stream *is, const void *_data, size_t length, uint16_t kbit_rate) { struct decoder_control *dc = decoder->dc; const char *data = _data; GError *error = NULL; enum decoder_command cmd; assert(dc->state == DECODE_STATE_DECODE); assert(dc->pipe != NULL); assert(length % audio_format_frame_size(&dc->in_audio_format) == 0); decoder_lock(dc); cmd = decoder_get_virtual_command(decoder); decoder_unlock(dc); if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK || length == 0) return cmd; /* send stream tags */ if (update_stream_tag(decoder, is)) { if (decoder->decoder_tag != NULL) { /* merge with tag from decoder plugin */ struct tag *tag; tag = tag_merge(decoder->decoder_tag, decoder->stream_tag); cmd = do_send_tag(decoder, is, tag); tag_free(tag); } else /* send only the stream tag */ cmd = do_send_tag(decoder, is, decoder->stream_tag); if (cmd != DECODE_COMMAND_NONE) return cmd; } if (!audio_format_equals(&dc->in_audio_format, &dc->out_audio_format)) { data = pcm_convert(&decoder->conv_state, &dc->in_audio_format, data, length, &dc->out_audio_format, &length, &error); if (data == NULL) { /* the PCM conversion has failed - stop playback, since we have no better way to bail out */ g_warning("%s", error->message); return DECODE_COMMAND_STOP; } } while (length > 0) { struct music_chunk *chunk; char *dest; size_t nbytes; bool full; chunk = decoder_get_chunk(decoder, is); if (chunk == NULL) { assert(dc->command != DECODE_COMMAND_NONE); return dc->command; } dest = music_chunk_write(chunk, &dc->out_audio_format, decoder->timestamp - dc->song->start_ms / 1000.0, kbit_rate, &nbytes); if (dest == NULL) { /* the chunk is full, flush it */ decoder_flush_chunk(decoder); player_lock_signal(); continue; } assert(nbytes > 0); if (nbytes > length) nbytes = length; /* copy the buffer */ memcpy(dest, data, nbytes); /* expand the music pipe chunk */ full = music_chunk_expand(chunk, &dc->out_audio_format, nbytes); if (full) { /* the chunk is full, flush it */ decoder_flush_chunk(decoder); player_lock_signal(); } data += nbytes; length -= nbytes; decoder->timestamp += (double)nbytes / audio_format_time_to_size(&dc->out_audio_format); if (dc->end_ms > 0 && decoder->timestamp >= dc->end_ms / 1000.0) /* the end of this range has been reached: stop decoding */ return DECODE_COMMAND_STOP; } return DECODE_COMMAND_NONE; }
static bool audio_output_open(struct audio_output *ao, const struct audio_format *audio_format, const struct music_pipe *mp) { bool open; assert(mp != NULL); if (ao->fail_timer != NULL) { g_timer_destroy(ao->fail_timer); ao->fail_timer = NULL; } if (ao->open && audio_format_equals(audio_format, &ao->in_audio_format)) { assert(ao->pipe == mp || (ao->always_on && ao->pause)); if (ao->pause) { ao->chunk = NULL; ao->pipe = mp; /* unpause with the CANCEL command; this is a hack, but suits well for forcing the thread to leave the ao_pause() thread, and we need to flush the device buffer anyway */ /* we're not using audio_output_cancel() here, because that function is asynchronous */ ao_command(ao, AO_COMMAND_CANCEL); /* the audio output is now waiting for a signal; wake it up immediately */ g_cond_signal(ao->cond); } return true; } ao->in_audio_format = *audio_format; ao->chunk = NULL; ao->pipe = mp; if (ao->thread == NULL) audio_output_thread_start(ao); ao_command(ao, ao->open ? AO_COMMAND_REOPEN : AO_COMMAND_OPEN); open = ao->open; if (open && ao->mixer != NULL) { GError *error = NULL; if (!mixer_open(ao->mixer, &error)) { g_warning("Failed to open mixer for '%s': %s", ao->name, error->message); g_error_free(error); } } return open; }
enum decoder_command decoder_data(struct decoder *decoder, struct input_stream *is, const void *_data, size_t length, float data_time, uint16_t bitRate, struct replay_gain_info *replay_gain_info) { const char *data = _data; assert(dc.state == DECODE_STATE_DECODE); assert(length % audio_format_frame_size(&dc.in_audio_format) == 0); if (dc.command == DECODE_COMMAND_STOP || dc.command == DECODE_COMMAND_SEEK || length == 0) return dc.command; /* send stream tags */ if (update_stream_tag(decoder, is)) { enum decoder_command cmd; if (decoder->decoder_tag != NULL) { /* merge with tag from decoder plugin */ struct tag *tag; tag = tag_merge(decoder->stream_tag, decoder->decoder_tag); cmd = do_send_tag(decoder, is, tag); tag_free(tag); } else /* send only the stream tag */ cmd = do_send_tag(decoder, is, decoder->stream_tag); if (cmd != DECODE_COMMAND_NONE) return cmd; } if (!audio_format_equals(&dc.in_audio_format, &dc.out_audio_format)) { data = pcm_convert(&decoder->conv_state, &dc.in_audio_format, data, length, &dc.out_audio_format, &length); /* under certain circumstances, pcm_convert() may return an empty buffer - this condition should be investigated further, but for now, do this check as a workaround: */ if (data == NULL) return DECODE_COMMAND_NONE; } while (length > 0) { struct music_chunk *chunk; char *dest; size_t nbytes; bool full; chunk = decoder_get_chunk(decoder, is); if (chunk == NULL) { assert(dc.command != DECODE_COMMAND_NONE); return dc.command; } dest = music_chunk_write(chunk, &dc.out_audio_format, data_time, bitRate, &nbytes); if (dest == NULL) { /* the chunk is full, flush it */ decoder_flush_chunk(decoder); notify_signal(&pc.notify); continue; } assert(nbytes > 0); if (nbytes > length) nbytes = length; /* copy the buffer */ memcpy(dest, data, nbytes); /* apply replay gain or normalization */ if (replay_gain_info != NULL && replay_gain_mode != REPLAY_GAIN_OFF) replay_gain_apply(replay_gain_info, dest, nbytes, &dc.out_audio_format); else if (normalizationEnabled) normalizeData(dest, nbytes, &dc.out_audio_format); /* expand the music pipe chunk */ full = music_chunk_expand(chunk, &dc.out_audio_format, nbytes); if (full) { /* the chunk is full, flush it */ decoder_flush_chunk(decoder); notify_signal(&pc.notify); } data += nbytes; length -= nbytes; } return DECODE_COMMAND_NONE; }