682 lines
23 KiB
C
682 lines
23 KiB
C
#include <Python.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <complex.h> // Use native C99 complex type for fftw3
|
|
#include <sys/types.h>
|
|
|
|
#include "quisk.h"
|
|
#include "freedv.h"
|
|
#include <stdint.h>
|
|
|
|
int DEBUG;
|
|
|
|
#define MAX_RECEIVERS 2
|
|
|
|
typedef struct { // from comp.h
|
|
float real;
|
|
float imag;
|
|
} COMP;
|
|
|
|
struct freedv; // from freedv_api.h
|
|
typedef void (*freedv_callback_rx)(void *, char);
|
|
typedef char (*freedv_callback_tx)(void *);
|
|
/* Protocol bits are packed MSB-first */
|
|
/* Called when a frame containing protocol data is decoded */
|
|
typedef void (*freedv_callback_protorx)(void *, char *);
|
|
/* Called when a frame containing protocol data is to be sent */
|
|
typedef void (*freedv_callback_prototx)(void *, char *);
|
|
/* Data packet callbacks */
|
|
/* Called when a packet has been received */
|
|
typedef void (*freedv_callback_datarx)(void *, unsigned char *packet, size_t size);
|
|
/* Called when a new packet can be send */
|
|
typedef void (*freedv_callback_datatx)(void *, unsigned char *packet, size_t *size);
|
|
|
|
/* advanced freedv open options required by some modes */
|
|
struct freedv_advanced {
|
|
int interleave_frames; // now unused but remains to prevent breaking API for
|
|
// legacy apps
|
|
|
|
// parameters for FREEDV_MODE_FSK_LDPC
|
|
int M; // 2 or 4 FSK
|
|
int Rs; // Symbol rate Hz
|
|
int Fs; // Sample rate Hz
|
|
int first_tone; // Freq of first tone Hz
|
|
int tone_spacing; // Spacing between tones Hz
|
|
char *codename; // LDPC codename, from codes listed in ldpc_codes.c
|
|
};
|
|
|
|
#ifdef MS_WINDOWS
|
|
#include <windows.h>
|
|
HMODULE hLib;
|
|
#define GET_HANDLE1 hLib = LoadLibrary("libcodec2.dll")
|
|
#define GET_HANDLE2 hLib = LoadLibrary(".\\freedvpkg\\libcodec2.dll")
|
|
#define GET_HANDLE3 hLib = LoadLibrary(".\\freedvpkg\\libcodec2_32.dll")
|
|
#define GET_HANDLE4 hLib = LoadLibrary(".\\freedvpkg\\libcodec2_64.dll")
|
|
#define GET_ADDR(name) (void *)GetProcAddress(hLib, name)
|
|
#define CLOSE_LIB FreeLibrary(hLib)
|
|
#else
|
|
#include <dlfcn.h>
|
|
void * hLib;
|
|
#define GET_HANDLE1 hLib = dlopen("libcodec2.so", RTLD_LAZY)
|
|
#define GET_HANDLE2 hLib = dlopen("./freedvpkg/libcodec2.so", RTLD_LAZY)
|
|
#define GET_HANDLE3 hLib = dlopen("./freedvpkg/libcodec2_32.so", RTLD_LAZY)
|
|
#define GET_HANDLE4 hLib = dlopen("./freedvpkg/libcodec2_64.so", RTLD_LAZY)
|
|
#define GET_ADDR(name) dlsym(hLib, name)
|
|
#define CLOSE_LIB dlclose(hLib)
|
|
#endif
|
|
|
|
static int requested_mode = -1; // requested mode
|
|
int freedv_current_mode = -1; // the current running mode
|
|
static int quisk_freedv_squelch;
|
|
static int interleave_frames = 1;
|
|
static int freedv_version = -1;
|
|
static int quisk_set_tx_bpf = 1;
|
|
static int handle_index = -1;
|
|
// need access to these here and in quisk.c
|
|
int n_speech_sample_rate = 8000;
|
|
int n_modem_sample_rate = 8000;
|
|
int n_max_modem_samples = 1000;
|
|
|
|
#define SPEECH_BUF_SIZE 12000 // speech buffer size increased from 3000
|
|
#define MAX_MODEM_SAMPLES 8192 // maximum of n_max_modem_samples for any mode
|
|
#define MAX_SPEECH_SAMPLES 8192 // maximum of n_speech_samples for any mode
|
|
static struct _rx_channel{
|
|
struct freedv * hFreedv;
|
|
short * demod_in;
|
|
int rxdata_index;
|
|
short speech_out[SPEECH_BUF_SIZE]; // output buffer
|
|
int speech_available; // number of samples in output buffer
|
|
int playing; // are we currently returning speech samples?
|
|
} rx_channel[MAX_RECEIVERS] = { 0 }; // safe initialisation to 0
|
|
|
|
// freedv_version is the library version number, or
|
|
// -1 no library was found
|
|
// -2 a library was found, but freedv_get_version is missing
|
|
|
|
// FreeDV API functions:
|
|
// open, close
|
|
struct freedv * (*freedv_open)(int mode);
|
|
struct freedv * (*freedv_open_advanced)(int mode, struct freedv_advanced *adv);
|
|
void (*freedv_close)(struct freedv *freedv);
|
|
// Transmit
|
|
void (*freedv_tx)(struct freedv *freedv, short *, short *);
|
|
void (*freedv_comptx)(struct freedv *freedv, COMP *, short *);
|
|
// Receive
|
|
int (*freedv_nin)(struct freedv *freedv);
|
|
int (*freedv_rx)(struct freedv *freedv, short *, short demod_in[]);
|
|
int (*freedv_floatrx)(struct freedv *freedv, short *, float demod_in[]);
|
|
int (*freedv_comprx)(struct freedv *freedv, short *, COMP demod_in[]);
|
|
// Set parameters
|
|
void (*freedv_set_callback_txt) (struct freedv *freedv, freedv_callback_rx rx, freedv_callback_tx tx, void *callback_state);
|
|
void (*freedv_set_test_frames) (struct freedv *freedv, int test_frames);
|
|
void (*freedv_set_smooth_symbols) (struct freedv *freedv, int smooth_symbols);
|
|
void (*freedv_set_squelch_en) (struct freedv *freedv, int squelch_en);
|
|
void (*freedv_set_snr_squelch_thresh) (struct freedv *freedv, float snr_squelch_thresh);
|
|
void (*freedv_set_tx_bpf) (struct freedv *freedv, int val);
|
|
// Get parameters
|
|
int (*freedv_get_version)(void);
|
|
void (*freedv_get_modem_stats)(struct freedv *freedv, int *sync, float *snr_est);
|
|
int (*freedv_get_test_frames) (struct freedv *freedv);
|
|
int (*freedv_get_n_speech_samples) (struct freedv *freedv);
|
|
int (*freedv_get_n_max_modem_samples) (struct freedv *freedv);
|
|
int (*freedv_get_n_nom_modem_samples) (struct freedv *freedv);
|
|
int (*freedv_get_total_bits) (struct freedv *freedv);
|
|
int (*freedv_get_total_bit_errors) (struct freedv *freedv);
|
|
int (*freedv_get_modem_sample_rate) (struct freedv *freedv);
|
|
int (*freedv_get_speech_sample_rate) (struct freedv *freedv);
|
|
//
|
|
int (*freedv_get_sync) (struct freedv *freedv);
|
|
void (*freedv_set_callback_protocol) (struct freedv *freedv, freedv_callback_protorx rx, freedv_callback_prototx tx, void *callback_state);
|
|
void (*freedv_set_callback_data) (struct freedv *freedv, freedv_callback_datarx datarx, freedv_callback_datatx datatx, void *callback_state);
|
|
|
|
//
|
|
// checkAvxSupport
|
|
//
|
|
// This function was copied from freedv, file fdmdv2_main.cpp and tweaked for use here
|
|
//
|
|
// Tests the underlying platform for AVX support. 2020 needs AVX support to run
|
|
// in real-time, and old processors do not offer AVX support
|
|
//
|
|
|
|
#if defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86)
|
|
#include <cpuid.h>
|
|
static int checkAvxSupport(void)
|
|
{
|
|
|
|
int isAvxPresent = 0;
|
|
uint32_t eax, ebx, ecx, edx;
|
|
eax = ebx = ecx = edx = 0;
|
|
__cpuid(1, eax, ebx, ecx, edx);
|
|
|
|
if (ecx & (1<<27) && ecx & (1<<28)) {
|
|
// CPU supports XSAVE and AVX
|
|
uint32_t xcr0, xcr0_high;
|
|
asm("xgetbv" : "=a" (xcr0), "=d" (xcr0_high) : "c" (0));
|
|
isAvxPresent = (xcr0 & 6) == 6; // AVX state saving enabled?
|
|
}
|
|
return isAvxPresent;
|
|
}
|
|
#else
|
|
static int checkAvxSupport(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Called when a new packet can be sent */
|
|
void my_datatx(void *callback_state, unsigned char *packet, size_t *size) {
|
|
*size = 0;
|
|
}
|
|
|
|
static void GetAddrs(void)
|
|
{
|
|
handle_index = 1;
|
|
GET_HANDLE1;
|
|
if ( ! hLib) {
|
|
handle_index = 2;
|
|
GET_HANDLE2;
|
|
if ( ! hLib) {
|
|
handle_index = 4;
|
|
GET_HANDLE4;
|
|
}
|
|
}
|
|
if (hLib) {
|
|
freedv_get_version = GET_ADDR("freedv_get_version");
|
|
if (freedv_get_version != NULL) {
|
|
freedv_version = freedv_get_version();
|
|
if (DEBUG)
|
|
printf("FreeDV codec2 library %d found, version %d\n", handle_index, freedv_version);
|
|
}
|
|
else {
|
|
freedv_version = -2;
|
|
if (DEBUG)
|
|
printf("FreeDV codec2 library %d found, but no freedv_get_version() !!!\n", handle_index);
|
|
CLOSE_LIB;
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
handle_index = -1;
|
|
if (DEBUG)
|
|
printf("Could not find the FreeDV codec2 shared library\n");
|
|
return;
|
|
}
|
|
|
|
// open, close
|
|
freedv_open = GET_ADDR("freedv_open");
|
|
freedv_open_advanced = GET_ADDR("freedv_open_advanced");
|
|
freedv_close = GET_ADDR("freedv_close");
|
|
// Transmit
|
|
freedv_tx = GET_ADDR("freedv_tx");
|
|
freedv_comptx = GET_ADDR("freedv_comptx");
|
|
// Receive
|
|
freedv_nin = GET_ADDR("freedv_nin");
|
|
freedv_rx = GET_ADDR("freedv_rx");
|
|
freedv_floatrx = GET_ADDR("freedv_floatrx");
|
|
freedv_comprx = GET_ADDR("freedv_comprx");
|
|
// Set parameters
|
|
freedv_set_callback_txt = GET_ADDR("freedv_set_callback_txt");
|
|
freedv_set_callback_protocol = GET_ADDR("freedv_set_callback_protocol");
|
|
freedv_set_callback_data = GET_ADDR("freedv_set_callback_data");
|
|
freedv_set_test_frames = GET_ADDR("freedv_set_test_frames");
|
|
freedv_set_smooth_symbols = GET_ADDR("freedv_set_smooth_symbols");
|
|
freedv_set_squelch_en = GET_ADDR("freedv_set_squelch_en");
|
|
freedv_set_snr_squelch_thresh = GET_ADDR("freedv_set_snr_squelch_thresh");
|
|
freedv_set_tx_bpf = GET_ADDR("freedv_set_tx_bpf");
|
|
// Get parameters
|
|
freedv_get_modem_stats = GET_ADDR("freedv_get_modem_stats");
|
|
freedv_get_test_frames = GET_ADDR("freedv_get_test_frames");
|
|
freedv_get_n_speech_samples = GET_ADDR("freedv_get_n_speech_samples");
|
|
freedv_get_n_max_modem_samples = GET_ADDR("freedv_get_n_max_modem_samples");
|
|
freedv_get_n_nom_modem_samples = GET_ADDR("freedv_get_n_nom_modem_samples");
|
|
freedv_get_total_bits = GET_ADDR("freedv_get_total_bits");
|
|
freedv_get_total_bit_errors = GET_ADDR("freedv_get_total_bit_errors");
|
|
freedv_get_sync = GET_ADDR("freedv_get_sync"); // requires version 11
|
|
if( freedv_version >= 11)
|
|
freedv_get_modem_sample_rate = GET_ADDR("freedv_get_modem_sample_rate"); // requires version 11 or later
|
|
if (freedv_version >= 12)
|
|
freedv_get_speech_sample_rate = GET_ADDR("freedv_get_speech_sample_rate"); // requires version 12
|
|
return;
|
|
}
|
|
|
|
static int quisk_freedv_rx(short * sSamples, double * dsamples, int count, int bank) // Called from the sound thread.
|
|
{ // Input digital modulation is sSamples; decoded voice is dsamples. Each "bank" is a stream of audio.
|
|
// caller will have decimated input audio down to n_modem_sample_rate and reduced block size to match reduction
|
|
// output data will be returned at n_speech_sample_rate with corresponding block sizes
|
|
int i, nout, need, have, sync;
|
|
int n_speech_samples;
|
|
struct freedv * hF;
|
|
struct _rx_channel * pCh;
|
|
int nCountOut = 1; // In case we have a higher (or lower) value for o/p count
|
|
|
|
if (sSamples == NULL) { // shutdown
|
|
for (i = 0; i < MAX_RECEIVERS; i++) {
|
|
if (rx_channel[i].demod_in) {
|
|
free(rx_channel[i].demod_in);
|
|
rx_channel[i].demod_in = NULL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (bank < 0 || bank >= MAX_RECEIVERS)
|
|
return 0;
|
|
hF = rx_channel[bank].hFreedv;
|
|
if ( ! hF)
|
|
return 0;
|
|
pCh = rx_channel + bank;
|
|
n_speech_samples = freedv_get_n_speech_samples(hF);
|
|
// if (say) input modem rate is the 2020 mode rate of 8000 sa/sec
|
|
// and output rate is the corresponding 16000 sa/sec then for an input
|
|
// block size of N, outbut 2N samples block size.
|
|
// Apply a multiplier/divisor to 'count' for the output samples count
|
|
if(n_speech_sample_rate >= n_modem_sample_rate ) {
|
|
int nRatio = n_speech_sample_rate / n_modem_sample_rate;
|
|
if( nRatio >= 1 && nRatio <= 6)
|
|
nCountOut = nRatio * count;
|
|
}
|
|
else {
|
|
int nRatio = n_modem_sample_rate / n_speech_sample_rate;
|
|
if( nRatio >= 1 && nRatio <= 6)
|
|
nCountOut = count / nRatio;
|
|
}
|
|
nout = 0;
|
|
need = freedv_nin(hF);
|
|
for (i = 0; i < count; i++) {
|
|
pCh->demod_in[pCh->rxdata_index] = sSamples[i];
|
|
pCh->rxdata_index++;
|
|
if (pCh->rxdata_index >= need) {
|
|
if (pCh->speech_available + n_speech_samples < SPEECH_BUF_SIZE) { // check for buffer space
|
|
have = freedv_rx(hF, pCh->speech_out + pCh->speech_available, pCh->demod_in);
|
|
if (freedv_version > 10)
|
|
sync = freedv_get_sync(hF);
|
|
else
|
|
freedv_get_modem_stats(hF, &sync, NULL);
|
|
if (freedv_current_mode == 0) { // mode 1600
|
|
if (sync) // throw away speech if not in sync
|
|
pCh->speech_available += have;
|
|
}
|
|
else if (pCh->speech_available < SPEECH_BUF_SIZE * 2 / 3) {
|
|
pCh->speech_available += have; // keep speech if there is space
|
|
}
|
|
else {
|
|
if (DEBUG) printf("Close to maximum in speech output buffer\n");
|
|
}
|
|
}
|
|
else { // no space in buffer
|
|
if (DEBUG) printf("Overflow in speech output buffer\n");
|
|
}
|
|
pCh->rxdata_index = 0;
|
|
need = freedv_nin(hF);
|
|
}
|
|
}
|
|
if ( ! pCh->playing) {
|
|
if (pCh->speech_available >= 2 * n_speech_samples) {
|
|
pCh->playing = 1;
|
|
}
|
|
else { // return zero samples
|
|
for (i = 0; i < nCountOut; i++)
|
|
dsamples[i] = 0;
|
|
//if (DEBUG) printf("Rx buffer playing %d available %d\n", pCh->playing, pCh->speech_available);
|
|
return nCountOut;
|
|
}
|
|
}
|
|
for (nout = 0; nout < pCh->speech_available && nout < nCountOut; nout++)
|
|
dsamples[nout] = pCh->speech_out[nout] * ((double)CLIP32 / CLIP16 * 0.99);
|
|
if (nout) {
|
|
pCh->speech_available -= nout;
|
|
memmove(pCh->speech_out, pCh->speech_out + nout, (pCh->speech_available) * sizeof(short));
|
|
}
|
|
if ( ! pCh->speech_available) {
|
|
pCh->playing = 0;
|
|
while (nout < nCountOut)
|
|
dsamples[nout++] = 0;
|
|
}
|
|
//if (DEBUG) printf("Rx buffer playing %d available %d\n", pCh->playing, pCh->speech_available);
|
|
return nout;
|
|
}
|
|
|
|
static int quisk_freedv_tx(complex double * cSamples, double * dsamples, int count, int is_real) // Called from the sound thread.
|
|
{ // Input voice samples are dsamples; output digital modulation is cSamples.
|
|
int i, nout;
|
|
int n_speech_samples;
|
|
int n_nom_modem_samples;
|
|
static COMP * mod_out = NULL;
|
|
static short * speech_in = NULL;
|
|
static int speech_index=0, mod_index=0;
|
|
// g8kbb - add for 800XA
|
|
static short *real_mod_out = NULL;
|
|
|
|
if (dsamples == NULL) { // shutdown
|
|
return 0; // nothing to do
|
|
}
|
|
if ( ! rx_channel[0].hFreedv)
|
|
return 0;
|
|
n_speech_samples = freedv_get_n_speech_samples(rx_channel[0].hFreedv);
|
|
n_nom_modem_samples = freedv_get_n_nom_modem_samples(rx_channel[0].hFreedv);
|
|
// find if need to output at faster rate
|
|
int nRatio = n_modem_sample_rate / n_speech_sample_rate;
|
|
if( nRatio < 1 || nRatio > 6 )
|
|
nRatio = 1;
|
|
if (mod_out == NULL) { // initialize
|
|
mod_out = (COMP *)malloc(sizeof(COMP) * MAX_MODEM_SAMPLES);
|
|
memset(mod_out, 0, sizeof(COMP) * MAX_MODEM_SAMPLES);
|
|
speech_in = (short*)malloc(sizeof(short) * MAX_SPEECH_SAMPLES);
|
|
speech_index=0;
|
|
mod_index=0;
|
|
real_mod_out = (short *)malloc(sizeof(short) * MAX_MODEM_SAMPLES);
|
|
memset(real_mod_out, 0, sizeof(short) * MAX_MODEM_SAMPLES);
|
|
//if( DEBUG ) printf("Initialise tx buffer size = %d, i/p sample size = %d\n", n_nom_modem_samples, n_speech_samples );
|
|
}
|
|
nout = 0;
|
|
for (i = 0; i < count; i++) {
|
|
speech_in[speech_index++] = (short)dsamples[i];
|
|
if (speech_index >= n_speech_samples) {
|
|
// Calculate a new block, but first write out the rest of the old block
|
|
// 800xa uses real not complex
|
|
if( is_real ) {
|
|
for ( ; mod_index < n_nom_modem_samples; mod_index++)
|
|
cSamples[nout++] = real_mod_out[mod_index];
|
|
freedv_tx(rx_channel[0].hFreedv, real_mod_out, speech_in);
|
|
}
|
|
else {
|
|
for ( ; mod_index < n_nom_modem_samples; mod_index++)
|
|
cSamples[nout++] = mod_out[mod_index].real + I * mod_out[mod_index].imag;
|
|
freedv_comptx(rx_channel[0].hFreedv, mod_out, speech_in);
|
|
}
|
|
//if( DEBUG ) printf("Block processed, speech index=%d, n_speech_samples=%d, count=%d\n", speech_index, n_speech_samples, count );
|
|
mod_index = 0;
|
|
speech_index = 0;
|
|
}
|
|
else { // write out samples
|
|
for( int j=0; j<nRatio; j++) {
|
|
if (mod_index < n_nom_modem_samples ) {
|
|
if( is_real ) {
|
|
cSamples[nout++] = real_mod_out[mod_index];
|
|
}
|
|
else {
|
|
cSamples[nout++] = mod_out[mod_index].real + I * mod_out[mod_index].imag;
|
|
}
|
|
mod_index++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (rxMode == FDV_L)
|
|
for (i = 0; i < nout; i++)
|
|
cSamples[i] = conj(cSamples[i]);
|
|
return nout;
|
|
}
|
|
|
|
#define TX_MSG_SIZE 80
|
|
static char quisk_tx_msg[TX_MSG_SIZE];
|
|
|
|
static char get_next_tx_char(void * callback_state)
|
|
{
|
|
char c;
|
|
static int index = 0;
|
|
|
|
c = quisk_tx_msg[index++];
|
|
if (index >= TX_MSG_SIZE)
|
|
index = 0;
|
|
if ( ! c) {
|
|
index = 0;
|
|
c = quisk_tx_msg[index++];
|
|
}
|
|
return c;
|
|
}
|
|
|
|
#define RX_MSG_SIZE 80
|
|
static char quisk_rx_msg[RX_MSG_SIZE + 1];
|
|
|
|
static void put_next_rx_char(void * callback_state, char ch)
|
|
{
|
|
if (ch == '\n' || ch == '\r')
|
|
ch = ' ';
|
|
if (ch < 32 || ch > 126) // printable characters
|
|
return;
|
|
if (strlen(quisk_rx_msg) < RX_MSG_SIZE)
|
|
strncat(quisk_rx_msg, &ch, 1);
|
|
}
|
|
|
|
PyObject * quisk_freedv_get_rx_char(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
{
|
|
PyObject * txt;
|
|
|
|
if (!PyArg_ParseTuple (args, ""))
|
|
return NULL;
|
|
txt = PyString_FromString(quisk_rx_msg);
|
|
quisk_rx_msg[0] = 0;
|
|
return txt;
|
|
}
|
|
|
|
static void CloseFreedv(void) // Called from the GUI thread or sound thread
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < MAX_RECEIVERS; i++) {
|
|
if (rx_channel[i].hFreedv) {
|
|
freedv_close(rx_channel[i].hFreedv);
|
|
rx_channel[i].hFreedv = NULL;
|
|
}
|
|
}
|
|
quisk_freedv_rx(NULL, NULL, 0, 0);
|
|
quisk_freedv_tx(NULL, NULL, 0, 0);
|
|
freedv_current_mode = -1;
|
|
}
|
|
|
|
static int OpenFreedv(void) // Called from the GUI thread or sound thread
|
|
{
|
|
int i;
|
|
struct freedv * hF;
|
|
|
|
if ( ! hLib)
|
|
GetAddrs(); // Get the entry points for funtions in the codec2 library
|
|
if (DEBUG) {
|
|
switch (handle_index) {
|
|
case 1:
|
|
printf("freedv_open: system codec2 library found, version %d\n", freedv_version);
|
|
break;
|
|
case 2:
|
|
printf("freedv_open: codec2 library freedvpkg/libcodec2.dll|so found, version %d\n", freedv_version);
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
printf("freedv_open: codec2 library freedvpkg/libcodec2_64 found, version %d\n", freedv_version);
|
|
break;
|
|
default:
|
|
printf("freedv_open: Could not find the FreeDV codec2 shared library\n");
|
|
break;
|
|
}
|
|
}
|
|
if (freedv_version < 10) {
|
|
CloseFreedv();
|
|
requested_mode = -1;
|
|
if (DEBUG)
|
|
printf("freedv_open: Failure because version is less than 10\n");
|
|
return 0; // failure
|
|
}
|
|
if (requested_mode == FREEDV_MODE_2020) {
|
|
if (checkAvxSupport() == 0) {
|
|
CloseFreedv();
|
|
requested_mode = -1;
|
|
if (DEBUG)
|
|
printf("freedv_open: Failure because mode 2020 requires Avx support\n");
|
|
return 0; // failure
|
|
}
|
|
if (handle_index >= 3) { // Quisk provided codec2
|
|
CloseFreedv();
|
|
requested_mode = -1;
|
|
if (DEBUG)
|
|
printf("freedv_open: Failure because mode 2020 requires a system installation of codec2\n");
|
|
return 0; // failure
|
|
}
|
|
}
|
|
|
|
if ((requested_mode == FREEDV_MODE_700D || requested_mode == FREEDV_MODE_700E) && freedv_open_advanced) {
|
|
struct freedv_advanced adv;
|
|
memset(&adv, 0, sizeof(adv));
|
|
adv.interleave_frames = interleave_frames;
|
|
hF = freedv_open_advanced(requested_mode, &adv);
|
|
}
|
|
else {
|
|
hF = freedv_open(requested_mode);
|
|
}
|
|
if (hF == NULL) {
|
|
CloseFreedv();
|
|
requested_mode = -1;
|
|
return 0; // failure
|
|
}
|
|
rx_channel[0].hFreedv = hF;
|
|
quisk_dvoice_freedv(&quisk_freedv_rx, &quisk_freedv_tx);
|
|
if (quisk_tx_msg[0])
|
|
freedv_set_callback_txt(hF, &put_next_rx_char, &get_next_tx_char, NULL);
|
|
else
|
|
freedv_set_callback_txt(hF, &put_next_rx_char, NULL, NULL);
|
|
if (freedv_set_callback_protocol)
|
|
freedv_set_callback_protocol(hF, NULL, NULL, NULL);
|
|
if (freedv_set_callback_data)
|
|
freedv_set_callback_data(hF, NULL, my_datatx, NULL);
|
|
freedv_set_squelch_en(hF, quisk_freedv_squelch);
|
|
if (freedv_set_tx_bpf)
|
|
freedv_set_tx_bpf(hF, quisk_set_tx_bpf);
|
|
n_max_modem_samples = freedv_get_n_max_modem_samples(hF);
|
|
n_speech_sample_rate = 8000;
|
|
if (freedv_version >= 12 && freedv_get_speech_sample_rate)
|
|
n_speech_sample_rate = freedv_get_speech_sample_rate(hF);
|
|
n_modem_sample_rate = 8000;
|
|
if (freedv_version >= 11 && freedv_get_modem_sample_rate)
|
|
n_modem_sample_rate = freedv_get_modem_sample_rate(hF);
|
|
for (i = 0; i < MAX_RECEIVERS; i++) {
|
|
rx_channel[i].rxdata_index = 0;
|
|
rx_channel[i].speech_available = 0;
|
|
rx_channel[i].playing = 0;
|
|
if (rx_channel[i].demod_in)
|
|
free(rx_channel[i].demod_in);
|
|
rx_channel[i].demod_in = (short *)malloc(sizeof(short) * n_max_modem_samples);
|
|
if( rx_channel[i].demod_in == NULL) {
|
|
CloseFreedv();
|
|
requested_mode = -1;
|
|
return 0; // failure
|
|
}
|
|
if (i > 0) {
|
|
rx_channel[i].hFreedv = freedv_open(requested_mode);
|
|
if (rx_channel[i].hFreedv)
|
|
freedv_set_squelch_en(rx_channel[i].hFreedv, quisk_freedv_squelch);
|
|
}
|
|
}
|
|
if (DEBUG) printf("n_nom_modem_samples %d\n", freedv_get_n_nom_modem_samples(rx_channel[0].hFreedv));
|
|
if (DEBUG) printf("n_speech_samples %d\n", freedv_get_n_speech_samples(rx_channel[0].hFreedv));
|
|
if (DEBUG) printf("n_max_modem_samples %d\n", n_max_modem_samples);
|
|
if (DEBUG) {
|
|
if (freedv_version >= 11) printf("modem_sample_rate %d\n", n_modem_sample_rate);
|
|
if (freedv_version >= 12) printf("speech_sample_rate %d\n", n_speech_sample_rate);
|
|
}
|
|
freedv_current_mode = requested_mode;
|
|
return 1; // success
|
|
}
|
|
|
|
void quisk_check_freedv_mode(void)
|
|
{ // see if we need to change the mode
|
|
if (requested_mode == freedv_current_mode)
|
|
return;
|
|
if (DEBUG) printf("Change in mode to %d\n", requested_mode);
|
|
CloseFreedv();
|
|
if (requested_mode >= 0)
|
|
OpenFreedv();
|
|
else
|
|
requested_mode = -1;
|
|
}
|
|
|
|
PyObject * quisk_freedv_open(PyObject * self, PyObject * args) // Called from the GUI thread before freedv is open
|
|
{
|
|
if (!PyArg_ParseTuple (args, ""))
|
|
return NULL;
|
|
return PyInt_FromLong(OpenFreedv());
|
|
}
|
|
|
|
PyObject * quisk_freedv_close(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
{
|
|
if (!PyArg_ParseTuple (args, ""))
|
|
return NULL;
|
|
requested_mode = -1; // request close
|
|
Py_INCREF (Py_None);
|
|
return Py_None;
|
|
}
|
|
|
|
PyObject * quisk_freedv_set_options(PyObject * self, PyObject * args, PyObject * keywds) // Called from the GUI thread.
|
|
{ // Call with keyword arguments ONLY to change parameters. Call before quisk_freedv_open() to set an initial mode.
|
|
int mode=-1; // Call again to change the mode.
|
|
int bpf=-1;
|
|
char * ptMsg=NULL;
|
|
static char * kwlist[] = {"mode", "tx_msg", "DEBUG", "squelch", "interleave_frames", "set_tx_bpf", NULL} ;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|isiiii", kwlist, &mode, &ptMsg, &DEBUG, &quisk_freedv_squelch, &interleave_frames, &bpf))
|
|
return NULL;
|
|
if (ptMsg)
|
|
strMcpy(quisk_tx_msg, ptMsg, TX_MSG_SIZE);
|
|
if (bpf != -1) {
|
|
quisk_set_tx_bpf = bpf;
|
|
if (freedv_set_tx_bpf && rx_channel[0].hFreedv)
|
|
freedv_set_tx_bpf(rx_channel[0].hFreedv, quisk_set_tx_bpf);
|
|
}
|
|
if (mode == -1) { // mode was not entered
|
|
;
|
|
}
|
|
else if (mode == FREEDV_MODE_700E && freedv_version >= 14) {
|
|
requested_mode = mode;
|
|
}
|
|
else if (mode == FREEDV_MODE_2020) {
|
|
if (checkAvxSupport() && handle_index <= 2 && freedv_version >= 13)
|
|
requested_mode = mode;
|
|
}
|
|
else if (freedv_version == 10) {
|
|
if (mode == 0)
|
|
requested_mode = mode;
|
|
}
|
|
else if (freedv_version == 11) {
|
|
if (mode <= 2)
|
|
requested_mode = mode;
|
|
}
|
|
else {
|
|
requested_mode = mode;
|
|
}
|
|
return PyInt_FromLong(requested_mode); // Return the mode
|
|
}
|
|
|
|
PyObject * quisk_freedv_get_snr(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
{
|
|
float snr_est = 0.0;
|
|
|
|
if (!PyArg_ParseTuple (args, ""))
|
|
return NULL;
|
|
if (rx_channel[0].hFreedv)
|
|
freedv_get_modem_stats(rx_channel[0].hFreedv, NULL, &snr_est);
|
|
return PyFloat_FromDouble(snr_est);
|
|
}
|
|
|
|
PyObject * quisk_freedv_get_version(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
{
|
|
if (!PyArg_ParseTuple (args, ""))
|
|
return NULL;
|
|
if ( ! hLib)
|
|
GetAddrs(); // Get the entry points for funtions in the codec2 library
|
|
return PyInt_FromLong(freedv_version);
|
|
}
|
|
|
|
PyObject * quisk_freedv_set_squelch_en(PyObject * self, PyObject * args) // Called from the GUI thread.
|
|
{
|
|
int i,state;
|
|
|
|
if (!PyArg_ParseTuple (args, "i", &state))
|
|
return NULL;
|
|
quisk_freedv_squelch = state;
|
|
for (i = 0; i < MAX_RECEIVERS; i++) {
|
|
if( rx_channel[i].hFreedv )
|
|
freedv_set_squelch_en(rx_channel[i].hFreedv, state);
|
|
}
|
|
return PyInt_FromLong(state);
|
|
}
|
|
|
|
|