Chris@1187: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@1187: Chris@1187: /* Chris@1187: Sonic Visualiser Chris@1187: An audio file viewer and annotation editor. Chris@1187: Centre for Digital Music, Queen Mary, University of London. Chris@1188: This file copyright 2006-2016 Chris Cannam and QMUL. Chris@1187: Chris@1187: This program is free software; you can redistribute it and/or Chris@1187: modify it under the terms of the GNU General Public License as Chris@1187: published by the Free Software Foundation; either version 2 of the Chris@1187: License, or (at your option) any later version. See the file Chris@1187: COPYING included with this distribution for more information. Chris@1187: */ Chris@1187: Chris@1187: #ifndef COLUMN_OP_H Chris@1187: #define COLUMN_OP_H Chris@1187: Chris@1187: #include "BaseTypes.h" Chris@1187: Chris@1187: #include Chris@1187: Chris@1190: /** Chris@1193: * Display normalization types for columns in e.g. grid plots. Chris@1193: * Chris@1193: * Max1 means to normalize to max value = 1.0. Chris@1193: * Sum1 means to normalize to sum of values = 1.0. Chris@1193: * Chris@1193: * Hybrid means normalize to max = 1.0 and then multiply by Chris@1193: * log10 of the max value, to retain some difference between Chris@1193: * levels of neighbouring columns. Chris@1193: * Chris@1193: * Area normalization is handled separately. Chris@1193: */ Chris@1193: enum class ColumnNormalization { Chris@1193: None, Chris@1193: Max1, Chris@1193: Sum1, Chris@1193: Hybrid Chris@1193: }; Chris@1193: Chris@1193: /** Chris@1190: * Class containing static functions for simple operations on data Chris@1190: * columns, for use by display layers. Chris@1190: */ Chris@1187: class ColumnOp Chris@1187: { Chris@1187: public: Chris@1190: /** Chris@1190: * Column type. Chris@1190: */ Chris@1187: typedef std::vector Column; Chris@1187: Chris@1190: /** Chris@1195: * Scale the given column using the given gain multiplier. Chris@1195: */ Chris@1197: static Column applyGain(const Column &in, double gain) { Chris@1195: Chris@1197: if (gain == 1.0) { Chris@1195: return in; Chris@1195: } Chris@1195: Column out; Chris@1195: out.reserve(in.size()); Chris@1195: for (auto v: in) { Chris@1197: out.push_back(float(v * gain)); Chris@1195: } Chris@1195: return out; Chris@1195: } Chris@1195: Chris@1195: /** Chris@1190: * Scale an FFT output by half the FFT size. Chris@1190: */ Chris@1187: static Column fftScale(const Column &in, int fftSize) { Chris@1197: return applyGain(in, 2.0 / fftSize); Chris@1187: } Chris@1187: Chris@1190: /** Chris@1190: * Determine whether an index points to a local peak. Chris@1190: */ Chris@1187: static bool isPeak(const Column &in, int ix) { Chris@1187: Chris@1187: if (!in_range_for(in, ix-1)) return false; Chris@1187: if (!in_range_for(in, ix+1)) return false; Chris@1187: if (in[ix] < in[ix+1]) return false; Chris@1187: if (in[ix] < in[ix-1]) return false; Chris@1187: Chris@1187: return true; Chris@1187: } Chris@1187: Chris@1190: /** Chris@1190: * Return a column containing only the local peak values (all Chris@1190: * others zero). Chris@1190: */ Chris@1187: static Column peakPick(const Column &in) { Chris@1187: Chris@1187: std::vector out(in.size(), 0.f); Chris@1187: for (int i = 0; in_range_for(in, i); ++i) { Chris@1187: if (isPeak(in, i)) { Chris@1187: out[i] = in[i]; Chris@1187: } Chris@1187: } Chris@1187: Chris@1187: return out; Chris@1187: } Chris@1187: Chris@1190: /** Chris@1190: * Return a column normalized from the input column according to Chris@1190: * the given normalization scheme. Chris@1190: */ Chris@1193: static Column normalize(const Column &in, ColumnNormalization n) { Chris@1187: Chris@1193: if (n == ColumnNormalization::None) { Chris@1187: return in; Chris@1187: } Chris@1187: Chris@1193: float scale = 1.f; Chris@1193: Chris@1193: if (n == ColumnNormalization::Sum1) { Chris@1193: Chris@1193: float sum = 0.f; Chris@1193: Chris@1193: for (auto v: in) { Chris@1193: sum += v; Chris@1193: } Chris@1193: Chris@1193: if (sum != 0.f) { Chris@1193: scale = 1.f / sum; Chris@1193: } Chris@1193: } else { Chris@1193: Chris@1193: float max = *max_element(in.begin(), in.end()); Chris@1193: Chris@1193: if (n == ColumnNormalization::Max1) { Chris@1193: if (max != 0.f) { Chris@1193: scale = 1.f / max; Chris@1193: } Chris@1193: } else if (n == ColumnNormalization::Hybrid) { Chris@1193: if (max > 0.f) { Chris@1193: scale = log10f(max + 1.f) / max; Chris@1193: } Chris@1193: } Chris@1193: } Chris@1187: Chris@1197: return applyGain(in, scale); Chris@1187: } Chris@1187: Chris@1190: /** Chris@1190: * Distribute the given column into a target vector of a different Chris@1190: * size, optionally using linear interpolation. The binfory vector Chris@1190: * contains a mapping from y coordinate (i.e. index into the Chris@1190: * target vector) to bin (i.e. index into the source column). Chris@1190: */ Chris@1187: static Column distribute(const Column &in, Chris@1187: int h, Chris@1187: const std::vector &binfory, Chris@1187: int minbin, Chris@1187: bool interpolate) { Chris@1198: Chris@1187: std::vector out(h, 0.f); Chris@1187: int bins = int(in.size()); Chris@1187: Chris@1187: for (int y = 0; y < h; ++y) { Chris@1187: Chris@1187: double sy0 = binfory[y] - minbin; Chris@1187: double sy1 = sy0 + 1; Chris@1187: if (y+1 < h) { Chris@1187: sy1 = binfory[y+1] - minbin; Chris@1187: } Chris@1187: Chris@1187: if (interpolate && fabs(sy1 - sy0) < 1.0) { Chris@1187: Chris@1187: double centre = (sy0 + sy1) / 2; Chris@1187: double dist = (centre - 0.5) - rint(centre - 0.5); Chris@1187: int bin = int(centre); Chris@1187: Chris@1187: int other = (dist < 0 ? (bin-1) : (bin+1)); Chris@1187: Chris@1187: if (bin < 0) bin = 0; Chris@1187: if (bin >= bins) bin = bins-1; Chris@1187: Chris@1187: if (other < 0 || other >= bins) { Chris@1187: other = bin; Chris@1187: } Chris@1187: Chris@1187: double prop = 1.0 - fabs(dist); Chris@1187: Chris@1187: double v0 = in[bin]; Chris@1187: double v1 = in[other]; Chris@1187: Chris@1187: out[y] = float(prop * v0 + (1.0 - prop) * v1); Chris@1187: Chris@1187: } else { // not interpolating this one Chris@1187: Chris@1187: int by0 = int(sy0 + 0.0001); Chris@1187: int by1 = int(sy1 + 0.0001); Chris@1187: if (by1 < by0 + 1) by1 = by0 + 1; Chris@1198: if (by1 >= bins) by1 = by1 - 1; Chris@1198: Chris@1187: for (int bin = by0; bin < by1; ++bin) { Chris@1187: Chris@1187: float value = in[bin]; Chris@1187: Chris@1201: if (bin == by0 || value > out[y]) { Chris@1187: out[y] = value; Chris@1187: } Chris@1187: } Chris@1187: } Chris@1187: } Chris@1187: Chris@1187: return out; Chris@1187: } Chris@1187: Chris@1187: }; Chris@1187: Chris@1187: #endif Chris@1187: