annotate base/ColumnOp.cpp @ 1379:96a6ea30933e

Fix occasional off-by-one error in resampled audio file reader
author Chris Cannam
date Tue, 21 Feb 2017 17:42:40 +0000
parents 47ee4706055c
children 9ef1cc26024c
rev   line source
Chris@1267 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1267 2
Chris@1267 3 /*
Chris@1267 4 Sonic Visualiser
Chris@1267 5 An audio file viewer and annotation editor.
Chris@1267 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1267 7 This file copyright 2006-2016 Chris Cannam and QMUL.
Chris@1267 8
Chris@1267 9 This program is free software; you can redistribute it and/or
Chris@1267 10 modify it under the terms of the GNU General Public License as
Chris@1267 11 published by the Free Software Foundation; either version 2 of the
Chris@1267 12 License, or (at your option) any later version. See the file
Chris@1267 13 COPYING included with this distribution for more information.
Chris@1267 14 */
Chris@1267 15
Chris@1267 16 #include "ColumnOp.h"
Chris@1267 17
Chris@1267 18 #include <cmath>
Chris@1267 19 #include <algorithm>
Chris@1267 20 #include <iostream>
Chris@1267 21
Chris@1283 22 #include "base/Debug.h"
Chris@1283 23
Chris@1267 24 using namespace std;
Chris@1267 25
Chris@1267 26 ColumnOp::Column
Chris@1267 27 ColumnOp::fftScale(const Column &in, int fftSize)
Chris@1267 28 {
Chris@1267 29 return applyGain(in, 2.0 / fftSize);
Chris@1267 30 }
Chris@1267 31
Chris@1267 32 ColumnOp::Column
Chris@1267 33 ColumnOp::peakPick(const Column &in)
Chris@1267 34 {
Chris@1267 35 vector<float> out(in.size(), 0.f);
Chris@1267 36
Chris@1267 37 for (int i = 0; in_range_for(in, i); ++i) {
Chris@1267 38 if (isPeak(in, i)) {
Chris@1267 39 out[i] = in[i];
Chris@1267 40 }
Chris@1267 41 }
Chris@1267 42
Chris@1267 43 return out;
Chris@1267 44 }
Chris@1267 45
Chris@1267 46 ColumnOp::Column
Chris@1267 47 ColumnOp::normalize(const Column &in, ColumnNormalization n) {
Chris@1267 48
Chris@1267 49 if (n == ColumnNormalization::None || in.empty()) {
Chris@1267 50 return in;
Chris@1267 51 }
Chris@1267 52
Chris@1267 53 float scale = 1.f;
Chris@1267 54
Chris@1267 55 if (n == ColumnNormalization::Sum1) {
Chris@1267 56
Chris@1267 57 float sum = 0.f;
Chris@1267 58
Chris@1267 59 for (auto v: in) {
Chris@1267 60 sum += fabsf(v);
Chris@1267 61 }
Chris@1267 62
Chris@1267 63 if (sum != 0.f) {
Chris@1267 64 scale = 1.f / sum;
Chris@1267 65 }
Chris@1267 66
Chris@1267 67 } else {
Chris@1267 68
Chris@1267 69 float max = 0.f;
Chris@1267 70
Chris@1267 71 for (auto v: in) {
Chris@1267 72 v = fabsf(v);
Chris@1267 73 if (v > max) {
Chris@1267 74 max = v;
Chris@1267 75 }
Chris@1267 76 }
Chris@1267 77
Chris@1267 78 if (n == ColumnNormalization::Max1) {
Chris@1267 79 if (max != 0.f) {
Chris@1267 80 scale = 1.f / max;
Chris@1267 81 }
Chris@1267 82 } else if (n == ColumnNormalization::Hybrid) {
Chris@1267 83 if (max > 0.f) {
Chris@1267 84 scale = log10f(max + 1.f) / max;
Chris@1267 85 }
Chris@1267 86 }
Chris@1267 87 }
Chris@1267 88
Chris@1267 89 return applyGain(in, scale);
Chris@1267 90 }
Chris@1267 91
Chris@1267 92 ColumnOp::Column
Chris@1267 93 ColumnOp::distribute(const Column &in,
Chris@1267 94 int h,
Chris@1267 95 const vector<double> &binfory,
Chris@1267 96 int minbin,
Chris@1267 97 bool interpolate)
Chris@1267 98 {
Chris@1267 99 vector<float> out(h, 0.f);
Chris@1267 100 int bins = int(in.size());
Chris@1267 101
Chris@1303 102 if (interpolate) {
Chris@1303 103 // If the bins are all closer together than the target y
Chris@1303 104 // coordinate increments, then we don't want to interpolate
Chris@1303 105 // after all. But because the binfory mapping isn't
Chris@1303 106 // necessarily linear, just checking e.g. whether bins > h is
Chris@1303 107 // not enough -- the bins could still be spaced more widely at
Chris@1303 108 // either end of the scale. We are prepared to assume however
Chris@1303 109 // that if the bins are closer at both ends of the scale, they
Chris@1303 110 // aren't going to diverge mysteriously in the middle.
Chris@1303 111 if (h > 1 &&
Chris@1303 112 fabs(binfory[1] - binfory[0]) >= 1.0 &&
Chris@1303 113 fabs(binfory[h-1] - binfory[h-2]) >= 1.0) {
Chris@1303 114 interpolate = false;
Chris@1303 115 }
Chris@1303 116 }
Chris@1303 117
Chris@1267 118 for (int y = 0; y < h; ++y) {
Chris@1267 119
cannam@1301 120 if (interpolate) {
Chris@1267 121
Chris@1267 122 double sy = binfory[y] - minbin - 0.5;
Chris@1267 123 double syf = floor(sy);
Chris@1267 124
Chris@1267 125 int mainbin = int(syf);
Chris@1267 126 int other = mainbin;
Chris@1267 127 if (sy > syf) {
Chris@1267 128 other = mainbin + 1;
Chris@1267 129 } else if (sy < syf) {
Chris@1267 130 other = mainbin - 1;
Chris@1267 131 }
Chris@1267 132
Chris@1267 133 if (mainbin < 0) {
Chris@1267 134 mainbin = 0;
Chris@1267 135 }
Chris@1267 136 if (mainbin >= bins) {
Chris@1267 137 mainbin = bins - 1;
Chris@1267 138 }
Chris@1267 139
Chris@1267 140 if (other < 0) {
Chris@1267 141 other = 0;
Chris@1267 142 }
Chris@1267 143 if (other >= bins) {
Chris@1267 144 other = bins - 1;
Chris@1267 145 }
Chris@1267 146
Chris@1267 147 double prop = 1.0 - fabs(sy - syf);
Chris@1267 148
Chris@1267 149 double v0 = in[mainbin];
Chris@1267 150 double v1 = in[other];
Chris@1267 151
Chris@1267 152 out[y] = float(prop * v0 + (1.0 - prop) * v1);
Chris@1267 153
Chris@1267 154 } else {
Chris@1267 155
Chris@1267 156 double sy0 = binfory[y] - minbin;
Chris@1267 157
Chris@1267 158 double sy1;
Chris@1267 159 if (y+1 < h) {
Chris@1267 160 sy1 = binfory[y+1] - minbin;
Chris@1267 161 } else {
Chris@1267 162 sy1 = bins;
Chris@1267 163 }
Chris@1267 164
Chris@1267 165 int by0 = int(sy0 + 0.0001);
Chris@1267 166 int by1 = int(sy1 + 0.0001);
Chris@1283 167
Chris@1283 168 if (by0 < 0 || by0 >= bins || by1 > bins) {
Chris@1283 169 SVCERR << "ERROR: bin index out of range in ColumnOp::distribute: by0 = " << by0 << ", by1 = " << by1 << ", sy0 = " << sy0 << ", sy1 = " << sy1 << ", y = " << y << ", binfory[y] = " << binfory[y] << ", minbin = " << minbin << ", bins = " << bins << endl;
Chris@1283 170 continue;
Chris@1283 171 }
Chris@1267 172
Chris@1267 173 for (int bin = by0; bin == by0 || bin < by1; ++bin) {
Chris@1267 174
Chris@1267 175 float value = in[bin];
Chris@1267 176
Chris@1267 177 if (bin == by0 || value > out[y]) {
Chris@1267 178 out[y] = value;
Chris@1267 179 }
Chris@1267 180 }
Chris@1267 181 }
Chris@1267 182 }
Chris@1267 183
Chris@1267 184 return out;
Chris@1267 185 }