Last active
October 31, 2021 19:37
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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