player_thread: move cross-fading to output thread
Remove cross_fade_apply(), and call pcm_mix() in the output thread, mixing the chunk and chunk->other together.
This commit is contained in:
parent
d093fb2441
commit
5399a72ec1
@ -50,6 +50,12 @@ struct music_chunk {
|
|||||||
*/
|
*/
|
||||||
struct music_chunk *other;
|
struct music_chunk *other;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current mix ratio for cross-fading: 1.0 means play 100%
|
||||||
|
* of this chunk, 0.0 means play 100% of the "other" chunk.
|
||||||
|
*/
|
||||||
|
float mix_ratio;
|
||||||
|
|
||||||
/** number of bytes stored in this chunk */
|
/** number of bytes stored in this chunk */
|
||||||
uint16_t length;
|
uint16_t length;
|
||||||
|
|
||||||
|
@ -131,46 +131,3 @@ unsigned cross_fade_calc(float duration, float total_time,
|
|||||||
|
|
||||||
return chunks;
|
return chunks;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cross_fade_apply(struct music_chunk *a, const struct music_chunk *b,
|
|
||||||
const struct audio_format *format,
|
|
||||||
float mix_ratio)
|
|
||||||
{
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
assert(a != NULL);
|
|
||||||
assert(b != NULL);
|
|
||||||
assert(a->length == 0 || b->length == 0 ||
|
|
||||||
audio_format_equals(&a->audio_format, &b->audio_format));
|
|
||||||
|
|
||||||
if (a->tag == NULL && b->tag != NULL)
|
|
||||||
/* merge the tag into the destination chunk */
|
|
||||||
a->tag = tag_dup(b->tag);
|
|
||||||
|
|
||||||
size = b->length > a->length
|
|
||||||
? a->length
|
|
||||||
: b->length;
|
|
||||||
|
|
||||||
pcm_mix(a->data,
|
|
||||||
b->data,
|
|
||||||
size,
|
|
||||||
format,
|
|
||||||
mix_ratio);
|
|
||||||
|
|
||||||
if (b->length > a->length) {
|
|
||||||
/* the second buffer is larger than the first one:
|
|
||||||
there is unmixed rest at the end. Copy it over.
|
|
||||||
The output buffer API guarantees that there is
|
|
||||||
enough room in a->data. */
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
if (a->length == 0)
|
|
||||||
a->audio_format = b->audio_format;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
memcpy(a->data + a->length,
|
|
||||||
b->data + a->length,
|
|
||||||
b->length - a->length);
|
|
||||||
a->length = b->length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -45,18 +45,4 @@ unsigned cross_fade_calc(float duration, float total_time,
|
|||||||
const struct audio_format *old_format,
|
const struct audio_format *old_format,
|
||||||
unsigned max_chunks);
|
unsigned max_chunks);
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies cross fading to two chunks, i.e. mixes these chunks.
|
|
||||||
* Internally, this calls pcm_mix().
|
|
||||||
*
|
|
||||||
* @param a the chunk in the current song (and the destination chunk)
|
|
||||||
* @param b the according chunk in the new song
|
|
||||||
* @param format the audio format of both chunks (must be the same)
|
|
||||||
* @param current_chunk the relative index of the current chunk
|
|
||||||
* @param num_chunks the number of chunks used for cross fading
|
|
||||||
*/
|
|
||||||
void cross_fade_apply(struct music_chunk *a, const struct music_chunk *b,
|
|
||||||
const struct audio_format *format,
|
|
||||||
float mix_ratio);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -301,4 +301,6 @@ void audio_output_finish(struct audio_output *ao)
|
|||||||
g_mutex_free(ao->mutex);
|
g_mutex_free(ao->mutex);
|
||||||
|
|
||||||
filter_free(ao->filter);
|
filter_free(ao->filter);
|
||||||
|
|
||||||
|
pcm_buffer_deinit(&ao->cross_fade_buffer);
|
||||||
}
|
}
|
||||||
|
@ -191,6 +191,8 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
|
|||||||
ao->pause = false;
|
ao->pause = false;
|
||||||
ao->fail_timer = NULL;
|
ao->fail_timer = NULL;
|
||||||
|
|
||||||
|
pcm_buffer_init(&ao->cross_fade_buffer);
|
||||||
|
|
||||||
/* set up the filter chain */
|
/* set up the filter chain */
|
||||||
|
|
||||||
ao->filter = filter_chain_new();
|
ao->filter = filter_chain_new();
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#define MPD_OUTPUT_INTERNAL_H
|
#define MPD_OUTPUT_INTERNAL_H
|
||||||
|
|
||||||
#include "audio_format.h"
|
#include "audio_format.h"
|
||||||
|
#include "pcm_buffer.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
@ -134,6 +135,11 @@ struct audio_output {
|
|||||||
*/
|
*/
|
||||||
struct audio_format out_audio_format;
|
struct audio_format out_audio_format;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The buffer used to allocate the cross-fading result.
|
||||||
|
*/
|
||||||
|
struct pcm_buffer cross_fade_buffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The filter object of this audio output. This is an
|
* The filter object of this audio output. This is an
|
||||||
* instance of chain_filter_plugin.
|
* instance of chain_filter_plugin.
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "chunk.h"
|
#include "chunk.h"
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
#include "player_control.h"
|
#include "player_control.h"
|
||||||
|
#include "pcm_mix.h"
|
||||||
#include "filter_plugin.h"
|
#include "filter_plugin.h"
|
||||||
#include "filter/convert_filter_plugin.h"
|
#include "filter/convert_filter_plugin.h"
|
||||||
#include "filter/replay_gain_filter_plugin.h"
|
#include "filter/replay_gain_filter_plugin.h"
|
||||||
@ -299,6 +300,35 @@ ao_filter_chunk(struct audio_output *ao, const struct music_chunk *chunk,
|
|||||||
ao->replay_gain_serial = chunk->replay_gain_serial;
|
ao->replay_gain_serial = chunk->replay_gain_serial;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* cross-fade */
|
||||||
|
|
||||||
|
if (chunk->other != NULL) {
|
||||||
|
size_t other_length;
|
||||||
|
const char *other_data = ao_chunk_data(ao, chunk->other,
|
||||||
|
&other_length);
|
||||||
|
if (other_length == 0) {
|
||||||
|
*length_r = 0;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if the "other" chunk is longer, then that trailer
|
||||||
|
is used as-is, without mixing; it is part of the
|
||||||
|
"next" song being faded in, and if there's a rest,
|
||||||
|
it means cross-fading ends here */
|
||||||
|
|
||||||
|
if (length > other_length)
|
||||||
|
length = other_length;
|
||||||
|
|
||||||
|
char *dest = pcm_buffer_get(&ao->cross_fade_buffer,
|
||||||
|
other_length);
|
||||||
|
memcpy(dest, other_data, other_length);
|
||||||
|
pcm_mix(dest, data, length, &ao->in_audio_format,
|
||||||
|
1.0 - chunk->mix_ratio);
|
||||||
|
|
||||||
|
data = dest;
|
||||||
|
length = other_length;
|
||||||
|
}
|
||||||
|
|
||||||
/* apply filter chain */
|
/* apply filter chain */
|
||||||
|
|
||||||
data = filter_filter(ao->filter, data, length, &length, &error);
|
data = filter_filter(ao->filter, data, length, &length, &error);
|
||||||
|
@ -651,10 +651,9 @@ play_next_chunk(struct player *player)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (other_chunk != NULL) {
|
if (other_chunk != NULL) {
|
||||||
float mix_ratio;
|
|
||||||
|
|
||||||
chunk = music_pipe_shift(player->pipe);
|
chunk = music_pipe_shift(player->pipe);
|
||||||
assert(chunk != NULL);
|
assert(chunk != NULL);
|
||||||
|
assert(chunk->other == NULL);
|
||||||
|
|
||||||
/* don't send the tags of the new song (which
|
/* don't send the tags of the new song (which
|
||||||
is being faded in) yet; postpone it until
|
is being faded in) yet; postpone it until
|
||||||
@ -665,16 +664,13 @@ play_next_chunk(struct player *player)
|
|||||||
other_chunk->tag = NULL;
|
other_chunk->tag = NULL;
|
||||||
|
|
||||||
if (isnan(pc.mixramp_delay_seconds)) {
|
if (isnan(pc.mixramp_delay_seconds)) {
|
||||||
mix_ratio = ((float)cross_fade_position)
|
chunk->mix_ratio = ((float)cross_fade_position)
|
||||||
/ player->cross_fade_chunks;
|
/ player->cross_fade_chunks;
|
||||||
} else {
|
} else {
|
||||||
mix_ratio = nan("");
|
chunk->mix_ratio = nan("");
|
||||||
}
|
}
|
||||||
|
|
||||||
cross_fade_apply(chunk, other_chunk,
|
chunk->other = other_chunk;
|
||||||
&dc->out_audio_format,
|
|
||||||
mix_ratio);
|
|
||||||
music_buffer_return(player_buffer, other_chunk);
|
|
||||||
} else {
|
} else {
|
||||||
/* there are not enough decoded chunks yet */
|
/* there are not enough decoded chunks yet */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user