diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 3055b6b..2733dbf 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,13 @@
+Quisk Version 4.2.45 October 2025
+==================================
+The new FreeDV including RADE mode is now a built-in mode of Quisk. Just press "FDV" to talk FreeDV.
+In brief, install FreeDV-GUI from the web site. Then configure the usual Quisk digital input and
+output that you use for digital modes. In FreeDV-GUI use the digital input and output. For the
+speakers and microphone use the same ones as Quisk. Don't forget to configure your call sign in
+FreeDV so it works with FreeDV Reporter. For detailed instructions see http://james.ahlstrom.name/rade.html.
+
+The Quisk digital output device is now always active; a DGT mode is not required.
+
Quisk Version 4.2.44 September 2025
====================================
Quisk now has Pure Signal for the Hermes Lite2. This is baseband predistortion to reduce IMD products.
diff --git a/__init__.py b/__init__.py
index e9a2a64..24b9b9b 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,2 +1,2 @@
-#Quisk version 4.2.44
+#Quisk version 4.2.45
from .quisk import main
diff --git a/ac2yd/control_common.py b/ac2yd/control_common.py
index 24d8bdc..9e492d0 100644
--- a/ac2yd/control_common.py
+++ b/ac2yd/control_common.py
@@ -395,7 +395,7 @@ class ControlCommon(BaseHardware): # This is the Hardware class for the control
for idName in ("Mute", "NR2", "AGC", "Sqlch", "NB 1", "Notch", "Test 1", "Spot", "FDX", "PTT", "VOX"):
self.RemoteCtlSend("%s;%d\n" % (idName, app.idName2Button[idName].GetIndex()))
# Menus
- for menu in (app.NB_menu, app.split_menu, app.freedv_menu, app.smeter_menu):
+ for menu in (app.NB_menu, app.split_menu, app.smeter_menu):
if menu:
for nid in menu.id2data:
menu_item = menu.FindItemById(nid)
diff --git a/docs.html b/docs.html
index 61c4a67..50a5657 100644
--- a/docs.html
+++ b/docs.html
@@ -200,7 +200,8 @@ If you have Python 3 installed on your computer, just continue to use that versi
On Windows Quisk requires Python 3.8, 3.9, 3.10, 3.11, 3.12 or 3.13.
If you want to install a more recent version of Python, uninstall the old version first.
Running multiple versions of Python is possible but can be confusing and is not necessary.
-A Quisk installation is needed for each version of Python you have.
+A Quisk installation is needed for each version of Python you have. If you upgrade to a newer Python
+you must re-install Quisk. But your Quisk settings will be preserved.
Windows Initial Installation
diff --git a/freedv.c b/freedv.c
index bf5a72e..bf04050 100644
--- a/freedv.c
+++ b/freedv.c
@@ -1,681 +1 @@
-#include
-#include
-#include
-#include // Use native C99 complex type for fftw3
-#include
-
-#include "quisk.h"
-#include "freedv.h"
-#include
-
-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
-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
-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
-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= 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);
-}
-
-
+// This file is no longer used.
diff --git a/freedv.h b/freedv.h
index c894c7c..f5df7e1 100644
--- a/freedv.h
+++ b/freedv.h
@@ -1,24 +1 @@
-extern int freedv_current_mode; // the current FreeDV running mode
-
-// available speech modes
-#define FREEDV_MODE_1600 0
-#define FREEDV_MODE_2400A 3
-#define FREEDV_MODE_2400B 4
-#define FREEDV_MODE_800XA 5
-#define FREEDV_MODE_700C 6
-#define FREEDV_MODE_700D 7
-#define FREEDV_MODE_700E 13
-#define FREEDV_MODE_2020 8
-#define FREEDV_MODE_2020B 16
-
-// available data modes
-#define FREEDV_MODE_FSK_LDPC 9
-#define FREEDV_MODE_DATAC1 10
-#define FREEDV_MODE_DATAC3 12
-#define FREEDV_MODE_DATAC0 14
-#define FREEDV_MODE_DATAC4 18
-#define FREEDV_MODE_DATAC13 19
-
-// Sample rates used
-#define FREEDV_FS_8000 8000
-#define FREEDV_FS_16000 16000
+// This header file is no longer used.
diff --git a/freedvpkg/README.txt b/freedvpkg/README.txt
index bf20708..68769c7 100644
--- a/freedvpkg/README.txt
+++ b/freedvpkg/README.txt
@@ -1,129 +1 @@
-
-Note: The directory freedvpkg no longer contains source files. It only contains copies of
- the codec2 libraries for use by Quisk. If any other files are present, delete them.
-
-FreeDV and Directory freedvpkg
-==============================
-
-FreeDV is the combination of the codec2 codec and the fdmdv modem. It provides digital voice
-suitable for HF and VHF transmission. Quisk has native (built-in) support for
-FreeDV. Just push the FDV mode and talk. This freedvpkg directory contains copies of the
-codec2 libraries for use by Quisk.
-
-You can also use the separate FreeDV program available at freedv.org, and the Quisk DGT-U mode
-which attaches to external digital programs. The setup is identical to fldigi and other external
-digital programs.
-
-Quisk will add the FDV button if specified on the Config/radio/Controls tab.
-If there is a problem with the freedv module, the button will be grayed out.
-
-The freedv module requires the codec2 library. This library is included for Windows and for
-Ubuntu 18.04 LTS 32-bit and 64-bit. For other systems (such as ARM) you will need to build another
-codec2. Just try the FDV mode and see if it works. It should always work on Windows, and may work
-on Linux. If the FDV button is grayed out, you need a different codec2 than the one included. Make
-a new codec2 library, and copy it to freedvpkg or to the correct system directory.
-
-2020 mode
-=========
-You can now use 2020 mode with Quisk. Note however that 2020 mode requires a processor with AVX.
-Also note a Raspberry PI (model 4B) does not have enough processing power even if neon is used
-and it is overclocked.
-
-There are two parts to this:
-1. build LPCNet
-2. rebuild codec2
-The details are found here: https://github.com/drowe67/codec2#freedv-2020-support-building-with-lpcnet
-
-Both liblpcnetfreedv and libcodec2 are needed. If you are running under windows, the resultant
-liblpcnetfreedv.dll file needs to be placed in the quisk folder. Libcodec2 may be placed in the
-quisk freedvpkg folder. Or install the DLL's in the correct system folders.
-
-Search Order
-============
-
-Quisk will search for a valid codec2 library in this order on Windows:
-1. The system codec2 library installed outside of Quisk by another program.
-2. freedvpkg/libcodec2.dll. Not included. You can copy your own codec2 here but a system-wide installation is better.
-3. freedvpkg/libcodec2_32.dll. The 32-bit codec2 shipped with Quisk.
-4. freedvpkg/libcodec2_64.dll. The 64-bit codec2 shipped with Quisk.
-
-Quisk will search for a valid codec2 library in this order on Linux:
-1. The system codec2 library installed outside of Quisk by another program.
-2. freedvpkg/libcodec2.so. Not included. You can copy your own codec2 here but a system-wide installation is better.
-3. freedvpkg/libcodec2_32.so. The 32-bit codec2 shipped with Quisk.
-4. freedvpkg/libcodec2_64.so. The 64-bit codec2 shipped with Quisk.
-
-How to Build a New codec2
-=========================
-
-The source for codec2 is available on github. Or google for other sources or perhaps a pre-built library.
-If you need to compile codec2 from source, first change to a suitable directory (not the Quisk directory)
-and download the source with git:
-
- git clone https://github.com/drowe67/codec2.git
-
-Then build codec2 using the directions found at https://github.com/drowe67/codec2/blob/master/INSTALL.
-The directions given below are current as of June 2020, but check for changes.
-Then copy the codec2 library to the freedvpkg directory under Quisk.
-
-Build a New codec2 on Linux
-===========================
-Create the codec2 shared library. This assumes a 64-bit linux. Change the directory
-name from build_linux64 to build_linux32 for 32-bit linux. Note the "../".
-
- cd codec2
- mkdir build_linux64
- cd build_linux64
- cmake -DCMAKE_BUILD_TYPE=Release ../
- make
- cd src
- cp libcodec2.so my-quisk-directory/freedvpkg
-
-Build a New 32-bit codec2 on 64-bit Linux
-=========================================
-Make sure package libc6-dev-i386 is installed.
-Create the codec2 shared library. Note the "../".
-
- cd codec2
- mkdir build_linux32
- cd build_linux32
- export CFLAGS=-m32
- cmake -DCMAKE_BUILD_TYPE=Release ../
- make
- cd src
- cp libcodec2.so my-quisk-directory/freedvpkg
-
-Build a New codec2 on Windows
-=============================
-For Windows you need to install MinGW-w64, MSYS2, and g++. Use the mingw shell. The Speex libraries
-are not needed by codec2, but are required for the Unit Test modules. To build the Unit Test modules,
-you need to install Speex and add -DSPEEXDSP_INCLUDE_DIR=../speex/include/speex -DSPEEXDSP_LIBRARY=../speex/bin.
-
- # Use mingw32.exe 32-bit shell:
- cd codec2
- mkdir build_win32
- cd build_win32
- cmake -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -DUNITTEST=OFF ../
- make codec2
- cd src
- cp libcodec2.dll my-quisk-directory/freedvpkg
-
- # Use mingw64.exe 64-bit shell:
- cd codec2
- mkdir build_win64
- cd build_win64
- cmake -G "MSYS Makefiles" -DCMAKE_BUILD_TYPE=Release -DUNITTEST=OFF -DCMAKE_SYSTEM_PROCESSOR=x86_64 ../
- make codec2
- cd src
- cp libcodec2.dll my-quisk-directory/freedvpkg
-
-Testing
-=======
-You can just start Quisk and see if the FDV button is not grayed out, and FDV works. Or you can
-test the import of freedv and look for error messages.
-
- cd my-quisk-directory
- python # Use the correct Python 2 or 3 in 32 or 64 bit
- import _quisk
- _quisk.freedv_get_version() # This should return 13 or higher for a recent codec2
-
+This directory is no longer used by FreeDV. Please see the documentation.
diff --git a/freedvpkg/libcodec2_64.dll b/freedvpkg/libcodec2_64.dll
deleted file mode 100644
index 5c6ee52..0000000
Binary files a/freedvpkg/libcodec2_64.dll and /dev/null differ
diff --git a/freedvpkg/libcodec2_64.so b/freedvpkg/libcodec2_64.so
deleted file mode 100644
index dd68024..0000000
Binary files a/freedvpkg/libcodec2_64.so and /dev/null differ
diff --git a/microphone.c b/microphone.c
index 726f8df..cc8a99c 100644
--- a/microphone.c
+++ b/microphone.c
@@ -8,7 +8,6 @@
#include
#include "microphone.h"
#include "filter.h"
-#include "freedv.h"
#ifdef MS_WINDOWS
#include
@@ -245,11 +244,9 @@ static void init_alc(struct alc * pt, int size)
case DGT_U:
case DGT_L:
case DGT_IQ:
- pt->gain_now[i] = 1.4;
- break;
case FDV_U:
case FDV_L:
- pt->gain_now[i] = 2.0;
+ pt->gain_now[i] = 1.4;
break;
default:
pt->gain_now[i] = 1.0;
@@ -617,7 +614,7 @@ static int tx_filter_digital(complex double * filtered, int count)
quisk_filt_dInit(&filter1, quiskDgtFilt48Coefs, sizeof(quiskDgtFilt48Coefs)/sizeof(double)); // pass 1350, stop 1650
}
if ( ! filtered) { // Change to rxMode
- quisk_filt_tune(&filter1, 1650.0 / 48000, rxMode != DGT_L && rxMode != LSB);
+ quisk_filt_tune(&filter1, 1650.0 / 48000, rxMode != DGT_L && rxMode != LSB && rxMode != FDV_L);
return 0;
}
for (i = 0; i < count; i++) // FIR bandpass filter; separate into I and Q
@@ -626,140 +623,6 @@ static int tx_filter_digital(complex double * filtered, int count)
return count;
}
-static int tx_filter_freedv(complex double * filtered, int count, int encode)
-{ // Input samples are creal(filtered), output is filtered.
- // This filter is used for digital voice.
- // Changes by Dave Roberts, G8KBB, for additional modes June, 2020.
- // Modified by N2ADR January, 2024.
- int i;
- // The input sample rate is mic_sample_rate, either 8000 or 48000 sps.
- // The FreeDV codec requires an input speech_sample_rate of either 8000 or 16000 sps depending on the mode. Input is always real.
- // The FreeDV codec will output samples at modem_sample_rate of 8000 or 48000 sps depending on the mode, and output may be real or complex.
- double dtmp, magn, dsample;
- int modem_sample_rate, speech_sample_rate, use_filter, is_real;
- static int samples_size = 0;
- static int do_init = 1;
- static double aaa, bbb, ccc, Xmin, Xmax, Ymax;
- static double time_long, time_short;
- static double inMax=0.3;
- static double * dsamples = NULL;
- static struct quisk_cFilter filter1, filter2;
- static struct quisk_dFilter filtDecim48to16, filtDecim16to8;;
- static struct quisk_cFilter cfiltInterp8to48;
-
- if (do_init) { // initialization
- //QuiskWavWriteOpen(&hWav, "jim.wav", 3, 1, 4, 8000, 1.0 / CLIP16);
- do_init = 0;
- quisk_filt_cInit(&filter1, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double)); // at 48000 sps pass 3000 stop 4000 times two plus 3000
- quisk_filt_cInit(&filter2, quiskFilt53D2Coefs, sizeof(quiskFilt53D2Coefs)/sizeof(double)); // at 8000 sps pass 1500 stop 1800 times two plus 1500
- quisk_filt_cInit(&cfiltInterp8to48, quiskLpFilt48Coefs, sizeof(quiskLpFilt48Coefs)/sizeof(double));
- quisk_filt_dInit(&filtDecim48to16, quiskAudio24p3Coefs, sizeof(quiskAudio24p3Coefs)/sizeof(double)); // pass 6000 stop 8000
- quisk_filt_dInit(&filtDecim16to8, quiskFilt16dec8Coefs, sizeof(quiskFilt16dec8Coefs)/sizeof(double)); // pass 3000 stop 4000
- Ymax = pow(10.0, - 1 / 20.0); // maximum y
- Xmax = pow(10.0, 3 / 20.0); // x where slope is zero; for x > Xmax, y == Ymax
- Xmin = Ymax - fabs(Ymax - Xmax); // x where slope is 1 and y = x; start of compression
- //printf ("Xmin %f\n", Xmin);
- aaa = 1.0 / (2.0 * (Xmin - Xmax)); // quadratic
- bbb = -2.0 * aaa * Xmax;
- ccc = Ymax - aaa * Xmax * Xmax - bbb * Xmax;
- }
- switch (freedv_current_mode) { // n_modem_sample_rate and n_speech_sample_rate may not be valid
- case FREEDV_MODE_2400A:
- case FREEDV_MODE_2400B:
- use_filter = 1; // center frequency 3000
- is_real = 1;
- modem_sample_rate = 48000;
- speech_sample_rate = 8000;
- break;
- case FREEDV_MODE_2020:
- use_filter = 0;
- is_real = 0;
- modem_sample_rate = 8000;
- speech_sample_rate = 16000;
- break;
- case FREEDV_MODE_800XA:
- use_filter = 2; // center frequency 1500
- is_real = 1;
- modem_sample_rate = 8000;
- speech_sample_rate = 8000;
- break;
- default:
- use_filter = 0;
- is_real = 0;
- modem_sample_rate = 8000;
- speech_sample_rate = 8000;
- break;
- }
- if ( ! filtered) { // rxMode changed
- quisk_filt_tune((struct quisk_dFilter *)&filter1, 3000.0 / 48000.0, rxMode != FDV_L);
- quisk_filt_tune((struct quisk_dFilter *)&filter2, 1500.0 / 8000.0, rxMode != FDV_L);
- return 0;
- }
- // check size of dsamples[] buffer
- if (count > samples_size) {
- samples_size = count * 2;
- if (dsamples)
- free(dsamples);
- dsamples = (double *)malloc(samples_size * sizeof(double));
- }
- // copy to dsamples[]
- for (i = 0; i < count; i++)
- dsamples[i] = creal(filtered[i]) / CLIP16; // normalize to 1.0000
- // Decimate to 8000 Hz or to 16000 depending on mode (and hence setting of sample rate)
- if (quisk_sound_state.mic_sample_rate == 48000) {
- switch (speech_sample_rate) {
- case 16000:
- count = quisk_dDecimate(dsamples, count, &filtDecim48to16, 3);
- break;
- case 8000:
- count = quisk_dDecimate(dsamples, count, &filtDecim48to16, 3);
- count = quisk_dDecimate(dsamples, count, &filtDecim16to8, 2);
- break;
- default:
- QuiskPrintf("Failure to convert speech input rate in tx_filter_freedv\n");
- break;
- }
- }
- else if (quisk_sound_state.mic_sample_rate == 8000 && speech_sample_rate != 8000) {
- QuiskPrintf("Failure to convert input rate in tx_filter_freedv\n");
- }
- // Measure average peak input audio level and limit
- dtmp = 1.0 / speech_sample_rate; // sample time
- time_long = 1.0 - exp(- dtmp / 3.000);
- time_short = 1.0 - exp(- dtmp / 0.005);
- for (i = 0; i < count; i++) {
- dsample = dsamples[i];
- magn = fabs(dsample);
- if (magn > inMax)
- inMax = inMax * (1 - time_short) + time_short * magn;
- else if(magn > mic_agc_level)
- inMax = inMax * (1 - time_long) + time_long * magn;
- else
- inMax = inMax * (1 - time_long) + time_long * mic_agc_level;
- dsample = dsample / inMax * Xmin * 0.7;
- magn = fabs(dsample);
- if (magn < Xmin)
- dsamples[i] = dsample;
- else if (magn > Xmax)
- dsamples[i] = copysign(Ymax, dsample);
- else
- dsamples[i] = copysign(aaa * magn * magn + bbb * magn + ccc, dsample);
- dsamples[i] = dsamples[i] * CLIP16;
- }
- //QuiskWavWriteD(&hWav, dsamples, count);
- if (encode && pt_quisk_freedv_tx) // Encode audio into digital modulation
- count = (* pt_quisk_freedv_tx)(filtered, dsamples, count, is_real);
- //quisk_calc_audio_graph(CLIP16, filtered, NULL, count, 0);
- if (use_filter == 1) // convert float samples to complex with an analytic filter
- count = quisk_cCDecimate(filtered, count, &filter1, 1);
- else if (use_filter == 2)
- count = quisk_cCDecimate(filtered, count, &filter2, 1);
- // Interpolate up to 48000
- if (MIC_OUT_RATE != modem_sample_rate )
- count = quisk_cInterpolate(filtered, count, &cfiltInterp8to48, MIC_OUT_RATE / modem_sample_rate);
- return count;
-}
-
PyObject * quisk_get_tx_filter(PyObject * self, PyObject * args)
{ // return the TX filter response to display on the graph
// This is for debugging. Change quisk.py to call QS.get_tx_filter() instead
@@ -1397,6 +1260,8 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
case DGT_L:
case DGT_IQ:
case DGT_FM:
+ case FDV_U: // FreeDV
+ case FDV_L:
count = tx_filter_digital(cSamples, count); // filter samples, minimal processing
process_alc(cSamples, count, &tx_alc, rxMode);
break;
@@ -1404,11 +1269,6 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
count *= interp;
transmit_mic_imd(cSamples, count, quiskImdLevel / 1000.0);
break;
- case FDV_U: // FDV
- case FDV_L:
- count = tx_filter_freedv(cSamples, count, 1);
- process_alc(cSamples, count, &tx_alc, rxMode);
- break;
default:
break;
}
@@ -1499,11 +1359,6 @@ int quisk_process_microphone(int mic_sample_rate, complex double * cSamples, int
count = tx_filter(cSamples, count);
process_alc(cSamples, count, &tx_alc, rxMode);
break;
- case FDV_U: // FDV
- case FDV_L:
- count = tx_filter_freedv(cSamples, count, 0);
- process_alc(cSamples, count, &tx_alc, rxMode);
- break;
default:
for (i = 0; i < count; i++)
cSamples[i] = 0;
@@ -1673,7 +1528,6 @@ void quisk_set_tx_mode(void) // called when the mode rxMode is changed
tx_filter(NULL, 0);
tx_filter_digital(NULL, 0);
transmit_udp(NULL, 0);
- tx_filter_freedv(NULL, 0, 0);
serial_key_samples(NULL, 0);
}
diff --git a/quisk.c b/quisk.c
index 781de63..c41709d 100644
--- a/quisk.c
+++ b/quisk.c
@@ -151,7 +151,6 @@ static int waterfall_scroll_mode = 1; // draw the first lines multiple times
static int quisk_use_sidetone; // is there a sidetone volume control?
static int hl2_txbuf_errors; // errors in the Hermes-Lite2 Tx buffer
static int hl2_txbuf_state; // state machine for errors in the Hermes-Lite2 Tx buffer
-static int freedv_monitor; // pass the Freedv audio to the speakers instead of to the demodulator
static int softrock_correct_freq; // gain and phase corrections for SoftRock
static double softrock_correct_gain;
static double softrock_correct_phase;
@@ -569,15 +568,10 @@ void QuiskWavClose(struct QuiskWav * hWav)
}
#endif
-// These are used for digital voice codecs
-ty_dvoice_codec_rx pt_quisk_freedv_rx;
-ty_dvoice_codec_tx pt_quisk_freedv_tx;
-
-void quisk_dvoice_freedv(ty_dvoice_codec_rx rx, ty_dvoice_codec_tx tx)
+void quisk_dvoice_freedv(void) // Not used.
{
- pt_quisk_freedv_rx = rx;
- pt_quisk_freedv_tx = tx;
}
+
#if 0
static int fFracDecim(double * dSamples, int nSamples, double fdecim)
{ // fractional decimation by fdecim > 1.0
@@ -1851,10 +1845,8 @@ static int quisk_process_decimate(complex double * cSamples, int nSamples, int b
static int quisk_process_demodulate(complex double * cSamples, double * dsamples, int nSamples, int bank, int nFilter, rx_mode_type rx_mode)
{ // Changes here will require changes to get_filter_rate();
int i;
- bool isLSB;
complex double cx;
double d, di, dd;
- static struct AgcState Agc1 = {0.3, 16000, 0}, Agc2 = {0.3, 16000, 0};
//static int count=0;
//static double phase=0;
static struct stStorage {
@@ -2090,6 +2082,7 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
MeasureSquelch[bank].squelch_active = MeasureSquelch[bank].squelch < squelch_level;
break;
case DGT_U: // digital mode DGT-U at 48 ksps
+ case FDV_U:
if (filter_bandwidth[nFilter] < DGT_NARROW_FREQ) { // filter at 6 ksps
quisk_filter_srate = quisk_decim_srate / 8;
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &Storage[bank].HalfBand5);
@@ -2116,6 +2109,7 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
}
break;
case DGT_L: // digital mode DGT-L
+ case FDV_L:
if (filter_bandwidth[nFilter] < DGT_NARROW_FREQ) { // filter at 6 ksps
quisk_filter_srate = quisk_decim_srate / 8;
nSamples = quisk_cDecim2HB45(cSamples, nSamples, &Storage[bank].HalfBand5);
@@ -2154,102 +2148,6 @@ static int quisk_process_demodulate(complex double * cSamples, double * dsamples
}
}
break;
- case FDV_U: // digital voice
- case FDV_L: // Extra modes added by Dave Roberts, G8KBB, June 2020. Modified by N2ADR 2024.
- quisk_check_freedv_mode();
- // current coding assumes input rate n_modem_sample_rate is 8000 or 48000.
- // decimate:
- switch (n_modem_sample_rate) {
- case 8000:
- quisk_filter_srate = quisk_decim_srate / 6;
- nSamples = quisk_cDecimate(cSamples, nSamples, &Storage[bank].filtDecim48to16, 3);
- nSamples = quisk_cDecimate(cSamples, nSamples, &Storage[bank].filtDecim16to8, 2);
- break;
- case 48000:
- quisk_filter_srate = quisk_decim_srate;
- break;
- default:
- quisk_filter_srate = quisk_decim_srate / 6;
- QuiskPrintf("Bad FreeDV modem rate in quisk_process_demodulate\n");
- break;
- }
- // filter and demodulate to mono audio:
- isLSB = (rx_mode == FDV_L);
- complex double cBuf[nSamples];
- for (i = 0; i < nSamples; i++) {
- cx = cRxFilterOut(cSamples[i], bank, nFilter);
- if (isLSB)
- dd = creal(cx) + cimag(cx);
- else
- dd = creal(cx) - cimag(cx);
- if(bank == 0) {
- measure_audio_sum += dd * dd;
- measure_audio_count += 1;
- }
- cBuf[i] = dd;
- }
- // Perhaps decimate by an additional fraction
- if (quisk_decim_srate != 48000) {
- dd = quisk_decim_srate / 48000.0;
- nSamples = cFracDecim(cBuf, nSamples, dd);
- quisk_decim_srate = 48000;
- }
- if (bank == 0)
- process_agc(&Agc1, cBuf, nSamples, 0);
- else
- process_agc(&Agc2, cBuf, nSamples, 0);
- // pass data decimated down to n_modem_sample_rate
- // return data is at n_speech_sample_rate
-#if 0
- static double dmax_in = 0;
- for (i = 0; i < nSamples; i++) {
- dd = fabs(creal(cBuf[i])) / CLIP16;
- if (dd > dmax_in)
- dmax_in = dd;
- }
- if (bank == 0 && measure_audio_count >= quisk_filter_srate * measure_audio_time) {
- //printf("Max sample in %.0lf\n", dmax_in);
- dmax_in = 0;
- }
-#endif
- if (nSamples > n_max_modem_samples) {
- printf ("FreeDV: nSamples %d exceeds max_modem_samples %d\n", nSamples, n_max_modem_samples);
- nSamples = n_max_modem_samples;
- }
- short sBuf[nSamples];
- for (i = 0; i < nSamples; i++)
- sBuf[i] = (short)(creal(cBuf[i]) / CLIP16);
- if (freedv_monitor) {
- for (i = 0; i < nSamples; i++)
- dsamples[i] = (float)sBuf[i] * CLIP16;
- }
- else {
- if (pt_quisk_freedv_rx)
- nSamples = (* pt_quisk_freedv_rx)(sBuf, dsamples, nSamples, bank);
- }
-#if 0
- static double dmax_out = 0;
- for (i = 0; i < nSamples; i++) {
- dd = fabs(dsamples[i]) / CLIP16;
- if (dd > dmax_out)
- dmax_out = dd;
- }
- if (bank == 0 && measure_audio_count >= quisk_filter_srate * measure_audio_time) {
- //printf(" Max sample out %.0lf\n", dmax_out);
- dmax_out = 0;
- }
-#endif
- // current coding assumes output rate n_speech_sample_rate is 8000 or 16000
- switch (n_speech_sample_rate) {
- case 8000:
- nSamples = quisk_dInterpolate(dsamples, nSamples, &Storage[bank].filtAudio24p3, 3);
- nSamples = quisk_dInterp2HB45(dsamples, nSamples, &Storage[bank].HalfBand7);
- break;
- case 16000:
- nSamples = quisk_dInterpolate(dsamples, nSamples, &Storage[bank].filtAudio24p3, 3);
- break;
- }
- break;
}
if (bank == 0 && measure_audio_count >= quisk_filter_srate * measure_audio_time) {
measured_audio = sqrt(measure_audio_sum / measure_audio_count) / CLIP32 * 1e6;
@@ -2785,9 +2683,6 @@ start_agc:
if (rxMode == EXT || rxMode == DGT_IQ) { // Ext and DGT-IQ stereo sound
process_agc(&Agc1, cSamples, nSamples, 1);
}
- else if (rxMode == FDV_U || rxMode == FDV_L) { // Agc already done
- ;
- }
else if (split_rxtx || multirx_play_channel >= 0) { // separate AGC for left and right channels
for (i = 0; i < nSamples; i++) {
orig_cSamples[i] = cimag(cSamples[i]);
@@ -2945,6 +2840,8 @@ static PyObject * get_filter_rate(PyObject * self, PyObject * args)
break;
case DGT_U: // digital modes DGT-*
case DGT_L:
+ case FDV_U: // digital voice
+ case FDV_L:
if (bandwidth < DGT_NARROW_FREQ)
filter_srate = decim_srate / 8;
else
@@ -2953,10 +2850,6 @@ static PyObject * get_filter_rate(PyObject * self, PyObject * args)
case DGT_IQ: // digital mode at 48 ksps
filter_srate = decim_srate;
break;
- case FDV_U: // digital voice
- case FDV_L:
- filter_srate = n_modem_sample_rate;
- break;
}
//QuiskPrintf("Filter rate %d\n", filter_srate);
return PyInt_FromLong(filter_srate);
@@ -3196,14 +3089,13 @@ static PyObject * set_params(PyObject * self, PyObject * args, PyObject * keywds
{ /* Call with keyword arguments ONLY; change local parameters */
static char * kwlist[] = {"quisk_is_vna", "rx_bytes", "rx_endian", "read_error", "clip",
"bscope_bytes", "bscope_endian", "bscope_size", "bandscopeScale", "hermes_pause",
- "freedv_monitor", NULL} ;
+ NULL} ;
int i, nbytes, read_error, clip, bscope_size, hermes_pause;
nbytes = read_error = clip = bscope_size = hermes_pause = -1;
if (!PyArg_ParseTupleAndKeywords (args, keywds, "|iiiiiiiidii", kwlist,
&quisk_is_vna, &nbytes, &py_sample_rx_endian, &read_error, &clip,
- &py_bscope_bytes, &py_bscope_endian, &bscope_size, &bandscopeScale, &hermes_pause,
- &freedv_monitor))
+ &py_bscope_bytes, &py_bscope_endian, &bscope_size, &bandscopeScale, &hermes_pause))
return NULL;
if (nbytes != -1) {
py_sample_rx_bytes = nbytes;
@@ -6269,13 +6161,6 @@ static PyMethodDef QuiskMethods[] = {
{"set_cwkey", set_hardware_cwkey, METH_VARARGS, "Change the CW key up/down state."},
{"set_remote_cwkey", set_remote_cwkey, METH_VARARGS, "Change the remote control CW key up/down state."},
{"set_PTT", set_PTT, METH_VARARGS, "Record the PTT button state."},
- {"freedv_open", quisk_freedv_open, METH_VARARGS, "Open FreeDV."},
- {"freedv_close", quisk_freedv_close, METH_VARARGS, "Close FreeDV."},
- {"freedv_get_snr", quisk_freedv_get_snr, METH_VARARGS, "Return the signal to noise ratio in dB."},
- {"freedv_get_version", quisk_freedv_get_version, METH_VARARGS, "Return the codec2 API version."},
- {"freedv_get_rx_char", quisk_freedv_get_rx_char, METH_VARARGS, "Get text characters received from freedv."},
- {"freedv_set_options", (PyCFunction)quisk_freedv_set_options, METH_VARARGS|METH_KEYWORDS, "Set the freedv parameters."},
- {"freedv_set_squelch_en", quisk_freedv_set_squelch_en, METH_VARARGS, "Enable or disable FreeDV squelch."},
{"wdsp_set_parameter", (PyCFunction)quisk_wdsp_set_parameter, METH_VARARGS|METH_KEYWORDS, "Set parameters for the WDSP SDR library."},
{"tmp_record_save", tmp_record_save, METH_VARARGS, "Save the temporary recording in a WAV file."},
{"watfall_RgbData", watfall_RgbData, METH_VARARGS, "Return a cookie for the Waterfall pixel data."},
diff --git a/quisk.h b/quisk.h
index 696296b..d22f73f 100644
--- a/quisk.h
+++ b/quisk.h
@@ -312,10 +312,6 @@ extern int quisk_active_sidetone; // Whether and how to generate a sidetone
extern int quisk_isFDX; // Are we in full duplex mode?
extern int quisk_use_serial_port; // Are we using the serial port for CW key or PTT?
extern play_state_t quisk_play_state; // startup, receiving, sidetone
-extern int freedv_current_mode; // current FreeDV mode; 700D, @)@) etc.
-extern int n_modem_sample_rate; // Receive data, decimate to modem_sample_rate, FreeDV codec output data at speech_sample_rate
-extern int n_speech_sample_rate; // Microphone decimate to speech_sample_rate, Freedv codec output data at modem_sample_rate, interpolate to 48000
-extern int n_max_modem_samples; // maximum input to freedv_rx()
extern int quisk_start_cw_delay; // milliseconds to delay output on serial or MIDI CW key down
extern int quisk_start_ssb_delay; // milliseconds to discard output for all modes except CW
extern struct sound_dev * quiskPlaybackDevices[]; // array of Playback sound devices
@@ -346,14 +342,7 @@ extern PyObject * quisk_set_hermes_filter(PyObject *, PyObject *);
extern PyObject * quisk_set_alex_hpf(PyObject *, PyObject *);
extern PyObject * quisk_set_alex_lpf(PyObject *, PyObject *);
-extern PyObject * quisk_freedv_open(PyObject *, PyObject *);
-extern PyObject * quisk_freedv_close(PyObject *, PyObject *);
-extern PyObject * quisk_freedv_get_snr(PyObject *, PyObject *);
-extern PyObject * quisk_freedv_get_version(PyObject *, PyObject *);
-extern PyObject * quisk_freedv_get_rx_char(PyObject *, PyObject *);
-extern PyObject * quisk_freedv_set_options(PyObject *, PyObject *, PyObject *);
extern PyObject * quisk_set_sparams(PyObject *, PyObject *, PyObject *);
-extern PyObject * quisk_freedv_set_squelch_en(PyObject *, PyObject *);
extern PyObject * quisk_open_key(PyObject *, PyObject *, PyObject *);
extern PyObject * quisk_close_key(PyObject *, PyObject *);
extern PyObject * quisk_set_sound_name(PyObject *, PyObject *);
@@ -397,7 +386,6 @@ void quisk_file_playback(complex double *, int, double);
void quisk_tmp_playback(complex double *, int, double);
void quisk_hermes_tx_send(int, int *);
void quisk_udp_mic_error(char *);
-void quisk_check_freedv_mode(void);
void quisk_calc_audio_graph(double, complex double *, double *, int, int);
double QuiskDeltaSec(int);
void * quisk_make_sidetone(struct sound_dev *, int);
@@ -407,12 +395,6 @@ void quisk_set_play_state(void);
void quisk_poll_hardware_key(void);
void PreDistort(complex double * amp_in_samples, complex double * amp_out_samples, int nSamples, complex double * tx_samples, int num_tx);
-// Functions supporting digital voice codecs
-typedef int (* ty_dvoice_codec_rx)(short *, double *, int, int);
-typedef int (* ty_dvoice_codec_tx)(complex double *, double *, int, int);
-extern ty_dvoice_codec_rx pt_quisk_freedv_rx;
-extern ty_dvoice_codec_tx pt_quisk_freedv_tx;
-
// Driver function definitions=================================================
int quisk_read_alsa(struct sound_dev *, complex double *);
void quisk_play_alsa(struct sound_dev *, int, complex double *, int, double);
@@ -468,7 +450,7 @@ int import_quisk_api(void); // used to initialize Quisk_API
#define QuiskSleepMicrosec (*( void (*) (int) )Quisk_API[5])
#define QuiskPrintTime (*( void (*) (const char *, int) )Quisk_API[6])
#define quisk_sample_source (*( void (*) (ty_sample_start, ty_sample_stop, ty_sample_read) )Quisk_API[7])
-#define quisk_dvoice_freedv (*( void (*) (ty_dvoice_codec_rx, ty_dvoice_codec_tx) )Quisk_API[8])
+#define quisk_dvoice_freedv (*( void (*) (void) )Quisk_API[8])
#define quisk_is_key_down (*( int (*) (void) )Quisk_API[9])
#define quisk_sample_source4 (*( void (*) (ty_sample_start, ty_sample_stop, ty_sample_read, ty_sample_write) )Quisk_API[10])
#define strMcpy (*( char * (*) (char *, const char *, size_t) )Quisk_API[11])
@@ -483,7 +465,7 @@ double QuiskTimeSec(void);
void QuiskSleepMicrosec(int);
void QuiskPrintTime(const char *, int);
void quisk_sample_source(ty_sample_start, ty_sample_stop, ty_sample_read);
-void quisk_dvoice_freedv(ty_dvoice_codec_rx, ty_dvoice_codec_tx);
+void quisk_dvoice_freedv(void);
int quisk_is_key_down(void);
void quisk_sample_source4(ty_sample_start, ty_sample_stop, ty_sample_read, ty_sample_write);
char * strMcpy(char *, const char *, size_t);
diff --git a/quisk.py b/quisk.py
index 8bfe03c..a0c6b68 100644
--- a/quisk.py
+++ b/quisk.py
@@ -292,9 +292,9 @@ class HamlibHandlerSerial:
# Additional logic contributed by Dr. Karsten Schmidt
- Mo2CoKen = {'CWL':7, 'CWU':3, 'LSB':1, 'USB':2, 'AM':5, 'FM':4, 'DGT-U':9, 'DGT-L':6, 'DGT-FM':4, 'DGT-IQ':9}
+ Mo2CoKen = {'CWL':7, 'CWU':3, 'LSB':1, 'USB':2, 'AM':5, 'FM':4, 'DGT-U':9, 'FDV-U':9, 'DGT-L':6, 'FDV-L':6, 'DGT-FM':4, 'DGT-IQ':9}
Co2MoKen = {1:'LSB', 2:'USB', 3:'CWU', 4:'FM', 5:'AM', 6:'DGT-L', 7:'CWL', 9:'DGT-U'}
- Mo2CoFlex = {'CWL':3, 'CWU':4, 'LSB':0, 'USB':1, 'AM':6, 'FM':5, 'DGT-U':7, 'DGT-L':9, 'DGT-FM':5, 'DGT-IQ':7}
+ Mo2CoFlex = {'CWL':3, 'CWU':4, 'LSB':0, 'USB':1, 'AM':6, 'FM':5, 'DGT-U':7, 'FDV-U':7, 'DGT-L':9, 'FDV-L':9, 'DGT-FM':5, 'DGT-IQ':7}
Co2MoFlex = {0:'LSB', 1:'USB', 3:'CWL', 4:'CWU', 5:'FM', 6:'AM', 7:'DGT-U', 9:'DGT-L'}
ConvertZZAC_P1_2_Step = { 0: 1,
1: 10,
@@ -1061,9 +1061,9 @@ class HamlibHandlerRig2: # Test with telnet localhost 4532
mode = 'CW'
elif mode == 'CWL': # Is this what CWR means?
mode = 'CWR'
- elif mode == 'DGT-U':
+ elif mode in ('DGT-U', 'FDV-U'):
mode = 'PKTUSB'
- elif mode == 'DGT-L':
+ elif mode in ('DGT-L', 'FDV-L'):
mode = 'PKTLSB'
elif mode == 'DGT-FM':
mode = 'PKTFM'
@@ -3727,7 +3727,7 @@ class App(wx.App):
'txAudioPreemphUsb', 'txAudioPreemphAm', 'txAudioPreemphFm', 'txAudioPreemphFdv',
'wfallScaleZ', 'wfallGrScaleZ', 'graphScaleZ', 'split_rxtx_play', 'modeFilter',
'file_name_rec_audio', 'file_name_rec_samples', 'file_name_rec_mic', 'file_name_rec_tmp',
- 'file_name_play_audio', 'file_name_play_samples', 'file_name_play_cq', 'freedv_mode',
+ 'file_name_play_audio', 'file_name_play_samples', 'file_name_play_cq',
'file_play_source', 'hermes_LNA_dB']
def __init__(self):
global application
@@ -3948,8 +3948,6 @@ class App(wx.App):
self.hot_key_ptt_active = False
self.serial_ptt_active = False
self.vox_ptt_active = False
- self.freedv_mode = 'Mode 700D' # restore FreeDV mode setting
- self.freedv_menu = None
self.hermes_LNA_dB = 20
if hasattr(Hardware, "OnChangeRxTx"):
self.want_RxTx = True
@@ -4711,8 +4709,6 @@ class App(wx.App):
self.hamlib_com1_handler.close()
if self.hamlib_com2_handler:
self.hamlib_com2_handler.close()
- if conf.add_freedv_button:
- QS.freedv_close()
msg = QS.GetQuiskPrintf()
if msg:
print(msg, end='')
@@ -5051,14 +5047,9 @@ class App(wx.App):
self.config_screen.config.EnableRecPlay()
### Right bank of buttons
mode_names = ['CWL', 'CWU', 'LSB', 'USB', 'AM', 'FM', 'DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ', 'FDV-U', 'FDV-L', 'IMD']
- labels = [('CWL', 'CWU'), ('LSB', 'USB'), 'AM', 'FM', ('DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ')]
- shortcuts = ['C', 'S', 'A', 'M', 'D']
- count = 5 # There is room for seven buttons
- if conf.add_freedv_button:
- n_freedv = count
- count += 1
- labels.append('FDV-U')
- shortcuts.append('F')
+ labels = [('CWL', 'CWU'), ('LSB', 'USB'), 'AM', 'FM', ('DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ'), ('FDV-U', 'FDV-L')]
+ shortcuts = ['C', 'S', 'A', 'M', 'D', 'F']
+ count = 6 # There is room for seven buttons
if conf.add_imd_button:
n_imd = count
count += 1
@@ -5080,43 +5071,11 @@ class App(wx.App):
self.modeButns.buttons[0].idName = "CW U/L"
self.modeButns.buttons[1].idName = "SSB U/L"
self.modeButns.buttons[4].idName = "DGT"
+ self.modeButns.buttons[5].idName = "FDV"
else:
labels = ['CWL', 'CWU', 'LSB', 'USB', 'AM', 'FM', 'DGT-U', 'DGT-L', 'DGT-FM', 'DGT-IQ', 'FDV-U', 'IMD']
self.modeButns = RadioBtnPopup(frame, self.OnBtnMode, labels, None, 'modeButns')
self.modeButns.idName = "modeButns"
- self.freedv_menu_items = {}
- if conf.add_freedv_button:
- self.freedv_menu = QuiskMenu("freedv_menu")
- msg = conf.freedv_tx_msg
- QS.freedv_set_options(mode=conf.freedv_modes[0][1], tx_msg=msg, DEBUG=0, squelch=1)
- self.freedv_menu.AppendCheckItem("Monitor", self.OnFreedvMonitor)
- self.freedv_menu.AppendSeparator()
- for mode, index in conf.freedv_modes:
- item = self.freedv_menu.AppendRadioItem(mode, self.OnFreedvMenu, mode == self.freedv_mode)
- self.freedv_menu_items[index] = item
- if mode == self.freedv_mode: # Restore mode
- QS.freedv_set_options(mode=index)
- if mode in ("Mode 2020", "Mode 2400A", "Mode 2400B"):
- item.Enable(False)
- if conf.button_layout == 'Large screen':
- b = QuiskCycleCheckbutton(frame, None, ('FDV-U', 'FDV-L'), is_radio=True)
- b.idName = "FDV"
- b.char_shortcut = 'F'
- self.btnFreeDV = WrapMenu(b, self.freedv_menu)
- self.modeButns.ReplaceButton(n_freedv, self.btnFreeDV)
- else:
- self.btnFreeDV = self.modeButns.AddMenu('FDV-U', self.freedv_menu)
- try:
- ok = QS.freedv_open()
- except:
- traceback.print_exc()
- ok = 0
- if not ok:
- conf.add_freedv_button = False
- if conf.button_layout == 'Large screen':
- self.modeButns.GetButtons()[n_freedv].Enable(0)
- else:
- self.modeButns.Enable('FDV-U', False)
if conf.add_imd_button:
val = 1000
QS.set_imd_level(val)
@@ -5395,16 +5354,6 @@ class App(wx.App):
t = '%13.2f' % (QS.measure_frequency(-1) + vfo)
t = t[0:4] + ' ' + t[4:7] + ' ' + t[7:] + ' Hz'
self.smeter.SetLabel(t)
- def NewDVmeter(self):
- if conf.add_freedv_button:
- snr = QS.freedv_get_snr()
- txt = QS.freedv_get_rx_char()
- self.graph.ScrollMsg(txt)
- self.waterfall.ScrollMsg(txt)
- else:
- snr = 0.0
- t = " SNR %3.0f" % snr
- self.smeter.SetLabel(t)
def NewSmeter(self):
self.smeter_db_count += 1 # count for average
x = QS.get_smeter()
@@ -5574,66 +5523,6 @@ class App(wx.App):
self.multi_rx_screen.waterfall.pane2.filter_center = center
if self.screen is self.filter_screen:
self.screen.NewFilter()
- def OnFreedvMonitor(self, event):
- for item in self.freedv_menu.GetMenuItems():
- if item.GetKind() == wx.ITEM_CHECK:
- if item.IsChecked():
- QS.set_params(freedv_monitor=1)
- else:
- QS.set_params(freedv_monitor=0)
- break
- def OnFreedvMenu(self, event):
- text = ''
- for item in self.freedv_menu.GetMenuItems():
- kind = item.GetKind()
- if kind != wx.ITEM_RADIO:
- continue
- if item.IsChecked():
- text = item.GetItemLabel()
- break
- for mode, index in conf.freedv_modes:
- if mode == text:
- break
- else:
- print ("Failure in OnFreedvMenu")
- return
- mode = QS.freedv_set_options(mode=index)
- if mode == index:
- self.freedv_mode = text
- elif not self.remote_control_slave: # change to new mode failed
- self.freedv_menu_items[mode].Check(1)
- pos = (self.width//2, self.height//2)
- if index == 8:
- dlg = wx.MessageDialog(self.main_frame, "Quisk does not install %s. Please install a system-wide codec2." % text, "FreeDV Modes", wx.OK, pos)
- else:
- dlg = wx.MessageDialog(self.main_frame, "No codec2 support for " + text, "FreeDV Modes", wx.OK, pos)
- dlg.ShowModal()
- else:
- self.freedv_menu_items[mode].Check(1)
- print ("FreeDV change mode failed.")
- self.OnChangeFilter()
- def OnChangeFilter(self):
- # Used by OnFreedvMenu because the FreeDV mode may change the filter sample rate
- bw = int(self.filterButns.GetLabel())
- # We can not use QS.get_filter_srate() because the FreeDV mode is not changed yet.
- if self.freedv_mode in ("Mode 2400A", "Mode 2400B"):
- frate = 48000;
- else:
- frate = 8000;
- bw = min(bw, frate // 2)
- self.filter_bandwidth = bw
- center = self.GetFilterCenter(self.mode, bw)
- filtI, filtQ = self.MakeFilterCoef(frate, None, bw, center)
- lower_edge = center - bw // 2
- QS.set_filters(filtI, filtQ, bw, lower_edge, 0)
- self.multi_rx_screen.graph.filter_bandwidth = bw
- self.multi_rx_screen.graph.filter_center = center
- self.multi_rx_screen.waterfall.pane1.filter_bandwidth = bw
- self.multi_rx_screen.waterfall.pane1.filter_center = center
- self.multi_rx_screen.waterfall.pane2.filter_bandwidth = bw
- self.multi_rx_screen.waterfall.pane2.filter_center = center
- if self.screen is self.filter_screen:
- self.screen.NewFilter()
def OnBtnHelp(self, event):
if event.GetEventObject().GetValue():
self.OnBtnScreen(None, 'Help')
@@ -6098,11 +5987,6 @@ class App(wx.App):
QS.set_squelch(self.levelSquelch / 12.0 - 120.0)
else:
QS.set_squelch(-999.0)
- elif self.mode[0:3] == 'FDV':
- if value:
- QS.freedv_set_squelch_en(1)
- else:
- QS.freedv_set_squelch_en(0)
else:
self.levelSquelchSSB = btn.slider_value
if value:
@@ -6896,10 +6780,7 @@ class App(wx.App):
d = 1E-10
self.smeter.SetLabel(" ADC %.0f%% %.0fdB" % (d * 100.0, 20 * math.log10(d)))
elif self.smeter_usage == "smeter": # update the S-meter
- if self.mode in ('FDV-U', 'FDV-L'):
- self.NewDVmeter()
- else:
- self.NewSmeter()
+ self.NewSmeter()
elif self.smeter_usage == "freq":
self.MeasureFrequency() # display measured frequency
else:
diff --git a/quisk_conf_defaults.py b/quisk_conf_defaults.py
index 3e9b949..67d7086 100644
--- a/quisk_conf_defaults.py
+++ b/quisk_conf_defaults.py
@@ -1797,23 +1797,6 @@ add_extern_demod = ""
add_fdx_button = 0
#add_fdx_button = 1
-## add_freedv_button Add FreeDv button, integer choice
-# These buttons add up to two additional mode buttons after CW, USB, etc.
-# Set this to add the FDV mode button for digital voice:
-add_freedv_button = 1
-#add_freedv_button = 0
-
-## freedv_tx_msg FreeDv Tx message, text
-# For freedv, this is the text message to send.
-freedv_tx_msg = ''
-#freedv_tx_msg = 'N2XXX Jim, New Jersey, USA \n'
-
-
-# This is the list of FreeDV modes and their index number. The starting mode is the first listed.
-freedv_modes = (('Mode 1600', 0),
- ('Mode 2400A', 3), ('Mode 2400B', 4), ('Mode 800XA', 5),
- ('Mode 700C', 6), ('Mode 700D', 7), ('Mode 700E', 13), ('Mode 2020', 8))
-
# These are the filter bandwidths for each mode. Quisk has built-in optimized filters
# for these values, but you can change them if you want.
FilterBwCW = (200, 400, 600, 1000, 1500, 3000)
diff --git a/setup.py b/setup.py
index 4fb20cd..b7bbc16 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ import os
# You must define the version here. A title string including
# the version will be written to __init__.py and read by quisk.py.
-Version = '4.2.44'
+Version = '4.2.45'
fp = open("__init__.py", "w") # write title string
fp.write("#Quisk version %s\n" % Version)
diff --git a/sound.c b/sound.c
index c2bce46..aab2415 100644
--- a/sound.c
+++ b/sound.c
@@ -1000,21 +1000,25 @@ int quisk_read_sound(void) // Called from sound thread
if (remote_control_slave)
send_remote_radio_sound_socket(cSamples, nSamples);
- is_DGT = rxMode == DGT_U || rxMode == DGT_L || rxMode == DGT_IQ || rxMode == DGT_FM;
+ is_DGT = (rxMode == DGT_U || rxMode == DGT_L || rxMode == DGT_IQ || rxMode == DGT_FM || rxMode == FDV_U || rxMode == FDV_L);
if (quisk_record_state == TMP_PLAY_SPKR_MIC)
quisk_tmp_playback(cSamples, nSamples, 1.0); // replace radio sound
else if (quisk_record_state == FILE_PLAY_SPKR_MIC)
quisk_file_playback(cSamples, nSamples, file_play_level); // replace radio sound
// Play the demodulated audio
#if DEBUG_MIC != 2
- if ( ! quisk_play_sidetone(&quisk_Playback)) // play sidetone
- play_sound_interface(&quisk_Playback, nSamples, cSamples, 1, quisk_audioVolume); // else play radio sound
+ if ( ! quisk_play_sidetone(&quisk_Playback)) { // play sidetone
+ if (rxMode == FDV_U || rxMode == FDV_L)
+ play_sound_interface(&quisk_Playback, nSamples, cSamples, 1, 0); // play silence
+ else
+ play_sound_interface(&quisk_Playback, nSamples, cSamples, 1, quisk_audioVolume); // play radio sound
+ }
#endif
if (radio_sound_socket != INVALID_SOCKET)
send_radio_sound_socket(cSamples, nSamples, quisk_audioVolume);
// Play digital if required
- if (is_DGT)
+ //if (is_DGT)
play_sound_interface(&DigitalOutput, nSamples, cSamples, 1, digital_output_level);
// Perhaps record the speaker audio to a file
diff --git a/sound_pulseaudio.c b/sound_pulseaudio.c
index 66bd672..6b38c00 100644
--- a/sound_pulseaudio.c
+++ b/sound_pulseaudio.c
@@ -64,12 +64,20 @@ volatile int streams_to_start;
// remember all open devices for easy cleanup on exit
static pa_stream *OpenPulseDevices[PA_LIST_SIZE * 2] = {NULL};
-static int have_QuiskDigitalInput; // Do we need to create this null sink?
-static int have_QuiskDigitalOutput; // Do we need to create this null sink?
-
static int underrunPlayback;
static int tlength_bytes;
+// list of additional sink devices to add
+struct _NewSinks {
+ char * name; // name of the new sink
+ int create; // create the sink if it doesn't exist
+};
+#define NUM_NEW_SINKS 2
+static struct _NewSinks sink_list[NUM_NEW_SINKS] = {
+ {"QuiskDigitalInput", 1},
+ {"QuiskDigitalOutput", 1}
+};
+
/* This callback happens any time a stream changes state. Here, it's primary used to
* tell the quisk thread when streams are ready.
*/
@@ -1043,19 +1051,23 @@ static void source_sink(const char * name, const char * descr, pa_proplist * pro
// pa_mainloop will call this function when it's ready to tell us about a sink.
static void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {
- if (eol > 0) // If eol is set to a positive number, you're at the end of the list
+ int i;
+
+ if (eol > 0) // If eol is set to a positive number, you're at the end of the list
return;
- source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
- if ( ! strcmp(l->name, "QuiskDigitalInput"))
- have_QuiskDigitalInput = 1;
- if ( ! strcmp(l->name, "QuiskDigitalOutput"))
- have_QuiskDigitalOutput = 1;
+ source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
+ for (i = 0; i < NUM_NEW_SINKS; i++) {
+ if ( ! strcmp(l->name, sink_list[i].name)) {
+ sink_list[i].create = 0;
+ }
+ }
}
-static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) {
- if (eol > 0)
+static void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata)
+{
+ if (eol > 0)
return;
- source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
+ source_sink(l->name, l->description, l->proplist, (PyObject *)userdata);
}
static void index_callback(pa_context *c, uint32_t idx, void *userdata) {
@@ -1070,6 +1082,9 @@ PyObject * quisk_pulseaudio_sound_devices(PyObject * self, PyObject * args)
pa_operation *pa_op=NULL;
pa_context *pa_names_ctx;
int state = 0;
+ int sink_index = 0;
+ char * sink_name;
+ char buf256[256];
if (!PyArg_ParseTuple (args, ""))
return NULL;
@@ -1113,59 +1128,41 @@ PyObject * quisk_pulseaudio_sound_devices(PyObject * self, PyObject * args)
pa_mainloop_iterate(pa_names_ml, 1, NULL);
break;
case 2:
- // Now we wait for our operation to complete. When it's
- // complete our pa_output_devicelist is filled out, and we move
- // along to the next state
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
pa_operation_unref(pa_op);
- // Now we perform another operation to get the source
- // (input device) list just like before.
- pa_op = pa_context_get_source_info_list(pa_names_ctx, pa_sourcelist_cb, pycapt);
- // Update the state so we know what to do next
state++;
}
- pa_mainloop_iterate(pa_names_ml, 1, NULL);
+ else
+ pa_mainloop_iterate(pa_names_ml, 1, NULL);
break;
+ // The following loads null sinks for digital input and output if they are not present.
case 3:
+ if (sink_index >= NUM_NEW_SINKS) {
+ state = 5;
+ continue;
+ }
+ if (sink_list[sink_index].create) {
+ sink_name = sink_list[sink_index].name;
+ snprintf(buf256, 256, "sink_name=%s sink_properties=device.description=%s", sink_name, sink_name);
+ pa_op = pa_context_load_module(pa_names_ctx, "module-null-sink", buf256, index_callback, NULL);
+ state++;
+ }
+ sink_index++;
+ break;
+ case 4:
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
pa_operation_unref(pa_op);
- state = 4;
+ state = 3;
}
else
pa_mainloop_iterate(pa_names_ml, 1, NULL);
break;
- // The following loads modules for digital input and output if they are not present.
- case 4:
- if ( ! have_QuiskDigitalInput) {
- pa_op = pa_context_load_module(pa_names_ctx, "module-null-sink",
- "sink_name=QuiskDigitalInput sink_properties=device.description=QuiskDigitalInput",
- index_callback, NULL);
- state = 5;
- pa_mainloop_iterate(pa_names_ml, 1, NULL);
- }
- else
- state = 6;
- break;
case 5:
- if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
- pa_operation_unref(pa_op);
- state = 6;
- }
- else
- pa_mainloop_iterate(pa_names_ml, 1, NULL);
+ pa_op = pa_context_get_source_info_list(pa_names_ctx, pa_sourcelist_cb, pycapt);
+ state++;
+ pa_mainloop_iterate(pa_names_ml, 1, NULL);
break;
case 6:
- if ( ! have_QuiskDigitalOutput) {
- pa_op = pa_context_load_module(pa_names_ctx, "module-null-sink",
- "sink_name=QuiskDigitalOutput sink_properties=device.description=QuiskDigitalOutput",
- index_callback, NULL);
- state = 7;
- pa_mainloop_iterate(pa_names_ml, 1, NULL);
- }
- else
- state = 9;
- break;
- case 7:
if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {
pa_operation_unref(pa_op);
state = 9;