WritableWaveFileModel.cpp
Go to the documentation of this file.
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2 
3 /*
4  Sonic Visualiser
5  An audio file viewer and annotation editor.
6  Centre for Digital Music, Queen Mary, University of London.
7  This file copyright 2006 QMUL.
8 
9  This program is free software; you can redistribute it and/or
10  modify it under the terms of the GNU General Public License as
11  published by the Free Software Foundation; either version 2 of the
12  License, or (at your option) any later version. See the file
13  COPYING included with this distribution for more information.
14 */
15 
16 #include "WritableWaveFileModel.h"
17 
18 #include "ReadOnlyWaveFileModel.h"
19 
20 #include "base/TempDirectory.h"
21 #include "base/Exceptions.h"
23 
24 #include "fileio/WavFileWriter.h"
25 #include "fileio/WavFileReader.h"
26 
27 #include <QDir>
28 #include <QTextStream>
29 
30 #include <cassert>
31 #include <iostream>
32 #include <stdint.h>
33 
34 using namespace std;
35 
37 
38 //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1
39 
41  sv_samplerate_t sampleRate,
42  int channels,
43  Normalisation norm) :
44  m_model(nullptr),
45  m_temporaryWriter(nullptr),
46  m_targetWriter(nullptr),
47  m_reader(nullptr),
48  m_normalisation(norm),
49  m_sampleRate(sampleRate),
50  m_channels(channels),
51  m_frameCount(0),
52  m_startFrame(0),
53  m_proportion(PROPORTION_UNKNOWN)
54 {
55  init(path);
56 }
57 
59  int channels,
60  Normalisation norm) :
61  m_model(nullptr),
62  m_temporaryWriter(nullptr),
63  m_targetWriter(nullptr),
64  m_reader(nullptr),
65  m_normalisation(norm),
66  m_sampleRate(sampleRate),
67  m_channels(channels),
68  m_frameCount(0),
69  m_startFrame(0),
71 {
72  init();
73 }
74 
76  int channels) :
77  m_model(nullptr),
78  m_temporaryWriter(nullptr),
79  m_targetWriter(nullptr),
80  m_reader(nullptr),
82  m_sampleRate(sampleRate),
83  m_channels(channels),
84  m_frameCount(0),
85  m_startFrame(0),
87 {
88  init();
89 }
90 
91 void
93 {
94  if (path.isEmpty()) {
95  try {
96  // Temp dir is exclusive to this run of the application,
97  // so the filename only needs to be unique within that -
98  // model ID should be ok
99  QDir dir(TempDirectory::getInstance()->getPath());
100  path = dir.filePath(QString("written_%1.wav")
101  .arg(getId().untyped));
102  } catch (const DirectoryCreationFailed &f) {
103  SVCERR << "WritableWaveFileModel: Failed to create temporary directory" << endl;
104  return;
105  }
106  }
107 
108  m_targetPath = path;
109  m_temporaryPath = "";
110 
111  // We don't delete or null-out writer/reader members after
112  // failures here - they are all deleted in the dtor, and the
113  // presence/existence of the model is what's used to determine
114  // whether to go ahead, not the writer/readers. If the model is
115  // non-null, then the necessary writer/readers must be OK, as the
116  // model is the last thing initialised
117 
120 
121  if (!m_targetWriter->isOK()) {
122  SVCERR << "WritableWaveFileModel: Error in creating WAV file writer: " << m_targetWriter->getError() << endl;
123  return;
124  }
125 
127 
128  // Temp dir is exclusive to this run of the application, so
129  // the filename only needs to be unique within that
130  QDir dir(TempDirectory::getInstance()->getPath());
131  m_temporaryPath = dir.filePath(QString("prenorm_%1.wav")
132  .arg(getId().untyped));
133 
137 
138  if (!m_temporaryWriter->isOK()) {
139  SVCERR << "WritableWaveFileModel: Error in creating temporary WAV file writer: " << m_temporaryWriter->getError() << endl;
140  return;
141  }
142  }
143 
144  FileSource source(m_targetPath);
145 
146  m_reader = new WavFileReader(source, true);
147  if (!m_reader->getError().isEmpty()) {
148  SVCERR << "WritableWaveFileModel: Error in creating wave file reader: " << m_reader->getError() << endl;
149  return;
150  }
151 
152  m_model = new ReadOnlyWaveFileModel(source, m_reader);
153  if (!m_model->isOK()) {
154  SVCERR << "WritableWaveFileModel: Error in creating wave file model" << endl;
155  delete m_model;
156  m_model = nullptr;
157  return;
158  }
160 
161  connect(m_model, SIGNAL(modelChanged(ModelId)),
162  this, SLOT(componentModelChanged(ModelId)));
165 
167  (getId().untyped, this);
168 }
169 
171 {
173  (getId().untyped);
174 
175  delete m_model;
176  delete m_targetWriter;
177  delete m_temporaryWriter;
178  delete m_reader;
179 }
180 
181 void
183 {
184  emit modelChanged(getId());
185 }
186 
187 void
189 {
190  emit modelChangedWithin(getId(), f0, f1);
191 }
192 
193 void
195 {
196  m_startFrame = startFrame;
197  if (m_model) {
198  m_model->setStartFrame(startFrame);
199  }
200 }
201 
202 bool
203 WritableWaveFileModel::addSamples(const float *const *samples, sv_frame_t count)
204 {
205  if (!m_model) return false;
206 
207 #ifdef DEBUG_WRITABLE_WAVE_FILE_MODEL
208 // SVDEBUG << "WritableWaveFileModel::addSamples(" << count << ")" << endl;
209 #endif
210 
211  WavFileWriter *writer = m_targetWriter;
213  writer = m_temporaryWriter;
214  }
215 
216  if (!writer->writeSamples(samples, count)) {
217  SVCERR << "ERROR: WritableWaveFileModel::addSamples: writer failed: " << writer->getError() << endl;
218  return false;
219  }
220 
221  m_frameCount += count;
222 
224  if (m_reader->getChannelCount() == 0) {
226  }
227  }
228 
229  return true;
230 }
231 
232 void
234 {
235  if (!m_model) return;
236 
238 }
239 
240 bool
242 {
243  return (m_model && m_model->isOK());
244 }
245 
246 void
248 {
249  m_proportion = proportion;
250 }
251 
252 int
254 {
255  return m_proportion;
256 }
257 
258 void
260 {
261  if (!m_model) return;
262 
265  } else {
268  }
269 
270  m_reader->updateDone();
271  m_proportion = 100;
272  emit modelChanged(getId());
273  emit writeCompleted(getId());
274 }
275 
276 void
278 {
279  if (m_temporaryPath == "") {
280  SVCERR << "WritableWaveFileModel::normaliseToTarget: No temporary path available" << endl;
281  return;
282  }
283 
284  WavFileReader normalisingReader(m_temporaryPath, false,
286 
287  if (!normalisingReader.getError().isEmpty()) {
288  SVCERR << "WritableWaveFileModel: Error in creating normalising reader: " << normalisingReader.getError() << endl;
289  return;
290  }
291 
292  sv_frame_t frame = 0;
293  sv_frame_t block = 65536;
294  sv_frame_t count = normalisingReader.getFrameCount();
295 
296  while (frame < count) {
297  auto frames = normalisingReader.getInterleavedFrames(frame, block);
298  if (!m_targetWriter->putInterleavedFrames(frames)) {
299  SVCERR << "ERROR: WritableWaveFileModel::normaliseToTarget: writer failed: " << m_targetWriter->getError() << endl;
300  return;
301  }
302  frame += block;
303  }
304 
306 
307  delete m_temporaryWriter;
308  m_temporaryWriter = nullptr;
309  QFile::remove(m_temporaryPath);
310 }
311 
314 {
315 // SVDEBUG << "WritableWaveFileModel::getFrameCount: count = " << m_frameCount << endl;
316  return m_frameCount;
317 }
318 
320 WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const
321 {
322  if (!m_model || m_model->getChannelCount() == 0) return {};
323  return m_model->getData(channel, start, count);
324 }
325 
326 vector<floatvec_t>
327 WritableWaveFileModel::getMultiChannelData(int fromchannel, int tochannel,
328  sv_frame_t start, sv_frame_t count) const
329 {
330  if (!m_model || m_model->getChannelCount() == 0) return {};
331  return m_model->getMultiChannelData(fromchannel, tochannel, start, count);
332 }
333 
334 int
336 {
337  if (!m_model) return desired;
338  return m_model->getSummaryBlockSize(desired);
339 }
340 
341 void
343  RangeBlock &ranges,
344  int &blockSize) const
345 {
346  ranges.clear();
347  if (!m_model || m_model->getChannelCount() == 0) return;
348  m_model->getSummaries(channel, start, count, ranges, blockSize);
349 }
350 
353 {
354  if (!m_model || m_model->getChannelCount() == 0) return Range();
355  return m_model->getSummary(channel, start, count);
356 }
357 
358 void
360  QString indent,
361  QString extraAttributes) const
362 {
363  // The assumption here is that the underlying wave file has
364  // already been saved somewhere (its location is available through
365  // getLocation()) and that the code that uses this class is
366  // dealing with the problem of making sure it remains available.
367  // We just write this out as if it were a normal wave file.
368 
370  (out, indent,
371  QString("type=\"wavefile\" file=\"%1\" subtype=\"writable\" %2")
373  .arg(extraAttributes));
374 }
375 
bool isOK() const
double sv_samplerate_t
Sample rate.
Definition: BaseTypes.h:51
void toXml(QTextStream &stream, QString indent="", QString extraAttributes="") const override
Stream this exportable object out to XML on a text stream.
Definition: Model.cpp:204
int getSummaryBlockSize(int desired) const override
static PlayParameterRepository * getInstance()
ReadOnlyWaveFileModel * m_model
void writeCompleted(ModelId)
int64_t sv_frame_t
Frame index, the unit of our time axis.
Definition: BaseTypes.h:31
void writeComplete()
Indicate that writing is complete.
std::vector< floatvec_t > getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const override
Get the specified set of samples from given contiguous range of channels of the model in single-preci...
int getWriteProportion() const
Get the proportion of the file which has been written so far, as a percentage.
Range getSummary(int channel, sv_frame_t start, sv_frame_t count) const override
Return the range from the given start frame, corresponding to the given number of underlying sample f...
Reader for audio files using libsndfile.
Definition: WavFileReader.h:41
void setWriteProportion(int proportion)
Set the proportion of the file which has been written so far, as a percentage.
void init(QString path="")
virtual QString getError() const
bool isOK() const override
Return true if the model was constructed successfully.
Id getId() const
Return an id for this object.
Definition: ById.h:193
std::vector< float, breakfastquay::StlAllocator< float > > floatvec_t
Definition: BaseTypes.h:53
static QString encodeEntities(QString)
void setStartFrame(sv_frame_t startFrame) override
static TempDirectory * getInstance()
void componentModelChangedWithin(ModelId, sv_frame_t, sv_frame_t)
int getSummaryBlockSize(int desired) const override
WavFileWriter * m_temporaryWriter
When normalising, this writer is used to write verbatim samples to the temporary file prior to normal...
static const int PROPORTION_UNKNOWN
floatvec_t getData(int channel, sv_frame_t start, sv_frame_t count) const override
Get the specified set of samples from the given channel of the model in single-precision floating-poi...
WavFileWriter * m_targetWriter
When not normalising, this writer is used to write verbatim samples direct to the target file...
WritableWaveFileModel(QString path, sv_samplerate_t sampleRate, int channels, Normalisation normalisation)
Create a WritableWaveFileModel of the given sample rate and channel count, storing data in a new floa...
FileSource is a class used to refer to the contents of a file that may be either local or at a remote...
Definition: FileSource.h:59
void removePlayable(int id)
Unregister a playable.
QString getError() const override
If isOK() is false, return an error string.
Definition: WavFileReader.h:52
void getSummaries(int channel, sv_frame_t start, sv_frame_t count, RangeBlock &ranges, int &blockSize) const override
Return ranges from the given start frame, corresponding to the given number of underlying sample fram...
void modelChangedWithin(ModelId myId, sv_frame_t startFrame, sv_frame_t endFrame)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
Range getSummary(int channel, sv_frame_t start, sv_frame_t count) const override
Return the range from the given start frame, corresponding to the given number of underlying sample f...
void toXml(QTextStream &out, QString indent="", QString extraAttributes="") const override
Stream this exportable object out to XML on a text stream.
bool isOK() const override
Return true if the model was constructed successfully.
void addPlayable(int id, const Playable *)
Register a playable.
void updateFrameCount()
void updateModel()
Tell the model to update its own (read) view of the (written) file.
#define SVCERR
Definition: Debug.h:109
sv_frame_t getFrameCount() const override
sv_frame_t getFrameCount() const
Return the number of audio sample frames (i.e.
floatvec_t getInterleavedFrames(sv_frame_t start, sv_frame_t count) const override
Must be safe to call from multiple threads with different arguments on the same object at the same ti...
void setStartFrame(sv_frame_t startFrame) override
int getChannelCount() const override
Return the number of distinct channels for this model.
Definition: ById.h:115
floatvec_t getData(int channel, sv_frame_t start, sv_frame_t count) const override
Get the specified set of samples from the given channel of the model in single-precision floating-poi...
virtual bool addSamples(const float *const *samples, sv_frame_t count)
Call addSamples to append a block of samples to the end of the file.
bool putInterleavedFrames(const floatvec_t &frames)
As writeSamples, but compatible with WavFileReader api. More expensive.
bool writeSamples(const float *const *samples, sv_frame_t count)
Write samples from raw arrays; count is per-channel.
void modelChanged(ModelId myId)
Emitted when a model has been edited (or more data retrieved from cache, in the case of a cached mode...
void getSummaries(int channel, sv_frame_t start, sv_frame_t count, RangeBlock &ranges, int &blockSize) const override
Return ranges from the given start frame, corresponding to the given number of underlying sample fram...
int getChannelCount() const
Return the number of channels in the file.
std::vector< floatvec_t > getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const override
Get the specified set of samples from given contiguous range of channels of the model in single-preci...