Songs¶
A full track built entirely in Python with openwig: a single script you run against a clean Bitwig project.
Nightdrive¶
A 12-track progressive house / techno sketch: drum grids, a tiled bass cell,
repeated stab and lead phrases, device chains, and arranger automation. The
repetitive parts are written as small generators (pulse / tile / roll)
instead of note-by-note, using stock factory devices so it runs anywhere.
"""Nightdrive - a full demo song.
A 12-track progressive house / techno sketch: drum grids, a tiled bass cell,
repeated stab and lead phrases, device chains, and arranger automation. The
repetitive parts are written as generators (pulse / tile / roll) instead of
note-by-note, with stock factory devices so it runs anywhere.
Run it against a scratch project - it opens with clean=True, which clears the
currently-open Bitwig project:
python examples/nightdrive.py
"""
from openwig import Song
def pulse(key, n, *, step=1.0, dur=0.24, vel=0.8, t0=0.0):
"""n notes of `key` on a regular grid (i*step), e.g. four-on-the-floor."""
return [(key, round(t0 + i * step, 4), dur, vel) for i in range(n)]
def tile(cell, times, period, *, t0=0.0):
"""Repeat a (key, t, dur, vel) note `cell` `times`, each shifted `period` beats."""
return [(k, round(t0 + r * period + t, 4), d, v)
for r in range(times) for (k, t, d, v) in cell]
def roll(key, t0, *, n=8, step=0.25, dur=0.1, v0=0.4, v1=0.85):
"""A rising 16th-note fill of `n` hits - a snare build before a drop."""
return [(key, round(t0 + i * step, 4), dur, round(v0 + (v1 - v0) * i / (n - 1), 4))
for i in range(n)]
s = Song(tempo=128, bars=32, clean=True)
# KICK - four on the floor
t_00_kick = s.track('KICK', device='v9 Kick')
t_00_kick.fader(0.7937)
t_00_kick.fx('Saturator')
t_00_kick.select_device(0)
t_00_kick.set_remote_values(0, {1: 0.23}) # Decay
t_00_kick.clips([(16, 96, pulse(36, 96, vel=0.82))])
# HATS - straight eighths
t_01_hats = s.track('HATS', device='v9 Hat Closed')
t_01_hats.fader(0.7937)
t_01_hats.clips([(64, 64, pulse(42, 128, step=0.5, dur=0.12, vel=0.4))])
# v1 Hat - straight 16ths + a rising decay-envelope automation
t_02_v1_hat = s.track('v1 Hat', device='v1 Hat')
t_02_v1_hat.fader(0.7937)
t_02_v1_hat.fx('Tool')
t_02_v1_hat.select_device(0)
t_02_v1_hat.set_remote_values(0, {1: 0.4083}) # Decay
t_02_v1_hat.clips([(64, 64, pulse(60, 256, step=0.25, dur=0.25, vel=0.7874))])
# Decay - a rising envelope that resets every 16 beats (bars 20-32)
_hat_rise = [(0.0, 0.408), (0.19, 0.428), (0.70, 0.438), (1.01, 0.463), (1.44, 0.523),
(1.83, 0.553), (1.97, 0.588), (2.40, 0.633), (2.75, 0.708), (3.08, 0.818), (3.08, 0.408)]
_hat_decay = [(32, 0.408)] + [(base + dt, v) for base in (76.78, 92.78, 108.78, 124.78)
for dt, v in _hat_rise]
t_02_v1_hat.automate('remote', _hat_decay, remote_index=1)
# CLAP - offbeat
t_03_clap = s.track('CLAP', device='v9 Clap')
t_03_clap.fader(0.7937)
t_03_clap.fx('Reverb')
t_03_clap.clips([(32, 96, pulse(39, 48, step=2, dur=0.2, vel=0.88, t0=1))])
# RIDE - quarters in the last 8 bars
t_04_v9_ride = s.track('v9 Ride', device='v9 Ride')
t_04_v9_ride.fader(0.7937)
t_04_v9_ride.clips([(96, 32, pulse(59, 32, dur=1.0, vel=0.7874))])
# CRASH - on each section start
t_05_v9_crash = s.track('v9 Crash', device='v9 Crash')
t_05_v9_crash.fader(0.7937)
t_05_v9_crash.fx('Reverb')
t_05_v9_crash.fx('Delay+')
t_05_v9_crash.fx('EQ+')
t_05_v9_crash.select_device(0)
t_05_v9_crash.set_remote_values(0, {0: 0.645, 1: 0.915, 2: 0.24, 3: 1, 4: 1}) # Tune, Decay, Impact, Density, Width
t_05_v9_crash.select_device(3) # EQ+
t_05_v9_crash.set_remote_values(0, {0: 0.4152}) # 1 Gain
t_05_v9_crash.set_remote_values(1, {0: 0.8952}) # 1 Freq
for _b in (32, 64, 96):
t_05_v9_crash.clips([(_b, 4, [(60, 0, 4, 0.7874)])])
# SHAKER - a 3-hit cell every bar
t_06_shaker = s.track('SHAKER', device='v9 Hat Closed')
t_06_shaker.fader(0.7937)
_shaker = [(70, 0.25, 0.1, 0.32), (70, 1.5, 0.1, 0.45), (70, 2.25, 0.1, 0.44)]
t_06_shaker.clips([(64, 64, tile(_shaker, 16, 4))])
# SNARE - rising 16th fills before each drop
t_07_snare = s.track('SNARE', device='v9 Snare')
t_07_snare.fader(0.7937)
t_07_snare.fx('Reverb')
t_07_snare.select_device(0)
t_07_snare.set_remote_values(0, {2: 0.665, 3: 0.795, 4: 0.7, 6: 0.985, 7: 0.8943}) # Snappy, Drive, Tone, Vel Sens., Output
t_07_snare.select_device(1) # Reverb
t_07_snare.set_remote_values(0, {3: 0.135, 7: 0.335}) # R. Time, Mix
for _b in (32, 64, 96, 128):
t_07_snare.clips([(_b - 2, 2, roll(38, 0))])
# BASS - syncopated root note
t_08_bass = s.track('BASS', device='Polysynth')
t_08_bass.fader(0.7937)
t_08_bass.fx('Filter')
t_08_bass.fx('Filter')
t_08_bass.select_device(0)
t_08_bass.set_remote_values(3, {0: 0.6, 5: 0.39, 7: 0.5}) # Filt Freq, FEG D, FEG Amount
t_08_bass.set_remote_values(4, {2: 0.5}) # FEG Amt
t_08_bass.select_device(1) # Filter
t_08_bass.set_remote_values(0, {0: 0, 2: 0.5714}) # Freq, Mode
_bass = [(33, 0.25, 0.5, 0.7874), (33, 0.75, 0.25, 0.7874), (33, 1.0, 0.5, 0.7874), (33, 1.5, 0.5, 0.7874)]
t_08_bass.clips([(32, 96, tile(_bass, 48, 2))])
t_08_bass.select_device(1) # Filter: Freq
t_08_bass.automate('remote', [(0, 0), (96, 0), (128, 1)], remote_index=0)
# STAB - a 4-note chord-stab motif every 16 beats
t_09_stab = s.track('STAB', device='Polysynth')
t_09_stab.fader(0.7937)
t_09_stab.fx('Delay+')
t_09_stab.select_device(0)
t_09_stab.set_remote_values(5, {7: 0.38}) # AEG R
_stab = [(57, 0.5, 0.4, 0.45), (53, 4.5, 0.4, 0.42), (48, 8.5, 0.4, 0.5), (55, 12.5, 0.4, 0.5)]
t_09_stab.clips([(32, 96, tile(_stab, 6, 16))])
t_09_stab.select_device(0) # AEG R
t_09_stab.automate('remote', [(96.7, 0.38), (124.3, 0.88), (128, 0.497), (392, 0.38)], remote_index=7, page=5)
# PAD - a held chord with a filter sweep
t_10_pad = s.track('PAD', device='Polysynth')
t_10_pad.fader(0.7937)
t_10_pad.pan(-0.5196)
t_10_pad.fx('Reverb')
t_10_pad.fx('Filter')
t_10_pad.fx('Chorus+')
t_10_pad.fx('Delay+')
t_10_pad.select_device(0)
t_10_pad.set_remote_values(0, {2: 0.2305}) # Uni1
t_10_pad.set_remote_values(3, {0: 0.877}) # Filt Freq
t_10_pad.select_device(2) # Filter
t_10_pad.set_remote_values(0, {2: 0.5714}) # Mode
t_10_pad.clips([(0, 32, [(45, 0, 64, 0.3), (52, 0, 64, 0.3), (57, 0, 64, 0.3)])])
t_10_pad.select_device(2) # Filter: Freq
t_10_pad.automate('remote', [(0.28, 1.0), (13.3, 0.0), (15.17, 0.0)], remote_index=0)
t_10_pad.select_device(0) # Filt Freq
t_10_pad.automate('remote', [(0.77, 0.877), (8.5, 0.52), (23.7, 0.89), (36.7, 0.0), (44.5, 0.0)], remote_index=0, page=3)
# LEAD - a 16-beat phrase, repeated
t_11_lead = s.track('LEAD', device='Polysynth')
t_11_lead.fader(0.7937)
t_11_lead.arm(True)
t_11_lead.fx('Reverb')
t_11_lead.fx('Delay+')
t_11_lead.select_device(0)
t_11_lead.set_remote_values(3, {0: 0.5477}) # Filt Freq
_lead = [(69, 0, 1.5, 0.59), (81, 2.5, 0.5, 0.35), (76, 3, 0.5, 0.38), (72, 4, 1.5, 0.65),
(82, 5.75, 0.25, 0.35), (84, 6.5, 0.5, 0.39), (79, 7, 0.5, 0.38), (76, 8, 1.5, 0.65),
(88, 10.5, 0.5, 0.34), (83, 11, 0.5, 0.36), (74, 12, 1.5, 0.52), (84, 13.75, 0.25, 0.29),
(86, 14.5, 0.5, 0.4), (81, 15, 0.5, 0.4)]
t_11_lead.clips([(64, 64, tile(_lead, 4, 16))])
t_11_lead.select_device(1) # Reverb: Mix
t_11_lead.automate('remote', [(0, 0.405), (100, 0.405), (126, 0.8693), (126, 0.905)], remote_index=7)
t_11_lead.select_device(0) # Filt Freq
t_11_lead.automate('remote', [(64, 0.5477), (68, 0.5477), (96, 0.6529), (100, 0.5477), (126, 0.6454), (126, 0.6529)], remote_index=0, page=3)
t_11_lead.select_device(1) # Reverb: R. Time
t_11_lead.automate('remote', [(0, 0.49), (64, 0.49), (80, 0.49), (96, 0.8037), (112, 0.49), (126, 0.8), (126, 0.8443)], remote_index=3)
s.master([
'Distortion',
'Saturator',
'Over',
])
# s.play(loop=True) # uncomment to hear it
# print(s.render('out.wav'))