Tuesday, March 18, 2008

The THX "Deep Note" Challenge

(from May 24th, 2007)

Every once in a while I get some crazy idea in my head and I can't seem to stop thinking about it until I give it a try. This time, the idea came from stumbling into an article about the famous THX sound. In this article, Dr. James "Andy" Moorer, the creator of the "most widely-recognized piece of computer-generated music in the world," describes his process in detail. Here's an excerpt of the article, with a few key sections that caught my eye:

I set up some synthesis programs for the ASP that made it behave like a huge digital music synthesizer. I used the waveform from a digitized cello tone as the basis waveform for the oscillators. I recall that it had 12 harmonics. I could get about 30 oscillators running in real-time on the device. Then I wrote the "score" for the piece.

The score consists of a C program of about 20,000 lines of code. The output of this program is not the sound itself, but is the sequence of parameters that drives the oscillators on the ASP. That 20,000 lines of code produce about 250,000 lines of statements of the form "set frequency of oscillator X to Y Hertz".

The oscillators were not simple - they had 1-pole smoothers on both amplitude and frequency. At the beginning, they form a cluster from 200 to 400 Hz. I randomly assigned and poked the frequencies so they drifted up and down in that range. At a certain time (where the producer assured me that the THX logo would start to come into view), I jammed the frequencies of the final chord into the smoothers and set the smoothing time for the time that I was told it would take for the logo to completely materialize on the screen. At the time the logo was supposed to be in full view, I set the smoothing times down to very low values so the frequencies would converge to the frequencies of the big chord (which had been typed in by hand - based on a 150-Hz root), but not converge so precisely that I would lose all the beats between oscillators. All followed by the fade-out. It took about 4 days to program and debug the thing. The sound was produced entirely in real-time on the ASP. (...)

There are many, many random numbers involved in the score for the piece. Every time I ran the C-program, it produced a new "performance" of the piece. The one we chose had that conspicuous descending tone that everybody liked. It just happened to end up real loud in that version.

With those bits of information in mind, and some logicla thinking, I figured I could make my own version of the THX Deep Note. Not just recreating the sound, but emulating the thought process behind a task such as the one Dr. Moorer had in his hands. Of course, it would be impossible to recreate the sound exactly like the original, so I took a few creative liberties while trying to be as scientific as possible about the whole thing.

Csound seemed like the best environment for this project. Besides, Dr. Moorer talks about a "score", and a "C program"...

Before I could get into coding anything, I had to find a "cello tone". So I sampled a cello waveform from Edirol Orchestral, brought it into Sound Forge, normalized it, trimmed it and this is what I got: cello.wav

Looking at the spectral view of this waveform, I could approximate the amplitudes of the first 22 partials fairly accurately. The original sound used only the first 12, but I figured it wouldn't hurt to go farther. Here's a view of the first few partials:

I jotted down those amplitudes in Excel, and adapted the decibel formula to get the relative amplitudes in percentile form. Like this:

PartialAmplitude10^(amp/25)
1-250.100*
2-120.331
3-320.052
4-270.083
5-520.008
6-340.044
7-380.030
8-520.008
9-370.033
10-390.028
11-410.023
12-510.009
13-630.003
14-480.012
15-510.009
16-600.004
17-630.003
18-600.004
19-630.003
20-530.008
21-570.005
22-550.006

Since the fundamental seemed too weak compared to the octave, I changed its amplitude from 0.1 to 0.5. In the end, it gave the sound a lot more body.

Before I could program anything, I had to figure out what notes were being played in the original Deep Note so I could program them into Csound. I did it by ear, and it sounded to me like a big C# "power chord", in 3 octaves: C#, G#, C#, G#, C#, G#. Here's a little discrepancy though... Dr. Moorer says the chord had a 150Hz root. The recording I have shows more of a 70Hz root, where the octave would be at 140Hz, not 150. So I went with 70Hz and added a good amount of 35Hz.

Then it was time to get down and dirty with Csound. I created the score first. The frequencies I ended up with were 35, 70, 105, 140, 210, 280, 420. I tried running it with just a simple oscillator and it didn't sound right. So I kept going... 560, 840, 1120, 1680, 2240. Ten frequencies, for the 30 oscillators described in the article. So I just tripled each oscillator, but that wasn't it. It seemed some oscillators had to have more weight than others, while some needed two or three for the detuning to sound right. But I'm jumping ahead of myself...

With a rough idea for the score, I went on to create the orchestra file. I needed oscillators that would go from somewhere between 200 and 400Hz to whatever their final frequency would be. A cluster of 30 randomly assigned oscillators sounded ugly in that range. Besides, sometimes the lowest notes would start too high, unlike the original Deep Note. So I put a "weight" to the random factor by adding 1/10th of the final frequency to 200. That way, the lowest oscillators would start anywhere between 200 and 203.5, while the highest would start between 200 and 424. That "conspicuous descending tone" would always be there as well.

An LFO could provide enough pitch drift so that the final chord would be thick enough without having regular beats, so each oscillator got its own sawtooth LFO whose frequency is anywhere between 0 and 2Hz, and whose amplitude is 1/100th of the final frequency. Again, the higher the final frequency, the more detuning the oscillator gets.

The final frequencies would also need an amount of randomness, so that the chord would have that synth-y, "unison spread" quality to it. At first I had the frequency envelope ending at exactly the 7 second mark, but that sounded too mechanical. Keeping Dr. Moorer's words in mind -- "many, many random numbers" -- I made it so the "landing" time would be anywhere between 6.5 and 7 seconds. Perfect.

Csound's linear envelopes weren't right for the amplitude, and the exponential ones were too steep. So I multiplied a linear envelope by an "assistant" envelope to generate a more musical fade-in and out.

Here's what the score file looks like:

sr = 44100
kr = 44100
ksmps = 1
nchnls = 1
instr 1
seed 0
iinit = 200+rnd(p4/10)
ifinal = p4+birnd(p4/200)
kfenv expseg iinit, 6.75+rnd(.25)-p2, ifinal, 9, ifinal
kaenv linseg 0, 6-p2, p5, 6, p5, 4, 0
ka1 linseg 0.5, 12, 1, 4, 0
klfo lfo p4/100, rnd(2), 4
a1 oscil ka1*kaenv*1.7, kfenv+klfo, 1
out a1


That's it! 14 lines of code versus the original 20,000... I'm sure there's a lot more to the original program, but this one still does a pretty good job. Here's the orchestra file:



f1 0 1024 10 .5 .331 .052 .083 .008 .044 .03 .028 .023 .009 .004 .003 .008 .005 .006
; sta dur freq amp
i1 0.0 16.0 35 2000
i1 0.1 15.9 35 2000
i1 0.2 15.8 35 2000
i1 0.3 15.7 70 2000
i1 0.4 15.6 70 2000
i1 0.5 15.5 70 2000
i1 0.6 15.4 70 2000
i1 0.7 15.3 105 1500
i1 0.8 15.2 105 1500
i1 0.9 15.1 140 1500
i1 1.0 15.0 140 1500
i1 1.1 14.9 140 1500
i1 1.2 14.8 140 1200
i1 1.3 14.7 210 1200
i1 1.4 14.6 210 1200
i1 1.5 14.5 280 1200
i1 1.6 14.4 280 1200
i1 1.7 14.3 280 1200
i1 1.8 14.2 420 1500
i1 1.9 14.1 420 1500
i1 2.0 14.0 420 1500
i1 2.1 13.9 560 1500
i1 2.2 13.8 560 1500
i1 2.3 13.7 560 1500
i1 2.4 13.6 840 1200
i1 2.5 13.5 840 1200
i1 2.6 13.4 1120 1200
i1 2.7 13.3 1120 1000
i1 2.8 13.2 1680 800
i1 2.9 13.1 2240 400
e


If you listen to the result without first hearing the original Deep Note, you'd almost think it's the real deal. Click Here to Download Sample -- of course, the original sounds much better, but it's mainly because I don't have the exact same cello sample as the original (and because I don't want to spend days staring at spectral pictures, waveforms and spreadsheets). Besides, this is the raw sample. I'm sure that with a little EQ, compression and reverb, you could get even closer to the original sound. And if you listen closely, the oscillators on the original don't come in in the order I programmed them. At any rate, the spirit of the sound is there.

Whew! Now I can finally get this out of my mind! Who knows what's next...

Psytrance in Csound - 5. I Give Up

Before you think I'm a flake, let me just say that... well, at least I tried. But I think I proved my point: it IS possible to produce a track entirely in Csound -- if one is willing to couple Csound's synthesis capabilities with good samples, well-programmed effects and a LOT of patience.

I'm confident that I'll still use Csound to design kicks, hats and the like, but arranging something in score form really is counter-intuitive and uninspiring. There are options though. blue integrates Csound with a user-friendly interface and powerful arrangement tools. Maybe someday I'll dig deeper into its capabilities.

IMHO, what would really take Csound to the next level would be the ability to compile instruments into VST plugins. CsoundVST came close, but no cigar. But I digress.

So I'm retiring this project, but I just found something I came up with that I almost forgot about... coming up next!

Sunday, January 13, 2008

Psytrance in Csound. 4 - Score Macros

Something is bugging me a little bit. So far, I've been auditioning only one bar of music and it's hard to get a good view of the soundscape in one bar. Four bars would be more in context with this project. Csound has a simple provision for macros. Here's what I did:

;=== SCORE ===

f 1 0 8192 10 1

t 0 150

#define KICK #
; p1 p2 p3 p4 p5 p6
; start dur freq level reverb
i 1 0 .5 44 .8 .01
i 1 1 .5 44 .8 .01
i 1 2 .5 44 .8 .01
i 1 3 .5 44 .8 .01
#

#define BASS #
; p1 p2 p3 p4 p5 p6
; start dur pitch level reverb
i 2 0.50 6 5.09 .8 .04
i 2 1.50 6 6.09 .8 .04
i 2 2.50 6 5.09 .8 .04
i 2 2.75 6 6.07 .8 .04
i 2 3.50 6 6.09 .8 .04
#

$KICK.
$BASS.
b 4
$KICK.
$BASS.
b 8
$KICK.
$BASS.
b 12
$KICK.
$BASS.

e

Listen (psy4-1.mp3) or Download Code & WAV (psy4-1.zip)

The b in the score creates a beat offset in the macro code that follows. So instead of 0, 1, 2 and 3, after "b 4" it turns the start times into 0+4, 1+4, 2+4 and 3+4.

Now it's starting to sound like a song!

Psytrance in Csound. 3 - Bass

So I'm happy enough with the kick, let's start building the groove. Next, I'll add a simple bassline to build the foundation of the track. I'm thinking something simple and typical: a filtered sawtooth with a touch of reverb. For the sake of saving space, I'll leave out the portion of the code that has already been used for the kick.

;=== ORCHESTRA ===


instr 2

p5 = 32768*p5
klvl expseg 1, .050, .5, .450, 0.00003
aosc vco p5*klvl, cpspch(p4), 1, .5, 1
kcut linseg 1, .030, .1
afilt moogladder aosc, 5000*kcut, .4
al,ar freeverb afilt, afilt, 0.8, 0.35, sr, 0
al = afilt+al*p6
ar = afilt+ar*p6
outs al, ar

endin


;=== SCORE ===


; instr start dur pitch level reverb
i 2 0.50 6 5.09 .8 .04
i 2 1.50 6 6.09 .8 .04
i 2 2.50 6 5.09 .8 .04
i 2 2.75 6 6.07 .8 .04
i 2 3.50 6 6.09 .8 .04

Listen (psy3-1.mp3) or Download Code & WAV (psy3-1.zip)

About the code... I used an exponential envelope for the amplitude, and the 0.00003 comes from the fact you can't use 0 in exponential envelopes in Csound. If you divide 1 by 32768 (to get the smallest 16-bit value) you get 0.0000305... in other words, 0.00003 is a "practical zero". Another thing I like about Csound is the ability to control filter frequencies with ultimate precision. In this case, the envelope takes the cutoff from 5000 to 500 in 30ms. In the score, note the long 6-beat duration of each note, to allow the reverb to come through.

It all sounds nice enough, but why not fatten it up a little? Let's add two more oscillators, detuned slightly.

;=== ORCHESTRA ===


instr 2

p5 = 32768*p5
klvl expseg 1, .050, .5, .450, 0.00003
aosc1 vco p5*klvl, cpspch(p4), 1, .5, 1
aosc2 vco p5*klvl, cpspch(p4)*1.01, 1, .5, 1
aosc3 vco p5*klvl, cpspch(p4)*0.99, 1, .5, 1
aoscall = aosc1+aosc2+aosc3
kcut linseg 1, .030, .1
afilt moogladder aoscall, 5000*kcut, .4
al,ar freeverb afilt, afilt, 0.8, 0.35, sr, 0
al = afilt+al*p6
ar = afilt+ar*p6
outs al, ar

endin

Listen (psy3-2.mp3)
or Download Code & WAV (psy3-2.zip)

I could improve it even further by adding random detuning to the oscillators, or by adding more oscillators, plus effects like chorus or delay, adding pitch tracking to the filter, etc. Maybe later, but we'll stick with that for now.

Psytrance in Csound. 2 - Kick

The common Psytrance kick is simple: a sweeping sine wave that starts high and quickly decays to a low pitch. When made in a sound editor like Sound Forge, it starts as a sine wave at a low frequency around 50-60 Hz, maybe even lower. Pitch shifting is applied to the first few milliseconds of the waveform until the kick feels right. The only problem with this process is that these shifts are performed almost randomly until the very last one. If the kick doesn't feel right, you have to undo and redo these shifts until you find the right combination of decay times and shifting amounts. Trying to duplicate the result is nearly impossible because of this haphazard method.


In Csound we can control these sweeps one by one, and tweak them until we find the right kick. In fact, the very makeup of the kick could be automated -- I'm not going there, but it's definitely possible. So I started with a simple architecture: three envelopes controlling the pitch of a sine wave. One for the main downward sweep (a small one of an octave or two spanning the full length of the kick), another for the initial decay of the wave, and the last one (a quick "thunk") for impact. This is what I came up with:

;=== ORCHESTRA ===

sr = 44100
kr = 44100
ksmps = 1
nchnls = 2

instr 1

p5 = 32768*p5
k1 linseg 1.5, .200, .5
k2 linseg 2, .060, 1
k3 linseg 4, .010, 1
akick oscil p5, p4*k1*k2*k3, 1
al = akick
ar = akick
outs al, ar

endin


;=== SCORE ===

f 1 0 8192 10 1
t 0 150

; instr start dur freq level
i 1 0 .5 44 .8
i 1 1 .5 44 .8
i 1 2 .5 44 .8
i 1 3 .5 44 .8

e

Listen (psy2-1.mp3) or Download Code & WAV (psy2-1.zip)

To briefly explain the code... I like notating levels and other parameters from 0 to 1, hence the p5 conversion in the first line. k1 sweeps the pitch from 1.5 octaves above the base pitch down to an octave below. It's a more dramatic sweep than you'd normally find in your common kick, but I like it like that. k2 adds a 60ms 2-octave sweep for the decay and k3 adds a 10ms 4-octave sweep for the attack. In the score file, I have one bar with 4 kicks for testing, each one an eighth-note long (half a beat). I picked 44Hz for the base frequency because it's "related" to A (440Hz, although a low A is 55Hz), so we'll keep this song in A. Some producers like to offset the kick perfectly to the 3rd or 5th of the key of the song, or pitch the kick by ear, but I'll keep it like this for now.

It's getting there, but it's missing a few things. The attack needs a little more bite, so I'll add another envelope for that. There's no dynamic control, that's why there's a click at the end of each kick, so I'll add an amplitude envelope too. Finally, a touch of reverb, gated to the duration of the kick, to add a bit of air. To make the amount of reverb more dynamic in the future, we'll add p6 to the score to control the wet/dry mix.

;=== ORCHESTRA ===

sr = 44100
kr = 44100
ksmps = 1
nchnls = 2


instr 1

p5 = 32768*p5
k1 linseg 1.5, .200, .5
k2 linseg 2, .060, 1
k3 linseg 4, .010, 1
k4 linseg 4, .002, 1
klvl linseg 0, .0002, 1, .030, .8, .080, .7, p3-.1102, 0
akick oscil p5*klvl, p4*k1*k2*k3*k4, 1
al,ar freeverb akick, akick, 0.8, 0.35, sr, 0
al = (akick*(1-p6))+(al*p6)
ar = (akick*(1-p6))+(ar*p6)
outs al, ar

endin


;=== SCORE ===

f 1 0 8192 10 1
t 0 150

; instr start dur freq level reverb
i 1 0 .5 44 .8 .01
i 1 1 .5 44 .8 .01
i 1 2 .5 44 .8 .01
i 1 3 .5 44 .8 .01

e

Listen (psy2-2.mp3) or Download Code & WAV (psy2-2.zip)

I like it. Next I'll add a simple bassline to go with it and create macros in the score to save on typing.

Psytrance in Csound. 1 - Intro

I recently read something about one of BT's tracks, All That Makes Us Human Continues, being completely written in Csound. It's amazing that all the nuances in the song were, at one point, nothing but hundreds of lines of code. That re-sparked my interest in Csound, so I decided to put a little more time into it.

I'm a hands-on kind of guy and decided that, if I'm going to put time into something as complex as Csound, I might as well make music with it. I've been listening to a lot of Psytrance, mainly Infected Mushroom's IM The Supervisor and other Israeli stuff like Hypersonic and Injection.

The Psytrance kick is the foundation of the genre, and the Infected Mushroom website has a nice little tutorial on how to create your own kicks in editors like Sound Forge. I made over a dozen kicks using their method, which seemed a little haphazard to me. If I could have more control over the pitch shifting described in the tutorial, I could design the perfect kick for each song. That's where I made the connection: Csound and Psytrance are made for each other!

In this article (and its following chapters) I'll narrate the steps of this experiment and post the Csound code. Keep in mind I'm new to Csound AND Psytrance, so don't expect breathtaking results. I just hope to have a finished song at the end of this madness.

So, let's start with the kick... in the next article.