annotate constant-q-cpp/src/CQSpectrogram.cpp @ 372:af71cbdab621 tip

Update bqvec code
author Chris Cannam
date Tue, 19 Nov 2019 10:13:32 +0000
parents 5d0a2ebb4d17
children
rev   line source
Chris@366 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@366 2 /*
Chris@366 3 Constant-Q library
Chris@366 4 Copyright (c) 2013-2014 Queen Mary, University of London
Chris@366 5
Chris@366 6 Permission is hereby granted, free of charge, to any person
Chris@366 7 obtaining a copy of this software and associated documentation
Chris@366 8 files (the "Software"), to deal in the Software without
Chris@366 9 restriction, including without limitation the rights to use, copy,
Chris@366 10 modify, merge, publish, distribute, sublicense, and/or sell copies
Chris@366 11 of the Software, and to permit persons to whom the Software is
Chris@366 12 furnished to do so, subject to the following conditions:
Chris@366 13
Chris@366 14 The above copyright notice and this permission notice shall be
Chris@366 15 included in all copies or substantial portions of the Software.
Chris@366 16
Chris@366 17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
Chris@366 18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
Chris@366 19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Chris@366 20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
Chris@366 21 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
Chris@366 22 CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
Chris@366 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Chris@366 24
Chris@366 25 Except as contained in this notice, the names of the Centre for
Chris@366 26 Digital Music; Queen Mary, University of London; and Chris Cannam
Chris@366 27 shall not be used in advertising or otherwise to promote the sale,
Chris@366 28 use or other dealings in this Software without prior written
Chris@366 29 authorization.
Chris@366 30 */
Chris@366 31
Chris@366 32 #include "CQSpectrogram.h"
Chris@366 33
Chris@366 34 #include <iostream>
Chris@366 35 #include <stdexcept>
Chris@366 36
Chris@366 37 using std::cerr;
Chris@366 38 using std::endl;
Chris@366 39
Chris@366 40 //#define DEBUG_CQSPECTROGRAM 1
Chris@366 41
Chris@366 42 CQSpectrogram::CQSpectrogram(CQParameters params,
Chris@366 43 Interpolation interpolation) :
Chris@366 44 m_cq(params),
Chris@366 45 m_interpolation(interpolation)
Chris@366 46 {
Chris@366 47 }
Chris@366 48
Chris@366 49 CQSpectrogram::~CQSpectrogram()
Chris@366 50 {
Chris@366 51 }
Chris@366 52
Chris@366 53 CQSpectrogram::RealBlock
Chris@366 54 CQSpectrogram::process(const RealSequence &td)
Chris@366 55 {
Chris@366 56 return postProcess(m_cq.process(td), false);
Chris@366 57 }
Chris@366 58
Chris@366 59 CQSpectrogram::RealBlock
Chris@366 60 CQSpectrogram::getRemainingOutput()
Chris@366 61 {
Chris@366 62 return postProcess(m_cq.getRemainingOutput(), true);
Chris@366 63 }
Chris@366 64
Chris@366 65 CQSpectrogram::RealBlock
Chris@366 66 CQSpectrogram::postProcess(const ComplexBlock &cq, bool insist)
Chris@366 67 {
Chris@366 68 int width = cq.size();
Chris@366 69
Chris@366 70 // convert to magnitudes
Chris@366 71 RealBlock spec;
Chris@366 72 for (int i = 0; i < width; ++i) {
Chris@366 73 int height = cq[i].size();
Chris@366 74 RealColumn col(height, 0);
Chris@366 75 for (int j = 0; j < height; ++j) {
Chris@366 76 #ifdef DEBUG_CQSPECTROGRAM
Chris@366 77 if (isnan(cq[i][j].real())) {
Chris@366 78 cerr << "WARNING: NaN in real at (" << i << "," << j << ")" << endl;
Chris@366 79 }
Chris@366 80 if (isnan(cq[i][j].imag())) {
Chris@366 81 cerr << "WARNING: NaN in imag at (" << i << "," << j << ")" << endl;
Chris@366 82 }
Chris@366 83 #endif
Chris@366 84 col[j] = abs(cq[i][j]);
Chris@366 85 }
Chris@366 86 spec.push_back(col);
Chris@366 87 }
Chris@366 88
Chris@366 89 if (m_interpolation == InterpolateZeros) {
Chris@366 90 for (int i = 0; i < width; ++i) {
Chris@366 91 int sh = spec[i].size();
Chris@366 92 int fh = getTotalBins();
Chris@366 93 for (int j = sh; j < fh; ++j) {
Chris@366 94 spec[i].push_back(0);
Chris@366 95 }
Chris@366 96 }
Chris@366 97 return spec;
Chris@366 98 }
Chris@366 99
Chris@366 100 for (int i = 0; i < width; ++i) {
Chris@366 101 m_buffer.push_back(spec[i]);
Chris@366 102 }
Chris@366 103
Chris@366 104 if (m_interpolation == InterpolateHold) {
Chris@366 105 return fetchHold(insist);
Chris@366 106 } else {
Chris@366 107 return fetchLinear(insist);
Chris@366 108 }
Chris@366 109 }
Chris@366 110
Chris@366 111 CQSpectrogram::RealBlock
Chris@366 112 CQSpectrogram::fetchHold(bool)
Chris@366 113 {
Chris@366 114 RealBlock out;
Chris@366 115
Chris@366 116 int width = m_buffer.size();
Chris@366 117 int height = getTotalBins();
Chris@366 118
Chris@366 119 for (int i = 0; i < width; ++i) {
Chris@366 120
Chris@366 121 RealColumn col = m_buffer[i];
Chris@366 122
Chris@366 123 int thisHeight = col.size();
Chris@366 124 int prevHeight = m_prevColumn.size();
Chris@366 125
Chris@366 126 for (int j = thisHeight; j < height; ++j) {
Chris@366 127 if (j < prevHeight) {
Chris@366 128 col.push_back(m_prevColumn[j]);
Chris@366 129 } else {
Chris@366 130 col.push_back(0.0);
Chris@366 131 }
Chris@366 132 }
Chris@366 133
Chris@366 134 m_prevColumn = col;
Chris@366 135 out.push_back(col);
Chris@366 136 }
Chris@366 137
Chris@366 138 m_buffer.clear();
Chris@366 139
Chris@366 140 return out;
Chris@366 141 }
Chris@366 142
Chris@366 143 CQSpectrogram::RealBlock
Chris@366 144 CQSpectrogram::fetchLinear(bool insist)
Chris@366 145 {
Chris@366 146 RealBlock out;
Chris@366 147
Chris@366 148 //!!! This is surprisingly messy. I must be missing something.
Chris@366 149
Chris@366 150 // We can only return any data when we have at least one column
Chris@366 151 // that has the full height in the buffer, that is not the first
Chris@366 152 // column.
Chris@366 153 //
Chris@366 154 // If the first col has full height, and there is another one
Chris@366 155 // later that also does, then we can interpolate between those, up
Chris@366 156 // to but not including the second full height column. Then we
Chris@366 157 // drop and return the columns we interpolated, leaving the second
Chris@366 158 // full-height col as the first col in the buffer. And repeat as
Chris@366 159 // long as enough columns are available.
Chris@366 160 //
Chris@366 161 // If the first col does not have full height, then (so long as
Chris@366 162 // we're following the logic above) we must simply have not yet
Chris@366 163 // reached the first full-height column in the CQ output, and we
Chris@366 164 // can interpolate nothing.
Chris@366 165
Chris@366 166 int width = m_buffer.size();
Chris@366 167 int height = getTotalBins();
Chris@366 168
Chris@366 169 if (width == 0) return out;
Chris@366 170
Chris@366 171 int firstFullHeight = -1;
Chris@366 172 int secondFullHeight = -1;
Chris@366 173
Chris@366 174 for (int i = 0; i < width; ++i) {
Chris@366 175 if ((int)m_buffer[i].size() == height) {
Chris@366 176 if (firstFullHeight == -1) {
Chris@366 177 firstFullHeight = i;
Chris@366 178 } else if (secondFullHeight == -1) {
Chris@366 179 secondFullHeight = i;
Chris@366 180 break;
Chris@366 181 }
Chris@366 182 }
Chris@366 183 }
Chris@366 184
Chris@366 185 // cerr << "fetchLinear: firstFullHeight = " << firstFullHeight << ", secondFullHeight = " << secondFullHeight << endl;
Chris@366 186
Chris@366 187 if (firstFullHeight < 0) {
Chris@366 188 if (insist) {
Chris@366 189 return fetchHold(true);
Chris@366 190 } else {
Chris@366 191 return out;
Chris@366 192 }
Chris@366 193 } else if (firstFullHeight > 0) {
Chris@366 194 // can interpolate nothing, stash up to first full height & recurse
Chris@366 195 out = RealBlock(m_buffer.begin(), m_buffer.begin() + firstFullHeight);
Chris@366 196 m_buffer = RealBlock(m_buffer.begin() + firstFullHeight, m_buffer.end());
Chris@366 197 RealBlock more = fetchLinear(insist);
Chris@366 198 out.insert(out.end(), more.begin(), more.end());
Chris@366 199 return out;
Chris@366 200 } else if (secondFullHeight < 0) {
Chris@366 201 // firstFullHeight == 0, but there is no second full height --
Chris@366 202 // wait for it unless insist flag is set
Chris@366 203 if (insist) {
Chris@366 204 return fetchHold(true);
Chris@366 205 } else {
Chris@366 206 return out;
Chris@366 207 }
Chris@366 208 } else {
Chris@366 209 // firstFullHeight == 0 and secondFullHeight also valid. Can interpolate
Chris@366 210 out = linearInterpolated(m_buffer, 0, secondFullHeight);
Chris@366 211 m_buffer = RealBlock(m_buffer.begin() + secondFullHeight, m_buffer.end());
Chris@366 212 RealBlock more = fetchLinear(insist);
Chris@366 213 out.insert(out.end(), more.begin(), more.end());
Chris@366 214 return out;
Chris@366 215 }
Chris@366 216 }
Chris@366 217
Chris@366 218 CQSpectrogram::RealBlock
Chris@366 219 CQSpectrogram::linearInterpolated(const RealBlock &g, int x0, int x1)
Chris@366 220 {
Chris@366 221 // g must be a grid with full-height columns at x0 and x1
Chris@366 222
Chris@366 223 if (x0 >= x1) {
Chris@366 224 throw std::logic_error("x0 >= x1");
Chris@366 225 }
Chris@366 226 if (x1 >= (int)g.size()) {
Chris@366 227 throw std::logic_error("x1 >= g.size()");
Chris@366 228 }
Chris@366 229 if (g[x0].size() != g[x1].size()) {
Chris@366 230 throw std::logic_error("x0 and x1 are not the same height");
Chris@366 231 }
Chris@366 232
Chris@366 233 int height = g[x0].size();
Chris@366 234 int width = x1 - x0;
Chris@366 235
Chris@366 236 RealBlock out(g.begin() + x0, g.begin() + x1);
Chris@366 237
Chris@366 238 for (int y = 0; y < height; ++y) {
Chris@366 239
Chris@366 240 int spacing = width;
Chris@366 241 for (int i = 1; i < width; ++i) {
Chris@366 242 int thisHeight = g[x0 + i].size();
Chris@366 243 if (thisHeight > height) {
Chris@366 244 throw std::logic_error("First column not full-height");
Chris@366 245 }
Chris@366 246 if (thisHeight > y) {
Chris@366 247 spacing = i;
Chris@366 248 break;
Chris@366 249 }
Chris@366 250 }
Chris@366 251
Chris@366 252 if (spacing < 2) continue;
Chris@366 253
Chris@366 254 for (int i = 0; i + spacing <= width; i += spacing) {
Chris@366 255 for (int j = 1; j < spacing; ++j) {
Chris@366 256 double proportion = double(j)/double(spacing);
Chris@366 257 double interpolated =
Chris@366 258 g[x0 + i][y] * (1.0 - proportion) +
Chris@366 259 g[x0 + i + spacing][y] * proportion;
Chris@366 260 out[i + j].push_back(interpolated);
Chris@366 261 }
Chris@366 262 }
Chris@366 263 }
Chris@366 264
Chris@366 265 return out;
Chris@366 266 }
Chris@366 267
Chris@366 268
Chris@366 269