annotate yeti/cqt.yeti @ 26:a5f71b5c9f85

Minor updates
author Chris Cannam <c.cannam@qmul.ac.uk>
date Mon, 04 Nov 2013 17:51:27 +0000
parents 5ca24ff67566
children 7f0133234f00
rev   line source
c@10 1
c@19 2 program cqt;
c@10 3
c@10 4 cqtkernel = load cqtkernel;
c@10 5 resample = load may.stream.resample;
c@10 6 manipulate = load may.stream.manipulate;
c@10 7 syn = load may.stream.syntheticstream;
c@10 8 cm = load may.matrix.complex;
c@10 9 mat = load may.matrix;
c@10 10 framer = load may.stream.framer;
c@10 11 cplx = load may.complex;
c@10 12 fft = load may.transform.fft;
c@10 13 vec = load may.vector;
c@17 14 af = load may.stream.audiofile;
c@22 15 plot = load may.plot;
c@10 16
c@10 17 { pow, round, floor, ceil, log2, nextPowerOfTwo } = load may.mathmisc;
c@10 18
c@10 19 cqt str =
c@10 20 (sampleRate = str.sampleRate;
c@10 21 maxFreq = sampleRate/2;
c@22 22 minFreq = 50;
c@10 23 binsPerOctave = 24;
c@10 24
c@16 25 eprintln "Here";
c@10 26
c@10 27 octaves = ceil (log2 (maxFreq / minFreq));
c@10 28
c@16 29 eprintln "Here: about to calculate stuff with \(octaves)";
c@10 30
c@10 31 actualMinFreq = (maxFreq / (pow 2 octaves)) * (pow 2 (1/binsPerOctave));
c@10 32
c@16 33 eprintln "sampleRate = \(sampleRate), maxFreq = \(maxFreq), minFreq = \(minFreq), actualMinFreq = \(actualMinFreq), octaves = \(octaves), binsPerOctave = \(binsPerOctave)";
c@10 34
c@10 35 kdata = cqtkernel.makeKernel { sampleRate, maxFreq, binsPerOctave };
c@10 36
c@16 37 eprintln "atomsPerFrame = \(kdata.atomsPerFrame)";
c@11 38
c@10 39 streams = manipulate.duplicated octaves str;
c@10 40
c@10 41 //!!! can't be right!
c@10 42 kernel = cm.transposed (cm.conjugateTransposed kdata.kernel);
c@10 43
c@16 44 eprintln "have kernel";
c@10 45
c@10 46 fftFunc = fft.forward kdata.fftSize;
c@10 47
c@10 48 cqblocks =
c@10 49 map do octave:
c@10 50 frames = framer.monoFrames //!!! mono for now
c@10 51 { framesize = kdata.fftSize, hop = kdata.fftHop }
c@10 52 (resample.decimated (pow 2 octave) streams[octave]);
c@10 53 map do frame:
c@10 54 freq = fftFunc (cplx.complexArray frame (vec.zeros kdata.fftSize));
c@10 55 cm.product kernel (cm.newComplexColumnVector freq);
c@10 56 done frames;
c@10 57 done [0..octaves-1];
c@10 58
c@13 59 // The cqblocks list is a list<list<matrix>>. Each top-level list
c@11 60 // corresponds to an octave, from highest to lowest, each having
c@11 61 // twice as many elements in its list as the next octave. The
c@11 62 // sub-lists are sampled in time with an effective spacing of
c@11 63 // fftSize * 2^(octave-1) audio frames, and the matrices are row
c@11 64 // vectors with atomsPerFrame * binsPerOctave complex elements.
c@13 65 //
c@13 66 // ***
c@13 67 //
c@13 68 // In a typical constant-Q structure, each (2^(octaves-1) *
c@13 69 // fftHop) input frames gives us an output structure conceptually
c@13 70 // like this:
c@10 71 //
c@10 72 // [][][][][][][][] <- fftHop frames per highest-octave output value
c@10 73 // [][][][][][][][] layered as many times as binsPerOctave (here 2)
c@10 74 // [--][--][--][--] <- fftHop*2 frames for the next lower octave
c@10 75 // [--][--][--][--] etc
c@10 76 // [------][------]
c@10 77 // [------][------]
c@10 78 // [--------------]
c@10 79 // [--------------]
c@10 80 //
c@13 81 // ***
c@13 82 //
c@13 83 // But the kernel we're using here has more than one temporally
c@13 84 // spaced atom; each individual cell is a row vector with
c@13 85 // atomsPerFrame * binsPerOctave elements, but that actually
c@13 86 // represents a rectangular matrix of result cells with width
c@13 87 // atomsPerFrame and height binsPerOctave. The columns of this
c@13 88 // matrix (the atoms) then need to be spaced by 2^(octave-1)
c@13 89 // relative to those from the highest octave.
c@10 90
c@15 91 // Reshape each row vector into the appropriate rectangular matrix
c@21 92 // and split into single-atom columns
c@19 93
c@21 94 emptyHops = kdata.firstCentre / kdata.atomSpacing;
c@21 95 maxDrop = emptyHops * (pow 2 (octaves-1)) - emptyHops;
c@21 96 eprintln "maxDrop = \(maxDrop)";
c@21 97
c@21 98 cqblocks = map do octlist:
c@21 99 concat
c@21 100 (map do rv:
c@21 101 cm.asColumns
c@21 102 (cm.generate do row col:
c@21 103 cm.at rv ((row * kdata.atomsPerFrame) + col) 0
c@21 104 done {
c@21 105 rows = kdata.binsPerOctave,
c@21 106 columns = kdata.atomsPerFrame
c@21 107 })
c@21 108 done octlist)
c@21 109 done cqblocks;
c@21 110
c@21 111 cqblocks = array (map2 do octlist octave:
c@21 112 d = emptyHops * (pow 2 (octaves-octave)) - emptyHops;
c@22 113
c@22 114 d = 0; //!!!
c@22 115
c@21 116 eprintln "dropping \(d)";
c@21 117 drop d octlist;
c@21 118 done cqblocks [1..octaves]);
c@14 119
c@17 120 assembleBlock bits =
c@19 121 (eprintln "assembleBlock: structure of bits is:";
c@19 122 eprintln (map length bits);
c@19 123
c@19 124 rows = octaves * kdata.binsPerOctave;
c@19 125 columns = (pow 2 (octaves - 1)) * kdata.atomsPerFrame;
c@19 126
c@18 127 cm.generate do row col:
c@19 128
c@19 129 // bits structure: [1,2,4,8,...]
c@19 130
c@19 131 // each elt of bits is a list of the chunks that should
c@19 132 // make up this block in that octave (lowest octave first)
c@19 133
c@19 134 // each chunk has atomsPerFrame * binsPerOctave elts in it
c@19 135
c@19 136 // row is disposed with 0 at the top, highest octave (in
c@19 137 // both pitch and index into bits structure)
c@19 138
c@18 139 oct = int (row / binsPerOctave);
c@19 140 binNo = row % kdata.binsPerOctave;
c@21 141
c@19 142 chunks = pow 2 oct;
c@21 143 colsPerAtom = int (columns / (chunks * kdata.atomsPerFrame));
c@21 144 atomNo = int (col / colsPerAtom);
c@21 145 atomOffset = col % colsPerAtom;
c@18 146
c@22 147 if /*!!! atomOffset == 0 and */ atomNo < length bits[oct] then
c@21 148 bits[oct][atomNo][binNo];
c@20 149 else
c@20 150 cplx.zero
c@20 151 fi;
c@19 152
c@19 153 done { rows, columns };
c@19 154 );
c@15 155
c@17 156 processOctaveLists octs =
c@17 157 case octs[0] of
c@17 158 block::rest:
c@19 159 (toAssemble = array
c@19 160 (map do oct:
c@21 161 n = kdata.atomsPerFrame * pow 2 oct;
c@17 162 if not empty? octs[oct] then
c@19 163 forBlock = array (take n octs[oct]);
c@17 164 octs[oct] := drop n octs[oct];
c@17 165 forBlock
c@17 166 else
c@19 167 array []
c@17 168 fi
c@19 169 done (keys octs));
c@17 170 assembleBlock toAssemble :. \(processOctaveLists octs));
c@17 171 _: []
c@15 172 esac;
c@15 173
c@19 174 eprintln "cqblocks has \(length cqblocks) entries";
c@15 175
c@17 176 octaveLists = [:];
c@19 177
c@19 178 cqblocks = array cqblocks;
c@17 179 for [1..octaves] do oct:
c@17 180 octaveLists[octaves - oct] := cqblocks[oct-1];
c@17 181 done;
c@17 182 /*
c@17 183 \() (map2 do octlist octave:
c@17 184 println "oct \(octaves) - \(octave) = \(octaves - octave)";
c@17 185 octaveLists[octaves - octave] := octlist
c@17 186 done cqblocks [1..octaves]);
c@17 187 */
c@19 188 eprintln "octaveLists keys are: \(keys octaveLists)";
c@17 189
c@17 190 processOctaveLists octaveLists;
c@15 191
c@10 192 );
c@10 193
c@17 194 //testStream = manipulate.withDuration 96000 (syn.sinusoid 48000 500);
c@17 195 //testStream = manipulate.withDuration 96000 (syn.pulseTrain 48000 4);
c@21 196 testStream = af.open "sweep-48000.wav";
c@22 197 //testStream = af.open "sweep.wav";
c@10 198
c@16 199 eprintln "have test stream";
c@10 200
c@22 201 cq = cqt testStream;
c@22 202
c@22 203 for cq do c:
c@21 204 mm = cm.magnitudes c;
c@21 205 for (mat.asColumns mm) (println . strJoin "," . vec.list);
c@21 206 done;
c@10 207
c@25 208 bigM = mat.concatHorizontal (map cm.magnitudes cq);
c@22 209
c@22 210 //\() (plot.plot [Contour bigM]);
c@22 211
c@16 212 ()
c@16 213