ChucK Code Blog
Perry Cook
May 6, 2016

Yes, May is the Month of Code!
And not to let any entendre go singular,
I give you for Week 1:

"Morse!!" (code, examples, readme)

Code only:

// Morse code!  by Perry R. Cook
//    originally in C++, about 2004
// Ported to ChucK, May 2016 (for Month of Code)

//  Run this in terminal and type.  Morse code is
// automagicically generated.  You can also try it in
// miniAudicle, but caution that you don't actually
// type into this program source file.
//
// In terminal, you can also pipe a text file into it:
//
//    >   cat Cheney.txt | chuck Morse.ck

// Morse code patterns to match characters in MorseChars array
[".-","-...","-.-.","-..",".","..-.","--.","....","..",
 ".---","-.-",".-..","--","-.","---",".--.","--.-",".-.",
 "...","-","..-","...-",".--","-..-","-.--","--..",
 "-----",".----","..---","...--","....-",
 ".....","-....","--...","---..","----.",
 ".-.-.-","--..--","..--..","-....-","---...",".----.",
 "-.--.","-.--.-","-...-",".--.-.",".-.-.","-..-."] @=> string MorseCode[];

// Characters in standard Morse Code Alphabet
["A" ,"B"   ,"C"   ,"D"  ,"E","F"   ,"G"  ,"H"   ,"I" ,
 "J"   , "K" ,"L"   ,"M" ,"N" ,"O"  ,"P"   ,"Q"   ,"R",
 "S"  ,"T","U"  ,"V"   ,"W"  ,"X"   ,"Y"   ,"Z"   ,
 "0"    ,"1"    ,"2"    ,"3"    ,"4",
 "5"    ,"6"    ,"7"    ,"8"    ,"9"    ,
 "."    ,","    ,"?",   "-", ":", "'", 
 "(", ")", "=", "@", "+", "/"] @=> string MorseChars[];

// key scramble between published morse table and ascii codes
[46, 44, 63, 45, 58, 39, 40, 41, 61, 64, 43, 47] @=> int MorseRest[];

SinOsc s => dac;                // our synthesizer beeper
880 => int BASE_FREQ => s.freq; // frequency of oscillator
0 => s.gain;                    // ghetto envelope

0.04*second => dur DOTLENGTH;   // fundamental (smallest) time period
3*DOTLENGTH => dur DASHLENGTH;  // dash is 3x dot
DOTLENGTH => dur DOTSILENCE;    // silence between symbols within char
DASHLENGTH => dur CHARSILENCE;  // 3dot silence between characters
7*DOTLENGTH => dur WORDSILENCE; // 7dot silence between words

KBHit kb;

0 => int foundOne; // global boolean if we found (or not) it
1 => int notDone;

int inKey;  // input character (global)

while (notDone)  {   // loop forever and ever and ever...
    
    kb => now;   // wait until key is pressed
    
    while (kb.more() & notDone)  { // empty key buffer
        0 => foundOne; // to begin our search
        kb.getchar() => inKey; // get next keyboard input character
        if (inKey == 27 || inKey == 94)  {  // excape or ^ key ends it all
            1 => foundOne;
            0 => notDone;
        }
        else if (inKey == 10 | inKey == 13) {   // carriage return or line feed
            1 => foundOne; //  not really, but we want to end it here
            <<< " <>", "" >>>;
            2*WORDSILENCE => now;
        }
        else if (inKey == 32) {          // space or space bar
            1 => foundOne; //  not really, but we want to end it here
            <<< " <>", "" >>>;
            WORDSILENCE => now;
        }
        else if (inKey > 64 & inKey < 91) gotOne(inKey, 65);       // upper case letters
        else if (inKey > 96 & inKey < 123) gotOne(inKey, 97); // lower case letters
        else if (inKey > 47 & inKey < 58) gotOne(inKey, 48-26); // numerals
        else { // clean up the rest of the unordered characters
            for (int i; i < 12; i++)  {
                if (inKey == MorseRest[i]) {
                    1 => foundOne;
                    gotOne(i,-36);
                    //              <<< inKey, MorseChars[i+36], MorseCode[i+36] >>>;
                }
            }
        }
        if (!foundOne)  {
            <<< "I don't know what this is:", inKey >>>;
        }
    }
}

<<< "Thanx for Morse-ing!!", "Please drive through..." >>>;

fun void gotOne(int key, int offset)  {  // what to do when we found one
    1 => foundOne;
    <<< inKey, MorseChars[key-offset], MorseCode[key-offset] >>>;
    synth(MorseCode[key-offset]);
}

fun void synth(string pattern) { // synthesize based on dots/dashes
    for (int i; i < pattern.length(); i++)  {
        if (pattern.substring(i,1) == "-") dash();
        else dot();
    }
    CHARSILENCE => now; // space between characters
}

fun void dot()  {     // play a single dot with silence after
    1 => s.gain; DOTLENGTH => now;
    0 => s.gain; DOTSILENCE => now;
}

fun void dash()  {    // play a single dash with silence after
    1 => s.gain; DASHLENGTH => now;
    0 => s.gain; DOTSILENCE => now;
}

// ASCII character values (decimal) of interest
/* 32 sp    33  !    34  "    35  #    36  $    37  %    38  &    39  '
      40  (    41  )    42  *    43  +    44  ,    45  -    46  .    47  /
      48  0    49  1    50  2    51  3    52  4    53  5    54  6    55  7
      56  8    57  9    58  :    59  ;    60  <    61  =    62  >    63  ?
      64  @    65  A    66  B    67  C    68  D    69  E    70  F    71  G
      72  H    73  I    74  J    75  K    76  L    77  M    78  N    79  O
      80  P    81  Q    82  R    83  S    84  T    85  U    86  V    87  W
      88  X    89  Y    90  Z    91  [    92  \    93  ]    94  ^    95  _
      96  `    97  a    98  b    99  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  {   124  |   125  }   126  ~   127 del
*/