how to code a synthesis algorithm

mixwhit's icon

I asked a question about real-time instrument processing in a previous post. I'm rethinking what I want based on those answers (thanks!) and have a related but different question.

I want to implement a two-part algorithm and am not sure how to go about it (or if Max is the right place for it).

Part I: Create a Template Waveform
First I want to generate a template waveform. The procedure is to split a period's worth of samples into a defined number of equal segments (say, 5), and to then draw curves from one segment to the next. So, for each segment, it will first pick a curve type (say exponential, linear, inverse expon, etc.), then pick a target amplitude, and finally draw a curve from 0 to the target amplitude at the time location of segment 1. Then it repeats for each segment, picking up from the last amplitude, heading for the new amplitude of segment 2 at the time location for segment 2.

Part II: Modify the Template Each Time It's Repeated
To generate sound using the waveform I want to not just loop it as is, but to modify it slightly each time it's repeated. For example, given a template waveform (think a simple sine wave), I want shift the original "midpoint" of the template to a new point offset from the center. So, with the sine wave, I want to take everything in the wave from 0 to periodlength/2 and redraw it to fit within, say, periodlength/3. I then want to take everything in the original sine from periodlength/2 to its end and redraw it in periodlength/3 to the end. In other words, I'll take the first half of the waveform and shrink its length to fit within one third of a new wave, and then take the second half of the waveform and stretch it to fit within the last two thirds of the new wave.

I did this long ago in C in a non real-time system. But I'd like to try implementing this in a real-time system that allows me to drive the creation of the template and how that new midpoint shifts based on incoming information.

Is something like this possible? Any pointers on where to start?

Here's an image to help illustrate what I mean. In Part I you see a sample waveform drawing like I describe. In Part II you see an example of the midpoint shifting, taking the first half of the wave and shrinking into the first third, and the second half into the last 2/3rds.

Roman Thilenius's icon

part I:
loadbang uzi expr peek buffer. dacota for "segments.

part I:
possible, but more difficult.
if your wavetable creation is based on expr->peek->buffer, realtime manipulation of the waveform(s) will be slow, and it will cause dropouts and clicks.
it is not much better with loading files from disk, or with writing to buffers using the record object.

to come around this, you would have to apply windowing™ to the whole oscillator DSP. that means using 3 such wavetable oscillators in parallel, so that you can modulate #1, while #2 is playing/currently read off.

i would design it different. for example you could use an extra buffer for each of these segments and just add their outputs. "silence, sine" for buffer a and "saw, silence" fpr buffer b.
this way could easily apply distortion (using kink~ or using a signal scaling algorithm), or just chance the gain of one segment using a *~ – without the requirement to change the data in the buffer objects.

-110

Roman Thilenius's icon

i just see that you (also) ant to change the lenghts/frequencies of the waves. this makes it a bit too complicated to do all at once IMO.
first get an oscillator running by reading from different buffers, using index~ or driven by phasors.

for exactly that example in your picture you could also create that in realtime, use index and then running 1,2,3,4,5 (signal) through the thirdparty external [expr~]. last step will be to divide the output of it /the amount of signals counted by the triggering index~.

the expression would look about this (in quasi code):

expr~ ($i1 < 250) * (formula for $i1-to-cosine here) + (($i1 < 500)*($i1>250)) * (formula for $i1-to-someotherwaveform here) + ....

the first input samples of values 1-250 will be translated to a waveform of your choice, and incoming signal values between 251 and 500 will be translated to another waveform.

you can now easily change the timepoint "sample # 251" by inputting them into the expression.
if you change it to 300, the first waveform will be stretched and the second one compressed.
when oyu use signals to change the breakpoints, and inpolate to new values by line~ , it will work the most possible click- and pop-free.

maybe it will be easier - or more flexible – to do it with single objects. i am not even sure if expr~ exists for max 6 (or for windows).

-110

mixwhit's icon

roman, thanks so much for the suggestions. i'll chew on these a bit and write back once i've tried them out.

unfortunately, while I did find a copy of expr~ and it runs on Max 6 (OS X), it seems very inefficient, sucking 100% CPU to add to cycle~ objs together.

mixwhit's icon

"dacota"? I can't find a reference for this.

Roman Thilenius's icon

dacota == repeat :)

sorry to hear that about expr~

but mabye it is a good idea anyway to use selector~, >=~, +~ and the like.

i am just not sure how you´d go about replacing log() or exp().

otoh, such curves wont produce the most musical waveforms anyway. ramps (saw or tri?) and layered harmonics (sine and cosine?) is where i would start.

leafcutter's icon
Max Patch
Copy patch and select New From Clipboard in Max.

this might get you started on the wave stretching part of your problem

mixwhit's icon

wow, thanks so much leafcutter! this definitely gives me ideas. appreciated.