Mercurial > hg > constant-q-cpp
changeset 22:701900c371b0
Calculate (so far unnormalised) CQ kernel
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Fri, 01 Nov 2013 16:13:22 +0000 |
parents | 5787785f4560 |
children | 4f2f28d5df58 |
files | cpp-qm-dsp/CQKernel.cpp cpp-qm-dsp/CQKernel.h cpp-qm-dsp/Makefile cpp-qm-dsp/test.cpp yeti/cqt.yeti |
diffstat | 5 files changed, 239 insertions(+), 4 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpp-qm-dsp/CQKernel.cpp Fri Nov 01 16:13:22 2013 +0000 @@ -0,0 +1,138 @@ + +#include "CQKernel.h" + +#include "qm-dsp/maths/MathUtilities.h" +#include "qm-dsp/dsp/transforms/FFT.h" +#include "qm-dsp/base/Window.h" + +#include <cmath> +#include <cassert> +#include <vector> + +using std::vector; + +CQKernel::CQKernel(double sampleRate, double maxFreq, int binsPerOctave) +{ + m_p.sampleRate = sampleRate; + m_p.maxFrequency = maxFreq; + m_p.binsPerOctave = binsPerOctave; + generateKernel(); +} + +CQKernel::~CQKernel() +{ + delete m_fft; +} + +void +CQKernel::generateKernel() +{ + double q = 1; + double atomHopFactor = 0.25; + double thresh = 0.0005; + + double bpo = m_p.binsPerOctave; + + m_p.minFrequency = (m_p.maxFrequency / 2) * pow(2, 1.0/bpo); + m_p.Q = q / (pow(2, 1.0/bpo) - 1.0); + + double maxNK = round(m_p.Q * m_p.sampleRate / m_p.minFrequency); + double minNK = round + (m_p.Q * m_p.sampleRate / + (m_p.minFrequency * pow(2, (bpo - 1.0) / bpo))); + + m_p.atomSpacing = round(minNK * atomHopFactor); + m_p.firstCentre = m_p.atomSpacing * ceil(ceil(maxNK / 2.0) / m_p.atomSpacing); + m_p.fftSize = MathUtilities::nextPowerOfTwo + (m_p.firstCentre + ceil(maxNK / 2.0)); + + m_p.atomsPerFrame = floor + (1.0 + (m_p.fftSize - ceil(maxNK / 2.0) - m_p.firstCentre) / m_p.atomSpacing); + + int lastCentre = m_p.firstCentre + (m_p.atomsPerFrame - 1) * m_p.atomSpacing; + + m_p.fftHop = (lastCentre + m_p.atomSpacing) - m_p.firstCentre; + + m_fft = new FFT(m_p.fftSize); + + for (int k = 1; k <= m_p.binsPerOctave; ++k) { + + int nk = round(m_p.Q * m_p.sampleRate / + (m_p.minFrequency * pow(2, ((k-1.0) / bpo)))); + + // The MATLAB version uses a symmetric window, but our windows + // are periodic. A symmetric window of size N is a periodic + // one of size N-1 with the first element stuck on the end + Window<double> w(BlackmanHarrisWindow, nk-1); + vector<double> win = w.getWindowData(); + win.push_back(win[0]); + + for (int i = 0; i < (int)win.size(); ++i) { + win[i] = sqrt(win[i]) / nk; + } + + double fk = m_p.minFrequency * pow(2, ((k-1.0) * bpo)); + + vector<double> reals, imags; + + for (int i = 0; i < nk; ++i) { + double arg = 2.0 * M_PI * fk * i / m_p.sampleRate; + reals[i] = win[i] * cos(arg); + imags[i] = win[i] * sin(arg); + } + + int atomOffset = m_p.firstCentre - ceil(nk/2); + + for (int i = 0; i < m_p.atomsPerFrame; ++i) { + + int shift = atomOffset + (i * m_p.atomSpacing); + + vector<double> rin(m_p.fftSize, 0.0); + vector<double> iin(m_p.fftSize, 0.0); + + for (int j = 0; j < nk; ++j) { + rin[j + shift] = reals[j]; + iin[i + shift] = imags[j]; + } + + vector<double> rout(m_p.fftSize, 0.0); + vector<double> iout(m_p.fftSize, 0.0); + + m_fft->process(false, + rin.data(), iin.data(), + rout.data(), iout.data()); + + int firstNZ = -1, lastNZ = -1; + + for (int j = 0; j < m_p.fftSize; ++j) { + if (sqrt(rout[i] * rout[i] + iout[i] * iout[i]) >= thresh) { + lastNZ = j; + if (firstNZ < 0) firstNZ = j; + } + } + + vector<double> rnz, inz; + + if (firstNZ >= 0) { + for (int j = firstNZ; j <= lastNZ; ++j) { + rnz.push_back(rout[j] / m_p.fftSize); + inz.push_back(iout[j] / m_p.fftSize); + } + m_kernel.offsets.push_back(firstNZ); + } else { + m_kernel.offsets.push_back(0); + } + + m_kernel.real.push_back(rnz); + m_kernel.imag.push_back(inz); + } + } + + assert((int)m_kernel.offsets.size() == m_p.binsPerOctave); + assert((int)m_kernel.real.size() == m_p.binsPerOctave); + assert((int)m_kernel.imag.size() == m_p.binsPerOctave); + + //!!! and normalise +} + +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpp-qm-dsp/CQKernel.h Fri Nov 01 16:13:22 2013 +0000 @@ -0,0 +1,44 @@ + +#ifndef CQ_KERNEL_H +#define CQ_KERNEL_H + +#include <vector> + +class FFT; + +class CQKernel +{ +public: + CQKernel(double sampleRate, double maxFreq, int binsPerOctave); + ~CQKernel(); + + struct Properties { + double sampleRate; + double maxFrequency; + double minFrequency; + int binsPerOctave; + int fftSize; + int fftHop; + int atomsPerFrame; + int atomSpacing; + int firstCentre; + double Q; + }; + + Properties getProperties() const { return m_p; } + +private: + Properties m_p; + FFT *m_fft; + + struct KernelMatrix { + std::vector<int> offsets; + std::vector<std::vector<double> > real; + std::vector<std::vector<double> > imag; + }; + KernelMatrix m_kernel; + + void generateKernel(); +}; + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpp-qm-dsp/Makefile Fri Nov 01 16:13:22 2013 +0000 @@ -0,0 +1,30 @@ + +DEFINES := -DUSE_PTHREADS + +CFLAGS := -I../.. $(CFLAGS) $(DEFINES) + +CXXFLAGS := -I../.. -Wall -g $(CXXFLAGS) $(DEFINES) +#CXXFLAGS := -I../.. -Wall -O3 -ffast-math -ftree-vectorize $(CXXFLAGS) $(DEFINES) + +LDFLAGS := $(LDFLAGS) + +#VG := valgrind + +LIBS := ../../qm-dsp/libqm-dsp.a -lpthread +PROGRAM_LIBS := -lsndfile + +SOURCES := CQKernel.cpp test.cpp + +OBJECTS := $(SOURCES:.cpp=.o) +OBJECTS := $(OBJECTS:.c=.o) + +PROGRAM := test + +all: $(PROGRAM) + +test: $(OBJECTS) + $(CXX) -o $@ $^ $(LDFLAGS) $(LIBS) $(PROGRAM_LIBS) + +clean: + rm -f *.o +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cpp-qm-dsp/test.cpp Fri Nov 01 16:13:22 2013 +0000 @@ -0,0 +1,13 @@ + +#include "CQKernel.h" + +#include <iostream> + +int main(int argc, char **argv) +{ + CQKernel k(96000, 48000, 24); + + std::cerr << "Q = " << k.getProperties().Q << std::endl; + +} +
--- a/yeti/cqt.yeti Wed Oct 30 18:26:04 2013 +0000 +++ b/yeti/cqt.yeti Fri Nov 01 16:13:22 2013 +0000 @@ -12,13 +12,14 @@ fft = load may.transform.fft; vec = load may.vector; af = load may.stream.audiofile; +plot = load may.plot; { pow, round, floor, ceil, log2, nextPowerOfTwo } = load may.mathmisc; cqt str = (sampleRate = str.sampleRate; maxFreq = sampleRate/2; - minFreq = 40; + minFreq = 50; binsPerOctave = 24; eprintln "Here"; @@ -109,6 +110,9 @@ cqblocks = array (map2 do octlist octave: d = emptyHops * (pow 2 (octaves-octave)) - emptyHops; + + d = 0; //!!! + eprintln "dropping \(d)"; drop d octlist; done cqblocks [1..octaves]); @@ -140,7 +144,7 @@ atomNo = int (col / colsPerAtom); atomOffset = col % colsPerAtom; - if atomOffset == 0 and atomNo < length bits[oct] then + if /*!!! atomOffset == 0 and */ atomNo < length bits[oct] then bits[oct][atomNo][binNo]; else cplx.zero @@ -190,14 +194,20 @@ //testStream = manipulate.withDuration 96000 (syn.sinusoid 48000 500); //testStream = manipulate.withDuration 96000 (syn.pulseTrain 48000 4); testStream = af.open "sweep-48000.wav"; +//testStream = af.open "sweep.wav"; eprintln "have test stream"; -for (cqt testStream) do c: +cq = cqt testStream; + +for cq do c: mm = cm.magnitudes c; for (mat.asColumns mm) (println . strJoin "," . vec.list); done; +bigM = mat.concat (Horizontal ()) (map cm.magnitudes cq); + +//\() (plot.plot [Contour bigM]); + () -