annotate data/fileio/WavFileWriter.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
parents 59e7fe1b1003
children cc27f35aa75c
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@148 26
Chris@148 27 WavFileWriter::WavFileWriter(QString path,
Chris@929 28 int sampleRate,
Chris@929 29 int channels,
Chris@684 30 FileWriteMode mode) :
Chris@148 31 m_path(path),
Chris@148 32 m_sampleRate(sampleRate),
Chris@174 33 m_channels(channels),
Chris@674 34 m_temp(0),
Chris@174 35 m_file(0)
Chris@148 36 {
Chris@174 37 SF_INFO fileInfo;
Chris@174 38 fileInfo.samplerate = m_sampleRate;
Chris@174 39 fileInfo.channels = m_channels;
Chris@174 40 fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT;
Chris@674 41
Chris@674 42 try {
Chris@684 43 if (mode == WriteToTemporary) {
Chris@684 44 m_temp = new TempWriteFile(m_path);
Chris@684 45 m_file = sf_open(m_temp->getTemporaryFilename().toLocal8Bit(),
Chris@684 46 SFM_WRITE, &fileInfo);
Chris@684 47 if (!m_file) {
Chris@843 48 cerr << "WavFileWriter: Failed to open file ("
Chris@843 49 << sf_strerror(m_file) << ")" << endl;
Chris@684 50 m_error = QString("Failed to open audio file '%1' for writing")
Chris@684 51 .arg(m_temp->getTemporaryFilename());
Chris@684 52 }
Chris@684 53 } else {
Chris@684 54 m_file = sf_open(m_path.toLocal8Bit(), SFM_WRITE, &fileInfo);
Chris@684 55 if (!m_file) {
Chris@843 56 cerr << "WavFileWriter: Failed to open file ("
Chris@843 57 << sf_strerror(m_file) << ")" << endl;
Chris@684 58 m_error = QString("Failed to open audio file '%1' for writing")
Chris@684 59 .arg(m_path);
Chris@684 60 }
Chris@684 61 }
Chris@674 62 } catch (FileOperationFailed &f) {
Chris@674 63 m_error = f.what();
Chris@674 64 m_temp = 0;
Chris@674 65 m_file = 0;
Chris@174 66 }
Chris@148 67 }
Chris@148 68
Chris@148 69 WavFileWriter::~WavFileWriter()
Chris@148 70 {
Chris@174 71 if (m_file) close();
Chris@148 72 }
Chris@148 73
Chris@148 74 bool
Chris@148 75 WavFileWriter::isOK() const
Chris@148 76 {
Chris@148 77 return (m_error.isEmpty());
Chris@148 78 }
Chris@148 79
Chris@148 80 QString
Chris@148 81 WavFileWriter::getError() const
Chris@148 82 {
Chris@148 83 return m_error;
Chris@148 84 }
Chris@148 85
Chris@684 86 QString
Chris@684 87 WavFileWriter::getWriteFilename() const
Chris@684 88 {
Chris@684 89 if (m_temp) {
Chris@684 90 return m_temp->getTemporaryFilename();
Chris@684 91 } else {
Chris@684 92 return m_path;
Chris@684 93 }
Chris@684 94 }
Chris@684 95
Chris@174 96 bool
Chris@174 97 WavFileWriter::writeModel(DenseTimeValueModel *source,
Chris@174 98 MultiSelection *selection)
Chris@148 99 {
Chris@174 100 if (source->getChannelCount() != m_channels) {
Chris@690 101 SVDEBUG << "WavFileWriter::writeModel: Wrong number of channels ("
Chris@174 102 << source->getChannelCount() << " != " << m_channels << ")"
Chris@687 103 << endl;
Chris@174 104 m_error = QString("Failed to write model to audio file '%1'")
Chris@684 105 .arg(getWriteFilename());
Chris@174 106 return false;
Chris@148 107 }
Chris@148 108
Chris@174 109 if (!m_file) {
Chris@174 110 m_error = QString("Failed to write model to audio file '%1': File not open")
Chris@684 111 .arg(getWriteFilename());
Chris@174 112 return false;
Chris@174 113 }
Chris@148 114
Chris@174 115 bool ownSelection = false;
Chris@174 116 if (!selection) {
Chris@148 117 selection = new MultiSelection;
Chris@174 118 selection->setSelection(Selection(source->getStartFrame(),
Chris@174 119 source->getEndFrame()));
Chris@174 120 ownSelection = true;
Chris@148 121 }
Chris@148 122
Chris@929 123 int bs = 2048;
Chris@148 124 float *ub = new float[bs]; // uninterleaved buffer (one channel)
Chris@174 125 float *ib = new float[bs * m_channels]; // interleaved buffer
Chris@148 126
Chris@148 127 for (MultiSelection::SelectionList::iterator i =
Chris@148 128 selection->getSelections().begin();
Chris@148 129 i != selection->getSelections().end(); ++i) {
Chris@148 130
Chris@929 131 int f0(i->getStartFrame()), f1(i->getEndFrame());
Chris@148 132
Chris@929 133 for (int f = f0; f < f1; f += bs) {
Chris@148 134
Chris@929 135 int n = std::min(bs, f1 - f);
Chris@148 136
Chris@174 137 for (int c = 0; c < int(m_channels); ++c) {
Chris@300 138 source->getData(c, f, n, ub);
Chris@929 139 for (int i = 0; i < n; ++i) {
Chris@174 140 ib[i * m_channels + c] = ub[i];
Chris@148 141 }
Chris@148 142 }
Chris@148 143
Chris@174 144 sf_count_t written = sf_writef_float(m_file, ib, n);
Chris@148 145
Chris@148 146 if (written < n) {
Chris@148 147 m_error = QString("Only wrote %1 of %2 frames at file frame %3")
Chris@148 148 .arg(written).arg(n).arg(f);
Chris@148 149 break;
Chris@148 150 }
Chris@148 151 }
Chris@148 152 }
Chris@148 153
Chris@148 154 delete[] ub;
Chris@148 155 delete[] ib;
Chris@174 156 if (ownSelection) delete selection;
Chris@174 157
Chris@174 158 return isOK();
Chris@174 159 }
Chris@174 160
Chris@174 161 bool
Chris@929 162 WavFileWriter::writeSamples(float **samples, int count)
Chris@174 163 {
Chris@174 164 if (!m_file) {
Chris@174 165 m_error = QString("Failed to write model to audio file '%1': File not open")
Chris@684 166 .arg(getWriteFilename());
Chris@174 167 return false;
Chris@174 168 }
Chris@174 169
Chris@174 170 float *b = new float[count * m_channels];
Chris@929 171 for (int i = 0; i < int(count); ++i) {
Chris@929 172 for (int c = 0; c < int(m_channels); ++c) {
Chris@174 173 b[i * m_channels + c] = samples[c][i];
Chris@174 174 }
Chris@174 175 }
Chris@174 176
Chris@174 177 sf_count_t written = sf_writef_float(m_file, b, count);
Chris@174 178
Chris@174 179 delete[] b;
Chris@174 180
Chris@929 181 if (written < int(count)) {
Chris@174 182 m_error = QString("Only wrote %1 of %2 frames")
Chris@174 183 .arg(written).arg(count);
Chris@174 184 }
Chris@174 185
Chris@174 186 return isOK();
Chris@174 187 }
Chris@174 188
Chris@174 189 bool
Chris@174 190 WavFileWriter::close()
Chris@174 191 {
Chris@174 192 if (m_file) {
Chris@174 193 sf_close(m_file);
Chris@174 194 m_file = 0;
Chris@174 195 }
Chris@684 196 if (m_temp) {
Chris@684 197 m_temp->moveToTarget();
Chris@684 198 delete m_temp;
Chris@684 199 m_temp = 0;
Chris@684 200 }
Chris@174 201 return true;
Chris@148 202 }
Chris@148 203