Writing alternating single cycle-ish waveforms to disk
I am writing an app that requires writing square waves of two different frequencies to an audio file. The audio file alternates these frequencies frequently, and at very precise times - often just a few cycles (2 cycles of the lower frequency (1300 Hz), 4 cycles of the higher frequency (2100 Hz)) at a time.
No big deal so far - I can just peek~ the samples for single cycles of the two waveforms into a buffer~. The challenge though is that it turns out that I don't need *exactly* 2 cycles or 4 cycles of the waveforms at a time, but fractional amounts - like 2.75 cycles and 3.75 cycles for example. What this does is to create discontinuities in the resulting waveform of the audio file when these partial cycle waveforms are appended to each other.
So how can I write say, 2.75 cycles of 1300 Hz, then 3.75 cycles of 2100 Hz, then 2.75 cycles of 1300 Hz to a buffer~, but have the result be a continuous, "smooth", audio file?
OK, so almost 14 years later, here I am responding to myself - turns out that I never really did figure out how to do the original task, and I now find myself on a different project but with essentially the same challenge: writing alternating chunks of two different frequencies to a buffer~. I am generating FSK like tones with a 300 baud rate, or 300 "bits" of information / second. Each "bit", 0 or 1, is represented by one of two frequencies, 1325 Hz and 2088 Hz, respectively.
I've taken a shot at it below, but it's not working, I think because the delay of only 3.333 msecs between bit transitions is too fast for Max, or perhaps something with the scheduler thread or something. Do I need to do this in gen~? Or is there another way within Max itself?
here is calculated number of cycles for each frequency with given length of 3.333333 ms

4.416666 cycles of 1325 Hz and 6.96 cycles of 2088 Hz
now you can

then add as many copies of each buffers above into final one.
there are several externals that can do that, append buffers.
P.S.
I forgot that buffer can't fill partial waveforms, sorry.
to make this work one would have to crop buffer
after filling it up, but that is also problematic
because of rounding to samples.
Thanks Source Audio - and yes, additionally even if it could fill partial waveforms, the challenge I anticipated would be that the resulting waveform would not be "smooth" when the chunks are appended to each other.
Still, an approach I may take is to manually create .wav files (in an external editor) of the appropriate lengths, and then import those into buffer~ objects. And then see if the appended chunks manage to work sufficiently...
I'd also still love to find a way to do it natively within Max though - seems like it should be do-able. And happy to move to gen~ if necessary - I need an excuse to finally learn it at least a little bit after all these years!
is your new application still about square waves?
No just sine waves Roman, but yes another one of my "Hack" programs, reverse engineering tape-save formats from old gear! This time: the Roland MC-8. :-)
ok, understood. because for rects that "smooth" issue should not come to play. (the only thing with those is the limits the samplingrate causes. all transitions will have ~0.5 samples timing jitter...)
@Source Audio, what externals did you have in mind for appending buffer~'s? I can do it manually using peek~, etc... but if there's an easier option would be nice....
I have used modified el.buffet~ from Eric lyon's Potpourri
for buffer manipulation since years,
but had to modify and recompile own version of it
that supports samples as values and more then 2 channels
some years ago.
Also added more functions or removed some.
Original build produces crashes, last versions were updated by Isabel Kaspriskie
but to be honest, also that collaboration did not form that external to really fully workable build.
also new build has no changes in el-buffet external.
If you want I can post my Mac version that can use samples,
but I have no time to fully document all differences to original at the moment.
In fact I did not finish it completely, some things need to be done yet.
But at least you can have example showing how to copy, overdub, merge,
crossfade etc between buffers.
the uzi/expr/peek~ approach is straightforward normally, while offering the highest possible flexibility.
unless you want to create a ramp or a sine, then modern max has one-click solutions (like "sin" to a buffer) which beats custom spaghetti code by miles.
Thanks so much Source Audio for all the info, will take a look!
And thanks also Roman, and yes re: "sin" message to buffer~, but the problem with that is that I need sample level precision to define how long the buffer~ content is, for my use-case.
3.333333333 ms is 147 samples at 44.1 Khz
you can not create buffer with multiple of that chunk size and fill it with frequency
of 1325 or 2088 Hz using sin or whatever message.
yeah, the problem lies in the math itself. otherwise it is no big deal to only allow rounded sample values as length input in the waveform generator.
maybe at the "breaks", when they theoretically lay between samples, one wants to add some interpolation? you could get that by upsampling the audio part, for example.
Thanks again to you both. Yeah I'm going to see if simply abutting the different frequency chunks, even if there's a discontinuity and the audio is not "smooth", matters or not for this application. It may not, which will makes things simpler!
That is a good point.
if you really need continuous waveform, one would have to
analyse last nn samples from previous freq
and then offset start phase of the 2nd to match.
woud also not be such big deal ....