annotate data/fileio/WavFileWriter.cpp @ 1412:b7a9edee85e0 scale-ticks

Change loop to something that feels more correct, though it makes no difference to the tests here. More tests, one failing.
author Chris Cannam
date Thu, 04 May 2017 08:32:41 +0100
parents 1bc6f70cb4c7
children 87ae75da6527
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@148 22
Chris@148 23 #include <QFileInfo>
Chris@148 24
Chris@148 25 #include <iostream>
Chris@1055 26 #include <cmath>
Chris@148 27
Chris@1096 28 using namespace std;
Chris@1096 29
Chris@148 30 WavFileWriter::WavFileWriter(QString path,
Chris@1350 31 sv_samplerate_t sampleRate,
Chris@929 32 int channels,
Chris@684 33 FileWriteMode mode) :
Chris@148 34 m_path(path),
Chris@148 35 m_sampleRate(sampleRate),
Chris@174 36 m_channels(channels),
Chris@674 37 m_temp(0),
Chris@1350 38 m_file(0)
Chris@148 39 {
Chris@174 40 SF_INFO fileInfo;
Chris@1040 41
Chris@1040 42 int fileRate = int(round(m_sampleRate));
Chris@1040 43 if (m_sampleRate != sv_samplerate_t(fileRate)) {
Chris@1350 44 cerr << "WavFileWriter: WARNING: Non-integer sample rate "
Chris@1350 45 << m_sampleRate << " presented, rounding to " << fileRate
Chris@1350 46 << endl;
Chris@1040 47 }
Chris@1040 48 fileInfo.samplerate = fileRate;
Chris@174 49 fileInfo.channels = m_channels;
Chris@174 50 fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
Chris@674 51
Chris@674 52 try {
Chris@1350 53 QString writePath = m_path;
Chris@684 54 if (mode == WriteToTemporary) {
Chris@684 55 m_temp = new TempWriteFile(m_path);
Chris@1350 56 writePath = m_temp->getTemporaryFilename();
Chris@1348 57 }
Chris@1350 58 #ifdef Q_OS_WIN
Chris@1350 59 m_file = sf_wchar_open((LPCWSTR)writePath.utf16(), SFM_WRITE, &fileInfo);
Chris@1350 60 #else
Chris@1350 61 m_file = sf_open(writePath.toLocal8Bit(), SFM_WRITE, &fileInfo);
Chris@1350 62 #endif
Chris@1350 63 if (!m_file) {
Chris@1350 64 cerr << "WavFileWriter: Failed to open file ("
Chris@1350 65 << sf_strerror(m_file) << ")" << endl;
Chris@1348 66 m_error = QString("Failed to open audio file '%1' for writing")
Chris@1350 67 .arg(writePath);
Chris@1348 68 }
Chris@674 69 } catch (FileOperationFailed &f) {
Chris@674 70 m_error = f.what();
Chris@674 71 m_temp = 0;
Chris@1350 72 m_file = 0;
Chris@174 73 }
Chris@148 74 }
Chris@148 75
Chris@148 76 WavFileWriter::~WavFileWriter()
Chris@148 77 {
Chris@1350 78 if (m_file) close();
Chris@148 79 }
Chris@148 80
Chris@148 81 bool
Chris@148 82 WavFileWriter::isOK() const
Chris@148 83 {
Chris@148 84 return (m_error.isEmpty());
Chris@148 85 }
Chris@148 86
Chris@148 87 QString
Chris@148 88 WavFileWriter::getError() const
Chris@148 89 {
Chris@148 90 return m_error;
Chris@148 91 }
Chris@148 92
Chris@684 93 QString
Chris@684 94 WavFileWriter::getWriteFilename() const
Chris@684 95 {
Chris@684 96 if (m_temp) {
Chris@684 97 return m_temp->getTemporaryFilename();
Chris@684 98 } else {
Chris@684 99 return m_path;
Chris@684 100 }
Chris@684 101 }
Chris@684 102
Chris@174 103 bool
Chris@174 104 WavFileWriter::writeModel(DenseTimeValueModel *source,
Chris@174 105 MultiSelection *selection)
Chris@148 106 {
Chris@174 107 if (source->getChannelCount() != m_channels) {
Chris@690 108 SVDEBUG << "WavFileWriter::writeModel: Wrong number of channels ("
Chris@174 109 << source->getChannelCount() << " != " << m_channels << ")"
Chris@687 110 << endl;
Chris@174 111 m_error = QString("Failed to write model to audio file '%1'")
Chris@684 112 .arg(getWriteFilename());
Chris@174 113 return false;
Chris@148 114 }
Chris@148 115
Chris@1350 116 if (!m_file) {
Chris@174 117 m_error = QString("Failed to write model to audio file '%1': File not open")
Chris@684 118 .arg(getWriteFilename());
Chris@1350 119 return false;
Chris@174 120 }
Chris@148 121
Chris@174 122 bool ownSelection = false;
Chris@174 123 if (!selection) {
Chris@1350 124 selection = new MultiSelection;
Chris@1350 125 selection->setSelection(Selection(source->getStartFrame(),
Chris@1350 126 source->getEndFrame()));
Chris@174 127 ownSelection = true;
Chris@148 128 }
Chris@148 129
Chris@1038 130 sv_frame_t bs = 2048;
Chris@148 131
Chris@148 132 for (MultiSelection::SelectionList::iterator i =
Chris@1350 133 selection->getSelections().begin();
Chris@1350 134 i != selection->getSelections().end(); ++i) {
Chris@148 135
Chris@1350 136 sv_frame_t f0(i->getStartFrame()), f1(i->getEndFrame());
Chris@148 137
Chris@1350 138 for (sv_frame_t f = f0; f < f1; f += bs) {
Chris@148 139
Chris@1350 140 sv_frame_t n = min(bs, f1 - f);
Chris@1326 141 floatvec_t interleaved(n * m_channels, 0.f);
Chris@148 142
Chris@1350 143 for (int c = 0; c < int(m_channels); ++c) {
Chris@1326 144 auto chanbuf = source->getData(c, f, n);
Chris@1350 145 for (int i = 0; in_range_for(chanbuf, i); ++i) {
Chris@1350 146 interleaved[i * m_channels + c] = chanbuf[i];
Chris@1350 147 }
Chris@1350 148 }
Chris@148 149
Chris@1350 150 sf_count_t written = sf_writef_float(m_file, interleaved.data(), n);
Chris@148 151
Chris@1350 152 if (written < n) {
Chris@1350 153 m_error = QString("Only wrote %1 of %2 frames at file frame %3")
Chris@1350 154 .arg(written).arg(n).arg(f);
Chris@1350 155 break;
Chris@1350 156 }
Chris@1350 157 }
Chris@148 158 }
Chris@148 159
Chris@174 160 if (ownSelection) delete selection;
Chris@174 161
Chris@174 162 return isOK();
Chris@174 163 }
Chris@174 164
Chris@174 165 bool
Chris@1325 166 WavFileWriter::writeSamples(const float *const *samples, sv_frame_t count)
Chris@174 167 {
Chris@1350 168 if (!m_file) {
Chris@174 169 m_error = QString("Failed to write model to audio file '%1': File not open")
Chris@684 170 .arg(getWriteFilename());
Chris@1350 171 return false;
Chris@174 172 }
Chris@174 173
Chris@174 174 float *b = new float[count * m_channels];
Chris@1038 175 for (sv_frame_t i = 0; i < count; ++i) {
Chris@929 176 for (int c = 0; c < int(m_channels); ++c) {
Chris@174 177 b[i * m_channels + c] = samples[c][i];
Chris@174 178 }
Chris@174 179 }
Chris@174 180
Chris@1350 181 sv_frame_t written = sf_writef_float(m_file, b, count);
Chris@174 182
Chris@174 183 delete[] b;
Chris@174 184
Chris@1038 185 if (written < count) {
Chris@174 186 m_error = QString("Only wrote %1 of %2 frames")
Chris@174 187 .arg(written).arg(count);
Chris@174 188 }
Chris@174 189
Chris@174 190 return isOK();
Chris@174 191 }
Chris@174 192
Chris@174 193 bool
Chris@174 194 WavFileWriter::close()
Chris@174 195 {
Chris@1350 196 if (m_file) {
Chris@1350 197 sf_close(m_file);
Chris@1350 198 m_file = 0;
Chris@174 199 }
Chris@684 200 if (m_temp) {
Chris@684 201 m_temp->moveToTarget();
Chris@684 202 delete m_temp;
Chris@684 203 m_temp = 0;
Chris@684 204 }
Chris@174 205 return true;
Chris@148 206 }
Chris@148 207