AY Emulator of the AY (aka YM) soundchip, used in Spectrum/Atari

AY.ar(tonea, toneb, tonec, noise, control, vola, volb, volc, envfreq, envstyle, chiptype, mul, add)

Emulates the General Instrument AY-3-8910 (a.k.a. the Yamaha YM2149) 3-voice sound chip, as found in the ZX Spectrum 128, the Atari ST, and various other home computers during the 1980s.

The inputs are as follows:

* tonea, toneb, tonec - integer "tone" value for each of the 3 voices, from 0 to 4095 (i.e. 12-bit range). Higher value = lower pitch. For convenience, the AY.freqtotone method converts a frequency value to something appropriate for these inputs.

* noise - the period of the pseudo-random noise generator, 0 to 31

* control - controls how the noise is mixed into the tone(s), 0 to 32 (0 is mute). This is a binary mask value which masks the noise/tone mixture in each channel, so it's not linear.

* vola, volb, volc - volume of the three channels, 0 to 15 (or 0 to 31 if using YM chiptype)

* envfreq - envelope frequency, 0 to 4095

* envstyle - type of envelope used, 0 to 15

* chiptype - 0 for AY (default), 1 for YM. The models behave slightly differently. This input cannot be modulated - its value is only handled at the moment the UGen starts.

* mul, add - general multiply/add controls for the resulting signal

The chip's inputs are integer values, so non-integer values will be rounded off.

The emulation is provided by the libayemu library: http://sourceforge.net/projects/libayemu - I have merely wrapped it up as a SC UGen.

Examples

s.boot;

// Default...

x = {AY.ar * 0.1}.play(s);

x.free;

// Mouse-controlled tones

(

x = {

Pan2.ar(AY.ar(

tonea: MouseY.kr(10, 3900, 1),

toneb: MouseX.kr(10, 3900, 1),

control: 3,

vola: 14,

volb: 14,

volc: 0,

mul: 0.1

))

}.play;

)

x.free;

// A random walk in a ZX Spectrum

(

x = {

var rate = MouseX.kr(0.1, 10); // You control your wandering speed

Pan2.ar(AY.ar(

tonea: LFDNoise3.kr(rate).range(10, 3900),

toneb: LFDNoise3.kr(rate).range(10, 3900),

tonec: LFDNoise3.kr(rate).range(10, 3900),

noise: LFDNoise3.kr(rate).range(0, 31),

control: LFDNoise0.kr(rate).range(0, 31),

vola: LFDNoise3.kr(rate).range(0, 15),

volb: LFDNoise3.kr(rate).range(0, 15),

volc: LFDNoise3.kr(rate).range(0, 15),

envfreq: LFDNoise3.kr(rate).range(0, 4095),

envstyle: LFDNoise3.kr(rate).range(0, 15),

mul: 0.1

))

}.play;

)

x.free;

// Now to define a synth which can be used in patterns etc

(

SynthDef(\ay1, { | freqa=440, freqb=550, freqc=660, vola=15, volb=0, volc=0, chink=1, wobbly=1, pan=0, amp=0.1, gate=1|

var ay, chinkenv, wobblyenv;

//chinkenv = if(chink>0, EnvGen.kr(Env.new([0.06125, 0.06125, 1, 1], [0.05, 0, 0.1], 0, 4, 4)), 1);

chinkenv = if(chink>0, EnvGen.kr(Env.new([2, 2, 1, 1], [0.05, 0, 0.1], 0, 4, 4)), 1);

wobblyenv = LFPulse.kr(10, 0.5, mul:wobbly).range(0.5, 1);

# freqa, freqb, freqc = [freqa, freqb, freqc] * [1, wobblyenv, wobblyenv] * chinkenv;

ay = AY.ar(AY.freqtotone(freqa), AY.freqtotone(freqb), AY.freqtotone(freqc),

0, 3, vola, volb, volc, mul: amp);

ay = ay * EnvGen.kr(Env.asr(0.01, 1, 0.05), gate, doneAction:2);

Out.ar(0, Pan2.ar(ay, pan));

}).load(s);

)

x = Synth(\ay1, [\wobbly, 0, \chink, 1, \tonea, 1000.rand]);

x.free;

SynthDescLib.read;

// Use the synth in a jerky lo-fi pattern of some sort...

(

Pbind(

\instrument, \ay1,

\freqa, Pseq((#[55, 55, 57, 58, 57, 55, 58, 50]-12).midicps, inf),

\freqb, Pseq([

Pseq( (#[55, 55, 54, 55, 54, 55, 58, 57]+12).midicps, 2),

Prand((#[55, 55, 54, 55, 54, 55, 58, 57]+12).midicps, 2)

], inf),

\dur, Pseq(#[3, 0.5, 0.5, 1.5, 0.5, 1, 1, 4] * 0.4, inf),

\wobbly, Pstutter(8 * 4, Prand(#[0, 1], inf)),

\vola, 15,

\volb, 14,

\chink, 1,

\amp, 0.4

).play

)