real sample rate conversion of buffer~
Hi,
I want to up or downsample a buffer~ destructively. There seems to be no native way. I want it to be always 32000 hz. I want to load soundfiles with any sr and always have them converted to 32000hz.
I don't want to use an external tool like sox. For what I have read, it's applying a steep lowpass at 16000hz and dropping or adding samples. But how to do that in a calculation with the floats of a buffer?
For example 44,1khz to 32khz. How to drop every 1,378125 th sample?
Any example is helpful. I can translate that for my needs.
Thanks,
O.
if you mean what you said then the answer is "no", because a buffer always has the samplingrate of the patcher and runtime it is in.
changing samplerate within one signal chain will mean to "stretch" the sample. the basi strategy is to upsample something like *10 or *16 and then downsample again and interpolate a bit.
a lowpassfilter is optional.
poly~ does it all in realtime since max 6... but as you know, only with multiples of 2. 44.1 to 32 is a bit mor ecomplicated if you want perfect sound. i would not do it in max/msp...
Hey Roman,
I don't want to do that in realtime, more like processing a buffer. I don't think it depends on the environment, it's just a property of the loaded soundfile.
a property of the soundfile which is, if i am not mistaken, ignored when loading the file, unless you can convert the samplerate on-the-fly (like you can "import" mp3 to sfplay for example)
and if you´d change the header of the soundfile - that is of course possible - the audio would change its pitch, which is no what you are looking for ;)
buffer~ always adopt the sr of the loaded file, I think the playback objects do the conversion on the fly to fit the environment.
I could convert in java if I could figure out a way to get a proper audio input stream from the float array of the buffer?
java sounds doable, there should be vast libraries for stuff like that, maybe even with antialiasing. but java is not my world and so i am clueless. you can still use sfinfo~ with it to find out if and what conversion is required.
Almost OT, but here is a website with some interesting sample-rate-conversion comparison results of different software from open-source to commercial, including matlab, fscape (which is java-based) and others. You may get some inspiration here.
Despite this threat being already 3 years old I just stumbled across it and want to share my attempt.
I use the sr message of the buffer~ to shift its samplerate to then record a playback of that buffer to another one. The trick is to use poly~ with its upsampling argument to make that recording super fast (I use a value of up 256)
I hope you get the idea of the patch, since it might be a bit cryptic with its numbers and calculations used.
Save the first posted patch as "sr_conversion" (no quotation marks) so that poly~ can find it.
Poly~ subpatch:
Mainpatch:
Old thread - but I have a similar need to the original poster. I need to non-realtime convert a user-selected audio file of any format/sample rate, to a very specific output format (24Khz, mu-law, 8bit, unsigned).
Suggestions for best approach in 2023? Ideally would be done in native Max, and not with Java, Python, or whatever, though I suppose Javascript would be do-able if necessary. My use case requires this to work in a standalone, and cross-platform as well.
Thanks in advance for tips and suggestions!
Along the lines of Javascript, wondering if this Node.js functionality might be helpful? Anyone have experience with it? https://www.npmjs.com/package/wavefile
ahoy dan,
destructive/non-interpolated, like 11olsen?
otherwise going from A over lcm(A,B) to B always works - in good quality, without any dithering science required.
in the case of 44.1 to 24 khz that would mean to first go up to a sampling frequency of 3528000 and then down to 24000.
one could use counters/index~ to copy every sample of the first buffer to the second buffer for 80 times, then lowpassfilter the content to match the 24khz spectrum, then copy the second buffer to the third buffer by reading out everything while calculating the mean (or rms) of every chunk of 147 samples and write the result in one new sample.
the math might be simple, but it will look a bit strange to build that kind of stuff with vanilla MSP objects.
can new versions of max export arbitrary samplingrates from buffer/record&co? if not, just ignore the rate setting, export to disk and change the header of the file later with some hex editor to the correct rate. (no, wait, you said standalone... but i think it can be done from max, too, only the "fileout" part will have to be be a .js job i guess)
The wavefile package seems to be limited to read wave as input format. But it can do the mu-law conversion. Only other tools I know of which can do it are sox or ffmpeg command line utilities.
Thanks to you both - actually, at this point I've simplified my task, and all I really have left to do is the sample rate conversion. Roman, your approach makes my head hurt! :-)
Ideally, I'd like to do interpolation (the audio will be used for musical purposes). I'm a DSP noobie - what does "A over Icm(A,B) to B" mean?
But yes, Max allows one to set the SR of a buffer~ now, and then save that to disk. So at least the stuff at the end I don't think I'd have to do....
Actually @11olsen, sox seems like it would make it pretty much trivial to do - did you try to use the shell object together with sox by any chance? I might give that a go first, before anything else.
Not in this combination, but I'm pretty sure you can make it work that way.
(i think for the 8 bit part all you have to do is to make sure that you have a bit of headroom, aka *~ 0.9 after normalization, the complicated part is the samplerate conversion)
lcm == least common multiple... for example the lcm of 44.1 and 88.2 is 88.2... so you can just divide or multiply the number of samples by an integer number, which avoids that antialiasing will be more complicated than simply taking the mean (which resulted in a linear interpolation)
the lcm of 50,000 and 24,000 is 600,000. so you can now reach 24 from 50 by multiplying and dividing using only integers.
it is like upsampling to (50,000*24,000) to avoid non integer relationships, just shorter.
as short as possible, to be exact.
here is a brain if xou need one: https://www.wolframalpha.com/input?i=LCM+%2850000%2C+24000%29&key=tplys
but now first good luck in avoiding using this way.
Thanks guys!
@11olsen - I'm trying, but so far no luck. In particular, what I'm trying to do is to not *install* SoX on my system, since this will need to be bundled in a standalone, so first trying to figure out where to put the SoX executable within the application bundle, and then figuring out the right way to both access it, and deliver it the appropriate message via the shell object. So far I've got the first part sorted (path to the shell executable), but not the second part (the actual message to it). I'll keep trying, and may post here if I get stuck.
@Roman - this is fantastic background, thank you! I need to educate myself a bit on this stuff. All very helpful!! And who knows, I may end up having to do it all within Max in the end...
to bundle executable binaries into max standalones is easy.
for example here I use qt-info to scan video files for frame rate.

as long as binary file is next to patch while editing in max
or next to mxf file in standalone it works.
On windows you need few backslashes, depending on
shell version used.
Thanks @Source Audio! That's similar to what I've tried so far, but can't get it working yet, probably something silly on my part. Will post later if I can't get it going to ask for help. And I'm using Jeremy Bernstein's shell, this one: https://cycling74.com/forums/shell
is it path problem or converter syntax ?
for path, try this, maybe it helps.
in case of command including in and out path + code
one hast to send all 3 paths in a single message,
like path to binary / code /path to in file / path to out file.
I think it's a path problem - and yes, I've been trying to include binary, command, in and out paths all together in one message. But looking at your example, I see that maybe I need to do it differently - I was using forward slashes, and not backslashes as your regexp introduces. Will try, thank you! (on my work machine at the moment so can't test fully).
Got it - my problem was not forward/backslashes (at least on Mac so far), but rather that I was enclosing the entire <binary> <command> <input file> <output file> in quotes, rather than enclosing each individual component in quotes, all within one message box. That worked! Here's the example, that does the SR conversion to 24k.

i´d like to add that for good results for upwards you´d still need to do sinc interpolation for non integer relations.
but i the end the results count, not the technique. :)
it is quite interesting how and since when different programs offer a perfect quality.
Thanks Roman - In this instance, upwards conversions are not likely to occur... And yes that site is very interesting!
Coming back to this, struggling a bit to get it working on Windows. This time it's definitely a path issue. I've reduced the patch down to this - this patch does nothing functional with sox per se as it's just a test, but on Windows, I get an error saying it can't find the sox executable; the path is trimmed to the point where the first space is in the original path to the sox executable.
If I change the message box to just [$2} instead of [$2 $1], it correctly sees the sox executable.
@Source Audio, above you said that I might need "a few backslashes" on Windows, but I've not been able to sort it out. What worked on Mac is definitely not working on Win!
patch I uploaded here on Jan 30 2023 | 5:21 pm
has backslashes formed path that works for me.
one has to form it so for each part of message to shell.
If you post your exact message as text, no matter what
kind of path form used, I will test it.
I am not sure if newest shell win version had this need to
fiddle with multiple backslashes fixed.
Next thing is - calling executable from dropbox,
which may be problematic ?
I don't think it's still necessary to reverse the slashes for shell Win version.
I found a patcher where I used sox and it seems to work if you make the whole command line a symbol and surround every item in escaped quotes.
"\"C:/PATH/sox -14.3.2/sox.exe\" \"E:/DM AGO.wav\" \"-r 22k\" \"E:/DM AGO out.wav\""
example patch
Thanks to you both.
@Source Audio: yes, I tried what you had posted previously, with the conversion to backslashes, and it did not work. I retested today with the new patch you just posted, and similarly it doesn't work - the space in the very first path name is not respected, and so the path is truncated at that point. Here's the text of the message generated by your patch:
"C:\\Users\\dnigrin\\Desktop\\My App 1.0\\sox.exe" "-r 24k" "C:\\Users\\dnigrin\\Desktop\\My App 1.0\\test.wav" "C:\\Users\\dnigrin\\Desktop\\My App 1.0\\output.wav"
And also don't worry about Dropbox, that was just temporary when I was testing before.
@11Olsen: thanks, your approach worked! That is, when I constructed the path manually in a message box. How do you go about getting those escaped quotes around each portion of the message in an automated way (i.e. for paths coming out of conformpath). And then an extra set of quotes around the whole thing Do I need some regexp wizardry or something?
Thanks again in advance!
Answering my own question - was able to do it with sprintf:
@DAN your tripple slash sprintf doesn't work here. This works for me:
I've added a little suggestion to auto-create the out file name..
Interesting - I thought you had to escape the backslash as well as the quote, but I guess not? At any rate, your version is working as well for me here, thank you.
And nice thing with these approaches is that they seem to be cross platform too...
And nice little hack with the regexp for the output file creation. I won't be using that for my use case (these are just test patches), but handy all the same!
Actually, take that back, it doesn't seem to work on the Mac side, but no big deal, I can accommodate that with two separate approaches based on current platform.
One last detail that maybe one of you has an answer for. For creating standalones with sox bundled, on Mac, it's easy to bundle the sox executable within the application bundle, and asking absolutepath to look for "sox" works just fine.
On Win though, since you've got to include the sox executable somewhere near the .mxf file, it works to include the sox.exe right next to it and look for "sox.exe", but then you're obligated to also include all of sox's associated .dll files next to it, and now my application folder containing my standalone .exe is a royal mess (yes I care about these silly things! :-)). I'd much rather just include the entire sox folder in my standalone's folder, and instead ask the standalone to look within there for the sox executable.
Doing the following doesn't seem to work though - any ideas?
can you reduce sox stuff to needed minimum ?
and bundle it into standalone...
I tried that - but then it gives warnings that it needs two different .dll's... and then I add those, and then it says it needs two more... and so on!
I think I will just use a sendapppath message to Max in the executable, and then fully construct a path to the sox folder next to the executable that way.
@DAN isn't this a use-case for the [standalone] attr "Make Application Folder Search Path"?
You should then be able put a sox folder into /support for example.
That would definitely work, but then I'd have the performance penalty on Mac as described in the info message for that attribute.... I suppose I could tweak the standalone's attributes upon app launch based on whether on Mac or Win? But probably just as easy for me to fully form the path as I mentioned above.
multiple ways possible, like in most of the times ;)
The beauty of Max! :-)
Turns out I was completely forgetting that those subfolders within the resources folder next to the .exe are *automatically* included in the standalone's path, irrespective of whether that attribute is enabled in the standalone object. So I just plopped the whole sox folder within resources/misc/ and presto, it works now!
Ok nice, thanks for the info.
by the way, to avoid security problems on MacOS,
you can convert using OS built-in afconvert instead
of sox.
https://ss64.com/osx/afconvert.html
here example - wave to 8bit 24k
Oh wow, I had not yet thought about the security stuff on the Mac. Would routine codesigning, etc.. not take care of the bundled sox executable and libraries? I haven't tried yet...
Either way, good to know that afconvert exists...
can't say, I am refusing apple codesigning games, and distribute my mac apps
only to customers which can handle it that way.
Haha - I can certainly understand that approach! I'll report back if I run into any troubles with the codesigning...
This thread has certainly strayed quite a bit from the original topic! :-)
If anyone is looking for sr conversion of a buffer: Alex Harker Hiss Tools Package contains an external "bufresample~". And I made an external "11ffload~" that can convert the samplerate while loading the audio file to the buffer~.
Thanks for those pointers! In my case, although I've only included sample rate conversion in the specific code above, I've actually got to do multiple transformations of the audio (mono conversion, normalization, bit reduction, and u-law conversion), and for that sox really does simplify the task quite a bit
Just in case this thread is found in the future based on the title ;-)