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.
This commit is contained in:
Andreas Eversberg
2016-04-25 20:20:54 +02:00
parent de0ce7ba98
commit 7434e21dc2
20 changed files with 709 additions and 161 deletions

View File

@@ -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)

View File

@@ -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);

View File

@@ -25,13 +25,17 @@
#include <signal.h>
#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 <level>\n");
printf(" Debug level: 0 = debug | 1 = info | 2 = notice (default = '%d')\n", debuglevel);
printf(" -k --kanal <channel>\n");
printf(" Channel number of \"Sender\" (default = '%d')\n", kanal);
printf(" Channel number of \"Sender\"\n");
printf(" -d --device hw:<card>,<device>\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 <rate>\n");
printf(" Sample rate of sound device (default = '%d')\n", samplerate);
printf(" -l --latency <delay>\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:<card>,<device>\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;
}

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}