# HG changeset patch # User Chris Cannam # Date 1159885057 0 # Node ID b0f4555b625e23645b7cbfd64a19c54a96468792 # Parent f8cf055dbf34c9e62e8b9b82614fc5782566599c * Introduce WritableWaveFileModel, and use it as an output model for audio real-time plugin transforms. Updates aren't working correctly yet. diff -r f8cf055dbf34 -r b0f4555b625e data/data.pro --- 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 diff -r f8cf055dbf34 -r b0f4555b625e data/fileio/AudioFileReader.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" + diff -r f8cf055dbf34 -r b0f4555b625e data/fileio/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 #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; diff -r f8cf055dbf34 -r b0f4555b625e data/fileio/WavFileReader.cpp --- 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 +#include + 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; } diff -r f8cf055dbf34 -r b0f4555b625e data/fileio/WavFileReader.h --- 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 &extensions); + void updateFrameCount(); + protected: SF_INFO m_fileInfo; SNDFILE *m_file; diff -r f8cf055dbf34 -r b0f4555b625e data/fileio/WavFileWriter.h --- 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); diff -r f8cf055dbf34 -r b0f4555b625e data/model/WaveFileModel.cpp --- 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 diff -r f8cf055dbf34 -r b0f4555b625e data/model/WaveFileModel.h --- 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; diff -r f8cf055dbf34 -r b0f4555b625e data/model/WritableWaveFileModel.cpp --- /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 + +#include +#include + +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 ""; +} + diff -r f8cf055dbf34 -r b0f4555b625e data/model/WritableWaveFileModel.h --- /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 +