Back at SC for the DX7 and midi

I used SC over 14 years ago,
Im back to using it because I remembered a wonderful DX7 model done by someone, It sounded so close to a real DX7. I really wanted to sample it.
this one;

I had to install an older version of SC and its plugins from around the time he stopped updating it. I remember it didnt work after that.

Long story long. How do I play the thing with my midi keyboard?

There are two pages. The Synth. The Giant DX7, And he has a page that plays through random presets. I just want to pick a preset and play it with my keyboard
below is one note played, how do I play it with midi, tho?
thank you

// Supercollider DX7 Clone v1.0
// Implemented by Aziz Ege Gonul for more info go to www.egegonul.com
// Under GNU GPL 3 as per SuperCollider license

//Example

( // init
	// Server.killAll
	s.boot;
    ~mainCaller = ("./DX7.scd").loadRelative.wrapAt(-1);
)

( // Note On 80 message with velocity value 100 and preset value 10000
    ~mainCaller.value(60, 100, 1447);
)

( // Note Off message
	~mainCaller.value(80, 0);
)

I would suggest to have a look at this tutorial which explains how to create a polyphonic midi synthesizer:

I can’t figure it out

This works for me. The code expects the midi device to use channel 0 (supercollider numbers from 0, on your keyboard it may be number 1 instead). You can switch to a different preset by calling ~changepreset.(number_of_the_preset); as shown at the end of the code:

You have to make sure the DX7.sc and DX7.afx files are present in the same folder as in which you save this code. (You must save it to file before running it, or it won’t work at all).

(
s.waitForBoot({
	var min;

	~presetz = Array.fill(2, 1);
	~mainCaller = ("./DX7.scd").loadRelative.wrapAt(-1);
	~changepreset = {
		| value |
		~presetz[0] = (value >> 2);
		~presetz[1] = (value & 127);
	};

	s.sync;

	if (MIDIClient.initialized.not) {
		MIDIClient.init;
	};

	min = MIDIIn.connectAll;

	MIDIdef.noteOn(\DX7, {arg vel, note;
		~mainCaller.value(note, vel, (~presetz[0] * 128) + ~presetz[1]);
	},chan:0).add;

	MIDIdef.noteOff(\DX7off, {arg vel, note;
		~mainCaller.value(note, 0);
	},chan:0).add;

});
)

~changepreset.(6); // change to preset 6

1 Like

Thank you, Im hoping I can play it with a Pbind Maybe \DX7 will be enough? I don’t know

Sure you can… Pbind can do everything, because it doesn’t do anything (by itself).

Pbind stuffs data into an Event. That’s all. Pbind imposes no assumptions at all about the data or the meaning you wish them to have.

It’s the event type that interprets the data and takes action. All those reserved names (\degree, \freq, \dur, \amp, etc.) belong completely to the default event prototype. Pbind has nothing to do with them.

Event.eventTypes[\note].postcs;

That’s the normal “play a note on the server” event function.

The catch is that DX7.scd doesn’t define an event type… so you have to add it on your own.

Starting and stopping notes is documented: ~mainCaller.value(note, vel, (~presetz[0] * 128) + ~presetz[1]); and velocity 0 for note-off.

So, just for basic note-on/off, without support for strum, or for sustain, lag or offset arrays:

Event.addEventType(\dx7, {|server|
	var freqs, lag, strum, sustain;
	var bndl, addAction, sendGate, ids;
	var msgFunc, instrumentName, offset, strumOffset;
	var presetVal;
	var latency = ~latency;
	var mainCaller = ~mainCaller;
	var vel;
	
	freqs = ~detunedFreq.value;
	
	// update values in the Event that may be determined by functions
	~freq = freqs;
	~amp = ~amp.value;
	vel = ~amp * 127.0;
	~sustain = sustain = ~sustain.value;
	lag = ~lag;
	offset = ~timingOffset;
	strum = ~strum;
	~server = server;
	~isPlaying = true;
	presetVal = (~presetz[0] * 128) + ~presetz[1];

	server.makeBundle(latency, {
		freqs.asArray.do { |freq|
			mainCaller.value(freq.cpsmidi, vel, presetVal);
		};
	});
	thisThread.clock.sched(sustain + offset, {
		server.makeBundle(latency, {
			freqs.asArray.do { |freq|
				mainCaller.value(freq.cpsmidi, 0);
			};
		});
	});
});

Then events should include:

	\mainCaller, ~mainCaller,
	\presetz, [0, 0],

I guess…? I don’t understand the presetz stuff in the readme at all, you’re on your own there.

hjh

1 Like

Thank you

I found an sc file I had that worked with the DX emulator this

( // init
// Server.killAll
s.boot;
~mainCaller = ("./DX7.scd").loadRelative.wrapAt(-1);
)

(
~patt = Pbind(
\degree, Rest(), // make pattern silent
\dur, Pseq([1,3,7,2], inf),
\note, Pseq([50,52,64], inf),
\vel, Pseq([80,50,10,127], inf),
\preset, Pseq([2333,3355,16383], inf),
\put, Pfunc({|values|
~mainCaller.value(
values.note,
values.vel,
values.preset)
})
).play(TempoClock(65/60*4));
)
~patt.stop;
~patt.pause;
~patt.resume;
~patt.free;

Its a bit fiddly, Its almost like you have to have anything related to this DX file in the same folder as the scd file . I tried direct linking the /DX7.scd file but it doesn’t work. It all has to be in the same folder.

But your stuff is way better, Thank you.

I never tried the DX7 but just want to point out that the FM7 ugen is pretty nice too, could be interesting to see how close it is to the DX7. Here is some old code I found using the FM7.

(
32.do{|k|
	SynthDef((\fm++k).asSymbol, {
		var freq = \freq.kr(440);
		var envs = Env.perc(\atk.kr(0.01), \rel.kr(1.0), \index.kr(1!6), \curve.kr(-4)).ar;
		var detunes = LFNoise2.kr(\rate.kr(10)).bipolar(\det.kr(0!6)).midiratio;
		var freqs = freq * \ratio.kr(1!6) * detunes * \transp.kr(0).midiratio;
		var ctlMatrix = [freqs, 0, envs].flop;
		var sig = FM7.arAlgo(k, ctlMatrix, \fb.kr(0.5));	
		sig = LeakDC.ar(Mix(sig));
		sig = LPF.ar(LPF.ar(sig, \lpf.kr(2000)));
		DetectSilence.ar(sig, doneAction: 2);
		sig = Pan2.ar(sig, \pan.kr(0), \amp.kr(0.5));
		Out.ar(\out.ir(0), sig * 0.1)
	}).add;
};
)

/// creates all 32 algos

// One particular instance:
Synth(\fm10, [\index, [10, 2, 5, 9, 6, 10], \ratio, [10, 1.4, 3, 5, 3, 7]])

// Choosing random algo and random parameters for index and ratio
Synth((\fm++rrand(0, 31)).asSymbol, [\index, {rrand(1, 10.0)}!6, \ratio, {rrand(1, 10.0)}!6])

// using a pattern to select random algo, indices and ratios 

(
Pdef(\fm, Pbind(
	\instrument, Prand(32.collect{|i| (\fm ++ i).asSymbol}, inf),
	\index, Pfunc{ {rrand(1, 10.0)}!6 },
	\ratio, Pfunc{ {rrand(1, 10.0)}!6 },
	\amp, 0.5,
)).play
)

Pdef(\fm).stop
2 Likes

Love it. Very fun. I bet you could make singing bowls with this Ugen


(
t = Task({
    loop {
        s.makeBundle(s.latency, {
            Synth((\fm++rrand(0, 31)).asSymbol, [\index, {rrand(1, 10.0)}!6, \ratio, {rrand(1, 10.0)}!6])
        });
        0.1.wait;
    }
}).play;
)


t.stop;
t.resume;

Divine madness:) and some characters…

I just want to play a Phat Wood Piano or E piano bass like Aphex

1 Like

You can also experiment with creating FM or PM synths yourself. FM and PM is essentially the same thing. Here is a PM synthdef - I think I got the basic ‘recipe’ from @jamshark70 and adopted it but I can’t quite recall.

(
SynthDef(\pm, {
	arg lt = 0.1, freq = 110, amp = 0.1, index = 1, ratio = 1, atk = 0.05, rel = 0.4, 
	ipeak = 5, iatk = 0.05, irel = 0.1, pan = 0, out = 0;
	var ienv = Env([1, ipeak, 1], [iatk, irel], [-3,-1]).ar;
	var env = Env.perc(atk, rel).ar(2);
	var phase = Phasor.ar(0, freq * SampleDur.ir, 0, 1) * 2pi;
	var mod = SinOsc.ar(freq * ratio) * index * ienv;
	var sig = SinOsc.ar(0, (phase + mod) % 4pi);
	sig = HPF.ar(sig, 60);
	sig = Pan2.ar(sig, pan, amp);
	sig = sig * env * 0.6;
	Out.ar(out, sig);
}).add;
)

(
Pdef(\pm, Pbind(
	\instrument, \pm,
	\dur, Prand([0.25, 0.5, 0.75] * 0.6, inf),
	\note, Prand( Scale.dorian.degrees, inf) - 24,
	\index, 1.4, 
	\ratio, 2,
	\iatk, 0.1,
	\irel, 0.1,
)).play
)

Pdef(\pm).stop
1 Like

Back when I experimented with the FM7 I found this chart over the arrangement of oscillators/modulators from the original DX7 synth which can give an idea of how to expand on the above concept. The DX7 have 6 oscillators which can be arranged in different formations.

1 Like

Some casual morning fun

(
SynthDef(\crazyFM1, {
    arg out=0, freq=440, amp=0.1, atk=0.01, rel=1.0, c1=1, c2=2, c3=3, m1=1, m2=1, m3=1;
    var env = EnvGen.kr(Env.perc(atk, rel), doneAction: 2);
    var mod3 = SinOsc.ar(freq * c3) * m3;
    var mod2 = SinOsc.ar(freq * c2 + (mod3 * 200)) * m2;
    var mod1 = SinOsc.ar(freq * c1 + (mod2 * 300)) * m1;
    var car = SinOsc.ar(freq + (mod1 * 400));
    Out.ar(out, Pan2.ar(car * env * amp, LFNoise1.kr(10.5)));
}).add;

SynthDef(\chaosticFM, {
    arg out=0, freq=440, amp=0.1, atk=0.01, rel=1.0, chaos=3.7;
    var env, x, mod, car;
    
    env = EnvGen.kr(Env.perc(atk, rel), doneAction: 2);
    
    x = LocalIn.ar(1);
    x = chaos * x * (1 - x);
    x = LeakDC.ar(x);
    LocalOut.ar(x);
    
    mod = LinLin.ar(x, 0, 1, 1, 2);
    car = SinOsc.ar(freq * mod);
    
    Out.ar(out, Pan2.ar(car * env * amp, LFNoise2.kr(6)));
}).add;

SynthDef(\granularFM, {
    arg out=0, freq=440, amp=0.1, grainDur=0.1, density=20;
    var trig = Dust.kr(density);
    var modFreq = LFNoise1.kr(0.5).exprange(50, 5000);
    var modIndex = LFNoise1.kr(0.7).range(0.1, 10);
    var pan = WhiteNoise.kr(1);
    var grain = SinOsc.ar(freq + (SinOsc.ar(modFreq) * modIndex * freq));
    var env = EnvGen.kr(Env.perc(0.001, grainDur), trig);
    var sig = GrainIn.ar(2, trig, grainDur, grain, pan, env);
    Out.ar(out, sig * amp);
}).add;

SynthDef(\nestedFM, {
    arg out=0, freq=440, amp=0.1, atk=0.01, rel=1.0;
    var env = EnvGen.kr(Env.perc(atk, rel), doneAction: 2);
    var modFreqs = Array.fill(5, { LFNoise2.kr(rrand(0.1, 1.0)).exprange(50, 5000) });
    var modIndices = Array.fill(5, { LFNoise1.kr(rrand(0.1, 1.0)).range(0.1, 10) });
    var sig = freq;
    5.do { |i|
        sig = SinOsc.ar(sig + (SinOsc.ar(modFreqs[i]) * modIndices[i] * sig));
    };
    Out.ar(out, Pan2.ar(sig * env * amp, SinOsc.kr(4.2)));
}).add;
)


(
Pdef(\crazyFMSeq,
    Pbind(
        \instrument, Prand([\crazyFM1, \chaosticFM, \granularFM, \nestedFM], inf),
        \dur, Pwhite(0.1, 0.4),
        \freq, Pexprand(100, 1000),
        \amp, 0.1,
        \atk, Pwhite(0.001, 0.1),
        \rel, Pwhite(0.1, 1.0),
        \c1, Pwhite(0.5, 4),
        \c2, Pwhite(0.5, 4),
        \c3, Pwhite(0.5, 4),
        \m1, Pwhite(0.1, 10),
        \m2, Pwhite(0.1, 10),
        \m3, Pwhite(0.1, 10),
        \chaos, Pwhite(3.5, 3.99), // logictic x(n+1) = r * x(n) * (1 - x(n)), unstable between/above 3.57~4
        \grainDur, Pwhite(0.01, 0.2),
        \density, Pwhite(10, 80)
    )
).play;
)

Pdef(\crazyFMSeq).stop;

Interestingly, many of the diagrams have some feedback somewhere.

It would also be cool to insert some non-standard signals beyond sinosc.

Yes, I am not quite sure how this is or could be implemented in the context of FM/PM

What is that graph?

It’s also the Envelope of the modulators that matter. Those FM graphs for the DX7 don’t include that right? Were they just a marketing thing?

1 Like

this

    15.do { |i|
        sig = SinOsc.ar(sig + (SinOsc.ar(modFreqs[i]) * modIndices[i] * sig));
    };

@sslew , have you checked this out?

1 Like

Wow, I’ll have to dig into this, thank you

Here’s a coloured version of the DX7 algorithms which makes it easier to see their structure.


The bottom row of each algorithm shows which oscillators (or “operators” in DX7-speak) are “carriers” which means you can hear their output. They are coloured yellow.
The rows above the carriers show the “modulator” oscillators which are grey. They modulate the oscillator(s) in the row below them, shown as black connections, but you cannot hear them directly.
Feedback, shown in red, mostly means an oscillator is modulating its own phase - except in algorithms 4 and 6, which have feedback loops with multiple oscillators.
The different structures allow you to get different sonic results from the different combinations - for example algorithms 16, 17, and 18 are essentially 1 carrier being modulated by 5 modulators in different ways. Whereas algorithm 32 is 5 sine waves, with a 6th sine wave modulating itself, all mixed together.
As mentioned, the envelopes are not shown in the algorithms and they are an essential part of the sound - both for carrier levels and modulation control.
It’s great what’s possible with FM/PM synthesis, even if it’s a bit less intuitive to use than some other synthesis techniques. (Of course, randomising parameters can be fun here and sometimes creates novel sounds).
Best,
Paul

1 Like