File: autotune.ck

package info (click to toggle)
chuck 1.5.5.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 41,056 kB
  • sloc: cpp: 123,473; ansic: 35,893; javascript: 2,111; yacc: 609; makefile: 457; python: 174; perl: 86
file content (241 lines) | stat: -rw-r--r-- 6,139 bytes parent folder | download | duplicates (2)
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] );
}