# HG changeset patch # User Chris Cannam # Date 1483720811 0 # Node ID b3cb0edc25cda004642a25f102872dac5b22ef6f # Parent 281a8c9d4886a0425ffd012681b7346da51e65cb Update WAV/MP3/BZipFileDevice code to avoid using local 8-bit encoding diff -r 281a8c9d4886 -r b3cb0edc25cd data/fileio/BZipFileDevice.cpp --- a/data/fileio/BZipFileDevice.cpp Fri Jan 06 15:53:02 2017 +0000 +++ b/data/fileio/BZipFileDevice.cpp Fri Jan 06 16:40:11 2017 +0000 @@ -23,6 +23,7 @@ BZipFileDevice::BZipFileDevice(QString fileName) : m_fileName(fileName), + m_qfile(fileName), m_file(0), m_bzFile(0), m_atEnd(true), @@ -72,9 +73,16 @@ if (mode & WriteOnly) { - m_file = fopen(m_fileName.toLocal8Bit().data(), "wb"); + if (!m_qfile.open(QIODevice::WriteOnly)) { + setErrorString(tr("Failed to open file for writing")); + m_ok = false; + return false; + } + + m_file = fdopen(m_qfile.handle(), "wb"); if (!m_file) { - setErrorString(tr("Failed to open file for writing")); + setErrorString(tr("Failed to open file handle for writing")); + m_qfile.close(); m_ok = false; return false; } @@ -85,6 +93,7 @@ if (!m_bzFile) { fclose(m_file); m_file = 0; + m_qfile.close(); setErrorString(tr("Failed to open bzip2 stream for writing")); m_ok = false; return false; @@ -99,9 +108,15 @@ if (mode & ReadOnly) { - m_file = fopen(m_fileName.toLocal8Bit().data(), "rb"); + if (!m_qfile.open(QIODevice::ReadOnly)) { + setErrorString(tr("Failed to open file for reading")); + m_ok = false; + return false; + } + + m_file = fdopen(m_qfile.handle(), "rb"); if (!m_file) { - setErrorString(tr("Failed to open file for reading")); + setErrorString(tr("Failed to open file handle for reading")); m_ok = false; return false; } @@ -112,6 +127,7 @@ if (!m_bzFile) { fclose(m_file); m_file = 0; + m_qfile.close(); setErrorString(tr("Failed to open bzip2 stream for reading")); m_ok = false; return false; @@ -150,6 +166,7 @@ setErrorString(tr("bzip2 stream write close error")); } fclose(m_file); + m_qfile.close(); m_bzFile = 0; m_file = 0; m_ok = false; @@ -162,6 +179,7 @@ setErrorString(tr("bzip2 stream read close error")); } fclose(m_file); + m_qfile.close(); m_bzFile = 0; m_file = 0; m_ok = false; diff -r 281a8c9d4886 -r b3cb0edc25cd data/fileio/BZipFileDevice.h --- a/data/fileio/BZipFileDevice.h Fri Jan 06 15:53:02 2017 +0000 +++ b/data/fileio/BZipFileDevice.h Fri Jan 06 16:40:11 2017 +0000 @@ -13,10 +13,11 @@ COPYING included with this distribution for more information. */ -#ifndef _BZIP_FILE_DEVICE_H_ -#define _BZIP_FILE_DEVICE_H_ +#ifndef SV_BZIP_FILE_DEVICE_H +#define SV_BZIP_FILE_DEVICE_H #include +#include #include @@ -41,6 +42,7 @@ QString m_fileName; + QFile m_qfile; FILE *m_file; BZFILE *m_bzFile; bool m_atEnd; diff -r 281a8c9d4886 -r b3cb0edc25cd data/fileio/MP3FileReader.cpp --- a/data/fileio/MP3FileReader.cpp Fri Jan 06 15:53:02 2017 +0000 +++ b/data/fileio/MP3FileReader.cpp Fri Jan 06 16:40:11 2017 +0000 @@ -77,16 +77,7 @@ CodedAudioFileReader::setFramesToTrim(DEFAULT_DECODER_DELAY, 0); } - SVDEBUG << "Codec for locale is " << (const char *)(QTextCodec::codecForLocale()->name().data()) << endl; - - struct stat stat; - if (::stat(m_path.toLocal8Bit().data(), &stat) == -1 || stat.st_size == 0) { - m_error = QString("File %1 does not exist.").arg(m_path); - SVDEBUG << "MP3FileReader: " << m_error << endl; - return; - } - - m_fileSize = stat.st_size; + m_fileSize = 0; m_fileBuffer = 0; m_fileBufferSize = 0; @@ -94,17 +85,15 @@ m_sampleBuffer = 0; m_sampleBufferSize = 0; - int fd = -1; - if ((fd = ::open(m_path.toLocal8Bit().data(), O_RDONLY -#ifdef _WIN32 - | O_BINARY -#endif - , 0)) < 0) { + QFile qfile(m_path); + if (!qfile.open(QIODevice::ReadOnly)) { m_error = QString("Failed to open file %1 for reading.").arg(m_path); SVDEBUG << "MP3FileReader: " << m_error << endl; return; } + m_fileSize = qfile.size(); + try { // We need a mysterious MAD_BUFFER_GUARD (== 8) zero bytes at // end of input, to ensure libmad decodes the last frame @@ -115,35 +104,22 @@ } catch (...) { m_error = QString("Out of memory"); SVDEBUG << "MP3FileReader: " << m_error << endl; - ::close(fd); return; } - - ssize_t sz = 0; - ssize_t offset = 0; - while (offset < m_fileSize) { - sz = ::read(fd, m_fileBuffer + offset, m_fileSize - offset); - if (sz < 0) { - m_error = QString("Read error for file %1 (after %2 bytes)") - .arg(m_path).arg(offset); - delete[] m_fileBuffer; - ::close(fd); - SVDEBUG << "MP3FileReader: " << m_error << endl; - return; - } else if (sz == 0) { - SVCERR << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes") - .arg(offset).arg(m_fileSize) << endl; - m_fileSize = offset; - m_fileBufferSize = m_fileSize + MAD_BUFFER_GUARD; - memset(m_fileBuffer + m_fileSize, 0, MAD_BUFFER_GUARD); - break; - } - offset += sz; + + auto amountRead = qfile.read(reinterpret_cast(m_fileBuffer), + m_fileSize); + + if (amountRead < m_fileSize) { + SVCERR << QString("MP3FileReader::MP3FileReader: Warning: reached EOF after only %1 of %2 bytes") + .arg(amountRead).arg(m_fileSize) << endl; + memset(m_fileBuffer + amountRead, 0, m_fileSize - amountRead); + m_fileSize = amountRead; } + + loadTags(qfile.handle()); - ::close(fd); - - loadTags(); + qfile.close(); if (decodeMode == DecodeAtOnce) { @@ -199,14 +175,13 @@ } void -MP3FileReader::loadTags() +MP3FileReader::loadTags(int fd) { m_title = ""; #ifdef HAVE_ID3TAG - id3_file *file = id3_file_open(m_path.toLocal8Bit().data(), - ID3_FILE_MODE_READONLY); + id3_file *file = id3_file_fdopen(fd, ID3_FILE_MODE_READONLY); if (!file) return; // We can do this a lot more elegantly, but we'll leave that for @@ -236,7 +211,8 @@ } } - id3_file_close(file); + // We don't id3_file_close(file) because that closes the fd, which + // was only lent to us #else SVDEBUG << "MP3FileReader::loadTags: ID3 tag support not compiled in" << endl; diff -r 281a8c9d4886 -r b3cb0edc25cd data/fileio/MP3FileReader.h --- a/data/fileio/MP3FileReader.h Fri Jan 06 15:53:02 2017 +0000 +++ b/data/fileio/MP3FileReader.h Fri Jan 06 16:40:11 2017 +0000 @@ -152,7 +152,7 @@ DecodeThread *m_decodeThread; - void loadTags(); + void loadTags(int fd); QString loadTag(void *vtag, const char *name); }; diff -r 281a8c9d4886 -r b3cb0edc25cd data/fileio/WavFileReader.cpp --- a/data/fileio/WavFileReader.cpp Fri Jan 06 15:53:02 2017 +0000 +++ b/data/fileio/WavFileReader.cpp Fri Jan 06 16:40:11 2017 +0000 @@ -26,9 +26,10 @@ using namespace std; WavFileReader::WavFileReader(FileSource source, bool fileUpdating) : - m_file(0), + m_sndfile(0), m_source(source), m_path(source.getLocalFilename()), + m_qfile(m_path), m_seekable(false), m_lastStart(0), m_lastCount(0), @@ -40,19 +41,24 @@ m_fileInfo.format = 0; m_fileInfo.frames = 0; - m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo); - if (!m_file || (!fileUpdating && m_fileInfo.channels <= 0)) { + if (!m_qfile.open(QIODevice::ReadOnly)) { SVDEBUG << "WavFileReader::initialize: Failed to open file at \"" - << m_path << "\" (" - << sf_strerror(m_file) << ")" << endl; + << m_path << "\"" << endl; + m_error = QString("Failed to open audio file '%1'").arg(m_path); + return; + } + + m_sndfile = sf_open_fd(m_qfile.handle(), SFM_READ, &m_fileInfo, false); - if (m_file) { + if (!m_sndfile || (!fileUpdating && m_fileInfo.channels <= 0)) { + SVDEBUG << "WavFileReader::initialize: Failed to open file at \"" + << m_path << "\" (" << sf_strerror(m_sndfile) << ")" << endl; + if (m_sndfile) { m_error = QString("Couldn't load audio file '%1':\n%2") - .arg(m_path).arg(sf_strerror(m_file)); + .arg(m_path).arg(sf_strerror(m_sndfile)); } else { - m_error = QString("Failed to open audio file '%1'") - .arg(m_path); + m_error = QString("Failed to open audio file '%1'").arg(m_path); } return; } @@ -89,7 +95,7 @@ WavFileReader::~WavFileReader() { - if (m_file) sf_close(m_file); + if (m_sndfile) sf_close(m_sndfile); } void @@ -99,12 +105,12 @@ sv_frame_t prevCount = m_fileInfo.frames; - if (m_file) { - sf_close(m_file); - m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo); - if (!m_file || m_fileInfo.channels <= 0) { - SVDEBUG << "WavFileReader::updateFrameCount: Failed to open file at \"" << m_path << "\" (" - << sf_strerror(m_file) << ")" << endl; + if (m_sndfile) { + sf_close(m_sndfile); + m_sndfile = sf_open_fd(m_qfile.handle(), SFM_READ, &m_fileInfo, false); + if (!m_sndfile || m_fileInfo.channels <= 0) { + SVCERR << "WavFileReader::updateFrameCount: Failed to reopen file at \"" << m_path << "\" (" + << sf_strerror(m_sndfile) << ")" << endl; } } @@ -140,7 +146,7 @@ Profiler profiler("WavFileReader::getInterleavedFrames"); - if (!m_file || !m_channelCount) { + if (!m_sndfile || !m_channelCount) { return {}; } @@ -171,7 +177,7 @@ lastRead.miss(); } - if (sf_seek(m_file, start, SEEK_SET) < 0) { + if (sf_seek(m_sndfile, start, SEEK_SET) < 0) { return {}; } @@ -183,7 +189,7 @@ m_lastCount = count; sf_count_t readCount = 0; - if ((readCount = sf_readf_float(m_file, data.data(), count)) < 0) { + if ((readCount = sf_readf_float(m_sndfile, data.data(), count)) < 0) { return {}; } diff -r 281a8c9d4886 -r b3cb0edc25cd data/fileio/WavFileReader.h --- a/data/fileio/WavFileReader.h Fri Jan 06 15:53:02 2017 +0000 +++ b/data/fileio/WavFileReader.h Fri Jan 06 16:40:11 2017 +0000 @@ -13,13 +13,14 @@ COPYING included with this distribution for more information. */ -#ifndef _WAV_FILE_READER_H_ -#define _WAV_FILE_READER_H_ +#ifndef SV_WAV_FILE_READER_H +#define SV_WAV_FILE_READER_H #include "AudioFileReader.h" #include #include +#include #include @@ -66,11 +67,12 @@ protected: SF_INFO m_fileInfo; - SNDFILE *m_file; + SNDFILE *m_sndfile; FileSource m_source; QString m_path; QString m_error; + QFile m_qfile; bool m_seekable; diff -r 281a8c9d4886 -r b3cb0edc25cd data/fileio/WavFileWriter.cpp --- a/data/fileio/WavFileWriter.cpp Fri Jan 06 15:53:02 2017 +0000 +++ b/data/fileio/WavFileWriter.cpp Fri Jan 06 16:40:11 2017 +0000 @@ -35,15 +35,16 @@ m_sampleRate(sampleRate), m_channels(channels), m_temp(0), - m_file(0) + m_sndfile(0), + m_qfile(0) { SF_INFO fileInfo; int fileRate = int(round(m_sampleRate)); if (m_sampleRate != sv_samplerate_t(fileRate)) { - cerr << "WavFileWriter: WARNING: Non-integer sample rate " - << m_sampleRate << " presented, rounding to " << fileRate - << endl; + SVCERR << "WavFileWriter: WARNING: Non-integer sample rate " + << m_sampleRate << " presented, rounding to " << fileRate + << endl; } fileInfo.samplerate = fileRate; fileInfo.channels = m_channels; @@ -52,33 +53,35 @@ try { if (mode == WriteToTemporary) { m_temp = new TempWriteFile(m_path); - m_file = sf_open(m_temp->getTemporaryFilename().toLocal8Bit(), - SFM_WRITE, &fileInfo); - if (!m_file) { - cerr << "WavFileWriter: Failed to open file (" - << sf_strerror(m_file) << ")" << endl; + m_qfile = new QFile(m_temp->getTemporaryFilename()); + } else { + m_qfile = new QFile(m_path); + } + if (!m_qfile->open(QIODevice::WriteOnly)) { + SVCERR << "WavFileWriter: Failed to open file for writing" << endl; + m_error = QString("Failed to open audio file '%1' for writing") + .arg(m_qfile->fileName()); + } else { + m_sndfile = sf_open_fd(m_qfile->handle(), + SFM_WRITE, &fileInfo, false); + if (!m_sndfile) { + SVCERR << "WavFileWriter: Failed to open file (" + << sf_strerror(m_sndfile) << ")" << endl; m_error = QString("Failed to open audio file '%1' for writing") - .arg(m_temp->getTemporaryFilename()); + .arg(m_qfile->fileName()); } - } else { - m_file = sf_open(m_path.toLocal8Bit(), SFM_WRITE, &fileInfo); - if (!m_file) { - cerr << "WavFileWriter: Failed to open file (" - << sf_strerror(m_file) << ")" << endl; - m_error = QString("Failed to open audio file '%1' for writing") - .arg(m_path); - } - } + } } catch (FileOperationFailed &f) { m_error = f.what(); m_temp = 0; - m_file = 0; + m_qfile = 0; + m_sndfile = 0; } } WavFileWriter::~WavFileWriter() { - if (m_file) close(); + if (m_sndfile) close(); } bool @@ -116,7 +119,7 @@ return false; } - if (!m_file) { + if (!m_sndfile) { m_error = QString("Failed to write model to audio file '%1': File not open") .arg(getWriteFilename()); return false; @@ -150,7 +153,7 @@ } } - sf_count_t written = sf_writef_float(m_file, interleaved.data(), n); + sf_count_t written = sf_writef_float(m_sndfile, interleaved.data(), n); if (written < n) { m_error = QString("Only wrote %1 of %2 frames at file frame %3") @@ -168,7 +171,7 @@ bool WavFileWriter::writeSamples(const float *const *samples, sv_frame_t count) { - if (!m_file) { + if (!m_sndfile) { m_error = QString("Failed to write model to audio file '%1': File not open") .arg(getWriteFilename()); return false; @@ -181,7 +184,7 @@ } } - sv_frame_t written = sf_writef_float(m_file, b, count); + sv_frame_t written = sf_writef_float(m_sndfile, b, count); delete[] b; @@ -196,15 +199,20 @@ bool WavFileWriter::close() { - if (m_file) { - sf_close(m_file); - m_file = 0; + if (m_sndfile) { + sf_close(m_sndfile); + m_sndfile = 0; } + + delete m_qfile; + m_qfile = 0; + if (m_temp) { m_temp->moveToTarget(); delete m_temp; m_temp = 0; } + return true; } diff -r 281a8c9d4886 -r b3cb0edc25cd data/fileio/WavFileWriter.h --- a/data/fileio/WavFileWriter.h Fri Jan 06 15:53:02 2017 +0000 +++ b/data/fileio/WavFileWriter.h Fri Jan 06 16:40:11 2017 +0000 @@ -13,10 +13,11 @@ COPYING included with this distribution for more information. */ -#ifndef _WAV_FILE_WRITER_H_ -#define _WAV_FILE_WRITER_H_ +#ifndef SV_WAV_FILE_WRITER_H +#define SV_WAV_FILE_WRITER_H #include +#include #include @@ -68,8 +69,9 @@ sv_samplerate_t m_sampleRate; int m_channels; TempWriteFile *m_temp; - SNDFILE *m_file; + SNDFILE *m_sndfile; QString m_error; + QFile *m_qfile; QString getWriteFilename() const; };