changeset 1348:b3cb0edc25cd 3.0-integration

Update WAV/MP3/BZipFileDevice code to avoid using local 8-bit encoding
author Chris Cannam
date Fri, 06 Jan 2017 16:40:11 +0000
parents 281a8c9d4886
children 330bcc92507d
files data/fileio/BZipFileDevice.cpp data/fileio/BZipFileDevice.h data/fileio/MP3FileReader.cpp data/fileio/MP3FileReader.h data/fileio/WavFileReader.cpp data/fileio/WavFileReader.h data/fileio/WavFileWriter.cpp data/fileio/WavFileWriter.h
diffstat 8 files changed, 119 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
--- 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;
--- 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 <QIODevice>
+#include <QFile>
 
 #include <bzlib.h>
 
@@ -41,6 +42,7 @@
 
     QString m_fileName;
 
+    QFile m_qfile;
     FILE *m_file;
     BZFILE *m_bzFile;
     bool m_atEnd;
--- 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<char *>(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;
--- 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);
 };
 
--- 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 {};
     }
 
--- 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 <sndfile.h>
 #include <QMutex>
+#include <QFile>
 
 #include <set>
 
@@ -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;
 
--- 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;
 }
 
--- 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 <QString>
+#include <QFile>
 
 #include <sndfile.h>
 
@@ -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;
 };