Rework on audio interface

Sound instance is now called audio instance and uses funcation pointers.
This gives a clean interface to be exchanged with other technologies,
linke SDR.
This commit is contained in:
Andreas Eversberg
2017-01-04 14:14:02 +01:00
parent d54d3ac265
commit 9ff8c3bb25
29 changed files with 231 additions and 162 deletions

View File

@@ -170,7 +170,7 @@ typedef struct call {
void *sound; /* headphone interface */
int latspl; /* sample latency at sound interface */
samplerate_t srstate; /* patterns/announcement upsampling */
jitter_t audio; /* headphone audio dejittering */
jitter_t dejitter; /* headphone audio dejittering */
int audio_pos; /* position when playing patterns */
int test_audio_pos; /* position for test tone toward mobile */
int dial_digits; /* number of digits to be dialed */
@@ -454,7 +454,7 @@ static void process_timeout(struct timer *timer)
}
}
int call_init(const char *station_id, const char *sounddev, int samplerate, int latency, int dial_digits, int loopback)
int call_init(const char *station_id, const char *audiodev, int samplerate, int latency, int dial_digits, int loopback)
{
int rc = 0;
@@ -469,11 +469,11 @@ int call_init(const char *station_id, const char *sounddev, int samplerate, int
call.dial_digits = dial_digits;
call.loopback = loopback;
if (!sounddev[0])
if (!audiodev[0])
return 0;
/* open sound device for call control */
call.sound = sound_open(sounddev, samplerate);
call.sound = sound_open(audiodev, NULL, NULL, 1, samplerate);
if (!call.sound) {
PDEBUG(DSENDER, DEBUG_ERROR, "No sound device!\n");
@@ -487,9 +487,9 @@ int call_init(const char *station_id, const char *sounddev, int samplerate, int
goto error;
}
rc = jitter_create(&call.audio, samplerate / 5);
rc = jitter_create(&call.dejitter, samplerate / 5);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init audio buffer!\n");
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init dejitter buffer!\n");
goto error;
}
@@ -509,7 +509,7 @@ void call_cleanup(void)
if (call.sound)
sound_close(call.sound);
jitter_destroy(&call.audio);
jitter_destroy(&call.dejitter);
if (process_head) {
PDEBUG(DMNCC, DEBUG_ERROR, "Not all MNCC instances have been released!\n");
@@ -658,17 +658,17 @@ void process_call(int c)
get_call_patterns(samples, count, PATTERN_RINGBACK);
count = samplerate_upsample(&call.srstate, samples, count, up);
/* prevent click after hangup */
jitter_clear(&call.audio);
jitter_clear(&call.dejitter);
break;
case CALL_DISCONNECTED:
count = (int)((double)count / call.srstate.factor + 0.5);
get_call_patterns(samples, count, cause2pattern(call.disc_cause));
count = samplerate_upsample(&call.srstate, samples, count, up);
/* prevent click after hangup */
jitter_clear(&call.audio);
jitter_clear(&call.dejitter);
break;
default:
jitter_load(&call.audio, up, count);
jitter_load(&call.dejitter, up, count);
}
spl_list[0] = up;
rc = sound_write(call.sound, spl_list, count, 1);
@@ -691,7 +691,7 @@ void process_call(int c)
int16_t down[count]; /* more than enough */
if (call.loopback == 3)
jitter_save(&call.audio, samples, count);
jitter_save(&call.dejitter, samples, count);
count = samplerate_downsample(&call.srstate, samples, count, down);
call_rx_audio(call.callref, down, count);
}
@@ -922,7 +922,7 @@ void call_tx_audio(int callref, int16_t *samples, int count)
if (call.sound) {
int16_t up[(int)((double)count * call.srstate.factor + 0.5) + 10];
count = samplerate_upsample(&call.srstate, samples, count, up);
jitter_save(&call.audio, up, count);
jitter_save(&call.dejitter, up, count);
} else
/* else, if no sound is used, send test tone to mobile */
if (call.state == CALL_CONNECT) {

View File

@@ -9,7 +9,7 @@ enum number_type {
TYPE_INTERNATIONAL,
};
int call_init(const char *station_id, const char *sounddev, int samplerate, int latency, int dial_digits, int loopback);
int call_init(const char *station_id, const char *audiodev, int samplerate, int latency, int dial_digits, int loopback);
void call_cleanup(void);
void process_call(int c);
void clear_console_text(void);

View File

@@ -1,9 +1,9 @@
extern int num_kanal;
extern int kanal[];
extern int num_sounddev;
extern const char *sounddev[];
extern const char *call_sounddev;
extern int num_audiodev;
extern const char *audiodev[];
extern const char *call_audiodev;
extern int samplerate;
extern int interval;
extern int latency;
@@ -39,7 +39,7 @@ void opt_switch_common(int c, char *arg0, int *skip_args);
extern int quit;
void sighandler(int sigset);
void main_loop(int *quit, int latency, int interval, void (*myhandler)(void));
void main_common(int *quit, int latency, int interval, void (*myhandler)(void));
void dump_info(void);

View File

@@ -36,9 +36,9 @@
/* common settings */
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 num_audiodev = 0;
const char *audiodev[MAX_SENDER] = { "hw:0,0" };
const char *call_audiodev = "";
int samplerate = 48000;
int interval = 1;
int latency = 50;
@@ -95,7 +95,7 @@ void print_help_common(const char *arg0, const char *ext_usage)
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");
printf(" Sound card and device number for headset (default = '%s')\n", call_sounddev);
printf(" Sound card and device number for headset (default = '%s')\n", call_audiodev);
printf(" -t --tones 0 | 1\n");
printf(" Connect call on setup/release to provide classic tones towards fixed\n");
printf(" network (default = '%d')\n", send_patterns);
@@ -208,7 +208,7 @@ void opt_switch_common(int c, char *arg0, int *skip_args)
*skip_args += 2;
break;
case 'a':
OPT_ARRAY(num_sounddev, sounddev, strdup(optarg))
OPT_ARRAY(num_audiodev, audiodev, strdup(optarg))
*skip_args += 2;
break;
case 's':
@@ -256,7 +256,7 @@ void opt_switch_common(int c, char *arg0, int *skip_args)
*skip_args += 1;
break;
case 'c':
call_sounddev = strdup(optarg);
call_audiodev = strdup(optarg);
*skip_args += 2;
break;
case 't':
@@ -322,7 +322,7 @@ static int get_char()
}
/* Loop through all transceiver instances of one network. */
void main_loop(int *quit, int latency, int interval, void (*myhandler)(void))
void main_common(int *quit, int latency, int interval, void (*myhandler)(void))
{
int latspl;
sender_t *sender;
@@ -330,11 +330,10 @@ void main_loop(int *quit, int latency, int interval, void (*myhandler)(void))
struct termios term, term_orig;
int c;
/* catch signals */
signal(SIGINT, sighandler);
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, sighandler);
/* open audio */
if (sender_open_audio())
return;
/* real time priority */
if (rt_prio > 0) {
@@ -356,6 +355,12 @@ void main_loop(int *quit, int latency, int interval, void (*myhandler)(void))
term.c_cc[VTIME]=2;
tcsetattr(0, TCSANOW, &term);
/* catch signals */
signal(SIGINT, sighandler);
signal(SIGHUP, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, sighandler);
while(!(*quit)) {
/* process sound of all transceivers */
for (sender = sender_head; sender; sender = sender->next) {
@@ -408,6 +413,12 @@ next_char:
usleep(interval * 1000);
}
/* reset signals */
signal(SIGINT, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
/* get rid of last entry */
clear_console_text();
@@ -422,11 +433,5 @@ next_char:
schedp.sched_priority = 0;
sched_setscheduler(0, SCHED_OTHER, &schedp);
}
/* reset signals */
signal(SIGINT, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
}

View File

@@ -32,13 +32,15 @@ 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, int kanal, const char *sounddev, int samplerate, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, int loopback, double loss_volume, enum pilot_signal pilot_signal)
int sender_create(sender_t *sender, int kanal, double sendefrequenz, double empfangsfrequenz, const char *audiodev, int samplerate, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, int loopback, double loss_volume, enum pilot_signal pilot_signal)
{
sender_t *master;
sender_t *master, *slave;
int rc = 0;
sender->kanal = kanal;
strncpy(sender->sounddev, sounddev, sizeof(sender->sounddev) - 1);
sender->sendefrequenz = sendefrequenz;
sender->empfangsfrequenz = empfangsfrequenz;
strncpy(sender->audiodev, audiodev, sizeof(sender->audiodev) - 1);
sender->samplerate = samplerate;
sender->rx_gain = rx_gain;
sender->pre_emphasis = pre_emphasis;
@@ -61,43 +63,36 @@ int sender_create(sender_t *sender, int kanal, const char *sounddev, int sampler
rc = -EIO;
goto error;
}
if (!strcmp(master->sounddev, sounddev))
if (!strcmp(master->audiodev, audiodev))
break;
}
if (master) {
// FIXME: link more than two channels
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->pilot_signal != PILOT_SIGNAL_NONE) {
PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share sound device with channel %d, because second channel is used for pilot signal!\n", master->kanal);
PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share audio device with channel %d, because second channel is used for pilot signal!\n", master->kanal);
rc = -EBUSY;
goto error;
}
if (pilot_signal != PILOT_SIGNAL_NONE) {
PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share sound device with channel %d, because we need a stereo channel for pilot signal!\n", master->kanal);
PDEBUG(DSENDER, DEBUG_ERROR, "Cannot share audio 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/slave */
master->slave = sender;
// FIXME: link more than two channels, so link to last slave
/* link us to a master */
sender->master = master;
/* link master (or last slave) to us */
for (slave = master; ; slave = slave->slave) {
if (!slave->slave)
break;
}
slave->slave = sender;
} 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;
/* link audio device */
{
sender->audio_open = sound_open;
sender->audio_close = sound_close;
sender->audio_read = sound_read;
sender->audio_write = sound_write;
sender->audio_get_inbuffer = sound_get_inbuffer;
}
}
@@ -107,7 +102,7 @@ int sender_create(sender_t *sender, int kanal, const char *sounddev, int sampler
goto error;
}
rc = jitter_create(&sender->audio, samplerate / 5);
rc = jitter_create(&sender->dejitter, samplerate / 5);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init audio buffer!\n");
goto error;
@@ -151,6 +146,42 @@ error:
return rc;
}
int sender_open_audio(void)
{
sender_t *master, *inst;
int channels;
int i;
for (master = sender_head; master; master = master->next) {
/* skip audio slaves */
if (master->master)
continue;
/* get list of frequencies */
channels = 0;
for (inst = master; inst; inst = inst->slave) {
channels++;
}
double tx_f[channels], rx_f[channels];
for (i = 0, inst = master; inst; i++, inst = inst->slave) {
tx_f[i] = inst->sendefrequenz;
if (inst->loopback)
rx_f[i] = inst->sendefrequenz;
else
rx_f[i] = inst->empfangsfrequenz;
}
/* open device */
master->audio = master->audio_open(master->audiodev, tx_f, rx_f, channels, master->samplerate);
if (!master->audio) {
PDEBUG(DSENDER, DEBUG_ERROR, "No audio device!\n");
return -EIO;
}
}
return 0;
}
/* Destroy transceiver instance and unlink from list. */
void sender_destroy(sender_t *sender)
{
@@ -164,16 +195,16 @@ void sender_destroy(sender_t *sender)
sender_tailp = &((*sender_tailp)->next);
}
if (sender->sound) {
sound_close(sender->sound);
sender->sound = NULL;
if (sender->audio) {
sender->audio_close(sender->audio);
sender->audio = NULL;
}
wave_destroy_record(&sender->wave_rx_rec);
wave_destroy_record(&sender->wave_tx_rec);
wave_destroy_playback(&sender->wave_rx_play);
jitter_destroy(&sender->audio);
jitter_destroy(&sender->dejitter);
}
static void gen_pilotton(sender_t *sender, int16_t *samples, int length)
@@ -221,19 +252,23 @@ void process_sender_audio(sender_t *sender, int *quit, int latspl)
/* count instances for audio channel */
for (num_chan = 0, inst = sender; inst; num_chan++, inst = inst->slave);
if (sender->pilot_signal) {
if (sender->pilot_signal != PILOT_SIGNAL_NONE) {
if (num_chan != 1) {
PDEBUG(DSENDER, DEBUG_ERROR, "Software error, please fix!\n");
abort();
}
num_chan++;
num_chan = 2;
}
int16_t buff[num_chan][latspl], *samples[num_chan];
for (i = 0; i < num_chan; i++)
samples[i] = buff[i];
count = sound_get_inbuffer(sender->sound);
count = sender->audio_get_inbuffer(sender->audio);
if (count < 0) {
/* special case when the device is not yet ready to transmit packets */
if (count == -EAGAIN) {
goto transmit_later;
}
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to get samples in buffer (rc = %d)!\n", count);
if (count == -EPIPE) {
if (cant_recover) {
@@ -252,7 +287,7 @@ cant_recover:
for (i = 0, inst = sender; inst; i++, inst = inst->slave) {
/* load TX data from audio loop or from sender instance */
if (inst->loopback == 3)
jitter_load(&inst->audio, samples[i], count);
jitter_load(&inst->dejitter, samples[i], count);
else
sender_send(inst, samples[i], count);
if (inst->wave_tx_rec.fp)
@@ -301,9 +336,9 @@ cant_recover:
break;
}
rc = sound_write(sender->sound, samples, count, num_chan);
rc = sender->audio_write(sender->audio, samples, count, num_chan);
if (rc < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to write TX data to sound device (rc = %d)\n", rc);
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to write TX data to audio device (rc = %d)\n", rc);
if (rc == -EPIPE) {
if (cant_recover)
goto cant_recover;
@@ -312,10 +347,16 @@ cant_recover:
return;
}
}
transmit_later:
count = sound_read(sender->sound, samples, latspl, num_chan);
count = sender->audio_read(sender->audio, samples, latspl, num_chan);
if (count < 0) {
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from sound device (rc = %d)!\n", count);
/* special case when audio_read wants us to quit */
if (count == -EPERM) {
*quit = 1;
return;
}
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to read from audio device (rc = %d)!\n", count);
if (count == -EPIPE) {
if (cant_recover)
goto cant_recover;
@@ -341,7 +382,7 @@ cant_recover:
sender_receive(inst, samples[i], count);
}
if (inst->loopback == 3)
jitter_save(&inst->audio, samples[i], count);
jitter_save(&inst->dejitter, samples[i], count);
}
}
}

View File

@@ -25,10 +25,17 @@ typedef struct sender {
/* system info */
int kanal; /* channel number */
double sendefrequenz; /* transmitter frequency */
double empfangsfrequenz; /* receiver frequency */
/* sound */
void *sound;
char sounddev[64]; /* sound device name */
/* audio */
void *audio;
char audiodev[64]; /* audio device name (alsa or sdr) */
void *(*audio_open)(const char *, double *, double *, int, int);
void (*audio_close)(void *);
int (*audio_write)(void *, int16_t **, int, int);
int (*audio_read)(void *, int16_t **, int, int);
int (*audio_get_inbuffer)(void *);
int samplerate;
samplerate_t srstate; /* sample rate conversion state */
double rx_gain; /* factor of level to apply on rx samples */
@@ -45,7 +52,7 @@ typedef struct sender {
wave_play_t wave_rx_play; /* wave playback (as rx) */
/* audio buffer for audio to send to transmitter (also used as loopback buffer) */
jitter_t audio;
jitter_t dejitter;
/* audio buffer for audio to send to caller (20ms = 160 samples @ 8000Hz) */
int16_t rxbuf[160];
@@ -69,8 +76,9 @@ typedef struct sender {
extern sender_t *sender_head;
extern int cant_recover;
int sender_create(sender_t *sender, int kanal, const char *sounddev, int samplerate, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, int loopback, double loss_volume, enum pilot_signal pilot_signal);
int sender_create(sender_t *sender, int kanal, double sendefrequenz, double empfangsfrequenz, const char *audiodev, int samplerate, double rx_gain, int pre_emphasis, int de_emphasis, const char *write_rx_wave, const char *write_tx_wave, const char *read_rx_wave, int loopback, double loss_volume, enum pilot_signal pilot_signal);
void sender_destroy(sender_t *sender);
int sender_open_audio(void);
void process_sender_audio(sender_t *sender, int *quit, int latspl);
void sender_send(sender_t *sender, int16_t *samples, int count);
void sender_receive(sender_t *sender, int16_t *samples, int count);

View File

@@ -1,9 +1,7 @@
void *sound_open(const char *device, int samplerate);
void *sound_open(const char *audiodev, double *tx_frequency, double *rx_frequency, int channels, int samplerate);
void sound_close(void *inst);
int sound_write(void *inst, int16_t **samples, int num, int channels);
int sound_read(void *inst, int16_t **samples, int num, int channels);
int sound_get_inbuffer(void *inst);
int sound_is_stereo_capture(void *inst);
int sound_is_stereo_playback(void *inst);

View File

@@ -128,26 +128,31 @@ static int sound_prepare(sound_t *sound)
return 0;
}
void *sound_open(const char *device, int samplerate)
void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_frequency, double __attribute__((unused)) *rx_frequency, int channels, int samplerate)
{
sound_t *sound;
int rc;
if (channels < 1 || channels > 2) {
PDEBUG(DSOUND, DEBUG_ERROR, "Cannot use more than two channels with the same sound card!\n");
return NULL;
}
sound = calloc(1, sizeof(sound_t));
if (!sound) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to alloc memory!\n");
return NULL;
}
rc = snd_pcm_open(&sound->phandle, device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
rc = snd_pcm_open(&sound->phandle, audiodev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (rc < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for playback! (%s)\n", device, snd_strerror(rc));
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for playback! (%s)\n", audiodev, snd_strerror(rc));
goto error;
}
rc = snd_pcm_open(&sound->chandle, device, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
rc = snd_pcm_open(&sound->chandle, audiodev, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (rc < 0) {
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for capture! (%s)\n", device, snd_strerror(rc));
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to open '%s' for capture! (%s)\n", audiodev, snd_strerror(rc));
goto error;
}
@@ -156,6 +161,10 @@ void *sound_open(const char *device, int samplerate)
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set playback hw params\n");
goto error;
}
if (sound->pchannels < channels) {
PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for playback.\n", sound->pchannels);
goto error;
}
PDEBUG(DSOUND, DEBUG_DEBUG, "Playback with %d channels.\n", sound->pchannels);
rc = set_hw_params(sound->chandle, samplerate, &sound->cchannels);
@@ -163,6 +172,10 @@ void *sound_open(const char *device, int samplerate)
PDEBUG(DSOUND, DEBUG_ERROR, "Failed to set capture hw params\n");
goto error;
}
if (sound->cchannels < channels) {
PDEBUG(DSOUND, DEBUG_ERROR, "Sound card only supports %d channel for capture.\n", sound->cchannels);
goto error;
}
PDEBUG(DSOUND, DEBUG_DEBUG, "Capture with %d channels.\n", sound->cchannels);
rc = sound_prepare(sound);