DecoderAPI: pass SignedSongTime to decoder_initialized()

This commit is contained in:
Max Kellermann 2014-08-29 20:52:39 +02:00
parent 94f6380d69
commit d9d97bd17b
26 changed files with 159 additions and 132 deletions

View File

@ -41,7 +41,7 @@
void void
decoder_initialized(Decoder &decoder, decoder_initialized(Decoder &decoder,
const AudioFormat audio_format, const AudioFormat audio_format,
bool seekable, float total_time) bool seekable, SignedSongTime duration)
{ {
DecoderControl &dc = decoder.dc; DecoderControl &dc = decoder.dc;
struct audio_format_string af_string; struct audio_format_string af_string;
@ -59,9 +59,7 @@ decoder_initialized(Decoder &decoder,
dc.out_audio_format = getOutputAudioFormat(audio_format); dc.out_audio_format = getOutputAudioFormat(audio_format);
dc.seekable = seekable; dc.seekable = seekable;
dc.total_time = total_time > 0 dc.total_time = duration;
? SignedSongTime::FromS(total_time)
: SignedSongTime::Negative();
FormatDebug(decoder_domain, "audio_format=%s, seekable=%s", FormatDebug(decoder_domain, "audio_format=%s, seekable=%s",
audio_format_to_string(dc.in_audio_format, &af_string), audio_format_to_string(dc.in_audio_format, &af_string),

View File

@ -53,12 +53,13 @@ class Error;
* @param audio_format the audio format which is going to be sent to * @param audio_format the audio format which is going to be sent to
* decoder_data() * decoder_data()
* @param seekable true if the song is seekable * @param seekable true if the song is seekable
* @param total_time the total number of seconds in this song; -1 if unknown * @param duration the total duration of this song; negative if
* unknown
*/ */
void void
decoder_initialized(Decoder &decoder, decoder_initialized(Decoder &decoder,
AudioFormat audio_format, AudioFormat audio_format,
bool seekable, float total_time); bool seekable, SignedSongTime duration);
/** /**
* Determines the pending decoder command. * Determines the pending decoder command.

View File

@ -62,7 +62,7 @@ adplug_file_decode(Decoder &decoder, Path path_fs)
assert(audio_format.IsValid()); assert(audio_format.IsValid());
decoder_initialized(decoder, audio_format, false, decoder_initialized(decoder, audio_format, false,
player->songlength() / 1000.); SongTime::FromMS(player->songlength()));
int16_t buffer[2048]; int16_t buffer[2048];
const unsigned frames_per_buffer = ARRAY_SIZE(buffer) / 2; const unsigned frames_per_buffer = ARRAY_SIZE(buffer) / 2;

View File

@ -64,13 +64,11 @@ struct AudioFileInputStream {
}; };
gcc_pure gcc_pure
static double static SongTime
audiofile_get_duration(AFfilehandle fh) audiofile_get_duration(AFfilehandle fh)
{ {
double frame_count = afGetFrameCount(fh, AF_DEFAULT_TRACK); return SongTime::FromScale<uint64_t>(afGetFrameCount(fh, AF_DEFAULT_TRACK),
double rate = afGetRate(fh, AF_DEFAULT_TRACK); afGetRate(fh, AF_DEFAULT_TRACK));
return frame_count / rate;
} }
static ssize_t static ssize_t
@ -208,10 +206,10 @@ audiofile_stream_decode(Decoder &decoder, InputStream &is)
return; return;
} }
const double total_time = audiofile_get_duration(fh); const auto total_time = audiofile_get_duration(fh);
const uint16_t kbit_rate = (uint16_t) const uint16_t kbit_rate = (uint16_t)
(is.GetSize() * 8.0 / total_time / 1000.0 + 0.5); (is.GetSize() * uint64_t(8000) / total_time.ToMS());
const unsigned frame_size = (unsigned) const unsigned frame_size = (unsigned)
afGetVirtualFrameSize(fh, AF_DEFAULT_TRACK, true); afGetVirtualFrameSize(fh, AF_DEFAULT_TRACK, true);
@ -258,7 +256,7 @@ audiofile_get_duration(InputStream &is)
if (fh == AF_NULL_FILEHANDLE) if (fh == AF_NULL_FILEHANDLE)
return -1; return -1;
int duration = int(audiofile_get_duration(fh)); int duration = audiofile_get_duration(fh).RoundS();
afCloseFile(fh); afCloseFile(fh);
return duration; return duration;
} }

View File

@ -437,8 +437,10 @@ dsdiff_stream_decode(Decoder &decoder, InputStream &is)
/* calculate song time from DSD chunk size and sample frequency */ /* calculate song time from DSD chunk size and sample frequency */
offset_type chunk_size = metadata.chunk_size; offset_type chunk_size = metadata.chunk_size;
float songtime = ((chunk_size / metadata.channels) * 8) /
(float) metadata.sample_rate; uint64_t n_frames = chunk_size / audio_format.channels;
auto songtime = SongTime::FromScale<uint64_t>(n_frames,
audio_format.sample_rate);
/* success: file was recognized */ /* success: file was recognized */
decoder_initialized(decoder, audio_format, is.IsSeekable(), songtime); decoder_initialized(decoder, audio_format, is.IsSeekable(), songtime);

View File

@ -318,8 +318,8 @@ dsf_stream_decode(Decoder &decoder, InputStream &is)
} }
/* Calculate song time from DSD chunk size and sample frequency */ /* Calculate song time from DSD chunk size and sample frequency */
const auto n_blocks = metadata.n_blocks; const auto n_blocks = metadata.n_blocks;
float songtime = float(n_blocks * DSF_BLOCK_BITS) / auto songtime = SongTime::FromScale<uint64_t>(n_blocks * DSF_BLOCK_SIZE,
(float) metadata.sample_rate; audio_format.sample_rate);
/* success: file was recognized */ /* success: file was recognized */
decoder_initialized(decoder, audio_format, is.IsSeekable(), songtime); decoder_initialized(decoder, audio_format, is.IsSeekable(), songtime);

View File

@ -107,13 +107,13 @@ adts_find_frame(DecoderBuffer *buffer)
} }
} }
static float static SignedSongTime
adts_song_duration(DecoderBuffer *buffer) adts_song_duration(DecoderBuffer *buffer)
{ {
const InputStream &is = decoder_buffer_get_stream(buffer); const InputStream &is = decoder_buffer_get_stream(buffer);
const bool estimate = !is.CheapSeeking(); const bool estimate = !is.CheapSeeking();
if (estimate && !is.KnownSize()) if (estimate && !is.KnownSize())
return -1; return SignedSongTime::Negative();
unsigned sample_rate = 0; unsigned sample_rate = 0;
@ -146,7 +146,7 @@ adts_song_duration(DecoderBuffer *buffer)
const auto offset = is.GetOffset() const auto offset = is.GetOffset()
- decoder_buffer_available(buffer); - decoder_buffer_available(buffer);
if (offset <= 0) if (offset <= 0)
return -1; return SignedSongTime::Negative();
const auto file_size = is.GetSize(); const auto file_size = is.GetSize();
frames = (frames * file_size) / offset; frames = (frames * file_size) / offset;
@ -155,20 +155,18 @@ adts_song_duration(DecoderBuffer *buffer)
} }
if (sample_rate == 0) if (sample_rate == 0)
return -1; return SignedSongTime::Negative();
float frames_per_second = (float)sample_rate / 1024.0; return SignedSongTime::FromScale<uint64_t>(frames * uint64_t(1024),
assert(frames_per_second > 0); sample_rate);
return (float)frames / frames_per_second;
} }
static float static SignedSongTime
faad_song_duration(DecoderBuffer *buffer, InputStream &is) faad_song_duration(DecoderBuffer *buffer, InputStream &is)
{ {
auto data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_need(buffer, 5)); auto data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_need(buffer, 5));
if (data.IsNull()) if (data.IsNull())
return -1; return SignedSongTime::Negative();
size_t tagsize = 0; size_t tagsize = 0;
if (data.size >= 10 && !memcmp(data.data, "ID3", 3)) { if (data.size >= 10 && !memcmp(data.data, "ID3", 3)) {
@ -180,20 +178,20 @@ faad_song_duration(DecoderBuffer *buffer, InputStream &is)
tagsize += 10; tagsize += 10;
if (!decoder_buffer_skip(buffer, tagsize)) if (!decoder_buffer_skip(buffer, tagsize))
return -1; return SignedSongTime::Negative();
data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_need(buffer, 5)); data = ConstBuffer<uint8_t>::FromVoid(decoder_buffer_need(buffer, 5));
if (data.IsNull()) if (data.IsNull())
return -1; return SignedSongTime::Negative();
} }
if (data.size >= 8 && adts_check_frame(data.data) > 0) { if (data.size >= 8 && adts_check_frame(data.data) > 0) {
/* obtain the duration from the ADTS header */ /* obtain the duration from the ADTS header */
if (!is.IsSeekable()) if (!is.IsSeekable())
return -1; return SignedSongTime::Negative();
float song_length = adts_song_duration(buffer); auto song_length = adts_song_duration(buffer);
is.LockSeek(tagsize, IgnoreError()); is.LockSeek(tagsize, IgnoreError());
@ -204,14 +202,14 @@ faad_song_duration(DecoderBuffer *buffer, InputStream &is)
/* obtain the duration from the ADIF header */ /* obtain the duration from the ADIF header */
if (!is.KnownSize()) if (!is.KnownSize())
return -1; return SignedSongTime::Negative();
size_t skip_size = (data.data[4] & 0x80) ? 9 : 0; size_t skip_size = (data.data[4] & 0x80) ? 9 : 0;
if (8 + skip_size > data.size) if (8 + skip_size > data.size)
/* not enough data yet; skip parsing this /* not enough data yet; skip parsing this
header */ header */
return -1; return SignedSongTime::Negative();
unsigned bit_rate = ((data.data[4 + skip_size] & 0x0F) << 19) | unsigned bit_rate = ((data.data[4 + skip_size] & 0x0F) << 19) |
(data.data[5 + skip_size] << 11) | (data.data[5 + skip_size] << 11) |
@ -220,11 +218,11 @@ faad_song_duration(DecoderBuffer *buffer, InputStream &is)
const auto size = is.GetSize(); const auto size = is.GetSize();
if (bit_rate == 0) if (bit_rate == 0)
return -1; return SignedSongTime::Negative();
return size * 8.0 / bit_rate; return SongTime::FromScale(size, bit_rate / 8);
} else } else
return -1; return SignedSongTime::Negative();
} }
static NeAACDecHandle static NeAACDecHandle
@ -301,19 +299,21 @@ faad_decoder_decode(NeAACDecHandle decoder, DecoderBuffer *buffer,
} }
/** /**
* Get a song file's total playing time in seconds, as a float. * Determine a song file's total playing time.
* Returns 0 if the duration is unknown, and a negative value if the *
* file is invalid. * The first return value specifies whether the file was recognized.
* The second return value is the duration.
*/ */
static float static std::pair<bool, SignedSongTime>
faad_get_file_time_float(InputStream &is) faad_get_file_time(InputStream &is)
{ {
DecoderBuffer *buffer = DecoderBuffer *buffer =
decoder_buffer_new(nullptr, is, decoder_buffer_new(nullptr, is,
FAAD_MIN_STREAMSIZE * MAX_CHANNELS); FAAD_MIN_STREAMSIZE * MAX_CHANNELS);
float length = faad_song_duration(buffer, is); auto duration = faad_song_duration(buffer, is);
bool recognized = !duration.IsNegative();
if (length < 0) { if (!recognized) {
NeAACDecHandle decoder = faad_decoder_new(); NeAACDecHandle decoder = faad_decoder_new();
decoder_buffer_fill(buffer); decoder_buffer_fill(buffer);
@ -321,36 +321,21 @@ faad_get_file_time_float(InputStream &is)
AudioFormat audio_format; AudioFormat audio_format;
if (faad_decoder_init(decoder, buffer, audio_format, if (faad_decoder_init(decoder, buffer, audio_format,
IgnoreError())) IgnoreError()))
length = 0; recognized = true;
NeAACDecClose(decoder); NeAACDecClose(decoder);
} }
decoder_buffer_free(buffer); decoder_buffer_free(buffer);
return length; return std::make_pair(recognized, duration);
}
/**
* Get a song file's total playing time in seconds, as an int.
* Returns 0 if the duration is unknown, and a negative value if the
* file is invalid.
*/
static int
faad_get_file_time(InputStream &is)
{
float length = faad_get_file_time_float(is);
if (length < 0)
return -1;
return int(length + 0.5);
} }
static void static void
faad_stream_decode(Decoder &mpd_decoder, InputStream &is, faad_stream_decode(Decoder &mpd_decoder, InputStream &is,
DecoderBuffer *buffer, const NeAACDecHandle decoder) DecoderBuffer *buffer, const NeAACDecHandle decoder)
{ {
const float total_time = faad_song_duration(buffer, is); const auto total_time = faad_song_duration(buffer, is);
if (adts_find_frame(buffer) == 0) if (adts_find_frame(buffer) == 0)
return; return;
@ -449,11 +434,15 @@ static bool
faad_scan_stream(InputStream &is, faad_scan_stream(InputStream &is,
const struct tag_handler *handler, void *handler_ctx) const struct tag_handler *handler, void *handler_ctx)
{ {
int file_time = faad_get_file_time(is); auto result = faad_get_file_time(is);
if (file_time < 0) if (!result.first)
return false; return false;
tag_handler_invoke_duration(handler, handler_ctx, file_time); unsigned duration = result.second.IsNegative()
? 0
: result.second.RoundS();
tag_handler_invoke_duration(handler, handler_ctx, duration);
return true; return true;
} }

View File

@ -514,9 +514,11 @@ ffmpeg_decode(Decoder &decoder, InputStream &input)
return; return;
} }
int total_time = format_context->duration != (int64_t)AV_NOPTS_VALUE const SignedSongTime total_time =
? format_context->duration / AV_TIME_BASE format_context->duration != (int64_t)AV_NOPTS_VALUE
: 0; ? SignedSongTime::FromScale<uint64_t>(format_context->duration,
AV_TIME_BASE)
: SignedSongTime::Negative();
decoder_initialized(decoder, audio_format, decoder_initialized(decoder, audio_format,
input.IsSeekable(), total_time); input.IsSeekable(), total_time);

View File

@ -136,10 +136,12 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header)
data->frame_size = data->audio_format.GetFrameSize(); data->frame_size = data->audio_format.GetFrameSize();
const auto duration = SongTime::FromScale<uint64_t>(data->total_frames,
data->audio_format.sample_rate);
decoder_initialized(data->decoder, data->audio_format, decoder_initialized(data->decoder, data->audio_format,
data->input_stream.IsSeekable(), data->input_stream.IsSeekable(),
(float)data->total_frames / duration);
(float)data->audio_format.sample_rate);
data->initialized = true; data->initialized = true;

View File

@ -144,10 +144,14 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd,
if (data->initialized) { if (data->initialized) {
/* done */ /* done */
const auto duration2 =
SongTime::FromScale<uint64_t>(data->total_frames,
data->audio_format.sample_rate);
decoder_initialized(data->decoder, data->audio_format, decoder_initialized(data->decoder, data->audio_format,
data->input_stream.IsSeekable(), data->input_stream.IsSeekable(),
(float)data->total_frames / duration2);
(float)data->audio_format.sample_rate);
return true; return true;
} }

View File

@ -166,7 +166,8 @@ fluidsynth_file_decode(Decoder &decoder, Path path_fs)
MPD core */ MPD core */
const AudioFormat audio_format(sample_rate, SampleFormat::S16, 2); const AudioFormat audio_format(sample_rate, SampleFormat::S16, 2);
decoder_initialized(decoder, audio_format, false, -1); decoder_initialized(decoder, audio_format, false,
SignedSongTime::Negative());
DecoderCommand cmd; DecoderCommand cmd;
while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) { while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) {

View File

@ -156,9 +156,9 @@ gme_file_decode(Decoder &decoder, Path path_fs)
return; return;
} }
const float song_len = ti->length > 0 const SignedSongTime song_len = ti->length > 0
? ti->length / 1000.0 ? SignedSongTime::FromMS(ti->length)
: -1.0; : SignedSongTime::Negative();
/* initialize the MPD decoder */ /* initialize the MPD decoder */

View File

@ -122,7 +122,7 @@ struct MadDecoder {
mad_timer_t timer; mad_timer_t timer;
unsigned char input_buffer[READ_BUFFER_SIZE]; unsigned char input_buffer[READ_BUFFER_SIZE];
int32_t output_buffer[MP3_DATA_OUTPUT_BUFFER_SIZE]; int32_t output_buffer[MP3_DATA_OUTPUT_BUFFER_SIZE];
float total_time; SignedSongTime total_time;
SongTime elapsed_time; SongTime elapsed_time;
SongTime seek_time; SongTime seek_time;
enum muteframe mute_frame; enum muteframe mute_frame;
@ -713,11 +713,10 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
return true; return true;
} }
static inline float static inline SongTime
mp3_frame_duration(const struct mad_frame *frame) mp3_frame_duration(const struct mad_frame *frame)
{ {
return mad_timer_count(frame->header.duration, return ToSongTime(frame->header.duration);
MAD_UNITS_MILLISECONDS) / 1000.0;
} }
inline offset_type inline offset_type
@ -745,13 +744,19 @@ MadDecoder::FileSizeToSongLength()
if (input_stream.KnownSize()) { if (input_stream.KnownSize()) {
offset_type rest = RestIncludingThisFrame(); offset_type rest = RestIncludingThisFrame();
float frame_duration = mp3_frame_duration(&frame); const SongTime frame_duration = mp3_frame_duration(&frame);
const SongTime duration =
SongTime::FromScale<uint64_t>(rest,
frame.header.bitrate / 8);
total_time = duration;
total_time = (rest * 8.0) / frame.header.bitrate; max_frames = (frame_duration.IsPositive()
max_frames = total_time / frame_duration + FRAMES_CUSHION; ? duration.count() / frame_duration.count()
: 0)
+ FRAMES_CUSHION;
} else { } else {
max_frames = FRAMES_CUSHION; max_frames = FRAMES_CUSHION;
total_time = 0; total_time = SignedSongTime::Negative();
} }
} }
@ -792,7 +797,7 @@ MadDecoder::DecodeFirstFrame(Tag **tag)
if ((xing.flags & XING_FRAMES) && xing.frames) { if ((xing.flags & XING_FRAMES) && xing.frames) {
mad_timer_t duration = frame.header.duration; mad_timer_t duration = frame.header.duration;
mad_timer_multiply(&duration, xing.frames); mad_timer_multiply(&duration, xing.frames);
total_time = ((float)mad_timer_count(duration, MAD_UNITS_MILLISECONDS)) / 1000; total_time = ToSongTime(duration);
max_frames = xing.frames; max_frames = xing.frames;
} }
@ -844,13 +849,13 @@ MadDecoder::~MadDecoder()
} }
/* this is primarily used for getting total time for tags */ /* this is primarily used for getting total time for tags */
static int static std::pair<bool, SignedSongTime>
mad_decoder_total_file_time(InputStream &is) mad_decoder_total_file_time(InputStream &is)
{ {
MadDecoder data(nullptr, is); MadDecoder data(nullptr, is);
return data.DecodeFirstFrame(nullptr) return data.DecodeFirstFrame(nullptr)
? data.total_time + 0.5 ? std::make_pair(true, data.total_time)
: -1; : std::make_pair(false, SignedSongTime::Negative());
} }
long long
@ -1085,11 +1090,15 @@ static bool
mad_decoder_scan_stream(InputStream &is, mad_decoder_scan_stream(InputStream &is,
const struct tag_handler *handler, void *handler_ctx) const struct tag_handler *handler, void *handler_ctx)
{ {
const int total_time = mad_decoder_total_file_time(is); const auto result = mad_decoder_total_file_time(is);
if (total_time < 0) if (!result.first)
return false; return false;
tag_handler_invoke_duration(handler, handler_ctx, total_time); unsigned duration = result.second.IsNegative()
? 0
: result.second.RoundS();
tag_handler_invoke_duration(handler, handler_ctx, duration);
return true; return true;
} }

View File

@ -170,7 +170,8 @@ mikmod_decoder_file_decode(Decoder &decoder, Path path_fs)
const AudioFormat audio_format(mikmod_sample_rate, SampleFormat::S16, 2); const AudioFormat audio_format(mikmod_sample_rate, SampleFormat::S16, 2);
assert(audio_format.IsValid()); assert(audio_format.IsValid());
decoder_initialized(decoder, audio_format, false, 0); decoder_initialized(decoder, audio_format, false,
SignedSongTime::Negative());
Player_Start(handle); Player_Start(handle);

View File

@ -153,7 +153,7 @@ mod_decode(Decoder &decoder, InputStream &is)
decoder_initialized(decoder, audio_format, decoder_initialized(decoder, audio_format,
is.IsSeekable(), is.IsSeekable(),
ModPlug_GetLength(f) / 1000.0); SongTime::FromMS(ModPlug_GetLength(f)));
DecoderCommand cmd; DecoderCommand cmd;
do { do {

View File

@ -149,7 +149,8 @@ mp4_file_decode(Decoder &mpd_decoder, Path path_fs)
/* initialize the MPD core */ /* initialize the MPD core */
const MP4Timestamp scale = MP4GetTrackTimeScale(handle, track); const MP4Timestamp scale = MP4GetTrackTimeScale(handle, track);
const float duration = ((float)MP4GetTrackDuration(handle, track)) / scale + 0.5f; const SongTime duration = SongTime::FromScale<uint64_t>(MP4GetTrackDuration(handle, track),
scale);
const MP4SampleId num_samples = MP4GetTrackNumberOfSamples(handle, track); const MP4SampleId num_samples = MP4GetTrackNumberOfSamples(handle, track);
decoder_initialized(mpd_decoder, audio_format, true, duration); decoder_initialized(mpd_decoder, audio_format, true, duration);

View File

@ -180,7 +180,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is)
decoder_initialized(mpd_decoder, audio_format, decoder_initialized(mpd_decoder, audio_format,
is.IsSeekable(), is.IsSeekable(),
mpc_streaminfo_get_length(&info)); SongTime::FromS(mpc_streaminfo_get_length(&info)));
DecoderCommand cmd = DecoderCommand::NONE; DecoderCommand cmd = DecoderCommand::NONE;
do { do {

View File

@ -131,9 +131,11 @@ mpd_mpg123_file_decode(Decoder &decoder, Path path_fs)
/* tell MPD core we're ready */ /* tell MPD core we're ready */
decoder_initialized(decoder, audio_format, true, const auto duration =
(float)num_samples / SongTime::FromScale<uint64_t>(num_samples,
(float)audio_format.sample_rate); audio_format.sample_rate);
decoder_initialized(decoder, audio_format, true, duration);
if (mpg123_info(handle, &info) != MPG123_OK) { if (mpg123_info(handle, &info) != MPG123_OK) {
info.vbr = MPG123_CBR; info.vbr = MPG123_CBR;

View File

@ -253,9 +253,10 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet)
eos_granulepos = LoadEOSGranulePos(input_stream, &decoder, eos_granulepos = LoadEOSGranulePos(input_stream, &decoder,
opus_serialno); opus_serialno);
const double duration = eos_granulepos >= 0 const auto duration = eos_granulepos >= 0
? double(eos_granulepos) / opus_sample_rate ? SignedSongTime::FromScale<uint64_t>(eos_granulepos,
: -1.0; opus_sample_rate)
: SignedSongTime::Negative();
const AudioFormat audio_format(opus_sample_rate, const AudioFormat audio_format(opus_sample_rate,
SampleFormat::S16, channels); SampleFormat::S16, channels);

View File

@ -40,12 +40,12 @@ pcm_stream_decode(Decoder &decoder, InputStream &is)
const bool reverse_endian = mime != nullptr && const bool reverse_endian = mime != nullptr &&
strcmp(mime, "audio/x-mpd-cdda-pcm-reverse") == 0; strcmp(mime, "audio/x-mpd-cdda-pcm-reverse") == 0;
const double time_to_size = audio_format.GetTimeToSize();
const auto frame_size = audio_format.GetFrameSize(); const auto frame_size = audio_format.GetFrameSize();
float total_time = -1; const auto total_time = is.KnownSize()
if (is.KnownSize()) ? SignedSongTime::FromScale<uint64_t>(is.GetSize() / frame_size,
total_time = is.GetSize() / time_to_size; audio_format.sample_rate)
: SignedSongTime::Negative();
decoder_initialized(decoder, audio_format, decoder_initialized(decoder, audio_format,
is.IsSeekable(), total_time); is.IsSeekable(), total_time);

View File

@ -159,11 +159,11 @@ get_song_num(const char *path_fs)
} }
/* get the song length in seconds */ /* get the song length in seconds */
static int static SignedSongTime
get_song_length(Path path_fs) get_song_length(Path path_fs)
{ {
if (songlength_database == nullptr) if (songlength_database == nullptr)
return -1; return SignedSongTime::Negative();
char *sid_file = get_container_name(path_fs); char *sid_file = get_container_name(path_fs);
SidTuneMod tune(sid_file); SidTuneMod tune(sid_file);
@ -171,7 +171,7 @@ get_song_length(Path path_fs)
if(!tune) { if(!tune) {
LogWarning(sidplay_domain, LogWarning(sidplay_domain,
"failed to load file for calculating md5 sum"); "failed to load file for calculating md5 sum");
return -1; return SignedSongTime::Negative();
} }
char md5sum[SIDTUNE_MD5_LENGTH+1]; char md5sum[SIDTUNE_MD5_LENGTH+1];
tune.createMD5(md5sum); tune.createMD5(md5sum);
@ -183,7 +183,7 @@ get_song_length(Path path_fs)
"Database", md5sum, &num_items, nullptr); "Database", md5sum, &num_items, nullptr);
if(!values || song_num>num_items) { if(!values || song_num>num_items) {
g_strfreev(values); g_strfreev(values);
return -1; return SignedSongTime::Negative();
} }
int minutes=strtol(values[song_num-1], nullptr, 10); int minutes=strtol(values[song_num-1], nullptr, 10);
@ -199,7 +199,7 @@ get_song_length(Path path_fs)
g_strfreev(values); g_strfreev(values);
return (minutes*60)+seconds; return SignedSongTime::FromS((minutes * 60) + seconds);
} }
static void static void
@ -220,8 +220,9 @@ sidplay_file_decode(Decoder &decoder, Path path_fs)
const int song_num = get_song_num(path_fs.c_str()); const int song_num = get_song_num(path_fs.c_str());
tune.selectSong(song_num); tune.selectSong(song_num);
int song_len=get_song_length(path_fs); auto duration = get_song_length(path_fs);
if(song_len==-1) song_len=default_songlength; if (duration.IsNegative() && default_songlength > 0)
duration = SongTime::FromS(default_songlength);
/* initialize the player */ /* initialize the player */
@ -292,12 +293,14 @@ sidplay_file_decode(Decoder &decoder, Path path_fs)
const AudioFormat audio_format(48000, SampleFormat::S16, channels); const AudioFormat audio_format(48000, SampleFormat::S16, channels);
assert(audio_format.IsValid()); assert(audio_format.IsValid());
decoder_initialized(decoder, audio_format, true, (float)song_len); decoder_initialized(decoder, audio_format, true, duration);
/* .. and play */ /* .. and play */
const unsigned timebase = player.timebase(); const unsigned timebase = player.timebase();
song_len *= timebase; const unsigned end = duration.IsNegative()
? 0u
: duration.ToScale<uint64_t>(timebase);
DecoderCommand cmd; DecoderCommand cmd;
do { do {
@ -334,7 +337,7 @@ sidplay_file_decode(Decoder &decoder, Path path_fs)
decoder_command_finished(decoder); decoder_command_finished(decoder);
} }
if (song_len > 0 && player.time() >= (unsigned)song_len) if (end > 0 && player.time() >= end)
break; break;
} while (cmd != DecoderCommand::STOP); } while (cmd != DecoderCommand::STOP);
@ -382,9 +385,10 @@ sidplay_scan_file(Path path_fs,
tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track); tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track);
/* time */ /* time */
int song_len=get_song_length(path_fs); const auto duration = get_song_length(path_fs);
if (song_len >= 0) if (!duration.IsNegative())
tag_handler_invoke_duration(handler, handler_ctx, song_len); tag_handler_invoke_duration(handler, handler_ctx,
duration.RoundS());
return true; return true;
} }

View File

@ -140,10 +140,11 @@ static SF_VIRTUAL_IO vio = {
/** /**
* Converts a frame number to a timestamp (in seconds). * Converts a frame number to a timestamp (in seconds).
*/ */
static float static SongTime
frame_to_time(sf_count_t frame, const AudioFormat *audio_format) frame_to_time(sf_count_t frame, const AudioFormat *audio_format)
{ {
return (float)frame / (float)audio_format->sample_rate; return SongTime::FromScale<uint64_t>(frame,
audio_format->sample_rate);
} }
static void static void

View File

@ -202,6 +202,16 @@ vorbis_init(gcc_unused const config_param &param)
return true; return true;
} }
gcc_pure
static SignedSongTime
vorbis_duration(OggVorbis_File &vf)
{
auto total = ov_time_total(&vf, -1);
return total >= 0
? SignedSongTime::FromS(total)
: SignedSongTime::Negative();
}
static void static void
vorbis_stream_decode(Decoder &decoder, vorbis_stream_decode(Decoder &decoder,
InputStream &input_stream) InputStream &input_stream)
@ -237,11 +247,8 @@ vorbis_stream_decode(Decoder &decoder,
return; return;
} }
float total_time = ov_time_total(&vf, -1); decoder_initialized(decoder, audio_format, vis.seekable,
if (total_time < 0) vorbis_duration(vf));
total_time = 0;
decoder_initialized(decoder, audio_format, vis.seekable, total_time);
#ifdef HAVE_TREMOR #ifdef HAVE_TREMOR
char buffer[4096]; char buffer[4096];

View File

@ -160,8 +160,9 @@ wavpack_decode(Decoder &decoder, WavpackContext *wpc, bool can_seek)
? format_samples_float ? format_samples_float
: format_samples_int; : format_samples_int;
const float total_time = float(WavpackGetNumSamples(wpc)) const auto total_time =
/ audio_format.sample_rate; SongTime::FromScale<uint64_t>(WavpackGetNumSamples(wpc),
audio_format.sample_rate);
const int bytes_per_sample = WavpackGetBytesPerSample(wpc); const int bytes_per_sample = WavpackGetBytesPerSample(wpc);
const int output_sample_size = audio_format.GetFrameSize(); const int output_sample_size = audio_format.GetFrameSize();

View File

@ -86,8 +86,11 @@ wildmidi_file_decode(Decoder &decoder, Path path_fs)
return; return;
} }
decoder_initialized(decoder, audio_format, true, const auto duration =
info->approx_total_samples / WILDMIDI_SAMPLE_RATE); SongTime::FromScale<uint64_t>(info->approx_total_samples,
WILDMIDI_SAMPLE_RATE);
decoder_initialized(decoder, audio_format, true, duration);
DecoderCommand cmd; DecoderCommand cmd;
do { do {

View File

@ -30,7 +30,7 @@ void
decoder_initialized(Decoder &decoder, decoder_initialized(Decoder &decoder,
const AudioFormat audio_format, const AudioFormat audio_format,
gcc_unused bool seekable, gcc_unused bool seekable,
float duration) SignedSongTime duration)
{ {
struct audio_format_string af_string; struct audio_format_string af_string;
@ -39,7 +39,7 @@ decoder_initialized(Decoder &decoder,
fprintf(stderr, "audio_format=%s duration=%f\n", fprintf(stderr, "audio_format=%s duration=%f\n",
audio_format_to_string(audio_format, &af_string), audio_format_to_string(audio_format, &af_string),
duration); duration.ToDoubleS());
decoder.initialized = true; decoder.initialized = true;
} }