From 7434e21dc21fe448fba3c119d9c211b790eb0153 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Mon, 25 Apr 2016 20:20:54 +0200 Subject: Multi transceiver support This can be multiple transceivers on multiple sound cards. Two transceivers can be bundled on one sound device as well, using both channels. --- src/common/call.c | 2 +- src/common/main.h | 15 ++++- src/common/main_common.c | 27 +++++--- src/common/sender.c | 166 +++++++++++++++++++++++++++++++++++++++-------- src/common/sender.h | 8 ++- src/common/sound.h | 5 +- src/common/sound_alsa.c | 31 +++++++-- 7 files changed, 206 insertions(+), 48 deletions(-) (limited to 'src/common') diff --git a/src/common/call.c b/src/common/call.c index 9523912..3139634 100644 --- a/src/common/call.c +++ b/src/common/call.c @@ -552,7 +552,7 @@ int process_call(void) return 0; } } - count = sound_read(call.sound, samples, call.latspl); + count = sound_read(call.sound, samples, samples, call.latspl); if (count < 0) { PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from sound device (rc = %d)!\n", count); if (count == -EPIPE) diff --git a/src/common/main.h b/src/common/main.h index 2914ce7..011dc1b 100644 --- a/src/common/main.h +++ b/src/common/main.h @@ -1,9 +1,12 @@ -extern int kanal; -extern const char *sounddev; +extern int num_kanal; +extern int kanal[]; +extern int num_sounddev; +extern const char *sounddev[]; extern const char *call_sounddev; extern int samplerate; extern int latency; +extern int cross_channels; extern int use_mncc_sock; extern int send_patterns; extern int loopback; @@ -20,6 +23,14 @@ extern char *optstring; void set_options_common(const char *optstring_special, struct option *long_options_special); void opt_switch_common(int c, char *arg0, int *skip_args); +#define OPT_ARRAY(num_name, name, value) \ +{ \ + if (num_name == MAX_SENDER) { \ + fprintf(stderr, "Too many channels defined!\n"); \ + exit(0); \ + } \ + name[num_name++] = value; \ +} extern int quit; void sighandler(int sigset); diff --git a/src/common/main_common.c b/src/common/main_common.c index ab71324..d2943ba 100644 --- a/src/common/main_common.c +++ b/src/common/main_common.c @@ -25,13 +25,17 @@ #include #include "main.h" #include "debug.h" +#include "../common/sender.h" /* common settings */ -int kanal = 0; -const char *sounddev = "hw:0,0"; +int num_kanal = 0; +int kanal[MAX_SENDER]; +int num_sounddev = 0; +const char *sounddev[MAX_SENDER] = { "hw:0,0" }; const char *call_sounddev = ""; int samplerate = 48000; int latency = 50; +int cross_channels = 0; int use_mncc_sock = 0; int send_patterns = 1; int loopback = 0; @@ -51,13 +55,16 @@ void print_help_common(const char *arg0, const char *ext_usage) printf(" -D --debug \n"); printf(" Debug level: 0 = debug | 1 = info | 2 = notice (default = '%d')\n", debuglevel); printf(" -k --kanal \n"); - printf(" Channel number of \"Sender\" (default = '%d')\n", kanal); + printf(" Channel number of \"Sender\"\n"); printf(" -d --device hw:,\n"); - printf(" Sound card and device number (default = '%s')\n", sounddev); + printf(" Sound card and device number (default = '%s')\n", sounddev[0]); printf(" -s --samplerate \n"); printf(" Sample rate of sound device (default = '%d')\n", samplerate); printf(" -l --latency \n"); printf(" How many milliseconds processed in advance (default = '%d')\n", latency); + printf(" -x --cross\n"); + printf(" Cross channels on sound card. 1st channel (right) is swapped with\n"); + printf(" second channel (left)\n"); printf(" -m --mncc-sock\n"); printf(" Disable built-in call contol and offer socket (to LCR)\n"); printf(" -c --call-device hw:,\n"); @@ -88,6 +95,7 @@ static struct option long_options_common[] = { {"call-device", 1, 0, 'c'}, {"samplerate", 1, 0, 's'}, {"latency", 1, 0, 'l'}, + {"cross", 0, 0, 'x'}, {"mncc-sock", 0, 0, 'm'}, {"send-patterns", 0, 0, 'p'}, {"loopback", 1, 0, 'L'}, @@ -99,7 +107,7 @@ static struct option long_options_common[] = { {0, 0, 0, 0} }; -const char *optstring_common = "hD:k:d:s:c:l:mp:L:r:EeW:R:"; +const char *optstring_common = "hD:k:d:s:c:l:xmp:L:r:EeW:R:"; struct option *long_options; char *optstring; @@ -134,11 +142,11 @@ void opt_switch_common(int c, char *arg0, int *skip_args) *skip_args += 2; break; case 'k': - kanal = atoi(optarg); + OPT_ARRAY(num_kanal, kanal, atoi(optarg)) *skip_args += 2; break; case 'd': - sounddev = strdup(optarg); + OPT_ARRAY(num_sounddev, sounddev, strdup(optarg)) *skip_args += 2; break; case 's': @@ -153,6 +161,10 @@ void opt_switch_common(int c, char *arg0, int *skip_args) latency = atoi(optarg); *skip_args += 2; break; + case 'x': + cross_channels = 1; + *skip_args += 1; + break; case 'm': use_mncc_sock = 1; *skip_args += 1; @@ -205,4 +217,3 @@ void sighandler(int sigset) quit = 1; } - diff --git a/src/common/sender.c b/src/common/sender.c index 6b7a738..c8e9b60 100644 --- a/src/common/sender.c +++ b/src/common/sender.c @@ -33,13 +33,64 @@ static sender_t **sender_tailp = &sender_head; int cant_recover = 0; /* Init transceiver instance and link to list of transceivers. */ -int sender_create(sender_t *sender, const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int loopback, double loss_volume, int use_pilot_signal) +int sender_create(sender_t *sender, int kanal, const char *sounddev, int samplerate, int cross_channels, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback, double loss_volume, int use_pilot_signal) { + sender_t *master; int rc = 0; PDEBUG(DSENDER, DEBUG_DEBUG, "Creating 'Sender' instance\n"); + /* if we find a channel that uses the same device as we do, + * we will link us as slave to this master channel. then we + * receive and send audio via second channel of the device + * of the master channel. + */ + for (master = sender_head; master; master = master->next) { + if (master->kanal == kanal) { + PDEBUG(DSENDER, DEBUG_ERROR, "Channel %d may not be defined for multiple transceivers!\n", kanal); + rc = -EIO; + goto error; + } + if (!strcmp(master->sounddev, sounddev)) + break; + } + if (master) { + if (master->slave) { + PDEBUG(DSENDER, DEBUG_ERROR, "Sound device '%s' cannot be used for channel %d. It is already shared by channel %d and %d!\n", sounddev, kanal, master->kanal, master->slave->kanal); + rc = -EBUSY; + goto error; + } + if (!sound_is_stereo_capture(master->sound) || !sound_is_stereo_playback(master->sound)) { + PDEBUG(DSENDER, DEBUG_ERROR, "Sound device '%s' cannot be used for more than one channel, because one direction is mono!\n", sounddev); + rc = -EBUSY; + goto error; + } + if (master->use_pilot_signal >= 0) { + PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share sound device with channel %d, because second channel is used for pilot signal!\n", master->kanal); + rc = -EBUSY; + goto error; + } + if (use_pilot_signal >= 0) { + PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share sound device with channel %d, because we need a stereo channel for pilot signal!\n", master->kanal); + rc = -EBUSY; + goto error; + } + /* link us to a master */ + master->slave = sender; + sender->master = master; + } else { + /* open own device */ + sender->sound = sound_open(sounddev, samplerate); + if (!sender->sound) { + PDEBUG(DSENDER, DEBUG_ERROR, "No sound device!\n"); + + rc = -EIO; + goto error; + } + } + sender->samplerate = samplerate; + sender->cross_channels = cross_channels; sender->pre_emphasis = pre_emphasis; sender->de_emphasis = de_emphasis; sender->kanal = kanal; @@ -47,14 +98,7 @@ int sender_create(sender_t *sender, const char *sounddev, int samplerate, int pr sender->loss_volume = loss_volume; sender->use_pilot_signal = use_pilot_signal; sender->pilotton_phaseshift = 1.0 / ((double)samplerate / 1000.0); - - sender->sound = sound_open(sounddev, samplerate); - if (!sender->sound) { - PDEBUG(DSENDER, DEBUG_ERROR, "No sound device!\n"); - - rc = -EIO; - goto error; - } + strncpy(sender->sounddev, sounddev, sizeof(sender->sounddev) - 1); rc = init_samplerate(&sender->srstate, samplerate); if (rc < 0) { @@ -105,10 +149,16 @@ void sender_destroy(sender_t *sender) sender_tailp = &sender_head; while (*sender_tailp) { if (sender == *sender_tailp) - *sender_tailp = sender->next; - sender_tailp = &sender->next; + *sender_tailp = (*sender_tailp)->next; + else + sender_tailp = &((*sender_tailp)->next); } + if (sender->slave) + sender->slave->master = NULL; + if (sender->master) + sender->master->slave = NULL; + if (sender->sound) sound_close(sender->sound); @@ -140,9 +190,10 @@ static void gen_pilotton(sender_t *sender, int16_t *samples, int length) } /* Handle audio streaming of one transceiver. */ -void process_sender(sender_t *sender, int *quit, int latspl) +static void process_sender_audio(sender_t *sender, int *quit, int latspl) { - int16_t samples[latspl], pilot[latspl]; + sender_t *slave = sender->slave; + int16_t samples[latspl], pilot[latspl], slave_samples[latspl]; int rc, count; count = sound_get_inbuffer(sender->sound); @@ -161,12 +212,36 @@ cant_recover: } if (count < latspl) { count = latspl - count; + /* load TX data from audio loop or from sender instance */ if (sender->loopback == 3) jitter_load(&sender->audio, samples, count); else sender_send(sender, samples, count); + /* internal loopback: loop back TX audio to RX */ + if (sender->loopback == 1) { + if (sender->wave_rec.fp) + wave_write(&sender->wave_rec, samples, count); + sender_receive(sender, samples, count); + } + /* do pre emphasis towards radio, not wave_write */ if (sender->pre_emphasis) pre_emphasis(&sender->estate, samples, count); + /* do above for audio slave, if set */ + if (slave) { + if (slave->loopback == 3) + jitter_load(&slave->audio, slave_samples, count); + else + sender_send(slave, slave_samples, count); + /* internal loopback, if audio slave is set */ + if (slave && slave->loopback == 1) { + if (slave->wave_rec.fp) + wave_write(&slave->wave_rec, slave_samples, count); + sender_receive(slave, slave_samples, count); + } + /* do pre emphasis towards radio, not wave_write */ + if (slave->pre_emphasis) + pre_emphasis(&slave->estate, slave_samples, count); + } switch (sender->use_pilot_signal) { case 2: /* tone if pilot signal is on */ @@ -174,7 +249,10 @@ cant_recover: gen_pilotton(sender, pilot, count); else memset(pilot, 0, count << 1); - rc = sound_write(sender->sound, samples, pilot, count); + if (!sender->cross_channels) + rc = sound_write(sender->sound, samples, pilot, count); + else + rc = sound_write(sender->sound, pilot, samples, count); break; case 1: /* positive signal if pilot signal is on */ @@ -182,7 +260,10 @@ cant_recover: memset(pilot, 127, count << 1); else memset(pilot, 128, count << 1); - rc = sound_write(sender->sound, samples, pilot, count); + if (!sender->cross_channels) + rc = sound_write(sender->sound, samples, pilot, count); + else + rc = sound_write(sender->sound, pilot, samples, count); break; case 0: /* negative signal if pilot signal is on */ @@ -190,10 +271,26 @@ cant_recover: memset(pilot, 128, count << 1); else memset(pilot, 127, count << 1); - rc = sound_write(sender->sound, samples, pilot, count); + if (!sender->cross_channels) + rc = sound_write(sender->sound, samples, pilot, count); + else + rc = sound_write(sender->sound, pilot, samples, count); break; default: - rc = sound_write(sender->sound, samples, samples, count); + /* if audio slave is set, write audio of both sender instances */ + if (slave) { + if (!sender->cross_channels) + rc = sound_write(sender->sound, samples, slave_samples, count); + else + rc = sound_write(sender->sound, slave_samples, samples, count); + }else { + /* use pilot tone buffer for silence */ + memset(pilot, 0, count << 1); + if (!sender->cross_channels) + rc = sound_write(sender->sound, samples, pilot, count); + else + rc = sound_write(sender->sound, pilot, samples, count); + } } if (rc < 0) { PDEBUG(DSENDER, DEBUG_ERROR, "Failed to write TX data to sound device (rc = %d)\n", rc); @@ -204,14 +301,12 @@ cant_recover: } return; } - if (sender->loopback == 1) { - if (sender->wave_rec.fp) - wave_write(&sender->wave_rec, samples, count); - sender_receive(sender, samples, count); - } } - count = sound_read(sender->sound, samples, latspl); + if (!sender->cross_channels) + count = sound_read(sender->sound, samples, slave_samples, latspl); + else + count = sound_read(sender->sound, slave_samples, samples, latspl); //printf("count=%d time= %.4f\n", count, (double)count * 1000 / sender->samplerate); if (count < 0) { PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from sound device (rc = %d)!\n", count); @@ -223,6 +318,7 @@ cant_recover: return; } if (count) { + /* do de emphasis from radio (then write_wave/wave_read), receive audio, process echo test */ if (sender->de_emphasis) de_emphasis(&sender->estate, samples, count); if (sender->wave_play.fp) @@ -232,8 +328,21 @@ cant_recover: wave_write(&sender->wave_rec, samples, count); sender_receive(sender, samples, count); } - if (sender->loopback == 3) { + if (sender->loopback == 3) jitter_save(&sender->audio, samples, count); + /* do above for audio slave, if set */ + if (slave) { + if (slave->de_emphasis) + de_emphasis(&slave->estate, slave_samples, count); + if (slave->wave_play.fp) + wave_read(&slave->wave_play, slave_samples, count); + if (slave->loopback != 1) { + if (slave->wave_rec.fp) + wave_write(&slave->wave_rec, slave_samples, count); + sender_receive(slave, slave_samples, count); + } + if (slave->loopback == 3) + jitter_save(&slave->audio, slave_samples, count); } } } @@ -247,11 +356,12 @@ void main_loop(int *quit, int latency) while(!(*quit)) { /* process sound of all transceivers */ - sender = sender_head; - while (sender) { + for (sender = sender_head; sender; sender = sender->next) { + /* do not process audio for an audio slave, since it is done by audio master */ + if (sender->master) /* if master is set, we are an audio slave */ + continue; latspl = sender->samplerate * latency / 1000; - process_sender(sender, quit, latspl); - sender = sender->next; + process_sender_audio(sender, quit, latspl); } /* process timers */ diff --git a/src/common/sender.h b/src/common/sender.h index 7e0eb74..db0936e 100644 --- a/src/common/sender.h +++ b/src/common/sender.h @@ -5,9 +5,13 @@ #include "loss.h" #include "emphasis.h" +#define MAX_SENDER 16 + /* common structure of each transmitter */ typedef struct sender { struct sender *next; + struct sender *slave; /* points to audio device slave member */ + struct sender *master; /* points to audio device master source */ /* call reference */ int callref; @@ -17,7 +21,9 @@ typedef struct sender { /* sound */ void *sound; + char sounddev[64]; /* sound device name */ int samplerate; + int cross_channels; /* swap right and left on IO */ samplerate_t srstate; /* sample rate conversion state */ int pre_emphasis; /* use pre_emhasis, done by sender */ int de_emphasis; /* use de_emhasis, done by sender */ @@ -52,7 +58,7 @@ typedef struct sender { extern sender_t *sender_head; extern int cant_recover; -int sender_create(sender_t *sender, const char *sounddev, int samplerate, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int kanal, int loopback, double loss_volume, int use_pilot_signal); +int sender_create(sender_t *sender, int kanal, const char *sounddev, int samplerate, int cross_channels, int pre_emphasis, int de_emphasis, const char *write_wave, const char *read_wave, int loopback, double loss_volume, int use_pilot_signal); void sender_destroy(sender_t *sender); void sender_send(sender_t *sender, int16_t *samples, int count); void sender_receive(sender_t *sender, int16_t *samples, int count); diff --git a/src/common/sound.h b/src/common/sound.h index 6685d45..82dc17a 100644 --- a/src/common/sound.h +++ b/src/common/sound.h @@ -2,5 +2,8 @@ void *sound_open(const char *device, int samplerate); void sound_close(void *inst); int sound_write(void *inst, int16_t *samples_left, int16_t *samples_right, int num); -int sound_read(void *inst, int16_t *samples, int num); +int sound_read(void *inst, int16_t *samples_left, int16_t *samples_right, int num); int sound_get_inbuffer(void *inst); +int sound_is_stereo_capture(void *inst); +int sound_is_stereo_playback(void *inst); + diff --git a/src/common/sound_alsa.c b/src/common/sound_alsa.c index 0ace241..9a35e3e 100644 --- a/src/common/sound_alsa.c +++ b/src/common/sound_alsa.c @@ -203,18 +203,17 @@ int sound_write(void *inst, int16_t *samples_left, int16_t *samples_right, int n return rc; } -int sound_read(void *inst, int16_t *samples, int num) +int sound_read(void *inst, int16_t *samples_left, int16_t *samples_right, int num) { sound_t *sound = (sound_t *)inst; int16_t buff[num << 1]; - int32_t s32; int rc; int i, ii; if (sound->cchannels == 2) rc = snd_pcm_readi(sound->chandle, buff, num); else - rc = snd_pcm_readi(sound->chandle, samples, num); + rc = snd_pcm_readi(sound->chandle, samples_left, num); if (rc < 0) { if (errno == EAGAIN) @@ -228,11 +227,11 @@ int sound_read(void *inst, int16_t *samples, int num) if (sound->cchannels == 2) { for (i = 0, ii = 0; i < rc; i++) { - s32 = buff[ii++]; - s32 += buff[ii++]; - *samples++ = s32 >> 1; + *samples_right++ = buff[ii++]; + *samples_left++ = buff[ii++]; } - } + } else + memcpy(samples_right, samples_left, num << 1); return rc; } @@ -261,3 +260,21 @@ int sound_get_inbuffer(void *inst) return delay; } +int sound_is_stereo_capture(void *inst) +{ + sound_t *sound = (sound_t *)inst; + + if (sound->cchannels == 2) + return 1; + return 0; +} + +int sound_is_stereo_playback(void *inst) +{ + sound_t *sound = (sound_t *)inst; + + if (sound->pchannels == 2) + return 1; + return 0; +} + -- cgit v1.2.3