Skip to content

Instantly share code, notes, and snippets.

@tildebyte
Created December 23, 2013 20:14
Show Gist options
  • Save tildebyte/8103851 to your computer and use it in GitHub Desktop.
Save tildebyte/8103851 to your computer and use it in GitHub Desktop.
<<< "Assignment 7 - whales" >>>;
// Coursera: Introduction to Programming for Musicians and Digital Artists
// 2013-12-5
// Sound settings are: 16bit@44100Hz
// whale.ck
// This could easily be (and was) a single file - no class, no function.
// For purposes of demonstrating "spork" in the Score file, it's here as a
// class with one method.
// Expanding it into a real class is an interesting idea, though...
// 625::ms/4th
public class Whale{
fun void whale(){
BPM t;
MasterFader fader; // Only for dBFStoGain.
// I'm sick of scrolling back-and-forth over a 130+ character line,
// and the more complex the signal chains get, the less sense it makes
// to try to instantiate and route all at once.
SinOsc lfo;
ADSR lfo_env;
TriOsc tri;
ADSR tri_env;
Gain instrGain;
DelayL delay;
Gain delayLevel;
Gain delayFB;
ResonZ lpf;
Gain master;
NRev rev;
Pan2 lPan;
Pan2 rPan;
// Dry (no delay).
lfo => lfo_env => tri => tri_env => lpf => instrGain => master;
// Wet (delay)
instrGain => delay => delayLevel => master;
// Delay feedback (essentially number of repeats heard).
delay => delayFB => delay;
// Trying something different. Maybe this spreads the sound out a little...
// Hard to tell.
master => rev => lPan.chan(0) => dac.chan(0);
master => rev => rPan.chan(1) => dac.chan(1);
// Using derived durations wherever possible. Some of these were reverse-
// engineered from tuning by ear using milliseconds, but once it's done,
// *everything* syncs together perfectly.
lfo.gain(fader.dBFStoGain(-2));
1.5::t.six10th => lfo.period;
// The LFO runs silently for almost a whole note - we only turn it on,
// using sustain, for the last 128th note.
lfo_env.set( (1::t.whole - 1::t.one28th), 0::ms, 1, 1::t.one28th );
0, 2480
1, 0
0, 2500
tri.gain(1);
tri.sync(2); // FM
// Swell in, run for a total of one whole note
tri_env.set( 3::t.six10th, 1::t.six10th, 0.95, (1::t.whole - 1::t.quarter) );
// Let's darken it up a bit, and give it a subtle "ring" from the resonance.
lpf.gain(1);
lpf.set(800, 1);
instrGain.gain(1);
delayLevel.gain(fader.dBFStoGain(-12));
delayFB.gain(fader.dBFStoGain(-2));
// The delay time, in derived durations. Anything less than t.whole feels too
// fast to me.
3::t.quarter => delay.max => delay.delay;
master.gain(fader.dBFStoGain(0));
rev.gain(1);
rev.mix(0.4);
lPan.pan(-1);
rPan.pan(1);
// [ 48, 50, 52, 53, 55, 57, 59 ] @=> int scale[];
[ 0, 2, 4, 5, 7, 9, 11 ] @=> int scale[]; // Scale in semitones.
[ -12, 0, 12 ] @=> int octaves[];
Std.mtof(36) => tri.freq; // Start on tonic.
while( true ){
tri_env.keyOn(1);
lfo_env.keyOn(1);
// The .keyOn for both envelopes has to run for exactly the duration of
// the AD section of the instrument env.
// This is where derived durations *really* start to come in handy.
1::t.quarter => now;
tri_env.keyOff(1);
// So, now the *sustain* portion if the instrument is triggered, and runs
// out to just before the sustain portion of the LFO should start.
1::t.whole - 1::t.quarter - 1::t.one28th => now;
lfo_env.keyOff(1);
// Trigger the LFO sustain and let it run for the remaining duration of
// this note.
1::t.one28th => now;
// Rest.
1::t.whole => now;
// God, this took forever.
// Use an offset (in the Std.mtof line) to set the octave, and the array
// of semitones as an offset to pick the note.
scale[ Math.random2(0, scale.cap() - 1) ] => int semis;
Std.mtof( 36 + semis + octaves[ Math.random2(0, 2) ] ) => tri.freq;
}
}
}
#!/usr/bin/env python
# encoding: utf-8
"""
whale
pyo pointers
When instantiating most pyo audio-rate classes:
- The first arg has to be an audio-rate signal.
- The second arg is usually frequency.
- Some classes, like the LFO, take frequency as their first arg.
- The last two args are 'mul' and 'add' for manipulating amplitude.
Most classes have '.play()' methods, which start them processing. Some also
have an '.out()' which will send their output to the dac. Usually (all the
examples I've seen), there's only one '.out()' - the last class in the chain.
"""
from pyo import *
# utility function
def dbToAmp(db):
return 10 ** (db / 10) * 10
# Server setup
s = Server(sr=48000, nchnls=2, buffersize=128, duplex=0)
s.setOutputDevice(10)
# Level is OK
# s.setAmp(dbToAmp(-20.0))
s.boot()
s.start()
# lfo -> lfoEnv -> triOsc -> triEnv -> lpf -> delay -> rev -> pan
lfoEnv = Adsr(2.48, 0, 1, 0.0195, dur=2500).play()
# Here, I'm using the Adsr on the multiplier, and using a fixed value for
# overall amplitude.
# Type 7 is Sine.
lfo = LFO(4.27, type=7, mul=lfoEnv, add=dbToAmp(-2.0)).play()
triEnv = Adsr(0.468, 0.156, 0.95, 1.875, dur=2500).play()
# Sawtooth wavetable; a simple breakpoint table. There's no built-in Saw,
# but this sounds fine.
triTable = LinTable([(0, 0), (2047, 1.0), (6143, -1.0), (8191, 0)])
# FM using the LFO. Maybe :)
triOsc = Osc(triTable, MToF(Sig(24)) * lfo, interp=4, mul=triEnv).play()
lpf = Resonx(triOsc, freq=350, q=2, stages=2).play()
# One of the truly lovely things about pyo: automatic GUI. '.ctrl()'
# instantiates appropriate GUI controls for the given class. Some classes
# have '.view()' (view data, e.g. for a table) and/or '.graph()' (view and
# *edit* data, live; new data is sent on mouse-up. Nice).
lpf.ctrl()
delay = Delay(lpf, delay=1.875, feedback=0.035,
maxdelay=1.875, mul=0.77).play()
delay.ctrl()
# 'bal' is the wet/dry mix.
rev = Freeverb(delay, size=0.75, damp=0.50, bal=0.54).play()
pan = SPan(rev, mul=1).out()
pan.ctrl()
# Show the server GUI. 'locals()' instantiates a Python command line, allowing
# for real-time manipulation of the local Python namespace.
s.gui(locals())
@nelsp
Copy link

nelsp commented Jan 1, 2014

many thanks for the example. I am trying to figure out pyo but the examples are very dense. this really helps me out!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment