DecoderAPI: pass SignedSongTime to decoder_initialized()
This commit is contained in:
parent
94f6380d69
commit
d9d97bd17b
@ -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),
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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 */
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -202,6 +202,16 @@ vorbis_init(gcc_unused const config_param ¶m)
|
|||||||
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];
|
||||||
|
@ -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();
|
||||||
|
@ -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 {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user