Mercurial > hg > svcore
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 |