Jedidiah Barber's Personal Site



Ada Bindings for Libao, Libsndfile, PortAudio

Git repositories: aao, asndfile, portadao

29/7/2023

Why am I making a single article about language bindings for three different libraries? A few reasons. These three libraries are all quite small, they all cover similar and related functionality, and all the bindings were written quickly one after the other. But ultimately I just don't want to clutter the index up.

The three libraries in question:

It is commonly thought that it is easy to bind a C library to another language. This is not the case. Even for the thinnest of thin bindings and even when the other language is trying to make it easy, there are problems. Enumerations, preprocessor macros, and callbacks are all awkward and they are all involved here. Also, these are all thick bindings so things are even more fun. Nonetheless it is all done. With the sole noteworthy exception of the RIFF chunk API in libsndfile, but we won't talk about that.

Error codes have been converted to exceptions, initialize/shutdown functions have been rendered automatic, manual deallocations have been made obsolete, void pointers have been wrapped up, callbacks have been made available, and dot notation for invoking subprograms has been used extensively. Oh, and the rather odd command API for libsndfile has been tamed. Mostly. The array types for all the sound samples have also been defined such that conversions between the types used in each binding should be as painless as possible, without sacrificing the strong typing Ada is known for.

An excerpt of PortAudio code in C taken from an example program:

err = Pa_Initialize(); if( err != paNoError ) goto error; err = Pa_OpenDefaultStream( &stream, 0, 2, paFloat32, SAMPLE_RATE, 256, patestCallback, &data ); if( err != paNoError ) goto error; err = Pa_StartStream( stream ); if( err != paNoError ) goto error; Pa_Sleep(NUM_SECONDS*1000); err = Pa_StopStream( stream ); if( err != paNoError ) goto error; err = Pa_CloseStream( stream ); if( err != paNoError ) goto error; Pa_Terminate();

And the corresponding code snippet in Ada:

Saw_Stream.Open_Default (Input_Channels => 0, Output_Channels => 2, Format => Portaudio.Streams.Float_32_Format, Sample_Rate => 44100.0, Buffer_Frames => 256, Callback => Saw_Callback'Unrestricted_Access); Saw_Stream.Start; delay 4.0; Saw_Stream.Stop; Saw_Stream.Close;

More detailed and complete examples can be found in each of the binding repositories. Alternatively, the documentation for each library can be consulted. Aside from doing away with C-isms, the function names and behaviours have been more or less imported wholesale so the information should mostly carry over.

I'll admit, the only reason why I ended up creating the binding for libao is because I came across that library first and it really is that minimalist. But I have specific purposes in mind for the other two.