From: dttsp Date: Sun, 12 Jun 2005 13:49:26 +0000 (+0000) Subject: Upgraded windows alternatives X-Git-Url: https://git.rkrishnan.org/somewhere?a=commitdiff_plain;h=e096691e78a12807cd4a36f0add48d0f7a2eab29;p=dttsp.git Upgraded windows alternatives --- diff --git a/jDttSP/win/DttSP.def b/jDttSP/win/DttSP.def new file mode 100644 index 0000000..1450e73 --- /dev/null +++ b/jDttSP/win/DttSP.def @@ -0,0 +1,5 @@ +LIBRARY DttSP +EXPORTS + audio_callback @1 + setup_sdr @2 + destroy_sdr @3 diff --git a/jDttSP/win/DttSP.sln b/jDttSP/win/DttSP.sln new file mode 100644 index 0000000..97d2d92 --- /dev/null +++ b/jDttSP/win/DttSP.sln @@ -0,0 +1,21 @@ +Microsoft Visual Studio Solution File, Format Version 8.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DttSP", "DttSP.vcproj", "{E8E40DF8-8A3B-422D-B7A1-44BB036F4994}" + ProjectSection(ProjectDependencies) = postProject + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + Debug = Debug + Release = Release + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {E8E40DF8-8A3B-422D-B7A1-44BB036F4994}.Debug.ActiveCfg = Debug|Win32 + {E8E40DF8-8A3B-422D-B7A1-44BB036F4994}.Debug.Build.0 = Debug|Win32 + {E8E40DF8-8A3B-422D-B7A1-44BB036F4994}.Release.ActiveCfg = Release|Win32 + {E8E40DF8-8A3B-422D-B7A1-44BB036F4994}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal diff --git a/jDttSP/win/DttSP.vcproj b/jDttSP/win/DttSP.vcproj new file mode 100644 index 0000000..9f7e285 --- /dev/null +++ b/jDttSP/win/DttSP.vcproj @@ -0,0 +1,346 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jDttSP/win/DttSPtmp.vcproj b/jDttSP/win/DttSPtmp.vcproj new file mode 100644 index 0000000..34a6f00 --- /dev/null +++ b/jDttSP/win/DttSPtmp.vcproj @@ -0,0 +1,385 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jDttSP/win/iosdr.h b/jDttSP/win/iosdr.h new file mode 100644 index 0000000..3425566 --- /dev/null +++ b/jDttSP/win/iosdr.h @@ -0,0 +1 @@ +extern int write(int, void*,unsigned int); diff --git a/jDttSP/win/keyb.c b/jDttSP/win/keyb.c new file mode 100644 index 0000000..d01707e --- /dev/null +++ b/jDttSP/win/keyb.c @@ -0,0 +1,558 @@ +/* keyb.c */ +/* +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAMP_RATE (48000) +#define HUGE_PHASE (1256637061.43593) + +#define RING_SIZE (01 << 020) + +static pthread_t input, play; +static sem_t ready, reader, writer; + +ringb_t *lring, *rring; +int size; + +static BOOLEAN playing = FALSE; +static double wpm = 18.0, freq = 750.0, gain = -6.0, ramp = 5.0; + +COMPLEX *zout = 0; + +// basic mapping, chars -> morse strings +char *morse_table[128]; + +// CW tone segments +#define ME_EOF (-1) +#define ME_ZERO (0) +#define ME_RAMP (1) +#define ME_STDY (2) + +struct { + double wpm, rise, fall, curr, incr, rate; + int type, size; +} morsel; + +int ditspacesize, dahspacesize, + ditstdysize, dahstdysize, + charspacesize, wordspacesize, + risesize, fallsize; +double riseincr, fallincr; + +#define MAX_ESC (512) +#define ESC_L '<' +#define ESC_R '>' + +void inlinecmd(char *, int); + +void send_sound(COMPLEX *, int); + +//------------------------------------------------------------ + +// try to map char -> morse string +char * +get_morse(int c) { return morse_table[c & 0x7F]; } + +// translate text input to timed, sub-morse-element +// audio segment specs; parcel the segments out +// one at a time to the sound player +void +reader_thread(void) { + BOOLEAN b = TRUE; // we're coming from silence + int c, e; + char *m; + + // keep reading 1 char at a time + while ((c = getchar()) != EOF) { + + // inline command? + if (c == ESC_L) { + int i = 0; + char buf[MAX_ESC]; + while ((c = getchar()) != EOF) { + if (c == ESC_R) break; + buf[i] = c; + if (++i >= (MAX_ESC - 1)) break; + } + if (c == EOF) goto finish; + buf[i] = 0; + inlinecmd(buf, i); + continue; + } + + // is char mapped to morse? + if (m = get_morse(c)) { + + // yup + // for each element in morse string + // (dit/dah, doesn't matter) + while (e = *m++) { + // first segment is ramp up... + sem_wait(&reader); + morsel.type = ME_RAMP, morsel.size = risesize; + morsel.curr = 0.0, morsel.incr = riseincr; + sem_post(&writer); + + // ...then steady state... + // (choose dit/dah here) + sem_wait(&reader); + morsel.type = ME_STDY; + morsel.size = e == '.' ? ditstdysize : dahstdysize; + sem_post(&writer); + + // ...then ramp down... + sem_wait(&reader); + morsel.type = ME_RAMP, morsel.size = fallsize; + morsel.curr = 1.0, morsel.incr = fallincr; + sem_post(&writer); + + // ...finally, post-element pause + sem_wait(&reader); + morsel.type = ME_ZERO; + morsel.size = ditspacesize; + sem_post(&writer); + } + + // post-character pause + sem_wait(&reader); + morsel.type = ME_ZERO; + // (we already emitted a dit-sized space) + morsel.size = charspacesize - ditspacesize; + sem_post(&writer); + + // wherever we go next, it won't have been from silence + b = FALSE; + + } else { + // anything else treated as interword space, + // which has only one segment (silence) + sem_wait(&reader); + morsel.type = ME_ZERO; + // was previous output also interword space? + if (b) + // yes, use full duration + morsel.size = wordspacesize; + else + // no, part of duration already played + morsel.size = wordspacesize - charspacesize; + b = TRUE; + sem_post(&writer); + } + } + + finish: + // indicate EOF on input + sem_wait(&reader); + morsel.type = ME_EOF; + sem_post(&writer); + pthread_exit(0); +} + +void +sound_thread_keyb(void) { + int i, k = 0; + double ofreq, scale, phase = 0.0; + COMPLEX z, delta_z; + + // keep looking for sub-element segments, one at a time + for (;;) { + + // pause for next sub-element segment + sem_post(&reader); + sem_wait(&writer); + + // no more data? + if (morsel.type == ME_EOF) break; + + // requires playing some tone? + if (morsel.type != ME_ZERO) { + // yes, reset params and + // set up CORDIC tone generation + ofreq = freq * 2.0 * M_PI / SAMP_RATE; + scale = pow(10.0, gain / 20.0); + if (phase > HUGE_PHASE) phase -= HUGE_PHASE; + z = Cmplx(cos(phase), sin(phase)); + delta_z = Cmplx(cos(ofreq), sin(ofreq)); + } + + // play out this segment + for (i = 0; i < morsel.size; i++) { + + // make silence + if (morsel.type == ME_ZERO) zout[k] = cxzero; + + // make tone + else { + z = Cmul(z, delta_z); + phase += ofreq; + // is this a ramping segment? + if (morsel.type == ME_RAMP) { + morsel.curr += morsel.incr; + zout[k] = Cscl(z, scale * sin(morsel.curr * M_PI / 2.0)); + } else + zout[k] = Cscl(z, scale); + } + + // have we played enough to fill a jack buffer? + if (++k >= size) { + // yes, send to output + send_sound(zout, k); + // wait until some audio has been drained + sem_wait(&ready); + k = 0; + if (morsel.type != ME_ZERO) { + // reset CORDIC + if (phase > HUGE_PHASE) phase -= HUGE_PHASE; + z = Cmplx(cos(phase), sin(phase)); + delta_z = Cmplx(cos(ofreq), sin(ofreq)); + } + } + } + } + + // anything left unsent? + if (k > 0) send_sound(zout, k); + + pthread_exit(0); +} + +//------------------------------------------------------------------------ +void +send_sound(COMPLEX *buff, int len) { + if (ringb_write_space(lring) < len * sizeof(float)) { + //write(2, "overrun\n", 8); + ringb_restart(lring, size * sizeof(float)); + ringb_restart(rring, size * sizeof(float)); + } else { + int i; + for (i = 0; i < len; i++) { + float l = (float)buff[i].re, r = (float)buff[i].im; + ringb_write(lring, (char *) &l, sizeof(float)); + ringb_write(rring, (char *) &r, sizeof(float)); + } + } +} +#ifndef _WINDOWS +PRIVATE void +jack_xrun(void *arg) { + char *str = "xrun!\n"; + write(2, str, strlen(str)); +} + +PRIVATE void +jack_shutdown(void *arg) {} + +PRIVATE void +jack_callback(jack_nframes_t nframes, void *arg) { + char *lp, *rp; + int nwant = nframes * sizeof(float), + nhave = ringb_read_space(lring); + + lp = jack_port_get_buffer(lport, nframes); + rp = jack_port_get_buffer(rport, nframes); + if (nhave >= nwant) { + ringb_read(lring, lp, nwant); + ringb_read(rring, rp, nwant); + sem_post(&ready); + } else { + memset(lp, 0, nwant); + memset(rp, 0, nwant); + } +} +#endif +void +resetparam(void) { + morsel.wpm = wpm; + morsel.rise = morsel.fall = ramp; + morsel.rate = SAMP_RATE; + + ditspacesize = (int)(SAMP_RATE * 1.2 / morsel.wpm + 0.5); + dahspacesize = (int)(3 * ditspacesize); + charspacesize = dahspacesize; + wordspacesize = 7 * ditspacesize; + + risesize = (int)(SAMP_RATE * morsel.rise / 1e3 + 0.5); + if (risesize > 1) + riseincr = 1.0 / (risesize - 1); + else + riseincr = 1.0; + + fallsize = (int)(SAMP_RATE * morsel.fall / 1e3 + 0.5); + if (fallsize > 1) + fallincr = -1.0 / (fallsize - 1); + else + fallincr = -1.0; + + ditstdysize = ditspacesize - risesize - fallsize; + dahstdysize = dahspacesize - risesize - fallsize; +} + +#ifndef _WINDOWS +int +main(int argc, char **argv) { + int i; + + for (i = 1; i < argc; i++) + if (argv[i][0] == '-') + switch (argv[i][1]) { + case 'f': + freq = atof(argv[++i]); + break; + case 'w': + wpm = atof(argv[++i]); + break; + case 'r': + ramp = atof(argv[++i]); + break; + default: + fprintf(stderr, "keyd [-w wpm] [-f freq] [-r ramp_ms] [infile]\n"); + exit(1); + } + else break; + + if (i < argc) { + if (!freopen(argv[i], "r", stdin)) + perror(argv[i]), exit(1); + i++; + } + + //------------------------------------------------------------ + + resetparam(); + + //------------------------------------------------------------ + + if (!(client = jack_client_new("keyb"))) + fprintf(stderr, "can't make client -- jack not running?\n"), exit(1); + jack_set_process_callback(client, (void *) jack_callback, 0); + jack_on_shutdown(client, (void *) jack_shutdown, 0); + jack_set_xrun_callback(client, (void *) jack_xrun, 0); + size = jack_get_buffer_size(client); + + lport = jack_port_register(client, + "ol", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + rport = jack_port_register(client, + "or", + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, + 0); + lring = ringb_create(RING_SIZE); + rring = ringb_create(RING_SIZE); + ringb_clear(lring, size * sizeof(float)); + ringb_clear(rring, size * sizeof(float)); + + //------------------------------------------------------------ + + zout = newvec_COMPLEX(size, "keyb sample buffer"); + + //------------------------------------------------------------ + + sem_init(&ready, 0, 0); + sem_init(&reader, 0, 0); + sem_init(&writer, 0, 0); + pthread_create(&input, 0, (void *) reader_thread, 0); + pthread_create(&play, 0, (void *) sound_thread, 0); + + //------------------------------------------------------------ + + jack_activate(client); + { + const char **ports; + if (!(ports = jack_get_ports(client, 0, 0, JackPortIsPhysical | JackPortIsInput))) { + fprintf(stderr, "can't find any physical playback ports\n"); + exit(1); + } + if (jack_connect(client, jack_port_name(lport), ports[0])) { + fprintf(stderr, "can't connect left output\n"); + exit(1); + } + if (jack_connect(client, jack_port_name(rport), ports[1])) { + fprintf(stderr, "can't connect right output\n"); + exit(1); + } + free(ports); + } + + pthread_join(input, 0); + pthread_join(play, 0); + jack_client_close(client); + + //------------------------------------------------------------ + + delvec_COMPLEX(zout); + + //------------------------------------------------------------ + + ringb_free(lring); + ringb_free(rring); + sem_destroy(&ready); + sem_destroy(&reader); + sem_destroy(&writer); + + //------------------------------------------------------------ + + exit(0); +} +#endif +char *morse_table[128] = { + /* 000 NUL */ 0, /* 001 SOH */ 0, /* 002 STX */ 0, /* 003 ETX */ 0, + /* 004 EOT */ 0, /* 005 ENQ */ 0, /* 006 ACK */ 0, /* 007 BEL */ 0, + /* 008 BS */ 0, /* 009 HT */ 0, /* 010 LF */ 0, /* 011 VT */ 0, + /* 012 FF */ 0, /* 013 CR */ 0, /* 014 SO */ 0, /* 015 SI */ 0, + /* 016 DLE */ 0, /* 017 DC1 */ 0, /* 018 DC2 */ 0, /* 019 DC3 */ 0, + /* 020 DC4 */ 0, /* 021 NAK */ 0, /* 022 SYN */ 0, /* 023 ETB */ 0, + /* 024 CAN */ 0, /* 025 EM */ 0, /* 026 SUB */ 0, /* 027 ESC */ 0, + /* 028 FS */ 0, /* 029 GS */ 0, /* 030 RS */ 0, /* 031 US */ 0, + /* 032 SP */ 0, + /* 033 ! */ "...-.", // [SN] + /* 034 " */ 0, + /* 035 # */ 0, + /* 036 $ */ 0, + /* 037 % */ ".-...", // [AS] + /* 038 & */ 0, + /* 039 ' */ 0, + /* 040 ( */ "-.--.", // [KN] + /* 041 ) */ 0, + /* 042 * */ "...-.-", // [SK] + /* 043 + */ ".-.-.", // [AR] + /* 044 , */ "--..--", + /* 045 - */ "-....-", + /* 046 . */ ".-.-.-", + /* 047 / */ "-..-.", + /* 048 0 */ "-----", + /* 049 1 */ ".----", + /* 050 2 */ "..---", + /* 051 3 */ "...--", + /* 052 4 */ "....-", + /* 053 5 */ ".....", + /* 054 6 */ "-....", + /* 055 7 */ "--...", + /* 056 8 */ "---..", + /* 057 9 */ "----.", + /* 058 : */ 0, + /* 059 ; */ 0, + /* 060 < */ 0, + /* 061 = */ "-...-", // [BT] + /* 062 > */ 0, + /* 063 ? */ "..__..", // [IMI] + /* 064 @ */ ".--.-.", + /* 065 A */ ".-", + /* 066 B */ "-...", + /* 067 C */ "-.-.", + /* 068 D */ "-..", + /* 069 E */ ".", + /* 070 F */ "..-.", + /* 071 G */ "--.", + /* 072 H */ "....", + /* 073 I */ "..", + /* 074 J */ ".---", + /* 075 K */ "-.-", + /* 076 L */ ".-..", + /* 077 M */ "--", + /* 078 N */ "-.", + /* 079 O */ "---", + /* 080 P */ ".--.", + /* 081 Q */ "--.-", + /* 082 R */ ".-.", + /* 083 S */ "...", + /* 084 T */ "-", + /* 085 U */ "..-", + /* 086 V */ "...-", + /* 087 W */ ".--", + /* 088 X */ "-..-", + /* 089 Y */ "-.--", + /* 090 Z */ "--..", + /* 091 [ */ 0, + /* 092 \ */ 0, + /* 093 ] */ 0, + /* 094 ^ */ 0, + /* 095 _ */ 0, + /* 096 ` */ 0, + /* 097 a */ ".-", + /* 098 b */ "-...", + /* 099 c */ "-.-.", + /* 100 d */ "-..", + /* 101 e */ ".", + /* 102 f */ "..-.", + /* 103 g */ "--.", + /* 104 h */ "....", + /* 105 i */ "..", + /* 106 j */ ".---", + /* 107 k */ "-.-", + /* 108 l */ ".-..", + /* 109 m */ "--", + /* 110 n */ "-.", + /* 111 o */ "---", + /* 112 p */ ".--.", + /* 113 q */ "--.-", + /* 114 r */ ".-.", + /* 115 s */ "...", + /* 116 t */ "-", + /* 117 u */ "..-", + /* 118 v */ "...-", + /* 119 w */ ".--", + /* 120 x */ "-..-", + /* 121 y */ "-.--", + /* 122 z */ "--..", + /* 123 { */ 0, + /* 124 | */ 0, + /* 125 } */ 0, + /* 126 ~ */ 0, + /* 127 DEL */ 0 +}; + +void +inlinecmd(char *buf, int len) { + if (!buf || len < 1) return; + if (!strncmp(buf, "wpm", 3)) { + wpm = atof(buf + 3); + resetparam(); + } else if (!strncmp(buf, "ramp", 4)) { + ramp = atof(buf + 4); + resetparam(); + } else if (!strncmp(buf, "freq", 4)) + freq = atof(buf + 4); + else if (!strncmp(buf, "gain", 4)) + gain = atof(buf + 4); +} diff --git a/jDttSP/win/keyd.c b/jDttSP/win/keyd.c new file mode 100644 index 0000000..f51dff8 --- /dev/null +++ b/jDttSP/win/keyd.c @@ -0,0 +1,342 @@ +/* keyd.c */ +/* +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SAMP_RATE (48000) + +// # times key is sampled per sec +// > 64 requires root on Linux +//#define RTC_RATE (128) +#define RTC_RATE (64) + +// # samples generated during 1 clock tick at RTC_RATE +#define TONE_SIZE (SAMP_RATE / RTC_RATE) + +// ring buffer size; > 1 sec at this sr +#define RING_SIZE (01 << 020) + +KeyerState ks; +KeyerLogic kl; + +static pthread_t play, key, update; +sem_t clock_fired, keyer_started, update_ok, poll_fired; + +int fdser, fdrtc; +/* +jack_client_t *client; +jack_port_t *lport, *rport; +jack_ringbuffer_t *lring, *rring; +jack_nframes_t size; */ +ringb_t *lring, *rring; +int size; + +CWToneGen gen; +static BOOLEAN playing = FALSE, iambic = FALSE; +static double wpm = 18.0, freq = 750.0, ramp = 5.0, gain = 1.0; + +//------------------------------------------------------------ + + +DttSP_EXP void +CWtoneExchange(float *bufl,float*bufr,int nframes) { + size_t bytesize = nframes*4; + size_t numsamps; + if ((numsamps = ringb_read_space(lring)) < bytesize) { + memset(bufl,0,bytesize); + memset(bufr,0,bytesize); + } else { + ringb_read(lring,(char *)bufl,bytesize); + ringb_read(rring,(char *)bufr,bytesize); + } +} + +//------------------------------------------------------------ + +// generated tone -> output ringbuffer +void +send_tone(void) { + if (ringb_write_space(lring) < TONE_SIZE * sizeof(float)) { + //write(2, "overrun tone\n", 13); + ringb_restart(lring, TONE_SIZE * sizeof(float)); + ringb_restart(rring, TONE_SIZE * sizeof(float)); + } else { + int i; + for (i = 0; i < gen->size; i++) { + float l = (float)CXBreal(gen->buf, i), + r = (float)CXBimag(gen->buf, i); + ringb_write(lring, (char *) &l, sizeof(float)); + ringb_write(rring, (char *) &r, sizeof(float)); + } + } +} + +// silence -> output ringbuffer +void +send_silence(void) { + if (ringb_write_space(lring) < TONE_SIZE * sizeof(float)) { + //write(2, "overrun zero\n", 13); + ringb_restart(lring, TONE_SIZE * sizeof(float)); + ringb_restart(rring, TONE_SIZE * sizeof(float)); + } else { + int i; + for (i = 0; i < gen->size; i++) { + float zero = 0.0; + ringb_write(lring, (char *) &zero, sizeof(float)); + ringb_write(rring, (char *) &zero, sizeof(float)); + } + } +} + +//------------------------------------------------------------------------ + +// sound/silence generation +// tone turned on/off asynchronously + +DttSP_EXP void +sound_thread_keyd(void) { + for (;;) { + sem_wait(&clock_fired); + + if (playing) { + // CWTone keeps playing for awhile after it's turned off, + // in order to allow for a decay envelope; + // returns FALSE when it's actually done. + playing = CWTone(gen); + send_tone(); + } else { + send_silence(); + // only let updates run when we've just generated silence +// sem_post(&update_ok); + } + } + + pthread_exit(0); +} + + +BOOLEAN +read_key(double del, BOOLEAN dot, BOOLEAN dash) { + extern BOOLEAN read_straight_key(KeyerState ks, BOOLEAN keyed); + extern BOOLEAN read_iambic_key(KeyerState ks, BOOLEAN dot, BOOLEAN dash, KeyerLogic kl, double ticklen); + + if (iambic) + return read_iambic_key(ks, dot, dash, kl, del); + else + return read_straight_key(ks, dot^dash); +} + +/// Main keyer function, called by a thread in the C# +DttSP_EXP void +key_thread(double del, BOOLEAN dash, BOOLEAN dot) { + BOOLEAN keydown; + + // called after next tick and passed the + // delay waitsince last one + + // read key; tell keyer elapsed time since last call + keydown = read_key(del,dot,dash); + + + if (!playing && keydown) + CWToneOn(gen), playing = TRUE; + else if (playing && !keydown) + CWToneOff(gen); + + sem_post(&clock_fired); +} + +//------------------------------------------------------------------------ + +// update keyer parameters via text input from stdin +// -> set keyer speed to xxx +// -> set gain to xxx (dB) +// -> set freq to xxx +// -> set attack/decay times to xxx ms + + +#define MAX_ESC (512) +#define ESC_L '<' +#define ESC_R '>' + +void +updater(void) { + for (;;) { + int c; + + // get or wait for next input char + if ((c = getchar()) == EOF) goto finish; + + // if we see the beginning of a command, + if (c == ESC_L) { + int i = 0; + char buf[MAX_ESC]; + + // gather up the remainder + while ((c = getchar()) != EOF) { + if (c == ESC_R) break; + buf[i] = c; + if (++i >= (MAX_ESC - 1)) break; + } + if (c == EOF) goto finish; + buf[i] = 0; + + // wait until changes are safe + sem_wait(&update_ok); + + if (!strncmp(buf, "wpm", 3)) + ks->wpm = wpm = atof(buf + 3); + else if (!strncmp(buf, "ramp", 4)) { + ramp = atof(buf + 4); + setCWToneGenVals(gen, gain, freq, ramp, ramp); + } else if (!strncmp(buf, "freq", 4)) { + freq = atof(buf + 4); + setCWToneGenVals(gen, gain, freq, ramp, ramp); + } else if (!strncmp(buf, "gain", 4)) { + gain = atof(buf + 4); + setCWToneGenVals(gen, gain, freq, ramp, ramp); + } else if (!strncmp(buf, "quit", 4)) + goto finish; + + } // otherwise go around again + } + + // we saw an EOF or quit; kill other threads and exit neatly + + finish: + pthread_cancel(play); + pthread_cancel(key); + pthread_exit(0); +} +DttSP_EXP void +updateKeyer(double nfreq, BOOLEAN niambic, double ngain, double nramp, double nwpm, + BOOLEAN revpdl, int weight, double SampleRate) { + ks->flag.iambic = niambic; + iambic = niambic; + ks->flag.revpdl = revpdl; + ks->weight = weight; + wpm = nwpm; + gain = ngain; + ramp = nramp; + freq = nfreq; + gen->osc.freq = 2.0 * M_PI * freq / SampleRate; +} +DttSP_EXP void +NewKeyer(double freq, BOOLEAN niambic, double gain, double ramp, double wpm, double SampleRate) { + + void *usemem; + + //------------------------------------------------------------ + + gen = newCWToneGen(gain, freq, ramp, ramp, TONE_SIZE, SampleRate); + + //------------------------------------------------------------ + + kl = newKeyerLogic(); + ks = newKeyerState(); + ks->flag.iambic = niambic; + ks->flag.revpdl = TRUE; // depends on port wiring + ks->flag.autospace.khar = ks->flag.autospace.word = FALSE; + ks->debounce = 1; // could be more if sampled faster + ks->mode = MODE_B; + ks->weight = 50; + ks->wpm = wpm; + iambic = niambic; + size = 2048; + usemem = safealloc(1,4096*sizeof(float)+sizeof(ringb_t),"Keyer RB Left"); + lring = ringb_create(usemem, 4096*sizeof(float)); + usemem = safealloc(1,4096*sizeof(float)+sizeof(ringb_t),"Keyer RB Right"); + rring = ringb_create(usemem,4096*sizeof(float)); + ringb_clear(lring, size * sizeof(float)); + ringb_clear(rring, size * sizeof(float)); + sem_init(&clock_fired, 0, 0); + sem_init(&poll_fired , 0, 0); + sem_init(&keyer_started,0,0); +} + +DttSP_EXP void +delKeyer() { + sem_destroy(&clock_fired); + sem_destroy(&poll_fired); + sem_destroy(&keyer_started); + delCWToneGen(gen); + delKeyerState(ks); + delKeyerLogic(kl); + safefree((char *)lring); + safefree((char *)rring); +} +DttSP_EXP void +KeyerClockFireWait() +{ + sem_wait(&clock_fired); +} +DttSP_EXP void +KeyerClockFireRelease() +{ + sem_post(&clock_fired); +} +DttSP_EXP void +KeyerStartedWait() +{ + sem_wait(&keyer_started); +} +DttSP_EXP void +KeyerStartedRelease() +{ + sem_post(&keyer_started); +} +DttSP_EXP void +PollTimerWait() +{ + sem_wait(&poll_fired); +} +DttSP_EXP void +PollTimerRelease() +{ + sem_post(&poll_fired); +} + +//------------------------------------------------------------------------ diff --git a/jDttSP/win/keyerio.c b/jDttSP/win/keyerio.c new file mode 100644 index 0000000..4fe2a61 --- /dev/null +++ b/jDttSP/win/keyerio.c @@ -0,0 +1,91 @@ +#include + +//======================================================================== + +/* Read a straight key connected to a serial port, do debouncing, then + return the key state */ + +BOOLEAN +read_straight_key(KeyerState ks, BOOLEAN keyed) { + int i, j; + static BOOLEAN keystate = 0; + static int debounce_buf_i = 0, + debounce_buf[DEBOUNCE_BUF_MAX_SIZE]; + debounce_buf[debounce_buf_i] = keyed; + debounce_buf_i++; + + // + //*************************************************** + // back to business as usual + //*************************************************** + + /* If the debounce buffer is full, determine the state of the key */ + if (debounce_buf_i >= ks->debounce) { + debounce_buf_i = 0; + + j = 0; + for (i = 0; i < ks->debounce; i++) + if (debounce_buf[i]) + j++; + keystate = (j > ks->debounce / 2) ? 1 : 0; + } + + return keystate; +} + +//------------------------------------------------------------------------ + +/* Read an iambic key connected to a serial port, do debouncing, emulate a + straight key, then return the emulated key state */ + +BOOLEAN +read_iambic_key(KeyerState ks, BOOLEAN dash, BOOLEAN dot, KeyerLogic kl, double ticklen) { + int i, j; + static BOOLEAN dah_debounce_buf[DEBOUNCE_BUF_MAX_SIZE], + dit_debounce_buf[DEBOUNCE_BUF_MAX_SIZE]; + static int dah = 0, debounce_buf_i = 0, dit = 0; + + if (ks->flag.revpdl) { + dah_debounce_buf[debounce_buf_i] = dot; + dit_debounce_buf[debounce_buf_i] = dash; + } else { + dah_debounce_buf[debounce_buf_i] = dash; + dit_debounce_buf[debounce_buf_i] = dot; + } + debounce_buf_i++; + + // + //*************************************************** + // back to business as usual + //*************************************************** + + /* If the debounce buffer is full, determine the state of the keys */ + if (debounce_buf_i >= ks->debounce) { + debounce_buf_i = 0; + + j = 0; + for (i = 0; i < ks->debounce; i++) + if (dah_debounce_buf[i]) j++; + dah = (j > ks->debounce / 2) ? 1 : 0; + + j = 0; + for (i = 0; i < ks->debounce; i++) + if (dit_debounce_buf[i]) j++; + dit = (j > ks->debounce / 2) ? 1 : 0; + } + + return klogic(kl, + dit, + dah, + ks->wpm, + ks->mode, + ks->flag.mdlmdB, + ks->flag.memory.dit, + ks->flag.memory.dah, + ks->flag.autospace.khar, + ks->flag.autospace.word, + ks->weight, + ticklen); +} + +//======================================================================== diff --git a/jDttSP/win/local.h b/jDttSP/win/local.h new file mode 100644 index 0000000..0c0e2e3 --- /dev/null +++ b/jDttSP/win/local.h @@ -0,0 +1,72 @@ +/* local.h + +Some manifest constants for the particular implementation + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +/* #include */ +/* #include */ +/* #include */ +/* #include */ +/* #include */ + +#ifndef _local_h +#define _local_h + +#include +//\\.\pipe\pipename +#define RCBASE ".DttSPrc" +#define PARMPATH "\\\\.\\pipe\\SDRcommands" +#define METERPATH "\\\\.\\pipe\\SDRmeter" +#define SPECPATH "\\\\.\\pipe\\SDRspectrum" +#define WISDOMPATH ".\\wisdom" + + +extern struct _loc { + char name[MAXPATHLEN]; + struct { + char rcfile[MAXPATHLEN], + parm[MAXPATHLEN], + meter[MAXPATHLEN], + spec[MAXPATHLEN], + wisdom[MAXPATHLEN]; + } path; + struct { + REAL rate; + int size, nrx, spec, comp; + SDRMODE mode; + } def; + struct { int ring;} mult; +} loc; + + +#endif diff --git a/jDttSP/win/sdr.c b/jDttSP/win/sdr.c new file mode 100644 index 0000000..9f52144 --- /dev/null +++ b/jDttSP/win/sdr.c @@ -0,0 +1,865 @@ +/* sdr.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +//======================================================================== +/* initialization and termination */ + +void +reset_meters(void) { + if (uni.meter.flag) { // reset metering completely + int i, k; + for (i = 0; i < RXMETERPTS; i++) + for (k = 0; k < MAXRX; k++) + uni.meter.rx.val[k][i] = uni.meter.rx.avg[k][i] = -200.0; + for (i = 0; i < TXMETERPTS; i++) + uni.meter.tx.val[i] = uni.meter.tx.avg[i] = -200.0; + } +} + +void +reset_spectrum(void) { + if (uni.spec.flag) + reinit_spectrum(&uni.spec); +} + +void +reset_counters(void) { + int k; + for (k = 0; k < uni.multirx.nrx; k++) rx[k].tick = 0; + tx.tick = 0; +} + +//======================================================================== + +/* global and general info, + not specifically attached to + tx, rx, or scheduling */ + +PRIVATE void +setup_all(void) { + + uni.samplerate = loc.def.rate; + uni.buflen = loc.def.size; + uni.mode.sdr = loc.def.mode; + uni.mode.trx = RX; + + uni.wisdom.path = loc.path.wisdom; + uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_ESTIMATE; + { + FILE *f = fopen(uni.wisdom.path, "r"); + if (f) { +#define WBUFLEN 2048 +#define WSTRLEN 64 + char *line = (char *)malloc(WBUFLEN); + fgets(line, WBUFLEN, f); + if ((strlen(line) > WSTRLEN) && + (fftw_import_wisdom_from_string(line) != FFTW_FAILURE)) + uni.wisdom.bits = FFTW_OUT_OF_PLACE | FFTW_MEASURE | FFTW_USE_WISDOM; +#undef WSTRLEN +#undef WBUFLEN + fclose(f); + free(line); + } + + } + + if (uni.meter.flag) { + uni.meter.rx.type = SIGNAL_STRENGTH; + uni.meter.tx.type = SIGNAL_STRENGTH; + reset_meters(); + } + + uni.spec.rxk = 0; + uni.spec.buflen = uni.buflen; + uni.spec.scale = SPEC_PWR; + uni.spec.type = SPEC_POST_FILT; + uni.spec.size = loc.def.spec; + uni.spec.planbits = uni.wisdom.bits; + init_spectrum(&uni.spec); + + // set which receiver is listening to commands + uni.multirx.lis = 0; + uni.multirx.nrx = loc.def.nrx; + + // set mixing of input from aux ports + uni.mix.rx.flag = uni.mix.tx.flag = FALSE; + uni.mix.rx.gain = uni.mix.tx.gain = 1.0; + + uni.tick = 0; +} + +/* purely rx */ + +PRIVATE void +setup_rx(int k) { + + /* conditioning */ + rx[k].iqfix = newCorrectIQ(0.0, 1.0); + rx[k].filt.coef = newFIR_Bandpass_COMPLEX(-4800.0, + 4800.0, + uni.samplerate, + uni.buflen + 1); + rx[k].filt.ovsv = newFiltOvSv(FIRcoef(rx[k].filt.coef), + FIRsize(rx[k].filt.coef), + uni.wisdom.bits); + normalize_vec_COMPLEX(rx[k].filt.ovsv->zfvec, + rx[k].filt.ovsv->fftlen); + + // hack for EQ + rx[k].filt.save = newvec_COMPLEX(rx[k].filt.ovsv->fftlen, "RX filter cache"); + memcpy((char *) rx[k].filt.save, + (char *) rx[k].filt.ovsv->zfvec, + rx[k].filt.ovsv->fftlen * sizeof(COMPLEX)); + + /* buffers */ + /* note we overload the internal filter buffers + we just created */ + rx[k].buf.i = newCXB(FiltOvSv_fetchsize(rx[k].filt.ovsv), + FiltOvSv_fetchpoint(rx[k].filt.ovsv), + "init rx.buf.i"); + rx[k].buf.o = newCXB(FiltOvSv_storesize(rx[k].filt.ovsv), + FiltOvSv_storepoint(rx[k].filt.ovsv), + "init rx[k].buf.o"); + + /* conversion */ + rx[k].osc.freq = -11025.0; + rx[k].osc.phase = 0.0; + rx[k].osc.gen = newOSC(uni.buflen, + ComplexTone, + rx[k].osc.freq, + rx[k].osc.phase, + uni.samplerate, + "SDR RX Oscillator"); + + rx[k].agc.gen = newDigitalAgc(agcMED, // Mode + 7, // Hang + 7, // Size + 48, // Ramp + 3, // Over + 3, // Rcov + CXBsize(rx[k].buf.o), // BufSize + 100.0, // MaxGain + 0.707, // Limit + 1.0, // CurGain + CXBbase(rx[k].buf.o)); + rx[k].agc.flag = TRUE; + + /* demods */ + rx[k].am.gen = newAMD(48000.0, // REAL samprate + 0.0, // REAL f_initial + -500.0, // REAL f_lobound, + 500.0, // REAL f_hibound, + 400.0, // REAL f_bandwid, + CXBsize(rx[k].buf.o), // int size, + CXBbase(rx[k].buf.o), // COMPLEX *ivec, + CXBbase(rx[k].buf.o), // COMPLEX *ovec, + AMdet, // AM Mode AMdet == rectifier, + // SAMdet == synchronous detector + "AM detector blew"); // char *tag + rx[k].fm.gen = newFMD(48000, // REAL samprate + 0.0, // REAL f_initial + -6000.0, // REAL f_lobound + 6000.0, // REAL f_hibound + 10000.0, // REAL f_bandwid + CXBsize(rx[k].buf.o), // int size + CXBbase(rx[k].buf.o), // COMPLEX *ivec + CXBbase(rx[k].buf.o), // COMPLEX *ovec + "New FM Demod structure"); // char *error message; + + /* noise reduction */ + rx[k].anf.gen = new_lmsr(rx[k].buf.o, // CXB signal, + 64, // int delay, + 0.01, // REAL adaptation_rate, + 0.00001, // REAL leakage, + 45, // int adaptive_filter_size, + LMADF_INTERFERENCE); + rx[k].anf.flag = FALSE; + rx[k].anr.gen = new_lmsr(rx[k].buf.o, // CXB signal, + 64, // int delay, + 0.01, // REAL adaptation_rate, + 0.00001, // REAL leakage, + 45, // int adaptive_filter_size, + LMADF_NOISE); + rx[k].anr.flag = FALSE; + + rx[k].nb.thresh = 3.3; + rx[k].nb.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb.thresh); + rx[k].nb.flag = FALSE; + + rx[k].nb_sdrom.thresh = 2.5; + rx[k].nb_sdrom.gen = new_noiseblanker(rx[k].buf.i, rx[k].nb_sdrom.thresh); + rx[k].nb_sdrom.flag = FALSE; + + rx[k].spot.gen = newSpotToneGen(-12.0, // gain + 700.0, // freq + 5.0, // ms rise + 5.0, // ms fall + uni.buflen, + uni.samplerate); + + rx[k].scl.pre.val = 1.0; + rx[k].scl.pre.flag = FALSE; + rx[k].scl.post.val = 1.0; + rx[k].scl.post.flag = FALSE; + + memset((char *) &rx[k].squelch, 0, sizeof(rx[k].squelch)); + rx[k].squelch.thresh = -30.0; + rx[k].squelch.power = 0.0; + rx[k].squelch.flag = rx[k].squelch.running = rx[k].squelch.set = FALSE; + rx[k].squelch.num = (int) (0.0395 * uni.samplerate + 0.5); + + rx[k].mode = uni.mode.sdr; + rx[k].bin.flag = FALSE; + + { + REAL pos = 0.5, // 0 <= pos <= 1, left->right + theta = (1.0 - pos) * M_PI / 2.0; + rx[k].azim = Cmplx(cos(theta), sin(theta)); + } + + rx[k].tick = 0; +} + +/* purely tx */ + +PRIVATE void +setup_tx(void) { + + /* conditioning */ + tx.iqfix = newCorrectIQ(0.0, 1.0); + tx.filt.coef = newFIR_Bandpass_COMPLEX(300.0, + 3000.0, + uni.samplerate, + uni.buflen + 1); + tx.filt.ovsv = newFiltOvSv(FIRcoef(tx.filt.coef), + FIRsize(tx.filt.coef), + uni.wisdom.bits); + normalize_vec_COMPLEX(tx.filt.ovsv->zfvec, + tx.filt.ovsv->fftlen); + + // hack for EQ + tx.filt.save = newvec_COMPLEX(tx.filt.ovsv->fftlen, "TX filter cache"); + memcpy((char *) tx.filt.save, + (char *) tx.filt.ovsv->zfvec, + tx.filt.ovsv->fftlen * sizeof(COMPLEX)); + + /* buffers */ + tx.buf.i = newCXB(FiltOvSv_fetchsize(tx.filt.ovsv), + FiltOvSv_fetchpoint(tx.filt.ovsv), + "init tx.buf.i"); + tx.buf.o = newCXB(FiltOvSv_storesize(tx.filt.ovsv), + FiltOvSv_storepoint(tx.filt.ovsv), + "init tx.buf.o"); + + /* conversion */ + tx.osc.freq = 0.0; + tx.osc.phase = 0.0; + tx.osc.gen = newOSC(uni.buflen, + ComplexTone, + tx.osc.freq, + tx.osc.phase, + uni.samplerate, + "SDR TX Oscillator"); + + tx.agc.gen = newDigitalAgc(agcFAST, // Mode + 3, // Hang + 3, // Size + 3, // Over + 3, // Rcov + 48, // Ramp + CXBsize(tx.buf.o), // BufSize + 1.0, // MaxGain + 0.900, // Limit + 1.0, // CurGain + CXBbase(tx.buf.o)); + tx.agc.flag = TRUE; + + tx.spr.gen = newSpeechProc(0.4, 10.0, CXBbase(tx.buf.i), CXBsize(tx.buf.i)); + tx.spr.flag = FALSE; + + tx.scl.dc = cxzero; + tx.scl.pre.val = 1.0; + tx.scl.pre.flag = FALSE; + tx.scl.post.val = 1.0; + tx.scl.post.flag = FALSE; + + tx.mode = uni.mode.sdr; + + tx.tick = 0; + /* not much else to do for TX */ +} + +/* how the outside world sees it */ + +void +setup_workspace(void) { + int k; + + setup_all(); + + for (k = 0; k < uni.multirx.nrx; k++) { + setup_rx(k); + uni.multirx.act[k] = FALSE; + } + uni.multirx.act[0] = TRUE; + uni.multirx.nac = 1; + + setup_tx(); +} + +void +destroy_workspace(void) { + int k; + + /* TX */ + delSpeechProc(tx.spr.gen); + delDigitalAgc(tx.agc.gen); + delOSC(tx.osc.gen); + delvec_COMPLEX(tx.filt.save); + delFiltOvSv(tx.filt.ovsv); + delFIR_Bandpass_COMPLEX(tx.filt.coef); + delCorrectIQ(tx.iqfix); + delCXB(tx.buf.o); + delCXB(tx.buf.i); + + /* RX */ + for (k = 0; k < uni.multirx.nrx; k++) { + delSpotToneGen(rx[k].spot.gen); + delDigitalAgc(rx[k].agc.gen); + del_nb(rx[k].nb_sdrom.gen); + del_nb(rx[k].nb.gen); + del_lmsr(rx[k].anf.gen); + del_lmsr(rx[k].anr.gen); + delAMD(rx[k].am.gen); + delFMD(rx[k].fm.gen); + delOSC(rx[k].osc.gen); + delvec_COMPLEX(rx[k].filt.save); + delFiltOvSv(rx[k].filt.ovsv); + delFIR_Bandpass_COMPLEX(rx[k].filt.coef); + delCorrectIQ(rx[k].iqfix); + delCXB(rx[k].buf.o); + delCXB(rx[k].buf.i); + } + + /* all */ + finish_spectrum(&uni.spec); +} + +////////////////////////////////////////////////////////////////////////// +// execution +////////////////////////////////////////////////////////////////////////// + +//======================================================================== +// util + +PRIVATE REAL +CXBnorm(CXB buff) { + int i; + double sum = 0.0; + for (i = 0; i < CXBhave(buff); i++) + sum += Csqrmag(CXBdata(buff, i)); + return sqrt(sum); +} + +//======================================================================== +/* all */ + +// unfortunate duplication here, due to +// multirx vs monotx + +PRIVATE void +do_rx_meter(int k, CXB buf, int tap) { + COMPLEX *vec = CXBbase(buf); + int i, len = CXBhave(buf); + + uni.meter.rx.val[k][tap] = 0; + + switch (uni.meter.rx.type) { + case AVG_SIGNAL_STRENGTH: + for (i = 0; i < len; i++) + uni.meter.rx.val[k][tap] += Csqrmag(vec[i]); + uni.meter.rx.val[k][tap] = + uni.meter.rx.avg[k][tap] = + 0.9 * uni.meter.rx.avg[k][tap] + log10(uni.meter.rx.val[k][tap] + 1e-20); + break; + case SIGNAL_STRENGTH: + for (i = 0; i < len; i++) + uni.meter.rx.val[k][tap] += Csqrmag(vec[i]); + uni.meter.rx.avg[k][tap] = + uni.meter.rx.val[k][tap] = + 10.0 * log10(uni.meter.rx.val[k][tap] + 1e-20); + break; + case ADC_REAL: + for(i = 0; i < len; i++) + uni.meter.rx.val[k][tap] = max(fabs(vec[i].re), uni.meter.rx.val[k][tap]); + uni.meter.rx.val[k][tap] = 20.0 * log10(uni.meter.rx.val[k][tap] + 1e-10); + break; + case ADC_IMAG: + for(i = 0; i < len; i++) + uni.meter.rx.val[k][tap] = max(fabs(vec[i].im), uni.meter.rx.val[k][tap]); + uni.meter.rx.val[k][tap] = 20.0 * log10(uni.meter.rx.val[k][tap] + 1e-10); + break; + default: + break; + } +} + +PRIVATE void +do_tx_meter(CXB buf, int tap) { + COMPLEX *vec = CXBbase(buf); + int i, len = CXBhave(buf); + + uni.meter.tx.val[tap] = 0; + + switch (uni.meter.tx.type) { + case AVG_SIGNAL_STRENGTH: + for (i = 0; i < len; i++) + uni.meter.tx.val[tap] += Csqrmag(vec[i]); + uni.meter.tx.val[tap] = + uni.meter.tx.avg[tap] = + 0.9 * uni.meter.tx.avg[tap] + log10(uni.meter.tx.val[tap] + 1e-20); + break; + case SIGNAL_STRENGTH: + for (i = 0; i < len; i++) + uni.meter.tx.val[tap] += Csqrmag(vec[i]); + uni.meter.tx.avg[tap] = + uni.meter.tx.val[tap] = + 10.0 * log10(uni.meter.tx.val[tap] + 1e-20); + break; + case ADC_REAL: + for(i = 0; i < len; i++) + uni.meter.tx.val[tap] = max(fabs(vec[i].re), uni.meter.tx.val[tap]); + uni.meter.tx.val[tap] = 20.0 * log10(uni.meter.tx.val[tap] + 1e-10); + break; + case ADC_IMAG: + for(i = 0; i < len; i++) + uni.meter.tx.val[tap] = max(fabs(vec[i].im), uni.meter.tx.val[tap]); + uni.meter.tx.val[tap] = 20.0 * log10(uni.meter.tx.val[tap] + 1e-10); + break; + default: + break; + } +} + +PRIVATE void +do_rx_spectrum(int k, CXB buf, int type) { + if (uni.spec.flag && k == uni.spec.rxk && type == uni.spec.type) { + memcpy((char *) &CXBdata(uni.spec.accum, uni.spec.fill), + (char *) CXBbase(buf), + CXBhave(buf)); + uni.spec.fill = (uni.spec.fill + uni.spec.buflen) % uni.spec.size; + } +} + +PRIVATE void +do_tx_spectrum(CXB buf) { + memcpy((char *) &CXBdata(uni.spec.accum, uni.spec.fill), + (char *) CXBbase(buf), + CXBhave(buf)); + uni.spec.fill = (uni.spec.fill + uni.spec.buflen) % uni.spec.size; +} + +//======================================================================== +/* RX processing */ + +PRIVATE BOOLEAN +should_do_rx_squelch(int k) { + if (rx[k].squelch.flag) { + int i, n = CXBhave(rx[k].buf.o); + rx[k].squelch.power = 0.0; + for (i = 0; i < n; i++) + rx[k].squelch.power += Csqrmag(CXBdata(rx[k].buf.o, i)); + return rx[k].squelch.thresh > 10.0 * log10(rx[k].squelch.power); + } else + return rx[k].squelch.set = FALSE; +} + +// apply squelch +// slew into silence first time + +PRIVATE void +do_squelch(int k) { + rx[k].squelch.set = TRUE; + if (!rx[k].squelch.running) { + int i, m = rx[k].squelch.num, n = CXBhave(rx[k].buf.o) - m; + for (i = 0; i < m; i++) + CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), 1.0 - (REAL) i / m); + memset((void *) (CXBbase(rx[k].buf.o) + m), 0, n * sizeof(COMPLEX)); + rx[k].squelch.running = TRUE; + } else + memset((void *) CXBbase(rx[k].buf.o), 0, CXBhave(rx[k].buf.o) * sizeof(COMPLEX)); +} + +// lift squelch +// slew out from silence to full scale + +PRIVATE void +no_squelch(int k) { + if (rx[k].squelch.running) { + int i, m = rx[k].squelch.num; + for (i = 0; i < m; i++) + CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), (REAL) i / m); + rx[k].squelch.running = FALSE; + } +} + +/* pre-condition for (nearly) all RX modes */ + +PRIVATE void +do_rx_pre(int k) { + int i, n = min(CXBhave(rx[k].buf.i), uni.buflen); + + if (rx[k].scl.pre.flag) + for (i = 0; i < n; i++) + CXBdata(rx[k].buf.i, i) = Cscl(CXBdata(rx[k].buf.i, i), + rx[k].scl.pre.val); + + if (rx[k].nb.flag) noiseblanker(rx[k].nb.gen); + if (rx[k].nb_sdrom.flag) SDROMnoiseblanker(rx[k].nb_sdrom.gen); + + // metering for uncorrected values here + + do_rx_meter(k, rx[k].buf.i, RXMETER_PRE_CONV); + + correctIQ(rx[k].buf.i, rx[k].iqfix); + + /* 2nd IF conversion happens here */ + + if (rx[k].osc.gen->Frequency != 0.0) { + ComplexOSC(rx[k].osc.gen); + for (i = 0; i < n; i++) + CXBdata(rx[k].buf.i, i) = Cmul(CXBdata(rx[k].buf.i, i), + OSCCdata(rx[k].osc.gen, i)); + } + + /* filtering, metering, spectrum, squelch, & AGC */ + + if (rx[k].mode == SPEC) + + do_rx_spectrum(k, rx[k].buf.i, SPEC_SEMI_RAW); + + else { + + do_rx_meter(k, rx[k].buf.i, RXMETER_PRE_FILT); + do_rx_spectrum(k, rx[k].buf.i, SPEC_PRE_FILT); + + if (rx[k].tick == 0) + reset_OvSv(rx[k].filt.ovsv); + + filter_OvSv(rx[k].filt.ovsv); + CXBhave(rx[k].buf.o) = CXBhave(rx[k].buf.i); + + do_rx_meter(k, rx[k].buf.o, RXMETER_POST_FILT); + do_rx_spectrum(k, rx[k].buf.o, SPEC_POST_FILT); + + if (should_do_rx_squelch(k)) + do_squelch(k); + + else if (rx[k].agc.flag) + DigitalAgc(rx[k].agc.gen, rx[k].tick); + + } +} + +PRIVATE void +do_rx_post(int k) { + int i, n = CXBhave(rx[k].buf.o); + + if (!rx[k].squelch.set) { + no_squelch(k); + // spotting tone + if (rx[k].spot.flag) { + // remember whether it's turned itself off during this pass + rx[k].spot.flag = SpotTone(rx[k].spot.gen); + for (i = 0; i < n; i++) + CXBdata(rx[k].buf.o, i) = Cadd(CXBdata(rx[k].buf.o, i), + CXBdata(rx[k].spot.gen->buf, i)); + } + } + + // final scaling + + if (rx[k].scl.post.flag) + for (i = 0; i < n; i++) + CXBdata(rx[k].buf.o, i) = Cscl(CXBdata(rx[k].buf.o, i), + rx[k].scl.post.val); + + // not binaural? + // position in stereo field + + if (!rx[k].bin.flag) + for (i = 0; i < n; i++) + CXBdata(rx[k].buf.o, i) = Cscl(rx[k].azim, CXBreal(rx[k].buf.o, i)); +} + +/* demod processing */ + +PRIVATE void +do_rx_SBCW(int k) { + if (rx[k].anr.flag) lmsr_adapt(rx[k].anr.gen); + if (rx[k].anf.flag) lmsr_adapt(rx[k].anf.gen); +} + +PRIVATE void +do_rx_AM(int k) { AMDemod(rx[k].am.gen); } + +PRIVATE void +do_rx_FM(int k) { FMDemod(rx[k].fm.gen); } + +PRIVATE void +do_rx_DRM(int k) {} + +PRIVATE void +do_rx_SPEC(int k) { + memcpy(CXBbase(rx[k].buf.o), + CXBbase(rx[k].buf.i), + sizeof(COMPLEX) * CXBhave(rx[k].buf.i)); + if (rx[k].agc.flag) DigitalAgc(rx[k].agc.gen, rx[k].tick); +} + +PRIVATE void +do_rx_NIL(int k) { + int i, n = min(CXBhave(rx[k].buf.i), uni.buflen); + for (i = 0; i < n; i++) CXBdata(rx[k].buf.o, i) = cxzero; +} + +/* overall dispatch for RX processing */ + +PRIVATE void +do_rx(int k) { + do_rx_pre(k); + switch (rx[k].mode) { + case USB: + case LSB: + case CWU: + case CWL: + case DSB: do_rx_SBCW(k); break; + case AM: + case SAM: do_rx_AM(k); break; + case FMN: do_rx_FM(k); break; + case DRM: do_rx_DRM(k); break; + case SPEC: + default: do_rx_SPEC(k); break; + } + do_rx_post(k); +} + +//============================================================== +/* TX processing */ + +/* pre-condition for (nearly) all TX modes */ + +PRIVATE void +do_tx_pre(void) { + +if (tx.scl.pre.flag) { +int i, n = CXBhave(tx.buf.i); + for (i = 0; i < n; i++) + CXBdata(tx.buf.i, i) = Cmplx(CXBreal(tx.buf.i, i) * tx.scl.pre.val, 0.0); + } + + correctIQ(tx.buf.i, tx.iqfix); + + if (tx.spr.flag) SpeechProcessor(tx.spr.gen); + + if (tx.tick == 0) reset_OvSv(tx.filt.ovsv); + filter_OvSv(tx.filt.ovsv); +} + +PRIVATE void +do_tx_post(void) { + CXBhave(tx.buf.o) = CXBhave(tx.buf.i); + + if (tx.agc.flag) DigitalAgc(tx.agc.gen, tx.tick); + + // meter modulated signal + + do_tx_meter(tx.buf.o, TXMETER_POST_MOD); + + if (tx.scl.post.flag) { + int i, n = CXBhave(tx.buf.o); + for (i = 0; i < n; i++) + CXBdata(tx.buf.o, i) = Cscl(CXBdata(tx.buf.o, i), tx.scl.post.val); + } + + if (uni.spec.flag) + do_tx_spectrum(tx.buf.o); + + if (tx.osc.gen->Frequency != 0.0) { + int i; + ComplexOSC(tx.osc.gen); + for (i = 0; i < CXBhave(tx.buf.o); i++) + CXBdata(tx.buf.o, i) = Cmul(CXBdata(tx.buf.o, i), OSCCdata(tx.osc.gen, i)); + } +} + +/* modulator processing */ + +PRIVATE void +do_tx_SBCW(void) { + int i, n = min(CXBhave(tx.buf.o), uni.buflen); + + if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0) + for (i = 0; i < n; i++) { + tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.99), + Cscl(CXBdata(tx.buf.o, i), -0.01)); + CXBdata(tx.buf.o, i) = Cadd(CXBdata(tx.buf.o, i), tx.scl.dc); + } +} + +PRIVATE void +do_tx_AM(void) { + int i, n = min(CXBhave(tx.buf.o), uni.buflen); + + if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0) + for (i = 0; i < n; i++) { + tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999), + Cscl(CXBdata(tx.buf.o, i), -0.001)); + CXBreal(tx.buf.o, i) = + 0.49995 + 0.49995 * (CXBreal(tx.buf.o, i) - tx.scl.dc.re); + CXBimag(tx.buf.o, i) = 0.0; + } +} + +PRIVATE void +do_tx_FM(void) { + int i, n = min(CXBhave(tx.buf.o), uni.buflen); + if ((tx.norm = CXBnorm(tx.buf.o)) > 0.0) + for (i = 0; i < n; i++) { + tx.scl.dc = Cadd(Cscl(tx.scl.dc, 0.999), + Cscl(CXBdata(tx.buf.o, i), 0.001)); + tx.osc.phase += (CXBreal(tx.buf.o, i) - tx.scl.dc.re) * CvtMod2Freq; + if (tx.osc.phase >= TWOPI) tx.osc.phase -= TWOPI; + if (tx.osc.phase < 0.0) tx.osc.phase += TWOPI; + CXBdata(tx.buf.o, i) = + Cscl(Cmplx(cos(tx.osc.phase), sin(tx.osc.phase)), 0.99999); + } +} + +PRIVATE void +do_tx_NIL(void) { + int i, n = min(CXBhave(tx.buf.i), uni.buflen); + for (i = 0; i < n; i++) CXBdata(tx.buf.o, i) = cxzero; +} + +/* general TX processing dispatch */ + +PRIVATE void +do_tx(void) { + do_tx_pre(); + switch (tx.mode) { + case USB: + case LSB: + case CWU: + case CWL: + case DSB: do_tx_SBCW(); break; + case AM: + case SAM: do_tx_AM(); break; + case FMN: do_tx_FM(); break; + case DRM: + case SPEC: + default: do_tx_NIL(); break; + } + do_tx_post(); +} + +//======================================================================== +/* overall buffer processing; + come here when there are buffers to work on */ + +void +process_samples(float *bufl, float *bufr, + float *auxl, float *auxr, + int n) { + int i, k; + + switch (uni.mode.trx) { + + case RX: + + // make copies of the input for all receivers + for (k = 0; k < uni.multirx.nrx; k++) + if (uni.multirx.act[k]) { + for (i = 0; i < n; i++) + CXBimag(rx[k].buf.i, i) = bufl[i], CXBreal(rx[k].buf.i, i) = bufr[i]; + CXBhave(rx[k].buf.i) = n; + } + + // prepare buffers for mixing + memset((char *) bufl, 0, n * sizeof(float)); + memset((char *) bufr, 0, n * sizeof(float)); + + // run all receivers + for (k = 0; k < uni.multirx.nrx; k++) + if (uni.multirx.act[k]) { + do_rx(k), rx[k].tick++; + // mix + for (i = 0; i < n; i++) + bufl[i] += (float)CXBimag(rx[k].buf.o, i), + bufr[i] += (float)CXBreal(rx[k].buf.o, i); + CXBhave(rx[k].buf.o) = n; + } + + // late mixing of aux buffers + if (uni.mix.rx.flag) + for (i = 0; i < n; i++) + bufl[i] += (float)(auxl[i] * uni.mix.rx.gain), + bufr[i] += (float)(auxr[i] * uni.mix.rx.gain); + + break; + + case TX: + + // early mixing of aux buffers + if (uni.mix.tx.flag) + for (i = 0; i < n; i++) + bufl[i] += (float)(auxl[i] * uni.mix.tx.gain), + bufr[i] += (float)(auxr[i] * uni.mix.tx.gain); + + for (i = 0; i < n; i++) + CXBimag(tx.buf.i, i) = bufl[i], CXBreal(tx.buf.i, i) = bufr[i]; + CXBhave(tx.buf.i) = n; + + do_tx(), tx.tick++; + + for (i = 0; i < n; i++) + bufl[i] = (float) CXBimag(tx.buf.o, i), bufr[i] = (float) CXBreal(tx.buf.o, i); + CXBhave(tx.buf.o) = n; + + break; + } + + uni.tick++; +} diff --git a/jDttSP/win/sdrexport.c b/jDttSP/win/sdrexport.c new file mode 100644 index 0000000..4213ef3 --- /dev/null +++ b/jDttSP/win/sdrexport.c @@ -0,0 +1,39 @@ +/* sdrexport.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +struct _uni uni; +struct _rx rx[MAXRX]; +struct _tx tx; +struct _top top; diff --git a/jDttSP/win/winmain.c b/jDttSP/win/winmain.c new file mode 100644 index 0000000..28ed4df --- /dev/null +++ b/jDttSP/win/winmain.c @@ -0,0 +1,820 @@ +/* main.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004-5 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +///////////////////////////////////////////////////////////////////////// + +// Windows SHTUFF + +PRIVATE CRITICAL_SECTION csobj; +PRIVATE CRITICAL_SECTION cs_updobj; +PRIVATE LPCRITICAL_SECTION cs; +PRIVATE LPCRITICAL_SECTION cs_upd; +PRIVATE BOOLEAN IC = FALSE; + +// elementary defaults +struct _loc loc; + +///////////////////////////////////////////////////////////////////////// +// most of what little we know here about the inner loop, +// functionally speaking + +extern void reset_meters(void); +extern void reset_spectrum(void); +extern void reset_counters(void); +extern void process_samples(float *, float *, float *, float *, int); +extern void setup_workspace(void); +extern void destroy_workspace(void); + +//======================================================================== + +PRIVATE void +spectrum_thread(void) +{ + DWORD NumBytesWritten; + while (top.running) { + sem_wait(&top.sync.pws.sem); + compute_spectrum(&uni.spec); + WriteFile(top.meas.spec.fd, (LPVOID) & uni.spec.label, + sizeof(int), &NumBytesWritten, NULL); + WriteFile(top.meas.spec.fd, (LPVOID) uni.spec.output, + sizeof(float) * uni.spec.size, &NumBytesWritten, NULL); + } + pthread_exit(0); +} + +/*PRIVATE void +scope_thread(void) { + DWORD NumBytesWritten; + while (top.running) { + sem_wait(&top.sync.scope.sem); + compute_spectrum(&uni.spec); + WriteFile(top.meas.scope.fd,(LPVOID)&uni.spec.label, + sizeof(int),&NumBytesWritten,NULL); + WriteFile(top.meas.scope.fd,(LPVOID)uni.spec.accum, + sizeof(float)*uni.spec.size,&NumBytesWritten,NULL); + } + pthread_exit(0); +} */ + +PRIVATE void +meter_thread(void) +{ + DWORD NumBytesWritten; + while (top.running) { + sem_wait(&top.sync.mtr.sem); + WriteFile(top.meas.mtr.fd, (LPVOID) & uni.meter.label, sizeof(int), + &NumBytesWritten, NULL); + WriteFile(top.meas.mtr.fd, (LPVOID) & uni.meter.snap.rx, + sizeof(REAL) * MAXRX * RXMETERPTS, &NumBytesWritten, NULL); + WriteFile(top.meas.mtr.fd, (LPVOID) & uni.meter.snap.tx, + sizeof(REAL) * TXMETERPTS, &NumBytesWritten, NULL); + } + pthread_exit(0); +} + +//======================================================================== + +PRIVATE void +monitor_thread(void) +{ + while (top.running) { + sem_wait(&top.sync.mon.sem); + /* If there is anything that needs monitoring, do it here */ + fprintf(stderr, + "@@@ mon [%d]: cb = %d rbi = %d rbo = %d xr = %d\n", + uni.tick, + top.jack.blow.cb, + top.jack.blow.rb.i, top.jack.blow.rb.o, top.jack.blow.xr); + memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow)); + } + pthread_exit(0); + +} + +//======================================================================== + +PRIVATE void +process_updates_thread(void) +{ + + while (top.running) { + DWORD NumBytesRead; + pthread_testcancel(); + while (ReadFile(top.parm.fd, top.parm.buff, 256, &NumBytesRead, NULL)) { + fprintf(stderr, "Update Bytes:%lu Msg:%s\n", NumBytesRead, + top.parm.buff), fflush(stderr); + if (NumBytesRead != 0) + do_update(top.parm.buff, top.verbose ? stderr : 0); + } + } + pthread_exit(0); +} + +//======================================================================== + + +PRIVATE void +gethold(void) +{ + EnterCriticalSection(cs); + if (ringb_write_space(top.jack.ring.o.l) + < top.hold.size.bytes) { + // pathology + ringb_reset(top.jack.ring.o.l); + ringb_reset(top.jack.ring.o.r); + top.jack.blow.rb.o++; + } + ringb_write(top.jack.ring.o.l, + (char *) top.hold.buf.l, top.hold.size.bytes); + ringb_write(top.jack.ring.o.r, + (char *) top.hold.buf.r, top.hold.size.bytes); + if (ringb_read_space(top.jack.ring.i.l) + < top.hold.size.bytes) { + // pathology + ringb_reset(top.jack.ring.i.l); + ringb_reset(top.jack.ring.i.r); + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + ringb_reset(top.jack.auxr.i.l); + ringb_reset(top.jack.auxr.i.r); + memset((char *) top.hold.aux.l, 0, top.hold.size.bytes); + memset((char *) top.hold.aux.r, 0, top.hold.size.bytes); + top.jack.blow.rb.i++; + } else { + ringb_read(top.jack.ring.i.l, + (char *) top.hold.buf.l, top.hold.size.bytes); + ringb_read(top.jack.ring.i.r, + (char *) top.hold.buf.r, top.hold.size.bytes); + ringb_read(top.jack.auxr.i.l, + (char *) top.hold.aux.l, top.hold.size.bytes); + ringb_read(top.jack.auxr.i.r, + (char *) top.hold.aux.r, top.hold.size.bytes); + } + LeaveCriticalSection(cs); +} + +PRIVATE BOOLEAN +canhold(void) +{ + BOOLEAN answer; + EnterCriticalSection(cs); + answer = (ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes); + LeaveCriticalSection(cs); + return answer; +} + + +//------------------------------------------------------------------------ + +PRIVATE void +run_mute(void) +{ + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + memset((char *) top.hold.aux.l, 0, top.hold.size.bytes); + memset((char *) top.hold.aux.r, 0, top.hold.size.bytes); + uni.tick++; +} + +PRIVATE void +run_pass(void) +{ + uni.tick++; +} + +PRIVATE void +run_play(void) +{ + process_samples(top.hold.buf.l, top.hold.buf.r, + top.hold.aux.l, top.hold.aux.r, top.hold.size.frames); +} + +// NB do not set RUN_SWCH directly via setRunState; +// use setSWCH instead + +PRIVATE void +run_swch(void) +{ + if (top.swch.bfct.have == 0) { + // first time + // apply ramp down + int i, m = top.swch.fade, n = top.swch.tail; + for (i = 0; i < m; i++) { + float w = (float) 1.0 - (float) i / m; + top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w; + } + memset((char *) (top.hold.buf.l + m), 0, n); + memset((char *) (top.hold.buf.r + m), 0, n); + top.swch.bfct.have++; + } else if (top.swch.bfct.have < top.swch.bfct.want) { + // in medias res + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + top.swch.bfct.have++; + } else { + // last time + // apply ramp up + int i, m = top.swch.fade, n = top.swch.tail; + for (i = 0; i < m; i++) { + float w = (float) i / m; + top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w; + } + uni.mode.trx = top.swch.trx.next; + switch (uni.mode.trx) { + int i; + case TX: + tx.agc.gen->over = tx.tick + 3; + break; + case RX: + for (i = 0; i < uni.multirx.nrx; i++) + rx[i].agc.gen->over = rx[i].tick + 3; + break; + } + + top.state = top.swch.run.last; + top.swch.bfct.want = top.swch.bfct.have = 0; + + ringb_reset(top.jack.ring.o.l); + ringb_reset(top.jack.ring.o.r); + ringb_clear(top.jack.ring.o.l, top.hold.size.bytes); + ringb_clear(top.jack.ring.o.r, top.hold.size.bytes); + + reset_meters(); + reset_spectrum(); + reset_counters(); + } + + process_samples(top.hold.buf.l, top.hold.buf.r, + top.hold.aux.l, top.hold.aux.r, top.hold.size.frames); +} + +//======================================================================== + + + +DttSP_EXP void +audio_callback(float *input_l, float *input_r, float *output_l, + float *output_r, int nframes) +{ + size_t nbytes = sizeof(float) * nframes; + + + EnterCriticalSection(cs); + + + if (ringb_read_space(top.jack.ring.o.l) >= nbytes) { + ringb_read(top.jack.ring.o.l, (char *) output_l, nbytes); + ringb_read(top.jack.ring.o.r, (char *) output_r, nbytes); + } else { // rb pathology + memset((char *) output_l, 0, nbytes); + memset((char *) output_r, 0, nbytes); + ringb_restart(top.jack.ring.o.l, nbytes); + ringb_restart(top.jack.ring.o.r, nbytes); + top.jack.blow.rb.o++; + } + + // input: copy from port to ring + if (ringb_write_space(top.jack.ring.i.l) >= nbytes) { + ringb_write(top.jack.ring.i.l, (char *) input_l, nbytes); + ringb_write(top.jack.ring.i.r, (char *) input_r, nbytes); + ringb_write(top.jack.auxr.i.l, (char *) input_l, nbytes); + ringb_write(top.jack.auxr.i.r, (char *) input_r, nbytes); + } else { // rb pathology + ringb_restart(top.jack.ring.i.l, nbytes); + ringb_restart(top.jack.ring.i.r, nbytes); + ringb_restart(top.jack.auxr.i.l, nbytes); + ringb_restart(top.jack.auxr.i.r, nbytes); + top.jack.blow.rb.i++; + } + LeaveCriticalSection(cs); + // if enough accumulated in ring, fire dsp + if (ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes) + sem_post(&top.sync.buf.sem); + + // check for blowups + if ((top.jack.blow.cb > 0) || + (top.jack.blow.rb.i > 0) || (top.jack.blow.rb.o > 0)) + sem_post(&top.sync.mon.sem); +} + +//======================================================================== + +DttSP_EXP void +process_samples_thread(void) +{ + while (top.running) { + sem_wait(&top.sync.buf.sem); + do { + gethold(); + sem_wait(&top.sync.upd.sem); + switch (top.state) { + case RUN_MUTE: + run_mute(); + break; + case RUN_PASS: + run_pass(); + break; + case RUN_PLAY: + run_play(); + break; + case RUN_SWCH: + run_swch(); + break; + } + sem_post(&top.sync.upd.sem); + } while (canhold()); + } +} + + +void +closeup(void) +{ + top.running = FALSE; + Sleep(50); + safefree((char *) top.jack.ring.o.r); + safefree((char *) top.jack.ring.o.l); + safefree((char *) top.jack.ring.i.r); + safefree((char *) top.jack.ring.i.l); + safefree((char *) top.jack.auxr.i.l); + safefree((char *) top.jack.auxr.i.r); + safefree((char *) top.jack.auxr.o.l); + safefree((char *) top.jack.auxr.o.r); + + CloseHandle(top.parm.fp); + DisconnectNamedPipe(top.parm.fd); + CloseHandle(top.parm.fd); + + + if (uni.meter.flag) { + CloseHandle(top.meas.mtr.fp); + DisconnectNamedPipe(top.meas.mtr.fd); + CloseHandle(top.meas.mtr.fd); + }; + + if (uni.spec.flag) { + CloseHandle(top.meas.spec.fp); + DisconnectNamedPipe(top.meas.spec.fd); + CloseHandle(top.meas.spec.fd); + }; + destroy_workspace(); +} + +//........................................................................ + +PRIVATE void +setup_switching(void) +{ + top.swch.fade = (int) (0.1 * uni.buflen + 0.5); + top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float); +} + +PRIVATE void +setup_local_audio(void) +{ + top.hold.size.frames = uni.buflen; + top.hold.size.bytes = top.hold.size.frames * sizeof(float); + top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float), + "main hold buffer left"); + top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float), + "main hold buffer right"); + top.hold.aux.l = (float *) safealloc(top.hold.size.frames, sizeof(float), + "aux hold buffer left"); + top.hold.aux.r = (float *) safealloc(top.hold.size.frames, sizeof(float), + "aux hold buffer right"); +} + +#include + +PRIVATE void +DisplayErrorText(DWORD dwLastError) +{ + HMODULE hModule = NULL; // default to system source + LPSTR MessageBuffer; + DWORD dwBufferLength; + + DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM; + + // + // If dwLastError is in the network range, + // load the message source. + // + + if (dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) { + hModule = LoadLibraryEx(TEXT("netmsg.dll"), + NULL, LOAD_LIBRARY_AS_DATAFILE); + + if (hModule != NULL) + dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE; + } + // + // Call FormatMessage() to allow for message + // text to be acquired from the system + // or from the supplied module handle. + // + + if (dwBufferLength = FormatMessageA(dwFormatFlags, hModule, // module to get message from (NULL == system) + dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language + (LPSTR) & MessageBuffer, 0, NULL)) { + DWORD dwBytesWritten; + + // + // Output message string on stderr. + // + WriteFile(GetStdHandle(STD_ERROR_HANDLE), + MessageBuffer, dwBufferLength, &dwBytesWritten, NULL); + + // + // Free the buffer allocated by the system. + // + LocalFree(MessageBuffer); + } + // + // If we loaded a message source, unload it. + // + if (hModule != NULL) + FreeLibrary(hModule); +} + + + +PRIVATE sem_t setup_update_sem; + +PRIVATE void +setup_update_server() +{ + + if (INVALID_HANDLE_VALUE == (top.parm.fd = CreateNamedPipe(top.parm.path, + PIPE_ACCESS_INBOUND, + PIPE_WAIT | + PIPE_TYPE_MESSAGE + | + PIPE_READMODE_MESSAGE, + PIPE_UNLIMITED_INSTANCES, + 512, 512, + INFINITE, + NULL))) { + fprintf(stderr, "Update server pipe setup failed:\n"), fflush(stderr); + DisplayErrorText(GetLastError()); + } +// fprintf(stderr,"Update NamedPipe made\n"),fflush(stderr); + sem_post(&setup_update_sem); + if (ConnectNamedPipe(top.parm.fd, NULL)) { +// fprintf(stderr,"Connected the server to the Update pipe\n"),fflush(stderr); + } else { + fprintf(stderr, "Connected the server to the Update pipe failed\n"), + fflush(stderr); + DisplayErrorText(GetLastError()); + } + pthread_exit(0); +} + + +PRIVATE void +setup_update_client() +{ +// fprintf(stderr,"Looking for the Update server\n"),fflush(stderr); + WaitNamedPipe(top.parm.path, INFINITE); +// fprintf(stderr,"Found the Update server\n"),fflush(stderr); + if (INVALID_HANDLE_VALUE == (top.parm.fp = CreateFile(top.parm.path, + GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL))) { + fprintf(stderr, "The Update Client Open Failed\n"), fflush(stderr); + DisplayErrorText(GetLastError()); + } + sem_post(&setup_update_sem); +/* { + DWORD numwritten; + WriteFile(top.parm.fp,"test",5,&numwritten,NULL); + fprintf(stderr,"Number written to server: %lu\n",numwritten),fflush(stderr); + }*/ + pthread_exit(0); +} + +PRIVATE void +setup_meter_server() +{ + top.meas.mtr.fd = CreateNamedPipe(top.meas.mtr.path, + PIPE_ACCESS_OUTBOUND, + PIPE_WAIT | PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE, + PIPE_UNLIMITED_INSTANCES, 512, 512, + INFINITE, NULL); +// fprintf(stderr,"meter handle = %08X\n",(DWORD)top.meas.mtr.fd),fflush(stderr); + if (top.meas.mtr.fd == INVALID_HANDLE_VALUE) { + fprintf(stderr, "Meter server pipe setup failed:\n"), fflush(stderr); + DisplayErrorText(GetLastError()); + } else { +// fprintf(stderr,"Meter Pipe Connect succeeded\n"),fflush(stderr); + sem_post(&setup_update_sem); + if (ConnectNamedPipe(top.meas.mtr.fd, NULL)) { +// fprintf(stderr,"Connected the Meter Pooch\n"),fflush(stderr); + } else { + fprintf(stderr, "Meter Pipe Connect failed\n"), fflush(stderr); + DisplayErrorText(GetLastError()); + } + } + pthread_exit(0); +} + +PRIVATE void +setup_meter_client() +{ +// fprintf(stderr,"Looking for the meter server\n"),fflush(stderr); + if (WaitNamedPipe(top.meas.mtr.path, INFINITE)) { +// fprintf(stderr,"Found the Meter server\n"),fflush(stderr); + if (INVALID_HANDLE_VALUE == + (top.meas.mtr.fp = + CreateFile(top.meas.mtr.path, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL))) { + fprintf(stderr, "The Meter Client Open Failed\n"), fflush(stderr); + DisplayErrorText(GetLastError()); + } else { +// fprintf(stderr,"The Meter Client Open Succeeded\n"),fflush(stderr); + } + } else { + fprintf(stderr, "Wait for meter pipe failed: Error message %d\n", + GetLastError()), fflush(stderr); + } + sem_post(&setup_update_sem); + pthread_exit(0); +} + +PRIVATE void +setup_spec_server() +{ + + if (INVALID_HANDLE_VALUE == + (top.meas.spec.fd = + CreateNamedPipe(top.meas.spec.path, PIPE_ACCESS_OUTBOUND, + PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, + PIPE_UNLIMITED_INSTANCES, 32768, 32768, INFINITE, + NULL))) { + fprintf(stderr, "Spectrum pipe create failed\n"), fflush(stderr); + DisplayErrorText(GetLastError()); + } else { +// fprintf(stderr,"Spectrum Pipe %s Create succeeded\n",top.meas.spec.path),fflush(stderr); + sem_post(&setup_update_sem); + if (ConnectNamedPipe(top.meas.spec.fd, NULL)) { +// fprintf(stderr,"Connected to the Spectrum Pipe\n"),fflush(stderr); + } else { + fprintf(stderr, "Spectrum pipe connect failed\n"), fflush(stderr); + DisplayErrorText(GetLastError()); + } + } + pthread_exit(0); +} + +PRIVATE void +setup_spec_client() +{ +// fprintf(stderr,"Looking for the spectrum server\n"),fflush(stderr); + if (WaitNamedPipe(top.meas.spec.path, INFINITE)) { +// fprintf(stderr,"Found the server\n"),fflush(stderr); + if (INVALID_HANDLE_VALUE == + (top.meas.spec.fp = + CreateFile(top.meas.spec.path, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL))) { + fprintf(stderr, "The Spectrum Client Open Failed\n"), fflush(stderr); + DisplayErrorText(GetLastError()); + } else { +// fprintf(stderr,"The Spectrum Client Open Succeeded\n"); +// fprintf(stderr,"Spec Read handle = %08X\n",(DWORD)top.meas.spec.fp),fflush(stderr); + } + } else { + fprintf(stderr, "Wait for spec pipe failed\n"), fflush(stderr); + DisplayErrorText(GetLastError()); + } + sem_post(&setup_update_sem); + pthread_exit(0); +} +PRIVATE pthread_t id1, id2, id3, id4, id5, id6; +PRIVATE void +setup_updates(void) +{ + + char mesg[16384] = "TEST TEST METER\n"; +// DWORD NumBytes; + top.parm.path = loc.path.parm; + sem_init(&setup_update_sem, 0, 0); + + + if (uni.meter.flag) { + top.meas.mtr.path = loc.path.meter; + } + if (uni.spec.flag) { + top.meas.spec.path = loc.path.spec; + } + + // Do this STUPID stuff to make use of the Named Pipe Mechanism in Windows + // For the update server + + + pthread_create(&id1, NULL, (void *) setup_update_server, NULL); + sem_wait(&setup_update_sem); + pthread_create(&id2, NULL, (void *) setup_update_client, NULL); + sem_wait(&setup_update_sem); + if (uni.meter.flag) { + pthread_create(&id3, NULL, (void *) setup_meter_server, NULL); + sem_wait(&setup_update_sem); + pthread_create(&id4, NULL, (void *) setup_meter_client, NULL); + sem_wait(&setup_update_sem); +/* if (WriteFile(top.meas.mtr.fd,mesg,strlen(mesg)+1,&NumBytes,NULL)) + { + fprintf(stderr,"Meter Pipe write succeeded and wrote %lu bytes\n",NumBytes),fflush(stderr); + } else { + fprintf(stderr,"Meter Pipe write failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } + if (ReadFile(top.meas.mtr.fp,mesg,256,&NumBytes,NULL)) + { + fprintf(stderr,"Meter Pipe read succeeded and %lu bytes read\n",NumBytes),fflush(stderr); + fprintf(stderr,"Meter message %s",mesg),fflush(stderr); + } else { + fprintf(stderr,"Meter Pipe read failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + }*/ + + } + + if (uni.spec.flag) { + memset(mesg, 0, 16384); + pthread_create(&id5, NULL, (void *) setup_spec_server, NULL); + sem_wait(&setup_update_sem); + pthread_create(&id6, NULL, (void *) setup_spec_client, NULL); + sem_wait(&setup_update_sem); + Sleep(0); +/* if (WriteFile(top.meas.spec.fd,mesg,16384,&NumBytes,NULL)) + { + fprintf(stderr,"Spec Pipe write succeeded and wrote %lu bytes\n",NumBytes),fflush(stderr); + } else { + fprintf(stderr,"Spec Pipe write failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } + fprintf(stderr,"Spec Read handle(2) = %08X\n",(DWORD)top.meas.spec.fp),fflush(stderr); + if (ReadFile(top.meas.spec.fp,mesg,16384,&NumBytes,NULL)) + { + fprintf(stderr,"Spec Pipe read succeeded and %lu bytes read\n",NumBytes),fflush(stderr); + } else { + fprintf(stderr,"Spec Pipe read failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } */ + } + sem_destroy(&setup_update_sem); +} +PRIVATE void +setup_system_audio(void) +{ + size_t ringsize; + void *usemem; + sprintf(top.jack.name, "sdr-%d", top.pid); + top.jack.size = uni.buflen; + ringsize = top.hold.size.bytes * loc.mult.ring + sizeof(ringb_t); + usemem = safealloc(ringsize, 1, "Ring Input Left"); + top.jack.ring.i.l = + ringb_create(usemem, top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize, 1, "Ring Input Right"); + top.jack.ring.i.r = + ringb_create(usemem, top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize, 1, "Ring Output Left"); + top.jack.ring.o.l = + ringb_create(usemem, top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize, 1, "Ring Output Right"); + top.jack.ring.o.r = + ringb_create(usemem, top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize, 1, "Ring Input Left Auxiliary"); + top.jack.auxr.i.l = + ringb_create(usemem, top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize, 1, "Ring Input Right Auxiliary"); + top.jack.auxr.i.r = + ringb_create(usemem, top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize, 1, "Ring Output Left Auxiliary"); + top.jack.auxr.o.l = + ringb_create(usemem, top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize, 1, "Ring Output Right Auxiliary"); + top.jack.auxr.o.r = + ringb_create(usemem, top.hold.size.bytes * loc.mult.ring); + + ringb_clear(top.jack.ring.o.l, top.jack.size * sizeof(float)); + ringb_clear(top.jack.ring.o.r, top.jack.size * sizeof(float)); + ringb_clear(top.jack.auxr.o.l, top.jack.size * sizeof(float)); + ringb_clear(top.jack.auxr.o.r, top.jack.size * sizeof(float)); +} + +PRIVATE void +setup_threading(void) +{ + sem_init(&top.sync.upd.sem, 0, 0); + pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, + NULL); + sem_init(&top.sync.buf.sem, 0, 0); + pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, + NULL); + sem_init(&top.sync.mon.sem, 0, 0); + pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL); + if (uni.meter.flag) { + sem_init(&top.sync.mtr.sem, 0, 0); + pthread_create(&top.thrd.mtr.id, NULL, (void *) meter_thread, NULL); + } + if (uni.spec.flag) { + sem_init(&top.sync.pws.sem, 0, 0); + pthread_create(&top.thrd.pws.id, NULL, (void *) spectrum_thread, NULL); + } + cs = &csobj; + InitializeCriticalSection(cs); +} + +//======================================================================== +// hard defaults, then environment + +PRIVATE void +setup_defaults(void) +{ + loc.name[0] = 0; // no default name for jack client + strcpy(loc.path.rcfile, RCBASE); + strcpy(loc.path.parm, PARMPATH); + strcpy(loc.path.meter, METERPATH); + strcpy(loc.path.spec, SPECPATH); + strcpy(loc.path.wisdom, WISDOMPATH); + loc.def.rate = DEFRATE; + loc.def.size = DEFSIZE; + loc.def.nrx = MAXRX; + loc.def.mode = DEFMODE; + loc.def.spec = DEFSPEC; + loc.mult.ring = RINGMULT; +} + +//======================================================================== +void +setup() +{ + + + top.pid = GetCurrentThreadId(); + top.uid = 0L; + top.start_tv = now_tv(); + top.running = TRUE; + top.verbose = FALSE; + top.state = RUN_PLAY; + + setup_defaults(); + top.verbose = FALSE; + uni.meter.flag = TRUE; + uni.spec.flag = TRUE; + + setup_workspace(); + setup_updates(); + + setup_local_audio(); + setup_system_audio(); + + setup_threading(); + setup_switching(); + uni.spec.flag = TRUE; + uni.spec.type = SPEC_POST_FILT; + uni.spec.scale = SPEC_PWR; + uni.spec.rxk = 0; + +} diff --git a/jDttSP/win/winmain.c~ b/jDttSP/win/winmain.c~ new file mode 100644 index 0000000..9832178 --- /dev/null +++ b/jDttSP/win/winmain.c~ @@ -0,0 +1,803 @@ +/* main.c + +This file is part of a program that implements a Software-Defined Radio. + +Copyright (C) 2004-5 by Frank Brickle, AB2KT and Bob McGwier, N4HY + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +The authors can be reached by email at + +ab2kt@arrl.net +or +rwmcgwier@comcast.net + +or by paper mail at + +The DTTS Microwave Society +6 Kathleen Place +Bridgewater, NJ 08807 +*/ + +#include + +///////////////////////////////////////////////////////////////////////// + +// Windows SHTUFF + +PRIVATE CRITICAL_SECTION csobj; +PRIVATE CRITICAL_SECTION cs_updobj; +PRIVATE LPCRITICAL_SECTION cs; +PRIVATE LPCRITICAL_SECTION cs_upd; +PRIVATE BOOLEAN IC=FALSE; + +// elementary defaults +struct _loc loc; + +///////////////////////////////////////////////////////////////////////// +// most of what little we know here about the inner loop, +// functionally speaking + +extern void reset_meters(void); +extern void reset_spectrum(void); +extern void reset_counters(void); +extern void process_samples(float *, float *, float *, float *, int); +extern void setup_workspace(void); +extern void destroy_workspace(void); + +//======================================================================== + +PRIVATE void +spectrum_thread(void) { + DWORD NumBytesWritten; + while (top.running) { + sem_wait(&top.sync.pws.sem); + compute_spectrum(&uni.spec); + WriteFile(top.meas.spec.fd,(LPVOID)&uni.spec.label, + sizeof(int),&NumBytesWritten,NULL); + WriteFile(top.meas.spec.fd,(LPVOID)uni.spec.output, + sizeof(float)*uni.spec.size,&NumBytesWritten,NULL); + } + pthread_exit(0); +} + +/*PRIVATE void +scope_thread(void) { + DWORD NumBytesWritten; + while (top.running) { + sem_wait(&top.sync.scope.sem); + compute_spectrum(&uni.spec); + WriteFile(top.meas.scope.fd,(LPVOID)&uni.spec.label, + sizeof(int),&NumBytesWritten,NULL); + WriteFile(top.meas.scope.fd,(LPVOID)uni.spec.accum, + sizeof(float)*uni.spec.size,&NumBytesWritten,NULL); + } + pthread_exit(0); +} */ + +PRIVATE void +meter_thread(void) { + DWORD NumBytesWritten; + while (top.running) { + sem_wait(&top.sync.mtr.sem); + WriteFile(top.meas.mtr.fd,(LPVOID)&uni.meter.label, sizeof(int),&NumBytesWritten,NULL); + WriteFile(top.meas.mtr.fd,(LPVOID)&uni.meter.snap.rx,sizeof(REAL)*MAXRX * RXMETERPTS,&NumBytesWritten,NULL); + WriteFile(top.meas.mtr.fd,(LPVOID)&uni.meter.snap.tx,sizeof(REAL)*TXMETERPTS,&NumBytesWritten,NULL); + } + pthread_exit(0); +} + +//======================================================================== + +PRIVATE void +monitor_thread(void) { + while (top.running) { + sem_wait(&top.sync.mon.sem); + /* If there is anything that needs monitoring, do it here */ + fprintf(stderr, + "@@@ mon [%d]: cb = %d rbi = %d rbo = %d xr = %d\n", + uni.tick, + top.jack.blow.cb, + top.jack.blow.rb.i, + top.jack.blow.rb.o, + top.jack.blow.xr); + memset((char *) &top.jack.blow, 0, sizeof(top.jack.blow)); + } + pthread_exit(0); + +} + +//======================================================================== + +PRIVATE void +process_updates_thread(void) { + + while (top.running) { + DWORD NumBytesRead; + pthread_testcancel(); + while (ReadFile(top.parm.fd,top.parm.buff,256,&NumBytesRead,NULL)) + { + fprintf(stderr,"Update Bytes:%lu Msg:%s\n",NumBytesRead,top.parm.buff),fflush(stderr); + if (NumBytesRead != 0) do_update(top.parm.buff, top.verbose ? stderr : 0); + } + } + pthread_exit(0); +} + +//======================================================================== + + +PRIVATE void +gethold(void) { + EnterCriticalSection(cs); + if (ringb_write_space(top.jack.ring.o.l) + < top.hold.size.bytes) { + // pathology + ringb_reset(top.jack.ring.o.l); + ringb_reset(top.jack.ring.o.r); + top.jack.blow.rb.o++; + } + ringb_write(top.jack.ring.o.l, + (char *) top.hold.buf.l, + top.hold.size.bytes); + ringb_write(top.jack.ring.o.r, + (char *) top.hold.buf.r, + top.hold.size.bytes); + if (ringb_read_space(top.jack.ring.i.l) + < top.hold.size.bytes) { + // pathology + ringb_reset(top.jack.ring.i.l); + ringb_reset(top.jack.ring.i.r); + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + ringb_reset(top.jack.auxr.i.l); + ringb_reset(top.jack.auxr.i.r); + memset((char *) top.hold.aux.l, 0, top.hold.size.bytes); + memset((char *) top.hold.aux.r, 0, top.hold.size.bytes); + top.jack.blow.rb.i++; + } else { + ringb_read(top.jack.ring.i.l, + (char *) top.hold.buf.l, + top.hold.size.bytes); + ringb_read(top.jack.ring.i.r, + (char *) top.hold.buf.r, + top.hold.size.bytes); + ringb_read(top.jack.auxr.i.l, + (char *) top.hold.aux.l, + top.hold.size.bytes); + ringb_read(top.jack.auxr.i.r, + (char *) top.hold.aux.r, + top.hold.size.bytes); + } + LeaveCriticalSection(cs); +} + +PRIVATE BOOLEAN +canhold(void) { + BOOLEAN answer; + EnterCriticalSection(cs); + answer = (ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes); + LeaveCriticalSection(cs); + return answer; +} + + +//------------------------------------------------------------------------ + +PRIVATE void +run_mute(void) { + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + memset((char *) top.hold.aux.l, 0, top.hold.size.bytes); + memset((char *) top.hold.aux.r, 0, top.hold.size.bytes); + uni.tick++; +} + +PRIVATE void +run_pass(void) { uni.tick++; } + +PRIVATE void +run_play(void) { + process_samples(top.hold.buf.l, top.hold.buf.r, + top.hold.aux.l, top.hold.aux.r, + top.hold.size.frames); +} + +// NB do not set RUN_SWCH directly via setRunState; +// use setSWCH instead + +PRIVATE void +run_swch(void) { + if (top.swch.bfct.have == 0) { + // first time + // apply ramp down + int i, m = top.swch.fade, n = top.swch.tail; + for (i = 0; i < m; i++) { + float w = (float) 1.0 - (float) i / m; + top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w; + } + memset((char *) (top.hold.buf.l + m), 0, n); + memset((char *) (top.hold.buf.r + m), 0, n); + top.swch.bfct.have++; + } else if (top.swch.bfct.have < top.swch.bfct.want) { + // in medias res + memset((char *) top.hold.buf.l, 0, top.hold.size.bytes); + memset((char *) top.hold.buf.r, 0, top.hold.size.bytes); + top.swch.bfct.have++; + } else { + // last time + // apply ramp up + int i, m = top.swch.fade, n = top.swch.tail; + for (i = 0; i < m; i++) { + float w = (float) i / m; + top.hold.buf.l[i] *= w, top.hold.buf.r[i] *= w; + } + uni.mode.trx = top.swch.trx.next; + switch (uni.mode.trx) { + int i; + case TX: + tx.agc.gen->over = tx.tick + 3; + break; + case RX: + for(i=0;iover = rx[i].tick + 3; + break; + } + + top.state = top.swch.run.last; + top.swch.bfct.want = top.swch.bfct.have = 0; + + ringb_reset(top.jack.ring.o.l); + ringb_reset(top.jack.ring.o.r); + ringb_clear(top.jack.ring.o.l,top.hold.size.bytes); + ringb_clear(top.jack.ring.o.r,top.hold.size.bytes); + + reset_meters(); + reset_spectrum(); + reset_counters(); + } + + process_samples(top.hold.buf.l, top.hold.buf.r, + top.hold.aux.l, top.hold.aux.r, + top.hold.size.frames); +} + +//======================================================================== + + + +DttSP_EXP void +audio_callback(float *input_l, float *input_r, float *output_l, float *output_r,int nframes) { + size_t nbytes = sizeof(float)*nframes; + + + EnterCriticalSection(cs); + + + if (ringb_read_space(top.jack.ring.o.l) >= nbytes) { + ringb_read(top.jack.ring.o.l, (char *) output_l, nbytes); + ringb_read(top.jack.ring.o.r, (char *) output_r, nbytes); + } else { // rb pathology + memset((char *) output_l, 0, nbytes); + memset((char *) output_r, 0, nbytes); + ringb_restart(top.jack.ring.o.l, nbytes); + ringb_restart(top.jack.ring.o.r, nbytes); + top.jack.blow.rb.o++; + } + + // input: copy from port to ring + if (ringb_write_space(top.jack.ring.i.l) >= nbytes) { + ringb_write(top.jack.ring.i.l, (char *) input_l, nbytes); + ringb_write(top.jack.ring.i.r, (char *) input_r, nbytes); + ringb_write(top.jack.auxr.i.l, (char *) input_l, nbytes); + ringb_write(top.jack.auxr.i.r, (char *) input_r, nbytes); + } else { // rb pathology + ringb_restart(top.jack.ring.i.l, nbytes); + ringb_restart(top.jack.ring.i.r, nbytes); + ringb_restart(top.jack.auxr.i.l, nbytes); + ringb_restart(top.jack.auxr.i.r, nbytes); + top.jack.blow.rb.i++; + } + LeaveCriticalSection(cs); + // if enough accumulated in ring, fire dsp + if (ringb_read_space(top.jack.ring.i.l) >= top.hold.size.bytes) + sem_post(&top.sync.buf.sem); + + // check for blowups + if ((top.jack.blow.cb > 0) || + (top.jack.blow.rb.i > 0) || + (top.jack.blow.rb.o > 0)) + sem_post(&top.sync.mon.sem); +} + +//======================================================================== + +DttSP_EXP void +process_samples_thread(void) { + while (top.running) { + sem_wait(&top.sync.buf.sem); + do { + gethold(); + sem_wait(&top.sync.upd.sem); + switch (top.state) { + case RUN_MUTE: run_mute(); break; + case RUN_PASS: run_pass(); break; + case RUN_PLAY: run_play(); break; + case RUN_SWCH: run_swch(); break; + } + sem_post(&top.sync.upd.sem); + } while (canhold()); + } +} + + +void +closeup(void) { + top.running = FALSE; + Sleep(50); + safefree((char *)top.jack.ring.o.r); + safefree((char *)top.jack.ring.o.l); + safefree((char *)top.jack.ring.i.r); + safefree((char *)top.jack.ring.i.l); + safefree((char *)top.jack.auxr.i.l); + safefree((char *)top.jack.auxr.i.r); + safefree((char *)top.jack.auxr.o.l); + safefree((char *)top.jack.auxr.o.r); + + CloseHandle(top.parm.fp); + DisconnectNamedPipe(top.parm.fd); + CloseHandle(top.parm.fd); + + + if (uni.meter.flag) { + CloseHandle(top.meas.mtr.fp); + DisconnectNamedPipe(top.meas.mtr.fd); + CloseHandle(top.meas.mtr.fd); + }; + + if (uni.spec.flag) { + CloseHandle(top.meas.spec.fp); + DisconnectNamedPipe(top.meas.spec.fd); + CloseHandle(top.meas.spec.fd); + }; + destroy_workspace(); +} + +//........................................................................ + +PRIVATE void +setup_switching(void) { + top.swch.fade = (int) (0.1 * uni.buflen + 0.5); + top.swch.tail = (top.hold.size.frames - top.swch.fade) * sizeof(float); +} + +PRIVATE void +setup_local_audio(void) { + top.hold.size.frames = uni.buflen; + top.hold.size.bytes = top.hold.size.frames * sizeof(float); + top.hold.buf.l = (float *) safealloc(top.hold.size.frames, sizeof(float), + "main hold buffer left"); + top.hold.buf.r = (float *) safealloc(top.hold.size.frames, sizeof(float), + "main hold buffer right"); + top.hold.aux.l = (float *) safealloc(top.hold.size.frames, sizeof(float), + "aux hold buffer left"); + top.hold.aux.r = (float *) safealloc(top.hold.size.frames, sizeof(float), + "aux hold buffer right"); +} + +#include + +PRIVATE void +DisplayErrorText(DWORD dwLastError) +{ + HMODULE hModule = NULL; // default to system source + LPSTR MessageBuffer; + DWORD dwBufferLength; + + DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM ; + + // + // If dwLastError is in the network range, + // load the message source. + // + + if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) { + hModule = LoadLibraryEx( + TEXT("netmsg.dll"), + NULL, + LOAD_LIBRARY_AS_DATAFILE + ); + + if(hModule != NULL) + dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE; + } + + // + // Call FormatMessage() to allow for message + // text to be acquired from the system + // or from the supplied module handle. + // + + if(dwBufferLength = FormatMessageA( + dwFormatFlags, + hModule, // module to get message from (NULL == system) + dwLastError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language + (LPSTR) &MessageBuffer, + 0, + NULL + )) + { + DWORD dwBytesWritten; + + // + // Output message string on stderr. + // + WriteFile( + GetStdHandle(STD_ERROR_HANDLE), + MessageBuffer, + dwBufferLength, + &dwBytesWritten, + NULL + ); + + // + // Free the buffer allocated by the system. + // + LocalFree(MessageBuffer); + } + + // + // If we loaded a message source, unload it. + // + if(hModule != NULL) + FreeLibrary(hModule); +} + + + +PRIVATE sem_t setup_update_sem; + +PRIVATE void setup_update_server() +{ + + if (INVALID_HANDLE_VALUE == (top.parm.fd = CreateNamedPipe(top.parm.path, + PIPE_ACCESS_INBOUND, + PIPE_WAIT|PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE, + PIPE_UNLIMITED_INSTANCES, + 512, + 512, + INFINITE, + NULL))) + { + fprintf(stderr,"Update server pipe setup failed:\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } +// fprintf(stderr,"Update NamedPipe made\n"),fflush(stderr); + sem_post(&setup_update_sem); + if (ConnectNamedPipe(top.parm.fd,NULL)) + { +// fprintf(stderr,"Connected the server to the Update pipe\n"),fflush(stderr); + } else { + fprintf(stderr,"Connected the server to the Update pipe failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } + pthread_exit(0); +} + + +PRIVATE void setup_update_client() +{ +// fprintf(stderr,"Looking for the Update server\n"),fflush(stderr); + WaitNamedPipe(top.parm.path,INFINITE); +// fprintf(stderr,"Found the Update server\n"),fflush(stderr); + if (INVALID_HANDLE_VALUE == (top.parm.fp = CreateFile(top.parm.path, + GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))) + { + fprintf(stderr,"The Update Client Open Failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } + sem_post(&setup_update_sem); +/* { + DWORD numwritten; + WriteFile(top.parm.fp,"test",5,&numwritten,NULL); + fprintf(stderr,"Number written to server: %lu\n",numwritten),fflush(stderr); + }*/ + pthread_exit(0); +} + +PRIVATE void setup_meter_server() +{ + top.meas.mtr.fd = CreateNamedPipe(top.meas.mtr.path, + PIPE_ACCESS_OUTBOUND, + PIPE_WAIT|PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE, + PIPE_UNLIMITED_INSTANCES, + 512, + 512, + INFINITE, + NULL); +// fprintf(stderr,"meter handle = %08X\n",(DWORD)top.meas.mtr.fd),fflush(stderr); + if (top.meas.mtr.fd == INVALID_HANDLE_VALUE) + { + fprintf(stderr,"Meter server pipe setup failed:\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } else { +// fprintf(stderr,"Meter Pipe Connect succeeded\n"),fflush(stderr); + sem_post(&setup_update_sem); + if (ConnectNamedPipe(top.meas.mtr.fd,NULL)) { +// fprintf(stderr,"Connected the Meter Pooch\n"),fflush(stderr); + } else { + fprintf(stderr,"Meter Pipe Connect failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } + } + pthread_exit(0); +} + +PRIVATE void setup_meter_client() +{ +// fprintf(stderr,"Looking for the meter server\n"),fflush(stderr); + if (WaitNamedPipe(top.meas.mtr.path,INFINITE)) { +// fprintf(stderr,"Found the Meter server\n"),fflush(stderr); + if (INVALID_HANDLE_VALUE == (top.meas.mtr.fp = CreateFile(top.meas.mtr.path, + GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))) + { + fprintf(stderr,"The Meter Client Open Failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } else { +// fprintf(stderr,"The Meter Client Open Succeeded\n"),fflush(stderr); + } + } else { + fprintf(stderr,"Wait for meter pipe failed: Error message %d\n",GetLastError()),fflush(stderr); + } + sem_post(&setup_update_sem); + pthread_exit(0); +} + +PRIVATE void setup_spec_server() +{ + + if (INVALID_HANDLE_VALUE == (top.meas.spec.fd = CreateNamedPipe(top.meas.spec.path, + PIPE_ACCESS_OUTBOUND, + PIPE_WAIT|PIPE_TYPE_MESSAGE|PIPE_READMODE_MESSAGE, + PIPE_UNLIMITED_INSTANCES, + 32768, + 32768, + INFINITE, + NULL))) + { + fprintf(stderr,"Spectrum pipe create failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } else { +// fprintf(stderr,"Spectrum Pipe %s Create succeeded\n",top.meas.spec.path),fflush(stderr); + sem_post(&setup_update_sem); + if (ConnectNamedPipe(top.meas.spec.fd,NULL)) + { +// fprintf(stderr,"Connected to the Spectrum Pipe\n"),fflush(stderr); + } else { + fprintf(stderr,"Spectrum pipe connect failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } + } + pthread_exit(0); +} + +PRIVATE void setup_spec_client() +{ +// fprintf(stderr,"Looking for the spectrum server\n"),fflush(stderr); + if (WaitNamedPipe(top.meas.spec.path,INFINITE)) { +// fprintf(stderr,"Found the server\n"),fflush(stderr); + if (INVALID_HANDLE_VALUE == (top.meas.spec.fp = CreateFile(top.meas.spec.path, + GENERIC_READ, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))) + { + fprintf(stderr,"The Spectrum Client Open Failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } else { +// fprintf(stderr,"The Spectrum Client Open Succeeded\n"); +// fprintf(stderr,"Spec Read handle = %08X\n",(DWORD)top.meas.spec.fp),fflush(stderr); + } + } else { + fprintf(stderr,"Wait for spec pipe failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } + sem_post(&setup_update_sem); + pthread_exit(0); +} +PRIVATE pthread_t id1,id2,id3,id4,id5,id6; +PRIVATE void +setup_updates(void) { + + char mesg[16384]="TEST TEST METER\n"; +// DWORD NumBytes; + top.parm.path = loc.path.parm; + sem_init(&setup_update_sem, 0, 0); + + + if (uni.meter.flag) { + top.meas.mtr.path = loc.path.meter; + } + if (uni.spec.flag) { + top.meas.spec.path = loc.path.spec; + } + + + // Do this STUPID stuff to make use of the Named Pipe Mechanism in Windows + // For the update server + + + pthread_create(&id1, NULL, (void *) setup_update_server, NULL); + sem_wait(&setup_update_sem); + pthread_create(&id2, NULL, (void *) setup_update_client, NULL); + sem_wait(&setup_update_sem); + if (uni.meter.flag) { + pthread_create(&id3, NULL, (void *) setup_meter_server, NULL); + sem_wait(&setup_update_sem); + pthread_create(&id4, NULL, (void *) setup_meter_client, NULL); + sem_wait(&setup_update_sem); +/* if (WriteFile(top.meas.mtr.fd,mesg,strlen(mesg)+1,&NumBytes,NULL)) + { + fprintf(stderr,"Meter Pipe write succeeded and wrote %lu bytes\n",NumBytes),fflush(stderr); + } else { + fprintf(stderr,"Meter Pipe write failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } + if (ReadFile(top.meas.mtr.fp,mesg,256,&NumBytes,NULL)) + { + fprintf(stderr,"Meter Pipe read succeeded and %lu bytes read\n",NumBytes),fflush(stderr); + fprintf(stderr,"Meter message %s",mesg),fflush(stderr); + } else { + fprintf(stderr,"Meter Pipe read failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + }*/ + + } + + if (uni.spec.flag) { + memset(mesg,0,16384); + pthread_create(&id5, NULL, (void *) setup_spec_server, NULL); + sem_wait(&setup_update_sem); + pthread_create(&id6, NULL, (void *) setup_spec_client, NULL); + sem_wait(&setup_update_sem); + Sleep(0); +/* if (WriteFile(top.meas.spec.fd,mesg,16384,&NumBytes,NULL)) + { + fprintf(stderr,"Spec Pipe write succeeded and wrote %lu bytes\n",NumBytes),fflush(stderr); + } else { + fprintf(stderr,"Spec Pipe write failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } + fprintf(stderr,"Spec Read handle(2) = %08X\n",(DWORD)top.meas.spec.fp),fflush(stderr); + if (ReadFile(top.meas.spec.fp,mesg,16384,&NumBytes,NULL)) + { + fprintf(stderr,"Spec Pipe read succeeded and %lu bytes read\n",NumBytes),fflush(stderr); + } else { + fprintf(stderr,"Spec Pipe read failed\n"),fflush(stderr); + DisplayErrorText(GetLastError()); + } */ + } + sem_destroy(&setup_update_sem); +} +PRIVATE void +setup_system_audio(void) { + size_t ringsize; + void *usemem; + sprintf(top.jack.name, "sdr-%d", top.pid); + top.jack.size = uni.buflen; + ringsize = top.hold.size.bytes * loc.mult.ring+sizeof(ringb_t); + usemem = safealloc(ringsize,1,"Ring Input Left"); + top.jack.ring.i.l = ringb_create(usemem,top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize,1,"Ring Input Right"); + top.jack.ring.i.r = ringb_create(usemem,top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize,1,"Ring Output Left"); + top.jack.ring.o.l = ringb_create(usemem,top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize,1,"Ring Output Right"); + top.jack.ring.o.r = ringb_create(usemem,top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize,1,"Ring Input Left Auxiliary"); + top.jack.auxr.i.l = ringb_create(usemem,top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize,1,"Ring Input Right Auxiliary"); + top.jack.auxr.i.r = ringb_create(usemem,top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize,1,"Ring Output Left Auxiliary"); + top.jack.auxr.o.l = ringb_create(usemem,top.hold.size.bytes * loc.mult.ring); + + usemem = safealloc(ringsize,1,"Ring Output Right Auxiliary"); + top.jack.auxr.o.r = ringb_create(usemem,top.hold.size.bytes * loc.mult.ring); + + ringb_clear(top.jack.ring.o.l, top.jack.size * sizeof(float)); + ringb_clear(top.jack.ring.o.r, top.jack.size * sizeof(float)); + ringb_clear(top.jack.auxr.o.l, top.jack.size * sizeof(float)); + ringb_clear(top.jack.auxr.o.r, top.jack.size * sizeof(float)); +} + +PRIVATE void +setup_threading(void) { + sem_init(&top.sync.upd.sem, 0, 0); + pthread_create(&top.thrd.upd.id, NULL, (void *) process_updates_thread, NULL); + sem_init(&top.sync.buf.sem, 0, 0); + pthread_create(&top.thrd.trx.id, NULL, (void *) process_samples_thread, NULL); + sem_init(&top.sync.mon.sem, 0, 0); + pthread_create(&top.thrd.mon.id, NULL, (void *) monitor_thread, NULL); + if (uni.meter.flag) { + sem_init(&top.sync.mtr.sem, 0, 0); + pthread_create(&top.thrd.mtr.id, NULL, (void *) meter_thread, NULL); + } + if (uni.spec.flag) { + sem_init(&top.sync.pws.sem, 0, 0); + pthread_create(&top.thrd.pws.id, NULL, (void *) spectrum_thread, NULL); + } + cs = &csobj; + InitializeCriticalSection(cs); +} + +//======================================================================== +// hard defaults, then environment + +PRIVATE void +setup_defaults(void) { + loc.name[0] = 0; // no default name for jack client + strcpy(loc.path.rcfile, RCBASE); + strcpy(loc.path.parm, PARMPATH); + strcpy(loc.path.meter, METERPATH); + strcpy(loc.path.spec, SPECPATH); + strcpy(loc.path.wisdom, WISDOMPATH); + loc.def.rate = DEFRATE; + loc.def.size = DEFSIZE; + loc.def.nrx = MAXRX; + loc.def.mode = DEFMODE; + loc.def.spec = DEFSPEC; + loc.mult.ring = RINGMULT; +} + +//======================================================================== +void +setup() { + + + top.pid = GetCurrentThreadId(); + top.uid = 0L; + top.start_tv = now_tv(); + top.running = TRUE; + top.verbose = FALSE; + top.state = RUN_PLAY; + + setup_defaults(); + top.verbose = FALSE; + uni.meter.flag = TRUE; + uni.spec.flag = TRUE; + + setup_workspace(); + setup_updates(); + + setup_local_audio(); + setup_system_audio(); + + setup_threading(); + setup_switching(); + uni.spec.flag = TRUE; + uni.spec.type = SPEC_POST_FILT; + uni.spec.scale = SPEC_PWR; + uni.spec.rxk = 0; + +} +