Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
M
mpd
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Иван Мажукин
mpd
Commits
a90685d6
Commit
a90685d6
authored
Aug 03, 2019
by
Max Kellermann
Browse files
Options
Browse Files
Download
Plain Diff
Merge tag 'v0.21.12'
release v0.21.12
parents
fe2f8c08
ae19bda1
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
235 additions
and
156 deletions
+235
-156
NEWS
NEWS
+8
-1
meson.build
meson.build
+6
-0
Control.hxx
src/decoder/Control.hxx
+5
-0
MadDecoderPlugin.cxx
src/decoder/plugins/MadDecoderPlugin.cxx
+174
-131
OggVisitor.cxx
src/lib/xiph/OggVisitor.cxx
+2
-2
OggVisitor.hxx
src/lib/xiph/OggVisitor.hxx
+13
-0
JackOutputPlugin.cxx
src/output/plugins/JackOutputPlugin.cxx
+1
-1
Thread.cxx
src/player/Thread.cxx
+13
-0
Compiler.h
src/util/Compiler.h
+0
-12
StaticFifoBuffer.hxx
src/util/StaticFifoBuffer.hxx
+13
-9
No files found.
NEWS
View file @
a90685d6
...
...
@@ -13,9 +13,16 @@ ver 0.22 (not yet released)
- hdcd: new plugin based on FFmpeg's "af_hdcd" for HDCD playback
- volume: convert S16 to S24 to preserve quality and reduce dithering noise
ver 0.21.12 (
not yet released
)
ver 0.21.12 (
2019/08/03
)
* decoder
- mad: update bit rate after seeking
- mad: fix several bugs preventing the plugin from decoding the last frame
- opus: ignore case in replay gain tag names
- opus, vorbis: decode the "end of stream" packet
* output
- jack: fix mono-to-stereo conversion
* player
- don't restart unseekable song after failed seek attempt
* Windows
- support backslash in relative URIs loaded from playlists
...
...
meson.build
View file @
a90685d6
...
...
@@ -15,6 +15,12 @@ version_cxx = vcs_tag(input: 'src/GitVersion.cxx', output: 'GitVersion.cxx')
compiler = meson.get_compiler('cpp')
c_compiler = meson.get_compiler('c')
if compiler.get_id() == 'gcc' and compiler.version().version_compare('<6')
warning('Your GCC version is too old. You need at least version 6.')
elif compiler.get_id() == 'clang' and compiler.version().version_compare('<3')
warning('Your clang version is too old. You need at least version 3.')
endif
conf = configuration_data()
conf.set_quoted('PACKAGE', meson.project_name())
conf.set_quoted('PACKAGE_NAME', meson.project_name())
...
...
src/decoder/Control.hxx
View file @
a90685d6
...
...
@@ -311,6 +311,11 @@ public:
bool
IsCurrentSong
(
const
DetachedSong
&
_song
)
const
noexcept
;
gcc_pure
bool
IsUnseekableCurrentSong
(
const
DetachedSong
&
_song
)
const
noexcept
{
return
!
seekable
&&
IsCurrentSong
(
_song
);
}
gcc_pure
bool
IsSeekableCurrentSong
(
const
DetachedSong
&
_song
)
const
noexcept
{
return
seekable
&&
IsCurrentSong
(
_song
);
}
...
...
src/decoder/plugins/MadDecoderPlugin.cxx
View file @
a90685d6
...
...
@@ -27,6 +27,7 @@
#include "tag/ReplayGain.hxx"
#include "tag/MixRamp.hxx"
#include "CheckAudioFormat.hxx"
#include "util/Clamp.hxx"
#include "util/StringCompare.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
...
...
@@ -75,33 +76,26 @@ ToSongTime(mad_timer_t t) noexcept
}
static
inline
int32_t
mad_fixed_to_24_sample
(
mad_fixed_t
sample
)
mad_fixed_to_24_sample
(
mad_fixed_t
sample
)
noexcept
{
static
constexpr
unsigned
bits
=
24
;
static
constexpr
mad_fixed_t
MIN
=
-
MAD_F_ONE
;
static
constexpr
mad_fixed_t
MAX
=
MAD_F_ONE
-
1
;
/* round */
sample
=
sample
+
(
1L
<<
(
MAD_F_FRACBITS
-
bits
));
/* clip */
if
(
gcc_unlikely
(
sample
>
MAX
))
sample
=
MAX
;
else
if
(
gcc_unlikely
(
sample
<
MIN
))
sample
=
MIN
;
/* quantize */
return
sample
>>
(
MAD_F_FRACBITS
+
1
-
bits
);
return
Clamp
(
sample
,
MAD_F_MIN
,
MAD_F_MAX
)
>>
(
MAD_F_FRACBITS
+
1
-
bits
);
}
static
void
mad_fixed_to_24_buffer
(
int32_t
*
dest
,
const
struct
mad_
synth
*
synth
,
unsigned
int
start
,
unsigned
in
t
end
,
mad_fixed_to_24_buffer
(
int32_t
*
dest
,
const
struct
mad_
pcm
&
src
,
size_t
start
,
size_
t
end
,
unsigned
int
num_channels
)
{
for
(
unsigned
i
=
start
;
i
<
end
;
++
i
)
for
(
size_t
i
=
start
;
i
<
end
;
++
i
)
for
(
unsigned
c
=
0
;
c
<
num_channels
;
++
c
)
*
dest
++
=
mad_fixed_to_24_sample
(
s
ynth
->
pcm
.
samples
[
c
][
i
]);
*
dest
++
=
mad_fixed_to_24_sample
(
s
rc
.
samples
[
c
][
i
]);
}
static
bool
...
...
@@ -112,45 +106,56 @@ mad_plugin_init(const ConfigBlock &block)
return
true
;
}
struct
MadDecoder
{
class
MadDecoder
{
static
constexpr
size_t
READ_BUFFER_SIZE
=
40960
;
static
constexpr
size_t
MP3_DATA_OUTPUT_BUFFER_SIZE
=
2048
;
struct
mad_stream
stream
;
struct
mad_frame
frame
;
struct
mad_synth
synth
;
mad_timer_t
timer
;
unsigned
char
input_buffer
[
READ_BUFFER_SIZE
];
int32_t
output_buffer
[
MP3_DATA_OUTPUT_BUFFER_SIZE
];
int32_t
output_buffer
[
sizeof
(
mad_pcm
::
samples
)
/
sizeof
(
mad_fixed_t
)
];
SignedSongTime
total_time
;
SongTime
elapsed_time
;
SongTime
seek_time
;
MadDecoderMuteFrame
mute_frame
=
MadDecoderMuteFrame
::
NONE
;
long
*
frame_offsets
=
nullptr
;
mad_timer_t
*
times
=
nullptr
;
unsigned
long
highest_frame
=
0
;
unsigned
long
max_frames
=
0
;
unsigned
long
current_frame
=
0
;
unsigned
int
drop_start_frames
=
0
;
unsigned
int
drop_end_frames
=
0
;
size_t
highest_frame
=
0
;
size_t
max_frames
=
0
;
size_t
current_frame
=
0
;
unsigned
int
drop_start_frames
;
unsigned
int
drop_end_frames
;
unsigned
int
drop_start_samples
=
0
;
unsigned
int
drop_end_samples
=
0
;
bool
found_replay_gain
=
false
;
bool
found_first_frame
=
false
;
bool
decoded_first_frame
=
false
;
unsigned
long
bit_rate
;
/**
* If this flag is true, then end-of-file was seen and a
* padding of 8 zero bytes were appended to #input_buffer, to
* allow libmad to decode the last frame.
*/
bool
was_eof
=
false
;
DecoderClient
*
const
client
;
InputStream
&
input_stream
;
enum
mad_layer
layer
=
mad_layer
(
0
);
MadDecoder
(
DecoderClient
*
client
,
InputStream
&
input_stream
);
~
MadDecoder
();
public
:
MadDecoder
(
DecoderClient
*
client
,
InputStream
&
input_stream
)
noexcept
;
~
MadDecoder
()
noexcept
;
void
RunDecoder
()
noexcept
;
bool
RunScan
(
TagHandler
&
handler
)
noexcept
;
bool
Seek
(
long
offset
);
bool
FillBuffer
();
void
ParseId3
(
size_t
tagsize
,
Tag
*
tag
);
MadDecoderAction
DecodeNextFrameHeader
(
Tag
*
tag
);
MadDecoderAction
DecodeNextFrame
();
private
:
bool
Seek
(
long
offset
)
noexcept
;
bool
FillBuffer
()
noexcept
;
void
ParseId3
(
size_t
tagsize
,
Tag
*
tag
)
noexcept
;
MadDecoderAction
DecodeNextFrameHeader
(
Tag
*
tag
)
noexcept
;
MadDecoderAction
DecodeNextFrame
()
noexcept
;
gcc_pure
offset_type
ThisFrameOffset
()
const
noexcept
;
...
...
@@ -161,11 +166,11 @@ struct MadDecoder {
/**
* Attempt to calulcate the length of the song from filesize
*/
void
FileSizeToSongLength
();
void
FileSizeToSongLength
()
noexcept
;
bool
DecodeFirstFrame
(
Tag
*
tag
);
bool
DecodeFirstFrame
(
Tag
*
tag
)
noexcept
;
void
AllocateBuffers
()
{
void
AllocateBuffers
()
noexcept
{
assert
(
max_frames
>
0
);
assert
(
frame_offsets
==
nullptr
);
assert
(
times
==
nullptr
);
...
...
@@ -175,27 +180,39 @@ struct MadDecoder {
}
gcc_pure
long
TimeToFrame
(
SongTime
t
)
const
noexcept
;
size_t
TimeToFrame
(
SongTime
t
)
const
noexcept
;
void
UpdateTimerNextFrame
();
/**
* Record the current frame's offset in the "frame_offsets"
* buffer and go forward to the next frame, updating the
* attributes "current_frame" and "timer".
*/
void
UpdateTimerNextFrame
()
noexcept
;
/**
* Sends the synthesized current frame via
* DecoderClient::SubmitData().
*/
DecoderCommand
S
endPCM
(
unsigned
i
,
unsigned
pcm_length
)
;
DecoderCommand
S
ubmitPCM
(
size_t
start
,
size_t
n
)
noexcept
;
/**
* Synthesize the current frame and send it via
* DecoderClient::SubmitData().
*/
DecoderCommand
SyncAndSend
();
DecoderCommand
SynthAndSubmit
()
noexcept
;
/**
* @return false to stop decoding
*/
bool
HandleCurrentFrame
()
noexcept
;
bool
Read
();
bool
LoadNextFrame
()
noexcept
;
bool
Read
()
noexcept
;
};
MadDecoder
::
MadDecoder
(
DecoderClient
*
_client
,
InputStream
&
_input_stream
)
InputStream
&
_input_stream
)
noexcept
:
client
(
_client
),
input_stream
(
_input_stream
)
{
mad_stream_init
(
&
stream
);
...
...
@@ -206,7 +223,7 @@ MadDecoder::MadDecoder(DecoderClient *_client,
}
inline
bool
MadDecoder
::
Seek
(
long
offset
)
MadDecoder
::
Seek
(
long
offset
)
noexcept
{
try
{
input_stream
.
LockSeek
(
offset
);
...
...
@@ -221,32 +238,38 @@ MadDecoder::Seek(long offset)
}
inline
bool
MadDecoder
::
FillBuffer
()
MadDecoder
::
FillBuffer
()
noexcept
{
size_t
remaining
,
length
;
unsigned
char
*
dest
;
/* amount of rest data still residing in the buffer */
size_t
rest_size
=
0
;
size_t
max_read_size
=
sizeof
(
input_buffer
);
unsigned
char
*
dest
=
input_buffer
;
if
(
stream
.
next_frame
!=
nullptr
)
{
remaining
=
stream
.
bufend
-
stream
.
next_frame
;
memmove
(
input_buffer
,
stream
.
next_frame
,
remaining
);
dest
=
input_buffer
+
remaining
;
length
=
READ_BUFFER_SIZE
-
remaining
;
}
else
{
remaining
=
0
;
length
=
READ_BUFFER_SIZE
;
dest
=
input_buffer
;
rest_size
=
stream
.
bufend
-
stream
.
next_frame
;
memmove
(
input_buffer
,
stream
.
next_frame
,
rest_size
);
dest
+=
rest_size
;
max_read_size
-=
rest_size
;
}
/* we've exhausted the read buffer, so give up!, these potential
* mp3 frames are way too big, and thus unlikely to be mp3 frames */
if
(
length
==
0
)
if
(
max_read_size
==
0
)
return
false
;
length
=
decoder_read
(
client
,
input_stream
,
dest
,
length
);
if
(
length
==
0
)
return
false
;
size_t
nbytes
=
decoder_read
(
client
,
input_stream
,
dest
,
max_read_size
);
if
(
nbytes
==
0
)
{
if
(
was_eof
||
max_read_size
<
MAD_BUFFER_GUARD
)
return
false
;
was_eof
=
true
;
nbytes
=
MAD_BUFFER_GUARD
;
memset
(
dest
,
0
,
nbytes
);
}
mad_stream_buffer
(
&
stream
,
input_buffer
,
length
+
remaining
);
mad_stream_buffer
(
&
stream
,
input_buffer
,
rest_size
+
nbytes
);
stream
.
error
=
MAD_ERROR_NONE
;
return
true
;
...
...
@@ -282,7 +305,7 @@ parse_id3_mixramp(struct id3_tag *tag) noexcept
#endif
inline
void
MadDecoder
::
ParseId3
(
size_t
tagsize
,
Tag
*
mpd_tag
)
MadDecoder
::
ParseId3
(
size_t
tagsize
,
Tag
*
mpd_tag
)
noexcept
{
#ifdef ENABLE_ID3TAG
std
::
unique_ptr
<
id3_byte_t
[]
>
allocated
;
...
...
@@ -350,7 +373,7 @@ MadDecoder::ParseId3(size_t tagsize, Tag *mpd_tag)
* of the ID3 frame.
*/
static
signed
long
id3_tag_query
(
const
void
*
p0
,
size_t
length
)
id3_tag_query
(
const
void
*
p0
,
size_t
length
)
noexcept
{
const
char
*
p
=
(
const
char
*
)
p0
;
...
...
@@ -361,7 +384,7 @@ id3_tag_query(const void *p0, size_t length)
#endif
/* !ENABLE_ID3TAG */
static
MadDecoderAction
RecoverFrameError
(
struct
mad_stream
&
stream
)
RecoverFrameError
(
const
struct
mad_stream
&
stream
)
noexcept
{
if
(
MAD_RECOVERABLE
(
stream
.
error
))
return
MadDecoderAction
::
SKIP
;
...
...
@@ -375,7 +398,7 @@ RecoverFrameError(struct mad_stream &stream)
}
MadDecoderAction
MadDecoder
::
DecodeNextFrameHeader
(
Tag
*
tag
)
MadDecoder
::
DecodeNextFrameHeader
(
Tag
*
tag
)
noexcept
{
if
((
stream
.
buffer
==
nullptr
||
stream
.
error
==
MAD_ERROR_BUFLEN
)
&&
!
FillBuffer
())
...
...
@@ -413,7 +436,7 @@ MadDecoder::DecodeNextFrameHeader(Tag *tag)
}
MadDecoderAction
MadDecoder
::
DecodeNextFrame
()
MadDecoder
::
DecodeNextFrame
()
noexcept
{
if
((
stream
.
buffer
==
nullptr
||
stream
.
error
==
MAD_ERROR_BUFLEN
)
&&
!
FillBuffer
())
...
...
@@ -472,7 +495,7 @@ struct lame {
};
static
bool
parse_xing
(
struct
xing
*
xing
,
struct
mad_bitptr
*
ptr
,
int
*
oldbitlen
)
parse_xing
(
struct
xing
*
xing
,
struct
mad_bitptr
*
ptr
,
int
*
oldbitlen
)
noexcept
{
int
bitlen
=
*
oldbitlen
;
...
...
@@ -552,7 +575,7 @@ parse_xing(struct xing *xing, struct mad_bitptr *ptr, int *oldbitlen)
}
static
bool
parse_lame
(
struct
lame
*
lame
,
struct
mad_bitptr
*
ptr
,
int
*
bitlen
)
parse_lame
(
struct
lame
*
lame
,
struct
mad_bitptr
*
ptr
,
int
*
bitlen
)
noexcept
{
/* Unlike the xing header, the lame tag has a fixed length. Fail if
* not all 36 bytes (288 bits) are there. */
...
...
@@ -643,7 +666,7 @@ parse_lame(struct lame *lame, struct mad_bitptr *ptr, int *bitlen)
}
static
inline
SongTime
mad_frame_duration
(
const
struct
mad_frame
*
frame
)
mad_frame_duration
(
const
struct
mad_frame
*
frame
)
noexcept
{
return
ToSongTime
(
frame
->
header
.
duration
);
}
...
...
@@ -668,7 +691,7 @@ MadDecoder::RestIncludingThisFrame() const noexcept
}
inline
void
MadDecoder
::
FileSizeToSongLength
()
MadDecoder
::
FileSizeToSongLength
()
noexcept
{
if
(
input_stream
.
KnownSize
())
{
offset_type
rest
=
RestIncludingThisFrame
();
...
...
@@ -690,7 +713,7 @@ MadDecoder::FileSizeToSongLength()
}
inline
bool
MadDecoder
::
DecodeFirstFrame
(
Tag
*
tag
)
MadDecoder
::
DecodeFirstFrame
(
Tag
*
tag
)
noexcept
{
struct
xing
xing
;
...
...
@@ -732,9 +755,17 @@ MadDecoder::DecodeFirstFrame(Tag *tag)
struct
lame
lame
;
if
(
parse_lame
(
&
lame
,
&
ptr
,
&
bitlen
))
{
if
(
gapless_playback
&&
input_stream
.
IsSeekable
())
{
/* libmad inserts 529 samples of
silence at the beginning and
removes those 529 samples at the
end */
drop_start_samples
=
lame
.
encoder_delay
+
DECODERDELAY
;
drop_end_samples
=
lame
.
encoder_padding
;
if
(
drop_end_samples
>
DECODERDELAY
)
drop_end_samples
-=
DECODERDELAY
;
else
drop_end_samples
=
0
;
}
/* Album gain isn't currently used. See comment in
...
...
@@ -763,7 +794,7 @@ MadDecoder::DecodeFirstFrame(Tag *tag)
return
true
;
}
MadDecoder
::~
MadDecoder
()
MadDecoder
::~
MadDecoder
()
noexcept
{
mad_synth_finish
(
&
synth
);
mad_frame_finish
(
&
frame
);
...
...
@@ -773,10 +804,10 @@ MadDecoder::~MadDecoder()
delete
[]
times
;
}
long
size_t
MadDecoder
::
TimeToFrame
(
SongTime
t
)
const
noexcept
{
unsigned
long
i
;
size_t
i
;
for
(
i
=
0
;
i
<
highest_frame
;
++
i
)
{
auto
frame_time
=
ToSongTime
(
times
[
i
]);
...
...
@@ -788,12 +819,11 @@ MadDecoder::TimeToFrame(SongTime t) const noexcept
}
void
MadDecoder
::
UpdateTimerNextFrame
()
MadDecoder
::
UpdateTimerNextFrame
()
noexcept
{
if
(
current_frame
>=
highest_frame
)
{
/* record this frame's properties in frame_offsets
(for seeking) and times */
bit_rate
=
frame
.
header
.
bitrate
;
if
(
current_frame
>=
max_frames
)
/* cap current_frame */
...
...
@@ -814,36 +844,22 @@ MadDecoder::UpdateTimerNextFrame()
}
DecoderCommand
MadDecoder
::
S
endPCM
(
unsigned
i
,
unsigned
pcm_length
)
MadDecoder
::
S
ubmitPCM
(
size_t
i
,
size_t
pcm_length
)
noexcept
{
unsigned
max_samples
=
sizeof
(
output_buffer
)
/
sizeof
(
output_buffer
[
0
])
/
MAD_NCHANNELS
(
&
frame
.
header
);
while
(
i
<
pcm_length
)
{
unsigned
int
num_samples
=
pcm_length
-
i
;
if
(
num_samples
>
max_samples
)
num_samples
=
max_samples
;
i
+=
num_samples
;
mad_fixed_to_24_buffer
(
output_buffer
,
&
synth
,
i
-
num_samples
,
i
,
MAD_NCHANNELS
(
&
frame
.
header
));
num_samples
*=
MAD_NCHANNELS
(
&
frame
.
header
);
size_t
num_samples
=
pcm_length
-
i
;
auto
cmd
=
client
->
SubmitData
(
input_stream
,
output_buffer
,
sizeof
(
output_buffer
[
0
])
*
num_samples
,
bit_rate
/
1000
);
if
(
cmd
!=
DecoderCommand
::
NONE
)
return
cmd
;
}
mad_fixed_to_24_buffer
(
output_buffer
,
synth
.
pcm
,
i
,
i
+
num_samples
,
MAD_NCHANNELS
(
&
frame
.
header
));
num_samples
*=
MAD_NCHANNELS
(
&
frame
.
header
);
return
DecoderCommand
::
NONE
;
return
client
->
SubmitData
(
input_stream
,
output_buffer
,
sizeof
(
output_buffer
[
0
])
*
num_samples
,
frame
.
header
.
bitrate
/
1000
);
}
inline
DecoderCommand
MadDecoder
::
Syn
cAndSend
()
MadDecoder
::
Syn
thAndSubmit
()
noexcept
{
mad_synth_frame
(
&
synth
,
&
frame
);
...
...
@@ -860,33 +876,33 @@ MadDecoder::SyncAndSend()
drop_start_frames
--
;
return
DecoderCommand
::
NONE
;
}
else
if
((
drop_end_frames
>
0
)
&&
(
current_frame
==
(
max_frames
+
1
-
drop_end_frames
))
)
{
current_frame
==
max_frames
-
drop_end_frames
)
{
/* stop decoding, effectively dropping all remaining
frames */
return
DecoderCommand
::
STOP
;
}
unsigned
i
=
0
;
size_t
i
=
0
;
if
(
!
decoded_first_frame
)
{
i
=
drop_start_samples
;
decoded_first_frame
=
true
;
}
unsigned
pcm_length
=
synth
.
pcm
.
length
;
size_t
pcm_length
=
synth
.
pcm
.
length
;
if
(
drop_end_samples
&&
(
current_frame
==
max_frames
-
drop_end_frames
)
)
{
current_frame
==
max_frames
-
drop_end_frames
-
1
)
{
if
(
drop_end_samples
>=
pcm_length
)
pcm_length
=
0
;
else
pcm_length
-=
drop_end_samples
;
return
DecoderCommand
::
STOP
;
pcm_length
-=
drop_end_samples
;
}
auto
cmd
=
S
end
PCM
(
i
,
pcm_length
);
auto
cmd
=
S
ubmit
PCM
(
i
,
pcm_length
);
if
(
cmd
!=
DecoderCommand
::
NONE
)
return
cmd
;
if
(
drop_end_samples
&&
(
current_frame
==
max_frames
-
drop_end_frames
)
)
current_frame
==
max_frames
-
drop_end_frames
-
1
)
/* stop decoding, effectively dropping
* all remaining samples */
return
DecoderCommand
::
STOP
;
...
...
@@ -895,10 +911,8 @@ MadDecoder::SyncAndSend()
}
inline
bool
MadDecoder
::
Read
()
MadDecoder
::
HandleCurrentFrame
()
noexcept
{
UpdateTimerNextFrame
();
switch
(
mute_frame
)
{
DecoderCommand
cmd
;
...
...
@@ -908,17 +922,20 @@ MadDecoder::Read()
case
MadDecoderMuteFrame
:
:
SEEK
:
if
(
elapsed_time
>=
seek_time
)
mute_frame
=
MadDecoderMuteFrame
::
NONE
;
UpdateTimerNextFrame
();
break
;
case
MadDecoderMuteFrame
:
:
NONE
:
cmd
=
SyncAndSend
();
cmd
=
SynthAndSubmit
();
UpdateTimerNextFrame
();
if
(
cmd
==
DecoderCommand
::
SEEK
)
{
assert
(
input_stream
.
IsSeekable
());
const
auto
t
=
client
->
GetSeekTime
();
unsigned
long
j
=
TimeToFrame
(
t
);
size_t
j
=
TimeToFrame
(
t
);
if
(
j
<
highest_frame
)
{
if
(
Seek
(
frame_offsets
[
j
]))
{
current_frame
=
j
;
was_eof
=
false
;
client
->
CommandFinished
();
}
else
client
->
SeekError
();
...
...
@@ -931,6 +948,12 @@ MadDecoder::Read()
return
false
;
}
return
true
;
}
inline
bool
MadDecoder
::
LoadNextFrame
()
noexcept
{
while
(
true
)
{
MadDecoderAction
ret
;
do
{
...
...
@@ -960,53 +983,73 @@ MadDecoder::Read()
}
}
static
void
mad_decode
(
DecoderClient
&
client
,
InputStream
&
input_stream
)
inline
bool
MadDecoder
::
Read
()
noexcept
{
MadDecoder
data
(
&
client
,
input_stream
);
return
HandleCurrentFrame
()
&&
LoadNextFrame
();
}
inline
void
MadDecoder
::
RunDecoder
()
noexcept
{
assert
(
client
!=
nullptr
);
Tag
tag
;
if
(
!
data
.
DecodeFirstFrame
(
&
tag
))
{
if
(
client
.
GetCommand
()
==
DecoderCommand
::
NONE
)
if
(
!
DecodeFirstFrame
(
&
tag
))
{
if
(
client
->
GetCommand
()
==
DecoderCommand
::
NONE
)
LogError
(
mad_domain
,
"input
/Input
does not appear to be a mp3 bit stream"
);
"input does not appear to be a mp3 bit stream"
);
return
;
}
data
.
AllocateBuffers
();
AllocateBuffers
();
client
.
Ready
(
CheckAudioFormat
(
data
.
frame
.
header
.
samplerate
,
SampleFormat
::
S24_P32
,
MAD_NCHANNELS
(
&
data
.
frame
.
header
)),
input_stream
.
IsSeekable
(),
data
.
total_time
);
client
->
Ready
(
CheckAudioFormat
(
frame
.
header
.
samplerate
,
SampleFormat
::
S24_P32
,
MAD_NCHANNELS
(
&
frame
.
header
)),
input_stream
.
IsSeekable
(),
total_time
);
if
(
!
tag
.
IsEmpty
())
client
.
SubmitTag
(
input_stream
,
std
::
move
(
tag
));
client
->
SubmitTag
(
input_stream
,
std
::
move
(
tag
));
while
(
data
.
Read
())
{}
while
(
Read
())
{}
}
static
bool
mad_decode
r_scan_stream
(
InputStream
&
is
,
TagHandler
&
handler
)
noexcept
static
void
mad_decode
(
DecoderClient
&
client
,
InputStream
&
input_stream
)
{
MadDecoder
data
(
nullptr
,
is
);
if
(
!
data
.
DecodeFirstFrame
(
nullptr
))
MadDecoder
data
(
&
client
,
input_stream
);
data
.
RunDecoder
();
}
inline
bool
MadDecoder
::
RunScan
(
TagHandler
&
handler
)
noexcept
{
if
(
!
DecodeFirstFrame
(
nullptr
))
return
false
;
if
(
!
data
.
total_time
.
IsNegative
())
handler
.
OnDuration
(
SongTime
(
data
.
total_time
));
if
(
!
total_time
.
IsNegative
())
handler
.
OnDuration
(
SongTime
(
total_time
));
try
{
handler
.
OnAudioFormat
(
CheckAudioFormat
(
data
.
frame
.
header
.
samplerate
,
handler
.
OnAudioFormat
(
CheckAudioFormat
(
frame
.
header
.
samplerate
,
SampleFormat
::
S24_P32
,
MAD_NCHANNELS
(
&
data
.
frame
.
header
)));
MAD_NCHANNELS
(
&
frame
.
header
)));
}
catch
(...)
{
}
return
true
;
}
static
bool
mad_decoder_scan_stream
(
InputStream
&
is
,
TagHandler
&
handler
)
noexcept
{
MadDecoder
data
(
nullptr
,
is
);
return
data
.
RunScan
(
handler
);
}
static
const
char
*
const
mad_suffixes
[]
=
{
"mp3"
,
"mp2"
,
nullptr
};
static
const
char
*
const
mad_mime_types
[]
=
{
"audio/mpeg"
,
nullptr
};
...
...
src/lib/xiph/OggVisitor.cxx
View file @
a90685d6
...
...
@@ -69,12 +69,12 @@ OggVisitor::HandlePacket(const ogg_packet &packet)
/* fail if BOS is missing */
throw
std
::
runtime_error
(
"BOS packet expected"
);
OnOggPacket
(
packet
);
if
(
packet
.
e_o_s
)
{
EndStream
();
return
;
}
OnOggPacket
(
packet
);
}
inline
void
...
...
src/lib/xiph/OggVisitor.hxx
View file @
a90685d6
...
...
@@ -67,8 +67,21 @@ private:
void
HandlePackets
();
protected
:
/**
* Called when the "beginning of stream" packet has been seen.
*
* @param packet the "beginning of stream" packet
*/
virtual
void
OnOggBeginning
(
const
ogg_packet
&
packet
)
=
0
;
/**
* Called for each follow-up packet.
*/
virtual
void
OnOggPacket
(
const
ogg_packet
&
packet
)
=
0
;
/**
* Called after the "end of stream" packet has been processed.
*/
virtual
void
OnOggEnd
()
=
0
;
};
...
...
src/output/plugins/JackOutputPlugin.cxx
View file @
a90685d6
...
...
@@ -539,7 +539,7 @@ JackOutput::Start()
std
::
fill
(
dports
+
num_dports
,
dports
+
audio_format
.
channels
,
dports
[
0
]);
}
else
if
(
num_dports
>
audio_format
.
channels
)
{
if
(
audio_format
.
channels
==
1
&&
num_dports
>
2
)
{
if
(
audio_format
.
channels
==
1
&&
num_dports
>
=
2
)
{
/* mono input file: connect the one source
channel to the both destination channels */
duplicate_port
=
dports
[
1
];
...
...
src/player/Thread.cxx
View file @
a90685d6
...
...
@@ -602,6 +602,19 @@ Player::SeekDecoder(std::unique_lock<Mutex> &lock) noexcept
{
assert
(
pc
.
next_song
!=
nullptr
);
if
(
pc
.
seek_time
>
SongTime
::
zero
()
&&
// TODO: allow this only if the song duration is known
dc
.
IsUnseekableCurrentSong
(
*
pc
.
next_song
))
{
/* seeking into the current song; but we already know
it's not seekable, so let's fail early */
/* note the seek_time>0 check: if seeking to the
beginning, we can simply restart the decoder */
pc
.
next_song
.
reset
();
pc
.
SetError
(
PlayerError
::
DECODER
,
std
::
make_exception_ptr
(
std
::
runtime_error
(
"Not seekable"
)));
pc
.
CommandFinished
();
return
true
;
}
CancelPendingSeek
();
{
...
...
src/util/Compiler.h
View file @
a90685d6
...
...
@@ -57,18 +57,6 @@
(GCC_VERSION > 0 && CLANG_VERSION == 0 && \
GCC_VERSION < GCC_MAKE_VERSION(major, minor, 0))
#ifdef __clang__
# if __clang_major__ < 3
# error Sorry, your clang version is too old. You need at least version 3.1.
# endif
#elif defined(__GNUC__)
# if GCC_OLDER_THAN(6,0)
# error Sorry, your gcc version is too old. You need at least version 6.0.
# endif
#else
# warning Untested compiler. Use at your own risk!
#endif
/**
* Are we building with the specified version of clang or newer?
*/
...
...
src/util/StaticFifoBuffer.hxx
View file @
a90685d6
...
...
@@ -56,11 +56,11 @@ protected:
T
data
[
size
];
public
:
constexpr
size_type
GetCapacity
()
const
{
constexpr
size_type
GetCapacity
()
const
noexcept
{
return
size
;
}
void
Shift
()
{
void
Shift
()
noexcept
{
if
(
head
==
0
)
return
;
...
...
@@ -74,15 +74,15 @@ public:
head
=
0
;
}
void
Clear
()
{
void
Clear
()
noexcept
{
head
=
tail
=
0
;
}
bool
empty
()
cons
t
{
constexpr
bool
empty
()
const
noexcep
t
{
return
head
==
tail
;
}
bool
IsFull
()
cons
t
{
constexpr
bool
IsFull
()
const
noexcep
t
{
return
head
==
0
&&
tail
==
size
;
}
...
...
@@ -90,7 +90,7 @@ public:
* Prepares writing. Returns a buffer range which may be written.
* When you are finished, call Append().
*/
Range
Write
()
{
Range
Write
()
noexcept
{
if
(
empty
())
Clear
();
else
if
(
tail
==
size
)
...
...
@@ -103,7 +103,7 @@ public:
* Expands the tail of the buffer, after data has been written to
* the buffer returned by Write().
*/
void
Append
(
size_type
n
)
{
void
Append
(
size_type
n
)
noexcept
{
assert
(
tail
<=
size
);
assert
(
n
<=
size
);
assert
(
tail
+
n
<=
size
);
...
...
@@ -111,18 +111,22 @@ public:
tail
+=
n
;
}
constexpr
size_type
GetAvailable
()
const
noexcept
{
return
tail
-
head
;
}
/**
* Return a buffer range which may be read. The buffer pointer is
* writable, to allow modifications while parsing.
*/
Range
Read
()
{
constexpr
Range
Read
()
noexcept
{
return
Range
(
data
+
head
,
tail
-
head
);
}
/**
* Marks a chunk as consumed.
*/
void
Consume
(
size_type
n
)
{
void
Consume
(
size_type
n
)
noexcept
{
assert
(
tail
<=
size
);
assert
(
head
<=
tail
);
assert
(
n
<=
tail
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment