diff --git a/src/decoder/DecoderAPI.cxx b/src/decoder/DecoderAPI.cxx index 354fed9a3..3d45cb325 100644 --- a/src/decoder/DecoderAPI.cxx +++ b/src/decoder/DecoderAPI.cxx @@ -41,7 +41,7 @@ void decoder_initialized(Decoder &decoder, const AudioFormat audio_format, - bool seekable, float total_time) + bool seekable, SignedSongTime duration) { DecoderControl &dc = decoder.dc; struct audio_format_string af_string; @@ -59,9 +59,7 @@ decoder_initialized(Decoder &decoder, dc.out_audio_format = getOutputAudioFormat(audio_format); dc.seekable = seekable; - dc.total_time = total_time > 0 - ? SignedSongTime::FromS(total_time) - : SignedSongTime::Negative(); + dc.total_time = duration; FormatDebug(decoder_domain, "audio_format=%s, seekable=%s", audio_format_to_string(dc.in_audio_format, &af_string), diff --git a/src/decoder/DecoderAPI.hxx b/src/decoder/DecoderAPI.hxx index ca33e26c7..b756331d9 100644 --- a/src/decoder/DecoderAPI.hxx +++ b/src/decoder/DecoderAPI.hxx @@ -53,12 +53,13 @@ class Error; * @param audio_format the audio format which is going to be sent to * decoder_data() * @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 decoder_initialized(Decoder &decoder, AudioFormat audio_format, - bool seekable, float total_time); + bool seekable, SignedSongTime duration); /** * Determines the pending decoder command. diff --git a/src/decoder/plugins/AdPlugDecoderPlugin.cxx b/src/decoder/plugins/AdPlugDecoderPlugin.cxx index 32a2432f4..6c8edfe02 100644 --- a/src/decoder/plugins/AdPlugDecoderPlugin.cxx +++ b/src/decoder/plugins/AdPlugDecoderPlugin.cxx @@ -62,7 +62,7 @@ adplug_file_decode(Decoder &decoder, Path path_fs) assert(audio_format.IsValid()); decoder_initialized(decoder, audio_format, false, - player->songlength() / 1000.); + SongTime::FromMS(player->songlength())); int16_t buffer[2048]; const unsigned frames_per_buffer = ARRAY_SIZE(buffer) / 2; diff --git a/src/decoder/plugins/AudiofileDecoderPlugin.cxx b/src/decoder/plugins/AudiofileDecoderPlugin.cxx index 412cda1e3..8d04f9596 100644 --- a/src/decoder/plugins/AudiofileDecoderPlugin.cxx +++ b/src/decoder/plugins/AudiofileDecoderPlugin.cxx @@ -64,13 +64,11 @@ struct AudioFileInputStream { }; gcc_pure -static double +static SongTime audiofile_get_duration(AFfilehandle fh) { - double frame_count = afGetFrameCount(fh, AF_DEFAULT_TRACK); - double rate = afGetRate(fh, AF_DEFAULT_TRACK); - - return frame_count / rate; + return SongTime::FromScale(afGetFrameCount(fh, AF_DEFAULT_TRACK), + afGetRate(fh, AF_DEFAULT_TRACK)); } static ssize_t @@ -208,10 +206,10 @@ audiofile_stream_decode(Decoder &decoder, InputStream &is) return; } - const double total_time = audiofile_get_duration(fh); + const auto total_time = audiofile_get_duration(fh); 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) afGetVirtualFrameSize(fh, AF_DEFAULT_TRACK, true); @@ -258,7 +256,7 @@ audiofile_get_duration(InputStream &is) if (fh == AF_NULL_FILEHANDLE) return -1; - int duration = int(audiofile_get_duration(fh)); + int duration = audiofile_get_duration(fh).RoundS(); afCloseFile(fh); return duration; } diff --git a/src/decoder/plugins/DsdiffDecoderPlugin.cxx b/src/decoder/plugins/DsdiffDecoderPlugin.cxx index 112de8b90..617d34ae4 100644 --- a/src/decoder/plugins/DsdiffDecoderPlugin.cxx +++ b/src/decoder/plugins/DsdiffDecoderPlugin.cxx @@ -437,8 +437,10 @@ dsdiff_stream_decode(Decoder &decoder, InputStream &is) /* calculate song time from DSD chunk size and sample frequency */ 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(n_frames, + audio_format.sample_rate); /* success: file was recognized */ decoder_initialized(decoder, audio_format, is.IsSeekable(), songtime); diff --git a/src/decoder/plugins/DsfDecoderPlugin.cxx b/src/decoder/plugins/DsfDecoderPlugin.cxx index c92c276fc..b84eadf66 100644 --- a/src/decoder/plugins/DsfDecoderPlugin.cxx +++ b/src/decoder/plugins/DsfDecoderPlugin.cxx @@ -318,8 +318,8 @@ dsf_stream_decode(Decoder &decoder, InputStream &is) } /* Calculate song time from DSD chunk size and sample frequency */ const auto n_blocks = metadata.n_blocks; - float songtime = float(n_blocks * DSF_BLOCK_BITS) / - (float) metadata.sample_rate; + auto songtime = SongTime::FromScale(n_blocks * DSF_BLOCK_SIZE, + audio_format.sample_rate); /* success: file was recognized */ decoder_initialized(decoder, audio_format, is.IsSeekable(), songtime); diff --git a/src/decoder/plugins/FaadDecoderPlugin.cxx b/src/decoder/plugins/FaadDecoderPlugin.cxx index 83fab7f9e..5b352c9f4 100644 --- a/src/decoder/plugins/FaadDecoderPlugin.cxx +++ b/src/decoder/plugins/FaadDecoderPlugin.cxx @@ -107,13 +107,13 @@ adts_find_frame(DecoderBuffer *buffer) } } -static float +static SignedSongTime adts_song_duration(DecoderBuffer *buffer) { const InputStream &is = decoder_buffer_get_stream(buffer); const bool estimate = !is.CheapSeeking(); if (estimate && !is.KnownSize()) - return -1; + return SignedSongTime::Negative(); unsigned sample_rate = 0; @@ -146,7 +146,7 @@ adts_song_duration(DecoderBuffer *buffer) const auto offset = is.GetOffset() - decoder_buffer_available(buffer); if (offset <= 0) - return -1; + return SignedSongTime::Negative(); const auto file_size = is.GetSize(); frames = (frames * file_size) / offset; @@ -155,20 +155,18 @@ adts_song_duration(DecoderBuffer *buffer) } if (sample_rate == 0) - return -1; + return SignedSongTime::Negative(); - float frames_per_second = (float)sample_rate / 1024.0; - assert(frames_per_second > 0); - - return (float)frames / frames_per_second; + return SignedSongTime::FromScale(frames * uint64_t(1024), + sample_rate); } -static float +static SignedSongTime faad_song_duration(DecoderBuffer *buffer, InputStream &is) { auto data = ConstBuffer::FromVoid(decoder_buffer_need(buffer, 5)); if (data.IsNull()) - return -1; + return SignedSongTime::Negative(); size_t tagsize = 0; if (data.size >= 10 && !memcmp(data.data, "ID3", 3)) { @@ -180,20 +178,20 @@ faad_song_duration(DecoderBuffer *buffer, InputStream &is) tagsize += 10; if (!decoder_buffer_skip(buffer, tagsize)) - return -1; + return SignedSongTime::Negative(); data = ConstBuffer::FromVoid(decoder_buffer_need(buffer, 5)); if (data.IsNull()) - return -1; + return SignedSongTime::Negative(); } if (data.size >= 8 && adts_check_frame(data.data) > 0) { /* obtain the duration from the ADTS header */ 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()); @@ -204,14 +202,14 @@ faad_song_duration(DecoderBuffer *buffer, InputStream &is) /* obtain the duration from the ADIF header */ if (!is.KnownSize()) - return -1; + return SignedSongTime::Negative(); size_t skip_size = (data.data[4] & 0x80) ? 9 : 0; if (8 + skip_size > data.size) /* not enough data yet; skip parsing this header */ - return -1; + return SignedSongTime::Negative(); unsigned bit_rate = ((data.data[4 + skip_size] & 0x0F) << 19) | (data.data[5 + skip_size] << 11) | @@ -220,11 +218,11 @@ faad_song_duration(DecoderBuffer *buffer, InputStream &is) const auto size = is.GetSize(); if (bit_rate == 0) - return -1; + return SignedSongTime::Negative(); - return size * 8.0 / bit_rate; + return SongTime::FromScale(size, bit_rate / 8); } else - return -1; + return SignedSongTime::Negative(); } 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. - * Returns 0 if the duration is unknown, and a negative value if the - * file is invalid. + * Determine a song file's total playing time. + * + * The first return value specifies whether the file was recognized. + * The second return value is the duration. */ -static float -faad_get_file_time_float(InputStream &is) +static std::pair +faad_get_file_time(InputStream &is) { DecoderBuffer *buffer = decoder_buffer_new(nullptr, is, 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(); decoder_buffer_fill(buffer); @@ -321,36 +321,21 @@ faad_get_file_time_float(InputStream &is) AudioFormat audio_format; if (faad_decoder_init(decoder, buffer, audio_format, IgnoreError())) - length = 0; + recognized = true; NeAACDecClose(decoder); } decoder_buffer_free(buffer); - return length; -} - -/** - * 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); + return std::make_pair(recognized, duration); } static void faad_stream_decode(Decoder &mpd_decoder, InputStream &is, 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) return; @@ -449,11 +434,15 @@ static bool faad_scan_stream(InputStream &is, const struct tag_handler *handler, void *handler_ctx) { - int file_time = faad_get_file_time(is); - if (file_time < 0) + auto result = faad_get_file_time(is); + if (!result.first) 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; } diff --git a/src/decoder/plugins/FfmpegDecoderPlugin.cxx b/src/decoder/plugins/FfmpegDecoderPlugin.cxx index e328da24b..0693ffd43 100644 --- a/src/decoder/plugins/FfmpegDecoderPlugin.cxx +++ b/src/decoder/plugins/FfmpegDecoderPlugin.cxx @@ -514,9 +514,11 @@ ffmpeg_decode(Decoder &decoder, InputStream &input) return; } - int total_time = format_context->duration != (int64_t)AV_NOPTS_VALUE - ? format_context->duration / AV_TIME_BASE - : 0; + const SignedSongTime total_time = + format_context->duration != (int64_t)AV_NOPTS_VALUE + ? SignedSongTime::FromScale(format_context->duration, + AV_TIME_BASE) + : SignedSongTime::Negative(); decoder_initialized(decoder, audio_format, input.IsSeekable(), total_time); diff --git a/src/decoder/plugins/FlacCommon.cxx b/src/decoder/plugins/FlacCommon.cxx index f06a52cf8..76762bdf9 100644 --- a/src/decoder/plugins/FlacCommon.cxx +++ b/src/decoder/plugins/FlacCommon.cxx @@ -136,10 +136,12 @@ flac_got_first_frame(struct flac_data *data, const FLAC__FrameHeader *header) data->frame_size = data->audio_format.GetFrameSize(); + const auto duration = SongTime::FromScale(data->total_frames, + data->audio_format.sample_rate); + decoder_initialized(data->decoder, data->audio_format, data->input_stream.IsSeekable(), - (float)data->total_frames / - (float)data->audio_format.sample_rate); + duration); data->initialized = true; diff --git a/src/decoder/plugins/FlacDecoderPlugin.cxx b/src/decoder/plugins/FlacDecoderPlugin.cxx index 60772398c..eea813401 100644 --- a/src/decoder/plugins/FlacDecoderPlugin.cxx +++ b/src/decoder/plugins/FlacDecoderPlugin.cxx @@ -144,10 +144,14 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd, if (data->initialized) { /* done */ + + const auto duration2 = + SongTime::FromScale(data->total_frames, + data->audio_format.sample_rate); + decoder_initialized(data->decoder, data->audio_format, data->input_stream.IsSeekable(), - (float)data->total_frames / - (float)data->audio_format.sample_rate); + duration2); return true; } diff --git a/src/decoder/plugins/FluidsynthDecoderPlugin.cxx b/src/decoder/plugins/FluidsynthDecoderPlugin.cxx index bdf30baea..f19ac5bf4 100644 --- a/src/decoder/plugins/FluidsynthDecoderPlugin.cxx +++ b/src/decoder/plugins/FluidsynthDecoderPlugin.cxx @@ -166,7 +166,8 @@ fluidsynth_file_decode(Decoder &decoder, Path path_fs) MPD core */ 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; while (fluid_player_get_status(player) == FLUID_PLAYER_PLAYING) { diff --git a/src/decoder/plugins/GmeDecoderPlugin.cxx b/src/decoder/plugins/GmeDecoderPlugin.cxx index 5588e2a1e..e1b281f93 100644 --- a/src/decoder/plugins/GmeDecoderPlugin.cxx +++ b/src/decoder/plugins/GmeDecoderPlugin.cxx @@ -156,9 +156,9 @@ gme_file_decode(Decoder &decoder, Path path_fs) return; } - const float song_len = ti->length > 0 - ? ti->length / 1000.0 - : -1.0; + const SignedSongTime song_len = ti->length > 0 + ? SignedSongTime::FromMS(ti->length) + : SignedSongTime::Negative(); /* initialize the MPD decoder */ diff --git a/src/decoder/plugins/MadDecoderPlugin.cxx b/src/decoder/plugins/MadDecoderPlugin.cxx index 7509faabb..3175d4bf6 100644 --- a/src/decoder/plugins/MadDecoderPlugin.cxx +++ b/src/decoder/plugins/MadDecoderPlugin.cxx @@ -122,7 +122,7 @@ struct MadDecoder { mad_timer_t timer; unsigned char input_buffer[READ_BUFFER_SIZE]; int32_t output_buffer[MP3_DATA_OUTPUT_BUFFER_SIZE]; - float total_time; + SignedSongTime total_time; SongTime elapsed_time; SongTime seek_time; enum muteframe mute_frame; @@ -713,11 +713,10 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen) return true; } -static inline float +static inline SongTime mp3_frame_duration(const struct mad_frame *frame) { - return mad_timer_count(frame->header.duration, - MAD_UNITS_MILLISECONDS) / 1000.0; + return ToSongTime(frame->header.duration); } inline offset_type @@ -745,13 +744,19 @@ MadDecoder::FileSizeToSongLength() if (input_stream.KnownSize()) { offset_type rest = RestIncludingThisFrame(); - float frame_duration = mp3_frame_duration(&frame); + const SongTime frame_duration = mp3_frame_duration(&frame); + const SongTime duration = + SongTime::FromScale(rest, + frame.header.bitrate / 8); + total_time = duration; - total_time = (rest * 8.0) / frame.header.bitrate; - max_frames = total_time / frame_duration + FRAMES_CUSHION; + max_frames = (frame_duration.IsPositive() + ? duration.count() / frame_duration.count() + : 0) + + FRAMES_CUSHION; } else { 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) { mad_timer_t duration = frame.header.duration; 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; } @@ -844,13 +849,13 @@ MadDecoder::~MadDecoder() } /* this is primarily used for getting total time for tags */ -static int +static std::pair mad_decoder_total_file_time(InputStream &is) { MadDecoder data(nullptr, is); return data.DecodeFirstFrame(nullptr) - ? data.total_time + 0.5 - : -1; + ? std::make_pair(true, data.total_time) + : std::make_pair(false, SignedSongTime::Negative()); } long @@ -1085,11 +1090,15 @@ static bool mad_decoder_scan_stream(InputStream &is, const struct tag_handler *handler, void *handler_ctx) { - const int total_time = mad_decoder_total_file_time(is); - if (total_time < 0) + const auto result = mad_decoder_total_file_time(is); + if (!result.first) 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; } diff --git a/src/decoder/plugins/MikmodDecoderPlugin.cxx b/src/decoder/plugins/MikmodDecoderPlugin.cxx index a1938617d..85633f1fc 100644 --- a/src/decoder/plugins/MikmodDecoderPlugin.cxx +++ b/src/decoder/plugins/MikmodDecoderPlugin.cxx @@ -170,7 +170,8 @@ mikmod_decoder_file_decode(Decoder &decoder, Path path_fs) const AudioFormat audio_format(mikmod_sample_rate, SampleFormat::S16, 2); assert(audio_format.IsValid()); - decoder_initialized(decoder, audio_format, false, 0); + decoder_initialized(decoder, audio_format, false, + SignedSongTime::Negative()); Player_Start(handle); diff --git a/src/decoder/plugins/ModplugDecoderPlugin.cxx b/src/decoder/plugins/ModplugDecoderPlugin.cxx index c88249fa5..279790b93 100644 --- a/src/decoder/plugins/ModplugDecoderPlugin.cxx +++ b/src/decoder/plugins/ModplugDecoderPlugin.cxx @@ -153,7 +153,7 @@ mod_decode(Decoder &decoder, InputStream &is) decoder_initialized(decoder, audio_format, is.IsSeekable(), - ModPlug_GetLength(f) / 1000.0); + SongTime::FromMS(ModPlug_GetLength(f))); DecoderCommand cmd; do { diff --git a/src/decoder/plugins/Mp4v2DecoderPlugin.cxx b/src/decoder/plugins/Mp4v2DecoderPlugin.cxx index ef5284437..d59be7f09 100644 --- a/src/decoder/plugins/Mp4v2DecoderPlugin.cxx +++ b/src/decoder/plugins/Mp4v2DecoderPlugin.cxx @@ -149,7 +149,8 @@ mp4_file_decode(Decoder &mpd_decoder, Path path_fs) /* initialize the MPD core */ const MP4Timestamp scale = MP4GetTrackTimeScale(handle, track); - const float duration = ((float)MP4GetTrackDuration(handle, track)) / scale + 0.5f; + const SongTime duration = SongTime::FromScale(MP4GetTrackDuration(handle, track), + scale); const MP4SampleId num_samples = MP4GetTrackNumberOfSamples(handle, track); decoder_initialized(mpd_decoder, audio_format, true, duration); diff --git a/src/decoder/plugins/MpcdecDecoderPlugin.cxx b/src/decoder/plugins/MpcdecDecoderPlugin.cxx index 3ad4ff40d..08e060bee 100644 --- a/src/decoder/plugins/MpcdecDecoderPlugin.cxx +++ b/src/decoder/plugins/MpcdecDecoderPlugin.cxx @@ -180,7 +180,7 @@ mpcdec_decode(Decoder &mpd_decoder, InputStream &is) decoder_initialized(mpd_decoder, audio_format, is.IsSeekable(), - mpc_streaminfo_get_length(&info)); + SongTime::FromS(mpc_streaminfo_get_length(&info))); DecoderCommand cmd = DecoderCommand::NONE; do { diff --git a/src/decoder/plugins/Mpg123DecoderPlugin.cxx b/src/decoder/plugins/Mpg123DecoderPlugin.cxx index 8779a6568..798cc953d 100644 --- a/src/decoder/plugins/Mpg123DecoderPlugin.cxx +++ b/src/decoder/plugins/Mpg123DecoderPlugin.cxx @@ -131,9 +131,11 @@ mpd_mpg123_file_decode(Decoder &decoder, Path path_fs) /* tell MPD core we're ready */ - decoder_initialized(decoder, audio_format, true, - (float)num_samples / - (float)audio_format.sample_rate); + const auto duration = + SongTime::FromScale(num_samples, + audio_format.sample_rate); + + decoder_initialized(decoder, audio_format, true, duration); if (mpg123_info(handle, &info) != MPG123_OK) { info.vbr = MPG123_CBR; diff --git a/src/decoder/plugins/OpusDecoderPlugin.cxx b/src/decoder/plugins/OpusDecoderPlugin.cxx index 18ccf8183..2de827c7f 100644 --- a/src/decoder/plugins/OpusDecoderPlugin.cxx +++ b/src/decoder/plugins/OpusDecoderPlugin.cxx @@ -253,9 +253,10 @@ MPDOpusDecoder::HandleBOS(const ogg_packet &packet) eos_granulepos = LoadEOSGranulePos(input_stream, &decoder, opus_serialno); - const double duration = eos_granulepos >= 0 - ? double(eos_granulepos) / opus_sample_rate - : -1.0; + const auto duration = eos_granulepos >= 0 + ? SignedSongTime::FromScale(eos_granulepos, + opus_sample_rate) + : SignedSongTime::Negative(); const AudioFormat audio_format(opus_sample_rate, SampleFormat::S16, channels); diff --git a/src/decoder/plugins/PcmDecoderPlugin.cxx b/src/decoder/plugins/PcmDecoderPlugin.cxx index 0f41099f2..c07a7b9b1 100644 --- a/src/decoder/plugins/PcmDecoderPlugin.cxx +++ b/src/decoder/plugins/PcmDecoderPlugin.cxx @@ -40,12 +40,12 @@ pcm_stream_decode(Decoder &decoder, InputStream &is) const bool reverse_endian = mime != nullptr && strcmp(mime, "audio/x-mpd-cdda-pcm-reverse") == 0; - const double time_to_size = audio_format.GetTimeToSize(); const auto frame_size = audio_format.GetFrameSize(); - float total_time = -1; - if (is.KnownSize()) - total_time = is.GetSize() / time_to_size; + const auto total_time = is.KnownSize() + ? SignedSongTime::FromScale(is.GetSize() / frame_size, + audio_format.sample_rate) + : SignedSongTime::Negative(); decoder_initialized(decoder, audio_format, is.IsSeekable(), total_time); diff --git a/src/decoder/plugins/SidplayDecoderPlugin.cxx b/src/decoder/plugins/SidplayDecoderPlugin.cxx index b0a398f56..2adfa6df9 100644 --- a/src/decoder/plugins/SidplayDecoderPlugin.cxx +++ b/src/decoder/plugins/SidplayDecoderPlugin.cxx @@ -159,11 +159,11 @@ get_song_num(const char *path_fs) } /* get the song length in seconds */ -static int +static SignedSongTime get_song_length(Path path_fs) { if (songlength_database == nullptr) - return -1; + return SignedSongTime::Negative(); char *sid_file = get_container_name(path_fs); SidTuneMod tune(sid_file); @@ -171,7 +171,7 @@ get_song_length(Path path_fs) if(!tune) { LogWarning(sidplay_domain, "failed to load file for calculating md5 sum"); - return -1; + return SignedSongTime::Negative(); } char md5sum[SIDTUNE_MD5_LENGTH+1]; tune.createMD5(md5sum); @@ -183,7 +183,7 @@ get_song_length(Path path_fs) "Database", md5sum, &num_items, nullptr); if(!values || song_num>num_items) { g_strfreev(values); - return -1; + return SignedSongTime::Negative(); } int minutes=strtol(values[song_num-1], nullptr, 10); @@ -199,7 +199,7 @@ get_song_length(Path path_fs) g_strfreev(values); - return (minutes*60)+seconds; + return SignedSongTime::FromS((minutes * 60) + seconds); } 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()); tune.selectSong(song_num); - int song_len=get_song_length(path_fs); - if(song_len==-1) song_len=default_songlength; + auto duration = get_song_length(path_fs); + if (duration.IsNegative() && default_songlength > 0) + duration = SongTime::FromS(default_songlength); /* initialize the player */ @@ -292,12 +293,14 @@ sidplay_file_decode(Decoder &decoder, Path path_fs) const AudioFormat audio_format(48000, SampleFormat::S16, channels); assert(audio_format.IsValid()); - decoder_initialized(decoder, audio_format, true, (float)song_len); + decoder_initialized(decoder, audio_format, true, duration); /* .. and play */ const unsigned timebase = player.timebase(); - song_len *= timebase; + const unsigned end = duration.IsNegative() + ? 0u + : duration.ToScale(timebase); DecoderCommand cmd; do { @@ -334,7 +337,7 @@ sidplay_file_decode(Decoder &decoder, Path path_fs) decoder_command_finished(decoder); } - if (song_len > 0 && player.time() >= (unsigned)song_len) + if (end > 0 && player.time() >= end) break; } while (cmd != DecoderCommand::STOP); @@ -382,9 +385,10 @@ sidplay_scan_file(Path path_fs, tag_handler_invoke_tag(handler, handler_ctx, TAG_TRACK, track); /* time */ - int song_len=get_song_length(path_fs); - if (song_len >= 0) - tag_handler_invoke_duration(handler, handler_ctx, song_len); + const auto duration = get_song_length(path_fs); + if (!duration.IsNegative()) + tag_handler_invoke_duration(handler, handler_ctx, + duration.RoundS()); return true; } diff --git a/src/decoder/plugins/SndfileDecoderPlugin.cxx b/src/decoder/plugins/SndfileDecoderPlugin.cxx index 96e61d088..78897da58 100644 --- a/src/decoder/plugins/SndfileDecoderPlugin.cxx +++ b/src/decoder/plugins/SndfileDecoderPlugin.cxx @@ -140,10 +140,11 @@ static SF_VIRTUAL_IO vio = { /** * Converts a frame number to a timestamp (in seconds). */ -static float +static SongTime frame_to_time(sf_count_t frame, const AudioFormat *audio_format) { - return (float)frame / (float)audio_format->sample_rate; + return SongTime::FromScale(frame, + audio_format->sample_rate); } static void diff --git a/src/decoder/plugins/VorbisDecoderPlugin.cxx b/src/decoder/plugins/VorbisDecoderPlugin.cxx index e740cede6..8348407d6 100644 --- a/src/decoder/plugins/VorbisDecoderPlugin.cxx +++ b/src/decoder/plugins/VorbisDecoderPlugin.cxx @@ -202,6 +202,16 @@ vorbis_init(gcc_unused const config_param ¶m) 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 vorbis_stream_decode(Decoder &decoder, InputStream &input_stream) @@ -237,11 +247,8 @@ vorbis_stream_decode(Decoder &decoder, return; } - float total_time = ov_time_total(&vf, -1); - if (total_time < 0) - total_time = 0; - - decoder_initialized(decoder, audio_format, vis.seekable, total_time); + decoder_initialized(decoder, audio_format, vis.seekable, + vorbis_duration(vf)); #ifdef HAVE_TREMOR char buffer[4096]; diff --git a/src/decoder/plugins/WavpackDecoderPlugin.cxx b/src/decoder/plugins/WavpackDecoderPlugin.cxx index 870a1a80d..1efd49850 100644 --- a/src/decoder/plugins/WavpackDecoderPlugin.cxx +++ b/src/decoder/plugins/WavpackDecoderPlugin.cxx @@ -160,8 +160,9 @@ wavpack_decode(Decoder &decoder, WavpackContext *wpc, bool can_seek) ? format_samples_float : format_samples_int; - const float total_time = float(WavpackGetNumSamples(wpc)) - / audio_format.sample_rate; + const auto total_time = + SongTime::FromScale(WavpackGetNumSamples(wpc), + audio_format.sample_rate); const int bytes_per_sample = WavpackGetBytesPerSample(wpc); const int output_sample_size = audio_format.GetFrameSize(); diff --git a/src/decoder/plugins/WildmidiDecoderPlugin.cxx b/src/decoder/plugins/WildmidiDecoderPlugin.cxx index 08078a792..221f7433b 100644 --- a/src/decoder/plugins/WildmidiDecoderPlugin.cxx +++ b/src/decoder/plugins/WildmidiDecoderPlugin.cxx @@ -86,8 +86,11 @@ wildmidi_file_decode(Decoder &decoder, Path path_fs) return; } - decoder_initialized(decoder, audio_format, true, - info->approx_total_samples / WILDMIDI_SAMPLE_RATE); + const auto duration = + SongTime::FromScale(info->approx_total_samples, + WILDMIDI_SAMPLE_RATE); + + decoder_initialized(decoder, audio_format, true, duration); DecoderCommand cmd; do { diff --git a/test/FakeDecoderAPI.cxx b/test/FakeDecoderAPI.cxx index 2516e9e62..dcc78125b 100644 --- a/test/FakeDecoderAPI.cxx +++ b/test/FakeDecoderAPI.cxx @@ -30,7 +30,7 @@ void decoder_initialized(Decoder &decoder, const AudioFormat audio_format, gcc_unused bool seekable, - float duration) + SignedSongTime duration) { struct audio_format_string af_string; @@ -39,7 +39,7 @@ decoder_initialized(Decoder &decoder, fprintf(stderr, "audio_format=%s duration=%f\n", audio_format_to_string(audio_format, &af_string), - duration); + duration.ToDoubleS()); decoder.initialized = true; }