Basic examples
Hello Csound
The simplest possible Csound program:import ctcsound
# Create Csound instance
cs = ctcsound.Csound()
# Define a simple orchestra
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
instr 1
a1 oscili 0.5, 440
outs a1, a1
endin
'''
# Compile orchestra
cs.compile_orc(orc)
# Start engine
cs.start()
# Play instrument 1 for 2 seconds
cs.event_string("i 1 0 2")
# Perform until done
while cs.perform_ksmps() == 0:
pass
# Clean up
cs.reset()
CSD file playback
Play a CSD file from disk:import ctcsound
import sys
def play_csd(filename):
cs = ctcsound.Csound()
# Compile the CSD file
result = cs.compile_(filename)
if result != 0:
print(f"Error compiling {filename}")
return result
# Start performance
result = cs.start()
if result != 0:
print("Error starting Csound")
return result
# Perform until complete
print(f"Playing {filename}...")
while cs.perform_ksmps() == 0:
pass
print("Finished.")
cs.reset()
return 0
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python play_csd.py <file.csd>")
sys.exit(1)
play_csd(sys.argv[1])
Command-line arguments
Pass command-line options to Csound:import ctcsound
cs = ctcsound.Csound()
# Set options before compilation
cs.set_option("-odac") # Audio output to DAC
cs.set_option("-m0") # No messages
cs.set_option("-d") # No displays
# Or compile with arguments
result = cs.compile_("csound", "-odac", "-m0", "myfile.csd")
cs.start()
while cs.perform_ksmps() == 0:
pass
cs.reset()
Real-time control
Control channels
Interactive control of instrument parameters:import ctcsound
import time
import math
cs = ctcsound.Csound()
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
instr 1
kfreq chnget "frequency"
kamp chnget "amplitude"
a1 oscili kamp, kfreq
outs a1, a1
endin
'''
cs.compile_orc(orc)
cs.start()
# Start long note
cs.event_string("i 1 0 10")
# Modulate frequency over time
for i in range(100):
freq = 440 + 200 * math.sin(i * 0.1)
amp = 0.3 + 0.2 * math.sin(i * 0.05)
cs.set_control_channel("frequency", freq)
cs.set_control_channel("amplitude", amp)
cs.perform_ksmps()
time.sleep(0.01)
cs.reset()
Reading control values
Get values back from Csound:import ctcsound
cs = ctcsound.Csound()
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
instr 1
; Output envelope value
kenv linseg 0, p3/2, 1, p3/2, 0
chnset kenv, "envelope"
a1 oscili kenv * 0.5, 440
outs a1, a1
endin
'''
cs.compile_orc(orc)
cs.start()
cs.event_string("i 1 0 2")
while cs.perform_ksmps() == 0:
# Read envelope value
env, err = cs.control_channel("envelope")
if err is None:
print(f"Envelope: {env:.3f}", end='\r')
print("\nDone.")
cs.reset()
Audio processing
Process audio buffers
Direct buffer manipulation with NumPy:import ctcsound
import numpy as np
cs = ctcsound.Csound()
# Disable default audio I/O
cs.set_option("-odac")
cs.set_option("-iadc")
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
instr 1
a1 oscili 0.5, 440
outs a1, a1
endin
'''
cs.compile_orc(orc)
cs.start()
cs.event_string("i 1 0 2")
# Get audio buffers
spout = cs.spout()
# Collect output
output_frames = []
while cs.perform_ksmps() == 0:
# Copy current output buffer
frame = spout.copy()
output_frames.append(frame)
# Combine all frames
audio_data = np.concatenate(output_frames)
print(f"Generated {len(audio_data)} samples")
print(f"Duration: {len(audio_data) / cs.sr() / cs.channels():.2f} seconds")
cs.reset()
Audio input processing
Feed external audio into Csound:import ctcsound
import numpy as np
import wave
def process_file_with_csound(input_file, output_file):
# Read input file
with wave.open(input_file, 'rb') as wf:
sr = wf.getframerate()
channels = wf.getnchannels()
audio_bytes = wf.readframes(wf.getnframes())
audio_data = np.frombuffer(audio_bytes, dtype=np.int16)
audio_float = audio_data.astype(ctcsound.MYFLT) / 32768.0
cs = ctcsound.Csound()
cs.set_option(f"-o{output_file}")
orc = f'''
sr = {sr}
ksmps = 64
nchnls = {channels}
0dbfs = 1
instr 1
; Apply reverb to input
ain1, ain2 inch 1, 2
aL, aR freeverb ain1, ain2, 0.9, 0.5
outs aL, aR
endin
'''
cs.compile_orc(orc)
cs.start()
cs.event_string("i 1 0 -1") # Infinite duration
# Get input buffer
spin = cs.spin()
ksmps_size = cs.ksmps() * channels
# Process in chunks
pos = 0
while pos < len(audio_float):
chunk_size = min(ksmps_size, len(audio_float) - pos)
spin[:chunk_size] = audio_float[pos:pos + chunk_size]
if cs.perform_ksmps() != 0:
break
pos += chunk_size
cs.reset()
process_file_with_csound("input.wav", "output.wav")
Performance thread
Basic threaded performance
Run Csound in a separate thread:import ctcsound
import time
cs = ctcsound.Csound()
cs.set_option("-odac")
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
instr 1
a1 oscili 0.5, p4
outs a1, a1
endin
'''
cs.compile_orc(orc)
# Create performance thread
pt = ctcsound.CsoundPerformanceThread(cs.csound())
# Start playing
pt.play()
# Send events while playing
for freq in [440, 550, 660, 880]:
pt.input_message(f"i 1 0 1 {freq}")
time.sleep(1)
# Wait for completion
pt.join()
Interactive threaded control
Combine threading with real-time control:import ctcsound
import time
cs = ctcsound.Csound()
cs.set_option("-odac")
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
instr 1
kfreq chnget "freq"
kamp chnget "amp"
a1 oscili kamp, kfreq
outs a1, a1
endin
'''
cs.compile_orc(orc)
# Set initial values
cs.set_control_channel("freq", 440.0)
cs.set_control_channel("amp", 0.5)
# Start performance thread
pt = ctcsound.CsoundPerformanceThread(cs.csound())
pt.play()
pt.input_message("i 1 0 -1") # Infinite note
# Change parameters while playing
try:
for i in range(50):
freq = 440 + i * 10
cs.set_control_channel("freq", float(freq))
time.sleep(0.1)
except KeyboardInterrupt:
pass
pt.stop()
pt.join()
Function tables
Create and read tables
Work with function tables:import ctcsound
import numpy as np
import matplotlib.pyplot as plt
cs = ctcsound.Csound()
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
; Create sine wave table
giSine ftgen 1, 0, 1024, 10, 1
; Create complex waveform
giComplex ftgen 2, 0, 1024, 10, 1, 0.5, 0.3, 0.1
instr 1
endin
'''
cs.compile_orc(orc)
cs.start()
# Read table data
table1 = cs.table(1)
table2 = cs.table(2)
if table1 is not None:
print(f"Table 1 length: {len(table1)}")
# Plot tables
plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)
plt.plot(table1)
plt.title("Table 1: Sine Wave")
plt.subplot(1, 2, 2)
plt.plot(table2)
plt.title("Table 2: Complex Waveform")
plt.tight_layout()
plt.savefig("tables.png")
print("Saved tables.png")
cs.reset()
Analyze table arguments
Inspect how tables were generated:import ctcsound
cs = ctcsound.Csound()
orc = '''
sr = 44100
ksmps = 64
giTable ftgen 1, 0, 1024, 10, 1, 0.5, 0.25
instr 1
endin
'''
cs.compile_orc(orc)
cs.start()
# Get table arguments
args = cs.tableArgs(1)
if args is not None:
print(f"GEN number: {int(args[0])}")
print(f"Harmonics: {args[1:]}")
cs.reset()
Advanced patterns
MIDI-like sequencer
Build a simple step sequencer:import ctcsound
import time
cs = ctcsound.Csound()
cs.set_option("-odac")
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
instr 1
ifreq = p4
iamp = p5
aenv expon 1, p3, 0.001
a1 oscili iamp * aenv, ifreq
outs a1, a1
endin
'''
cs.compile_orc(orc)
# Note sequence (MIDI note numbers)
sequence = [60, 64, 67, 72, 67, 64, 60, 55]
# Convert MIDI to frequency
def mtof(midi):
return 440 * (2 ** ((midi - 69) / 12))
pt = ctcsound.CsoundPerformanceThread(cs.csound())
pt.play()
# Play sequence
tempo = 120 # BPM
beat_duration = 60.0 / tempo
try:
while True:
for note in sequence:
freq = mtof(note)
pt.input_message(f"i 1 0 {beat_duration} {freq} 0.5")
time.sleep(beat_duration)
except KeyboardInterrupt:
pass
pt.stop()
pt.join()
Dynamic compilation
Add instruments during performance:import ctcsound
import time
cs = ctcsound.Csound()
cs.set_option("-odac")
# Initial orchestra
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
'''
cs.compile_orc(orc)
cs.start()
# Start performance thread
pt = ctcsound.CsoundPerformanceThread(cs.csound())
pt.play()
# Add instrument 1
instr1 = '''
instr 1
a1 oscili 0.5, 440
outs a1, a1
endin
'''
cs.compile_orc(instr1)
pt.input_message("i 1 0 1")
time.sleep(2)
# Add instrument 2
instr2 = '''
instr 2
a1 oscili 0.5, 550
outs a1, a1
endin
'''
cs.compile_orc(instr2)
pt.input_message("i 2 0 1")
time.sleep(2)
pt.stop()
pt.join()
Recording output
Record performance to file:import ctcsound
import time
cs = ctcsound.Csound()
cs.set_option("-odac") # Real-time output
orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
instr 1
kfreq expon 880, p3, 220
a1 oscili 0.5, kfreq
outs a1, a1
endin
'''
cs.compile_orc(orc)
pt = ctcsound.CsoundPerformanceThread(cs.csound())
# Start recording
pt.record("output.wav", samplebits=16, numbufs=4)
print("Recording started...")
pt.play()
# Play some notes
for i in range(5):
pt.input_message(f"i 1 {i} 1")
time.sleep(6)
# Stop recording
pt.stop_record()
print("Recording stopped.")
pt.stop()
pt.join()
Error handling
Robust error handling:import ctcsound
import sys
def safe_csound_run(csd_file):
cs = None
try:
cs = ctcsound.Csound()
# Compile
result = cs.compile_(csd_file)
if result != ctcsound.CSOUND_SUCCESS:
print(f"Compilation failed: {result}")
return 1
# Start
result = cs.start()
if result != ctcsound.CSOUND_SUCCESS:
print(f"Start failed: {result}")
return 1
# Perform
while True:
result = cs.perform_ksmps()
if result != 0:
if result > 0:
print("Performance completed normally")
else:
print(f"Performance error: {result}")
break
return 0
except Exception as e:
print(f"Exception: {e}")
return 1
finally:
if cs is not None:
cs.reset()
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python script.py <file.csd>")
sys.exit(1)
sys.exit(safe_csound_run(sys.argv[1]))
NumPy integration
Signal analysis
Analyze Csound output with NumPy:import ctcsound
import numpy as np
cs = ctcsound.Csound()
cs.set_option("-n") # No audio output
orc = '''
sr = 44100
ksmps = 64
nchnls = 1
0dbfs = 1
instr 1
a1 oscili 0.5, 440
out a1
endin
'''
cs.compile_orc(orc)
cs.start()
cs.event_string("i 1 0 1")
# Collect one second of audio
frames_needed = int(cs.sr() / cs.ksmps())
output_frames = []
for _ in range(frames_needed):
if cs.perform_ksmps() != 0:
break
output_frames.append(cs.spout().copy())
# Combine and analyze
audio = np.concatenate(output_frames)
print(f"RMS level: {np.sqrt(np.mean(audio**2)):.4f}")
print(f"Peak level: {np.max(np.abs(audio)):.4f}")
print(f"Mean: {np.mean(audio):.6f}")
cs.reset()
Batch processing
Process multiple files efficiently:import ctcsound
import numpy as np
import os
def apply_csound_effect(input_files, output_dir, effect_orc):
"""Apply Csound effect to multiple files."""
os.makedirs(output_dir, exist_ok=True)
for input_file in input_files:
basename = os.path.basename(input_file)
output_file = os.path.join(output_dir, f"processed_{basename}")
cs = ctcsound.Csound()
cs.set_option(f"-i{input_file}")
cs.set_option(f"-o{output_file}")
cs.compile_orc(effect_orc)
cs.start()
print(f"Processing {basename}...")
while cs.perform_ksmps() == 0:
pass
cs.reset()
print(f"Saved to {output_file}")
# Example effect: reverb
reverb_orc = '''
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
instr 1
ain1, ain2 inch 1, 2
aL, aR freeverb ain1, ain2, 0.8, 0.5
outs aL, aR
endin
; Auto-start
i 1 0 -1
'''
input_files = ["file1.wav", "file2.wav"]
apply_csound_effect(input_files, "processed", reverb_orc)