In addition to the thesis, reference implementations in all major languages are available online. This function uses a specialised implementation for the AudioPoint structure: https://github.com/sveinn-steinarsson/flot-downsample.
318{
319 const auto source_size = source.get_sample_count();
320
321
322
323 if (threshold == 0 || source_size == 0)
324
325 return std::unique_ptr<Signal>(new Signal(name));
326
327 if (threshold >= source_size)
328
330
331 auto downsampled = std::unique_ptr<Signal>(new Signal(name));
332 downsampled->reserve_samples(threshold);
333
334 if (threshold == 1) {
335
336 downsampled->emplace_sample(source.get_time_at_index(0), source[0]);
337 return downsampled;
338 }
339
340 if (threshold == 2) {
341
342 downsampled->emplace_sample(source.get_time_at_index(0), source[0]);
343 downsampled->emplace_sample(source.get_time_at_index(source_size - 1), source[source_size - 1]);
344 return downsampled;
345 }
346
347 const auto bucket_size =
348 static_cast<unsigned int>(static_cast<double>(source_size - 2) / static_cast<double>(threshold - 2));
349 std::size_t fixed_point_idx = 0;
350
351
352 downsampled->emplace_sample(source.get_time_at_index(0), source[0]);
353
354 for (std::size_t dst_point_idx = 0; dst_point_idx < threshold - 2; ++dst_point_idx) {
357
358
359
360 const auto average_range_start =
static_cast<std::size_t
>(
std::floor((dst_point_idx + 1) * bucket_size)) + 1;
361 const auto average_range_end =
362 std::min(
static_cast<std::uint64_t
>(
std::floor((dst_point_idx + 2) * bucket_size)) + 1, source_size);
363 const auto average_range_length = average_range_end - average_range_start;
364
365 for (auto range_idx = average_range_start; range_idx < average_range_end; ++range_idx) {
366 average_time += source.get_time_at_index(range_idx);
367 average_amplitude += source[range_idx];
368 }
369
372
373
374 const auto fp_time = source.get_time_at_index(fixed_point_idx);
375 const auto fp_amplitude = source[fixed_point_idx];
376
377
378 const auto range_lower =
static_cast<std::size_t
>(
std::floor(dst_point_idx * bucket_size)) + 1;
380 static_cast<std::uint64_t
>(
std::floor((dst_point_idx + 1) * bucket_size)) + 1,
381 source_size - 1
382 );
383
384
386 auto next_fixed_point_idx = range_lower;
387
388
389 for (auto range_idx = range_lower; range_idx < range_upper; ++range_idx) {
390 const float area = std::abs(
391 (fp_time - average_time) * (source[range_idx] - fp_amplitude) -
392 (fp_time - source.get_time_at_index(range_idx)) * (average_amplitude - fp_amplitude)
393 );
394
395 if (area > max_area) {
396 max_area = area;
397 next_fixed_point_idx = range_idx;
398 }
399 }
400
401
402
403
404
405 downsampled->emplace_sample(source.get_time_at_index(next_fixed_point_idx), source[next_fixed_point_idx]);
406
407 fixed_point_idx = next_fixed_point_idx;
408 }
409
410
411 downsampled->emplace_sample(source.get_time_at_index(source_size - 1), source[source_size - 1]);
412
413 assert(downsampled->get_sample_count() == threshold);
414 return downsampled;
415}
float AmplitudeT
Type for sample amplitudes.
float TimeT
Type for sample times.