//----------------------------------------------------------------------------- // name: 12Sentiments_Logo.ck // desc: A sound logo for the 12 Sentiments for VR project // // author: Jack Atherton // // Combines various timbres I wrote for the project with // some pretty basic sequencing and filter sweeps. //----------------------------------------------------------------------------- // ===== SUPERSAW MELODY =========/ class Supersaw extends Chubgraph { SawOsc osc => Gain out => outlet; 5 => int numDelays; 1.0 / (1 + numDelays) => out.gain; DelayA theDelays[numDelays]; SinOsc lfos[numDelays]; dur baseDelays[numDelays]; float baseFreqs[numDelays]; for( int i; i < numDelays; i++ ) { osc => theDelays[i] => out; 0.15::second => theDelays[i].max; // crucial to modify! Math.random2f( 0.001, 0.002 )::second => baseDelays[i]; lfos[i] => blackhole; // Math.random2f( -0.1, 0.1) => baseFreqs[i]; Math.random2f( 0, pi ) => lfos[i].phase; } 0.05::second => dur baseDelay; 0.333 => float baseFreq; 1 => float lfoGain; fun void AttachLFOs() { while( true ) { for( int i; i < numDelays; i++ ) { baseFreq + baseFreqs[i] => lfos[i].freq; lfoGain * lfos[i].last()::second + baseDelay + baseDelays[i] => theDelays[i].delay; } 1::ms => now; } } spork ~ AttachLFOs(); SinOsc pitchLFO => blackhole; 0.77 => pitchLFO.freq; 1.0 / 300 => float pitchLFODepth; 440 => float basePitch; fun void FreqMod() { while( true ) { // calc freq basePitch + ( basePitch * pitchLFODepth ) * pitchLFO.last() => float f; // set f => osc.freq; 1.0 / f => lfoGain; // seconds per cycle == gain amount // wait 1::ms => now; } } spork ~ FreqMod(); fun void freq( float f ) { f => basePitch; } fun void delay( dur d ) { d => baseDelay; } fun void timbreLFO( float f ) { f => baseFreq; } fun void pitchLFORate( float f ) { f => pitchLFO.freq; } fun void pitchLFODepthMultiplier( float r ) { r => pitchLFODepth; } } HPF Lhpf => LPF Llpf => Gain LampMod => NRev Lreverb => Gain Lpredac => dac.left; HPF Rhpf => LPF Rlpf => Gain RampMod => NRev Rreverb => Gain Rpredac => dac.right; 200 => Lhpf.freq => Rhpf.freq; // TODO: modulate downward 2500 => Llpf.freq => Rlpf.freq; 0.09 => Lreverb.mix => Rreverb.mix; fun void AmpMod() { SinOsc lfo => blackhole; 0.13 => lfo.freq; while( true ) { 0.85 + 0.15 * lfo.last() => LampMod.gain => RampMod.gain; 1::ms => now; } } spork ~ AmpMod(); 0.18::second => dur noteLength; // don't pan mySaw Supersaw mySaw => ADSR myAdsr => Lhpf; myAdsr => Rhpf; myAdsr.set( 0.1::noteLength, 0.3::noteLength, 0.85, 0.05::noteLength ); 67 => int G4; 69 => int A4; 71 => int B4; 72 => int C5; 74 => int D5; 76 => int E5; 77 => int F5; 79 => int G5; 81 => int A5; 84 => int C6; [0, G5, A5, E5, G5] @=> int melodyNotes[]; [2, 1, 1, 1, 3 ] @=> int melodyDurs[]; [false, true, false, false, false] @=> int shouldAttack[]; [false, false, false, false, true] @=> int shouldDecay[]; [0, G4, A4, C5, F5, G5, A5, C6] @=> int beginningMelodyNotes[]; [false, true, false, false] @=> int beginningShouldAttack[]; [false, false, false, true] @=> int beginningShouldDecay[]; [2, 2, 2, 1, 1] @=> int beginningShouldRepeat[]; // how much freq waves up and down as a % of current freq 1.2 / 300 => mySaw.pitchLFODepthMultiplier; // how quickly the freq lfo moves 3.67 => mySaw.pitchLFORate; // higher == wider pitch spread 0.33 => mySaw.timbreLFO; // basic loudness 0.1 => mySaw.gain; float goalNote, currentNote; 0.3 => float slewNote; beginningMelodyNotes[1] => goalNote => currentNote; fun void SlewFreq() { while( true ) { slewNote * (goalNote - currentNote) +=> currentNote; currentNote => Std.mtof => mySaw.freq; 5::ms => now; } } spork ~ SlewFreq(); fun void PlayMelody() { while( true ) { for( int i; i < melodyNotes.size(); i++ ) { if( melodyNotes[i] > 10 ) { melodyNotes[i] => goalNote; if( shouldAttack[i] ) { goalNote => currentNote => Std.mtof => mySaw.freq; myAdsr.keyOn( true ); } melodyDurs[i]::noteLength - myAdsr.releaseTime() => now; if( shouldDecay[i] ) { myAdsr.keyOff( true ); } myAdsr.releaseTime() => now; } else { melodyDurs[i]::noteLength => now; } } } } fun void PlayBeginningMelody() { for( int i; i < beginningShouldRepeat.size(); i++ ) { repeat( beginningShouldRepeat[i] ) { for( int j; j < 4; j++ ) { if( j > 0 ) { beginningMelodyNotes[i+j] => goalNote; if( beginningShouldAttack[j] ) { goalNote => currentNote => Std.mtof => mySaw.freq; myAdsr.keyOn( true ); } noteLength - myAdsr.releaseTime() => now; if( beginningShouldDecay[j] ) { myAdsr.keyOff( true ); } myAdsr.releaseTime() => now; } else { 0.5::noteLength => now; // get rid of jumping note, which I was already trying to do above, // but doing it there failed, so I'll do it here, earlier, instead if( beginningShouldAttack[j+1] ) { beginningMelodyNotes[i+j+1] => goalNote; goalNote => currentNote => Std.mtof => mySaw.freq; } 0.5::noteLength => now; } } } } } // ============================================ // // ================== Bass =================== // class AbsDistortion extends Chugen { fun float tick( float in ) { //return in; return 1.9 * in / ( 1 + Math.fabs( in ) ); } } class NoDistortion extends Chugen { fun float tick( float in ) { return in; } } class Abs3Distortion extends Chugen { fun float tick( float in ) { in * in * in => float three; //return in; return three * 1.5 / ( 1 + Math.fabs( three ) ); } } class TanDistortion extends Chugen { fun float tick( float in ) { in * in => float square; in * square => float cubic; cubic * square => float five; five * square => float seven; return 0.5 * (in - cubic / 3 + 2 * five / 15 - 17 * seven / 315); } } class SailLead extends Chubgraph { SawOsc osc1 => Gain common => LPF lpf => TanDistortion distortion => ADSR a => Gain out => outlet; SawOsc osc2 => common; SqrOsc osc3 => common; 0.35 => osc1.gain; 0.45 => osc2.gain; 0.20 => osc3.gain; 1.2 => common.gain; // input to overdrive to distort... //1 => overdrive.sync; // SinOsc overdrive // dyno.compress(); // Dyno dyno 13 => lpf.Q; 0.5 => out.gain; staccato( true ); 200 => float goalFreq; 200 => float currentFreq; 0.15 => float slewFreq; false => int noteIsOn; fun void staccato( int doStaccato ) { if( doStaccato ) { a.set( 20::ms, 150::ms, 0.3, 1::ms ); } else { a.set( 20::ms, 5::ms, 0.9, 20::ms ); } } fun void SetFreq() { SinOsc pitchLfo => blackhole; 1.1 => pitchLfo.freq; while( true ) { slewFreq * ( goalFreq - currentFreq ) +=> currentFreq; // 0.5% of current frequency currentFreq + 0.005 * currentFreq * pitchLfo.last() => float waveringFreq; waveringFreq => osc1.freq; waveringFreq / 2 => osc2.freq; waveringFreq => osc3.freq; // what is "80% of the way up"? Math.min( waveringFreq * 32, 20000 ) => lpf.freq; 1::ms => now; } } spork ~ SetFreq(); fun float freq( float f ) { f => goalFreq; if( noteIsOn ) { 0.026 => slewFreq; } else { 0.15 => slewFreq; } // putting this only here instead of in the while loop // makes the attack sound harder because // the resonant filter doesn't glide //Math.min( goalFreq * 32, 20000 ) => lpf.freq; return f; } fun float freq() { return goalFreq; } fun void noteOn() { if( !noteIsOn ) { 1 => a.keyOn; goalFreq * 8 => currentFreq; // higher = harder attack true => noteIsOn; } } fun void noteOff() { 1 => a.keyOff; false => noteIsOn; } } // do not pan bass SailLead bass => LPF bassLPF => Lreverb; bassLPF => Rreverb; 220 => bassLPF.freq; 60 - 24 => int C2; C2 + 2 => int D2; C2 - 7 => int F1; C2 + 7 => int G2; [C2, 0, 0, G2] @=> int bassNotes[]; [1.7, 0.3, 1, 1] @=> float bassDurs[]; [F1, 0, F1, 0, 0, D2, 0] @=> int beginningBassNotes[]; [1.7, 6.3, 1.7, 0.3, 4, 1.7, 0.3] @=> float beginningBassDurs[]; // dummy ADSR to hold lengths of time conveniently ADSR bassADSR; bassADSR.set( 0.1::noteLength, 0.3::noteLength, 0.45, 0.1::noteLength ); 10::ms => dur nonMelodyStartDelay; fun void PlayBass() { nonMelodyStartDelay => now; while( true ) { for( int i; i < bassNotes.size(); i++ ) { if( bassNotes[i] > 10 ) { bassNotes[i] +12 => Std.mtof => bass.freq; bass.noteOn(); bassDurs[i] * 2::noteLength - bassADSR.releaseTime() => now; bass.noteOff(); bassADSR.releaseTime() => now; } else { bassDurs[i] * 2::noteLength => now; } } } } fun void PlayBeginningBass() { nonMelodyStartDelay => now; for( int i; i < beginningBassNotes.size(); i++ ) { if( beginningBassNotes[i] > 10 ) { beginningBassNotes[i] + 12 => Std.mtof => bass.freq; bass.noteOn(); beginningBassDurs[i] * 2::noteLength - bassADSR.releaseTime() => now; bass.noteOff(); bassADSR.releaseTime() => now; } else { beginningBassDurs[i] * 2::noteLength => now; } } } // =================================================== // // ============= AhhSynth =========================== // class AhhSynth extends Chubgraph { LiSa lisa => outlet; // spawn rate: how often a new grain is spawned (ms) 25 => float grainSpawnRateMS; 0 => float grainSpawnRateVariationMS; 0.0 => float grainSpawnRateVariationRateMS; // position: where in the file is a grain (0 to 1) 0.61 => float grainPosition; 0.2 => float grainPositionRandomness; // grain length: how long is a grain (ms) 300 => float grainLengthMS; 10 => float grainLengthRandomnessMS; // grain rate: how quickly is the grain scanning through the file 1.004 => float grainRate; // 1.002 == in-tune Ab 0.015 => float grainRateRandomness; // ramp up/down: how quickly we ramp up / down 50 => float rampUpMS; 200 => float rampDownMS; // gain: how loud is everything overall 1 => float gainMultiplier; float myFreq; fun float freq( float f ) { f => myFreq; 61 => Std.mtof => float baseFreq; // 1.002 == in tune for 56 for aah4.wav // 1.004 == in tune for 60 for aah5.wav myFreq / baseFreq * 0.98 => grainRate; return myFreq; } fun float freq() { return myFreq; } fun float gain( float g ) { g => lisa.gain; return g; } fun float gain() { return lisa.gain(); } SndBuf buf; me.dir() + "aah5.wav" => buf.read; buf.length() => lisa.duration; // copy samples in for( int i; i < buf.samples(); i++ ) { lisa.valueAt( buf.valueAt( i ), i::samp ); } buf.length() => dur bufferlen; // LiSa params 100 => lisa.maxVoices; 0.1 => lisa.gain; true => lisa.loop; false => lisa.record; // modulate SinOsc freqmod => blackhole; 0.1 => freqmod.freq; 0.1 => float maxGain; fun void SetGain() { while( true ) { maxGain * gainMultiplier => lisa.gain; 1::ms => now; } } spork ~ SetGain(); fun void SpawnGrains() { // create grains while( true ) { // grain length ( grainLengthMS + Math.random2f( -grainLengthRandomnessMS / 2, grainLengthRandomnessMS / 2 ) ) * 1::ms => dur grainLength; // grain rate grainRate + Math.random2f( -grainRateRandomness / 2, grainRateRandomness / 2 ) => float grainRate; // grain position ( grainPosition + Math.random2f( -grainPositionRandomness / 2, grainPositionRandomness / 2 ) ) * bufferlen => dur playPos; // grain: grainlen, rampup, rampdown, rate, playPos spork ~ PlayGrain( grainLength, rampUpMS::ms, rampDownMS::ms, grainRate, playPos); // advance time (time per grain) // PARAM: GRAIN SPAWN RATE grainSpawnRateMS::ms + freqmod.last() * grainSpawnRateVariationMS::ms => now; grainSpawnRateVariationRateMS => freqmod.freq; } } spork ~ SpawnGrains(); // sporkee fun void PlayGrain( dur grainlen, dur rampup, dur rampdown, float rate, dur playPos ) { lisa.getVoice() => int newvoice; if(newvoice > -1) { lisa.rate( newvoice, rate ); lisa.playPos( newvoice, playPos ); lisa.rampUp( newvoice, rampup ); ( grainlen - ( rampup + rampdown ) ) => now; lisa.rampDown( newvoice, rampdown) ; rampdown => now; } } } 53 => int F3; 57 => int A3; 60 => int C4; 62 => int D4; 65 => int F4; // notes [ [C4, D4, G4], [C4, F4, G4] ] @=> int chordNotes[][]; // beginning notes [ [F3, C4, A4] ] @=> int beginningChordNotes[][]; AhhSynth ahhs[ chordNotes[0].size() ]; Pan2 ahhPans[ ahhs.size() ]; // Pan2.pan from -1 to 1 [-0.7, 0, 0.7] @=> float pans[]; Gain LahhGain => Llpf; Gain RahhGain => Rlpf; 0.5 => LahhGain.gain => RahhGain.gain; for( int i; i < ahhs.size(); i++ ) { 1.0 / ahhs.size() => ahhs[i].gain; ahhs[i] => ahhPans[i]; ahhPans[i].left => LahhGain; ahhPans[i].right => RahhGain; pans[i] => ahhPans[i].pan; } fun void PlayAhhSynth() { nonMelodyStartDelay => now; while( true ) { for( int i; i < chordNotes.size(); i++ ) { for( int j; j < chordNotes[i].size(); j++ ) { chordNotes[i][j] => Std.mtof => ahhs[j].freq; } 32::noteLength => now; } } } fun void PlayBeginningAhhSynth() { nonMelodyStartDelay => now; for( int i; i < beginningChordNotes.size(); i++ ) { for( int j; j < beginningChordNotes[i].size(); j++ ) { beginningChordNotes[i][j] => Std.mtof => ahhs[j].freq; } 32::noteLength => now; } } fun void ChangeLPFFreq( LPF lpf, float start, float end, dur howLong ) { 10::ms => dur delta; (howLong / delta) $ int => int timeSteps; (end - start) / timeSteps => float freqDelta; start => lpf.freq; for( int i; i < timeSteps; i++ ) { start + i * freqDelta => lpf.freq; delta => now; } } fun void ChangeGain( Gain g, float start, float end, dur howLong ) { 10::ms => dur delta; (howLong / delta) $ int => int timeSteps; (end - start) / timeSteps => float gainDelta; start => g.gain; for( int i; i < timeSteps; i++ ) { start + i * gainDelta => g.gain; delta => now; } } // record dac => WvOut2 wavOut => blackhole; me.dir() + "12Sentiments_Logo.wav" => wavOut.wavFilename; fun void PlayBeginning() { spork ~ PlayBeginningMelody(); spork ~ PlayBeginningBass(); spork ~ PlayBeginningAhhSynth(); spork ~ ChangeLPFFreq( Llpf, 500, 2000, 16::noteLength ); spork ~ ChangeLPFFreq( Rlpf, 500, 2000, 16::noteLength ); 16::noteLength => now; spork ~ ChangeLPFFreq( Llpf, 2000, 6000, 16::noteLength ); spork ~ ChangeLPFFreq( Rlpf, 2000, 6000, 16::noteLength ); 16::noteLength => now; } PlayBeginning(); fun void PlayEnding() { spork ~ PlayMelody(); spork ~ PlayBass(); spork ~ PlayAhhSynth(); 16::noteLength => now; spork ~ ChangeLPFFreq( Llpf, 6000, 2000, 16::noteLength ); spork ~ ChangeLPFFreq( Rlpf, 6000, 2000, 16::noteLength ); spork ~ ChangeLPFFreq( bassLPF, 220, 180, 16::noteLength ); 16::noteLength => now; spork ~ ChangeLPFFreq( Llpf, 2000, 1000, 32::noteLength ); spork ~ ChangeLPFFreq( Rlpf, 2000, 1000, 32::noteLength ); spork ~ ChangeLPFFreq( bassLPF, 180, 100, 32::noteLength ); 32::noteLength => now; spork ~ ChangeLPFFreq( Llpf, 1000, 20, 48::noteLength ); spork ~ ChangeLPFFreq( Rlpf, 1000, 20, 48::noteLength ); spork ~ ChangeLPFFreq( bassLPF, 100, 20, 48::noteLength ); 48::noteLength => now; spork ~ ChangeGain( Lpredac, 1, 0, 16::noteLength ); spork ~ ChangeGain( Rpredac, 1, 0, 16::noteLength ); 16::noteLength => now; } PlayEnding(); wavOut.closeFile();