Libsndfile mismatch? on MacOS

Hello all,

in 2016 a student of mine laid the foundations for a voice analysis system The student has moved on and has not since worked with SuperCollider, but I have continued to develop the system, which includes about a dozen custom UGens.
One of them is GatedDiskOut, which is like DiskOut but with a gate input such that frames are added to its buffer only when the gate is open. It also removes the undocumented 32-input limit of DiskOut, and implements a mechanism that allows waiting for the last buffer to be written when GatedDiskOut_Dtor is invoked (the UGen is freed), using pthread. To build this UGen the compiler/linker needs to find both PThread and Libsndfile, and it seems to do so. The build instructions given by the student say that the versions of libsndfile used to build SC and this UGen should match, or it might not work.
This UGen still builds and works fine on Windows 10, which is where I live, but it has stopped working on MacOS. I think it stopped working with the arrival of /sc/platform/mac/lib/scUBlibsndfile.a, but I am not sure, and I now have only a Big Sur 11.6.4 machine to test on (with XCode 13.2.1). I can see from the file list.txt that this is the library to which the UGen GatedDiskOut.scx has been linked. I have tried going back to SC 3.11.2+BigSur and rebuilding, but the behaviour is the same (that version already contains scUBlibsndfile.a).
For recording, the client part of the app first creates a .wav file using Buffer.write(…), leaving the buffer open. The file gets created with a .wav header of 44 bytes, but after that, no data gets written to it any more (on MacOS, that is; on Windows, it still works). I have put traces into the .cpp file and it looks like sf_writef_float() is being called with what seems to be the right argument values at the right times. But the data do not make it into the file.
Any suggestions would be much appreciated, for why the disk-writing on Mac has stopped working in recent versions, or for how to debug this further.

sternsc

Hello @sternsc

We haven’t been using platform/mac/lib/scUBlibsndfile.a for building SuperCollider in a while… at least since 2019, but likely earlier. The current build instructions for SC support libsndfile installed with homebrew. I’d guess that the plugin should be linking with that as well.

Technically the 3.12.x releases use libsndfile built manually to maintain compatibility with macOS 10.13 and earlier, but I’m not sure if that makes much difference. My guess is that the scUBlibsndfile.a might not be compatible with recent OSs, so the first thing to try would be to make sure that the plugin links against the homebrew library.

I’m also not sure about the channel limit of DiskOut. I just tried creating a 64-channel file and it seemed to work (on macOS 12 using a recent develop build).

Thanks @MarcinP so much for your quick reply!

So, I tried first to link with the libsndfile.a from homebrew, but it is much smaller than scUBlibsndfile.a, and the linker complained about lots of missing symbols. I then linked with libsndfile.dylib from homebrew, which did not give any build errors, but at runtime the outcome was still that no data get written to the soundfile. The UGen’s calls to sf_writef_float() [inside an NRTLock] still return zero, when they should return the framecount written.

On the client side, just before the enclosing Synth is created, a Buffer.query() posts the correct values for the audio buffer. Maybe it is not a problem with libsndfile, but with something else. Sigh.

sternsc

one more thing - would I have to build all of SuperCollider locally, or the sc3-plugins locally, before I can build my own UGens? I have not done that. /sternsc

The nuances of linking go beyond my expertise, unfortunately. I have a hunch the problem might have to do with SC shipping one version of libsndfile, while the plugin is using another one (from homebrew)…

would I have to build all of SuperCollider locally, or the sc3-plugins locally, before I can build my own UGens? I have not done that.

Generally - not. You just need to use the version of sc source you’re using for linking in the same “Plugin API” version as the one you’re running, and we haven’t changed that since - I believe - 3.10 or earlier.

However, if it is about SC using one version of libsndfile and the plugin using another, I would try building SC (that should link against homebrew’s libsndfile), compiling the plugin so that it uses the homebrew library, and seeing if that works.

There’s no need to recompile sc3-plugins I believe (I don’t recall there are plugins that use libsndfile there).

hi again,

you wrote - “I have a hunch the problem might have to do with SC shipping one version of libsndfile, while the plugin is using another one (from homebrew)…”. Indeed, the file in the source code for 3.12.2, ./external_libraries/libsndfile/VERSION.txt contains 1.0.20, which is quite old; while homebrew installs libsndfile 1.0.31.

Some progress, though: I managed to link my .scx to /usr/local/lib/libsndfile.dylib as installed by homebrew, and now the UGen runs as intended (hooray!) - but only on the computer where it is built. When I copy it to an innocent Mac computer, as my end users will do, the system there complains that the .scx file is not signed/notarized. My app uses about a dozen custom UGens, so I guess I will have to go through the process described by Brian Heim in the wiki " Signing and Notarizing for macOS" and have them all signed.

thanks for your help,
sternsc

Yes, you’ll need to sign the application if you are planning to run it on other machines (thanks Apple!).

You’ll still need to resolve the linking though. Libsndfile gets copied to the application bundle as part of the “Install” build step. All the executables are changed so that they link with that bundled version, so you might consider doing that for your plugin as well (otherwise your users would need to install libsndfile from homebrew). I’m not an expert on this, but I think you can see how other disk ugens are linked using otool -L and than use install_name_tool to change path for libsndfile. (Both tools are present on macOS, at least when XCode command line tools are installed).

Again, to clarify:
We are NOT currently using bundled libsndfile - neither the the source in external_libraries, nor the binary in platform/mac/lib. SC releases and standard build instructions use libsndfile from homebrew.

just to report on some progress: on the Mac, I located libsndfile.1.dylib as supplied with SC in /Applications/SuperCollider.app/Contents/Frameworks, and managed to link my .scx to it. This seems to work fine. That’s good because this location should be the same for all users. For some reason, MacOS now does not complain about lack of notarization either, on an innocent machine with the latest OS version. So I am on track again! Thanks for your help!