Mercurial > hg > svcore
comparison data/model/WaveFileModel.cpp @ 300:5877d68815c7
* Change WaveFileModel API from getValues(start,end) to getData(start,count).
It's much less error-prone to pass in frame counts instead of start/end
locations. Should have done this ages ago. This closes #1794563.
* Add option to apply a transform to only the selection region, instead of
the whole audio.
* (to make the above work properly) Add start frame offset to wave models
author | Chris Cannam |
---|---|
date | Mon, 01 Oct 2007 13:48:38 +0000 |
parents | c022976d18e8 |
children | 70a232b1f12a |
comparison
equal
deleted
inserted
replaced
299:576be0d0d218 | 300:5877d68815c7 |
---|---|
39 WaveFileModel::m_zoomConstraint; | 39 WaveFileModel::m_zoomConstraint; |
40 | 40 |
41 WaveFileModel::WaveFileModel(QString path, size_t targetRate) : | 41 WaveFileModel::WaveFileModel(QString path, size_t targetRate) : |
42 m_path(path), | 42 m_path(path), |
43 m_myReader(true), | 43 m_myReader(true), |
44 m_startFrame(0), | |
44 m_fillThread(0), | 45 m_fillThread(0), |
45 m_updateTimer(0), | 46 m_updateTimer(0), |
46 m_lastFillExtent(0), | 47 m_lastFillExtent(0), |
47 m_exiting(false) | 48 m_exiting(false) |
48 { | 49 { |
54 } | 55 } |
55 | 56 |
56 WaveFileModel::WaveFileModel(QString path, QString originalLocation, size_t targetRate) : | 57 WaveFileModel::WaveFileModel(QString path, QString originalLocation, size_t targetRate) : |
57 m_path(originalLocation), | 58 m_path(originalLocation), |
58 m_myReader(true), | 59 m_myReader(true), |
60 m_startFrame(0), | |
59 m_fillThread(0), | 61 m_fillThread(0), |
60 m_updateTimer(0), | 62 m_updateTimer(0), |
61 m_lastFillExtent(0), | 63 m_lastFillExtent(0), |
62 m_exiting(false) | 64 m_exiting(false) |
63 { | 65 { |
69 } | 71 } |
70 | 72 |
71 WaveFileModel::WaveFileModel(QString path, AudioFileReader *reader) : | 73 WaveFileModel::WaveFileModel(QString path, AudioFileReader *reader) : |
72 m_path(path), | 74 m_path(path), |
73 m_myReader(false), | 75 m_myReader(false), |
76 m_startFrame(0), | |
74 m_fillThread(0), | 77 m_fillThread(0), |
75 m_updateTimer(0), | 78 m_updateTimer(0), |
76 m_lastFillExtent(0), | 79 m_lastFillExtent(0), |
77 m_exiting(false) | 80 m_exiting(false) |
78 { | 81 { |
160 if (rate == 0) rate = getSampleRate(); | 163 if (rate == 0) rate = getSampleRate(); |
161 return rate; | 164 return rate; |
162 } | 165 } |
163 | 166 |
164 size_t | 167 size_t |
165 WaveFileModel::getValues(int channel, size_t start, size_t end, | 168 WaveFileModel::getData(int channel, size_t start, size_t count, |
166 float *buffer) const | 169 float *buffer) const |
167 { | 170 { |
168 // Always read these directly from the file. | 171 // Always read these directly from the file. |
169 // This is used for e.g. audio playback. | 172 // This is used for e.g. audio playback. |
170 // Could be much more efficient (although compiler optimisation will help) | 173 // Could be much more efficient (although compiler optimisation will help) |
171 | 174 |
172 if (end < start) { | 175 if (start > m_startFrame) { |
173 std::cerr << "ERROR: WaveFileModel::getValues[float]: end < start (" | 176 start -= m_startFrame; |
174 << end << " < " << start << ")" << std::endl; | 177 } else { |
175 assert(end >= start); | 178 for (size_t i = 0; i < count; ++i) buffer[i] = 0.f; |
176 } | 179 if (count <= m_startFrame - start) { |
177 | 180 return 0; |
178 if (!m_reader || !m_reader->isOK()) return 0; | 181 } else { |
182 count -= (m_startFrame - start); | |
183 start = 0; | |
184 } | |
185 } | |
186 | |
187 if (!m_reader || !m_reader->isOK() || count == 0) { | |
188 for (size_t i = 0; i < count; ++i) buffer[i] = 0.f; | |
189 return 0; | |
190 } | |
179 | 191 |
180 #ifdef DEBUG_WAVE_FILE_MODEL | 192 #ifdef DEBUG_WAVE_FILE_MODEL |
181 // std::cerr << "WaveFileModel::getValues(" << channel << ", " | 193 // std::cerr << "WaveFileModel::getValues(" << channel << ", " |
182 // << start << ", " << end << "): calling reader" << std::endl; | 194 // << start << ", " << end << "): calling reader" << std::endl; |
183 #endif | 195 #endif |
184 | 196 |
185 SampleBlock frames; | 197 SampleBlock frames; |
186 m_reader->getInterleavedFrames(start, end - start, frames); | 198 m_reader->getInterleavedFrames(start, count, frames); |
187 | 199 |
188 size_t i = 0; | 200 size_t i = 0; |
189 | 201 |
190 int ch0 = channel, ch1 = channel, channels = getChannelCount(); | 202 int ch0 = channel, ch1 = channel, channels = getChannelCount(); |
191 if (channel == -1) { | 203 if (channel == -1) { |
192 ch0 = 0; | 204 ch0 = 0; |
193 ch1 = channels - 1; | 205 ch1 = channels - 1; |
194 } | 206 } |
195 | 207 |
196 while (i < end - start) { | 208 while (i < count) { |
197 | 209 |
198 buffer[i] = 0.0; | 210 buffer[i] = 0.0; |
199 | 211 |
200 for (int ch = ch0; ch <= ch1; ++ch) { | 212 for (int ch = ch0; ch <= ch1; ++ch) { |
201 | 213 |
211 | 223 |
212 return i; | 224 return i; |
213 } | 225 } |
214 | 226 |
215 size_t | 227 size_t |
216 WaveFileModel::getValues(int channel, size_t start, size_t end, | 228 WaveFileModel::getData(int channel, size_t start, size_t count, |
217 double *buffer) const | 229 double *buffer) const |
218 { | 230 { |
219 if (end < start) { | 231 if (start > m_startFrame) { |
220 std::cerr << "ERROR: WaveFileModel::getValues[double]: end < start (" | 232 start -= m_startFrame; |
221 << end << " < " << start << ")" << std::endl; | 233 } else { |
222 assert(end >= start); | 234 for (size_t i = 0; i < count; ++i) buffer[i] = 0.0; |
223 } | 235 if (count <= m_startFrame - start) { |
224 | 236 return 0; |
225 if (!m_reader || !m_reader->isOK()) return 0; | 237 } else { |
238 count -= (m_startFrame - start); | |
239 start = 0; | |
240 } | |
241 } | |
242 | |
243 if (!m_reader || !m_reader->isOK() || count == 0) { | |
244 for (size_t i = 0; i < count; ++i) buffer[i] = 0.0; | |
245 return 0; | |
246 } | |
226 | 247 |
227 SampleBlock frames; | 248 SampleBlock frames; |
228 m_reader->getInterleavedFrames(start, end - start, frames); | 249 m_reader->getInterleavedFrames(start, count, frames); |
229 | 250 |
230 size_t i = 0; | 251 size_t i = 0; |
231 | 252 |
232 int ch0 = channel, ch1 = channel, channels = getChannelCount(); | 253 int ch0 = channel, ch1 = channel, channels = getChannelCount(); |
233 if (channel == -1) { | 254 if (channel == -1) { |
234 ch0 = 0; | 255 ch0 = 0; |
235 ch1 = channels - 1; | 256 ch1 = channels - 1; |
236 } | 257 } |
237 | 258 |
238 while (i < end - start) { | 259 while (i < count) { |
239 | 260 |
240 buffer[i] = 0.0; | 261 buffer[i] = 0.0; |
241 | 262 |
242 for (int ch = ch0; ch <= ch1; ++ch) { | 263 for (int ch = ch0; ch <= ch1; ++ch) { |
243 | 264 |
253 | 274 |
254 return i; | 275 return i; |
255 } | 276 } |
256 | 277 |
257 void | 278 void |
258 WaveFileModel::getRanges(size_t channel, size_t start, size_t end, | 279 WaveFileModel::getSummaries(size_t channel, size_t start, size_t count, |
259 RangeBlock &ranges, size_t &blockSize) const | 280 RangeBlock &ranges, size_t &blockSize) const |
260 { | 281 { |
261 ranges.clear(); | 282 ranges.clear(); |
262 if (!isOK()) return; | 283 if (!isOK()) return; |
263 | 284 |
264 if (end <= start) { | 285 if (start > m_startFrame) start -= m_startFrame; |
265 std::cerr << "WARNING: Internal error: end <= start in WaveFileModel::getRanges (end = " << end << ", start = " << start << ", blocksize = " << blockSize << ")" << std::endl; | 286 else if (count <= m_startFrame - start) return; |
266 return; | 287 else { |
288 count -= (m_startFrame - start); | |
289 start = 0; | |
267 } | 290 } |
268 | 291 |
269 int cacheType = 0; | 292 int cacheType = 0; |
270 int power = m_zoomConstraint.getMinCachePower(); | 293 int power = m_zoomConstraint.getMinCachePower(); |
271 blockSize = m_zoomConstraint.getNearestBlockSize | 294 blockSize = m_zoomConstraint.getNearestBlockSize |
282 // We could fairly trivially handle this for most cases that | 305 // We could fairly trivially handle this for most cases that |
283 // matter by putting a single cache in getInterleavedFrames | 306 // matter by putting a single cache in getInterleavedFrames |
284 // for short queries. | 307 // for short queries. |
285 | 308 |
286 SampleBlock frames; | 309 SampleBlock frames; |
287 m_reader->getInterleavedFrames(start, end - start, frames); | 310 m_reader->getInterleavedFrames(start, count, frames); |
288 float max = 0.0, min = 0.0, total = 0.0; | 311 float max = 0.0, min = 0.0, total = 0.0; |
289 size_t i = 0, count = 0; | 312 size_t i = 0, got = 0; |
290 | 313 |
291 while (i < end - start) { | 314 while (i < count) { |
292 | 315 |
293 size_t index = i * channels + channel; | 316 size_t index = i * channels + channel; |
294 if (index >= frames.size()) break; | 317 if (index >= frames.size()) break; |
295 | 318 |
296 float sample = frames[index]; | 319 float sample = frames[index]; |
297 if (sample > max || count == 0) max = sample; | 320 if (sample > max || got == 0) max = sample; |
298 if (sample < min || count == 0) min = sample; | 321 if (sample < min || got == 0) min = sample; |
299 total += fabsf(sample); | 322 total += fabsf(sample); |
300 | 323 |
301 ++i; | 324 ++i; |
302 ++count; | 325 ++got; |
303 | 326 |
304 if (count == blockSize) { | 327 if (got == blockSize) { |
305 ranges.push_back(Range(min, max, total / count)); | 328 ranges.push_back(Range(min, max, total / got)); |
306 min = max = total = 0.0f; | 329 min = max = total = 0.0f; |
307 count = 0; | 330 got = 0; |
308 } | 331 } |
309 } | 332 } |
310 | 333 |
311 if (count > 0) { | 334 if (got > 0) { |
312 ranges.push_back(Range(min, max, total / count)); | 335 ranges.push_back(Range(min, max, total / got)); |
313 } | 336 } |
314 | 337 |
315 return; | 338 return; |
316 | 339 |
317 } else { | 340 } else { |
329 cacheBlock = ((unsigned int)((1 << m_zoomConstraint.getMinCachePower()) * sqrt(2) + 0.01)); | 352 cacheBlock = ((unsigned int)((1 << m_zoomConstraint.getMinCachePower()) * sqrt(2) + 0.01)); |
330 div = ((unsigned int)((1 << power) * sqrt(2) + 0.01)) / cacheBlock; | 353 div = ((unsigned int)((1 << power) * sqrt(2) + 0.01)) / cacheBlock; |
331 } | 354 } |
332 | 355 |
333 size_t startIndex = start / cacheBlock; | 356 size_t startIndex = start / cacheBlock; |
334 size_t endIndex = end / cacheBlock; | 357 size_t endIndex = (start + count) / cacheBlock; |
335 | 358 |
336 float max = 0.0, min = 0.0, total = 0.0; | 359 float max = 0.0, min = 0.0, total = 0.0; |
337 size_t i = 0, count = 0; | 360 size_t i = 0, got = 0; |
338 | 361 |
339 #ifdef DEBUG_WAVE_FILE_MODEL | 362 #ifdef DEBUG_WAVE_FILE_MODEL |
340 cerr << "blockSize is " << blockSize << ", cacheBlock " << cacheBlock << ", start " << start << ", end " << end << " (frame count " << getFrameCount() << "), power is " << power << ", div is " << div << ", startIndex " << startIndex << ", endIndex " << endIndex << endl; | 363 cerr << "blockSize is " << blockSize << ", cacheBlock " << cacheBlock << ", start " << start << ", count " << count << " (frame count " << getFrameCount() << "), power is " << power << ", div is " << div << ", startIndex " << startIndex << ", endIndex " << endIndex << endl; |
341 #endif | 364 #endif |
342 | 365 |
343 for (i = 0; i < endIndex - startIndex; ) { | 366 for (i = 0; i <= endIndex - startIndex; ) { |
344 | 367 |
345 size_t index = (i + startIndex) * channels + channel; | 368 size_t index = (i + startIndex) * channels + channel; |
346 if (index >= cache.size()) break; | 369 if (index >= cache.size()) break; |
347 | 370 |
348 const Range &range = cache[index]; | 371 const Range &range = cache[index]; |
349 if (range.max > max || count == 0) max = range.max; | 372 if (range.max > max || got == 0) max = range.max; |
350 if (range.min < min || count == 0) min = range.min; | 373 if (range.min < min || got == 0) min = range.min; |
351 total += range.absmean; | 374 total += range.absmean; |
352 | 375 |
353 ++i; | 376 ++i; |
354 ++count; | 377 ++got; |
355 | 378 |
356 if (count == div) { | 379 if (got == div) { |
357 ranges.push_back(Range(min, max, total / count)); | 380 ranges.push_back(Range(min, max, total / got)); |
358 min = max = total = 0.0f; | 381 min = max = total = 0.0f; |
359 count = 0; | 382 got = 0; |
360 } | 383 } |
361 } | 384 } |
362 | 385 |
363 if (count > 0) { | 386 if (got > 0) { |
364 ranges.push_back(Range(min, max, total / count)); | 387 ranges.push_back(Range(min, max, total / got)); |
365 } | 388 } |
366 } | 389 } |
367 | 390 |
368 #ifdef DEBUG_WAVE_FILE_MODEL | 391 #ifdef DEBUG_WAVE_FILE_MODEL |
369 cerr << "returning " << ranges.size() << " ranges" << endl; | 392 cerr << "returning " << ranges.size() << " ranges" << endl; |
370 #endif | 393 #endif |
371 return; | 394 return; |
372 } | 395 } |
373 | 396 |
374 WaveFileModel::Range | 397 WaveFileModel::Range |
375 WaveFileModel::getRange(size_t channel, size_t start, size_t end) const | 398 WaveFileModel::getSummary(size_t channel, size_t start, size_t count) const |
376 { | 399 { |
377 Range range; | 400 Range range; |
378 if (!isOK()) return range; | 401 if (!isOK()) return range; |
379 | 402 |
380 if (end <= start) { | 403 if (start > m_startFrame) start -= m_startFrame; |
381 std::cerr << "WARNING: Internal error: end <= start in WaveFileModel::getRange (end = " << end << ", start = " << start << ")" << std::endl; | 404 else if (count <= m_startFrame - start) return range; |
382 return range; | 405 else { |
406 count -= (m_startFrame - start); | |
407 start = 0; | |
383 } | 408 } |
384 | 409 |
385 size_t blockSize; | 410 size_t blockSize; |
386 for (blockSize = 1; blockSize <= end - start; blockSize *= 2); | 411 for (blockSize = 1; blockSize <= count; blockSize *= 2); |
387 blockSize /= 2; | 412 if (blockSize > 1) blockSize /= 2; |
388 | 413 |
389 bool first = false; | 414 bool first = false; |
390 | 415 |
391 size_t blockStart = (start / blockSize) * blockSize; | 416 size_t blockStart = (start / blockSize) * blockSize; |
392 size_t blockEnd = (end / blockSize) * blockSize; | 417 size_t blockEnd = ((start + count) / blockSize) * blockSize; |
393 | 418 |
394 if (blockStart < start) blockStart += blockSize; | 419 if (blockStart < start) blockStart += blockSize; |
395 | 420 |
396 if (blockEnd > blockStart) { | 421 if (blockEnd > blockStart) { |
397 RangeBlock ranges; | 422 RangeBlock ranges; |
398 getRanges(channel, blockStart, blockEnd, ranges, blockSize); | 423 getSummaries(channel, blockStart, blockEnd - blockStart, ranges, blockSize); |
399 for (size_t i = 0; i < ranges.size(); ++i) { | 424 for (size_t i = 0; i < ranges.size(); ++i) { |
400 if (first || ranges[i].min < range.min) range.min = ranges[i].min; | 425 if (first || ranges[i].min < range.min) range.min = ranges[i].min; |
401 if (first || ranges[i].max > range.max) range.max = ranges[i].max; | 426 if (first || ranges[i].max > range.max) range.max = ranges[i].max; |
402 if (first || ranges[i].absmean < range.absmean) range.absmean = ranges[i].absmean; | 427 if (first || ranges[i].absmean < range.absmean) range.absmean = ranges[i].absmean; |
403 first = false; | 428 first = false; |
404 } | 429 } |
405 } | 430 } |
406 | 431 |
407 if (blockStart > start) { | 432 if (blockStart > start) { |
408 Range startRange = getRange(channel, start, blockStart); | 433 Range startRange = getSummary(channel, start, blockStart - start); |
409 range.min = std::min(range.min, startRange.min); | 434 range.min = std::min(range.min, startRange.min); |
410 range.max = std::max(range.max, startRange.max); | 435 range.max = std::max(range.max, startRange.max); |
411 range.absmean = std::min(range.absmean, startRange.absmean); | 436 range.absmean = std::min(range.absmean, startRange.absmean); |
412 } | 437 } |
413 | 438 |
414 if (blockEnd < end) { | 439 if (blockEnd < start + count) { |
415 Range endRange = getRange(channel, blockEnd, end); | 440 Range endRange = getSummary(channel, blockEnd, start + count - blockEnd); |
416 range.min = std::min(range.min, endRange.min); | 441 range.min = std::min(range.min, endRange.min); |
417 range.max = std::max(range.max, endRange.max); | 442 range.max = std::max(range.max, endRange.max); |
418 range.absmean = std::min(range.absmean, endRange.absmean); | 443 range.absmean = std::min(range.absmean, endRange.absmean); |
419 } | 444 } |
420 | 445 |