Commit d9d97bd1 authored by Max Kellermann's avatar Max Kellermann

DecoderAPI: pass SignedSongTime to decoder_initialized()

parent 94f6380d
......@@ -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),
......
......@@ -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.
......
......@@ -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;
......
......@@ -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<uint64_t>(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;
}
......
......@@ -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<uint64_t>(n_frames,
audio_format.sample_rate);
/* success: file was recognized */
decoder_initialized(decoder, audio_format, is.IsSeekable(), songtime);
......
......@@ -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<uint64_t>(n_blocks * DSF_BLOCK_SIZE,
audio_format.sample_rate);
/* success: file was recognized */
decoder_initialized(decoder, audio_format, is.IsSeekable(), songtime);
......
......@@ -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<uint64_t>(frames * uint64_t(1024),
sample_rate);
}
static float
static SignedSongTime
faad_song_duration(DecoderBuffer *buffer, InputStream &is)
{
auto data = ConstBuffer<uint8_t>::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<uint8_t>::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<bool, SignedSongTime>
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;
}
......
......@@ -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<uint64_t>(format_context->duration,
AV_TIME_BASE)
: SignedSongTime::Negative();
decoder_initialized(decoder, audio_format,
input.IsSeekable(), total_time);
......
......@@ -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<uint64_t>(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;
......
......@@ -144,10 +144,14 @@ flac_decoder_initialize(struct flac_data *data, FLAC__StreamDecoder *sd,
if (data->initialized) {
/* done */
const auto duration2 =
SongTime::FromScale<uint64_t>(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;
}
......
......@@ -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) {
......
......@@ -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 */
......
......@@ -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<uint64_t>(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<bool, SignedSongTime>
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;
}
......
......@@ -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);
......
......@@ -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 {
......
......@@ -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<uint64_t>(MP4GetTrackDuration(handle, track),
scale);
const MP4SampleId num_samples = MP4GetTrackNumberOfSamples(handle, track);
decoder_initialized(mpd_decoder, audio_format, true, duration);
......
......@@ -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 {
......
......@@ -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<uint64_t>(num_samples,
audio_format.sample_rate);
decoder_initialized(decoder, audio_format, true, duration);
if (mpg123_info(handle, &info) != MPG123_OK) {
info.vbr = MPG123_CBR;
......
......@@ -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<uint64_t>(eos_granulepos,
opus_sample_rate)
: SignedSongTime::Negative();
const AudioFormat audio_format(opus_sample_rate,
SampleFormat::S16, channels);
......
......@@ -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<uint64_t>(is.GetSize() / frame_size,
audio_format.sample_rate)
: SignedSongTime::Negative();
decoder_initialized(decoder, audio_format,
is.IsSeekable(), total_time);
......
......@@ -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<uint64_t>(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;
}
......
......@@ -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<uint64_t>(frame,
audio_format->sample_rate);
}
static void
......
......@@ -202,6 +202,16 @@ vorbis_init(gcc_unused const config_param &param)
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];
......
......@@ -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<uint64_t>(WavpackGetNumSamples(wpc),
audio_format.sample_rate);
const int bytes_per_sample = WavpackGetBytesPerSample(wpc);
const int output_sample_size = audio_format.GetFrameSize();
......
......@@ -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<uint64_t>(info->approx_total_samples,
WILDMIDI_SAMPLE_RATE);
decoder_initialized(decoder, audio_format, true, duration);
DecoderCommand cmd;
do {
......
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment