3 related:: Classes/Index, Classes/WrapIndex
4 categories:: UGens>Buffer
7 Performs waveshaping on the input signal by indexing into the table.
13 The number of a buffer filled in wavetable format containing the
21 Examples use the Internal Server to show the effect of waveshaping via a scope. Use .play instead if necessary.
23 Server.default = s = Server.internal; s.boot;
25 b = Buffer.alloc(s, 512, 1, { |buf| buf.chebyMsg([1,0,1,1,0,1])});
31 SinOsc.ar(300, 0, Line.kr(0,1,6)),
40 image::chebyshevpolynomials.png::
42 Wave shaping transfer functions are typically designed by using Chebyshev polynomials to control which harmonics are generated when a cosine wave is passed in. The implementation in SuperCollider compensates for the DC offset due to even polynomial terms, making sure that when 0 is put into the transfer function, you get 0 out. By default, normalization is set to true, which avoids output overload. If you want to construct a transfer function without this, you need to be careful with the final output scaling, since it could easily overload the -1 to 1 range for audio.
44 // I want the first harmonic at 0.25 amplitude, second at 0.5, third at 0.25
45 b = Buffer.alloc(s, 512, 1, {arg buf; buf.chebyMsg([0.25,0.5,0.25], false)});
51 SinOsc.ar(440, 0.5pi, Line.kr(0,1,6)), //input cosine wave
52 0.5 //scale output down because otherwise it goes between -1.05 and 0.5, distorting...
60 For those who like to make their own wavetables for arbitrary shapers, your buffer must be in wavetable format to have a valid transfer function. Wavetable format is a special representation to make linear interpolation faster (see at the bottom of this file). You don't have to worry about this directly, because there are two straight forward ways to get wavetables into a server buffer. First, the server can generate them (see the Buffer help file for the methods sine1, sine2, sine3 and cheby):
62 b = Buffer.alloc(s, 1024, 1);
63 b.cheby([1, 0.5, 1, 0.125]);
66 { var sig = Shaper.ar(b, SinOsc.ar(440, 0, 0.4));
73 Or, you can calculate the transfer function in a client-side array (Signal class) then convert it to a wavetable and send the data over.
75 b = Buffer.alloc(s, 1024, 1);
77 //size must be power of two plus 1
78 t = Signal.chebyFill(513,[1, 0.5, 1, 0.125]);
83 // t.asWavetableNoWrap will convert it to the official Wavetable format at next power of two size
84 b.sendCollection(t.asWavetableNoWrap); // may also use loadCollection here
89 { var sig = Shaper.ar(b, SinOsc.ar(440, 0, 0.4));
97 This way of working then allows you to get creative with your transfer functions!
99 b = Buffer.alloc(s, 1024, 1);
101 // or, for an arbitrary transfer function, create the data at 1/2 buffer size + 1
102 t = Signal.fill(513, { |i| i.linlin(0.0, 512.0, -1.0, 1.0) });
107 // t.asWavetable will convert it to the official Wavetable format at twice the size
108 b.sendCollection(t.asWavetableNoWrap); // may also use loadCollection here
110 // shaper has no effect because of the linear transfer function
112 { var sig = Shaper.ar(b, SinOsc.ar(440, 0, 0.4));
120 a = Signal.fill(256, { |i|
122 t + (0.1 * (max(t, 0.1) - 0.1) * sin(2pi * t * 80 + sin(2pi * 25.6 * t)))
128 d = (a.copy.reverse.neg) ++(Signal[0])++ a;
132 d.size //must be buffer size/2 + 1, so 513 is fine
134 b.sendCollection(d.asWavetableNoWrap); // may also use loadCollection here
136 b.plot // wavetable format!
143 SinOsc.ar(440, 0.5, Line.kr(0,0.9,6))
149 subsection:: Advanced notes: wavetable format
151 Signal: [a0, a1, a2...]
152 Wavetable: [2*a0-a1, a1-a0, 2*a1-a2, a2-a1, 2*a2-a3, a3-a2...]
154 This strange format is not a standard linear interpolation (integer + frac), but for (integer part -1) and (1+frac)) due to some efficient maths for integer to float conversion in the underlying C code.