Mercurial > hg > constant-q-cpp
view yeti/cqt.yeti @ 15:7f9a9af75d91
Towards shuffling the cq cells into the right arrangement for return
author | Chris Cannam <c.cannam@qmul.ac.uk> |
---|---|
date | Tue, 29 Oct 2013 16:55:55 +0000 |
parents | 26d61c9f1f40 |
children | 310435497b76 |
line wrap: on
line source
module cqt; cqtkernel = load cqtkernel; resample = load may.stream.resample; manipulate = load may.stream.manipulate; syn = load may.stream.syntheticstream; cm = load may.matrix.complex; mat = load may.matrix; framer = load may.stream.framer; cplx = load may.complex; fft = load may.transform.fft; vec = load may.vector; { pow, round, floor, ceil, log2, nextPowerOfTwo } = load may.mathmisc; cqt str = (sampleRate = str.sampleRate; maxFreq = sampleRate/2; minFreq = 40; binsPerOctave = 24; println "Here"; octaves = ceil (log2 (maxFreq / minFreq)); println "Here: about to calculate stuff with \(octaves)"; actualMinFreq = (maxFreq / (pow 2 octaves)) * (pow 2 (1/binsPerOctave)); println "sampleRate = \(sampleRate), maxFreq = \(maxFreq), minFreq = \(minFreq), actualMinFreq = \(actualMinFreq), octaves = \(octaves), binsPerOctave = \(binsPerOctave)"; kdata = cqtkernel.makeKernel { sampleRate, maxFreq, binsPerOctave }; println "atomsPerFrame = \(kdata.atomsPerFrame)"; streams = manipulate.duplicated octaves str; //!!! can't be right! kernel = cm.transposed (cm.conjugateTransposed kdata.kernel); println "have kernel"; fftFunc = fft.forward kdata.fftSize; cqblocks = map do octave: frames = framer.monoFrames //!!! mono for now { framesize = kdata.fftSize, hop = kdata.fftHop } (resample.decimated (pow 2 octave) streams[octave]); map do frame: freq = fftFunc (cplx.complexArray frame (vec.zeros kdata.fftSize)); cm.product kernel (cm.newComplexColumnVector freq); done frames; done [0..octaves-1]; // The cqblocks list is a list<list<matrix>>. Each top-level list // corresponds to an octave, from highest to lowest, each having // twice as many elements in its list as the next octave. The // sub-lists are sampled in time with an effective spacing of // fftSize * 2^(octave-1) audio frames, and the matrices are row // vectors with atomsPerFrame * binsPerOctave complex elements. // // *** // // In a typical constant-Q structure, each (2^(octaves-1) * // fftHop) input frames gives us an output structure conceptually // like this: // // [][][][][][][][] <- fftHop frames per highest-octave output value // [][][][][][][][] layered as many times as binsPerOctave (here 2) // [--][--][--][--] <- fftHop*2 frames for the next lower octave // [--][--][--][--] etc // [------][------] // [------][------] // [--------------] // [--------------] // // *** // // But the kernel we're using here has more than one temporally // spaced atom; each individual cell is a row vector with // atomsPerFrame * binsPerOctave elements, but that actually // represents a rectangular matrix of result cells with width // atomsPerFrame and height binsPerOctave. The columns of this // matrix (the atoms) then need to be spaced by 2^(octave-1) // relative to those from the highest octave. // Reshape each row vector into the appropriate rectangular matrix cqblocks = map do octlist: map do rv: cm.generate do row col: cm.at rv ((row * kdata.atomsPerFrame) + col) 0 done { rows = kdata.binsPerOctave, columns = kdata.atomsPerFrame } done octlist done cqblocks; //println "lengths per oct: \(map length cqblocks)"; // Slice each octave's blocks into a list of columns with zeros // interspersed. This doesn't look very elegant (?) cqblocks = map2 do octlist octave: concat (map do m: concat (map do col: // col :: map \(cplx.zeros kdata.binsPerOctave) [1..pow 2 octave - 1] map \col [0..pow 2 octave - 1] done (cm.asColumns m)) done octlist) done cqblocks [0..octaves-1]; // Then pile up the slices into taller slices spanning all octaves pileOf octaves acc = case (head octaves) of h::t: (heads = map head octaves; // tall = concat (map reverse heads); // are the bpos values low->high or high->low? tall = concat heads; pileOf (map tail octaves) (acc ++ [tall])); _: acc; esac; pileOf cqblocks []; //println "lengths per oct: \(map length cqblocks)"; //cqblocks; ); testStream = manipulate.withDuration 96000 (syn.sinusoid 48000 500); println "have test stream"; cqt testStream;