Colin Sullivan

Bass Distortion with ChucK

This semester I have been pursuing an independent study in DSP Programming for Music/Audio Applications. For my final project, I will be designing an Overdrive/Distortion effect that is customized for the sound of my bass.

I've decided to implement this using a programming language called ChucK, which so far has been working out extremely well. ChucK is a relatively new multimedia programming language which allows you to accomplish some complicated tasks in very little time. Since I have not really done any DSP Programming before, it is extremely gratifying to be able to hear results immediately.

The way Independent Study works at RPI, is you make proposals, set goals, and complete them. Here is my proposal for my end-of-semester project: Project Proposal

Curtis Bahn is my advising professor for this project, and he has been more than helpful.

Here is an audio sample of the effect in action. There are no other alterations to the bass signal besides mixing in a bit of clean as well. The affected signal was recorded through my stack (GK-700RBII, Ampeg SVT410HLF) using my Zoom H4n. The distortion increases as I play my bass louder, as you can hear towards the end of the clip.

Overdrive effect test

ChucK ended up working very well. I think the effect sounds pretty good. As you can see below, there are infinitely many options available for the coefficients of the wavetable function (Gen17 object in ChucK). I can spend hours tweaking them, but the ones I settled on are uncommented below.

/**
* Overdrive/Distortion effect (meant for bass guitar)
*
* Copyright (c) 2009 Colin Sullivan
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
**/
 
// patch
//envelope follower
adc => Gain g => OnePole p => blackhole;
//clean lows
adc => LPF lowpass => Gain lowClean => Gain mix;
//overdrive on lows
lowpass => Gen17 lowOD => mix;
//clean highs
adc => BPF highpass => Gain highClean => mix;
//overdrive on highs
adc => highpass => Gen17 highOD => mix;
//mix them together
mix => dac;
 
//master Levels
mix.gain(0);
1 => float masterVolumeLimit;
 
//low levels
lowClean.gain(1); //1
lowOD.gain(0); //0.3
 
//high levels
highClean.gain(1); //0.3
highOD.gain(0); //1
 
//frequency of pass filters
lowpass.freq(1000);
highpass.set(1000, 1000/(1200-800) );
 

//square input
adc => g;
//multiply
3 => g.op;
// set pole position
0.9999 => p.pole;
 

// load up the coeffs; amplitudes for successive partials
lowOD.coefs( [ 1., 0.5, 0, 0.5, 1. ] ); //Nice fat fuzz
//lowOD.coefs( [1., 0.5, 0, -0.5, -1.] ); //thrashy, but buzz
//lowOD.coefs( [.99, .88, 0, .88, -.99] ); //loud and farty fuzz
// load up the coeffs; amplitudes for successive partials
//highOD.coefs( [ 1., 0.5, 0, 0.5, 1. ] );
//highOD.coefs( [1., 0.5, 0, -0.5, -1.] );
//highOD.coefs( [.99, .88, 0, .88, -.99] );
//highOD.coefs( [0.5, 0, -0.5] );
highOD.coefs( [.256, -.576, .432, -.120, .9] ); //not grungy enough
 
//master volume
0 => float masterVolume;
0 => float i;
 
// will hold last value of filter
0 => float pLast;
// full overdrive flag
0 => int fullOD;
 
// Allows output to chuck~ object outlet in Max/MSP
MaxMessage m;
 
while (true) {
while(masterVolume < masterVolumeLimit) {
mix.gain(masterVolume);
0.001 +=> masterVolume;
1::ms => now;
}
 
// Get last value of filter
p.last()*50 => pLast;
 
// Debug to max
//pLast => m.floatout;
 

// Set high overdrive gain based on dynamics of playing
if(pLast < 1 && fullOD == 0)
{
highOD.gain(pLast);
lowOD.gain(0.3*pLast);
}
else if(pLast >= 1)
{
highOD.gain(1);
lowOD.gain(0.3);
1 => fullOD;
}
else if(pLast < 0.5 && fullOD == 1)
{
// turn fullOD flag off if done lowering
if(highOD.gain() - pLast < 0.001)
{
0 => fullOD;
}
else
{
// Lower gain
highOD.gain(highOD.gain() - 0.001);
}
}
0.01 +=> i;
1::ms => now;
}

Java