Programming Digital Media

Sound synthesis with Csound

Sound synthesis with Csound The Csound sound synthesis system uses a text file as input to generate a digital sound file. In the first part of this tutorial, we take a look at Csound input files and edit them by hand. In the second part, we'll use a Python library that generates Csound input files, which we can in turn process with Csound to produce a sound file.

Resources

The Csound community has produced an enormous amount of reference and tutorial material. The Csound Book includes two CDs containing instrument definitions and compositions. The book text is also contained in the CDs that accompany the book; the first chapter in HTML format is available on the Web. This documentation may use the older Csound convention of a separate file for the orchestra (the .orc file) and the score (the .sco file). These defintions can be inserted without modification in the tagged sections of the CSD file. The Canonical Csound Manual is also available on-line.

Installation

Download these three files to your pdm/sound directory:

csound The Csound command
Csound.py Csound Python library
pitch_table.py A dictionary of named pitches

Once you have downloaded the Csound command, you will need to make it executable with the chmod command. Enter this in the directory in which you downloaded csound:

    chmod a+x csound 

To play a sound file, you can open it in QuickTime on the command line by explicitly specifying QuickTime with the "-a" (for "application") flag:

    open -a "QuickTime Player" csound_1.aif 


Csound input files in CSD format

Download these files to your sound directory by clicking on the filename. To generate a sound file, run csound with the CSD file as the argument:
    csound csound_1.csd 

The output sound file that will be produced follows the "-o" (for "output") flag in the CsOptions block.

Try changing the various parameters and rerunning the CSD file. The instrument names in the title line are links to the documentation.


csound_1.csd       oscil      

<CsoundSynthesizer> 
 
<CsOptions> 
-A -o csound_1.aif 
</CsOptions> 
 
<CsInstruments> 
	instr	1 
a1	oscil	p4, p5, 1 
	out	a1 
	endin 
</CsInstruments> 
 
<CsScore> 
; Table 
f	1	0	8192	10	1 
; Note	Instr	Start	Dur	Amp	Freq 
i	1	0	1	10000	440 
</CsScore> 
 
</CsoundSynthesizer> 


csound_2.csd       oscil      

<CsoundSynthesizer> 
 
<CsOptions> 
-A -o csound_2.aif 
</CsOptions> 
 
<CsInstruments> 
	instr	1 
a1	oscil	p4, p5, 1 
	out	a1 
	endin 
</CsInstruments> 
 
<CsScore> 
; Table 
f	1	0	8192	10	1 
; Note	Instr	Start	Dur	Amp	Freq 
i	1	0	1	10000	440 
i	1	1	1	10000	450 
i	1	2	1	10000	460 
i	1	3	1	10000	470 
i	1	4	1	10000	480 
</CsScore> 
 
</CsoundSynthesizer> 


csound_3.csd       oscil buzz      

<CsoundSynthesizer> 
 
<CsOptions> 
-A -o csound_3.aif 
</CsOptions> 
 
<CsInstruments> 
	instr	1 
a1	oscil	p4, p5, 1 
	out	a1 
	endin 
 
	instr	2 
a1	buzz	p4, p5, p6, 1 
	out	a1 
	endin 
</CsInstruments> 
 
<CsScore> 
; Table	 
f	1	0	8192	10	1 
; Note	Instr	Start	Dur	Amp	Freq 
i	1	0	1	10000	440 
i	2	1	1	10000	440	20 
</CsScore> 
 
</CsoundSynthesizer> 
'

Csound CSD files produced by Python

Download these Python scripts by clicking on the Python filename. You can also download the CSD file the Python script produces by clicking on the CSD filename.

To produce the sound file, first create the .csd file with Python, and then run csound as before. For example:

    python example_1.py 
    csound example_1.csd 
    open -a "QuickTime Player" example_1.aif 

example_1.py   creates    example_1.csd       oscil      

from Csound import * 
 
# Make an "oscil" instrument: 
oscillator = oscil() 
 
# Make a note.  
start_time = 0  # In seconds from the beginning of the score 
duration = 1    # In seconds 
frequency = 440 # Hz (cycles per second) 
amplitude = .5  # The possible values are 0.0-1.0; 11 is clamped to 1.0 
note = oscillator.note(start_time, duration, frequency, amplitude) 
 
# Create the CSD file generator: 
csd = CSD('example_1.csd') 
 
# Add the instrument to the orchestra: 
csd.orchestra(oscillator) 
 
# Add the note to the score: 
csd.score(note) 
 
# Output the CSD file: 
csd.output() 

example_2.py   creates    example_2.csd       oscil      

from Csound import * 
 
csd = CSD('example_2.csd') 
 
oscillator = oscil() 
csd.orchestra(oscillator) 
 
# You can add a note to the score without using a variable: 
csd.score(oscillator.note(0, 1, 440, .5)) 
 
# You can add several notes at the same time: 
csd.score(oscillator.note(1, 1, 480, .5), 
          oscillator.note(2, 1, 520, .5), 
          oscillator.note(3, 1, 560, .5)) 
 
# Output the CSD file: 
csd.output() 

example_3.py   creates    example_3.csd       oscil      

from Csound import * 
 
csd = CSD('example_3.csd') 
 
oscillator = oscil() 
csd.orchestra(oscillator) 
 
# You can use Python language structures (like loops) to make notes: 
 
time = 0 
note_duration = .1 
for frequency in range(200, 800, 20): 
    csd.score(oscillator.note(time, note_duration, frequency, .5)) 
    time = time + note_duration 
 
csd.output() 

example_4.py   creates    example_4.csd       oscil      

from Csound import * 
from whrandom import uniform 
 
csd = CSD('example_4.csd') 
 
oscillator = oscil() 
csd.orchestra(oscillator) 
 
# You can figure out where 1950s science fiction soundtracks came from: 
time = 0 
note_duration = .1 
note_count = 50 
for i in range(note_count): 
    frequency = uniform(200, 800) 
    csd.score(oscillator.note(time, note_duration, frequency, .5)) 
    time = time + note_duration 
 
csd.output() 

example_5.py   creates    example_5.csd       oscil buzz fmvoice      

from Csound import * 
 
csd = CSD('example_5.csd') 
 
# Multiple instruments can be used in a score. 
# (See the file Csounds.py for other instrument types.) 
oscillator = oscil() 
buzzer = buzz() 
voice = fmvoice() 
 
# You can add the instruments to the orchestra one at a time or 
# together: 
csd.orchestra(oscillator, buzzer, voice) 
 
# You use the note method for the individual instruments: 
csd.score(oscillator.note(0, 1, 480, .5), 
          buzzer.note(1, 1, 520, .5), 
          voice.note(2, 1, 560, .5)) 
 
csd.output() 

example_6.py   creates    example_6.csd       oscil buzz fmvoice      

from Csound import * 
 
csd = CSD('example_6.csd') 
 
oscillator = oscil() 
buzzer = buzz() 
voice = fmvoice() 
csd.orchestra(oscillator, buzzer, voice) 
 
# You play notes at the same time, but you need to make sure that 
# their total amplitude (volume) doesn't exceed 1.0 or the samples 
# will be clipped: 
csd.score(oscillator.note(0, 3, 380, .2), 
          buzzer.note(0, 3, 520, .2), 
          voice.note(0, 3, 660, .2)) 
 
csd.output() 

example_7.py   creates    example_7.csd       oscil buzz fmvoice      

from Csound import * 
 
csd = CSD('example_7.csd') 
 
oscillator = oscil() 
buzzer = buzz() 
voice = fmvoice() 
csd.orchestra(oscillator, buzzer, voice) 
 
# The "note" method of instruments will convert a note name into a 
# frequency if you use that instead: 
csd.score(oscillator.note(0, 3, 'C2', .1), 
          oscillator.note(0, 3, 'C4', .1), 
          buzzer.note(0, 3, 'E4', .1), 
          voice.note(0, 3, 'G4', .1), 
          buzzer.note(0, 3, 'C5', .1)) 
 
csd.output() 

example_8.py   creates    example_8.csd       oscil buzz fmvoice      

from Csound import * 
 
csd = CSD('example_8.csd') 
 
oscillator = oscil() 
buzzer = buzz() 
voice = fmvoice() 
csd.orchestra(oscillator, buzzer, voice) 
 
# You can also "de-tune" the note by adding a "+" character after the 
# note name and then the offset you want to add to the named 
# frequency: "A4+10".  For negative numbers, use a hyphen after the 
# plus sign: "A4+-10". 
csd.score(oscillator.note(0, 3, 'C2+-10', .1), 
          oscillator.note(0, 3, 'C4+-5', .1), 
          buzzer.note(0, 3, 'E4+-20', .1), 
          voice.note(0, 3, 'G4+5', .1), 
          buzzer.note(0, 3, 'C5+10', .1)) 
 
csd.output() 

example_9.py   creates    example_9.csd       oscil buzz fmvoice      

from Csound import * 
 
# A Python function can use the CSD and instruments instances as 
# arguments. The way that the notes are produced — varying the 
# duration and amplitude — can also be part of the function. 
 
def motif(csd, instrument, start, note_length, amplitude=.5): 
    time = start 
    for note in 'C4 D4 D#4 F4 G4 D#4 C4 D4 F4 G#4 G4 D4 C4'.split(): 
        csd.score(instrument.note(time, note_length, note, amplitude)) 
        time += note_length 
    return time 
 
# Create the CSD and instruments as usual: 
csd = CSD('example_9.csd') 
 
oscillator = oscil() 
buzzer = buzz() 
voice = fmvoice() 
csd.orchestra(oscillator, buzzer, voice) 
 
# Now add notes to the score using the function: 
motif(csd, buzzer, 0, .2, .25) 
 
csd.output() 

example_10.py   creates    example_10.csd       oscil buzz fmvoice      

from Csound import * 
 
# Notice that at the end of the note loop the "time" variable is the 
# total duration of the the note sequence.  By returning this value as 
# the result of the function, we can use this to string together a 
# series of fuction calls. 
 
def motif(csd, instrument, start, note_length, amplitude=.5): 
    time = start 
    for note in 'C4 D4 D#4 F4 G4 D#4 C4 D4 F4 G#4 G4 D4 C4'.split(): 
        csd.score(instrument.note(time, note_length, note, amplitude)) 
        time += note_length 
    return time 
 
csd = CSD('example_10.csd') 
 
oscillator = oscil() 
buzzer = buzz() 
voice = fmvoice() 
csd.orchestra(oscillator, buzzer, voice) 
 
# Slow tempo: 
motif(csd, oscillator, 0, 1, .2) 
 
# Medium tempo: 
time = 0 
for i in range(2): 
    time = motif(csd, voice, time, .5, .2) 
 
# Fast tempo: 
time = 0 
for i in range(4): 
    time = motif(csd, buzzer, time, .25, .2) 
 
csd.output() 


Realistic instrument modeling

These simple examples hardly suggest the capabilities of Csound for realistic modeling of traditional musical instruments. Chapter 8 of The Csound Book is an article by Andrew Horner and Lydia Ayers entitled "Contiguous-Group Wavetable Synthesis of the French Horn in Csound."

horn.csd      

<CsoundSynthesizer> 
 
<CsOptions> 
-A -o horn.aif 
</CsOptions> 
 
<CsInstruments> 
sr      =      	44100 
kr    	=     	4410 
ksmps  	=     	10 
nchnls	=	1 
 
giseed	=	.5	 
garev	init	0	 
			 
	instr 	801		 
iamp	=	p4			; OVERALL AMPL. SCALING FACTOR 
ifreq	=	p5			; PITCH IN HERTZ 
ivibd	=	p6*ifreq/100.0		; VIB DEPTH RELATIVE TO FUND. 
iatt	=	p7			; ATTACK TIME 
idec	=	p8			; DECAY TIME 
isus	=	p3-iatt-idec-.005	; SUSTAIN TIME 
ifcut	tablei	p9, 2			; LP FILTER CUTOFF FREQUENCY 			 
kvibd	linseg	.1, .8*p3, 1, .2*p3, .7	; VIBRATO 
kvibd	=	kvibd*ivibd		; VIBRATO DEPTH 
ivibr1	=	2.5+giseed 	 
giseed	=	frac(giseed*105.947)	 
ivibr2	=	4.5+giseed	 
giseed	=	frac(giseed*105.947)	 
kvrate	linseg	ivibr1, p3, ivibr2	; TIME-VARYING VIBRATO RATE 
kvib	oscil	kvibd, kvrate, 1	 
kfreq	=	ifreq+kvib	 
amp1	linseg	0, .001, 0, .5*iatt, .5, .5*iatt, .9, .5*isus, 1, .5*isus, .9, .5*idec, .3, .5*idec, 0, 1, 0 
amp2	=	amp1*amp1 		; WAVETABLE ENVELOPES 
amp3	=	amp2*amp1	 
amp4	=	amp3*amp1	 
irange	tablei	ifreq, 4	 
iwt1	=	1			; WAVETABLE NUMBERS 
iwt2	table	(irange*4), 3	 
iwt3	table	(irange*4)+1, 3	 
iwt4	table	(irange*4)+2, 3	 
inorm	table	(irange*4)+3, 3		; NORMALIZATION FACTOR 
iphase	=	giseed			; SAME PHASE FOR ALL TABLES 
giseed	=	frac(giseed*105.947)	 
awt1	oscil	amp1, kfreq, iwt1, iphase ; WAVETABLE LOOKUP 
awt2	oscil	amp2, kfreq, iwt2, iphase	 
awt3	oscil	amp3, kfreq, iwt3, iphase	 
awt4	oscil	amp4, kfreq, iwt4, iphase	 
asig	=	(awt1+awt2+awt3+awt4)*iamp/inorm 
afilt	tone	asig, ifcut		; LP FILTER... 
asig	balance	afilt, asig		; ... TO CONTROL BRIGHTNESS 
garev	=	garev+asig	 
		out	asig	 
		endin		 
			 
		instr 899		 
arev	reverb	garev, 1.2		; OUTPUT REVERB SIGNAL 
		out	.1*arev		; SET garev TO 0 ... 
garev	=	0			; ... TO PREVENT FEEDBACK 
		endin		 
</CsInstruments> 
 
<CsScore> 
f 1 0   4097    -9  1 1.0 0 
f 2 0   16  -2  40 40 80 160 320 640 1280 2560 5120 10240 10240 
f 3 0   64  -2  11 12 13 52.476 14 15 16 18.006 17 18 19 11.274 20 21 22 6.955 23 24 25 2.260 26 27 10 1.171 28 29 10 1.106 30 10 10 1.019 
f 4 0   2048    -17 0 0 85 1 114 2 153 3 204 4 272 5 364 6 486 7 
f 10    0   5   -9  1 0.0 0 
f 11    0   4097    -9  2 6.236 0 3 12.827 0 
f 12    0   4097    -9  4 21.591 0 5 11.401 0 6 3.570 0 7 2.833 0 
f 13    0   4097    -9  8 3.070 0 9 1.053 0 10 0.773 0 11 1.349 0 12 0.819 0 13 0.369 0 14 0.362 0 15 0.165 0 16 0.124 0 18 0.026 0 19 0.042 0 
f 14    0   4097    -9  2 3.236 0 3 6.827 0 
f 15    0   4097    -9  4 5.591 0 5 2.401 0 6 1.870 0 7 0.733 0 
f 16    0   4097    -9  8 0.970 0 9 0.553 0 10 0.373 0 11 0.549 0 12 0.319 0 13 0.119 0 14 0.092 0 15 0.045 0 16 0.034 0 
f 17    0   4097    -9  2 5.019 0 3 4.281 0 
f 18    0   4097    -9  4 2.091 0 5 1.001 0 6 0.670 0 7 0.233 0 
f 19    0   4097    -9  8 0.200 0 9 0.103 0 10 0.073 0 11 0.089 0 12 0.059 0 13 0.029 0 
f 20    0   4097    -9  2 4.712 0 3 1.847 0 
f 21    0   4097    -9  4 0.591 0 5 0.401 0 6 0.270 0 7 0.113 0 
f 22    0   4097    -9  8 0.060 0 9 0.053 0 10 0.023 0 
f 23    0   4097    -9  2 1.512 0 3 0.247 0 
f 24    0   4097    -9  4 0.121 0 5 0.101 0 6 0.030 0 7 0.053 0 
f 25    0   4097    -9  8 0.030 0 
f 26    0   4097    -9  2 0.412 0 3 0.087 0 
f 27    0   4097    -9  4 0.071 0 5 0.021 0 
f 28    0   4097    -9  2 0.309 0 3 0.067 0 
f 29    0   4097    -9  4 0.031 0 
f 30    0   4097    -9  2 0.161 0 3 0.047 0 
                 
t   0   324      
                                     
;       ST  DUR AMP    FREQ    VIBR AT  DEC BR  PITCH 
i 801   1   0.7 6500    261.6   .5  .04 .04 9   ;C4 
i 801   2   0.7 8000    348.8   .5  .04 .04 9   ;F4 
i 801   3   0.7 7200    392.4   .5  .04 .04 9   ;G4 
i 801   4   3.1 6500    418.5   .5  .04 .04 9   ;G#4 
i 801   7   .65 8000    436.0   .5  .04 .04 9   ;A4 
i 801   8   0.7 6000    261.6   .5  .04 .04 9   ;C4 
i 801   9   0.7 7200    348.8   .5  .04 .04 9   ;F4 
i 801   10  .7  8000    392.4   .5  .04 .04 9   ;G4 
i 801   11  3.1 7500    418.5   .5  .04 .04 9   ;G#4 
i 801   14  .65 9000    436.0   .5  .04 .04 9   ;A4 
i 801   15  0.7 5500    261.6   .5  .04 .04 9   ;C4 
i 801   16  0.7 6500    348.8   .5  .04 .04 9   ;F4 
i 801   17  0.7 7500    392.4   .5  .04 .04 9   ;G4 
i 801   18  1.7 10000   418.5   .5  .06 .1  9   ;G#4 
i 801   20  1.7 10000   436.0   .5  .06 .1  9   ;A4 
i 801   22  0.7 6500    470.9   .5  .04 .04 9   ;A#4 
i 801   23  0.7 7500    490.5   .5  .04 .04 9   ;B4 
i 801   24  0.7 10000   588.6   .5  .04 .04 9   ;D5 
i 801   25  0.7 6500    523.2   .5  .04 .04 9   ;C5 
i 801   26  0.7 7500    436.0   .5  .04 .04 9   ;A4 
i 801   27  0.7 8000    348.8   .5  .04 .04 9   ;F4 
i 801   28  0.7 6500    261.6   .5  .04 .04 9   ;C4 
i 801   29  0.7 7500    218.0   .5  .04 .04 9   ;A3 
i 801   30  3.0 10000   174.4   0   .06 .25 9   ;F3 
i 801   33  3.0 5000    130.8   0   .04 .25 9   ;C3 
i 801   36  3.1 2000    87.2    0   .04 .25 9   ;F2 
                                     
i 899   0   46 
</CsScore> 
 
</CsoundSynthesizer> 


Catalogs of Csound instruments

Catalogs of Csound instruments are available. You could use the instrument code as it is, and then write Python programs to create more complex scores than are possible by hand.

Take a look/listen at the various examples in the Csound Catalog with Audio.