aboutsummaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2016-04-25 20:20:54 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2016-05-08 15:48:13 +0200
commit7434e21dc21fe448fba3c119d9c211b790eb0153 (patch)
treeecff6ebbe91f38e1ab68d50780d770424a74364d /src/common
parentde0ce7ba983e98976763612410da7b31e3b46dd5 (diff)
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.
Diffstat (limited to 'src/common')
-rw-r--r--src/common/call.c2
-rw-r--r--src/common/main.h15
-rw-r--r--src/common/main_common.c27
-rw-r--r--src/common/sender.c166
-rw-r--r--src/common/sender.h8
-rw-r--r--src/common/sound.h5
-rw-r--r--src/common/sound_alsa.c31
7 files changed, 206 insertions, 48 deletions
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 <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;
}
-
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;
+}
+