comparison data/fileio/CodedAudioFileReader.cpp @ 297:c022976d18e8

* Merge from sv-match-alignment branch (excluding alignment-specific document). - add aggregate wave model (not yet complete enough to be added as a true model in a layer, but there's potential) - add play solo mode - add alignment model -- unused in plain SV - fix two plugin leaks - add m3u playlist support (opens all files at once, potentially hazardous) - fix retrieval of pre-encoded URLs - add ability to resample audio files on import, so as to match rates with other files previously loaded; add preference for same - add preliminary support in transform code for range and rate of transform input - reorganise preferences dialog, move dark-background option to preferences, add option for temporary directory location
author Chris Cannam
date Fri, 28 Sep 2007 13:56:38 +0000
parents 92e8dbde73cd
children 4fc6f49436b3
comparison
equal deleted inserted replaced
296:2b6c99b607f1 297:c022976d18e8
2 2
3 /* 3 /*
4 Sonic Visualiser 4 Sonic Visualiser
5 An audio file viewer and annotation editor. 5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London. 6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006 Chris Cannam. 7 This file copyright 2006-2007 Chris Cannam and QMUL.
8 8
9 This program is free software; you can redistribute it and/or 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 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 11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file 12 License, or (at your option) any later version. See the file
17 17
18 #include "WavFileReader.h" 18 #include "WavFileReader.h"
19 #include "base/TempDirectory.h" 19 #include "base/TempDirectory.h"
20 #include "base/Exceptions.h" 20 #include "base/Exceptions.h"
21 #include "base/Profiler.h" 21 #include "base/Profiler.h"
22 #include "base/Serialiser.h"
23 #include "base/Resampler.h"
22 24
23 #include <iostream> 25 #include <iostream>
24 #include <QDir> 26 #include <QDir>
25 #include <QMutexLocker> 27 #include <QMutexLocker>
26 28
27 CodedAudioFileReader::CodedAudioFileReader(CacheMode cacheMode) : 29 CodedAudioFileReader::CodedAudioFileReader(CacheMode cacheMode,
30 size_t targetRate) :
28 m_cacheMode(cacheMode), 31 m_cacheMode(cacheMode),
29 m_initialised(false), 32 m_initialised(false),
33 m_serialiser(0),
34 m_fileRate(0),
30 m_cacheFileWritePtr(0), 35 m_cacheFileWritePtr(0),
31 m_cacheFileReader(0), 36 m_cacheFileReader(0),
32 m_cacheWriteBuffer(0), 37 m_cacheWriteBuffer(0),
33 m_cacheWriteBufferIndex(0), 38 m_cacheWriteBufferIndex(0),
34 m_cacheWriteBufferSize(16384) 39 m_cacheWriteBufferSize(16384),
35 { 40 m_resampler(0),
41 m_resampleBuffer(0)
42 {
43 std::cerr << "CodedAudioFileReader::CodedAudioFileReader: rate " << targetRate << std::endl;
44
45 m_frameCount = 0;
46 m_sampleRate = targetRate;
36 } 47 }
37 48
38 CodedAudioFileReader::~CodedAudioFileReader() 49 CodedAudioFileReader::~CodedAudioFileReader()
39 { 50 {
40 QMutexLocker locker(&m_cacheMutex); 51 QMutexLocker locker(&m_cacheMutex);
41 52
53 endSerialised();
54
42 if (m_cacheFileWritePtr) sf_close(m_cacheFileWritePtr); 55 if (m_cacheFileWritePtr) sf_close(m_cacheFileWritePtr);
43 if (m_cacheFileReader) delete m_cacheFileReader; 56
44 if (m_cacheWriteBuffer) delete[] m_cacheWriteBuffer; 57 delete m_cacheFileReader;
58 delete[] m_cacheWriteBuffer;
45 59
46 if (m_cacheFileName != "") { 60 if (m_cacheFileName != "") {
47 if (!QFile(m_cacheFileName).remove()) { 61 if (!QFile(m_cacheFileName).remove()) {
48 std::cerr << "WARNING: CodedAudioFileReader::~CodedAudioFileReader: Failed to delete cache file \"" << m_cacheFileName.toStdString() << "\"" << std::endl; 62 std::cerr << "WARNING: CodedAudioFileReader::~CodedAudioFileReader: Failed to delete cache file \"" << m_cacheFileName.toStdString() << "\"" << std::endl;
49 } 63 }
50 } 64 }
65
66 delete m_resampler;
67 delete[] m_resampleBuffer;
68 }
69
70 void
71 CodedAudioFileReader::startSerialised(QString id)
72 {
73 // std::cerr << "CodedAudioFileReader::startSerialised(" << id.toStdString() << ")" << std::endl;
74
75 delete m_serialiser;
76 m_serialiser = new Serialiser(id);
77 }
78
79 void
80 CodedAudioFileReader::endSerialised()
81 {
82 // std::cerr << "CodedAudioFileReader::endSerialised" << std::endl;
83
84 delete m_serialiser;
85 m_serialiser = 0;
51 } 86 }
52 87
53 void 88 void
54 CodedAudioFileReader::initialiseDecodeCache() 89 CodedAudioFileReader::initialiseDecodeCache()
55 { 90 {
56 QMutexLocker locker(&m_cacheMutex); 91 QMutexLocker locker(&m_cacheMutex);
57 92
93 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: file rate = " << m_fileRate << std::endl;
94
95 if (m_fileRate == 0) {
96 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: ERROR: File sample rate unknown (bug in subclass implementation?)" << std::endl;
97 m_fileRate = 48000; // got to have something
98 }
99 if (m_sampleRate == 0) {
100 m_sampleRate = m_fileRate;
101 }
102 if (m_fileRate != m_sampleRate) {
103 std::cerr << "CodedAudioFileReader: resampling " << m_fileRate << " -> " << m_sampleRate << std::endl;
104 m_resampler = new Resampler(Resampler::FastestTolerable,
105 m_channelCount,
106 m_cacheWriteBufferSize);
107 float ratio = float(m_sampleRate) / float(m_fileRate);
108 m_resampleBuffer = new float
109 [lrintf(ceilf(m_cacheWriteBufferSize * m_channelCount * ratio))];
110 }
111
112 m_cacheWriteBuffer = new float[m_cacheWriteBufferSize * m_channelCount];
113 m_cacheWriteBufferIndex = 0;
114
58 if (m_cacheMode == CacheInTemporaryFile) { 115 if (m_cacheMode == CacheInTemporaryFile) {
59
60 m_cacheWriteBuffer = new float[m_cacheWriteBufferSize * m_channelCount];
61 m_cacheWriteBufferIndex = 0;
62 116
63 try { 117 try {
64 QDir dir(TempDirectory::getInstance()->getPath()); 118 QDir dir(TempDirectory::getInstance()->getPath());
65 m_cacheFileName = dir.filePath(QString("decoded_%1.wav") 119 m_cacheFileName = dir.filePath(QString("decoded_%1.wav")
66 .arg((intptr_t)this)); 120 .arg((intptr_t)this));
67 121
68 SF_INFO fileInfo; 122 SF_INFO fileInfo;
69 fileInfo.samplerate = m_sampleRate; 123 fileInfo.samplerate = m_sampleRate;
70 fileInfo.channels = m_channelCount; 124 fileInfo.channels = m_channelCount;
71 fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; 125
126 // No point in writing 24-bit or float; generally this
127 // class is used for decoding files that have come from a
128 // 16 bit source or that decode to only 16 bits anyway.
129 fileInfo.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
72 130
73 m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(), 131 m_cacheFileWritePtr = sf_open(m_cacheFileName.toLocal8Bit(),
74 SFM_WRITE, &fileInfo); 132 SFM_WRITE, &fileInfo);
75 133
76 if (m_cacheFileWritePtr) { 134 if (m_cacheFileWritePtr) {
77 135
78 //!!! really want to do this now only if we're in a 136 // Ideally we would do this now only if we were in a
79 //threaded mode -- creating the reader later if we're 137 // threaded mode -- creating the reader later if we're
80 //not threaded -- but we don't have access to that 138 // not threaded -- but we don't have access to that
81 //information here 139 // information here
82 140
83 m_cacheFileReader = new WavFileReader(m_cacheFileName); 141 m_cacheFileReader = new WavFileReader(m_cacheFileName);
84 142
85 if (!m_cacheFileReader->isOK()) { 143 if (!m_cacheFileReader->isOK()) {
86 std::cerr << "ERROR: CodedAudioFileReader::initialiseDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError().toStdString() << std::endl; 144 std::cerr << "ERROR: CodedAudioFileReader::initialiseDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError().toStdString() << std::endl;
87 delete m_cacheFileReader; 145 delete m_cacheFileReader;
88 m_cacheFileReader = 0; 146 m_cacheFileReader = 0;
89 m_cacheMode = CacheInMemory; 147 m_cacheMode = CacheInMemory;
90 sf_close(m_cacheFileWritePtr); 148 sf_close(m_cacheFileWritePtr);
91 } 149 }
150
92 } else { 151 } else {
93 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to open cache file \"" << m_cacheFileName.toStdString() << "\" (" << m_channelCount << " channels, sample rate " << m_sampleRate << " for writing, falling back to in-memory cache" << std::endl; 152 std::cerr << "CodedAudioFileReader::initialiseDecodeCache: failed to open cache file \"" << m_cacheFileName.toStdString() << "\" (" << m_channelCount << " channels, sample rate " << m_sampleRate << " for writing, falling back to in-memory cache" << std::endl;
94 m_cacheMode = CacheInMemory; 153 m_cacheMode = CacheInMemory;
95 } 154 }
96 155
106 165
107 m_initialised = true; 166 m_initialised = true;
108 } 167 }
109 168
110 void 169 void
111 CodedAudioFileReader::addSampleToDecodeCache(float sample) 170 CodedAudioFileReader::addSamplesToDecodeCache(float **samples, size_t nframes)
112 { 171 {
113 QMutexLocker locker(&m_cacheMutex); 172 QMutexLocker locker(&m_cacheMutex);
114 173
115 if (!m_initialised) return; 174 if (!m_initialised) return;
116 175
117 switch (m_cacheMode) { 176 for (size_t i = 0; i < nframes; ++i) {
118 177
119 case CacheInTemporaryFile: 178 for (size_t c = 0; c < m_channelCount; ++c) {
120 179
180 float sample = samples[c][i];
181
182 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
183
184 if (m_cacheWriteBufferIndex ==
185 m_cacheWriteBufferSize * m_channelCount) {
186
187 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
188 m_cacheWriteBufferIndex = 0;
189 }
190
191 if (m_cacheWriteBufferIndex % 10240 == 0 &&
192 m_cacheFileReader) {
193 m_cacheFileReader->updateFrameCount();
194 }
195 }
196 }
197 }
198
199 void
200 CodedAudioFileReader::addSamplesToDecodeCache(float *samples, size_t nframes)
201 {
202 QMutexLocker locker(&m_cacheMutex);
203
204 if (!m_initialised) return;
205
206 for (size_t i = 0; i < nframes; ++i) {
207
208 for (size_t c = 0; c < m_channelCount; ++c) {
209
210 float sample = samples[i * m_channelCount + c];
211
212 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
213
214 if (m_cacheWriteBufferIndex ==
215 m_cacheWriteBufferSize * m_channelCount) {
216
217 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
218 m_cacheWriteBufferIndex = 0;
219 }
220
221 if (m_cacheWriteBufferIndex % 10240 == 0 &&
222 m_cacheFileReader) {
223 m_cacheFileReader->updateFrameCount();
224 }
225 }
226 }
227 }
228
229 void
230 CodedAudioFileReader::addSamplesToDecodeCache(const SampleBlock &samples)
231 {
232 QMutexLocker locker(&m_cacheMutex);
233
234 if (!m_initialised) return;
235
236 for (size_t i = 0; i < samples.size(); ++i) {
237
238 float sample = samples[i];
239
121 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample; 240 m_cacheWriteBuffer[m_cacheWriteBufferIndex++] = sample;
122 241
123 if (m_cacheWriteBufferIndex == 242 if (m_cacheWriteBufferIndex ==
124 m_cacheWriteBufferSize * m_channelCount) { 243 m_cacheWriteBufferSize * m_channelCount) {
125 244
126 //!!! check for return value! out of disk space, etc! 245 pushBuffer(m_cacheWriteBuffer, m_cacheWriteBufferSize, false);
127 sf_writef_float(m_cacheFileWritePtr,
128 m_cacheWriteBuffer,
129 m_cacheWriteBufferSize);
130
131 m_cacheWriteBufferIndex = 0; 246 m_cacheWriteBufferIndex = 0;
132 } 247 }
133 248
134 if (m_cacheWriteBufferIndex % 10240 == 0 && 249 if (m_cacheWriteBufferIndex % 10240 == 0 &&
135 m_cacheFileReader) { 250 m_cacheFileReader) {
136 m_cacheFileReader->updateFrameCount(); 251 m_cacheFileReader->updateFrameCount();
137 } 252 }
138 break;
139
140 case CacheInMemory:
141 m_data.push_back(sample);
142 break;
143 } 253 }
144 } 254 }
145 255
146 void 256 void
147 CodedAudioFileReader::finishDecodeCache() 257 CodedAudioFileReader::finishDecodeCache()
153 if (!m_initialised) { 263 if (!m_initialised) {
154 std::cerr << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << std::endl; 264 std::cerr << "WARNING: CodedAudioFileReader::finishDecodeCache: Cache was never initialised!" << std::endl;
155 return; 265 return;
156 } 266 }
157 267
158 switch (m_cacheMode) { 268 if (m_cacheWriteBufferIndex > 0) {
159 269 //!!! check for return value! out of disk space, etc!
160 case CacheInTemporaryFile: 270 pushBuffer(m_cacheWriteBuffer,
161 271 m_cacheWriteBufferIndex / m_channelCount,
162 if (m_cacheWriteBufferIndex > 0) { 272 true);
163 //!!! check for return value! out of disk space, etc! 273 }
164 sf_writef_float(m_cacheFileWritePtr, 274
165 m_cacheWriteBuffer, 275 delete[] m_cacheWriteBuffer;
166 m_cacheWriteBufferIndex / m_channelCount); 276 m_cacheWriteBuffer = 0;
167 } 277
168 278 delete[] m_resampleBuffer;
169 if (m_cacheWriteBuffer) { 279 m_resampleBuffer = 0;
170 delete[] m_cacheWriteBuffer; 280
171 m_cacheWriteBuffer = 0; 281 delete m_resampler;
172 } 282 m_resampler = 0;
173 283
174 m_cacheWriteBufferIndex = 0; 284 if (m_cacheMode == CacheInTemporaryFile) {
175
176 sf_close(m_cacheFileWritePtr); 285 sf_close(m_cacheFileWritePtr);
177 m_cacheFileWritePtr = 0; 286 m_cacheFileWritePtr = 0;
178 287 if (m_cacheFileReader) m_cacheFileReader->updateFrameCount();
179 m_cacheFileReader->updateFrameCount(); 288 }
180 /* 289 }
181 m_cacheFileReader = new WavFileReader(m_cacheFileName); 290
182 291 void
183 if (!m_cacheFileReader->isOK()) { 292 CodedAudioFileReader::pushBuffer(float *buffer, size_t sz, bool final)
184 std::cerr << "ERROR: CodedAudioFileReader::finishDecodeCache: Failed to construct WAV file reader for temporary file: " << m_cacheFileReader->getError().toStdString() << std::endl; 293 {
185 delete m_cacheFileReader; 294 if (m_resampler) {
186 m_cacheFileReader = 0; 295
187 }*/ 296 float ratio = float(m_sampleRate) / float(m_fileRate);
188 297
298 if (ratio != 1.f) {
299 size_t out = m_resampler->resampleInterleaved
300 (buffer,
301 m_resampleBuffer,
302 sz,
303 ratio,
304 final);
305
306 buffer = m_resampleBuffer;
307 sz = out;
308 }
309 }
310
311 m_frameCount += sz;
312
313 switch (m_cacheMode) {
314
315 case CacheInTemporaryFile:
316 //!!! check for return value! out of disk space, etc!
317 sf_writef_float(m_cacheFileWritePtr, buffer, sz);
189 break; 318 break;
190 319
191 case CacheInMemory: 320 case CacheInMemory:
192 // nothing to do 321 for (size_t s = 0; s < sz; ++s) {
322 m_data.push_back(buffer[sz]);
323 }
324 MUNLOCK_SAMPLEBLOCK(m_data);
193 break; 325 break;
194 } 326 }
195 } 327 }
196 328
197 void 329 void