Use the lame tag's encoder delay/padding to implement gapless mp3 playback
git-svn-id: https://svn.musicpd.org/mpd/trunk@4462 09075e82-0dd4-0310-85a5-a0d7c8717e4f
This commit is contained in:
parent
bf3848ef94
commit
b926dd100b
@ -52,6 +52,9 @@
|
||||
#define MUTEFRAME_SKIP 1
|
||||
#define MUTEFRAME_SEEK 2
|
||||
|
||||
/* the number of samples of silence the decoder inserts at start */
|
||||
#define DECODERDELAY 529
|
||||
|
||||
/* this is stolen from mpg321! */
|
||||
struct audio_dither {
|
||||
mad_fixed_t error[3];
|
||||
@ -131,6 +134,12 @@ typedef struct _mp3DecodeData {
|
||||
long highestFrame;
|
||||
long maxFrames;
|
||||
long currentFrame;
|
||||
int dropFramesAtStart;
|
||||
int dropFramesAtEnd;
|
||||
int dropSamplesAtStart;
|
||||
int dropSamplesAtEnd;
|
||||
int foundFirstFrame;
|
||||
int decodedFirstFrame;
|
||||
int flush;
|
||||
unsigned long bitRate;
|
||||
InputStream *inStream;
|
||||
@ -148,6 +157,12 @@ static void initMp3DecodeData(mp3DecodeData * data, InputStream * inStream)
|
||||
data->frameOffset = NULL;
|
||||
data->times = NULL;
|
||||
data->currentFrame = 0;
|
||||
data->dropFramesAtStart = 0;
|
||||
data->dropFramesAtEnd = 0;
|
||||
data->dropSamplesAtStart = 0;
|
||||
data->dropSamplesAtEnd = 0;
|
||||
data->foundFirstFrame = 0;
|
||||
data->decodedFirstFrame = 0;
|
||||
data->flush = 1;
|
||||
data->inStream = inStream;
|
||||
memset(&(data->dither), 0, sizeof(struct audio_dither));
|
||||
@ -625,6 +640,11 @@ static int decodeFirstFrame(mp3DecodeData * data, DecoderControl * dc,
|
||||
found_xing = parse_xing(&xing, &ptr, &bitlen);
|
||||
found_lame = (found_xing ? parse_lame(&lame, &ptr, &bitlen) : 0);
|
||||
|
||||
if (found_lame) {
|
||||
data->dropSamplesAtStart = lame.encoderDelay + DECODERDELAY;
|
||||
data->dropSamplesAtEnd = lame.encoderPadding;
|
||||
}
|
||||
|
||||
if (found_xing) {
|
||||
if (xing.flags & XING_FRAMES) {
|
||||
mad_timer_t duration = data->frame.header.duration;
|
||||
@ -707,6 +727,8 @@ static int openMp3FromInputStream(InputStream * inStream, mp3DecodeData * data,
|
||||
static int mp3Read(mp3DecodeData * data, OutputBuffer * cb, DecoderControl * dc,
|
||||
ReplayGainInfo ** replayGainInfo)
|
||||
{
|
||||
int samplesPerFrame;
|
||||
int samplesLeft;
|
||||
int i;
|
||||
int ret;
|
||||
int skip;
|
||||
@ -749,6 +771,24 @@ static int mp3Read(mp3DecodeData * data, OutputBuffer * cb, DecoderControl * dc,
|
||||
default:
|
||||
mad_synth_frame(&data->synth, &data->frame);
|
||||
|
||||
if (!data->foundFirstFrame) {
|
||||
samplesPerFrame = (data->synth).pcm.length;
|
||||
data->dropFramesAtStart = data->dropSamplesAtStart / samplesPerFrame;
|
||||
data->dropFramesAtEnd = data->dropSamplesAtEnd / samplesPerFrame;
|
||||
data->dropSamplesAtStart = data->dropSamplesAtStart % samplesPerFrame;
|
||||
data->dropSamplesAtEnd = data->dropSamplesAtEnd % samplesPerFrame;
|
||||
data->foundFirstFrame = 1;
|
||||
}
|
||||
|
||||
if (data->dropFramesAtStart > 0) {
|
||||
data->dropFramesAtStart--;
|
||||
break;
|
||||
} else if ((data->dropFramesAtEnd > 0) &&
|
||||
(data->currentFrame == (data->maxFrames + 1 - data->dropFramesAtEnd))) {
|
||||
data->dropFramesAtEnd--;
|
||||
break;
|
||||
}
|
||||
|
||||
if (data->inStream->metaTitle) {
|
||||
MpdTag *tag = newMpdTag();
|
||||
if (data->inStream->metaName) {
|
||||
@ -764,9 +804,20 @@ static int mp3Read(mp3DecodeData * data, OutputBuffer * cb, DecoderControl * dc,
|
||||
freeMpdTag(tag);
|
||||
}
|
||||
|
||||
samplesLeft = (data->synth).pcm.length;
|
||||
|
||||
for (i = 0; i < (data->synth).pcm.length; i++) {
|
||||
mpd_sint16 *sample;
|
||||
|
||||
if (!data->decodedFirstFrame &&
|
||||
(i < data->dropSamplesAtStart)) {
|
||||
continue;
|
||||
} else if (data->dropSamplesAtEnd &&
|
||||
(data->currentFrame == (data->maxFrames - data->dropFramesAtEnd))) {
|
||||
samplesLeft--;
|
||||
if (samplesLeft < data->dropSamplesAtEnd) break;
|
||||
}
|
||||
|
||||
sample = (mpd_sint16 *) data->outputPtr;
|
||||
*sample = (mpd_sint16) audio_linear_dither(16,
|
||||
(data->
|
||||
@ -821,6 +872,8 @@ static int mp3Read(mp3DecodeData * data, OutputBuffer * cb, DecoderControl * dc,
|
||||
}
|
||||
}
|
||||
|
||||
data->decodedFirstFrame = 1;
|
||||
|
||||
if (dc->seek && data->inStream->seekable) {
|
||||
long i = 0;
|
||||
data->muteFrame = MUTEFRAME_SEEK;
|
||||
|
Loading…
Reference in New Issue
Block a user