class:: VOsc
summary:: Variable wavetable oscillator.
related:: Classes/COsc, Classes/Osc, Classes/OscN, Classes/VOsc3
categories::  UGens>Generators>Deterministic


Description::

A wavetable lookup oscillator which can be swept smoothly across
wavetables. Fractional values of table will interpolate between two adjacent tables.


This oscillator requires at least two buffers to be filled with a wavetable format
signal. This preprocesses the signal into a form which can be used efficiently by
the oscillator. All the wavetables must be allocated to the same size, and the size
must be a power of two.

subsection:: Setting your tables with Buffers
The wavetable can be generated by filling a buffer via the
link::Classes/Buffer#Buffer Fill Commands:: (e.g. link::Classes/Buffer#-sine1::).
Set code::wavetable: true:: to ensure the proper data formatting.

Identically, you can create a link::Classes/Buffer:: object by sending one of the
code::\b_gen:: messages code::\sine1::, code::\sine2::,
code::\sine3:: (see
link::Reference/Server-Command-Reference#Wave Fill Commands:: and examples below),
again remembering to enable the wavetable format.

You can use link::Classes/Buffer#*allocConsecutive:: to ensure a contiguous buffers
numbers to be interpolated.

subsection:: Setting your tables with Signals
This can also be achieved by creating a link::Classes/Signal:: object and sending
it the link::Classes/Signal#-asWavetable:: message, saving it to disk, and having
the server load it from there.


classmethods::

method::ar, kr

argument::bufpos

Buffer index. Can be swept continuously among adjacent wavetable
buffers of the same size.


argument::freq

Frequency in Hertz. (non-interpolated control-rate)


argument::phase

Phase offset or modulator in radians.


argument::mul

Output will be multiplied by this value.


argument::add

This value will be added to the output.


Examples::

code::
// allocate and fill the buffers to be used by VOsc
(
s.waitForBoot({

	var numBufs = 8;

	// allocate table of consecutive buffers
	~bufs = Buffer.allocConsecutive(numBufs, s, 1024, 1);
	s.sync;

	~bufs.do({ arg buf, i;
		var n, a;

		// generate array of harmonic amplitudes
		n = (i+1)**2;
		a = Array.fill(n, { arg j; ((n-j)/n).squared.round(0.001) });

		// fill table
		// argument '7': flag for the \sine1 wave fill method
		// see "Wave Fill Commands" in the Server Command Reference
		s.performList(\sendMsg, \b_gen, buf.bufnum, \sine1, 7, a);

		s.sync;
	});
	"Buffers prepared.".postln;
})
)

// play the synth, indexing into the buffers with MouseX
(
x = SynthDef("help-VOsc",{ arg out = 0, bufoffset = 0;
	// mouse x controls the wavetable position
	var bufindex = MouseX.kr(0, ~bufs.size - 1) + bufoffset;

	Out.ar(out,
		VOsc.ar(bufindex, [120, 121], 0, 0.3)
	)
}).play(s,[\out, 0, \bufoffset, ~bufs.first.bufnum]);
)

// fill the buffers with new harmonics on-the-fly
(
~bufs.do({ arg buf, i;
	var a;
	a = Array.fill(i, 0) ++ [0.5, 1, 0.5];
	s.performList(\sendMsg, \b_gen, buf.bufnum, \sine1, 7, a);
});
)

// new harmonics...
(
~bufs.do({ arg buf, i;
	var a = Array.fill(32,0);
	12.do({ arg i; a.put(32.rand, 1) });
	s.performList(\sendMsg, \b_gen, buf.bufnum, \sine1, 7, a);
});
)

// new harmonics...
(
~bufs.do({ arg buf, i;
	var a = Array.fill((i+1)**2, { arg j; 1.0.rand2 });
	s.performList(\sendMsg, \b_gen, buf.bufnum, \sine1, 7, a);
});
)

// cleanup
(
x.free;
~bufs.do(_.free);
)

::
