quisk-kc4upr/quisk.h
Rob French e440b07548 (1) Allowed use of the internal key state at the same time as the GPIO Keyer, in order to enable easy switching between CW and other modes.
(2) Clean comments in the code.
Remaining effort:
- Cleanup GPIO Keyer for release.
- Eventually, add PTT button and possibly separate straight key input to GPIO Keyer.
2020-02-29 23:18:03 -06:00

417 lines
18 KiB
C
Executable File

#define DEBUG_IO 0
#define DEBUG_MIC 0
#define ENABLE_GPIO_KEYER 1
// Sound parameters
//
#define QUISK_SC_SIZE 128
#define QUISK_PATH_SIZE 256 // max file path length
#define IP_SIZE 32
#define MAX_FILTER_SIZE 10001
#define BIG_VOLUME 2.2e9
#define CLOSED_TEXT "The sound device is closed."
#define CLIP32 2147483647
#define CLIP16 32767
#define SAMP_BUFFER_SIZE 66000 // size of arrays used to capture samples
#define IMD_TONE_1 1200 // frequency of IMD test tones
#define IMD_TONE_2 1600
#define INTERP_FILTER_TAPS 85 // interpolation filter
#define MIC_OUT_RATE 48000 // mic post-processing sample rate
#define PA_LIST_SIZE 16 // max number of pulseaudio devices
#define QUISK_MAX_RECEIVERS 9 // max number of receiver channels
// Test the audio: 0 == No test; normal operation;
// 1 == Copy real data to the output; 2 == copy imaginary data to the output;
// 3 == Copy transmit audio to the output.
#define TEST_AUDIO 0
#ifdef MS_WINDOWS
#define QUISK_SHUT_RD SD_RECEIVE
#define QUISK_SHUT_BOTH SD_BOTH
#else
#define SOCKET int
#define INVALID_SOCKET -1
#define QUISK_SHUT_RD SHUT_RD
#define QUISK_SHUT_BOTH SHUT_RDWR
#endif
#if PY_MAJOR_VERSION >= 3
#define PyInt_FromLong PyLong_FromLong
#define PyInt_Check PyLong_Check
#define PyInt_AsLong PyLong_AsLong
#define PyInt_AsUnsignedLongMask PyLong_AsUnsignedLongMask
#endif
#define PyString_FromString PyUnicode_FromString
typedef enum _rx_mode {
CWL = 0,
CWU = 1,
LSB = 2,
USB = 3,
AM = 4,
FM = 5,
EXT = 6,
DGT_U = 7,
DGT_L = 8,
DGT_IQ = 9,
IMD = 10,
FDV_U = 11,
FDV_L = 12,
DGT_FM = 13
} rx_mode_type;
// Pulseaudio support added by Philip G. Lee. Many thanks!
/*!
* \brief Specifies which driver a \c sound_dev is opened with
*/
typedef enum dev_driver{
DEV_DRIVER_NONE = 0,
DEV_DRIVER_PORTAUDIO,
DEV_DRIVER_ALSA,
DEV_DRIVER_PULSEAUDIO
} dev_driver_t;
struct sound_dev { // data for sound capture or playback device
char name[QUISK_SC_SIZE]; // string name of device
char stream_description[QUISK_SC_SIZE]; // Short description of device/stream
void * handle; // Handle of open device, or NULL
dev_driver_t driver; // Which audio driver the device is using
void * buffer; // Handle of buffer for device
int portaudio_index; // index of portaudio device, or -1
int doAmplPhase; // Amplitude and Phase corrections
double AmPhAAAA;
double AmPhCCCC;
double AmPhDDDD;
double portaudio_latency; // Suggested latency for portaudio device
int sample_rate; // Sample rate such as 48000, 96000, 192000
int sample_bytes; // Size of one channel sample in bytes, either 2 or 3 or 4
int num_channels; // number of channels per frame: 1, 2, 3, ...
int channel_I; // Index of I and Q channels: 0, 1, ...
int channel_Q;
int channel_Delay; // Delay this channel by one sample; -1 for no delay, else channel_I or _Q
int overrange; // Count for ADC overrange (clip) for device
// Number of frames for a read request.
// If 0, the read should be non-blocking and read all available
// frames.
int read_frames;
int latency_frames; // desired latency in audio play samples
int play_buf_size; // size of playback buffer in samples
int use_float; // DirectX: Use IEEE floating point
int dataPos; // DirectX: data position
int oldPlayPos; // DirectX: previous value of playPos
int play_delay; // DirectX: bytes of sound available to play
int started; // DirectX: started flag or state
int dev_error; // read or write error
int dev_underrun; // lack of samples to play
int dev_latency; // latency frames
unsigned int rate_min; // min and max available sample rates
unsigned int rate_max;
unsigned int chan_min; // min and max available number of channels
unsigned int chan_max;
complex double dc_remove; // filter to remove DC from samples
double save_sample; // Used to delay the I or Q sample
char msg1[QUISK_SC_SIZE]; // string for information message
int stream_dir_record; // 1 for recording, 0 for playback
char server[IP_SIZE]; // server string for remote pulseaudio
int stream_format; // format of pulseaudio device
int pulse_stream_state; // state of the pulseaudio stream
volatile int cork_status; // 1 for corked, 0 for uncorked
double average_square; // average of squared sample magnitude
} ;
struct sound_conf {
char dev_capt_name[QUISK_SC_SIZE];
char dev_play_name[QUISK_SC_SIZE];
int sample_rate; // Input sample rate from the ADC
int playback_rate; // Output play rate to sound card
int data_poll_usec;
int latency_millisecs;
unsigned int rate_min;
unsigned int rate_max;
unsigned int chan_min;
unsigned int chan_max;
int read_error;
int write_error;
int underrun_error;
int overrange; // count of ADC overrange (clip) for non-soundcard device
int latencyCapt;
int latencyPlay;
int interupts;
char msg1[QUISK_SC_SIZE];
char err_msg[QUISK_SC_SIZE];
// These parameters are for the microphone:
char mic_dev_name[QUISK_SC_SIZE]; // capture device
char name_of_mic_play[QUISK_SC_SIZE]; // playback device
char mic_ip[IP_SIZE];
int mic_sample_rate; // capture sample rate
int mic_playback_rate; // playback sample rate
int tx_audio_port;
int mic_read_error;
int mic_channel_I; // channel number for microphone: 0, 1, ...
int mic_channel_Q;
double mic_out_volume;
char IQ_server[IP_SIZE]; //IP address of optional streaming IQ server (pulseaudio)
int verbose_pulse; //verbose output for pulse audio
} ;
enum quisk_rec_state {
IDLE,
RECORD_RADIO,
RECORD_MIC,
PLAYBACK,
PLAY_FILE,
PLAY_SAMPLES } ;
extern enum quisk_rec_state quisk_record_state;
struct QuiskWav { // data to create a WAV or RAW audio file
double scale;
int sample_rate;
short format; // RAW is 0; PCM integer is 1; IEEE float is 3.
short nChan;
short bytes_per_sample;
FILE * fp;
unsigned int samples;
int fpStart;
int fpEnd;
int fpPos;
} ;
void QuiskWavClose(struct QuiskWav *);
int QuiskWavWriteOpen(struct QuiskWav *, char *, short, short, short, int, double);
void QuiskWavWriteC(struct QuiskWav *, complex double *, int);
void QuiskWavWriteD(struct QuiskWav *, double *, int);
int QuiskWavReadOpen(struct QuiskWav *, char *, short, short, short, int, double);
void QuiskWavReadC(struct QuiskWav *, complex double *, int);
void QuiskWavReadD(struct QuiskWav *, double *, int);
void QuiskMeasureRate(const char *, int);
extern struct sound_conf quisk_sound_state, * pt_quisk_sound_state;
extern int mic_max_display; // display value of maximum microphone signal level
extern int quiskSpotLevel; // 0 for no spotting; else the level 10 to 1000
extern int data_width;
extern int quisk_using_udp; // is a UDP port used for capture (0 or 1)?
extern int quisk_rx_udp_started; // have we received any data?
extern rx_mode_type rxMode; // mode CWL, USB, etc.
extern int quisk_tx_tune_freq; // Transmit tuning frequency as +/- sample_rate / 2
extern PyObject * quisk_pyConfig; // Configuration module instance
extern double quisk_mic_preemphasis; // Mic preemphasis 0.0 to 1.0; or -1.0
extern double quisk_mic_clip; // Mic clipping; try 3.0 or 4.0
extern int quisk_noise_blanker; // Noise blanker level, 0 for off
extern int quisk_sidetoneCtrl; // sidetone control value 0 to 1000
extern int quiskKeyupDelay; // key-up delay from the config file
extern double quisk_audioVolume; // volume control for radio sound playback, 0.0 to 1.0
extern int quiskImdLevel; // level for rxMode IMD
extern int quiskTxHoldState; // state machine for Tx wait for repeater frequency shift
extern double quisk_ctcss_freq; // frequency in Hertz
extern unsigned char quisk_pc_to_hermes[17 * 4]; // Data to send from the PC to the Hermes hardware
extern unsigned char quisk_hermeslite_writequeue[4 * 5]; // One-time writes to Hermes-Lite
extern unsigned int quisk_hermeslite_writepointer; // write pointer into write queue, nonzero value triggers writes,
extern unsigned int quisk_hermeslite_writeattempts; // counter for write retries
extern unsigned int quisk_hermes_code_version; // Hermes code version from Hermes to PC
extern unsigned int quisk_hermes_board_id; // Hermes board ID from Hermes to PC
extern int quisk_use_rx_udp; // Method of access to UDP hardware
extern complex double cRxFilterOut(complex double, int, int);
extern int quisk_multirx_count; // number of additional receivers zero or 1, 2, 3, ..
extern struct sound_dev quisk_DigitalRx1Output; // Output sound device for sub-receiver 1
extern int quisk_is_vna; // is this the VNA program?
extern PyObject * quisk_set_spot_level(PyObject * , PyObject *);
extern PyObject * quisk_get_tx_filter(PyObject * , PyObject *);
extern PyObject * quisk_set_ampl_phase(PyObject * , PyObject *);
extern PyObject * quisk_capt_channels(PyObject * , PyObject *);
extern PyObject * quisk_play_channels(PyObject * , PyObject *);
extern PyObject * quisk_micplay_channels(PyObject * , PyObject *);
extern PyObject * quisk_sound_devices(PyObject * , PyObject *);
extern PyObject * quisk_pa_sound_devices(PyObject * , PyObject *);
extern PyObject * quisk_sound_errors(PyObject *, PyObject *);
extern PyObject * quisk_set_file_record(PyObject *, PyObject *);
extern PyObject * quisk_set_file_name(PyObject *, PyObject *, PyObject *);
extern PyObject * quisk_set_tx_audio(PyObject *, PyObject *, PyObject *);
extern PyObject * quisk_is_vox(PyObject *, PyObject *);
extern PyObject * quisk_set_udp_tx_correct(PyObject *, PyObject *);
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 *);
// These function pointers are the Start/Stop/Read interface for
// the SDR-IQ and any other C-language extension modules that return
// radio data samples.
typedef void (* ty_sample_start)(void);
typedef void (* ty_sample_stop)(void);
typedef int (* ty_sample_read)(complex double *);
typedef int (* ty_sample_write)(complex double *, int);
extern ty_sample_write quisk_pt_sample_write;
void quisk_open_sound(void);
void quisk_close_sound(void);
int quisk_process_samples(complex double *, int);
void quisk_play_samples(complex double *, int);
void quisk_play_zeros(int);
void quisk_start_sound(void);
int quisk_get_overrange(void);
void quisk_mixer_set(char *, int, PyObject *, char *, int);
int quisk_read_sound(void);
int quisk_process_microphone(int, complex double *, int);
void quisk_open_mic(void);
void quisk_close_mic(void);
int quisk_open_key(const char *);
void quisk_close_key(void);
void quisk_set_key_down(int);
void quisk_set_tx_mode(void);
void ptimer(int);
int quisk_extern_demod(complex double *, int, double);
void quisk_tmp_microphone(complex double *, int);
void quisk_tmp_record(complex double * , int, double);
void quisk_file_microphone(complex double *, int);
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);
int QuiskDeltaMsec(int);
#if defined(ENABLE_GPIO_KEYER)
/* KC4UPR: Define some additional functions for the GPIO Keyer. These
* allow setting various parameters for the keyer.
* mode - straight/bug, Iambic A, Iambic B
* speed - speed in words-per-minute (WPM), 1-60
* weight - dot vs dash weight, in range 33-66
* reverse - reverse left/right paddles (dot/dash by default)
* strict - enforce strict character spacing
* enabled - enable/disable the keyer
*/
void quisk_set_gpio_keyer_mode(int);
void quisk_set_gpio_keyer_speed(int);
void quisk_set_gpio_keyer_weight(int);
void quisk_set_gpio_keyer_reversed(int);
void quisk_set_gpio_keyer_strict(int);
void quisk_set_gpio_keyer_enabled(int);
#endif
// Functions supporting digital voice codecs
typedef int (* ty_dvoice_codec_rx)(complex double *, double *, int, int);
typedef int (* ty_dvoice_codec_tx)(complex double *, double *, 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);
void quisk_start_sound_alsa(struct sound_dev **, struct sound_dev **);
void quisk_close_sound_alsa(struct sound_dev **, struct sound_dev **);
int quisk_read_portaudio(struct sound_dev *, complex double *);
void quisk_play_portaudio(struct sound_dev *, int, complex double *, int, double);
void quisk_start_sound_portaudio(struct sound_dev **, struct sound_dev **);
void quisk_close_sound_portaudio(void);
void play_sound_interface(struct sound_dev * , int, complex double * , int, double);
int quisk_read_pulseaudio(struct sound_dev *, complex double *);
void quisk_play_pulseaudio(struct sound_dev *, int, complex double *, int, double);
void quisk_start_sound_pulseaudio(struct sound_dev **, struct sound_dev **);
void quisk_close_sound_pulseaudio(void);
void quisk_cork_pulseaudio(struct sound_dev *, int);
void quisk_flush_pulseaudio(struct sound_dev *);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
/*
Functions defined below this point are available for export to other extension modules using the
standard Python CObject or Capsule interface. See the documentation in import_quisk_api.c. Note
that index zero is used for a structure pointer, not a function pointer.
To add a function, declare it twice, use the next array index, and add it to QUISK_API_INIT.
Be very careful; here be dragons!
*/
#ifdef IMPORT_QUISK_API
// For use by modules that import the _quisk symbols
extern void ** Quisk_API; // array of pointers to functions and variables from module _quisk
int import_quisk_api(void); // used to initialize Quisk_API
#define QuiskGetConfigInt (*( int (*) (const char *, int) )Quisk_API[1])
#define QuiskGetConfigDouble (*( double (*) (const char *, double) )Quisk_API[2])
#define QuiskGetConfigString (*( char * (*) (const char *, char *) )Quisk_API[3])
#define QuiskTimeSec (*( double (*) (void) )Quisk_API[4])
#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_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])
//#if defined(ENABLE_GPIO_KEYER)
/* KC4UPR: Provide API definitions for the GPIO Keyer functions.
* However... since this doesn't seem to be required for various other
* functions that are accessible from Python, I've commented it all out,
* since in truth I really don't know what it's for/how to use it.
*/
//#define quisk_set_gpio_keyer_mode (*( void (*) (int) )Quisk_API[11])
//#define quisk_set_gpio_keyer_speed (*( void (*) (int) )Quisk_API[12])
//#define quisk_set_gpio_keyer_weight (*( void (*) (int) )Quisk_API[13])
//#define quisk_set_gpio_keyer_reversed (*( void (*) (int) )Quisk_API[14])
//#define quisk_set_gpio_keyer_strict (*( void (*) (int) )Quisk_API[15])
//#define quisk_set_gpio_keyer_enabled (*( void (*) (int) )Quisk_API[16])
//#endif
#else
// Used to export symbols from _quisk in quisk.c
int QuiskGetConfigInt(const char *, int);
double QuiskGetConfigDouble(const char *, double);
char * QuiskGetConfigString(const char *, char *);
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);
int quisk_is_key_down(void);
void quisk_sample_source4(ty_sample_start, ty_sample_stop, ty_sample_read, ty_sample_write);
//#if defined(ENABLE_GPIO_KEYER)
/* KC4UPR: Provide API definitions for the GPIO Keyer functions.
* However... since this doesn't seem to be required for various other
* functions that are accessible from Python, I've commented it all out,
* since in truth I really don't know what it's for/how to use it.
*/
//void quisk_set_gpio_keyer_mode(int);
//void quisk_set_gpio_keyer_speed(int);
//void quisk_set_gpio_keyer_weight(int);
//void quisk_set_gpio_keyer_reversed(int);
//void quisk_set_gpio_keyer_strict(int);
//void quisk_set_gpio_keyer_enabled(int);
//#endif
//#if defined(ENABLE_GPIO_KEYER)
/* KC4UPR: Provide API definitions for the GPIO Keyer functions.
* However... since this doesn't seem to be required for various other
* functions that are accessible from Python, I've commented it all out,
* since in truth I really don't know what it's for/how to use it.
*/
//#define QUISK_API_INIT { \
// &quisk_sound_state, &QuiskGetConfigInt, &QuiskGetConfigDouble, &QuiskGetConfigString, &QuiskTimeSec, \
// &QuiskSleepMicrosec, &QuiskPrintTime, &quisk_sample_source, &quisk_dvoice_freedv, &quisk_is_key_down, \
// &quisk_sample_source4, &quisk_set_gpio_keyer_mode, &quisk_set_gpio_keyer_speed, \
// &quisk_set_gpio_keyer_weight, &quisk_set_gpio_keyer_reversed, &quisk_set_gpio_keyer_strict, &quisk_set_gpio_keyer_enabled \
// }
//#else
#define QUISK_API_INIT { \
&quisk_sound_state, &QuiskGetConfigInt, &QuiskGetConfigDouble, &QuiskGetConfigString, &QuiskTimeSec, \
&QuiskSleepMicrosec, &QuiskPrintTime, &quisk_sample_source, &quisk_dvoice_freedv, &quisk_is_key_down, \
&quisk_sample_source4 \
}
//#endif
#endif