Mercurial > hg > svcore
comparison data/fileio/MP3FileReader.cpp @ 1290:fa574c909c3d 3.0-integration
Add MAD_BUFFER_GUARD padding at end of mp3 buffer, in order to ensure last frame is decoded successfully (otherwise the decoded audio is truncated). Another thing learned from madplay.
author | Chris Cannam |
---|---|
date | Thu, 24 Nov 2016 17:06:31 +0000 |
parents | a45312bd9306 |
children | 9f9f55a8af92 |
comparison
equal
deleted
inserted
replaced
1289:a45312bd9306 | 1290:fa574c909c3d |
---|---|
71 return; | 71 return; |
72 } | 72 } |
73 | 73 |
74 m_fileSize = stat.st_size; | 74 m_fileSize = stat.st_size; |
75 | 75 |
76 m_filebuffer = 0; | 76 m_fileBuffer = 0; |
77 m_samplebuffer = 0; | 77 m_fileBufferSize = 0; |
78 m_samplebuffersize = 0; | 78 |
79 m_sampleBuffer = 0; | |
80 m_sampleBufferSize = 0; | |
79 | 81 |
80 int fd = -1; | 82 int fd = -1; |
81 if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY | 83 if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY |
82 #ifdef _WIN32 | 84 #ifdef _WIN32 |
83 | O_BINARY | 85 | O_BINARY |
86 m_error = QString("Failed to open file %1 for reading.").arg(m_path); | 88 m_error = QString("Failed to open file %1 for reading.").arg(m_path); |
87 return; | 89 return; |
88 } | 90 } |
89 | 91 |
90 try { | 92 try { |
91 m_filebuffer = new unsigned char[m_fileSize]; | 93 // We need a mysterious MAD_BUFFER_GUARD (== 8) zero bytes at |
94 // end of input, to ensure libmad decodes the last frame | |
95 // correctly. Otherwise the decoded audio is truncated. | |
96 m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD; | |
97 m_fileBuffer = new unsigned char[m_fileBufferSize]; | |
98 memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD); | |
92 } catch (...) { | 99 } catch (...) { |
93 m_error = QString("Out of memory"); | 100 m_error = QString("Out of memory"); |
94 ::close(fd); | 101 ::close(fd); |
95 return; | 102 return; |
96 } | 103 } |
97 | 104 |
98 ssize_t sz = 0; | 105 ssize_t sz = 0; |
99 ssize_t offset = 0; | 106 ssize_t offset = 0; |
100 while (offset < m_fileSize) { | 107 while (offset < m_fileSize) { |
101 sz = ::read(fd, m_filebuffer + offset, m_fileSize - offset); | 108 sz = ::read(fd, m_fileBuffer + offset, m_fileSize - offset); |
102 if (sz < 0) { | 109 if (sz < 0) { |
103 m_error = QString("Read error for file %1 (after %2 bytes)") | 110 m_error = QString("Read error for file %1 (after %2 bytes)") |
104 .arg(m_path).arg(offset); | 111 .arg(m_path).arg(offset); |
105 delete[] m_filebuffer; | 112 delete[] m_fileBuffer; |
106 ::close(fd); | 113 ::close(fd); |
107 return; | 114 return; |
108 } else if (sz == 0) { | 115 } else if (sz == 0) { |
109 cerr << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes") | 116 SVCERR << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes") |
110 .arg(offset).arg(m_fileSize) << endl; | 117 .arg(offset).arg(m_fileSize) << endl; |
111 m_fileSize = offset; | 118 m_fileSize = offset; |
119 m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD; | |
120 memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD); | |
112 break; | 121 break; |
113 } | 122 } |
114 offset += sz; | 123 offset += sz; |
115 } | 124 } |
116 | 125 |
124 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); | 133 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); |
125 m_reporter->setMessage | 134 m_reporter->setMessage |
126 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName())); | 135 (tr("Decoding %1...").arg(QFileInfo(m_path).fileName())); |
127 } | 136 } |
128 | 137 |
129 if (!decode(m_filebuffer, m_fileSize)) { | 138 if (!decode(m_fileBuffer, m_fileBufferSize)) { |
130 m_error = QString("Failed to decode file %1.").arg(m_path); | 139 m_error = QString("Failed to decode file %1.").arg(m_path); |
131 } | 140 } |
132 | 141 |
133 delete[] m_filebuffer; | 142 delete[] m_fileBuffer; |
134 m_filebuffer = 0; | 143 m_fileBuffer = 0; |
135 | 144 |
136 if (isDecodeCacheInitialised()) finishDecodeCache(); | 145 if (isDecodeCacheInitialised()) finishDecodeCache(); |
137 endSerialised(); | 146 endSerialised(); |
138 | 147 |
139 } else { | 148 } else { |
265 } | 274 } |
266 | 275 |
267 void | 276 void |
268 MP3FileReader::DecodeThread::run() | 277 MP3FileReader::DecodeThread::run() |
269 { | 278 { |
270 if (!m_reader->decode(m_reader->m_filebuffer, m_reader->m_fileSize)) { | 279 if (!m_reader->decode(m_reader->m_fileBuffer, m_reader->m_fileBufferSize)) { |
271 m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path); | 280 m_reader->m_error = QString("Failed to decode file %1.").arg(m_reader->m_path); |
272 } | 281 } |
273 | 282 |
274 delete[] m_reader->m_filebuffer; | 283 delete[] m_reader->m_fileBuffer; |
275 m_reader->m_filebuffer = 0; | 284 m_reader->m_fileBuffer = 0; |
276 | 285 |
277 if (m_reader->m_samplebuffer) { | 286 if (m_reader->m_sampleBuffer) { |
278 for (int c = 0; c < m_reader->m_channelCount; ++c) { | 287 for (int c = 0; c < m_reader->m_channelCount; ++c) { |
279 delete[] m_reader->m_samplebuffer[c]; | 288 delete[] m_reader->m_sampleBuffer[c]; |
280 } | 289 } |
281 delete[] m_reader->m_samplebuffer; | 290 delete[] m_reader->m_sampleBuffer; |
282 m_reader->m_samplebuffer = 0; | 291 m_reader->m_sampleBuffer = 0; |
283 } | 292 } |
284 | 293 |
285 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache(); | 294 if (m_reader->isDecodeCacheInitialised()) m_reader->finishDecodeCache(); |
286 | 295 |
287 m_reader->m_done = true; | 296 m_reader->m_done = true; |
428 m_completion = p; | 437 m_completion = p; |
429 m_reporter->setProgress(m_completion); | 438 m_reporter->setProgress(m_completion); |
430 } | 439 } |
431 } | 440 } |
432 | 441 |
433 if (m_cancelled) return MAD_FLOW_STOP; | 442 if (m_cancelled) { |
443 SVDEBUG << "MP3FileReader: Decoding cancelled" << endl; | |
444 return MAD_FLOW_STOP; | |
445 } | |
434 | 446 |
435 if (!isDecodeCacheInitialised()) { | 447 if (!isDecodeCacheInitialised()) { |
436 initialiseDecodeCache(); | 448 initialiseDecodeCache(); |
437 } | 449 } |
438 | 450 |
439 if (int(m_samplebuffersize) < frames) { | 451 if (m_sampleBufferSize < size_t(frames)) { |
440 if (!m_samplebuffer) { | 452 if (!m_sampleBuffer) { |
441 m_samplebuffer = new float *[channels]; | 453 m_sampleBuffer = new float *[channels]; |
442 for (int c = 0; c < channels; ++c) { | 454 for (int c = 0; c < channels; ++c) { |
443 m_samplebuffer[c] = 0; | 455 m_sampleBuffer[c] = 0; |
444 } | 456 } |
445 } | 457 } |
446 for (int c = 0; c < channels; ++c) { | 458 for (int c = 0; c < channels; ++c) { |
447 delete[] m_samplebuffer[c]; | 459 delete[] m_sampleBuffer[c]; |
448 m_samplebuffer[c] = new float[frames]; | 460 m_sampleBuffer[c] = new float[frames]; |
449 } | 461 } |
450 m_samplebuffersize = frames; | 462 m_sampleBufferSize = frames; |
451 } | 463 } |
452 | 464 |
453 int activeChannels = int(sizeof(pcm->samples) / sizeof(pcm->samples[0])); | 465 int activeChannels = int(sizeof(pcm->samples) / sizeof(pcm->samples[0])); |
454 | 466 |
455 for (int ch = 0; ch < channels; ++ch) { | 467 for (int ch = 0; ch < channels; ++ch) { |
460 if (ch < activeChannels) { | 472 if (ch < activeChannels) { |
461 sample = pcm->samples[ch][i]; | 473 sample = pcm->samples[ch][i]; |
462 } | 474 } |
463 float fsample = float(sample) / float(MAD_F_ONE); | 475 float fsample = float(sample) / float(MAD_F_ONE); |
464 | 476 |
465 m_samplebuffer[ch][i] = fsample; | 477 m_sampleBuffer[ch][i] = fsample; |
466 } | 478 } |
467 } | 479 } |
468 | 480 |
469 addSamplesToDecodeCache(m_samplebuffer, frames); | 481 addSamplesToDecodeCache(m_sampleBuffer, frames); |
470 | 482 |
471 ++m_mp3FrameCount; | 483 ++m_mp3FrameCount; |
472 | 484 |
473 return MAD_FLOW_CONTINUE; | 485 return MAD_FLOW_CONTINUE; |
474 } | 486 } |
477 MP3FileReader::error_callback(void *dp, | 489 MP3FileReader::error_callback(void *dp, |
478 struct mad_stream *stream, | 490 struct mad_stream *stream, |
479 struct mad_frame *) | 491 struct mad_frame *) |
480 { | 492 { |
481 DecoderData *data = (DecoderData *)dp; | 493 DecoderData *data = (DecoderData *)dp; |
494 | |
495 if (stream->error == MAD_ERROR_LOSTSYNC && | |
496 data->length == 0) { | |
497 // We are at end of file, losing sync is expected behaviour | |
498 return MAD_FLOW_CONTINUE; | |
499 } | |
500 | |
482 if (!data->reader->m_decodeErrorShown) { | 501 if (!data->reader->m_decodeErrorShown) { |
483 char buffer[256]; | 502 char buffer[256]; |
484 snprintf(buffer, 255, | 503 snprintf(buffer, 255, |
485 "MP3 decoding error 0x%04x (%s) at byte offset %lu", | 504 "MP3 decoding error 0x%04x (%s) at byte offset %lu", |
486 stream->error, mad_stream_errorstr(stream), | 505 stream->error, mad_stream_errorstr(stream), |