To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

The primary repository for this project is hosted at https://github.com/cannam/constant-q-cpp/ .
This repository is a read-only copy which is updated automatically every hour.

Statistics Download as Zip
| Branch: | Revision:

root / src / CQSpectrogram.cpp

History | View | Annotate | Download (7.37 KB)

1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2
/*
3
    Constant-Q library
4
    Copyright (c) 2013-2014 Queen Mary, University of London
5

6
    Permission is hereby granted, free of charge, to any person
7
    obtaining a copy of this software and associated documentation
8
    files (the "Software"), to deal in the Software without
9
    restriction, including without limitation the rights to use, copy,
10
    modify, merge, publish, distribute, sublicense, and/or sell copies
11
    of the Software, and to permit persons to whom the Software is
12
    furnished to do so, subject to the following conditions:
13

14
    The above copyright notice and this permission notice shall be
15
    included in all copies or substantial portions of the Software.
16

17
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
21
    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
22
    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24

25
    Except as contained in this notice, the names of the Centre for
26
    Digital Music; Queen Mary, University of London; and Chris Cannam
27
    shall not be used in advertising or otherwise to promote the sale,
28
    use or other dealings in this Software without prior written
29
    authorization.
30
*/
31

    
32
#include "CQSpectrogram.h"
33

    
34
#include <iostream>
35
#include <stdexcept>
36

    
37
using std::cerr;
38
using std::endl;
39

    
40
//#define DEBUG_CQSPECTROGRAM 1
41

    
42
CQSpectrogram::CQSpectrogram(CQParameters params,
43
                             Interpolation interpolation) :
44
    m_cq(params),
45
    m_interpolation(interpolation)
46
{
47
}
48

    
49
CQSpectrogram::~CQSpectrogram()
50
{
51
}
52

    
53
CQSpectrogram::RealBlock
54
CQSpectrogram::process(const RealSequence &td)
55
{
56
    return postProcess(m_cq.process(td), false);
57
}
58

    
59
CQSpectrogram::RealBlock
60
CQSpectrogram::getRemainingOutput()
61
{
62
    return postProcess(m_cq.getRemainingOutput(), true);
63
}
64

    
65
CQSpectrogram::RealBlock
66
CQSpectrogram::postProcess(const ComplexBlock &cq, bool insist)
67
{
68
    int width = cq.size();
69

    
70
    // convert to magnitudes
71
    RealBlock spec;
72
    for (int i = 0; i < width; ++i) {
73
        int height = cq[i].size();
74
        RealColumn col(height, 0);
75
        for (int j = 0; j < height; ++j) {
76
#ifdef DEBUG_CQSPECTROGRAM
77
            if (isnan(cq[i][j].real())) {
78
                cerr << "WARNING: NaN in real at (" << i << "," << j << ")" << endl;
79
            }
80
            if (isnan(cq[i][j].imag())) {
81
                cerr << "WARNING: NaN in imag at (" << i << "," << j << ")" << endl;
82
            }
83
#endif
84
            col[j] = abs(cq[i][j]);
85
        }
86
        spec.push_back(col);
87
    }
88

    
89
    if (m_interpolation == InterpolateZeros) {
90
        for (int i = 0; i < width; ++i) {
91
            int sh = spec[i].size();
92
            int fh = getTotalBins();
93
            for (int j = sh; j < fh; ++j) {
94
                spec[i].push_back(0);
95
            }
96
        }
97
        return spec;
98
    }
99

    
100
    for (int i = 0; i < width; ++i) {
101
        m_buffer.push_back(spec[i]);
102
    }
103
    
104
    if (m_interpolation == InterpolateHold) {
105
        return fetchHold(insist);
106
    } else {
107
        return fetchLinear(insist);
108
    }
109
}
110

    
111
CQSpectrogram::RealBlock
112
CQSpectrogram::fetchHold(bool)
113
{
114
    RealBlock out;
115
    
116
    int width = m_buffer.size();
117
    int height = getTotalBins();
118

    
119
    for (int i = 0; i < width; ++i) {
120
        
121
        RealColumn col = m_buffer[i];
122

    
123
        int thisHeight = col.size();
124
        int prevHeight = m_prevColumn.size();
125

    
126
        for (int j = thisHeight; j < height; ++j) {
127
            if (j < prevHeight) {
128
                col.push_back(m_prevColumn[j]);
129
            } else {
130
                col.push_back(0.0);
131
            }
132
        }
133

    
134
        m_prevColumn = col;
135
        out.push_back(col);
136
    }
137

    
138
    m_buffer.clear();
139

    
140
    return out;
141
}
142

    
143
CQSpectrogram::RealBlock
144
CQSpectrogram::fetchLinear(bool insist)
145
{
146
    RealBlock out;
147

    
148
    //!!! This is surprisingly messy. I must be missing something.
149

    
150
    // We can only return any data when we have at least one column
151
    // that has the full height in the buffer, that is not the first
152
    // column.
153
    //
154
    // If the first col has full height, and there is another one
155
    // later that also does, then we can interpolate between those, up
156
    // to but not including the second full height column.  Then we
157
    // drop and return the columns we interpolated, leaving the second
158
    // full-height col as the first col in the buffer. And repeat as
159
    // long as enough columns are available.
160
    //
161
    // If the first col does not have full height, then (so long as
162
    // we're following the logic above) we must simply have not yet
163
    // reached the first full-height column in the CQ output, and we
164
    // can interpolate nothing.
165
    
166
    int width = m_buffer.size();
167
    int height = getTotalBins();
168

    
169
    if (width == 0) return out;
170
    
171
    int firstFullHeight = -1;
172
    int secondFullHeight = -1;
173

    
174
    for (int i = 0; i < width; ++i) {
175
        if ((int)m_buffer[i].size() == height) {
176
            if (firstFullHeight == -1) {
177
                firstFullHeight = i;
178
            } else if (secondFullHeight == -1) {
179
                secondFullHeight = i;
180
                break;
181
            }
182
        }
183
    }
184

    
185
//    cerr << "fetchLinear: firstFullHeight = " << firstFullHeight << ", secondFullHeight = " << secondFullHeight << endl;
186

    
187
    if (firstFullHeight < 0) {
188
        if (insist) {
189
            return fetchHold(true);
190
        } else {
191
            return out;
192
        }
193
    } else if (firstFullHeight > 0) {
194
        // can interpolate nothing, stash up to first full height & recurse
195
        out = RealBlock(m_buffer.begin(), m_buffer.begin() + firstFullHeight);
196
        m_buffer = RealBlock(m_buffer.begin() + firstFullHeight, m_buffer.end());
197
        RealBlock more = fetchLinear(insist);
198
        out.insert(out.end(), more.begin(), more.end());
199
        return out;
200
    } else if (secondFullHeight < 0) {
201
        // firstFullHeight == 0, but there is no second full height --
202
        // wait for it unless insist flag is set
203
        if (insist) {
204
            return fetchHold(true);
205
        } else {
206
            return out;
207
        }
208
    } else {
209
        // firstFullHeight == 0 and secondFullHeight also valid. Can interpolate
210
        out = linearInterpolated(m_buffer, 0, secondFullHeight);
211
        m_buffer = RealBlock(m_buffer.begin() + secondFullHeight, m_buffer.end());
212
        RealBlock more = fetchLinear(insist);
213
        out.insert(out.end(), more.begin(), more.end());
214
        return out;
215
    }
216
}
217

    
218
CQSpectrogram::RealBlock
219
CQSpectrogram::linearInterpolated(const RealBlock &g, int x0, int x1)
220
{
221
    // g must be a grid with full-height columns at x0 and x1
222

    
223
    if (x0 >= x1) {
224
        throw std::logic_error("x0 >= x1");
225
    }
226
    if (x1 >= (int)g.size()) {
227
        throw std::logic_error("x1 >= g.size()");
228
    }
229
    if (g[x0].size() != g[x1].size()) {
230
        throw std::logic_error("x0 and x1 are not the same height");
231
    }
232

    
233
    int height = g[x0].size();
234
    int width = x1 - x0;
235

    
236
    RealBlock out(g.begin() + x0, g.begin() + x1);
237

    
238
    for (int y = 0; y < height; ++y) {
239

    
240
        int spacing = width;
241
        for (int i = 1; i < width; ++i) {
242
            int thisHeight = g[x0 + i].size();
243
            if (thisHeight > height) {
244
                throw std::logic_error("First column not full-height");
245
            }
246
            if (thisHeight > y) {
247
                spacing = i;
248
                break;
249
            }
250
        }
251

    
252
        if (spacing < 2) continue;
253

    
254
        for (int i = 0; i + spacing <= width; i += spacing) {
255
            for (int j = 1; j < spacing; ++j) {
256
                double proportion = double(j)/double(spacing);
257
                double interpolated = 
258
                    g[x0 + i][y] * (1.0 - proportion) +
259
                    g[x0 + i + spacing][y] * proportion;
260
                out[i + j].push_back(interpolated);
261
            }
262
        }
263
    }
264

    
265
    return out;
266
}
267

    
268

    
269