changeset 175:b0f4555b625e

* Introduce WritableWaveFileModel, and use it as an output model for audio real-time plugin transforms. Updates aren't working correctly yet.
author Chris Cannam
date Tue, 03 Oct 2006 14:17:37 +0000
parents f8cf055dbf34
children 570794f6f6a7
files data/data.pro data/fileio/AudioFileReader.cpp data/fileio/AudioFileReader.h data/fileio/WavFileReader.cpp data/fileio/WavFileReader.h data/fileio/WavFileWriter.h data/model/WaveFileModel.cpp data/model/WaveFileModel.h data/model/WritableWaveFileModel.cpp data/model/WritableWaveFileModel.h
diffstat 10 files changed, 403 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/data/data.pro	Tue Oct 03 10:06:37 2006 +0000
+++ b/data/data.pro	Tue Oct 03 14:17:37 2006 +0000
@@ -46,10 +46,12 @@
            model/SparseTimeValueModel.h \
            model/SparseValueModel.h \
            model/TextModel.h \
-           model/WaveFileModel.h
+           model/WaveFileModel.h \
+           model/WritableWaveFileModel.h
 SOURCES += fft/FFTDataServer.cpp \
            fft/FFTFileCache.cpp \
            fft/FFTMemoryCache.cpp \
+           fileio/AudioFileReader.cpp \
            fileio/AudioFileReaderFactory.cpp \
            fileio/BZipFileDevice.cpp \
            fileio/CodedAudioFileReader.cpp \
@@ -71,4 +73,5 @@
            model/PowerOfSqrtTwoZoomConstraint.cpp \
            model/PowerOfTwoZoomConstraint.cpp \
            model/RangeSummarisableTimeValueModel.cpp \
-           model/WaveFileModel.cpp
+           model/WaveFileModel.cpp \
+           model/WritableWaveFileModel.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fileio/AudioFileReader.cpp	Tue Oct 03 14:17:37 2006 +0000
@@ -0,0 +1,17 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "AudioFileReader.h"
+
--- a/data/fileio/AudioFileReader.h	Tue Oct 03 10:06:37 2006 +0000
+++ b/data/fileio/AudioFileReader.h	Tue Oct 03 14:17:37 2006 +0000
@@ -19,8 +19,10 @@
 #include <QString>
 #include "model/Model.h" // for SampleBlock
 
-class AudioFileReader
+class AudioFileReader : public QObject
 {
+    Q_OBJECT
+
 public:
     virtual ~AudioFileReader() { }
 
@@ -39,6 +41,9 @@
      */
     virtual void getInterleavedFrames(size_t start, size_t count,
 				      SampleBlock &frames) const = 0;
+
+signals:
+    void frameCountChanged();
     
 protected:
     size_t m_frameCount;
--- a/data/fileio/WavFileReader.cpp	Tue Oct 03 10:06:37 2006 +0000
+++ b/data/fileio/WavFileReader.cpp	Tue Oct 03 14:17:37 2006 +0000
@@ -17,6 +17,8 @@
 
 #include <iostream>
 
+#include <QMutexLocker>
+
 WavFileReader::WavFileReader(QString path) :
     m_file(0),
     m_path(path),
@@ -33,7 +35,7 @@
     m_fileInfo.frames = 0;
     m_file = sf_open(m_path.toLocal8Bit(), SFM_READ, &m_fileInfo);
 
-    if (!m_file || m_fileInfo.frames <= 0 || m_fileInfo.channels <= 0) {
+    if (!m_file || m_fileInfo.channels <= 0) {
 	std::cerr << "WavFileReader::initialize: Failed to open file ("
 		  << sf_strerror(m_file) << ")" << std::endl;
 
@@ -50,6 +52,9 @@
     m_frameCount = m_fileInfo.frames;
     m_channelCount = m_fileInfo.channels;
     m_sampleRate = m_fileInfo.samplerate;
+
+    std::cerr << "WavFileReader: Frame count " << m_frameCount << ", channel count " << m_channelCount << ", sample rate " << m_sampleRate << std::endl;
+
 }
 
 WavFileReader::~WavFileReader()
@@ -58,14 +63,42 @@
 }
 
 void
+WavFileReader::updateFrameCount()
+{
+    QMutexLocker locker(&m_mutex);
+
+    size_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) {
+            std::cerr << "WavFileReader::updateFrameCount: Failed to open file ("
+                      << sf_strerror(m_file) << ")" << std::endl;
+        }
+    }
+
+    std::cerr << "WavFileReader::updateFrameCount: now " << m_fileInfo.frames << std::endl;
+
+    if (m_fileInfo.frames != prevCount) emit frameCountChanged();
+}
+
+void
 WavFileReader::getInterleavedFrames(size_t start, size_t count,
 				    SampleBlock &results) const
 {
+    if (count == 0) return;
     results.clear();
-    if (!m_file || !m_channelCount) return;
-    if (count == 0) return;
+
+    QMutexLocker locker(&m_mutex);
+
+    if (!m_file || !m_channelCount) {
+        return;
+    }
 
     if ((long)start >= m_fileInfo.frames) {
+//        std::cerr << "WavFileReader::getInterleavedFrames: " << start
+//                  << " > " << m_fileInfo.frames << std::endl;
 	return;
     }
 
@@ -75,12 +108,10 @@
 
     sf_count_t readCount = 0;
 
-    m_mutex.lock();
-
     if (start != m_lastStart || count != m_lastCount) {
 
 	if (sf_seek(m_file, start, SEEK_SET) < 0) {
-	    m_mutex.unlock();
+//            std::cerr << "sf_seek failed" << std::endl;
 	    return;
 	}
 	
@@ -94,7 +125,7 @@
 	}
 	
 	if ((readCount = sf_readf_float(m_file, m_buffer, count)) < 0) {
-	    m_mutex.unlock();
+//            std::cerr << "sf_readf_float failed" << std::endl;
 	    return;
 	}
 
@@ -106,7 +137,6 @@
 	results.push_back(m_buffer[i]);
     }
 
-    m_mutex.unlock();
     return;
 }
 
--- a/data/fileio/WavFileReader.h	Tue Oct 03 10:06:37 2006 +0000
+++ b/data/fileio/WavFileReader.h	Tue Oct 03 14:17:37 2006 +0000
@@ -40,6 +40,8 @@
     
     static void getSupportedExtensions(std::set<QString> &extensions);
 
+    void updateFrameCount();
+
 protected:
     SF_INFO m_fileInfo;
     SNDFILE *m_file;
--- a/data/fileio/WavFileWriter.h	Tue Oct 03 10:06:37 2006 +0000
+++ b/data/fileio/WavFileWriter.h	Tue Oct 03 14:17:37 2006 +0000
@@ -33,6 +33,8 @@
 
     virtual QString getError() const;
 
+    QString getPath() const { return m_path; }
+
     bool writeModel(DenseTimeValueModel *source,
                     MultiSelection *selection = 0);
 
--- a/data/model/WaveFileModel.cpp	Tue Oct 03 10:06:37 2006 +0000
+++ b/data/model/WaveFileModel.cpp	Tue Oct 03 14:17:37 2006 +0000
@@ -35,12 +35,30 @@
 
 WaveFileModel::WaveFileModel(QString path) :
     m_path(path),
+    m_myReader(true),
     m_fillThread(0),
     m_updateTimer(0),
     m_lastFillExtent(0),
     m_exiting(false)
 {
     m_reader = AudioFileReaderFactory::createReader(path);
+    connect(m_reader, SIGNAL(frameCountChanged()),
+            this, SLOT(frameCountChanged()));
+    setObjectName(QFileInfo(path).fileName());
+    if (isOK()) fillCache();
+}
+
+WaveFileModel::WaveFileModel(QString path, AudioFileReader *reader) :
+    m_path(path),
+    m_myReader(false),
+    m_fillThread(0),
+    m_updateTimer(0),
+    m_lastFillExtent(0),
+    m_exiting(false)
+{
+    m_reader = reader;
+    connect(m_reader, SIGNAL(frameCountChanged()),
+            this, SLOT(frameCountChanged()));
     setObjectName(QFileInfo(path).fileName());
     if (isOK()) fillCache();
 }
@@ -49,7 +67,7 @@
 {
     m_exiting = true;
     if (m_fillThread) m_fillThread->wait();
-    delete m_reader;
+    if (m_myReader) delete m_reader;
     m_reader = 0;
 }
 
@@ -112,6 +130,9 @@
 
     if (!m_reader || !m_reader->isOK()) return 0;
 
+//    std::cerr << "WaveFileModel::getValues(" << channel << ", "
+//              << start << ", " << end << "): calling reader" << std::endl;
+
     SampleBlock frames;
     m_reader->getInterleavedFrames(start, end - start, frames);
 
@@ -357,18 +378,38 @@
     connect(m_fillThread, SIGNAL(finished()), this, SLOT(cacheFilled()));
     m_mutex.unlock();
     m_fillThread->start();
+    std::cerr << "WaveFileModel::fillCache: started fill thread" << std::endl;
 }   
 
 void
+WaveFileModel::frameCountChanged()
+{
+    m_mutex.lock();
+    if (m_updateTimer) {
+        std::cerr << "WaveFileModel::frameCountChanged: updating existing fill thread" << std::endl;
+        m_fillThread->frameCountChanged();
+        m_mutex.unlock();
+    } else {
+        std::cerr << "WaveFileModel::frameCountChanged: restarting [inefficient]" << std::endl;
+        m_cache[0].clear();
+        m_cache[1].clear();
+        m_mutex.unlock();
+        fillCache();
+    }
+}
+
+void
 WaveFileModel::fillTimerTimedOut()
 {
     if (m_fillThread) {
 	size_t fillExtent = m_fillThread->getFillExtent();
+        cerr << "WaveFileModel::fillTimerTimedOut: extent = " << fillExtent << endl;
 	if (fillExtent > m_lastFillExtent) {
 	    emit modelChanged(m_lastFillExtent, fillExtent);
 	    m_lastFillExtent = fillExtent;
 	}
     } else {
+        cerr << "WaveFileModel::fillTimerTimedOut: no thread" << std::endl;
 	emit modelChanged();
     }
 }
@@ -383,7 +424,13 @@
     m_updateTimer = 0;
     m_mutex.unlock();
     emit modelChanged();
-//    cerr << "WaveFileModel::cacheFilled" << endl;
+    cerr << "WaveFileModel::cacheFilled" << endl;
+}
+
+void
+WaveFileModel::RangeCacheFillThread::frameCountChanged()
+{
+    m_frameCount = m_model.getFrameCount();
 }
 
 void
@@ -401,13 +448,14 @@
     if (!m_model.isOK()) return;
     
     size_t channels = m_model.getChannelCount();
-    size_t frames = m_model.getFrameCount();
 
     Range *range = new Range[2 * channels];
     size_t count[2];
     count[0] = count[1] = 0;
     
-    while (frame < frames) {
+    while (frame < m_frameCount) {
+
+        std::cerr << "WaveFileModel::fill: frame = " << frame << ", count = " << m_frameCount << std::endl;
 
 	m_model.m_reader->getInterleavedFrames(frame, readBlockSize, block);
 
@@ -472,11 +520,11 @@
     
     delete[] range;
 
-    m_fillExtent = frames;
+    m_fillExtent = m_frameCount;
         
-//    for (size_t ct = 0; ct < 2; ++ct) {
-//        cerr << "Cache type " << ct << " now contains " << m_model.m_cache[ct].size() << " ranges" << endl;
-//    }
+    for (size_t ct = 0; ct < 2; ++ct) {
+        cerr << "Cache type " << ct << " now contains " << m_model.m_cache[ct].size() << " ranges" << endl;
+    }
 }
 
 void
--- a/data/model/WaveFileModel.h	Tue Oct 03 10:06:37 2006 +0000
+++ b/data/model/WaveFileModel.h	Tue Oct 03 14:17:37 2006 +0000
@@ -34,6 +34,7 @@
 
 public:
     WaveFileModel(QString path);
+    WaveFileModel(QString path, AudioFileReader *reader);
     ~WaveFileModel();
 
     bool isOK() const;
@@ -71,6 +72,7 @@
 
 protected slots:
     void fillTimerTimedOut();
+    void frameCountChanged();
     void cacheFilled();
     
 protected:
@@ -80,20 +82,24 @@
     {
     public:
         RangeCacheFillThread(WaveFileModel &model) :
-	    m_model(model), m_fillExtent(0) { }
+	    m_model(model), m_fillExtent(0),
+            m_frameCount(model.getFrameCount()) { }
     
 	size_t getFillExtent() const { return m_fillExtent; }
+        void frameCountChanged();
         virtual void run();
 
     protected:
         WaveFileModel &m_model;
 	size_t m_fillExtent;
+        size_t m_frameCount;
     };
          
     void fillCache();
     
     QString m_path;
     AudioFileReader *m_reader;
+    bool m_myReader;
 
     RangeBlock m_cache[2]; // interleaved at two base resolutions
     mutable QMutex m_mutex;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/model/WritableWaveFileModel.cpp	Tue Oct 03 14:17:37 2006 +0000
@@ -0,0 +1,191 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "WritableWaveFileModel.h"
+
+#include "base/TempDirectory.h"
+#include "base/Exceptions.h"
+
+#include "fileio/WavFileWriter.h"
+#include "fileio/WavFileReader.h"
+
+#include <QDir>
+
+#include <cassert>
+#include <iostream>
+
+WritableWaveFileModel::WritableWaveFileModel(size_t sampleRate,
+					     size_t channels,
+					     QString path) :
+    m_model(0),
+    m_writer(0),
+    m_reader(0),
+    m_sampleRate(sampleRate),
+    m_channels(channels),
+    m_frameCount(0)
+{
+    if (path.isEmpty()) {
+        try {
+            QDir dir(TempDirectory::getInstance()->getPath());
+            path = dir.filePath(QString("written_%1.wav")
+                                .arg((intptr_t)this));
+        } catch (DirectoryCreationFailed f) {
+            std::cerr << "WritableWaveFileModel: Failed to create temporary directory" << std::endl;
+            return;
+        }
+    }
+
+    m_writer = new WavFileWriter(path, sampleRate, channels);
+    if (!m_writer->isOK()) {
+        std::cerr << "WritableWaveFileModel: Error in creating WAV file writer: " << m_writer->getError().toStdString() << std::endl;
+        delete m_writer; 
+        m_writer = 0;
+        return;
+    }
+}
+
+WritableWaveFileModel::~WritableWaveFileModel()
+{
+    delete m_model;
+    delete m_writer;
+    delete m_reader;
+}
+
+bool
+WritableWaveFileModel::addSamples(float **samples, size_t count)
+{
+    if (!m_writer) return false;
+
+    if (!m_writer->writeSamples(samples, count)) {
+        std::cerr << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << m_writer->getError().toStdString() << std::endl;
+        return false;
+    }
+
+    m_frameCount += count;
+
+    if (!m_model) {
+
+        m_reader = new WavFileReader(m_writer->getPath());
+        if (!m_reader->getError().isEmpty()) {
+            std::cerr << "WritableWaveFileModel: Error in creating wave file reader" << std::endl;
+            delete m_reader;
+            m_reader = 0;
+            return false;
+        }
+
+        m_model = new WaveFileModel(m_writer->getPath(), m_reader);
+        if (!m_model->isOK()) {
+            std::cerr << "WritableWaveFileModel: Error in creating wave file model" << std::endl;
+            delete m_model;
+            m_model = 0;
+            delete m_reader;
+            m_reader = 0;
+            return false;
+        }
+    }
+    
+    static int updateCounter = 0;
+    if (++updateCounter == 100) {
+        if (m_reader) m_reader->updateFrameCount();
+        updateCounter = 0;
+    }
+
+    return true;
+}
+
+void
+WritableWaveFileModel::sync()
+{
+    if (m_reader) m_reader->updateFrameCount();
+}    
+
+bool
+WritableWaveFileModel::isOK() const
+{
+    bool ok = (m_model && m_model->isOK());
+    std::cerr << "WritableWaveFileModel::isOK(): ok = " << ok << std::endl;
+    return ok;
+}
+
+bool
+WritableWaveFileModel::isReady(int *completion) const
+{
+    bool ready = (m_model && m_model->isReady(completion));
+    std::cerr << "WritableWaveFileModel::isReady(): ready = " << ready << std::endl;
+    return ready;
+}
+
+size_t
+WritableWaveFileModel::getFrameCount() const
+{
+    std::cerr << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << std::endl;
+    return m_frameCount;
+}
+
+Model *
+WritableWaveFileModel::clone() const
+{
+    assert(0); //!!!
+}
+
+size_t
+WritableWaveFileModel::getValues(int channel, size_t start, size_t end,
+                                 float *buffer) const
+{
+    if (!m_model) return 0;
+    return m_model->getValues(channel, start, end, buffer);
+}
+
+size_t
+WritableWaveFileModel::getValues(int channel, size_t start, size_t end,
+                                 double *buffer) const
+{
+    if (!m_model) return 0;
+//    std::cerr << "WritableWaveFileModel::getValues(" << channel << ", "
+//              << start << ", " << end << "): calling model" << std::endl;
+    return m_model->getValues(channel, start, end, buffer);
+}
+
+WritableWaveFileModel::RangeBlock
+WritableWaveFileModel::getRanges(size_t channel, size_t start, size_t end,
+                                 size_t &blockSize) const
+{
+    if (!m_model) return RangeBlock();
+    return m_model->getRanges(channel, start, end, blockSize);
+}
+
+WritableWaveFileModel::Range
+WritableWaveFileModel::getRange(size_t channel, size_t start, size_t end) const
+{
+    if (!m_model) return Range();
+    return m_model->getRange(channel, start, end);
+}
+
+void
+WritableWaveFileModel::toXml(QTextStream &out,
+                             QString indent,
+                             QString extraAttributes) const
+{
+    assert(0); //!!!
+}
+
+QString
+WritableWaveFileModel::toXmlString(QString indent,
+                                   QString extraAttributes) const
+{
+    assert(0); //!!!
+    return "";
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/model/WritableWaveFileModel.h	Tue Oct 03 14:17:37 2006 +0000
@@ -0,0 +1,79 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef _WRITABLE_WAVE_FILE_MODEL_H_
+#define _WRITABLE_WAVE_FILE_MODEL_H_
+
+#include "WaveFileModel.h"
+
+class WavFileWriter;
+class WavFileReader;
+
+class WritableWaveFileModel : public RangeSummarisableTimeValueModel,
+                              virtual public PowerOfSqrtTwoZoomConstraint
+{
+    Q_OBJECT
+
+public:
+    WritableWaveFileModel(size_t sampleRate, size_t channels, QString path = "");
+    ~WritableWaveFileModel();
+
+    virtual bool addSamples(float **samples, size_t count);
+    virtual void sync();
+    
+    bool isOK() const;
+    bool isReady(int *) const;
+
+    size_t getFrameCount() const;
+    size_t getChannelCount() const { return m_channels; }
+    size_t getSampleRate() const { return m_sampleRate; }
+
+    virtual Model *clone() const;
+
+    float getValueMinimum() const { return -1.0f; }
+    float getValueMaximum() const { return  1.0f; }
+
+    virtual size_t getStartFrame() const { return 0; }
+    virtual size_t getEndFrame() const { return getFrameCount(); }
+
+    virtual size_t getValues(int channel, size_t start, size_t end,
+			     float *buffer) const;
+
+    virtual size_t getValues(int channel, size_t start, size_t end,
+			     double *buffer) const;
+
+    virtual RangeBlock getRanges(size_t channel, size_t start, size_t end,
+				 size_t &blockSize) const;
+
+    virtual Range getRange(size_t channel, size_t start, size_t end) const;
+
+    virtual void toXml(QTextStream &out,
+                       QString indent = "",
+                       QString extraAttributes = "") const;
+
+    virtual QString toXmlString(QString indent = "",
+				QString extraAttributes = "") const;
+
+protected:
+    WaveFileModel *m_model;
+    WavFileWriter *m_writer;
+    WavFileReader *m_reader;
+    size_t m_sampleRate;
+    size_t m_channels;
+    size_t m_frameCount;
+};
+
+#endif
+