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
5 years ago
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
...
...
This diff is collapsed.
Click to expand it.
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())
...
...
This diff is collapsed.
Click to expand it.
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
);
}
...
...
This diff is collapsed.
Click to expand it.
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
};
...
...
This diff is collapsed.
Click to expand it.
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
...
...
This diff is collapsed.
Click to expand it.
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
;
};
...
...
This diff is collapsed.
Click to expand it.
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
];
...
...
This diff is collapsed.
Click to expand it.
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
();
{
...
...
This diff is collapsed.
Click to expand it.
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?
*/
...
...
This diff is collapsed.
Click to expand it.
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
);
...
...
This diff is collapsed.
Click to expand it.
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