1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
|
//---------------------------------------------------------------
// name: autotune.ck
// desc: because no one asked for it
// (and it shows how PitchTrack works)
//
// API reference for PitchTrack
// https://chuck.stanford.edu/doc/reference/chugins.html#PitchTrack
//
// required data file:
// https://chuck.stanford.edu/doc/examples/effects/data/obama.wav
//
// note: PitchTrack is a standard distribution chugin (ChucK plugin)
// that is included with the macOS and Windows installers;
// on Linux or if you are building chuck from source, you will
// need to compile PitchTrack.chug for your system
// https://github.com/ccrma/chugins/
//---------------------------------------------------------------
// ananlysis
SndBuf obama => PitchTrack tracker => blackhole;
// frame size
512 => tracker.frame;
// frame overlap
4 => tracker.overlap;
// synthesis
obama => HPF highpass => Delay del => PitShift autotune => Envelope fade => dac;
// high pass to get rid of the low din
100 => highpass.freq;
// ramp duration
1::second => fade.duration;
// set up envelope value and target
0 => fade.value;
1 => fade.target;
// input file name
"data/obama.wav" => string filename;
// print
cherr <= "reading input file: '" <= filename <= "'..." <= IO.nl();
// convert to full path relative to this file; read into SndBuf
me.dir() + filename => obama.read;
// modify the '0' to '1' to record a wav file of the output in real-time
// FYI to render faster-than-realtime, run this example with `--silent`
// > chuck autotune.ck --silent
// FYI this code always records if --silent is enabled
if( 0 || !Machine.realtime() )
{
dac => WvOut2 record => blackhole;
// output file name
"./autotuned-obama.wav" => string outfile;
// print
cherr <= "recording to file: '" <= outfile <= "'..." <= IO.nl();
// set output file name
me.dir() + outfile => record.wavFilename;
}
else
{
// print
cherr <= "recording to file: (disabled)" <= IO.nl();
}
// set delay using pitch tracker frame size
tracker.frame()::samp => del.delay;
// set input file to start
0 => obama.pos;
// length of input file
obama.length() => dur length;
// effect mix
1 => autotune.mix;
// start at 100% (no pitch shift yet)
1 => autotune.shift;
// echoing bells matching vocal pitch
TriOsc tri => ADSR env => Echo e => dac;
// some feedback for recirculating echoes
e => e;
// set oscilator gain
0.15 => tri.gain;
// set ADSR envelope
env.set(5::ms, 250::ms, 0, 0::ms);
// set delay
250::ms => e.max => e.delay;
// set echo
0.5 => e.mix;
// volume
0.8 => e.gain;
// m7 chord
[0, 3, 7, 10] @=> int pentatonic[];
// some variables we'll need later
float last_target;
// compute end time (not to be confused with end times, which is what we are living in)
now + length => time endtime;
// compute when to start fade out
now + (length - fade.duration()) => time fadetime;
// spork parallel processes (can comment out to remove their effect)
// scale switcher (chord progression generator)
spork ~ switchScale();
// play the ostinato pattern
spork ~ ostinato();
// for the duration of the file
while( now < endtime )
{
// one sample at a time
samp => now;
// get pitch estimate
tracker.get() => float track;
// find closest pitch
closest( track, pentatonic ) => float target;
// only trigger new note if different from previous
if( target > 60 && Std.fabs(target - last_target) > 0.1 )
{
1 => env.keyOn;
target => tri.freq;
target => last_target;
}
// perform autotune
if( track > 0 ) target / track => autotune.shift;
// begin fade out if the time is right
if( now >= fadetime ) 0 => fade.target;
}
// wait a little extra time
5::second => now;
// hold pitch
fun void hold_pitch( float t )
{
t => last_target;
250::ms => now;
-100 => last_target;
}
// scale switcher (chord progression generator)
fun void switchScale()
{
while( true )
{
12::second => now;
pentatonic[0]--;
pentatonic[2]--;
pentatonic[3]++;
11.5::second => now;
pentatonic[0]--;
2 -=> pentatonic[1];
pentatonic[2]--;
3 -=> pentatonic[3];
for (int i; i<pentatonic.size(); i++)
{
if (pentatonic[i] < -5) 12 +=> pentatonic[i];
}
}
}
// our repeated pattern
fun void ostinato()
{
// modal bar into revert
ModalBar mb => GVerb rev => fade;
// set gain
0.5 => mb.gain;
// which Modal Bar preset
1 => mb.preset;
// 0.01 => rev.mix;
// time loop
while( true )
{
// go through the scale
for( int degree : pentatonic )
{
// pitch to frequency and set in modal bar
(degree + 48) => Std.mtof => mb.freq;
// note one!
1 => mb.noteOn;
// for an 1/8 of a second
125::ms => now;
}
}
}
// helper function to find equal tempered pitch in list
// which is closest to freq testval
fun float closest( float testval, int list[] )
{
// list length
list.size() => int len;
int octave;
// freq to midi note number
Std.ftom(testval) => float testmidi;
// calculate octave
while( testmidi - (list[len-1] + octave) > 12 )
{
12 +=> octave;
}
48000.0 => float lowdiff;
int closest_index;
int closest_octave;
// loop over list
for( int i; i<len; i++ )
{
Std.mtof(octave + list[i]) => float listnote;
Math.fabs(listnote - testval) => float diff;
if (diff < lowdiff)
{
i => closest_index;
diff => lowdiff;
octave => closest_octave;
}
}
// loop over list 8va
for( int i; i<len; i++ )
{
Std.mtof(octave + 12 + list[i]) => float listnote;
Math.fabs(listnote - testval) => float diff;
if (diff < lowdiff)
{
i => closest_index;
diff => lowdiff;
octave + 12 => closest_octave;
}
}
// convert back to freq
return Std.mtof( closest_octave + list[closest_index] );
}
|