Writing alternating single cycle-ish waveforms to disk

Dan Nigrin's icon

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?

Dan Nigrin's icon

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?

Max Patch
Copy patch and select New From Clipboard in Max.

Source Audio's icon

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.

Dan Nigrin's icon

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!

Roman Thilenius's icon

is your new application still about square waves?

Dan Nigrin's icon

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. :-)

Roman Thilenius's icon

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...)

Dan Nigrin's icon

@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....

Source Audio's icon

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.

Roman Thilenius's icon

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.

Dan Nigrin's icon

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.

Source Audio's icon

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.

Source Audio's icon
Roman Thilenius's icon

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.

Dan Nigrin's icon

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!

Source Audio's icon

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 ....