changeset 684:bcca512445f3

Provide option for WavFileWriter to write directly to target file, rather than always using a temporary; make use of it in WritableWaveFileModel so we can read from target file without having to close it first
author Chris Cannam
date Wed, 11 May 2011 11:04:35 +0100
parents f84f147572b9
children be43b2fe68e8
files data/fileio/WavFileWriter.cpp data/fileio/WavFileWriter.h data/model/WritableWaveFileModel.cpp
diffstat 3 files changed, 64 insertions(+), 28 deletions(-) [+]
line wrap: on
line diff
--- a/data/fileio/WavFileWriter.cpp	Wed May 11 11:04:02 2011 +0100
+++ b/data/fileio/WavFileWriter.cpp	Wed May 11 11:04:35 2011 +0100
@@ -26,7 +26,8 @@
 
 WavFileWriter::WavFileWriter(QString path,
 			     size_t sampleRate,
-                             size_t channels) :
+                             size_t channels,
+                             FileWriteMode mode) :
     m_path(path),
     m_sampleRate(sampleRate),
     m_channels(channels),
@@ -39,15 +40,25 @@
     fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
 
     try {
-        m_temp = new TempWriteFile(m_path);
-        m_file = sf_open(m_temp->getTemporaryFilename().toLocal8Bit(),
-                         SFM_WRITE, &fileInfo);
-        if (!m_file) {
-            std::cerr << "WavFileWriter: Failed to open file ("
-                      << sf_strerror(m_file) << ")" << std::endl;
-            m_error = QString("Failed to open audio file '%1' for writing")
-                .arg(m_temp->getTemporaryFilename());
-        }
+        if (mode == WriteToTemporary) {
+            m_temp = new TempWriteFile(m_path);
+            m_file = sf_open(m_temp->getTemporaryFilename().toLocal8Bit(),
+                             SFM_WRITE, &fileInfo);
+            if (!m_file) {
+                std::cerr << "WavFileWriter: Failed to open file ("
+                          << sf_strerror(m_file) << ")" << std::endl;
+                m_error = QString("Failed to open audio file '%1' for writing")
+                    .arg(m_temp->getTemporaryFilename());
+            }
+        } else {
+            m_file = sf_open(m_path.toLocal8Bit(), SFM_WRITE, &fileInfo);
+            if (!m_file) {
+                std::cerr << "WavFileWriter: Failed to open file ("
+                          << sf_strerror(m_file) << ")" << std::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;
@@ -72,27 +83,32 @@
     return m_error;
 }
 
+QString
+WavFileWriter::getWriteFilename() const
+{
+    if (m_temp) {
+        return m_temp->getTemporaryFilename();
+    } else {
+        return m_path;
+    }
+}
+
 bool
 WavFileWriter::writeModel(DenseTimeValueModel *source,
                           MultiSelection *selection)
 {
-    if (!m_temp) {
-        m_error = QString("Failed to write model to audio file: No file open");
-        return false;
-    }
-
     if (source->getChannelCount() != m_channels) {
         std::cerr << "WavFileWriter::writeModel: Wrong number of channels ("
                   << source->getChannelCount()  << " != " << m_channels << ")"
                   << std::endl;
         m_error = QString("Failed to write model to audio file '%1'")
-            .arg(m_temp->getTemporaryFilename());
+            .arg(getWriteFilename());
         return false;
     }
 
     if (!m_file) {
         m_error = QString("Failed to write model to audio file '%1': File not open")
-            .arg(m_temp->getTemporaryFilename());
+            .arg(getWriteFilename());
 	return false;
     }
 
@@ -145,14 +161,9 @@
 bool
 WavFileWriter::writeSamples(float **samples, size_t count)
 {
-    if (!m_temp) {
-        m_error = QString("Failed to write model to audio file: No file open");
-        return false;
-    }
-
     if (!m_file) {
         m_error = QString("Failed to write model to audio file '%1': File not open")
-            .arg(m_temp->getTemporaryFilename());
+            .arg(getWriteFilename());
 	return false;
     }
 
@@ -182,9 +193,11 @@
         sf_close(m_file);
         m_file = 0;
     }
-    m_temp->moveToTarget();
-    delete m_temp;
-    m_temp = 0;
+    if (m_temp) {
+        m_temp->moveToTarget();
+        delete m_temp;
+        m_temp = 0;
+    }
     return true;
 }
 
--- a/data/fileio/WavFileWriter.h	Wed May 11 11:04:02 2011 +0100
+++ b/data/fileio/WavFileWriter.h	Wed May 11 11:04:35 2011 +0100
@@ -27,7 +27,25 @@
 class WavFileWriter
 {
 public:
-    WavFileWriter(QString path, size_t sampleRate, size_t channels);
+    /**
+     * Specify the method used to open the destination file.
+     * 
+     * If WriteToTemporary, the destination will be opened as a
+     * temporary file which is moved to the target location when the
+     * WavFileWriter is closed or deleted (to avoid clobbering an
+     * existing file with a partially written replacement).
+     * 
+     * If WriteToTarget, the target file will be opened directly
+     * (necessary when e.g. doing a series of incremental writes to a
+     * file while keeping it open for reading).
+     */
+    enum FileWriteMode {
+        WriteToTemporary,
+        WriteToTarget
+    };
+
+    WavFileWriter(QString path, size_t sampleRate, size_t channels,
+                  FileWriteMode mode);
     virtual ~WavFileWriter();
 
     bool isOK() const;
@@ -50,6 +68,8 @@
     TempWriteFile *m_temp;
     SNDFILE *m_file;
     QString m_error;
+
+    QString getWriteFilename() const;
 };
 
 
--- a/data/model/WritableWaveFileModel.cpp	Wed May 11 11:04:02 2011 +0100
+++ b/data/model/WritableWaveFileModel.cpp	Wed May 11 11:04:35 2011 +0100
@@ -52,7 +52,10 @@
         }
     }
 
-    m_writer = new WavFileWriter(path, sampleRate, channels);
+    // Write directly to the target file, so that we can do
+    // incremental writes and concurrent reads
+    m_writer = new WavFileWriter(path, sampleRate, channels,
+                                 WavFileWriter::WriteToTarget);
     if (!m_writer->isOK()) {
         std::cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError().toStdString() << std::endl;
         delete m_writer;