sfrecord~ creating inaccurate integer sample values

Emmett Palaima's icon

Hi, I'm having an issue where sfrecord~ seems to be producing files with slightly inaccurate integer values, or at least inaccurate as compared to what comes out of my interface. Ordinarily these would be definitely be too small to be audible, but I'm working on a project where an audio stream is used to control 64 1-bit audio sources, with each bit of a 4-channel 16-bit file representing an on/off signal for a speaker, hence each bit needs to be accurate: 

For composition I'm sending 16-bit audio via SPDIF to a Teensy microcontroller that runs the speaker array, which I have working well. For installation purposes, I'd like to be able to record a performance as an audio file on an SD card for embedded playback via the Teensy's built-in SD card reader.

The problem arises in that the sfrecord~ produces a different 16-bit integer value for a given input than dac~ into my interface. For example:

"1.0 / 32768.0" (binary 1) registers as 0001 (hex sample) via my interface, but shows up as 0000 when recorded via sfrecord~

"-1" registers as 8000 via my interface, but shows up as 8001 in the output from sfrecord~

I've made a patch which demonstrates this here:

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

I get this is a pretty esoteric issue, but this project is something I've been working extremely hard on and any help is much appreciated. For the time being I've figured out recording by routing audio into Ableton (which produces the correct integer values when exported), but this process is really clunky and I'd like to encapsulate it all in a streamlined max patch so that I can share the instrument I've created with other composers. I'm entirely there except for sfrecord~ working incorrectly. Seems like the object is doing some extra processing somewhere rather than directly recording the values at its input. 

Let me know if there's something I'm doing on my end that might be causing this, or if you have any suggestions for a workaround. I'd also love to take a look at the source code for sfrecord~ if possible, since that could shed a lot of light on what's happening here, and I could potentially modify it to produce an accurate output. 

Emmett Palaima's icon
Jean-Francois Charles's icon

Did you try using [record~] to a [buffer~] and saving the buffer?
(There is a known bug in Max with sfrecord~, there was a recent thread on that in the forum. Let's hope c74 comes up with a fix in an upcoming update.)

Emmett Palaima's icon

Hey, thank you for the suggestion. I tried saving using record~ and buffer~ and that does indeed produce the correct output values. Potential material for a workaround.

However, there are several reasons that buffer~ isn't good for this purpose.

It cannot dynamically allocate memory, so its size has to be set in advance. This means I either have to know how long the recording will be, which in a lot of cases I don't, or just choose an arbitrarily large number. The latter option might be acceptable (though possibly very taxing on memory usage in cases where I am recording a long file), except that when it export to a file it exports the entire length of the buffer, rather than just the section that was recorded to. Therefore, I end up with my recording and a huge chunk of silence at the end. Resizing the buffer erases its contents so there's really no way around this.

I could work around this by post-editing in Audacity, or I could potentially hack together some kludgey system where I fight against the fact that the buffer~ object erases and/or resizes itself whenever it reads new data. For lack of a better term, both these options suck. My goal here is to develop a stable system I can share with other people, which requires a level of dependability and ease of use that neither of these options offer. In any event, they are both worse than the Soundflower > Audacity workaround I am already using.

I really just need to find a way to sfrecord~ to work as expected.

Emmett Palaima's icon

After doing some research and contacting Cycling 74 support directly, I'm realizing that this may be the result of a built-in dithering process performed by sfrecord which isn't exposed to the user. When performing a workaround by recording into a DAW I always made sure to turn dithering off.

Does anyone know whether sfrecord~ actually performs any dithering or any other pre-processing on the recorded output, and whether or not there is any way to disable this feature?

Asher's icon

not a direct answer to your question, but you can actually resize a buffer without clearing it using the crop message (you'd maybe need to convert ms to samples in your case)

Emmett Palaima's icon

Okay, so I tried putting together a workaround using buffer~ and record~. It works, except that buffer~ adds zero value sample to the end of the exported file, like this:

8000 8000 8000 8000 8000 8000 8000 8000
0000

I've tried cropping the buffer to different sizes in ms, this appears to have nothing to do with the actual values stored in the buffer or where the buffer ends, but instead is just thrown in as a byproduct of the saving process. Again, something that under normal circumstances might be inaudible, but in my application can potentially cause problems.

Any idea what's up with this?

Heres a screenshot of the patcher I'm using for the workaround:

Here is the patcher itself:

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

Asher's icon

Hi, I would avoid using sfrecord for this type of file operation. It sounds like what you need is sample accurate so I would instead iterate through the buffer with uzi and peek.

I double checked the .dat output of below patch, and as long as I use the 'import raw' option in Audacity .with the settings that match the output (SR 44100, PCM 16bit, Mono audio), it reads the correct amount of samples.

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

Emmett Palaima's icon

So the number of samples being detected by Audacity is not at all what I'm worried about. I don't doubt that the number of samples read will be the same in both Max and Audacity.

What I'm concerned with is the fact that I'm giving the buffer a constant value in my test patch, and that the exported output contains a random sample at the end of a different value.

I am giving the buffer~ a constant value of -1 during recording, which shows up as 8000 in hex. When I export the raw file there is a random 0000 sample inexplicably added at the end. This is the problem I am referring to, not the number of samples.

The uzi thing could be a potential workaround, although it is really, really ugly 😅. We're talking about a sample count that is easily in the millions, and it doesn't seem like producing over a million bangs for a simple file write operation is a very good idea.

Emmett Palaima's icon

I thought this might be the crop overshooting the recorded section, but this happens regardless of where the buffer is cropped. Exporting a file using the buffer~ object just automatically adds a junk sample at the end. Great... 🤦‍♂️

Really didn't expect the simple task of getting a sample accurate audio output to be so difficult 😅