Mercurial > hg > svcore
comparison data/fileio/MP3FileReader.cpp @ 1527:710e6250a401 zoom
Merge from default branch
author | Chris Cannam |
---|---|
date | Mon, 17 Sep 2018 13:51:14 +0100 |
parents | aadfb395e933 |
children | 70e172e6cc59 |
comparison
equal
deleted
inserted
replaced
1324:d4a28d1479a8 | 1527:710e6250a401 |
---|---|
30 | 30 |
31 #ifdef HAVE_ID3TAG | 31 #ifdef HAVE_ID3TAG |
32 #include <id3tag.h> | 32 #include <id3tag.h> |
33 #endif | 33 #endif |
34 | 34 |
35 #ifdef _WIN32 | |
36 #include <io.h> | |
37 #include <fcntl.h> | |
38 #else | |
39 #include <fcntl.h> | |
40 #include <unistd.h> | |
41 #endif | |
42 | |
35 #include <QFileInfo> | 43 #include <QFileInfo> |
36 | 44 |
37 #ifdef _MSC_VER | 45 #include <QTextCodec> |
38 #include <io.h> | |
39 #define open _open | |
40 #endif | |
41 | 46 |
42 using std::string; | 47 using std::string; |
43 | 48 |
44 static sv_frame_t DEFAULT_DECODER_DELAY = 529; | 49 static sv_frame_t DEFAULT_DECODER_DELAY = 529; |
45 | 50 |
73 | 78 |
74 if (m_gaplessMode == GaplessMode::Gapless) { | 79 if (m_gaplessMode == GaplessMode::Gapless) { |
75 CodedAudioFileReader::setFramesToTrim(DEFAULT_DECODER_DELAY, 0); | 80 CodedAudioFileReader::setFramesToTrim(DEFAULT_DECODER_DELAY, 0); |
76 } | 81 } |
77 | 82 |
78 struct stat stat; | 83 m_fileSize = 0; |
79 if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) { | |
80 m_error = QString("File %1 does not exist.").arg(m_path); | |
81 return; | |
82 } | |
83 | |
84 m_fileSize = stat.st_size; | |
85 | 84 |
86 m_fileBuffer = 0; | 85 m_fileBuffer = 0; |
87 m_fileBufferSize = 0; | 86 m_fileBufferSize = 0; |
88 | 87 |
89 m_sampleBuffer = 0; | 88 m_sampleBuffer = 0; |
90 m_sampleBufferSize = 0; | 89 m_sampleBufferSize = 0; |
91 | 90 |
92 int fd = -1; | 91 QFile qfile(m_path); |
93 if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY | 92 if (!qfile.open(QIODevice::ReadOnly)) { |
94 #ifdef _WIN32 | 93 m_error = QString("Failed to open file %1 for reading.").arg(m_path); |
95 | O_BINARY | 94 SVDEBUG << "MP3FileReader: " << m_error << endl; |
96 #endif | 95 return; |
97 , 0)) < 0) { | 96 } |
98 m_error = QString("Failed to open file %1 for reading.").arg(m_path); | 97 |
99 return; | 98 m_fileSize = qfile.size(); |
100 } | 99 |
101 | |
102 try { | 100 try { |
103 // We need a mysterious MAD_BUFFER_GUARD (== 8) zero bytes at | 101 // We need a mysterious MAD_BUFFER_GUARD (== 8) zero bytes at |
104 // end of input, to ensure libmad decodes the last frame | 102 // end of input, to ensure libmad decodes the last frame |
105 // correctly. Otherwise the decoded audio is truncated. | 103 // correctly. Otherwise the decoded audio is truncated. |
104 SVDEBUG << "file size = " << m_fileSize << ", buffer guard = " << MAD_BUFFER_GUARD << endl; | |
106 m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD; | 105 m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD; |
107 m_fileBuffer = new unsigned char[m_fileBufferSize]; | 106 m_fileBuffer = new unsigned char[m_fileBufferSize]; |
108 memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD); | 107 memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD); |
109 } catch (...) { | 108 } catch (...) { |
110 m_error = QString("Out of memory"); | 109 m_error = QString("Out of memory"); |
111 ::close(fd); | 110 SVDEBUG << "MP3FileReader: " << m_error << endl; |
112 return; | 111 return; |
113 } | 112 } |
114 | 113 |
115 ssize_t sz = 0; | 114 auto amountRead = qfile.read(reinterpret_cast<char *>(m_fileBuffer), |
116 ssize_t offset = 0; | 115 m_fileSize); |
117 while (offset < m_fileSize) { | 116 |
118 sz = ::read(fd, m_fileBuffer + offset, m_fileSize - offset); | 117 if (amountRead < m_fileSize) { |
119 if (sz < 0) { | 118 SVCERR << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes") |
120 m_error = QString("Read error for file %1 (after %2 bytes)") | 119 .arg(amountRead).arg(m_fileSize) << endl; |
121 .arg(m_path).arg(offset); | 120 memset(m_fileBuffer + amountRead, 0, m_fileSize - amountRead); |
122 delete[] m_fileBuffer; | 121 m_fileSize = amountRead; |
123 ::close(fd); | 122 } |
124 return; | 123 |
125 } else if (sz == 0) { | 124 loadTags(qfile.handle()); |
126 SVCERR << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes") | 125 |
127 .arg(offset).arg(m_fileSize) << endl; | 126 qfile.close(); |
128 m_fileSize = offset; | |
129 m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD; | |
130 memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD); | |
131 break; | |
132 } | |
133 offset += sz; | |
134 } | |
135 | |
136 ::close(fd); | |
137 | |
138 loadTags(); | |
139 | 127 |
140 if (decodeMode == DecodeAtOnce) { | 128 if (decodeMode == DecodeAtOnce) { |
141 | 129 |
142 if (m_reporter) { | 130 if (m_reporter) { |
143 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); | 131 connect(m_reporter, SIGNAL(cancelled()), this, SLOT(cancelled())); |
146 } | 134 } |
147 | 135 |
148 if (!decode(m_fileBuffer, m_fileBufferSize)) { | 136 if (!decode(m_fileBuffer, m_fileBufferSize)) { |
149 m_error = QString("Failed to decode file %1.").arg(m_path); | 137 m_error = QString("Failed to decode file %1.").arg(m_path); |
150 } | 138 } |
139 | |
140 if (m_sampleBuffer) { | |
141 for (int c = 0; c < m_channelCount; ++c) { | |
142 delete[] m_sampleBuffer[c]; | |
143 } | |
144 delete[] m_sampleBuffer; | |
145 m_sampleBuffer = 0; | |
146 } | |
151 | 147 |
152 delete[] m_fileBuffer; | 148 delete[] m_fileBuffer; |
153 m_fileBuffer = 0; | 149 m_fileBuffer = 0; |
154 | 150 |
155 if (isDecodeCacheInitialised()) finishDecodeCache(); | 151 if (isDecodeCacheInitialised()) finishDecodeCache(); |
189 { | 185 { |
190 m_cancelled = true; | 186 m_cancelled = true; |
191 } | 187 } |
192 | 188 |
193 void | 189 void |
194 MP3FileReader::loadTags() | 190 MP3FileReader::loadTags(int fd) |
195 { | 191 { |
196 m_title = ""; | 192 m_title = ""; |
197 | 193 |
198 #ifdef HAVE_ID3TAG | 194 #ifdef HAVE_ID3TAG |
199 | 195 |
200 id3_file *file = id3_file_open(m_path.toLocal8Bit().data(), | 196 #ifdef _WIN32 |
201 ID3_FILE_MODE_READONLY); | 197 int id3fd = _dup(fd); |
198 #else | |
199 int id3fd = dup(fd); | |
200 #endif | |
201 | |
202 id3_file *file = id3_file_fdopen(id3fd, ID3_FILE_MODE_READONLY); | |
202 if (!file) return; | 203 if (!file) return; |
203 | 204 |
204 // We can do this a lot more elegantly, but we'll leave that for | 205 // We can do this a lot more elegantly, but we'll leave that for |
205 // when we implement support for more than just the one tag! | 206 // when we implement support for more than just the one tag! |
206 | 207 |
207 id3_tag *tag = id3_file_tag(file); | 208 id3_tag *tag = id3_file_tag(file); |
208 if (!tag) { | 209 if (!tag) { |
209 SVDEBUG << "MP3FileReader::loadTags: No ID3 tag found" << endl; | 210 SVDEBUG << "MP3FileReader::loadTags: No ID3 tag found" << endl; |
210 id3_file_close(file); | 211 id3_file_close(file); // also closes our dup'd fd |
211 return; | 212 return; |
212 } | 213 } |
213 | 214 |
214 m_title = loadTag(tag, "TIT2"); // work title | 215 m_title = loadTag(tag, "TIT2"); // work title |
215 if (m_title == "") m_title = loadTag(tag, "TIT1"); | 216 if (m_title == "") m_title = loadTag(tag, "TIT1"); |
226 m_tags[tag->frames[i]->id] = value; | 227 m_tags[tag->frames[i]->id] = value; |
227 } | 228 } |
228 } | 229 } |
229 } | 230 } |
230 | 231 |
231 id3_file_close(file); | 232 id3_file_close(file); // also closes our dup'd fd |
232 | 233 |
233 #else | 234 #else |
234 SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in" << endl; | 235 SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in" << endl; |
235 #endif | 236 #endif |
236 } | 237 } |
477 return data->reader->accept(header, pcm); | 478 return data->reader->accept(header, pcm); |
478 } | 479 } |
479 | 480 |
480 enum mad_flow | 481 enum mad_flow |
481 MP3FileReader::accept(struct mad_header const *header, | 482 MP3FileReader::accept(struct mad_header const *header, |
482 struct mad_pcm *pcm) | 483 struct mad_pcm *pcm) |
483 { | 484 { |
484 int channels = pcm->channels; | 485 int channels = pcm->channels; |
485 int frames = pcm->length; | 486 int frames = pcm->length; |
486 | 487 |
487 if (header) { | 488 if (header) { |
493 | 494 |
494 if (m_channelCount == 0) { | 495 if (m_channelCount == 0) { |
495 | 496 |
496 m_fileRate = pcm->samplerate; | 497 m_fileRate = pcm->samplerate; |
497 m_channelCount = channels; | 498 m_channelCount = channels; |
499 | |
500 SVDEBUG << "MP3FileReader::accept: file rate = " << pcm->samplerate | |
501 << ", channel count = " << channels << ", about to init " | |
502 << "decode cache" << endl; | |
498 | 503 |
499 initialiseDecodeCache(); | 504 initialiseDecodeCache(); |
500 | 505 |
501 if (m_cacheMode == CacheInTemporaryFile) { | 506 if (m_cacheMode == CacheInTemporaryFile) { |
502 // SVDEBUG << "MP3FileReader::accept: channel count " << m_channelCount << ", file rate " << m_fileRate << ", about to start serialised section" << endl; | 507 // SVDEBUG << "MP3FileReader::accept: channel count " << m_channelCount << ", file rate " << m_fileRate << ", about to start serialised section" << endl; |
523 SVDEBUG << "MP3FileReader: Decoding cancelled" << endl; | 528 SVDEBUG << "MP3FileReader: Decoding cancelled" << endl; |
524 return MAD_FLOW_STOP; | 529 return MAD_FLOW_STOP; |
525 } | 530 } |
526 | 531 |
527 if (!isDecodeCacheInitialised()) { | 532 if (!isDecodeCacheInitialised()) { |
533 SVDEBUG << "MP3FileReader::accept: fallback case: file rate = " << pcm->samplerate | |
534 << ", channel count = " << channels << ", about to init " | |
535 << "decode cache" << endl; | |
528 initialiseDecodeCache(); | 536 initialiseDecodeCache(); |
529 } | 537 } |
530 | 538 |
531 if (m_sampleBufferSize < size_t(frames)) { | 539 if (m_sampleBufferSize < size_t(frames)) { |
532 if (!m_sampleBuffer) { | 540 if (!m_sampleBuffer) { |
546 | 554 |
547 for (int ch = 0; ch < channels; ++ch) { | 555 for (int ch = 0; ch < channels; ++ch) { |
548 | 556 |
549 for (int i = 0; i < frames; ++i) { | 557 for (int i = 0; i < frames; ++i) { |
550 | 558 |
551 mad_fixed_t sample = 0; | 559 mad_fixed_t sample = 0; |
552 if (ch < activeChannels) { | 560 if (ch < activeChannels) { |
553 sample = pcm->samples[ch][i]; | 561 sample = pcm->samples[ch][i]; |
554 } | 562 } |
555 float fsample = float(sample) / float(MAD_F_ONE); | 563 float fsample = float(sample) / float(MAD_F_ONE); |
556 | 564 |
557 m_sampleBuffer[ch][i] = fsample; | 565 m_sampleBuffer[ch][i] = fsample; |
558 } | 566 } |
559 } | 567 } |
560 | 568 |
561 addSamplesToDecodeCache(m_sampleBuffer, frames); | 569 addSamplesToDecodeCache(m_sampleBuffer, frames); |
562 | 570 |
563 ++m_mp3FrameCount; | 571 ++m_mp3FrameCount; |
582 } | 590 } |
583 | 591 |
584 if (!data->reader->m_decodeErrorShown) { | 592 if (!data->reader->m_decodeErrorShown) { |
585 char buffer[256]; | 593 char buffer[256]; |
586 snprintf(buffer, 255, | 594 snprintf(buffer, 255, |
587 "MP3 decoding error 0x%04x (%s) at byte offset %lu", | 595 "MP3 decoding error 0x%04x (%s) at byte offset %lld", |
588 stream->error, mad_stream_errorstr(stream), ix); | 596 stream->error, mad_stream_errorstr(stream), (long long int)ix); |
589 SVCERR << "Warning: in file \"" << data->reader->m_path << "\": " | 597 SVCERR << "Warning: in file \"" << data->reader->m_path << "\": " |
590 << buffer << " (continuing; will not report any further decode errors for this file)" << endl; | 598 << buffer << " (continuing; will not report any further decode errors for this file)" << endl; |
591 data->reader->m_decodeErrorShown = true; | 599 data->reader->m_decodeErrorShown = true; |
592 } | 600 } |
593 | 601 |