linux: shared memory interface - link with librt
[supercollider.git] / HelpSource / Classes / KeyTrack.schelp
blob7138e0e6aa2d31f1881aa3d9551c01f04eea3524
1 class:: KeyTrack
2 summary:: Key tracker
3 categories:: UGens>Analysis>Pitch
4 related:: Classes/BeatTrack, Classes/Loudness, Classes/MFCC, Classes/Onsets, Classes/Pitch
6 description::
7 A (12TET major/minor) key tracker based on a pitch class profile of energy across FFT bins and matching this to templates for major and minor scales in all transpositions. It assumes a 440 Hz concert A reference. Output is 0-11 C major to B major, 12-23 C minor to B minor.
9 classmethods::
11 method:: kr
13 argument:: chain
14 [fft] Audio input to track. This must have been pre-analysed by a 4096 size FFT. No other FFT sizes are valid except as noted below.
15 code::
16 // With standard hop of half FFT size = 2048 samples
17 b = Buffer.alloc(s,4096,1); // for sampling rates 44100 and 48000
18 //b = Buffer.alloc(s,8192,1); // for sampling rates 88200 and 96000
21 argument:: keydecay
22 [sk] Number of seconds for the influence of a window on the final key decision to decay by 40dB (to 0.01 its original value).
24 argument:: chromaleak
25 [sk] Each frame, the chroma values are set to the previous value multiplied by the chromadecay. 0.0 will start each frame afresh with no memory.
27 examples::
29 code::
30 // The following files are test materials on my machine; you will subsitute your own filenames here
31 // A major
32 d = Buffer.read(s,"/Volumes/data/stevebeattrack/samples/100.wav");
33 // F major; hard to track!
34 d = Buffer.read(s,"/Volumes/data/stevebeattrack/samples/115.wav");
36 // straight forward since no transients; training set from MIREX2006
37 // 01 = A major
38 // 57 = b minor
39 // 78 e minor
40 // 08 Bb major
41 d = Buffer.read(s, "/Users/nickcollins/Desktop/ML/training_wav/78.wav")
43 b = Buffer.alloc(s, 4096, 1); // for sampling rates 44100 and 48000
47 var in, fft, resample;
48 var key, transientdetection;
50 in = PlayBuf.ar(1, d, BufRateScale.kr(d), 1, 0, 1);
52 fft = FFT(b, in);
54 key=KeyTrack.kr(fft, 2.0, 0.5);
56 key.poll;
58 Out.ar(0,Pan2.ar(in));
59 }.play
65 code::
66 // alternating major and minor chords as a test
69 var in, fft, resample;
70 var key, transientdetection;
72 in = Mix(SinOsc.ar((60 + [0, MouseX.kr(3, 4).round(1), 7]).midicps, 0, 0.1));
74 // major dom 7 and minor 7; major keys preferred here
75 //in = Mix(SinOsc.ar((60 + (MouseY.kr(0, 11).round(1.0)) + [0, MouseX.kr(3, 4).round(1), 7, 10]).midicps, 0, 0.1));
77 fft = FFT(b, in);
79 key = KeyTrack.kr(fft);
81 key.poll;
83 Out.ar(0,Pan2.ar(in));
84 }.play
90 code::
91 // Nice to hear what KeyTrack thinks:
93 d = Buffer.read(s, "/Users/nickcollins/Desktop/ML/training_wav/78.wav")
94 b = Buffer.alloc(s, 4096, 1); // for sampling rates 44100 and 48000
98 var in, fft, resample, chord, rootnote, sympath;
99 var key, transientdetection;
101 in = PlayBuf.ar(1, d, BufRateScale.kr(d), 1, 0, 1);
103 fft = FFT(b, in);
105 key = KeyTrack.kr(fft, 2.0, 0.5);
106 key.poll;
107 key = Median.kr(101, key); // Remove outlier wibbles
109 chord = if(key<12, #[0, 4, 7], #[0, 3, 7]);
110 rootnote = if(key<12, key, key-12) + 60;
112 sympath = SinOsc.ar((rootnote + chord).midicps, 0, 0.4).mean;
114 Out.ar(0,Pan2.ar(in, -0.5) + Pan2.ar(sympath, 0.5));
115 }.play
121 code::
122 // Research Notes:
123 // See the MIREX2006 audio key tracking competition and Emilia Gomez's PhD thesis, Tonal Description of Music Audio Signals
125 // The following code was used to create the datasets for the UGen, and would be the basis of extensions
127 // Need one set of bin data for 44100 and one for 48000
129 // KeyTrack calculations, need to make arrays of FFT bins and weights for each chromatic tone.
130 // greater resolution, 4096 FFT, avoid lower octaves, too messy there
131 // 60*6*2 output arrays
134 var fftN, fftBins, binsize;
135 var midinotes;
136 var sr;
137 var wtlist, binlist;
139 sr = 48000; //44100;
141 fftN = 4096;
142 fftBins = fftN.div(2);
143 binsize = sr / fftN;
145 midinotes = (33..92); // 60 notes, 55 Hz up to 1661.2187903198 Hz
147 wtlist = List[];
148 binlist = List[];
150 // for each note have six harmonic locations
151 midinotes.do{ |note|
152         var freq, whichbin, lowerbin, prop;
154         freq = note.midicps;
156         6.do{|j|
157                 var partialfreq, partialamp;
159                 partialamp = 1.0 / (j + 1);
160                 partialfreq = freq * (j + 1);
162                 whichbin = partialfreq / binsize;
163                 lowerbin = whichbin.asInteger;
164                 prop = 1.0 - (whichbin - lowerbin);
166                 binlist.add(lowerbin).add(lowerbin + 1);
167                 wtlist.add(prop * partialamp).add((1.0 - prop) * partialamp);
169         };
174 Post << (binlist) << nl<< nl;
176 Post << (wtlist) << nl<< nl;
178 binlist.size.postln;
179 wtlist.size.postln;