c@92: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ c@92: c@92: /* c@92: QM Vamp Plugin Set c@92: c@92: Centre for Digital Music, Queen Mary, University of London. c@135: c@135: This program is free software; you can redistribute it and/or c@135: modify it under the terms of the GNU General Public License as c@135: published by the Free Software Foundation; either version 2 of the c@135: License, or (at your option) any later version. See the file c@135: COPYING included with this distribution for more information. c@92: */ c@92: c@92: #ifndef _ADAPTIVE_SPECTROGRAM_H_ c@92: #define _ADAPTIVE_SPECTROGRAM_H_ c@92: c@92: #include c@92: #include c@92: #include c@92: c@108: #include c@107: #include c@105: c@156: #include c@156: #include c@156: #include c@156: c@156: class Decimator; c@104: c@92: class AdaptiveSpectrogram : public Vamp::Plugin c@92: { c@92: public: c@92: AdaptiveSpectrogram(float inputSampleRate); c@92: virtual ~AdaptiveSpectrogram(); c@92: c@92: bool initialise(size_t channels, size_t stepSize, size_t blockSize); c@92: void reset(); c@92: c@92: InputDomain getInputDomain() const { return TimeDomain; } c@92: c@92: std::string getIdentifier() const; c@92: std::string getName() const; c@92: std::string getDescription() const; c@92: std::string getMaker() const; c@92: int getPluginVersion() const; c@92: std::string getCopyright() const; c@92: c@92: size_t getPreferredStepSize() const; c@92: size_t getPreferredBlockSize() const; c@92: c@92: ParameterList getParameterDescriptors() const; c@92: float getParameter(std::string id) const; c@92: void setParameter(std::string id, float value); c@92: c@92: OutputList getOutputDescriptors() const; c@92: c@92: FeatureSet process(const float *const *inputBuffers, c@92: Vamp::RealTime timestamp); c@92: c@92: FeatureSet getRemainingFeatures(); c@92: c@92: protected: c@92: int m_w; c@92: int m_n; c@114: bool m_coarse; c@109: bool m_threaded; c@156: int m_decFactor; c@156: float *m_buffer; c@156: int m_buflen; c@156: Decimator *m_decimator; c@92: c@100: struct Spectrogram c@100: { c@100: int resolution; c@100: int width; c@100: double **data; c@100: c@100: Spectrogram(int r, int w) : c@100: resolution(r), width(w) { c@100: data = new double *[width]; c@100: for (int i = 0; i < width; ++i) data[i] = new double[resolution]; c@100: } c@100: c@100: ~Spectrogram() { c@100: for (int i = 0; i < width; ++i) delete[] data[i]; c@100: delete[] data; c@100: } c@100: }; c@100: c@100: struct Spectrograms c@100: { c@100: int minres; c@100: int maxres; c@100: int n; c@100: Spectrogram **spectrograms; c@100: c@100: Spectrograms(int mn, int mx, int widthofmax) : c@100: minres(mn), maxres(mx) { c@100: n = log2(maxres/minres) + 1; c@100: spectrograms = new Spectrogram *[n]; c@100: int r = mn; c@100: for (int i = 0; i < n; ++i) { c@100: spectrograms[i] = new Spectrogram(r, widthofmax * (mx / r)); c@100: r = r * 2; c@100: } c@100: } c@100: ~Spectrograms() { c@100: for (int i = 0; i < n; ++i) { c@100: delete spectrograms[i]; c@100: } c@100: delete[] spectrograms; c@100: } c@100: }; c@100: c@100: struct Cutting c@100: { c@100: enum Cut { Horizontal, Vertical, Finished }; c@100: Cut cut; c@100: Cutting *first; c@100: Cutting *second; c@100: double cost; c@100: double value; c@110: BlockAllocator *allocator; c@100: c@100: ~Cutting() { c@110: if (first) first->erase(); c@110: if (second) second->erase(); c@110: } c@110: c@110: void erase() { c@110: if (allocator) { c@110: if (first) first->erase(); c@110: if (second) second->erase(); c@110: allocator->deallocate(this); c@110: } else { c@110: delete this; c@110: } c@100: } c@100: }; c@100: c@105: class FFTThread : public AsynchronousTask c@104: { c@104: public: c@107: FFTThread(int w) : c@107: m_window(HanningWindow, w) { c@106: m_w = w; c@106: m_fft = new FFTReal(m_w); c@106: m_rin = new double[m_w]; c@106: m_rout = new double[m_w]; c@106: m_iout = new double[m_w]; c@106: } c@106: ~FFTThread() { c@106: delete[] m_rin; c@106: delete[] m_rout; c@106: delete[] m_iout; c@106: delete m_fft; c@106: } c@106: c@106: int getW() const { return m_w; } c@105: c@109: void startCalculation(const float *timeDomain, Spectrograms &s, c@109: int res, int maxwidth) { c@109: setParameters(timeDomain, s, res, maxwidth); c@105: startTask(); c@105: } c@105: c@105: void await() { c@105: awaitTask(); c@105: } c@105: c@109: void setParameters(const float *timeDomain, Spectrograms &s, c@109: int res, int maxwidth) { c@109: m_in = timeDomain; c@109: m_s = &s; c@109: m_res = res; c@109: m_maxwid = maxwidth; c@109: } c@109: c@105: void performTask() { c@105: for (int i = 0; i < m_maxwid / m_w; ++i) { c@105: int origin = m_maxwid/4 - m_w/4; // for 50% overlap c@105: for (int j = 0; j < m_w; ++j) { c@109: m_rin[j] = m_in[origin + i * m_w/2 + j]; c@105: } c@107: m_window.cut(m_rin); c@153: m_fft->forward(m_rin, m_rout, m_iout); c@105: for (int j = 0; j < m_w/2; ++j) { c@105: int k = j+1; // include Nyquist but not DC c@106: double mag = sqrt(m_rout[k] * m_rout[k] + c@106: m_iout[k] * m_iout[k]); c@105: double scaled = mag / (m_w/2); c@105: m_s->spectrograms[m_res]->data[i][j] = scaled; c@105: } c@105: } c@105: } c@105: c@105: private: c@107: Window m_window; c@106: FFTReal *m_fft; c@105: const float *m_in; c@106: double *m_rin; c@106: double *m_rout; c@106: double *m_iout; c@105: Spectrograms *m_s; c@105: int m_res; c@105: int m_w; c@105: int m_maxwid; c@105: }; c@105: c@106: typedef std::map FFTMap; c@106: FFTMap m_fftThreads; c@105: c@105: class CutThread : public AsynchronousTask c@105: { c@105: public: c@110: CutThread(const AdaptiveSpectrogram *as) : m_as(as), m_result(0) { c@110: m_allocator = new BlockAllocator(sizeof(Cutting)); c@110: } c@110: ~CutThread() { c@110: delete m_allocator; c@110: } c@105: c@104: void cut(const Spectrograms &s, int res, int x, int y, int h) { c@104: m_s = &s; c@104: m_res = res; c@104: m_x = x; c@104: m_y = y; c@104: m_h = h; c@105: startTask(); c@104: } c@104: c@104: Cutting *get() { c@105: awaitTask(); c@105: return m_result; c@104: } c@104: c@104: protected: c@105: void performTask() { c@110: m_result = m_as->cut(*m_s, m_res, m_x, m_y, m_h, m_allocator); c@104: } c@104: c@105: private: c@104: const AdaptiveSpectrogram *m_as; c@110: BlockAllocator *m_allocator; c@104: const Spectrograms *m_s; c@104: int m_res; c@104: int m_x; c@104: int m_y; c@104: int m_h; c@104: Cutting *m_result; c@104: }; c@105: c@109: mutable std::vector m_cutThreads; c@109: mutable bool m_threadsInUse; c@104: c@110: inline double xlogx(double x) const { c@104: if (x == 0.0) return 0.0; c@104: else return x * log(x); c@104: } c@104: c@110: inline double cost(const Spectrogram &s, int x, int y) const { c@100: return xlogx(s.data[x][y]); c@100: } c@100: c@110: inline double value(const Spectrogram &s, int x, int y) const { c@100: return s.data[x][y]; c@100: } c@100: c@114: inline double normalize(double vcost, double venergy) const { c@114: return (vcost + (venergy * log(venergy))) / venergy; c@114: } c@114: c@114: inline bool isResolutionWanted(const Spectrograms &s, int res) const { c@114: if (!m_coarse) return true; c@114: if (res == s.minres || res == s.maxres) return true; c@114: int n = 0; c@114: for (int r = res; r > s.minres; r >>= 1) ++n; c@114: return ((n & 0x1) == 0); c@114: } c@114: c@110: Cutting *cut(const Spectrograms &, int res, int x, int y, int h, c@110: BlockAllocator *allocator) const; c@100: c@104: void getSubCuts(const Spectrograms &, int res, int x, int y, int h, c@114: Cutting **top, Cutting **bottom, c@114: Cutting **left, Cutting **right, c@113: BlockAllocator *allocator) const; c@100: c@104: void printCutting(Cutting *, std::string) const; c@104: c@104: void assemble(const Spectrograms &, const Cutting *, c@104: std::vector > &, c@104: int x, int y, int w, int h) const; c@156: }; c@92: c@92: c@92: #endif