Fellow ChucKists,
Yesterday I wanted to wish Dan a happy birthday on FaceBook, hoping for many more years of fun with strings and code but thought it'd be nicer to write this in ChucK. Then stuff happened and I was too late.
Belated but no less sincere; emulating strings in LiSa... because we can, and because LiSa can (LiSa can actually do anything, she's just modest). We start out with a very literal implementation of Karplus-Strong at concert A4 tuning. Then we start mucking with various parameters in ways that are less traditionally Karplus-Strong for some more interesting tembral effects.
Yours,
Kas.
/*
Do copy, do share.
No warranties, no refunds.
Physical models can (and will!) blow up; please mind your speakers, neighbours and pets.
Remixing and sharing results strongly encouraged (many parameters left....).
Happy belated birthday to Dan!
*/
//the patch (noise or a saw plus a envelope could emulate a bow instead of a pluck)
Impulse i => LiSa l => dac;
//needs to be defined
second => l.duration;
//two voices for averaging
2 => l.maxVoices;
//buffer duration = source of the pitch
second / 440 => dur base_cycle;
//create a looping delay
//looping for both recording and playback is on by default
base_cycle => l.loopEndRec;
l.loopEnd (0, base_cycle);
l.loopEnd (1, base_cycle);
1 => l.record;
//offset the second voice by one sample
//so we average between the current and the last sample value
l.play (0, 1);
l.playPos (1, -1 * samp); //"::" should work here but gives me a error? [BUG]
l.play( 1, 1);
//average between the two values. ratio sets the "filter's" effect.
//make sure the two add up to unity (1.0).
l.voiceGain (0, .5);
l.voiceGain (1, .5);
//feedback sets the tone's decay
.995 => l.feedback;
//off we go!
1 => i.next;
2::second => now;
//tradition ends here.
//fun starts
l.loopEnd (1, base_cycle * .5);
1 => i.next;
2::second => now;
l.loopEnd (1, base_cycle * 2);
1 => i.next;
2::second => now;
l.loopEnd (1, base_cycle * .333333);
1 => i.next;
2::second => now;
l.loopEnd (1, base_cycle * 1.5);
1 => i.next;
2::second => now;
l.loopEndRec() * 2 => l.loopEndRec;
l.loopEnd (1, base_cycle - samp);
1 => i.next;
4::second => now;