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:
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user