Skip to content

Instantly share code, notes, and snippets.

@Daniel-Abrecht
Last active October 31, 2021 19:37
Show Gist options
  • Save Daniel-Abrecht/e5481ad80715499d7cbca024a8f2a03c to your computer and use it in GitHub Desktop.
Save Daniel-Abrecht/e5481ad80715499d7cbca024a8f2a03c to your computer and use it in GitHub Desktop.
Makes all alsa devices available to jack using alsa_in and alsa_out programs. Polls alsa hw devices every 5 seconds to spawn / kill alsa_out programs as needed.
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <spawn.h>
#include <alloca.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <assert.h>
#include <stdbool.h>
#include <alsa/asoundlib.h>
struct device_entry {
struct device_entry* next;
int card, dev, stream;
char* desc;
pid_t pid;
};
struct device_entry* device_list = 0;
bool running = true;
void on_card_removed(struct device_entry* entry){
printf("on_card_removed: %c %s\n", entry->stream == SND_PCM_STREAM_PLAYBACK ? '>' : '<', entry->desc);
if(entry->desc)
free(entry->desc);
entry->desc = 0;
if(entry->pid){
kill(entry->pid, SIGTERM);
if(fork() == 0){
sleep(2);
kill(entry->pid, SIGKILL);
_Exit(0);
}
}
}
void on_card_added(struct device_entry* entry){
char name[30];
snprintf(name, sizeof(name), "hw:%d,%d", entry->card, entry->dev);
printf("on_card_added: %c %s\n", entry->stream == SND_PCM_STREAM_PLAYBACK ? '>' : '<',entry->desc);
int pid = 0;
char* bin = "alsa_out";
if(entry->stream == SND_PCM_STREAM_CAPTURE)
bin = "alsa_in";
if(posix_spawnp(&pid, bin, 0, 0, (char*const[]){bin, "-d",name,"-j",entry->desc,0}, 0) == 0){
entry->pid = pid;
}else{
perror("posix_spawnp failed");
}
}
void update_card(struct device_entry*** lit, int card, int dev, int stream, const char* desc){
struct device_entry** it = *lit;
struct device_entry* entry = *it;
while(entry && entry->card < card && entry->dev < dev && entry->stream < stream){
struct device_entry* old = entry;
*it = entry = old->next;
on_card_removed(old);
free(old);
}
bool new = false;
bool changed = false;
if(!entry || entry->card != card || entry->dev != dev || entry->stream != stream){
struct device_entry* enew = calloc(1, sizeof(struct device_entry));
enew->card = card;
enew->dev = dev;
enew->stream = stream;
enew->next = entry;
entry = enew;
*it = enew;
changed = true;
new = true;
}
*lit = it = &entry->next;
if(entry->desc && desc && strcmp(entry->desc, desc))
changed = true;
if(!changed)
return;
if(!new)
on_card_removed(entry);
entry->desc = strdup(desc);
on_card_added(entry);
}
void check_cards(void){
snd_ctl_card_info_t *info = 0;
snd_ctl_card_info_alloca(&info);
snd_pcm_info_t* pcminfo = 0;
snd_pcm_info_alloca(&pcminfo);
struct device_entry** it = &device_list;
for(int card=-1;;){
{
int ret = snd_card_next(&card);
if(ret){
fprintf(stderr, "snd_card_next failed: %s\n", snd_strerror(ret));
break;
}
}
if(card < 0)
break;
char name[32];
snprintf(name, sizeof(name), "hw:%d", card);
snd_ctl_t* handle=0;
{
int ret = snd_ctl_open(&handle, name, 0);
if(ret){
fprintf(stderr, "snd_ctl_open(\"%s\") failed: %s\n", name, snd_strerror(ret));
goto next_card;
}
}
{
int ret = snd_ctl_card_info(handle, info);
if(ret){
fprintf(stderr, "snd_ctl_card_info(\"%s\") failed: %s\n", name, snd_strerror(ret));
goto next_card;
}
}
for(int dev=-1;;){
{
int ret = snd_ctl_pcm_next_device(handle, &dev);
if(ret){
fprintf(stderr, "snd_ctl_pcm_next_device(\"%s\"): %s\n", name, snd_strerror(ret));
break;
}
}
if(dev < 0)
break;
for(int i=0; i<2; i++){
static_assert(SND_PCM_STREAM_PLAYBACK < SND_PCM_STREAM_CAPTURE);
const int stream = ((int[]){SND_PCM_STREAM_PLAYBACK,SND_PCM_STREAM_CAPTURE})[i];
snd_pcm_info_set_device(pcminfo, dev);
snd_pcm_info_set_subdevice(pcminfo, 0);
snd_pcm_info_set_stream(pcminfo, stream);
{
int ret = snd_ctl_pcm_info(handle, pcminfo);
if(ret){
if(ret == -ENOENT)
continue;
fprintf(stderr, "snd_ctl_pcm_info(\"%s,%d\"): %s\n", name, dev, snd_strerror(ret));
continue;
}
}
char desc[30]; // Jack doesn't seam to be able to handle more...
*desc = 0;
snprintf(desc, sizeof(desc), "hw:%d.%d\t%s\t%s",
card, dev, snd_ctl_card_info_get_name(info), snd_pcm_info_get_id(pcminfo));
update_card(&it, card, dev, stream, desc);
}
}
next_card:
snd_ctl_close(handle);
}
while(*it){
struct device_entry* entry = *it;
*it = entry->next;
on_card_removed(entry);
free(entry);
}
}
void cleanup(void){
struct device_entry* entry = device_list;
while(entry){
struct device_entry* next = entry->next;
on_card_removed(entry);
free(entry);
entry = next;
}
device_list = 0;
}
void set_quit(int signo){
(void)signo;
running = false;
}
int main(){
atexit(cleanup);
signal(SIGCHLD, SIG_IGN);
signal(SIGTERM, set_quit);
signal(SIGINT, set_quit);
signal(SIGHUP, SIG_IGN);
while(running){
check_cards();
snd_config_update_free_global();
sleep(5);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment