2011
08.09

Let’s start by hearing the sinusoid oscillator we built in part 11:

Import Approx
Import part7-simpleosc

Package FM{

	Op(freq)
	{
		Op = Approx:Sin(0.5 - periodic-ramp(Audio:Clock(freq)) #7)
	}
}

Sweet! Sounds quite pure, and you can bump up the approximation to #8 or #9 if desired. Let’s compare the performance by making a reference oscillator and trying out 10 of each.

ref-osc(freq)
{
	ref-osc = Crt:sin(periodic-ramp(Audio:Clock(freq)) * 2 * Pi)
}

test(osc)
{
	Use Algorithm
	freqs = Expand(#10 'arg + 0.008 0.008)
	test = Reduce(Add Map(osc freqs))
}
PS > .\k2cli.exe --audio-out "test(ref-osc)" .\part12-fm.k
K2CLI 0.1
(c) 2011 Vesa Norilo

Reactive processing active... Press any key to quit
CPU: 8.7%
PS > .\k2cli.exe --audio-out "test(FM:Op)" .\part12-fm.k
K2CLI 0.1
(c) 2011 Vesa Norilo

Reactive processing active... Press any key to quit
CPU: 1.5%

Not bad, a significant performance improvement! The sounds are frankly indistinguishable in this case. Let’s build a MIDI-controllable synth with it.

MIDI Control

Internally, Kronos handles all MIDI as OSC. MIDI input triggers OSC methods at path ‘/midi/device_name/event_type’. MIDI events show up as packed doublewords in 32-bit integer format. The system provides an unpacking helper function as ‘MIDI:Unpack’ that converts this doubleword to a tuple of integers. To listen to all MIDI events, you can listen to OSC method ‘/midi/*’, as in this example:

test-midi()
{
	test-midi = MIDI:Unpack(OSC:In("/midi/*" '0i))
}
PS > .\k2cli.exe --console "test-midi()" .\part12-fm.k
K2CLI 0.1
(c) 2011 Vesa Norilo

[OSC] Routing '/midi/Babyface Midi Port 1' to '/midi/*'
(0i 0i 0i 0i)
Reactive processing active... Press any key to quit
(0i 144i 71i 55i)
(0i 128i 71i 64i)

The system provides a helper function that converts MIDI data into a frequency/gate pair. The following example will assume a sampling rate of 44.1kHz;

test-synth()
{
	note-on = IO:OSC:In("/midi/*/note-on" '0i)
	note-off = IO:OSC:In("/midi/*/note-off" '0i)
	(gate freq) = MIDI:To-CV(note-on note-off 440)
	test-synth = FM:Op(freq / 44100) * gate
}

It should give you an oscillator to play with any MIDI input available to your system. If there’s a lot of latency, it’s time to revisit the setup; see part 1¬†on how to select an audio device. WASAPI or ASIO is recommended.

Next order of business is to improve the sound. Let’s get rid of the snaps by smoothing the gate signal to create an envelope;

smooth(sig lag)
{
	out = z-1('0 sig + lag * (out - sig))
	smooth = out
}

test-synth-env()
{
	note-on = IO:OSC:In("/midi/*/note-on" '0i)
	note-off = IO:OSC:In("/midi/*/note-off" '0i)
	(gate freq) = MIDI:To-CV(note-on note-off 440)

	env = smooth(Audio:Clock(gate) 0.99)

	test-synth-env = FM:Op(freq / 44100) * env
}

FM Synthesis

Let’s add frequency modulation; we need a carrier operator and a modulator operator, to modulate the carrier frequency.

test-synth-fm()
{
	note-on = IO:OSC:In("/midi/*/note-on" '0i)
	note-off = IO:OSC:In("/midi/*/note-off" '0i)
	(gate freq) = MIDI:To-CV(note-on note-off 440)

	amp-env = smooth(Audio:Clock(gate) 0.99)
	fm-env = smooth(Audio:Clock(gate) 0.9999)

	modulator = FM:Op(3 * freq / 44100) * fm-env * 8
	test-synth-fm = FM:Op((1 + modulator) * freq / 44100) * amp-env * 0.8
}

Nice! In upcoming parts, we will learn to make more involved envelopes.

Harnessing generics

Maybe we’d like to try the synth with different oscillators? Let’s make the oscillator an user supplied parameter:

test-generic-fm(oscillator)
{
	note-on = IO:OSC:In("/midi/*/note-on" '0i)
	note-off = IO:OSC:In("/midi/*/note-off" '0i)
	(gate freq) = MIDI:To-CV(note-on note-off 440)

	amp-env = smooth(Audio:Clock(gate) 0.99)
	fm-env = smooth(Audio:Clock(gate) 0.9999)

	modulator = Eval(oscillator 3 * freq / 44100) * fm-env * 8
	test-synth-fm = Eval(oscillator (1 + modulator) * freq / 44100) * amp-env * 0.8
}

Try the reference oscillator for CPU use comparisons:

PS > .\k2cli.exe --audio-dev 16 16 --audio-out "test-generic-fm(ref-osc)" .\part12-fm.k

Or even the geometric oscillators we made before for some rougher sounds:

PS > .\k2cli.exe --audio-dev 16 16 --audio-out "test-generic-fm(tri-osc)" .\part12-fm.k
PS > .\k2cli.exe --audio-dev 16 16 --audio-out "test-generic-fm(saw-osc)" .\part12-fm.k

No Comment.

Add Your Comment


× 8 = thirty two