decoder/Client: add Submit methods

Replaces decoder_data() and others.
This commit is contained in:
Max Kellermann 2016-11-18 08:27:30 +01:00
parent 47a0f46ce8
commit a88040e4d5
29 changed files with 214 additions and 228 deletions

View File

@ -28,6 +28,10 @@
#include <stdint.h> #include <stdint.h>
struct AudioFormat; struct AudioFormat;
struct Tag;
struct ReplayGainInfo;
class MixRampInfo;
class InputStream;
/** /**
* An interface between the decoder plugin and the MPD core. * An interface between the decoder plugin and the MPD core.
@ -84,6 +88,64 @@ public:
* failed. * failed.
*/ */
virtual void SeekError() = 0; virtual void SeekError() = 0;
/**
* Sets the time stamp for the next data chunk [seconds]. The MPD
* core automatically counts it up, and a decoder plugin only needs to
* use this function if it thinks that adding to the time stamp based
* on the buffer size won't work.
*/
virtual void SubmitTimestamp(double t) = 0;
/**
* This function is called by the decoder plugin when it has
* successfully decoded block of input data.
*
* @param is an input stream which is buffering while we are waiting
* for the player
* @param data the source buffer
* @param length the number of bytes in the buffer
* @return the current command, or DecoderCommand::NONE if there is no
* command pending
*/
virtual DecoderCommand SubmitData(InputStream *is,
const void *data, size_t length,
uint16_t kbit_rate) = 0;
DecoderCommand SubmitData(InputStream &is,
const void *data, size_t length,
uint16_t kbit_rate) {
return SubmitData(&is, data, length, kbit_rate);
}
/**
* This function is called by the decoder plugin when it has
* successfully decoded a tag.
*
* @param is an input stream which is buffering while we are waiting
* for the player
* @param tag the tag to send
* @return the current command, or DecoderCommand::NONE if there is no
* command pending
*/
virtual DecoderCommand SubmitTag(InputStream *is, Tag &&tag) = 0 ;
DecoderCommand SubmitTag(InputStream &is, Tag &&tag) {
return SubmitTag(&is, std::move(tag));
}
/**
* Set replay gain values for the following chunks.
*
* @param replay_gain_info the replay_gain_info object; may be nullptr
* to invalidate the previous replay gain values
*/
virtual void SubmitReplayGain(const ReplayGainInfo *replay_gain_info) = 0;
/**
* Store MixRamp tags.
*/
virtual void SubmitMixRamp(MixRampInfo &&mix_ramp) = 0;
}; };
#endif #endif

View File

@ -382,12 +382,11 @@ decoder_skip(DecoderClient *client, InputStream &is, size_t size)
} }
void void
decoder_timestamp(DecoderClient &client, double t) Decoder::SubmitTimestamp(double t)
{ {
assert(t >= 0); assert(t >= 0);
auto &decoder = (Decoder &)client; timestamp = t;
decoder.timestamp = t;
} }
/** /**
@ -446,56 +445,52 @@ update_stream_tag(DecoderClient &client, InputStream *is)
} }
DecoderCommand DecoderCommand
decoder_data(DecoderClient &client, Decoder::SubmitData(InputStream *is,
InputStream *is, const void *data, size_t length,
const void *data, size_t length, uint16_t kbit_rate)
uint16_t kbit_rate)
{ {
auto &decoder = (Decoder &)client;
DecoderControl &dc = decoder.dc;
assert(dc.state == DecoderState::DECODE); assert(dc.state == DecoderState::DECODE);
assert(dc.pipe != nullptr); assert(dc.pipe != nullptr);
assert(length % dc.in_audio_format.GetFrameSize() == 0); assert(length % dc.in_audio_format.GetFrameSize() == 0);
DecoderCommand cmd = decoder_lock_get_virtual_command(decoder); DecoderCommand cmd = decoder_lock_get_virtual_command(*this);
if (cmd == DecoderCommand::STOP || cmd == DecoderCommand::SEEK || if (cmd == DecoderCommand::STOP || cmd == DecoderCommand::SEEK ||
length == 0) length == 0)
return cmd; return cmd;
assert(!decoder.initial_seek_pending); assert(!initial_seek_pending);
assert(!decoder.initial_seek_running); assert(!initial_seek_running);
/* send stream tags */ /* send stream tags */
if (update_stream_tag(decoder, is)) { if (update_stream_tag(*this, is)) {
if (decoder.decoder_tag != nullptr) { if (decoder_tag != nullptr) {
/* merge with tag from decoder plugin */ /* merge with tag from decoder plugin */
Tag *tag = Tag::Merge(*decoder.decoder_tag, Tag *tag = Tag::Merge(*decoder_tag,
*decoder.stream_tag); *stream_tag);
cmd = do_send_tag(decoder, *tag); cmd = do_send_tag(*this, *tag);
delete tag; delete tag;
} else } else
/* send only the stream tag */ /* send only the stream tag */
cmd = do_send_tag(decoder, *decoder.stream_tag); cmd = do_send_tag(*this, *stream_tag);
if (cmd != DecoderCommand::NONE) if (cmd != DecoderCommand::NONE)
return cmd; return cmd;
} }
if (decoder.convert != nullptr) { if (convert != nullptr) {
assert(dc.in_audio_format != dc.out_audio_format); assert(dc.in_audio_format != dc.out_audio_format);
try { try {
auto result = decoder.convert->Convert({data, length}); auto result = convert->Convert({data, length});
data = result.data; data = result.data;
length = result.size; length = result.size;
} catch (const std::runtime_error &e) { } catch (const std::runtime_error &e) {
/* the PCM conversion has failed - stop /* the PCM conversion has failed - stop
playback, since we have no better way to playback, since we have no better way to
bail out */ bail out */
decoder.error = std::current_exception(); error = std::current_exception();
return DecoderCommand::STOP; return DecoderCommand::STOP;
} }
} else { } else {
@ -503,10 +498,9 @@ decoder_data(DecoderClient &client,
} }
while (length > 0) { while (length > 0) {
MusicChunk *chunk;
bool full; bool full;
chunk = decoder.GetChunk(); auto *chunk = GetChunk();
if (chunk == nullptr) { if (chunk == nullptr) {
assert(dc.command != DecoderCommand::NONE); assert(dc.command != DecoderCommand::NONE);
return dc.command; return dc.command;
@ -514,12 +508,12 @@ decoder_data(DecoderClient &client,
const auto dest = const auto dest =
chunk->Write(dc.out_audio_format, chunk->Write(dc.out_audio_format,
SongTime::FromS(decoder.timestamp) - SongTime::FromS(timestamp) -
dc.song->GetStartTime(), dc.song->GetStartTime(),
kbit_rate); kbit_rate);
if (dest.IsEmpty()) { if (dest.IsEmpty()) {
/* the chunk is full, flush it */ /* the chunk is full, flush it */
decoder.FlushChunk(); FlushChunk();
continue; continue;
} }
@ -534,17 +528,17 @@ decoder_data(DecoderClient &client,
full = chunk->Expand(dc.out_audio_format, nbytes); full = chunk->Expand(dc.out_audio_format, nbytes);
if (full) { if (full) {
/* the chunk is full, flush it */ /* the chunk is full, flush it */
decoder.FlushChunk(); FlushChunk();
} }
data = (const uint8_t *)data + nbytes; data = (const uint8_t *)data + nbytes;
length -= nbytes; length -= nbytes;
decoder.timestamp += (double)nbytes / timestamp += (double)nbytes /
dc.out_audio_format.GetTimeToSize(); dc.out_audio_format.GetTimeToSize();
if (dc.end_time.IsPositive() && if (dc.end_time.IsPositive() &&
decoder.timestamp >= dc.end_time.ToDoubleS()) timestamp >= dc.end_time.ToDoubleS())
/* the end of this range has been reached: /* the end of this range has been reached:
stop decoding */ stop decoding */
return DecoderCommand::STOP; return DecoderCommand::STOP;
@ -554,11 +548,8 @@ decoder_data(DecoderClient &client,
} }
DecoderCommand DecoderCommand
decoder_tag(DecoderClient &client, InputStream *is, Decoder::SubmitTag(InputStream *is, Tag &&tag)
Tag &&tag)
{ {
auto &decoder = (Decoder &)client;
gcc_unused const DecoderControl &dc = decoder.dc;
DecoderCommand cmd; DecoderCommand cmd;
assert(dc.state == DecoderState::DECODE); assert(dc.state == DecoderState::DECODE);
@ -566,16 +557,16 @@ decoder_tag(DecoderClient &client, InputStream *is,
/* save the tag */ /* save the tag */
delete decoder.decoder_tag; delete decoder_tag;
decoder.decoder_tag = new Tag(std::move(tag)); decoder_tag = new Tag(std::move(tag));
/* check for a new stream tag */ /* check for a new stream tag */
update_stream_tag(decoder, is); update_stream_tag(*this, is);
/* check if we're seeking */ /* check if we're seeking */
if (decoder_prepare_initial_seek(decoder)) if (decoder_prepare_initial_seek(*this))
/* during initial seek, no music chunk must be created /* during initial seek, no music chunk must be created
until seeking is finished; skip the rest of the until seeking is finished; skip the rest of the
function here */ function here */
@ -583,28 +574,24 @@ decoder_tag(DecoderClient &client, InputStream *is,
/* send tag to music pipe */ /* send tag to music pipe */
if (decoder.stream_tag != nullptr) { if (stream_tag != nullptr) {
/* merge with tag from input stream */ /* merge with tag from input stream */
Tag *merged; Tag *merged;
merged = Tag::Merge(*decoder.stream_tag, merged = Tag::Merge(*stream_tag, *decoder_tag);
*decoder.decoder_tag); cmd = do_send_tag(*this, *merged);
cmd = do_send_tag(decoder, *merged);
delete merged; delete merged;
} else } else
/* send only the decoder tag */ /* send only the decoder tag */
cmd = do_send_tag(decoder, *decoder.decoder_tag); cmd = do_send_tag(*this, *decoder_tag);
return cmd; return cmd;
} }
void void
decoder_replay_gain(DecoderClient &client, Decoder::SubmitReplayGain(const ReplayGainInfo *new_replay_gain_info)
const ReplayGainInfo *replay_gain_info)
{ {
auto &decoder = (Decoder &)client; if (new_replay_gain_info != nullptr) {
if (replay_gain_info != nullptr) {
static unsigned serial; static unsigned serial;
if (++serial == 0) if (++serial == 0)
serial = 1; serial = 1;
@ -614,32 +601,29 @@ decoder_replay_gain(DecoderClient &client,
if (rgm != REPLAY_GAIN_ALBUM) if (rgm != REPLAY_GAIN_ALBUM)
rgm = REPLAY_GAIN_TRACK; rgm = REPLAY_GAIN_TRACK;
const auto &tuple = replay_gain_info->tuples[rgm]; const auto &tuple = new_replay_gain_info->tuples[rgm];
const auto scale = const auto scale =
tuple.CalculateScale(replay_gain_preamp, tuple.CalculateScale(replay_gain_preamp,
replay_gain_missing_preamp, replay_gain_missing_preamp,
replay_gain_limit); replay_gain_limit);
decoder.dc.replay_gain_db = 20.0 * log10f(scale); dc.replay_gain_db = 20.0 * log10f(scale);
} }
decoder.replay_gain_info = *replay_gain_info; replay_gain_info = *new_replay_gain_info;
decoder.replay_gain_serial = serial; replay_gain_serial = serial;
if (decoder.current_chunk != nullptr) { if (current_chunk != nullptr) {
/* flush the current chunk because the new /* flush the current chunk because the new
replay gain values affect the following replay gain values affect the following
samples */ samples */
decoder.FlushChunk(); FlushChunk();
} }
} else } else
decoder.replay_gain_serial = 0; replay_gain_serial = 0;
} }
void void
decoder_mixramp(DecoderClient &client, MixRampInfo &&mix_ramp) Decoder::SubmitMixRamp(MixRampInfo &&mix_ramp)
{ {
auto &decoder = (Decoder &)client;
DecoderControl &dc = decoder.dc;
dc.SetMixRamp(std::move(mix_ramp)); dc.SetMixRamp(std::move(mix_ramp));
} }

View File

@ -104,73 +104,4 @@ decoder_read_full(DecoderClient *decoder, InputStream &is,
bool bool
decoder_skip(DecoderClient *decoder, InputStream &is, size_t size); decoder_skip(DecoderClient *decoder, InputStream &is, size_t size);
/**
* Sets the time stamp for the next data chunk [seconds]. The MPD
* core automatically counts it up, and a decoder plugin only needs to
* use this function if it thinks that adding to the time stamp based
* on the buffer size won't work.
*/
void
decoder_timestamp(DecoderClient &decoder, double t);
/**
* This function is called by the decoder plugin when it has
* successfully decoded block of input data.
*
* @param decoder the decoder object
* @param is an input stream which is buffering while we are waiting
* for the player
* @param data the source buffer
* @param length the number of bytes in the buffer
* @return the current command, or DecoderCommand::NONE if there is no
* command pending
*/
DecoderCommand
decoder_data(DecoderClient &decoder, InputStream *is,
const void *data, size_t length,
uint16_t kbit_rate);
static inline DecoderCommand
decoder_data(DecoderClient &decoder, InputStream &is,
const void *data, size_t length,
uint16_t kbit_rate)
{
return decoder_data(decoder, &is, data, length, kbit_rate);
}
/**
* This function is called by the decoder plugin when it has
* successfully decoded a tag.
*
* @param is an input stream which is buffering while we are waiting
* for the player
* @param tag the tag to send
* @return the current command, or DecoderCommand::NONE if there is no
* command pending
*/
DecoderCommand
decoder_tag(DecoderClient &decoder, InputStream *is, Tag &&tag);
static inline DecoderCommand
decoder_tag(DecoderClient &decoder, InputStream &is, Tag &&tag)
{
return decoder_tag(decoder, &is, std::move(tag));
}
/**
* Set replay gain values for the following chunks.
*
* @param replay_gain_info the replay_gain_info object; may be nullptr
* to invalidate the previous replay gain values
*/
void
decoder_replay_gain(DecoderClient &decoder,
const ReplayGainInfo *replay_gain_info);
/**
* Store MixRamp tags.
*/
void
decoder_mixramp(DecoderClient &decoder, MixRampInfo &&mix_ramp);
#endif #endif

View File

@ -124,6 +124,13 @@ struct Decoder final : DecoderClient {
SongTime GetSeekTime() override; SongTime GetSeekTime() override;
uint64_t GetSeekFrame() override; uint64_t GetSeekFrame() override;
void SeekError() override; void SeekError() override;
void SubmitTimestamp(double t) override;
DecoderCommand SubmitData(InputStream *is,
const void *data, size_t length,
uint16_t kbit_rate) override;
DecoderCommand SubmitTag(InputStream *is, Tag &&tag) override ;
void SubmitReplayGain(const ReplayGainInfo *replay_gain_info) override;
void SubmitMixRamp(MixRampInfo &&mix_ramp) override;
}; };
#endif #endif

View File

@ -245,14 +245,14 @@ decoder_run_stream_fallback(Decoder &decoder, InputStream &is)
/** /**
* Attempt to load replay gain data, and pass it to * Attempt to load replay gain data, and pass it to
* decoder_replay_gain(). * DecoderClient::SubmitReplayGain().
*/ */
static void static void
LoadReplayGain(Decoder &decoder, InputStream &is) LoadReplayGain(DecoderClient &client, InputStream &is)
{ {
ReplayGainInfo info; ReplayGainInfo info;
if (replay_gain_ape_read(is, info)) if (replay_gain_ape_read(is, info))
decoder_replay_gain(decoder, &info); client.SubmitReplayGain(&info);
} }
/** /**

View File

@ -73,9 +73,9 @@ adplug_file_decode(DecoderClient &client, Path path_fs)
int16_t buffer[2048]; int16_t buffer[2048];
constexpr unsigned frames_per_buffer = ARRAY_SIZE(buffer) / 2; constexpr unsigned frames_per_buffer = ARRAY_SIZE(buffer) / 2;
opl.update(buffer, frames_per_buffer); opl.update(buffer, frames_per_buffer);
cmd = decoder_data(client, nullptr, cmd = client.SubmitData(nullptr,
buffer, sizeof(buffer), buffer, sizeof(buffer),
0); 0);
} while (cmd == DecoderCommand::NONE); } while (cmd == DecoderCommand::NONE);
delete player; delete player;

View File

@ -223,9 +223,9 @@ audiofile_stream_decode(DecoderClient &client, InputStream &is)
if (nframes <= 0) if (nframes <= 0)
break; break;
cmd = decoder_data(client, nullptr, cmd = client.SubmitData(nullptr,
chunk, nframes * frame_size, chunk, nframes * frame_size,
kbit_rate); kbit_rate);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
AFframecount frame = client.GetSeekFrame(); AFframecount frame = client.GetSeekFrame();

View File

@ -408,8 +408,8 @@ dsdiff_decode_chunk(DecoderClient &client, InputStream &is,
if (lsbitfirst) if (lsbitfirst)
bit_reverse_buffer(buffer, buffer + nbytes); bit_reverse_buffer(buffer, buffer + nbytes);
cmd = decoder_data(client, is, buffer, nbytes, cmd = client.SubmitData(is, buffer, nbytes,
sample_rate / 1000); sample_rate / 1000);
} }
return true; return true;

View File

@ -289,9 +289,9 @@ dsf_decode_chunk(DecoderClient &client, InputStream &is,
uint8_t interleaved_buffer[MAX_CHANNELS * DSF_BLOCK_SIZE]; uint8_t interleaved_buffer[MAX_CHANNELS * DSF_BLOCK_SIZE];
InterleaveDsfBlock(interleaved_buffer, buffer, channels); InterleaveDsfBlock(interleaved_buffer, buffer, channels);
cmd = decoder_data(client, is, cmd = client.SubmitData(is,
interleaved_buffer, block_size, interleaved_buffer, block_size,
sample_rate / 1000); sample_rate / 1000);
++i; ++i;
} }

View File

@ -393,9 +393,9 @@ faad_stream_decode(DecoderClient &client, InputStream &is,
/* send PCM samples to MPD */ /* send PCM samples to MPD */
cmd = decoder_data(client, is, decoded, cmd = client.SubmitData(is, decoded,
(size_t)frame_info.samples * 2, (size_t)frame_info.samples * 2,
bit_rate); bit_rate);
} while (cmd != DecoderCommand::STOP); } while (cmd != DecoderCommand::STOP);
} }

View File

@ -218,7 +218,8 @@ PtsToPcmFrame(uint64_t pts, const AVStream &stream,
} }
/** /**
* Invoke decoder_data() with the contents of an #AVFrame. * Invoke DecoderClient::SubmitData() with the contents of an
* #AVFrame.
*/ */
static DecoderCommand static DecoderCommand
FfmpegSendFrame(DecoderClient &client, InputStream &is, FfmpegSendFrame(DecoderClient &client, InputStream &is,
@ -250,9 +251,9 @@ FfmpegSendFrame(DecoderClient &client, InputStream &is,
skip_bytes = 0; skip_bytes = 0;
} }
return decoder_data(client, is, return client.SubmitData(is,
output_buffer.data, output_buffer.size, output_buffer.data, output_buffer.size,
codec_context.bit_rate / 1000); codec_context.bit_rate / 1000);
} }
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 0) #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 0)
@ -330,9 +331,8 @@ ffmpeg_send_packet(DecoderClient &client, InputStream &is,
if (cur_frame < min_frame) if (cur_frame < min_frame)
skip_bytes = pcm_frame_size * (min_frame - cur_frame); skip_bytes = pcm_frame_size * (min_frame - cur_frame);
} else } else
decoder_timestamp(client, client.SubmitTimestamp(FfmpegTimeToDouble(pts,
FfmpegTimeToDouble(pts, stream.time_base));
stream.time_base));
} }
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 0) #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(57, 37, 0)
@ -540,10 +540,10 @@ FfmpegParseMetaData(DecoderClient &client,
FfmpegParseMetaData(format_context, audio_stream, rg, mr); FfmpegParseMetaData(format_context, audio_stream, rg, mr);
if (rg.IsDefined()) if (rg.IsDefined())
decoder_replay_gain(client, &rg); client.SubmitReplayGain(&rg);
if (mr.IsDefined()) if (mr.IsDefined())
decoder_mixramp(client, std::move(mr)); client.SubmitMixRamp(std::move(mr));
} }
static void static void
@ -576,7 +576,7 @@ FfmpegScanTag(const AVFormatContext &format_context, int audio_stream,
/** /**
* Check if a new stream tag was received and pass it to * Check if a new stream tag was received and pass it to
* decoder_tag(). * DecoderClient::SubmitTag().
*/ */
static void static void
FfmpegCheckTag(DecoderClient &client, InputStream &is, FfmpegCheckTag(DecoderClient &client, InputStream &is,
@ -593,7 +593,7 @@ FfmpegCheckTag(DecoderClient &client, InputStream &is,
TagBuilder tag; TagBuilder tag;
FfmpegScanTag(format_context, audio_stream, tag); FfmpegScanTag(format_context, audio_stream, tag);
if (!tag.IsEmpty()) if (!tag.IsEmpty())
decoder_tag(client, is, tag.Commit()); client.SubmitTag(is, tag.Commit());
} }
#endif #endif

View File

@ -77,9 +77,9 @@ FlacDecoder::OnVorbisComment(const FLAC__StreamMetadata_VorbisComment &vc)
{ {
ReplayGainInfo rgi; ReplayGainInfo rgi;
if (flac_parse_replay_gain(rgi, vc)) if (flac_parse_replay_gain(rgi, vc))
decoder_replay_gain(*GetClient(), &rgi); GetClient()->SubmitReplayGain(&rgi);
decoder_mixramp(*GetClient(), flac_parse_mixramp(vc)); GetClient()->SubmitMixRamp(flac_parse_mixramp(vc));
tag = flac_vorbis_comments_to_tag(&vc); tag = flac_vorbis_comments_to_tag(&vc);
} }
@ -148,9 +148,9 @@ FlacDecoder::OnWrite(const FLAC__Frame &frame,
unsigned bit_rate = nbytes * 8 * frame.header.sample_rate / unsigned bit_rate = nbytes * 8 * frame.header.sample_rate /
(1000 * frame.header.blocksize); (1000 * frame.header.blocksize);
auto cmd = decoder_data(*GetClient(), GetInputStream(), auto cmd = GetClient()->SubmitData(GetInputStream(),
data.data, data.size, data.data, data.size,
bit_rate); bit_rate);
switch (cmd) { switch (cmd) {
case DecoderCommand::NONE: case DecoderCommand::NONE:
case DecoderCommand::START: case DecoderCommand::START:

View File

@ -147,8 +147,8 @@ flac_decoder_loop(FlacDecoder *data, FLAC__StreamDecoder *flac_dec)
while (true) { while (true) {
DecoderCommand cmd; DecoderCommand cmd;
if (!data->tag.IsEmpty()) { if (!data->tag.IsEmpty()) {
cmd = decoder_tag(client, data->GetInputStream(), cmd = client.SubmitTag(data->GetInputStream(),
std::move(data->tag)); std::move(data->tag));
data->tag.Clear(); data->tag.Clear();
} else } else
cmd = client.GetCommand(); cmd = client.GetCommand();

View File

@ -176,8 +176,7 @@ fluidsynth_file_decode(DecoderClient &client, Path path_fs)
if (ret != 0) if (ret != 0)
break; break;
cmd = decoder_data(client, nullptr, buffer, sizeof(buffer), cmd = client.SubmitData(nullptr, buffer, sizeof(buffer), 0);
0);
if (cmd != DecoderCommand::NONE) if (cmd != DecoderCommand::NONE)
break; break;
} }

View File

@ -190,7 +190,7 @@ gme_file_decode(DecoderClient &client, Path path_fs)
return; return;
} }
cmd = decoder_data(client, nullptr, buf, sizeof(buf), 0); cmd = client.SubmitData(nullptr, buf, sizeof(buf), 0);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
unsigned where = client.GetSeekTime().ToMS(); unsigned where = client.GetSeekTime().ToMS();
gme_err = gme_seek(emu, where); gme_err = gme_seek(emu, where);

View File

@ -182,13 +182,14 @@ struct MadDecoder {
void UpdateTimerNextFrame(); void UpdateTimerNextFrame();
/** /**
* Sends the synthesized current frame via decoder_data(). * Sends the synthesized current frame via
* DecoderClient::SubmitData().
*/ */
DecoderCommand SendPCM(unsigned i, unsigned pcm_length); DecoderCommand SendPCM(unsigned i, unsigned pcm_length);
/** /**
* Synthesize the current frame and send it via * Synthesize the current frame and send it via
* decoder_data(). * DecoderClient::SubmitData().
*/ */
DecoderCommand SyncAndSend(); DecoderCommand SyncAndSend();
@ -361,11 +362,11 @@ MadDecoder::ParseId3(size_t tagsize, Tag **mpd_tag)
ReplayGainInfo rgi; ReplayGainInfo rgi;
if (parse_id3_replay_gain_info(rgi, id3_tag)) { if (parse_id3_replay_gain_info(rgi, id3_tag)) {
decoder_replay_gain(*client, &rgi); client->SubmitReplayGain(&rgi);
found_replay_gain = true; found_replay_gain = true;
} }
decoder_mixramp(*client, parse_id3_mixramp(id3_tag)); client->SubmitMixRamp(parse_id3_mixramp(id3_tag));
} }
id3_tag_delete(id3_tag); id3_tag_delete(id3_tag);
@ -806,7 +807,7 @@ MadDecoder::DecodeFirstFrame(Tag **tag)
rgi.Clear(); rgi.Clear();
rgi.tuples[REPLAY_GAIN_TRACK].gain = lame.track_gain; rgi.tuples[REPLAY_GAIN_TRACK].gain = lame.track_gain;
rgi.tuples[REPLAY_GAIN_TRACK].peak = lame.peak; rgi.tuples[REPLAY_GAIN_TRACK].peak = lame.peak;
decoder_replay_gain(*client, &rgi); client->SubmitReplayGain(&rgi);
} }
} }
} }
@ -903,9 +904,9 @@ MadDecoder::SendPCM(unsigned i, unsigned pcm_length)
MAD_NCHANNELS(&frame.header)); MAD_NCHANNELS(&frame.header));
num_samples *= MAD_NCHANNELS(&frame.header); num_samples *= MAD_NCHANNELS(&frame.header);
auto cmd = decoder_data(*client, input_stream, output_buffer, auto cmd = client->SubmitData(input_stream, output_buffer,
sizeof(output_buffer[0]) * num_samples, sizeof(output_buffer[0]) * num_samples,
bit_rate / 1000); bit_rate / 1000);
if (cmd != DecoderCommand::NONE) if (cmd != DecoderCommand::NONE)
return cmd; return cmd;
} }
@ -1010,8 +1011,8 @@ MadDecoder::Read()
ret = DecodeNextFrameHeader(&tag); ret = DecodeNextFrameHeader(&tag);
if (tag != nullptr) { if (tag != nullptr) {
decoder_tag(*client, input_stream, client->SubmitTag(input_stream,
std::move(*tag)); std::move(*tag));
delete tag; delete tag;
} }
} while (ret == DECODE_CONT); } while (ret == DECODE_CONT);
@ -1057,7 +1058,7 @@ mp3_decode(DecoderClient &client, InputStream &input_stream)
data.total_time); data.total_time);
if (tag != nullptr) { if (tag != nullptr) {
decoder_tag(client, input_stream, std::move(*tag)); client.SubmitTag(input_stream, std::move(*tag));
delete tag; delete tag;
} }

View File

@ -177,7 +177,7 @@ mikmod_decoder_file_decode(DecoderClient &client, Path path_fs)
DecoderCommand cmd = DecoderCommand::NONE; DecoderCommand cmd = DecoderCommand::NONE;
while (cmd == DecoderCommand::NONE && Player_Active()) { while (cmd == DecoderCommand::NONE && Player_Active()) {
ret = VC_WriteBytes(buffer, sizeof(buffer)); ret = VC_WriteBytes(buffer, sizeof(buffer));
cmd = decoder_data(client, nullptr, buffer, ret, 0); cmd = client.SubmitData(nullptr, buffer, ret, 0);
} }
Player_Stop(); Player_Stop();

View File

@ -160,9 +160,9 @@ mod_decode(DecoderClient &client, InputStream &is)
if (ret <= 0) if (ret <= 0)
break; break;
cmd = decoder_data(client, nullptr, cmd = client.SubmitData(nullptr,
audio_buffer, ret, audio_buffer, ret,
0); 0);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
ModPlug_Seek(f, client.GetSeekTime().ToMS()); ModPlug_Seek(f, client.GetSeekTime().ToMS());

View File

@ -172,7 +172,7 @@ mpcdec_decode(DecoderClient &client, InputStream &is)
rgi.tuples[REPLAY_GAIN_TRACK].gain = MPC_OLD_GAIN_REF - (info.gain_title / 256.); rgi.tuples[REPLAY_GAIN_TRACK].gain = MPC_OLD_GAIN_REF - (info.gain_title / 256.);
rgi.tuples[REPLAY_GAIN_TRACK].peak = pow(10, info.peak_title / 256. / 20) / 32767; rgi.tuples[REPLAY_GAIN_TRACK].peak = pow(10, info.peak_title / 256. / 20) / 32767;
decoder_replay_gain(client, &rgi); client.SubmitReplayGain(&rgi);
client.Ready(audio_format, is.IsSeekable(), client.Ready(audio_format, is.IsSeekable(),
SongTime::FromS(mpc_streaminfo_get_length(&info))); SongTime::FromS(mpc_streaminfo_get_length(&info)));
@ -213,9 +213,9 @@ mpcdec_decode(DecoderClient &client, InputStream &is)
long bit_rate = unsigned(frame.bits) * audio_format.sample_rate long bit_rate = unsigned(frame.bits) * audio_format.sample_rate
/ (1000 * frame.samples); / (1000 * frame.samples);
cmd = decoder_data(client, is, cmd = client.SubmitData(is,
chunk, ret * sizeof(chunk[0]), chunk, ret * sizeof(chunk[0]),
bit_rate); bit_rate);
} while (cmd != DecoderCommand::STOP); } while (cmd != DecoderCommand::STOP);
mpc_demux_exit(demux); mpc_demux_exit(demux);

View File

@ -129,7 +129,7 @@ mpd_mpg123_id3v2_tag(DecoderClient &client, const mpg123_id3v2 &id3v2)
for (size_t i = 0, n = id3v2.comments; i < n; ++i) for (size_t i = 0, n = id3v2.comments; i < n; ++i)
AddTagItem(tag, TAG_COMMENT, id3v2.comment_list[i].text); AddTagItem(tag, TAG_COMMENT, id3v2.comment_list[i].text);
decoder_tag(client, nullptr, tag.Commit()); client.SubmitTag(nullptr, tag.Commit());
} }
static void static void
@ -154,10 +154,10 @@ mpd_mpg123_id3v2_extras(DecoderClient &client, const mpg123_id3v2 &id3v2)
} }
if (found_replay_gain) if (found_replay_gain)
decoder_replay_gain(client, &replay_gain); client.SubmitReplayGain(&replay_gain);
if (found_mixramp) if (found_mixramp)
decoder_mixramp(client, std::move(mix_ramp)); client.SubmitMixRamp(std::move(mix_ramp));
} }
static void static void
@ -257,7 +257,7 @@ mpd_mpg123_file_decode(DecoderClient &client, Path path_fs)
/* send to MPD */ /* send to MPD */
cmd = decoder_data(client, nullptr, buffer, nbytes, info.bitrate); cmd = client.SubmitData(nullptr, buffer, nbytes, info.bitrate);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
off_t c = client.GetSeekFrame(); off_t c = client.GetSeekFrame();
@ -266,7 +266,7 @@ mpd_mpg123_file_decode(DecoderClient &client, Path path_fs)
client.SeekError(); client.SeekError();
else { else {
client.CommandFinished(); client.CommandFinished();
decoder_timestamp(client, c/(double)audio_format.sample_rate); client.SubmitTimestamp(c / (double)audio_format.sample_rate);
} }
cmd = DecoderCommand::NONE; cmd = DecoderCommand::NONE;

View File

@ -212,10 +212,10 @@ MPDOpusDecoder::HandleTags(const ogg_packet &packet)
&rgi, &rgi,
add_tag_handler, &tag_builder) && add_tag_handler, &tag_builder) &&
!tag_builder.IsEmpty()) { !tag_builder.IsEmpty()) {
decoder_replay_gain(client, &rgi); client.SubmitReplayGain(&rgi);
Tag tag = tag_builder.Commit(); Tag tag = tag_builder.Commit();
auto cmd = decoder_tag(client, input_stream, std::move(tag)); auto cmd = client.SubmitTag(input_stream, std::move(tag));
if (cmd != DecoderCommand::NONE) if (cmd != DecoderCommand::NONE)
throw cmd; throw cmd;
} }
@ -237,16 +237,15 @@ MPDOpusDecoder::HandleAudio(const ogg_packet &packet)
if (nframes > 0) { if (nframes > 0) {
const size_t nbytes = nframes * frame_size; const size_t nbytes = nframes * frame_size;
auto cmd = decoder_data(client, input_stream, auto cmd = client.SubmitData(input_stream,
output_buffer, nbytes, output_buffer, nbytes,
0); 0);
if (cmd != DecoderCommand::NONE) if (cmd != DecoderCommand::NONE)
throw cmd; throw cmd;
if (packet.granulepos > 0) if (packet.granulepos > 0)
decoder_timestamp(client, client.SubmitTimestamp(double(packet.granulepos)
double(packet.granulepos) / opus_sample_rate);
/ opus_sample_rate);
} }
} }

View File

@ -154,7 +154,8 @@ pcm_stream_decode(DecoderClient &client, InputStream &is)
auto r = buffer.Read(); auto r = buffer.Read();
/* round down to the nearest frame size, because we /* round down to the nearest frame size, because we
must not pass partial frames to decoder_data() */ must not pass partial frames to
DecoderClient::SubmitData() */
r.size -= r.size % frame_size; r.size -= r.size % frame_size;
buffer.Consume(r.size); buffer.Consume(r.size);
@ -165,7 +166,7 @@ pcm_stream_decode(DecoderClient &client, InputStream &is)
(uint16_t *)(r.data + r.size)); (uint16_t *)(r.data + r.size));
cmd = !r.IsEmpty() cmd = !r.IsEmpty()
? decoder_data(client, is, r.data, r.size, 0) ? client.SubmitData(is, r.data, r.size, 0)
: client.GetCommand(); : client.GetCommand();
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
uint64_t frame = client.GetSeekFrame(); uint64_t frame = client.GetSeekFrame();

View File

@ -366,9 +366,9 @@ sidplay_file_decode(DecoderClient &client, Path path_fs)
const size_t nbytes = result; const size_t nbytes = result;
#endif #endif
decoder_timestamp(client, (double)player.time() / timebase); client.SubmitTimestamp((double)player.time() / timebase);
cmd = decoder_data(client, nullptr, buffer, nbytes, 0); cmd = client.SubmitData(nullptr, buffer, nbytes, 0);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
unsigned data_time = player.time(); unsigned data_time = player.time();

View File

@ -221,9 +221,9 @@ sndfile_stream_decode(DecoderClient &client, InputStream &is)
if (num_frames <= 0) if (num_frames <= 0)
break; break;
cmd = decoder_data(client, is, cmd = client.SubmitData(is,
buffer, num_frames * frame_size, buffer, num_frames * frame_size,
0); 0);
if (cmd == DecoderCommand::SEEK) { if (cmd == DecoderCommand::SEEK) {
sf_count_t c = client.GetSeekFrame(); sf_count_t c = client.GetSeekFrame();
c = sf_seek(sf, c, SEEK_SET); c = sf_seek(sf, c, SEEK_SET);

View File

@ -156,7 +156,7 @@ vorbis_send_comments(DecoderClient &client, InputStream &is,
if (!tag) if (!tag)
return; return;
decoder_tag(client, is, std::move(*tag)); client.SubmitTag(is, std::move(*tag));
delete tag; delete tag;
} }
@ -211,9 +211,9 @@ VorbisDecoder::SubmitSomePcm()
vorbis_synthesis_read(&dsp, n_frames); vorbis_synthesis_read(&dsp, n_frames);
const size_t nbytes = n_frames * frame_size; const size_t nbytes = n_frames * frame_size;
auto cmd = decoder_data(client, input_stream, auto cmd = client.SubmitData(input_stream,
buffer, nbytes, buffer, nbytes,
0); 0);
if (cmd != DecoderCommand::NONE) if (cmd != DecoderCommand::NONE)
throw cmd; throw cmd;
@ -252,7 +252,7 @@ VorbisDecoder::OnOggPacket(const ogg_packet &_packet)
ReplayGainInfo rgi; ReplayGainInfo rgi;
if (vorbis_comments_to_replay_gain(rgi, vc.user_comments)) if (vorbis_comments_to_replay_gain(rgi, vc.user_comments))
decoder_replay_gain(client, &rgi); client.SubmitReplayGain(&rgi);
} else { } else {
if (!dsp_initialized) { if (!dsp_initialized) {
dsp_initialized = true; dsp_initialized = true;
@ -277,8 +277,7 @@ VorbisDecoder::OnOggPacket(const ogg_packet &_packet)
#ifndef HAVE_TREMOR #ifndef HAVE_TREMOR
if (packet.granulepos > 0) if (packet.granulepos > 0)
decoder_timestamp(client, client.SubmitTimestamp(vorbis_granule_time(&dsp, packet.granulepos));
vorbis_granule_time(&dsp, packet.granulepos));
#endif #endif
} }
} }

View File

@ -196,9 +196,9 @@ wavpack_decode(DecoderClient &client, WavpackContext *wpc, bool can_seek)
format_samples(bytes_per_sample, chunk, format_samples(bytes_per_sample, chunk,
samples_got * audio_format.channels); samples_got * audio_format.channels);
cmd = decoder_data(client, nullptr, chunk, cmd = client.SubmitData(nullptr, chunk,
samples_got * output_sample_size, samples_got * output_sample_size,
bitrate); bitrate);
} }
} }
@ -563,7 +563,7 @@ wavpack_filedecode(DecoderClient &client, Path path_fs)
ReplayGainInfo rgi; ReplayGainInfo rgi;
if (wavpack_replaygain(rgi, wpc)) if (wavpack_replaygain(rgi, wpc))
decoder_replay_gain(client, &rgi); client.SubmitReplayGain(&rgi);
wavpack_decode(client, wpc, true); wavpack_decode(client, wpc, true);

View File

@ -75,7 +75,7 @@ wildmidi_output(DecoderClient &client, midi *wm)
if (length <= 0) if (length <= 0)
return DecoderCommand::STOP; return DecoderCommand::STOP;
return decoder_data(client, nullptr, buffer, length, 0); return client.SubmitData(nullptr, buffer, length, 0);
} }
static void static void

View File

@ -127,16 +127,14 @@ decoder_skip(DecoderClient *client, InputStream &is, size_t size)
} }
void void
decoder_timestamp(gcc_unused DecoderClient &client, FakeDecoder::SubmitTimestamp(gcc_unused double t)
gcc_unused double t)
{ {
} }
DecoderCommand DecoderCommand
decoder_data(gcc_unused DecoderClient &client, FakeDecoder::SubmitData(gcc_unused InputStream *is,
gcc_unused InputStream *is, const void *data, size_t datalen,
const void *data, size_t datalen, gcc_unused uint16_t kbit_rate)
gcc_unused uint16_t kbit_rate)
{ {
static uint16_t prev_kbit_rate; static uint16_t prev_kbit_rate;
if (kbit_rate != prev_kbit_rate) { if (kbit_rate != prev_kbit_rate) {
@ -149,9 +147,8 @@ decoder_data(gcc_unused DecoderClient &client,
} }
DecoderCommand DecoderCommand
decoder_tag(gcc_unused DecoderClient &client, FakeDecoder::SubmitTag(gcc_unused InputStream *is,
gcc_unused InputStream *is, Tag &&tag)
Tag &&tag)
{ {
fprintf(stderr, "TAG: duration=%f\n", tag.duration.ToDoubleS()); fprintf(stderr, "TAG: duration=%f\n", tag.duration.ToDoubleS());
@ -162,8 +159,7 @@ decoder_tag(gcc_unused DecoderClient &client,
} }
void void
decoder_replay_gain(gcc_unused DecoderClient &client, FakeDecoder::SubmitReplayGain(const ReplayGainInfo *rgi)
const ReplayGainInfo *rgi)
{ {
const ReplayGainTuple *tuple = &rgi->tuples[REPLAY_GAIN_ALBUM]; const ReplayGainTuple *tuple = &rgi->tuples[REPLAY_GAIN_ALBUM];
if (tuple->IsDefined()) if (tuple->IsDefined())
@ -177,7 +173,7 @@ decoder_replay_gain(gcc_unused DecoderClient &client,
} }
void void
decoder_mixramp(gcc_unused DecoderClient &client, gcc_unused MixRampInfo &&mix_ramp) FakeDecoder::SubmitMixRamp(gcc_unused MixRampInfo &&mix_ramp)
{ {
fprintf(stderr, "MixRamp: start='%s' end='%s'\n", fprintf(stderr, "MixRamp: start='%s' end='%s'\n",
mix_ramp.GetStart(), mix_ramp.GetEnd()); mix_ramp.GetStart(), mix_ramp.GetEnd());

View File

@ -39,6 +39,13 @@ struct FakeDecoder final : DecoderClient {
SongTime GetSeekTime() override; SongTime GetSeekTime() override;
uint64_t GetSeekFrame() override; uint64_t GetSeekFrame() override;
void SeekError() override; void SeekError() override;
void SubmitTimestamp(double t) override;
DecoderCommand SubmitData(InputStream *is,
const void *data, size_t length,
uint16_t kbit_rate) override;
DecoderCommand SubmitTag(InputStream *is, Tag &&tag) override ;
void SubmitReplayGain(const ReplayGainInfo *replay_gain_info) override;
void SubmitMixRamp(MixRampInfo &&mix_ramp) override;
}; };
#endif #endif