From 6d84cfee0653643007dbc3c519b941ba4d129051 Mon Sep 17 00:00:00 2001 From: Jonathan Hodgson Date: Sat, 13 Mar 2021 14:06:45 +0000 Subject: [PATCH] Copies megiaudioroute from sxmo utils package I don't want all the utils from sxmo, just this so I copied it. --- Makefile | 11 ++ README.md | 5 + megiaudioroute | Bin 0 -> 29240 bytes megiaudioroute.c | 386 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 402 insertions(+) create mode 100644 Makefile create mode 100644 README.md create mode 100755 megiaudioroute create mode 100644 megiaudioroute.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..506b08b --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +PREFIX = /usr/local + +.PHONY: install + +megiaudioroute: megiaudioroute.c + gcc -o megiaudioroute megiaudioroute.c -lX11 + +install: megiaudioroute + mkdir -p $(PREFIX)/bin + cp -f megiaudioroute $(PREFIX)/bin/megiaudioroute + chmod 755 $(PREFIX)/bin/megiaudioroute diff --git a/README.md b/README.md new file mode 100644 index 0000000..9d1bf00 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +This is a tool for setting up audio on phone calls + +Taken from the sxmo project. + + diff --git a/megiaudioroute b/megiaudioroute new file mode 100755 index 0000000000000000000000000000000000000000..84bb02db5721ec0e8ee1f22754c13df243def116 GIT binary patch literal 29240 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 Wv|MQjBE9~A24-yUXo%3@6#p9>M|GwE literal 0 HcmV?d00001 diff --git a/megiaudioroute.c b/megiaudioroute.c new file mode 100644 index 0000000..4a67507 --- /dev/null +++ b/megiaudioroute.c @@ -0,0 +1,386 @@ +/* + * Voice call audio setup tool + * + * Copyright (C) 2020 Ondřej Jirman + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * 2020-09-29: Updated for the new Samuel's digital codec driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0])) + +void syscall_error(int is_err, const char* fmt, ...) +{ + va_list ap; + + if (!is_err) + return; + + printf("ERROR: "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf(": %s\n", strerror(errno)); + + exit(1); +} + +void error(const char* fmt, ...) +{ + va_list ap; + + printf("ERROR: "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); + + exit(1); +} + +struct audio_control_state { + char name[128]; + union { + int64_t i[4]; + const char* e[4]; + } vals; + bool used; +}; + +static bool audio_restore_state(struct audio_control_state* controls, int n_controls) +{ + int fd; + int ret; + + fd = open("/dev/snd/controlC0", O_CLOEXEC | O_NONBLOCK); + if (fd < 0) + error("failed to open card\n"); + + struct snd_ctl_elem_list el = { + .offset = 0, + .space = 0, + }; + ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &el); + syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_LIST failed"); + + struct snd_ctl_elem_id ids[el.count]; + el.pids = ids; + el.space = el.count; + ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &el); + syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_LIST failed"); + + for (int i = 0; i < el.used; i++) { + struct snd_ctl_elem_info inf = { + .id = ids[i], + }; + + ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &inf); + syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_INFO failed"); + + if ((inf.access & SNDRV_CTL_ELEM_ACCESS_READ) && (inf.access & SNDRV_CTL_ELEM_ACCESS_WRITE)) { + struct snd_ctl_elem_value val = { + .id = ids[i], + }; + int64_t cval = 0; + + ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_READ, &val); + syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_READ failed"); + + struct audio_control_state* cs = NULL; + for (int j = 0; j < n_controls; j++) { + if (!strcmp(controls[j].name, ids[i].name)) { + cs = &controls[j]; + break; + } + } + + if (!cs) { + printf("Control \"%s\" si not defined in the controls state\n", ids[i].name); + continue; + } + + cs->used = 1; + + // check if value needs changing + + switch (inf.type) { + case SNDRV_CTL_ELEM_TYPE_BOOLEAN: + case SNDRV_CTL_ELEM_TYPE_INTEGER: + for (int j = 0; j < inf.count; j++) { + if (cs->vals.i[j] != val.value.integer.value[j]) { + // update + //printf("%s <=[%d]= %"PRIi64"\n", ids[i].name, j, cs->vals.i[j]); + + val.value.integer.value[j] = cs->vals.i[j]; + ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &val); + syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_WRITE failed"); + } + } + + break; + case SNDRV_CTL_ELEM_TYPE_INTEGER64: + for (int j = 0; j < inf.count; j++) { + if (cs->vals.i[j] != val.value.integer64.value[j]) { + // update + //printf("%s <=[%d]= %"PRIi64"\n", ids[i].name, j, cs->vals.i[j]); + + val.value.integer64.value[j] = cs->vals.i[j]; + ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &val); + syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_WRITE failed"); + } + } + + break; + + case SNDRV_CTL_ELEM_TYPE_ENUMERATED: { + for (int k = 0; k < inf.count; k++) { + int eval = -1; + for (int j = 0; j < inf.value.enumerated.items; j++) { + inf.value.enumerated.item = j; + + ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &inf); + syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_INFO failed"); + + if (!strcmp(cs->vals.e[k], inf.value.enumerated.name)) { + eval = j; + break; + } + } + + if (eval < 0) + error("enum value %s not found\n", cs->vals.e[k]); + + if (eval != val.value.enumerated.item[k]) { + // update + //printf("%s <=%d= %s\n", ids[i].name, k, cs->vals.e[k]); + + val.value.enumerated.item[k] = eval; + ret = ioctl(fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &val); + syscall_error(ret < 0, "SNDRV_CTL_IOCTL_ELEM_WRITE failed"); + } + } + + break; + } + } + } + } + + for (int j = 0; j < n_controls; j++) + if (!controls[j].used) + printf("Control \"%s\" is defined in state but not present on the card\n", controls[j].name); + + close(fd); + return true; +} + +struct audio_setup { + bool mic_on; + bool spk_on; + bool hp_on; + bool ear_on; + + // when sending audio to modem from AIF1 R, also play that back + // to me locally (just like AIF1 L plays just to me) + // + // this is to monitor what SW is playing to the modem (so that + // I can hear my robocaller talking) + bool modem_playback_monitor; + + // enable modem routes to DAC/from ADC (spk/mic) + // digital paths to AIF1 are always on + bool to_modem_on; + bool from_modem_on; + + // shut off/enable all digital paths to the modem: + // keep this off until the call starts, then turn it on + bool dai2_en; + + int mic_gain; + int spk_vol; + int ear_vol; + int hp_vol; +}; + +static void audio_set_controls(struct audio_setup* s) +{ + struct audio_control_state controls[] = { + // + // Analog input: + // + + // Mic 1 (daughterboard) + { .name = "Mic1 Boost Volume", .vals.i = { s->mic_gain } }, + + // Mic 2 (headphones) + { .name = "Mic2 Boost Volume", .vals.i = { 0 } }, + + // Line in (unused on PP) + // no controls yet + + // Input mixers before ADC + + { .name = "Mic1 Capture Switch", .vals.i = { !!s->mic_on, !!s->mic_on } }, + { .name = "Mic2 Capture Switch", .vals.i = { 0, 0 } }, + { .name = "Line In Capture Switch", .vals.i = { 0, 0 } }, // Out Mix -> In Mix + { .name = "Mixer Capture Switch", .vals.i = { 0, 0 } }, + { .name = "Mixer Reversed Capture Switch", .vals.i = { 0, 0 } }, + + // ADC + { .name = "ADC Gain Capture Volume", .vals.i = { 0 } }, + { .name = "ADC Capture Volume", .vals.i = { 160, 160 } }, // digital gain + + // + // Digital paths: + // + + // AIF1 (SoC) + + // AIF1 slot0 capture mixer sources + { .name = "AIF1 Data Digital ADC Capture Switch", .vals.i = { 1, 0 } }, + { .name = "AIF1 Slot 0 Digital ADC Capture Switch", .vals.i = { 0, 0 } }, + { .name = "AIF2 Digital ADC Capture Switch", .vals.i = { 0, 1 } }, + { .name = "AIF2 Inv Digital ADC Capture Switch", .vals.i = { 0, 0 } }, //XXX: capture right from the left AIF2? + + // AIF1 slot0 capture/playback mono mixing/digital volume + { .name = "AIF1 AD0 Capture Volume", .vals.i = { 160, 160 } }, + { .name = "AIF1 AD0 Stereo Capture Route", .vals.e = { "Stereo", "Stereo" } }, + { .name = "AIF1 DA0 Playback Volume", .vals.i = { 160, 160 } }, + { .name = "AIF1 DA0 Stereo Playback Route", .vals.e = { "Stereo", "Stereo" } }, + + // AIF2 (modem) + + // AIF2 capture mixer sources + { .name = "AIF2 ADC Mixer ADC Capture Switch", .vals.i = { !!s->to_modem_on && !!s->dai2_en, 0 } }, // from adc/mic + { .name = "AIF2 ADC Mixer AIF1 DA0 Capture Switch", .vals.i = { 0, 1 } }, // from aif1 R + { .name = "AIF2 ADC Mixer AIF2 DAC Rev Capture Switch", .vals.i = { 0, 0 } }, + + // AIF2 capture/playback mono mixing/digital volume + { .name = "AIF2 ADC Capture Volume", .vals.i = { 160, 160 } }, + { .name = "AIF2 DAC Playback Volume", .vals.i = { 160, 160 } }, + { .name = "AIF2 ADC Stereo Capture Route", .vals.e = { "Mix Mono", "Mix Mono" } }, // we mix because we're sending two channels (from mic and AIF1 R) + { .name = "AIF2 DAC Stereo Playback Route", .vals.e = { "Sum Mono", "Sum Mono" } }, // we sum because modem is sending a single channel + + // AIF3 (bluetooth) + + { .name = "AIF3 ADC Source Capture Route", .vals.e = { "None" } }, + { .name = "AIF2 DAC Source Playback Route", .vals.e = { "AIF2" } }, + + // DAC + + // DAC input mixers (sources from ADC, and AIF1/2) + { .name = "ADC Digital DAC Playback Switch", .vals.i = { 0, 0 } }, // we don't play our mic to ourselves + { .name = "AIF1 Slot 0 Digital DAC Playback Switch", .vals.i = { 1, !!s->modem_playback_monitor } }, + { .name = "AIF2 Digital DAC Playback Switch", .vals.i = { 0, !!s->dai2_en && !!s->from_modem_on } }, + + // + // Analog output: + // + + // Output mixer after DAC + + { .name = "DAC Playback Switch", .vals.i = { 1, 1 } }, + { .name = "DAC Reversed Playback Switch", .vals.i = { 1, 1 } }, + { .name = "DAC Playback Volume", .vals.i = { 160, 160 } }, + { .name = "Mic1 Playback Switch", .vals.i = { 0, 0 } }, + { .name = "Mic1 Playback Volume", .vals.i = { 0 } }, + { .name = "Mic2 Playback Switch", .vals.i = { 0, 0 } }, + { .name = "Mic2 Playback Volume", .vals.i = { 0 } }, + { .name = "Line In Playback Switch", .vals.i = { 0, 0 } }, + { .name = "Line In Playback Volume", .vals.i = { 0 } }, + + // Outputs + + { .name = "Earpiece Source Playback Route", .vals.e = { "Left Mixer" } }, + { .name = "Earpiece Playback Switch", .vals.i = { !!s->ear_on } }, + { .name = "Earpiece Playback Volume", .vals.i = { s->ear_vol } }, + + { .name = "Headphone Source Playback Route", .vals.e = { "Mixer", "Mixer" } }, + { .name = "Headphone Playback Switch", .vals.i = { !!s->hp_on, !!s->hp_on } }, + { .name = "Headphone Playback Volume", .vals.i = { s->hp_vol } }, + + // Loudspeaker + { .name = "Line Out Source Playback Route", .vals.e = { "Mono Differential", "Mono Differential" } }, + { .name = "Line Out Playback Switch", .vals.i = { !!s->spk_on, !!s->spk_on } }, + { .name = "Line Out Playback Volume", .vals.i = { s->spk_vol } }, + }; + + audio_restore_state(controls, ARRAY_SIZE(controls)); +} + +static struct audio_setup audio_setup = { + .mic_on = true, + .ear_on = true, + .spk_on = false, + .hp_on = false, + + .from_modem_on = true, + .to_modem_on = true, + .modem_playback_monitor = false, + + .dai2_en = false, + + .hp_vol = 15, + .spk_vol = 15, + .ear_vol = 31, + .mic_gain = 1, +}; + +int main(int ac, char* av[]) +{ + int opt; + + while ((opt = getopt(ac, av, "smhe2")) != -1) { + switch (opt) { + case 's': + audio_setup.spk_on = 1; + break; + case 'm': + audio_setup.mic_on = 1; + break; + case 'h': + audio_setup.hp_on = 1; + break; + case 'e': + audio_setup.ear_on = 1; + break; + case '2': + audio_setup.dai2_en = 1; + break; + default: /* '?' */ + fprintf(stderr, "Usage: %s [-s] [-m] [-h] [-e] [-2]\n", av[0]); + exit(EXIT_FAILURE); + } + } + + audio_set_controls(&audio_setup); + return 0; +}