]> git.rkrishnan.org Git - dttsp.git/blobdiff - jDttSP/win/keyb.c
Upgraded windows alternatives
[dttsp.git] / jDttSP / win / keyb.c
diff --git a/jDttSP/win/keyb.c b/jDttSP/win/keyb.c
new file mode 100644 (file)
index 0000000..d01707e
--- /dev/null
@@ -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 <fromsys.h>
+#include <banal.h>
+#include <splitfields.h>
+#include <datatypes.h>
+#include <bufvec.h>
+#include <cxops.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <ringb.h>
+
+#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);
+}