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;
awesomely twisted use of LiSa Kassen! i don't think this is really karplus-strong though. in KP, the lowpass filter is inside the feedback loop. for LiSa, the feedback ratio doesn't actually feed the output of all its voices back into the buffer (i'd have to think about how to implement that inside the ugen), it just retains its buffer, scaled by whatever you set the ratio to (meant to emulate what various loopers can do when loop recording). i can see, however, that it would be really useful to have it actually feedback all its voice outputs into its buffer; i'll take a look at it! maybe a second feedback mode. best, dan On Jun 17, 2010, at 11:28 PM, Kassen wrote:
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;
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
On 18 June 2010 14:50, Daniel Trueman
awesomely twisted use of LiSa Kassen!
:-)
i don't think this is really karplus-strong though. in KP, the lowpass filter is inside the feedback loop.
Yes, of course.
for LiSa, the feedback ratio doesn't actually feed the output of all its voices back into the buffer (i'd have to think about how to implement that inside the ugen), it just retains its buffer, scaled by whatever you set the ratio to (meant to emulate what various loopers can do when loop recording). i can see, however, that it would be really useful to have it actually feedback all its voice outputs into its buffer; i'll take a look at it! maybe a second feedback mode.
It's not? WHOOOPS! I suppose that would explain why some of the more exotic modifications to KS stayed so stable :-). I feel quite silly now. That said; this second kind of feedback would be quite easy and I see no need to put it into the UGen; Impulse i => LiSa l => dac; l => l; //feedback .995 => l.gain; //feedback amplitude 1 => l.record; stuff.etc() Not really worth a update to LiSa, I think. That will also mean the whole rest of the code will need a look, but I'm positive the principle is still valid. Voice 1 would need to read from the location that the last tick wrote to (for proper IIR-filtering) but that's possible, I think. I'll try to get it right later. I should have given all this a bit more thought and testing, but as you get to say in such situations; it's the thought that counts ;-) Yours, Kas.
participants (2)
-
Daniel Trueman
-
Kassen