Skip to content

Instantly share code, notes, and snippets.

@iitalics
Created April 13, 2025 21:34
Show Gist options
  • Select an option

  • Save iitalics/4210d64b413308c27ed4a3412d3c7bb3 to your computer and use it in GitHub Desktop.

Select an option

Save iitalics/4210d64b413308c27ed4a3412d3c7bb3 to your computer and use it in GitHub Desktop.
#include <inttypes.h>
#include <math.h>
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <jack/jack.h>
#include <jack/types.h>
static pthread_mutex_t io_mx;
typedef struct Synth {
float t;
float dt;
} synth_t;
const float AMPL = 0.08;
const float FREQ_L = 110.0;
const float FREQ_R = FREQ_L * 1.02;
static void synth_init(synth_t* syn, float f, uint32_t sr)
{
pthread_mutex_lock(&io_mx);
printf("Synth initialized at sample rate %d.\n", (int) sr);
pthread_mutex_unlock(&io_mx);
syn->t = 0.0;
syn->dt = 2.0 * f / (float) sr;
}
static float synth_step(synth_t* syn)
{
float t = syn->t;
syn->t += syn->dt;
if (syn->t > 1.0) {
syn->t -= 2.0;
}
return t * AMPL;
}
typedef struct App {
jack_client_t* jackc;
jack_port_t* playback_fl;
jack_port_t* playback_fr;
jack_nframes_t sample_rate;
int playback_registered;
int playback_connected;
synth_t synth_l;
synth_t synth_r;
} app_t;
static void app_init(app_t* app)
{
app->jackc = NULL;
app->playback_fl = NULL;
app->playback_fr = NULL;
app->playback_registered = 0;
app->playback_connected = 0;
app->sample_rate = 0;
}
static void app_free(app_t* app)
{
if (app->jackc) {
jack_client_close(app->jackc);
}
app->jackc = NULL;
app->playback_fl = NULL;
app->playback_fr = NULL;
app->playback_connected = 0;
}
static void app_register_callback(
jack_port_id_t id,
int registered,
void* arg)
{
app_t* app = arg;
jack_port_t* port = jack_port_by_id(app->jackc, id);
if (port == app->playback_fl) {
app->playback_registered |= 1;
}
if (port == app->playback_fr) {
app->playback_registered |= 2;
}
pthread_mutex_lock(&io_mx);
printf(
"[Callback] %s: %s\n",
registered ? "Registered" : "Deregistered",
jack_port_name(port));
pthread_mutex_unlock(&io_mx);
}
static void app_connect_callback(
jack_port_id_t a,
jack_port_id_t b,
int connected,
void* arg)
{
app_t* app = arg;
jack_port_t* port_a = jack_port_by_id(app->jackc, a);
jack_port_t* port_b = jack_port_by_id(app->jackc, b);
if (connected) {
if (port_a == app->playback_fl) {
app->playback_connected |= 1;
}
if (port_a == app->playback_fr) {
app->playback_connected |= 2;
}
} else {
if (port_a == app->playback_fl) {
app->playback_connected &= ~1;
}
if (port_a == app->playback_fr) {
app->playback_connected &= ~2;
}
}
pthread_mutex_lock(&io_mx);
printf(
"[Callback] %s: %s -> %s\n",
connected ? "Connected" : "Disconnected",
jack_port_name(port_a),
jack_port_name(port_b));
pthread_mutex_unlock(&io_mx);
}
static void app_shutdown_callback(
void* arg)
{
pthread_mutex_lock(&io_mx);
printf("[Callback] Shutting down.\n");
pthread_mutex_unlock(&io_mx);
}
static int app_srate_callback(
jack_nframes_t sr,
void* arg)
{
app_t* app = arg;
app->sample_rate = sr;
synth_init(&app->synth_l, FREQ_L, sr);
synth_init(&app->synth_r, FREQ_R, sr);
return 0;
}
static int app_bufsz_callback(
jack_nframes_t bs,
void* arg)
{
app_t* app = arg;
if (app->sample_rate == 0) {
printf("Error: buffer size callback: no sample rate.\n");
return 1;
}
float dt_ms = (float) bs * 1000.0 / (float) app->sample_rate;
pthread_mutex_lock(&io_mx);
printf("[Callback] Buffer size: %d (%.1f ms)\n", (int) bs, dt_ms);
pthread_mutex_unlock(&io_mx);
return 0;
}
static int app_process_callback(
jack_nframes_t nf,
void* arg)
{
app_t* app = arg;
if (app->sample_rate == 0 || app->playback_connected != 3) {
// not ready
return 0;
}
float* buffer_fl = jack_port_get_buffer(app->playback_fl, nf);
float* buffer_fr = jack_port_get_buffer(app->playback_fr, nf);
for (jack_nframes_t i = 0; i < nf; i++) {
*buffer_fl++ = synth_step(&app->synth_l);
*buffer_fr++ = synth_step(&app->synth_r);
}
return 0;
}
int main()
{
pthread_mutex_init(&io_mx, NULL);
int rv;
app_t app[1];
app_init(app);
jack_options_t open_opts = JackNoStartServer;
jack_status_t open_stat;
app->jackc = jack_client_open("JACK test", open_opts, &open_stat);
if (open_stat != 0) {
printf("Error: jack_client_open: %d\n", (int) open_stat);
goto fail;
}
jack_on_shutdown(app->jackc, app_shutdown_callback, app);
jack_set_port_registration_callback(app->jackc, app_register_callback, app);
jack_set_port_connect_callback(app->jackc, app_connect_callback, app);
jack_set_sample_rate_callback(app->jackc, app_srate_callback, app);
jack_set_buffer_size_callback(app->jackc, app_bufsz_callback, app);
jack_set_process_callback(app->jackc, app_process_callback, app);
pthread_mutex_lock(&io_mx);
printf("Connected to JACK.\n");
printf(" Client name: '%s'\n", jack_get_client_name(app->jackc));
printf(" Realtime: %d\n", jack_is_realtime(app->jackc));
pthread_mutex_unlock(&io_mx);
if (jack_activate(app->jackc) != 0) {
printf("Error: jack_activate\n");
goto fail;
}
app->playback_fl = jack_port_register(
app->jackc,
"playback_FL",
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput,
0);
if (!app->playback_fl) {
printf("Error: jack_port_register(playback_FL)\n");
goto fail;
}
app->playback_fr = jack_port_register(
app->jackc,
"playback_FR",
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput,
0);
if (!app->playback_fr) {
printf("Error: jack_port_register(playback_FR)\n");
goto fail;
}
const char** ports;
ports = jack_get_ports(app->jackc, NULL, NULL, JackPortIsInput);
if (!ports[0] || !ports[1]) {
printf("No ports available for playback.\n");
goto fail;
}
jack_connect(app->jackc, jack_port_name(app->playback_fl), ports[0]);
jack_connect(app->jackc, jack_port_name(app->playback_fr), ports[1]);
jack_free(ports);
for (;;) {
switch (getc(stdin)) {
case '\n':
case EOF:
goto bye;
}
}
bye:
printf("Bye.\n");
rv = 0;
goto cleanup;
fail:
rv = 1;
cleanup:
app_free(app);
return rv;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment