mirror of
https://github.com/cookiengineer/audacity
synced 2025-07-12 14:47:43 +02:00
Fix truncated decode of mp3 files on import
The MAD decoder will not decode the final frame in an mp3 stream unless it has a small amount of padding afterwards (MAD_BUFFER_GUARD bytes, actually 8). Without this, it loses sync before returning any decoded data from the final frame. The result is that the imported audio is truncated by up to 1152 samples. This commit addresses that by using a final round of input callback calls to provide the last MAD_BUFFER_GUARD bytes after the underlying file has reached eof. The logic is based on madplay.
This commit is contained in:
parent
639bfcbad4
commit
30eaa0d52c
@ -95,12 +95,14 @@ extern "C" {
|
|||||||
struct private_data {
|
struct private_data {
|
||||||
wxFile *file; /* the file containing the mp3 data we're feeding the encoder */
|
wxFile *file; /* the file containing the mp3 data we're feeding the encoder */
|
||||||
unsigned char *inputBuffer;
|
unsigned char *inputBuffer;
|
||||||
|
int inputBufferFill; /* amount of data in inputBuffer */
|
||||||
TrackFactory *trackFactory;
|
TrackFactory *trackFactory;
|
||||||
TrackHolders channels;
|
TrackHolders channels;
|
||||||
ProgressDialog *progress;
|
ProgressDialog *progress;
|
||||||
unsigned numChannels;
|
unsigned numChannels;
|
||||||
int updateResult;
|
int updateResult;
|
||||||
bool id3checked;
|
bool id3checked;
|
||||||
|
bool eof; /* having supplied both underlying file and guard pad data */
|
||||||
};
|
};
|
||||||
|
|
||||||
class MP3ImportPlugin final : public ImportPlugin
|
class MP3ImportPlugin final : public ImportPlugin
|
||||||
@ -216,11 +218,13 @@ int MP3ImportFileHandle::Import(TrackFactory *trackFactory, TrackHolders &outTra
|
|||||||
|
|
||||||
mPrivateData.file = mFile.get();
|
mPrivateData.file = mFile.get();
|
||||||
mPrivateData.inputBuffer = new unsigned char [INPUT_BUFFER_SIZE];
|
mPrivateData.inputBuffer = new unsigned char [INPUT_BUFFER_SIZE];
|
||||||
|
mPrivateData.inputBufferFill = 0;
|
||||||
mPrivateData.progress = mProgress.get();
|
mPrivateData.progress = mProgress.get();
|
||||||
mPrivateData.updateResult= eProgressSuccess;
|
mPrivateData.updateResult= eProgressSuccess;
|
||||||
mPrivateData.id3checked = false;
|
mPrivateData.id3checked = false;
|
||||||
mPrivateData.numChannels = 0;
|
mPrivateData.numChannels = 0;
|
||||||
mPrivateData.trackFactory= trackFactory;
|
mPrivateData.trackFactory= trackFactory;
|
||||||
|
mPrivateData.eof = false;
|
||||||
|
|
||||||
mad_decoder_init(&mDecoder, &mPrivateData, input_cb, 0, 0, output_cb, error_cb, 0);
|
mad_decoder_init(&mDecoder, &mPrivateData, input_cb, 0, 0, output_cb, error_cb, 0);
|
||||||
|
|
||||||
@ -404,7 +408,10 @@ enum mad_flow input_cb(void *_data, struct mad_stream *stream)
|
|||||||
if(data->updateResult != eProgressSuccess)
|
if(data->updateResult != eProgressSuccess)
|
||||||
return MAD_FLOW_STOP;
|
return MAD_FLOW_STOP;
|
||||||
|
|
||||||
if(data->file->Eof()) {
|
if (data->eof) {
|
||||||
|
/* different from data->File->Eof(), this means the underlying
|
||||||
|
file has reached eof *and* we have subsequently supplied the
|
||||||
|
final padding zeros */
|
||||||
return MAD_FLOW_STOP;
|
return MAD_FLOW_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,20 +438,41 @@ enum mad_flow input_cb(void *_data, struct mad_stream *stream)
|
|||||||
* mad_stream_buffer()"
|
* mad_stream_buffer()"
|
||||||
* -- Rob Leslie, on the mad-dev mailing list */
|
* -- Rob Leslie, on the mad-dev mailing list */
|
||||||
|
|
||||||
unsigned int unconsumedBytes;
|
int unconsumedBytes;
|
||||||
if(stream->next_frame) {
|
if (stream->next_frame) {
|
||||||
unconsumedBytes = data->inputBuffer + INPUT_BUFFER_SIZE - stream->next_frame;
|
/* we must use inputBufferFill instead of INPUT_BUFFER_SIZE here
|
||||||
|
because the final buffer of the file may be only partially
|
||||||
|
filled, and we would otherwise be providing too much input
|
||||||
|
after eof */
|
||||||
|
unconsumedBytes = data->inputBuffer + data->inputBufferFill
|
||||||
|
- stream->next_frame;
|
||||||
memmove(data->inputBuffer, stream->next_frame, unconsumedBytes);
|
memmove(data->inputBuffer, stream->next_frame, unconsumedBytes);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
unconsumedBytes = 0;
|
unconsumedBytes = 0;
|
||||||
|
|
||||||
|
if (data->file->Eof() &&
|
||||||
|
(unconsumedBytes + MAD_BUFFER_GUARD < INPUT_BUFFER_SIZE)) {
|
||||||
|
|
||||||
|
/* supply the requisite MAD_BUFFER_GUARD zero bytes to ensure
|
||||||
|
the final frame gets decoded properly, then finish */
|
||||||
|
|
||||||
|
memset(data->inputBuffer + unconsumedBytes, 0, MAD_BUFFER_GUARD);
|
||||||
|
mad_stream_buffer
|
||||||
|
(stream, data->inputBuffer, MAD_BUFFER_GUARD + unconsumedBytes);
|
||||||
|
|
||||||
|
data->eof = true; /* so on next call, we will tell mad to stop */
|
||||||
|
|
||||||
|
return MAD_FLOW_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
off_t read = data->file->Read(data->inputBuffer + unconsumedBytes,
|
off_t read = data->file->Read(data->inputBuffer + unconsumedBytes,
|
||||||
INPUT_BUFFER_SIZE - unconsumedBytes);
|
INPUT_BUFFER_SIZE - unconsumedBytes);
|
||||||
|
|
||||||
mad_stream_buffer(stream, data->inputBuffer, read + unconsumedBytes);
|
mad_stream_buffer(stream, data->inputBuffer, read + unconsumedBytes);
|
||||||
|
|
||||||
|
data->inputBufferFill = int(read + unconsumedBytes);
|
||||||
|
|
||||||
return MAD_FLOW_CONTINUE;
|
return MAD_FLOW_CONTINUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user