LiquidSFZ is a library to load and replay sample based instruments in .sfz format. If you want to use libquidsfz in your own project, use the LiquidSFZ::Synth class.

A Simple Example

// Compile this example with:
// $ g++ -o example `pkg-config --libs --cflags liquidsfz`
#include <liquidsfz.hh>
main (int argc, char **argv)
if (argc != 2)
printf ("usage: %s <sfz_filename>\n", argv[0]);
return 1;
synth.set_sample_rate (48000);
if (!synth.load (argv[1])) // load sfz
printf ("%s: failed to load sfz file '%s'\n", argv[0], argv[1]);
return 1;
// start a note
synth.add_event_note_on (0, 0, 60, 100);
// render one block of audio data
float left_output[1024];
float right_output[1024];
float *output[2] = { left_output, right_output };
synth.process (output, 1024);
// at this point we would typically play back the audio data
for (int i = 0; i < 1024; i++)
printf ("%d %f %f\n", i, left_output[i], right_output[i]);

Using multiple threads

The LiquidSFZ API, unless otherwise documented, doesn't use locks to make the API thread safe. This means that you are responsible that only one API function is executed at a time.

So you must not call a function (i.e. Synth::process) in thread A and another function (i.e. Synth::load) in thread B so that both are running at the same time. Instead you need to either use a mutex to ensure that only one function is running at a time, or use other synchronization between the threads A and B to ensure that only one thread uses the Synth instance at any point in time.

Note that if you have more than one Synth instance (object), these can be treated as isolated instances, i.e. different instances can be use concurrently from different threads.

The audio thread

Most applications will have one single real-time thread that generates the audio output. In this real-time thread, only some of the API functions provided by liquidsfz should be used, to avoid stalling the audio thread (for instance by waiting for a lock, allocating memory or doing file I/O). Each of the functions on this list is implemented wait-free (which means there is an upper bound on the number of steps required).

Typically these restrictions mean that at the beginning of your application, you setup the Synth object by loading an .sfz file, setting the sample rate, number of voices and so forth. Eventually this is done, and you start a real-time thread that does audio I/O. Since you must ensure that only one thread at a time is using the Synth object, you would then only use the functions of this list in the audio thread and no longer use any other function from any other thread.

If you need to access one of the other functions (for instance for loading a new .sfz file using Synth::load), you would ensure that the audio thread is no longer using the Synth object. Then the setup phase would start again, loading, setting sample rate and so forth, until you can restart the audio thread.

Callbacks from load

It is possible to request progress information for Synth::load by using Synth::set_progress_function. If you enable this, the progress function will be invoked in the same thread that you called Synth::load.

Callbacks from the logging framework

If you setup a custom log function using Synth::set_log_function, callbacks will occur in the threads that you call functions in. For example, if you Synth::load during initialization, you will get callbacks in the thread you started load in.

Messages that are relevant to end users are errors, warnings and info messages. For these log levels, we guarantee that such log messages are never produced from functions that can be run in the audio thread (real-time thread). These log messages are enabled by default.

This means that the callback set by Synth::set_log_function doesn't need to be realtime safe, as these callbacks only occur in non-rt-safe functions such as Synth::load.

Messages that are relevant to developers use the log level debug. These can occur from any thread, including real-time safe functions like Synth::process. Debug messages are only relevant for developers, so in almost every case you should keep them disabled, which is the default. If you enable debug messages using Synth::set_log_level, you either need to provide an realtime safe logging callback or accept that during development, debug messages will break realtime capabilty.

SFZ Synthesizer (main API)
Definition: liquidsfz.hh:133
void add_event_note_on(uint time_frames, int channel, int key, int velocity)
Add a note on event.
void process(float **outputs, uint n_frames)
Synthesize audio.
bool load(const std::string &filename)
Load .sfz file including all samples.
This header contains the public API for liquidsfz.
void set_sample_rate(uint sample_rate)
Set sample rate of the synthesizer.