annotate yeti/cqt.yeti @ 20:dad5d8a06a5d

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