diff --git a/overlay/etc/skel/.fldigi/fldigi_def.xml b/overlay/etc/skel/.fldigi/fldigi_def.xml index f30f20d..acc0cc1 100644 --- a/overlay/etc/skel/.fldigi/fldigi_def.xml +++ b/overlay/etc/skel/.fldigi/fldigi_def.xml @@ -2144,7 +2144,7 @@ Operator call sign, if distinct from the station call MYCALL --> -3 +1 @@ -2156,19 +2156,19 @@ For compatibility with older versions --> -default +{{ET_PORTAUDIO_DEVICE}} -19 +{{ET_PORTAUDIO_INDEX}} -default +{{ET_PORTAUDIO_DEVICE}} -19 +{{ET_PORTAUDIO_INDEX}} diff --git a/src/et-portaudio/.gitignore b/src/et-portaudio/.gitignore new file mode 100644 index 0000000..015d2e8 --- /dev/null +++ b/src/et-portaudio/.gitignore @@ -0,0 +1,3 @@ +et-portaudio +*.o +conf.tmp diff --git a/src/et-portaudio/Makefile b/src/et-portaudio/Makefile new file mode 100644 index 0000000..c54137a --- /dev/null +++ b/src/et-portaudio/Makefile @@ -0,0 +1,14 @@ +CC = gcc +CFLAGS = -Wall -O2 +LDFLAGS = -lportaudio -lasound + +TARGET = et-portaudio +SRC = main.c + +all: $(TARGET) + +$(TARGET): $(SRC) + $(CC) $(CFLAGS) -o $(TARGET) $(SRC) $(LDFLAGS) + +clean: + rm -f $(TARGET) *.o diff --git a/src/et-portaudio/README.md b/src/et-portaudio/README.md new file mode 100644 index 0000000..43afd91 --- /dev/null +++ b/src/et-portaudio/README.md @@ -0,0 +1,26 @@ +# et-portaudio + +A utility program intended for script use to convert a supported +EmComm Tools ALSA audio device (i.e. `ET_AUDIO`) to a PulseAudio device. + +## Usage + +Returns a JSON object with the PulseAudio device details on success. + +```bash +./et-portaudio 2>/dev/null +{ + "device": "USB Audio Device: - (hw:1,0)", + "index": 6 +} +``` + +If no supported device is found or on error, the utility exits with an +exit status of 1. + + +## Build + +``bash +make +``` diff --git a/src/et-portaudio/gen-fldigi-cfg.sh b/src/et-portaudio/gen-fldigi-cfg.sh new file mode 100755 index 0000000..34346bf --- /dev/null +++ b/src/et-portaudio/gen-fldigi-cfg.sh @@ -0,0 +1,49 @@ +#!/bin/bash +# Author : Gaston Gonzalez +# Date : 1 October 2025 +# Purpose : Temporary script to fast-fail dynamic fldigi config for PnP +# +# NOTE: +# +# THIS IS JUST USED DURING DEVELOPMENT. AN XML PARSER WILL BE USED TO MODIFY +# THE FLDIGI CONFIG FOR THE FINAL PNP SOLUTION. SO THAT USER CHANGES CAN STILL +# BE PERSISTED. + +CMD=et-portaudio +PORT_AUDIO_CONF=conf.tmp +FLDIGI_CONF_FILE="${HOME}/.fldigi/fldigi_def.xml" + +# clean up temporary config that may exist +[[ -e ${PORT_AUDIO_CONF} ]] && rm ${PORT_AUDIO_CONF} + +[[ ! -e ${CMD} ]] && make +./${CMD} 2>/dev/null > ${PORT_AUDIO_CONF} + +if [[ $? -ne 0 ]]; then + echo "No supported ET_AUDIO device found" + exit 1 +fi + +DEVICE=$(jq -r -e '.device' ${PORT_AUDIO_CONF}) +if [[ $? -ne 0 ]]; then + echo "Error getting PulseAudio device name for ET_AUDIO device" + exit 1 +fi + +INDEX=$(jq -r -e '.index' ${PORT_AUDIO_CONF}) +if [[ $? -ne 0 ]]; then + echo "Error getting PulseAudio device index for ET_AUDIO device" + exit 1 +fi + +et-log "Copying fldigi template..." +cp -v ../../overlay/etc/skel/.fldigi/fldigi_def.xml ~/.fldigi/ + +et-log "Updating PortAudio devices..." +sed -i "s|^.*|1|" ${FLDIGI_CONF_FILE} + +sed -i "s|^.*|${DEVICE}|" ${FLDIGI_CONF_FILE} +sed -i "s|^.*|${INDEX}|" ${FLDIGI_CONF_FILE} + +sed -i "s|^.*|${DEVICE}|" ${FLDIGI_CONF_FILE} +sed -i "s|^.*|${INDEX}|" ${FLDIGI_CONF_FILE} diff --git a/src/et-portaudio/main.c b/src/et-portaudio/main.c new file mode 100644 index 0000000..76aabfc --- /dev/null +++ b/src/et-portaudio/main.c @@ -0,0 +1,89 @@ +/* + * Author : Gaston Gonzalez + * Date : 1 October 2025 + * Purpose : Maps an ET_AUDIO ALSA device to a PortAudio device. + * + * This utility enumerates all ALSA audio devices using PortAudio + * to identify a currently plugged-in ET_AUDIO device. If a matching + * device is found, it generates a JSON object on STDOUT containing: + * + * - PortAudio device name + * - PortAudio device index + * + * Exit Status: + * 0 - ET_AUDIO device found and JSON output generated successfully + * 1 - ET_AUDIO device not found + */ + +#include +#include +#include +#include "portaudio.h" + +#define PROC_ALSA_PATH_FMT "/proc/asound/card%d/id" + +// Read the ALSA card ID from the /proc filesystem +int get_alsa_card_id(int card, char *id_buf, size_t buf_size) { + char path[64]; + snprintf(path, sizeof(path), PROC_ALSA_PATH_FMT, card); + FILE *f = fopen(path, "r"); + if (!f) return -1; + if (!fgets(id_buf, buf_size, f)) { + fclose(f); + return -1; + } + id_buf[strcspn(id_buf, "\n")] = 0; + fclose(f); + + return 0; +} + +int main(void) { + PaError err; + int i, numDevices; + const PaDeviceInfo *deviceInfo; + const PaHostApiInfo *hostApiInfo; + int found = 0; + + err = Pa_Initialize(); + if (err != paNoError) { + fprintf(stderr, "PortAudio error: %s\n", Pa_GetErrorText(err)); + return 1; + } + + numDevices = Pa_GetDeviceCount(); + if (numDevices < 0) { + fprintf(stderr, "No audio devices found"); + Pa_Terminate(); + return 1; + } + + for (i = 0; i < numDevices; i++) { + deviceInfo = Pa_GetDeviceInfo(i); + hostApiInfo = Pa_GetHostApiInfo(deviceInfo->hostApi); + + // Only show ALSA devices + if (hostApiInfo && strcmp(hostApiInfo->name, "ALSA") == 0) { + int card = -1, device = -1; + + // Extract ALSA card and device (hw:X,Y) + if (sscanf(deviceInfo->name, "%*[^()](hw:%d,%d)", &card, &device) == 2) { + char card_id[64]; + if (get_alsa_card_id(card, card_id, sizeof(card_id)) == 0) { + + // Only print if card ID matches ET_AUDIO + if (strcmp(card_id, "ET_AUDIO") == 0) { + found = 1; + printf("{\n"); + printf(" \"device\": \"%s\",\n", deviceInfo->name); + printf(" \"index\": %d\n", i); + printf("}\n"); + } + } + } + } + } + + Pa_Terminate(); + return found ? 0 : 1; +}