annotate data/fileio/WavFileWriter.cpp @ 1496:fde8c497373f

Avoid crashing if an effects plugin can't be instantiated and so the output vector is empty in the transformer's run() method
author Chris Cannam
date Mon, 13 Aug 2018 15:25:32 +0100
parents 48e9f538e6e9
children b837ccdd4946
rev   line source
Chris@148 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@148 2
Chris@148 3 /*
Chris@148 4 Sonic Visualiser
Chris@148 5 An audio file viewer and annotation editor.
Chris@148 6 Centre for Digital Music, Queen Mary, University of London.
Chris@202 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@148 8
Chris@148 9 This program is free software; you can redistribute it and/or
Chris@148 10 modify it under the terms of the GNU General Public License as
Chris@148 11 published by the Free Software Foundation; either version 2 of the
Chris@148 12 License, or (at your option) any later version. See the file
Chris@148 13 COPYING included with this distribution for more information.
Chris@148 14 */
Chris@148 15
Chris@148 16 #include "WavFileWriter.h"
Chris@148 17
Chris@148 18 #include "model/DenseTimeValueModel.h"
Chris@148 19 #include "base/Selection.h"
Chris@674 20 #include "base/TempWriteFile.h"
Chris@674 21 #include "base/Exceptions.h"
Chris@1428 22 #include "base/Debug.h"
Chris@148 23
Chris@148 24 #include <QFileInfo>
Chris@148 25
Chris@148 26 #include <iostream>
Chris@1055 27 #include <cmath>
Chris@1428 28 #include <string>
Chris@148 29
Chris@1096 30 using namespace std;
Chris@1096 31
Chris@148 32 WavFileWriter::WavFileWriter(QString path,
Chris@1350 33 sv_samplerate_t sampleRate,
Chris@929 34 int channels,
Chris@684 35 FileWriteMode mode) :
Chris@148 36 m_path(path),
Chris@148 37 m_sampleRate(sampleRate),
Chris@174 38 m_channels(channels),
Chris@674 39 m_temp(0),
Chris@1350 40 m_file(0)
Chris@148 41 {
Chris@174 42 SF_INFO fileInfo;
Chris@1040 43
Chris@1040 44 int fileRate = int(round(m_sampleRate));
Chris@1040 45 if (m_sampleRate != sv_samplerate_t(fileRate)) {
Chris@1428 46 SVCERR << "WavFileWriter: WARNING: Non-integer sample rate "
Chris@1350 47 << m_sampleRate << " presented, rounding to " << fileRate
Chris@1350 48 << endl;
Chris@1040 49 }
Chris@1040 50 fileInfo.samplerate = fileRate;
Chris@174 51 fileInfo.channels = m_channels;
Chris@174 52 fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
Chris@674 53
Chris@674 54 try {
Chris@1350 55 QString writePath = m_path;
Chris@684 56 if (mode == WriteToTemporary) {
Chris@684 57 m_temp = new TempWriteFile(m_path);
Chris@1350 58 writePath = m_temp->getTemporaryFilename();
Chris@1348 59 }
Chris@1350 60 #ifdef Q_OS_WIN
Chris@1350 61 m_file = sf_wchar_open((LPCWSTR)writePath.utf16(), SFM_WRITE, &fileInfo);
Chris@1350 62 #else
Chris@1350 63 m_file = sf_open(writePath.toLocal8Bit(), SFM_WRITE, &fileInfo);
Chris@1350 64 #endif
Chris@1350 65 if (!m_file) {
Chris@1428 66 SVCERR << "WavFileWriter: Failed to open file ("
Chris@1350 67 << sf_strerror(m_file) << ")" << endl;
Chris@1348 68 m_error = QString("Failed to open audio file '%1' for writing")
Chris@1350 69 .arg(writePath);
Chris@1348 70 }
Chris@674 71 } catch (FileOperationFailed &f) {
Chris@674 72 m_error = f.what();
Chris@674 73 m_temp = 0;
Chris@1350 74 m_file = 0;
Chris@174 75 }
Chris@148 76 }
Chris@148 77
Chris@148 78 WavFileWriter::~WavFileWriter()
Chris@148 79 {
Chris@1350 80 if (m_file) close();
Chris@148 81 }
Chris@148 82
Chris@148 83 bool
Chris@148 84 WavFileWriter::isOK() const
Chris@148 85 {
Chris@148 86 return (m_error.isEmpty());
Chris@148 87 }
Chris@148 88
Chris@148 89 QString
Chris@148 90 WavFileWriter::getError() const
Chris@148 91 {
Chris@148 92 return m_error;
Chris@148 93 }
Chris@148 94
Chris@684 95 QString
Chris@684 96 WavFileWriter::getWriteFilename() const
Chris@684 97 {
Chris@684 98 if (m_temp) {
Chris@684 99 return m_temp->getTemporaryFilename();
Chris@684 100 } else {
Chris@684 101 return m_path;
Chris@684 102 }
Chris@684 103 }
Chris@684 104
Chris@174 105 bool
Chris@174 106 WavFileWriter::writeModel(DenseTimeValueModel *source,
Chris@174 107 MultiSelection *selection)
Chris@148 108 {
Chris@174 109 if (source->getChannelCount() != m_channels) {
Chris@690 110 SVDEBUG << "WavFileWriter::writeModel: Wrong number of channels ("
Chris@174 111 << source->getChannelCount() << " != " << m_channels << ")"
Chris@687 112 << endl;
Chris@174 113 m_error = QString("Failed to write model to audio file '%1'")
Chris@684 114 .arg(getWriteFilename());
Chris@174 115 return false;
Chris@148 116 }
Chris@148 117
Chris@1350 118 if (!m_file) {
Chris@174 119 m_error = QString("Failed to write model to audio file '%1': File not open")
Chris@684 120 .arg(getWriteFilename());
Chris@1350 121 return false;
Chris@174 122 }
Chris@148 123
Chris@174 124 bool ownSelection = false;
Chris@174 125 if (!selection) {
Chris@1350 126 selection = new MultiSelection;
Chris@1350 127 selection->setSelection(Selection(source->getStartFrame(),
Chris@1350 128 source->getEndFrame()));
Chris@174 129 ownSelection = true;
Chris@148 130 }
Chris@148 131
Chris@1038 132 sv_frame_t bs = 2048;
Chris@148 133
Chris@148 134 for (MultiSelection::SelectionList::iterator i =
Chris@1350 135 selection->getSelections().begin();
Chris@1350 136 i != selection->getSelections().end(); ++i) {
Chris@1429 137
Chris@1350 138 sv_frame_t f0(i->getStartFrame()), f1(i->getEndFrame());
Chris@148 139
Chris@1350 140 for (sv_frame_t f = f0; f < f1; f += bs) {
Chris@1429 141
Chris@1350 142 sv_frame_t n = min(bs, f1 - f);
Chris@1326 143 floatvec_t interleaved(n * m_channels, 0.f);
Chris@148 144
Chris@1350 145 for (int c = 0; c < int(m_channels); ++c) {
Chris@1326 146 auto chanbuf = source->getData(c, f, n);
Chris@1350 147 for (int i = 0; in_range_for(chanbuf, i); ++i) {
Chris@1350 148 interleaved[i * m_channels + c] = chanbuf[i];
Chris@1350 149 }
Chris@1350 150 }
Chris@148 151
Chris@1350 152 sf_count_t written = sf_writef_float(m_file, interleaved.data(), n);
Chris@148 153
Chris@1350 154 if (written < n) {
Chris@1350 155 m_error = QString("Only wrote %1 of %2 frames at file frame %3")
Chris@1350 156 .arg(written).arg(n).arg(f);
Chris@1350 157 break;
Chris@1350 158 }
Chris@1350 159 }
Chris@148 160 }
Chris@148 161
Chris@174 162 if (ownSelection) delete selection;
Chris@174 163
Chris@174 164 return isOK();
Chris@174 165 }
Chris@1429 166
Chris@174 167 bool
Chris@1325 168 WavFileWriter::writeSamples(const float *const *samples, sv_frame_t count)
Chris@174 169 {
Chris@1350 170 if (!m_file) {
Chris@174 171 m_error = QString("Failed to write model to audio file '%1': File not open")
Chris@684 172 .arg(getWriteFilename());
Chris@1350 173 return false;
Chris@174 174 }
Chris@174 175
Chris@174 176 float *b = new float[count * m_channels];
Chris@1038 177 for (sv_frame_t i = 0; i < count; ++i) {
Chris@929 178 for (int c = 0; c < int(m_channels); ++c) {
Chris@174 179 b[i * m_channels + c] = samples[c][i];
Chris@174 180 }
Chris@174 181 }
Chris@174 182
Chris@1350 183 sv_frame_t written = sf_writef_float(m_file, b, count);
Chris@174 184
Chris@174 185 delete[] b;
Chris@174 186
Chris@1038 187 if (written < count) {
Chris@174 188 m_error = QString("Only wrote %1 of %2 frames")
Chris@174 189 .arg(written).arg(count);
Chris@174 190 }
Chris@174 191
Chris@174 192 return isOK();
Chris@174 193 }
Chris@174 194
Chris@174 195 bool
Chris@174 196 WavFileWriter::close()
Chris@174 197 {
Chris@1350 198 if (m_file) {
Chris@1350 199 sf_close(m_file);
Chris@1350 200 m_file = 0;
Chris@174 201 }
Chris@684 202 if (m_temp) {
Chris@684 203 m_temp->moveToTarget();
Chris@684 204 delete m_temp;
Chris@684 205 m_temp = 0;
Chris@684 206 }
Chris@174 207 return true;
Chris@148 208 }
Chris@148 209