Morse code

From Rosetta Code
Revision as of 19:40, 31 January 2013 by rosettacode>Gerard Schildberger (→‎{{header|REXX}}: clarified explanation of what REXX doesn't issue sounds (on the internal speaker). -- ~~~~)
Task
Morse code
You are encouraged to solve this task according to the task description, using any language you may know.

Morse code is one of the simplest and most versatile methods of telecommunication in existence. It has been in use for more than 160 years — longer than any other electronic encoding system.

The task: Send a string as audible morse code to an audio device (e.g., the PC speaker).

As the standard Morse code does not contain all possible characters, you may either ignore unknown characters in the file, or indicate them somehow (e.g. with a different pitch).

Ada

Conforms to Ada95. Works for Windows 32 XP , not for Vista since Beep is no longer effective. Specification of the package : <lang Ada>package Morse is

  type Symbols is (Nul, '-', '.', ' ');
  -- Nul is the letter separator, space the word separator;
  Dash : constant Symbols := '-';
  Dot : constant Symbols := '.';
  type Morse_Str is array (Positive range <>) of Symbols;
  pragma Pack (Morse_Str);
  function Convert (Input : String) return Morse_Str;
  procedure Morsebeep (Input : Morse_Str);

private

  subtype Reschars is Character range ' ' .. 'Z';
  -- restricted set of characters from 16#20# to 16#60#
  subtype Length is Natural range 1 .. 5;
  subtype Codes is Morse_Str (Length);
  -- using the current ITU standard with 5 signs
  -- only alphanumeric characters  are taken into consideration
  type Codings is record
     L : Length;
     Code : Codes;
  end record;
  Table : constant array (Reschars) of Codings :=
    ('A' => (2, ".-   "), 'B' => (4, "-... "),  'C' => (4, "-.-. "),
     'D' => (3, "-..  "), 'E' => (1, ".    "),  'F' => (4, "..-. "),
     'G' => (3, "--.  "), 'H' => (4, ".... "),  'I' => (2, "..   "),
     'J' => (4, ".--- "), 'K' => (3, "-.-  "),  'L' => (4, ".-.. "),
     'M' => (2, "--   "), 'N' => (2, "-.   "),  'O' => (3, "---  "),
     'P' => (4, "--.- "), 'Q' => (4, "--.- "),  'R' => (3, ".-.  "),
     'S' => (3, "...  "), 'T' => (1, "-    "),  'U' => (3, "..-  "),
     'V' => (4, "...- "), 'W' => (3, ".--  "),  'X' => (4, "-..- "),
     'Y' => (4, "-.-- "), 'Z' => (4, "--.. "),  '1' => (5, ".----"),
     '2' => (5, "..---"), '3' => (5, "...--"),  '4' => (5, "....-"),
     '5' => (5, "....."), '6' => (5, "-...."),  '7' => (5, "--..."),
     '8' => (5, "---.."), '9' => (5, "----."),  '0' => (5, "-----"),
     others => (1, "     ")); -- Dummy => Other characters do not need code.

end Morse;</lang> <lang Ada>with Ada.Strings.Maps, Ada.Characters.Handling, Interfaces.C; use Ada, Ada.Strings, Ada.Strings.Maps, Interfaces;

package body Morse is

  Dit, Dah, Lettergap, Wordgap : Duration; -- in seconds
  Dit_ms, Dah_ms : C.unsigned; -- durations expressed in ms
  Freq : constant C.unsigned := 1280; -- in Herz
  Morse_Sequence : constant Character_Sequence :=
     " ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  Morse_charset : constant Character_Set := To_Set (Morse_Sequence);
  function Convert (Input : String) return Morse_Str is
     Cap_String : constant String := Characters.Handling.To_Upper (Input);
     Result : Morse_Str (1 .. 7 * Input'Length); -- Upper Capacity
     First, Last : Natural := 0;
     Char_code : Codings;
  begin
     for I in Cap_String'Range loop
        if Is_In (Cap_String (I), Morse_charset) then
           First := Last + 1;
           if Cap_String (I) = ' ' then
              Result (First) := ' ';
              Last := Last + 1;
           else
              Char_code := Table (Reschars (Cap_String (I)));
              Last := First + Char_code.L - 1;
              Result (First .. Last) := Char_code.Code (1 .. Char_code.L);
              Last := Last + 1;
              Result (Last) := Nul;
           end if;
        end if;
     end loop;
     if Result (Last) /= ' ' then
        Last := Last + 1;
        Result (Last) := ' ';
     end if;
     return Result (1 .. Last);
  end Convert;
  procedure Morsebeep (Input : Morse_Str) is
     -- Beep is not portable : adapt to your OS/sound board
     -- Implementation for Windows XP / interface to fn in stdlib
     procedure win32xp_beep
       (dwFrequency : C.unsigned;
        dwDuration : C.unsigned);
     pragma Import (C, win32xp_beep, "_beep");
  begin
     for I in Input'Range loop
        case Input (I) is
           when Nul =>
              delay Lettergap;
           when Dot =>
              win32xp_beep (Freq, Dit_ms);
              delay Dit;
           when Dash =>
              win32xp_beep (Freq, Dah_ms);
              delay Dit;
           when ' ' =>
              delay Wordgap;
        end case;
     end loop;
  end Morsebeep;

begin

  Dit := 0.20;
  Lettergap := 2 * Dit;
  Dah := 3 * Dit;
  Wordgap := 4 * Dit;
  Dit_ms := C.unsigned (Integer (Dit * 1000));
  Dah_ms := C.unsigned (Integer (Dah * 1000));

end Morse;</lang> Main program : <lang Ada>with Morse; use Morse; procedure Morse_Tx is begin

  Morsebeep (Convert ("Science sans Conscience"));

end Morse_Tx;</lang>

AutoHotkey

This function converts acceptable characters into dot-dash notation then uses SoundBeep to play them. The frequency and element length are stored near the bottom of the script. <lang AutoHotkey> TestString := "Hello World! abcdefg @\;" ; Create a string to be sent with multiple caps and some punctuation MorseBeep(teststring)  ; Beeps our string after conversion return  ; End Auto-Execute Section


MorseBeep(passedString) { StringLower, passedString, passedString  ; Convert to lowercase for simpler checking loop, parse, passedString  ; This loop stores each character in A_loopField one by one { If (A_LoopField = " ") morse .= " "  ; Add a long delay between words (5*e) If (A_LoopField = "a") morse .=".- "  ; Morse is a local variable If (A_LoopField = "b") morse .="-... "  ; .= is the simple way of appending to a string If (A_LoopField = "c") morse .="-.-. "  ; we add a space after every character to pause for e If (A_LoopField = "d") morse .="-.. " If (A_LoopField = "e") morse .=". " If (A_LoopField = "f") morse .="..-. " If (A_LoopField = "g") morse .="--. " If (A_LoopField = "h") morse .=".... " If (A_LoopField = "i") morse .=".. " If (A_LoopField = "j") morse .=".--- " If (A_LoopField = "k") morse .="-.- " If (A_LoopField = "l") morse .=".-.. " If (A_LoopField = "m") morse .="-- " If (A_LoopField = "n") morse .="-. " If (A_LoopField = "o") morse .="--- " If (A_LoopField = "p") morse .=".--. " If (A_LoopField = "q") morse .="--.- " If (A_LoopField = "r") morse .=".-. " If (A_LoopField = "s") morse .="... " If (A_LoopField = "t") morse .="- " If (A_LoopField = "u") morse .="..- " If (A_LoopField = "v") morse .="...- " If (A_LoopField = "w") morse .=".-- " If (A_LoopField = "x") morse .="-..- " If (A_LoopField = "y") morse .="-.-- " If (A_LoopField = "z") morse .="--.. " If (A_LoopField = "!") morse .="---. " If (A_LoopField = "\") morse .=".-..-. " If (A_LoopField = "$") morse .="...-..- " If (A_LoopField = "'") morse .=".----. " If (A_LoopField = "(") morse .="-.--. " If (A_LoopField = ")") morse .="-.--.- " If (A_LoopField = "+") morse .=".-.-. " If (A_LoopField = ",") morse .="--..-- " If (A_LoopField = "-") morse .="-....- " If (A_LoopField = ".") morse .=".-.-.- " If (A_LoopField = "/") morse .="-..-. " If (A_LoopField = "0") morse .="----- " If (A_LoopField = "1") morse .=".---- " If (A_LoopField = "2") morse .="..--- " If (A_LoopField = "3") morse .="...-- " If (A_LoopField = "4") morse .="....- " If (A_LoopField = "5") morse .="..... " If (A_LoopField = "6") morse .="-.... " If (A_LoopField = "7") morse .="--... " If (A_LoopField = "8") morse .="---.. " If (A_LoopField = "9") morse .="----. " If (A_LoopField = ":") morse .="---... " If (A_LoopField = ";") morse .="-.-.-. " If (A_LoopField = "=") morse .="-...- " If (A_LoopField = "?") morse .="..--.. " If (A_LoopField = "@") morse .=".--.-. " If (A_LoopField = "[") morse .="-.--. " If (A_LoopField = "]") morse .="-.--.- " If (A_LoopField = "_") morse .="..--.- " }  ; ---End conversion loop---

Freq=1280  ; Frequency between 37 and 32767 e=120  ; element time in milliseconds  ; . is one e, - is 3, and a space is a pause of one e loop, parse, morse { if (A_LoopField = ".") SoundBeep, Freq, e ;Format: SoundBeep, frequency, duration

If (A_LoopField = "-") SoundBeep, Freq, 3*e  ; duration can be an expression

If (A_LoopField = " ") Sleep, e  ; Above, each character is followed by a space, and literal }  ; spaces are extended. Sleep pauses the script. } ; ---End Function Morse--- </lang>


AWK

<lang AWK> BEGIN { FS="";

m="A.-B-...C-.-.D-..E.F..-.G--.H....I..J.---K-.-L.-..M--N-.";
m=m "O---P.--.Q--.-R.-.S...T-U..-V...-W.--X-..-Y-.--Z--..  ";

}

{ for(i=1; i<=NF; i++)

 {
   c=toupper($i); n=1; b=".";
                                                                              
   while((c!=b)&&(b!=" ")) { b=substr(m,n,1); n++; }
                                                                               
   b=substr(m,n,1);
                                                                               
   while((b==".")||(b=="-")) { printf("%s",b); n++; b=substr(m,n,1); }

   printf("|");
 }
 printf("\n");

}

usage: awk -f morse.awk [inputfile]

sos sos titanic ...|---|...||...|---|...||-|..|-|.-|-.|..|-.-.| </lang>

BASIC

Works with: QBasic

This replaces all non-supported characters with a hash (#) and plays the lowest supported note in their place.

Note that this will only work as-is under DOS (and Windows 9x); under NT systems, the player routine must be changed to use the Beep API call. (This forum post details how to use the speaker under Linux, DOS, and Windows in FreeBASIC; the Linux & DOS code differs further from the below by require inline assembly.)

<lang qbasic>DECLARE SUB player (what AS STRING)

'this determines the length of the notes 'lower number = longer duration CONST noteLen = 16

DIM tones(62) AS STRING

FOR n% = 0 TO 62

   READ tones(n%)

NEXT n%

'set up the playing system PLAY "t255o4l" + LTRIM$(STR$(noteLen))

LINE INPUT "String to convert to Morse code: "; x$

FOR n% = 1 TO LEN(x$)

   c$ = UCASE$(MID$(x$, n%, 1))
   PLAY "p" + LTRIM$(STR$(noteLen / 2)) + "."
   SELECT CASE UCASE$(c$)
       CASE " "
           'since each char is effectively wrapped with 6 p's, we only need to add 1:
           PLAY "p" + LTRIM$(STR$(noteLen))
           PRINT "  ";
       CASE "!" TO "_"
           PRINT tones(ASC(c$) - 33); " ";
           player tones(ASC(c$) - 33)
       CASE ELSE
           PRINT "# ";
           player "#"
   END SELECT
   PLAY "p" + LTRIM$(STR$(noteLen / 2)) + "."

NEXT n% PRINT

'all the Morse codes in ASCII order, from "!" to "_" DATA "-.-.--", ".-..-.", "#", "...-..-", "#", ".-...", ".----.", "-.--." DATA "-.--.-", "#", ".-.-.", "--..--", "-....-", ".-.-.-", "-..-.", "-----" DATA ".----", "..---", "...--", "....-", ".....", "-....", "--...", "---.." DATA "----.", "---...", "-.-.-.", "#", "-...-", "#", "..--..", ".--.-.", ".-" DATA "-...", "-.-.", "-..", ".", "..-.", "--.", "....", "..", ".---", "-.-" DATA ".-..", "--", "-.", "---", ".--.", "--.-", ".-.", "...", "-", "..-" DATA "...-", ".--", "-..-", "-.--", "--..", "#", "#", "#", "#", "..--.-"

SUB player (what AS STRING)

   FOR i% = 1 TO LEN(what)
       z$ = MID$(what, i%, 1)
       SELECT CASE z$
           CASE "."
               o$ = "g"
           CASE "-"
               o$ = "g" + LTRIM$(STR$(noteLen / 2)) + "."
           CASE ELSE
               o$ = "<<<<c>>>>"
       END SELECT
       PLAY o$
       PLAY "p" + LTRIM$(STR$(noteLen))
   NEXT i%

END SUB</lang>

Sample outputs (2 runs):

String to convert to Morse code: Rosetta Code
.-. --- ... . - - .-   -.-. --- -.. .
String to convert to Morse code: ~!@#$%^&*()_+
# -.-.-- .--.-. # ...-..- # # .-... # -.--. -.--.- ..--.- .-.-.

BBC BASIC

<lang bbcbasic> *TEMPO 8

     DIM morse$(63)
     FOR char% = 0 TO 63 : READ morse$(char%) : NEXT char%
     
     PROCmorse("The five boxing wizards jump quickly.")
     END
     
     DEF PROCmorse(text$)
     LOCAL element%, index%, char&, morse$
     FOR index% = 1 TO LEN(text$)
       char& = ASC(MID$(text$,index%)) AND &7F
       IF char& < 32 char& = 32
       IF char& > 95 char& -= 32
       morse$ = morse$(char&-32)
       FOR element% = 1 TO LEN(morse$)
         SOUND 1, -15, 148, VAL(MID$(morse$,element%,1))
         SOUND 1, -15, 0, 1
       NEXT element%
       SOUND 1, -15, 0, 2
     NEXT index%
     ENDPROC
     
     DATA 00,313133,131131,6,1113113,6,13111,133331,31331,313313,6,13131,331133,311113,131313,31131
     DATA 33333,13333,11333,11133,11113,11111,31111,33111,33311,33331,333111,313131,6,31113,6,113311
     DATA 133131,13,3111,3131,311,1,1131,331,1111,11,1333,313,1311,33,31,333
     DATA 1331,3313,131,111,3,113,1113,133,3113,3133,3311,6,6,6,6,113313</lang>

C

One could substitute another program for ubuntu beep command.

<lang C> /*

 David Lambert, 2010-Dec-09
 filter producing morse beep commands.
 build:
   make morse
 use:
   $ echo tie a. | ./morse
   beep -n -f 440 -l 300 -D 100 -n -D 200 -n -f 440 -l 100 -D 100 -n -f 440 -l 100 -D 100 -n -D 200 -n -f 440 -l 100 -D 100 -n -D 200 -n -D 400 -n -f 440 -l 100 -D 100 -n -f 440 -l 300 -D 100 -n -D 200 -n -f 440 -l 100 -D 100 -n -f 440 -l 300 -D 100 -n -f 440 -l 100 -D 100 -n -f 440 -l 300 -D 100 -n -f 440 -l 100 -D 100 -n -f 440 -l 300 -D 100 -n -D 200 -n -D 400 -n -D 400
 bugs:
   What is the space between letter and punctuation?
   Demo truncates input lines at 71 characters or so.
*/
  1. include <ctype.h>
  2. include <stdio.h>
  3. include <stdlib.h>
  4. include <string.h>
  1. define BIND(A,L,H) ((L)<(A)?(A)<(H)?(A):(H):(L))

/*

 BIND(-1,0,9) is 0
 BIND( 7,0,9) is 7
 BIND(77,0,9) is 9
  • /

char

 /* beep args for */
 /* dit  dah     extra space */
 dih[50],dah[50],medium[30],word[30],
 *dd[2] = {dih,dah};

const char

 *ascii = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,?'!/()&:;=+-_\"$@",
 *itu[] = {
    "13","3111","3131","311","1","1131","331","1111","11","1333","313","1311","33","31","333","1331","3313","131","111","3","113","1113","133","3113","3133","3311","33333","13333","11333","11133","11113","11111","31111","33111","33311","33331","131313","331133","113311","133331","313133","31131","31331","313313","13111","333111","313131","31113","13131","311113","113313","131131","1113113","133131"
 };

void append(char*s,const char*morse) {

 for (; *morse; ++morse)
   strcat(s,dd['3'==*morse]);
 strcat(s,medium);

}

char*translate(const char*i,char*o) {

 const char*pc;
 sprintf(o,"beep");
 for (; *i; ++i)
   if (NULL == (pc = strchr(ascii,toupper(*i))))
     strcat(o,word);
   else
     append(o,itu[pc-ascii]);
 strcat(o,word);
 return o;

}

int main(int ac,char*av[]) {

 char
   sin[73],sout[100000];
 int
   dit = 100;
 if (1 < ac) {
   if (strlen(av[1]) != strspn(av[1],"0123456789"))
     return 0*fprintf(stderr,"use: %s [duration]   dit in ms, default %d\n",*av,dit);
   dit = BIND(atoi(av[1]),1,1000);
 }
 sprintf(dah," -n -f 440 -l %d -D %d",3*dit,dit);
 sprintf(dih," -n -f 440 -l %d -D %d",dit,dit);
 sprintf(medium," -n -D %d",(3-1)*dit);
 sprintf(word," -n -D %d",(7-(3-1)-1)*dit);
 while (NULL != fgets(sin,72,stdin))
   puts(translate(sin,sout));
 return 0;

} </lang>

C#

<lang CSharp>using System; using System.Collections.Generic;

namespace Morse {

   class Morse
   {
       static void Main(string[] args)
       {
           string word = "sos";
           Dictionary<string, string> Codes = new Dictionary<string, string>
           {
               {"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", "....-"}    
           };
           foreach (char c in word.ToCharArray())
           {
               string rslt = Codes[c.ToString()].Trim();
               foreach (char c2 in rslt.ToCharArray())
               {
                   if (c2 == '.')
                       Console.Beep(1000, 250);
                   else
                       Console.Beep(1000, 750);
               }
               System.Threading.Thread.Sleep(50);
           }
       }
   }

} </lang>

Clojure

<lang Clojure>(import [javax.sound.sampled AudioFormat AudioSystem SourceDataLine])

(defn play [sample-rate bs]

 (let [af (AudioFormat. sample-rate 8 1 true true)]
   (doto (AudioSystem/getSourceDataLine af)
     (.open af sample-rate)
     .start
     (.write bs 0 (count bs))
     .drain
     .close)))

(defn note [hz sample-rate ms]

 (let [period (/ hz sample-rate)]
   (->> (range (* sample-rate ms 1/1000))

(map #(->> (* 2 Math/PI % period) Math/sin (* 127 ,) byte) ,))))

(def morse-codes

    {\A ".-"   \J ".---" \S "..."   \1 ".----" \. ".-.-.-" \: "---..."
     \B "-..." \K "-.-"  \T "-"     \2 "..---" \, "--..--" \; "-.-.-."
     \C "-.-." \L ".-.." \U "..-"   \3 "...--" \? "..--.." \= "-...-"
     \D "-.."  \M "--"   \V "...-"  \4 "....-" \' ".----." \+ ".-.-."
     \E "."    \N "-."   \W ".--"   \5 "....." \! "-.-.--" \- "-....-"
     \F "..-." \O "---"  \X "-..-"  \6 "-...." \/ "-..-."  \_ "..--.-"
     \G "--."  \P ".--." \Y "-.--"  \7 "--..." \( "-.--."  \" ".-..-."  ;"
     \H "...." \Q "--.-" \Z "--.."  \8 "---.." \) "-.--.-" \$ "...-..-"
     \I ".."   \R ".-."  \0 "-----" \9 "----." \& ".-..."  \@ ".--.-."
     \space " "})

(def sample-rate 1024)

(let [hz 440

     ms 50]
 (def sounds
      {\. (note hz sample-rate (* 1 ms))

\- (note hz sample-rate(* 3 ms)) :element-gap (note 0 sample-rate (* 1 ms)) :letter-gap (note 0 sample-rate (* 3 ms)) \space (note 0 sample-rate (* 1 ms))})) ;includes letter-gap on either side

(defn convert-letter [letter]

 (->> (get morse-codes letter "")
      (map sounds ,)
      (interpose (:element-gap sounds) ,)
      (apply concat ,)))

(defn morse [s]

 (->> (.toUpperCase s)
      (map convert-letter ,)
      (interpose (:letter-gap sounds) ,)
      (apply concat ,)
      byte-array
      (play sample-rate ,)))</lang>

Haskell

This implementation requires that the "play" program of the SoX (Sound eXchange) package be installed. However, it is easy to replace the module that uses it with a different one.

The main program. <lang Haskell> import System.IO import MorseCode import MorsePlaySox

-- Read standard input, converting text to Morse code, then playing the result. -- We turn off buffering on stdin so it will play as you type. main = do

 hSetBuffering stdin NoBuffering
 text <- getContents
 play $ toMorse text

</lang>

The module to convert text to Morse code symbols. <lang Haskell> module MorseCode (Morse, MSym(..), toMorse) where

import Data.List import Data.Maybe import qualified Data.Map as M

type Morse = [MSym] data MSym = Dot | Dash | SGap | CGap | WGap deriving (Show)

-- Based on the table of International Morse Code letters and numerals at -- http://en.wikipedia.org/wiki/Morse_code. dict = M.fromList

      [('a', m ".-"   ), ('b', m "-..." ), ('c', m "-.-." ), ('d', m "-.."  ),
       ('e', m "."    ), ('f', m "..-." ), ('g', m "--."  ), ('h', m "...." ),
       ('i', m ".."   ), ('j', m ".---" ), ('k', m "-.-"  ), ('l', m ".-.." ),
       ('m', m "--"   ), ('n', m "-."   ), ('o', m "---"  ), ('p', m ".--." ),
       ('q', m "--.-" ), ('r', m ".-."  ), ('s', m "..."  ), ('t', m "-"    ),
       ('u', m "..-"  ), ('v', m "...-" ), ('w', m ".--"  ), ('x', m "-..-" ),
       ('y', m "-.--" ), ('z', m "--.." ), ('1', m ".----"), ('2', m "..---"), 
       ('3', m "...--"), ('4', m "....-"), ('5', m "....."), ('6', m "-...."), 
       ('7', m "--..."), ('8', m "---.."), ('9', m "----."), ('0', m "-----")]
   where m = intersperse SGap . map toSym
         toSym '.' = Dot
         toSym '-' = Dash

-- Convert a string to a stream of Morse symbols. We enhance the usual dots -- and dashes with special "gap" symbols, which indicate the border between -- symbols, characters and words. This allows a player to easily adjust its -- timing by simply looking at the current symbol, rather than trying to keep -- track of state. toMorse :: String -> Morse toMorse = fromWords . words . weed

   where fromWords = intercalate [WGap] . map fromWord
         fromWord  = intercalate [CGap] . map fromChar
         fromChar  = fromJust . flip M.lookup dict
         weed      = filter (\c -> c == ' ' || M.member c dict)

</lang>

The module to interpret Morse code symbols as sound. <lang Haskell> module MorsePlaySox (play) where

import Sound.Sox.Play import Sound.Sox.Option.Format import Sound.Sox.Signal.List import Data.Int import System.Exit import MorseCode

samps = 15 -- samples/cycle freq = 700 -- cycles/second (frequency) rate = samps * freq -- samples/second (sampling rate)

type Samples = [Int16]

-- One cycle of silence and a sine wave. mute, sine :: Samples mute = replicate samps 0 sine = let n = fromIntegral samps

          f k = 8000.0 * sin (2*pi*k/n)
      in map (round . f . fromIntegral) [0..samps-1]

-- Repeat samples until we have the specified duration in seconds. rep :: Float -> Samples -> Samples rep dur = take n . cycle

   where n = round (dur * fromIntegral rate)

-- Convert Morse symbols to samples. Durations are in seconds, based on -- http://en.wikipedia.org/wiki/Morse_code#Representation.2C_timing_and_speeds. toSamples :: MSym -> Samples toSamples Dot = rep 0.1 sine toSamples Dash = rep 0.3 sine toSamples SGap = rep 0.1 mute toSamples CGap = rep 0.3 mute toSamples WGap = rep 0.7 mute

-- Interpret the stream of Morse symbols as sound. play :: Morse -> IO ExitCode play = simple put none rate . concatMap toSamples </lang>

J

<lang j>require'strings media/wav' morse=:[:; [:(' ',~' .-' {~ 3&#.inv)&.> (_96{.".0 :0-.LF) {~ a.&i.@toupper

79 448 0 1121 0 0 484 214 644 0 151 692 608 455 205 242 161 134 125 122
121 202 229 238 241 715 637 0 203 0 400 475 5 67 70 22 1 43 25 40 4 53
23 49 8 7 26 52 77 16 13 2 14 41 17 68 71 76 214 0 644 0 401

)

onoffdur=: 0.01*100<.@*(1.2%[)*(4 4#:2 5 13){~' .-'i.] playmorse=: 30&$: :((wavnote&, 63 __"1)@(onoffdur morse))</lang>

Example use:

<lang> morse'this is an example' - .... .. ... .. ... .- -. . -..- .- -- .--. .-.. .

  playmorse'this is an example'

1

  12 playmorse'as is this'

1</lang>

morse converts from text to dot dash notation

playmorse converts from text to sound (and produces an inconsequential result which should always be 1).

Note that playmorse takes an optional left argument (wpm). However, the current implementation is limited as it truncates the length of a morse element to the nearest 10 milliseconds in duration, to work around a limitation in wavnote. This means generated morse code sounds jump directly from 30wpm to 40wpm.

(If wpm were a part of the task spec, it might be worthwhile doing away with wavnote and generating sound samples directly. However, currently this task has no such requirement so merely documenting this issue should be sufficient.)

The long sequence of numbers in the definition of "morse" are meant to be interpreted as base 3 numbers, where a 1 digit represents a dot and a 2 digit represents a dash and a 0 digit represents a pause. The first of these numbers corresponds to the character ! and the last of the numbers corresponds to the character _ . To convert morse text to base 3 use: 3&#.@i.~&' .-'&>.

3&#.@i.~&' .-'&>, on the international morse for printable ascii characters, one character per line, with blank lines for the missing characters, was how that big long numeric list was originally generated. To recover that list you can use morse ::(''"_)"1,.a., but note that this will include blank lines for every missing ascii character. If you instead use morse ::('*'"_)"1,.a., you will get asterisks for the ascii characters after the final representable character.

The short sequence of numbers (2 5 13) in the definition of "onoffdur" are meant to be interpreted base 4 and correspond to the three characters <<space, dot and dash>> which can appear in a morse text sequence. The first "base 4 digit" in each of these numbers represents the relative "on" duration for the sound of one of these three characters and the second represents the relative "off" duration for that character.

The sequence 63 __ in the implementation corresponds to the note D# in the fifth octave above middle C's octave, and silence. In other words, the number 63 corresponds to midi note number 123, or almost 10kHz, and the number __ (negative infinity) corresponds to 0kHz or silence. (Note: midi uses 60 for middle C, where J's wavnote uses 0, because midi note numbers can not be negative.) In other words 63 is the "on note" and __ is the "off note" for generating morse sound.

Liberty BASIC

<lang lb>'The following code relies on the Windows API Input "Input the text to translate to Morse Code... "; string$ Print PlayMorse$(string$) End

Function PlayMorse$(string$)

   'LetterGap = (3 * BaseTime)
   'WordGap = (7 * BaseTime)
   BaseTime = 50
   freq = 1250
   PlayMorse$ = TranslateToMorse$(string$)
   morseCode$ = "./-"
   For i = 1 To Len(PlayMorse$)
       Scan
       dwDuration = (Instr(morseCode$, Mid$(PlayMorse$, i, 1)) * BaseTime)
       If (Mid$(PlayMorse$, i, 1) <> " ") Then
           CallDLL #kernel32, "Beep", freq As ulong, dwDuration As ulong, ret As long
           CallDLL #kernel32, "Sleep", BaseTime As long, ret As void
       End If
       If (Mid$(PlayMorse$, i, 1) <> " ") Then
           sleep = (3 * BaseTime)
       Else
           sleep = (7 * BaseTime)
       End If
       CallDLL #kernel32, "Sleep", sleep As long, ret As void
   Next i

End Function

Function TranslateToMorse$(string$)

   string$ = Upper$(string$)
   For i = 1 To Len(string$)
       While desc$ <> "End"
           Read desc$, value$
           If desc$ = "" Then desc$ = chr$(34)
           If desc$ = Mid$(string$, i, 1) Then
               If Mid$(string$, i, 1) <> " " Then value$ = " " + value$
               TranslateToMorse$ = TranslateToMorse$ + value$
               Exit While
           End If
       Wend
       If desc$ = "End" Then Notice Mid$(string$, i, 1) + " is not accounted for in the Morse Code Table."
       Restore
   Next i
   TranslateToMorse$ = Trim$(TranslateToMorse$)
   Data "A", ".-", "B", "-...", "C", "-.-.", "D", "-..", "E", ".", "F", "..-.", "G", "--."
   Data "H", "....", "I", "..", "J", ".---", "K", "-.-", "L", ".-..", "M", "--", "N", "-."
   Data "O", "---", "P", ".--.", "Q", "--.-", "R", ".-.", "S", "...", "T", "-", "U", "..-"
   Data "V", "...-", "W", ".--", "X", "-..-", "Y", "-.--", "Z", "--..", "Á", "--.-", "Ä", ".-.-"
   Data "É", "..-..", "Ñ", "--.--", "Ö", "---.", "Ü", "..--", "1", ".----", "2", "..---"
   Data "3", "...--", "4", "....-", "5", ".....", "6", "-....", "7", "--...", "8", "---.."
   Data "9", "----.", "0", "-----", ",", "--..--", ".", ".-.-.-", "?", "..--..", ";", "-.-.-"
   Data ":", "---...", "/", "-..-.", "-", "-....-", "'", ".----.", "+", ".-.-.", "", ".-..-."
   Data "@", ".--.-.", "(", "-.--.", ")", "-.--.-", "_", "..--.-", "$", "...-..-", "&", ".-..."
   Data "=", "-...-", "!", "..--.", " ", " ", "End", ""

End Function </lang>

MATLAB

This function will remove any characters not defined in the morse code.

<lang MATLAB>function [morseText,morseSound] = text2morse(string,playSound)

%% Translate AlphaNumeric Text to Morse Text

   string = lower(string);
   
   %Defined such that the ascii code of the characters in the string map
   %to the indecies of the dictionary.
   morseDictionary = {{' ',' '},{,},{,},{,},...
                      {,},{,},{,},{,},{,},{,},...
                      {,},{,},{,},{,},{,},{,},...
                      {'0','-----'},{'1','.----'},{'2','..---'},{'3','...--'},...
                      {'4','....-'},{'5','.....'},{'6','-....'},{'7','--...'},...
                      {'8','---..'},{'9','----.'},...
                      {,},{,},{,},{,},{,},{,},...
                      {,},{,},{,},{,},{,},{,},...
                      {,},{,},{,},{,},{,},{,},...
                      {,},{,},{,},{,},{,},{,},...
                      {,},{,},{,},{,},{,},{,},...
                      {,},{,},{,},{,},{,},{,},...
                      {,},{,},{,},...
                      {'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','--..'}};
   
   %Iterates through each letter in the string and converts it to morse
   %code
   morseText = arrayfun(@(x)[morseDictionary{x}{2} '|'],(string - 31),'UniformOutput',false);
   
   %The output of the previous operation is a cell array, we want it to be
   %a string. This line accomplishes that.
   morseText = cell2mat(morseText);
   
   morseText(end) = []; %delete extra pipe
   

%% Translate Morse Text to Morse Audio

   %Generate the tones for each element of the code
   SamplingFrequency = 8192; %Hz
   ditLength = .1; %s
   dit = (0:1/SamplingFrequency:ditLength);
   dah = (0:1/SamplingFrequency:3*ditLength);
   dit = sin(3520*dit);
   dah = sin(3520*dah);
   silent = zeros(1,length(dit));
   %A dictionary of the audio components of each symbol
   morseTiming = {{'.',[dit silent]},{'-',[dah silent]},{'|',[silent silent]},{' ',[silent silent]}};
   morseSound = [];
   for i = (1:length(morseText))
       %Iterate through each cell in the morseTiming cell array and
       %find which timing sequence corresponds to the current morse
       %text symbol.
       cellNum = find(cellfun(@(x)(x{1}==morseText(i)),morseTiming));
       morseSound = [morseSound morseTiming{cellNum}{2}];
   end
   morseSound(end-length(silent):end) = []; %Delete the extra silent tone at the end
   
   if(playSound)
       sound(morseSound,SamplingFrequency); %Play sound
   end
   

end %text2morse</lang>

Sample Output: This will play the audio automatically, because the playSound argument is "true". <lang MATLAB>>> text2morse('Call me Ishmael.',true)

ans =

-.-.|.-|.-..|.-..| |--|.| |..|...|....|--|.-|.|.-..|</lang>


Mathematica

A Morse "codec" based on replacement rule programming. Replacement rules also translate the text Morse code into audible Morse code. The dots and dashes become clarinet middle-C notes of different lengths. Unknown characters encode into "?" in Morse text and clarinet F# in Morse audio. The function, sonicMorse[s_String], plays the Morse code audio translation of a string.

<lang Mathematica>Dictionary = Join[CharacterRange["a", "z"], CharacterRange["0", "9"]]; mark = 0.1; gap = 0.125; (* gap should be equal to mark. But longer gap makes audio code easier to decode *) shortgap = 3*gap; medgap = 7*gap; longmark = 3*mark; MorseDictionary = {

  ".-", "-...", "-.-.", "-..",
  ".", "..-.", "--.", "....", "..",
  ".---", "-.-", ".-..", "--", "-.",
  "---", ".--.", "--.-", ".-.",
  "...", "-", "..-", "...-", ".--",
  "-..-", "-.--", "--..",
  "-----", ".----", "..---", "...--", "....-", ".....",
  "-....", "--...", "---..", "----."
  };

MorseDictionary = # <> " " & /@ MorseDictionary; (* Force short gap silence after each letter/digit *)

Tones = {

  SoundNote[None, medgap],
  SoundNote[None, shortgap],
  {SoundNote["C", mark, "Clarinet"], SoundNote[None, gap]},
  {SoundNote["C", longmark, "Clarinet"], SoundNote[None, gap]},
  {SoundNote["F#", mark, "Clarinet"], SoundNote[None, gap]}  (* Use F# short mark to denote unrecognized character *)
  };

codeRules = MapThread[Rule, {Dictionary, MorseDictionary}]; decodeRules = MapThread[Rule, {MorseDictionary, Dictionary}]; soundRules = MapThread[Rule, {{" ", " ", ".", "-", "?"}, Tones}]; (* The order of the rules here is important. Otherwise medium gaps and short gaps get confounded *)

morseCode[s_String] := StringReplace[ToLowerCase@s, codeRules~Join~{x_ /; FreeQ[Flatten@{Dictionary, " "}, x] -> "? "}] morseDecode[s_String] := StringReplace[s, decodeRules] sonicMorse[s_String] := EmitSound@Sound@Flatten[Characters@morseCode@s /. soundRules]</lang>

Text example:

morseCode["SOS   soS"]
morseDecode[%]
morseCode["s@os|"]

Output:

"... --- ...    ... --- ... "

"sos   sos"

"... ? --- ... ? "


OCaml

Using /dev/dsp:

<lang ocaml>let codes = [

 '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', "----.";

]

let oc = open_out "/dev/dsp"

let bip u =

 for i = 0 to pred u do
   let j = sin(0.6 *. (float i)) in
   let k = ((j +. 1.0) /. 2.0) *. 127.0 in
   output_byte oc (truncate k)
 done

let gap u =

 for i = 0 to pred u do
   output_byte oc 0
 done

let morse =

 let u = 1000 in  (* length of one unit *)
 let u2 = u * 2 in
 let u3 = u * 3 in
 let u6 = u * 6 in
 String.iter (function
 | ' ' -> gap u6
 | 'a'..'z' | 'A'..'Z' | '0'..'9' as c ->
     let s = List.assoc c codes in
     String.iter (function
       '.' -> bip u; gap u
     | '-' -> bip u3; gap u
     | _ -> assert false
     ) s; gap u2
 | _ -> prerr_endline "unknown char")

let () = morse "rosettacode morse"</lang>


PARI/GP

<lang parigp>sleep(ms)={

 while((ms-=gettime()) > 0,);

}; dot()=print1(Strchr([7]));sleep(250); dash()=print1(Strchr([7]));sleep(10);print1(Strchr([7]));sleep(10);print1(Strchr([7]));sleep(250); Morse(s)={

 s=Vec(s);
 for(i=1,#s,
   if(s[i] == ".", dot(),
     if(s[i] == "-", dash(), sleep(250))
   )
 )

};

Morse("...---...")</lang>

Perl

<lang Perl>use Acme::AGMorse qw(SetMorseVals SendMorseMsg); SetMorseVals(20,30,400); SendMorseMsg('Hello World! abcdefg @\;'); # note, caps are ingnored in Morse Code exit;</lang>


The above code requires:

Acme::AGMorse
Audio::Beep
Switch

Some known problems on UNIX:

1) pcspkr may not be available by default. Run: sudo modprobe pcspkr
2) pcspkr may be available but muted.
    - Check your sound prefrences,usually a right click over the speaker icon

Perl 6

Here we use the user as the audio device. Just read the output, leaving extra pauses where indicated by either whitespace or underscore. <lang>my %m = ' ', '_ _ ', <

   !	---.
   "	.-..-.
   $	...-..-
   '	.----.
   (	-.--.
   )	-.--.-
   +	.-.-.
   ,	--..--
   -	-....-
   .	.-.-.-
   /	-..-.
   :	---...
   ;	-.-.-.
   =	-...-
   ?	..--..
   @	.--.-.
   [	-.--.
   ]	-.--.-
   _	..--.-
   0	-----
   1	.----
   2	..---
   3	...--
   4	....-
   5	.....
   6	-....
   7	--...
   8	---..
   9	----.
   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	--..

>.map: -> $c, $m is copy {

   $m.=subst(rx/'-'/, 'BGAAACK!!! ', :g);
   $m.=subst(rx/'.'/, 'buck ', :g);
   $c => $m ~ '_ ';

}

say prompt("Gimme a string: ").uc.comb.map: { %m{$_} // "<scratch> " }</lang> Sample run:

Gimme a string: Howdy, World!
buck buck buck buck _ BGAAACK!!! BGAAACK!!! BGAAACK!!! _ buck BGAAACK!!! BGAAACK!!! _ BGAAACK!!! buck buck _ BGAAACK!!! buck BGAAACK!!! BGAAACK!!! _ BGAAACK!!! BGAAACK!!! buck buck BGAAACK!!! BGAAACK!!! _ _ _ buck BGAAACK!!! BGAAACK!!! _ BGAAACK!!! BGAAACK!!! BGAAACK!!! _ buck BGAAACK!!! buck _ buck BGAAACK!!! buck buck _ BGAAACK!!! buck buck _ BGAAACK!!! BGAAACK!!! BGAAACK!!! buck _

PicoLisp

The following simply uses the 'beep' pc-speaker beeper utility. <lang PicoLisp># *Morse *Dit *Dah

(balance '*Morse

  (mapcar
     '((L)
        (def (car L)
           (mapcar = (chop (cadr L)) '("." .)) ) )
     (quote
        ("!"  "---.")     ("\"" ".-..-.")   ("$" "...-..-")   ("'" ".----.")
        ("(" "-.--.")     (")" "-.--.-")    ("+" ".-.-.")     ("," "--..--")
        ("-" "-....-")    ("." ".-.-.-")    ("/" "-..-.")
        ("0" "-----")     ("1" ".----")     ("2" "..---")     ("3" "...--")
        ("4" "....-")     ("5" ".....")     ("6" "-....")     ("7" "--...")
        ("8" "---..")     ("9" "----.")
        (":" "---...")    (";" "-.-.-.")    ("=" "-...-")     ("?" "..--..")
        ("@" ".--.-.")
        ("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" "--..")
        ("[" "-.--.")     ("]" "-.--.-")    ("_" "..--.-") ) ) )
  1. Words per minute

(de wpm (N)

  (setq *Dit (*/ 1200 N)  *Dah (* 3 *Dit)) )

(wpm 20)

  1. Morse a string

(de morse (Str)

  (for C (chop Str)
     (cond
        ((sp? C) (wait (+ *Dah *Dit)))         # White space: Pause
        ((idx '*Morse (uppc C))                # Known character
           (for Flg (val (car @))
              (call "/usr/bin/beep" "-D" *Dit "-l" (if Flg *Dit *Dah)) ) )
        (T (call "/usr/bin/beep" "-f" 370)) )  # Unkown character
     (wait (- *Dah *Dit)) ) )

(morse "Hello world!")</lang>

PL/I

<lang PL/I> /* Sound Morse code via the PC buzzer. June 2011 */ MORSE: procedure options (main);

  declare (i, j) fixed binary;
  declare buzz character (1) static initial ('07'x);
  declare text character (100) varying,
     c character (1);
  declare alphabet character (36) static initial (
     'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
  declare morse_character character(5) varying;
  declare morse_codes(36) character(5) varying static initial (
     /* Letters A-Z */
     '.-',    '-...',   '-.-.',   '-..',    '.',
     '..-.',  '--.',    '....',   '..',     '.---',
     '-.-',   '.-..',   '--',     '-.',     '---',
     '--.-',  '--.-',   '.-.',    '...',    '-',
     '..-',   '...-',   '.--',    '-..-',   '-.--',
     '--..',
     /* Digits 0-9 */
     '-----', '.----',  '..---', '...--',   '....-',
     '.....', '-....',  '--...', '---..',   '----.' );
  put skip list ('Please type the text to be transmitted:');
  get edit (text) (L);
  do i = 1 to length (text);
     c = substr(text, i, 1);
     j = index(alphabet, uppercase(c));
     if j > 0 then
        do;
           morse_character = morse_codes(j);
           put skip list (morse_character); /* Display the Morse. */
           call send_morse (morse_character);
        end;
  end;

send_morse: procedure (morse_character);

  declare morse_character character(*) varying;
  declare i fixed binary;
  do i = 1 to length(morse_character);
     if substr(morse_character, 1, 1) = '-' then
        put skip edit (buzz, ' ', buzz) (a(1), A, skip, a(1));
     else
        put skip edit (buzz, ' ') (a(1));
     delay (1000); /* Delay one period. */
  end;
  delay (1000);
     /* Making a delay of 2 periods after each English letter. */

end send_morse;

END MORSE;</lang>

PureBasic

<lang PureBasic>#BaseTime =50

  1. Frequence=1250
  2. Short = #BaseTime
  3. Long =3* #BaseTime
  4. Intergap = #BaseTime
  5. LetterGap=3* #BaseTime
  6. WordGap =7* #BaseTime

Declare.s TextToMorse(Text$) Declare.i PlayMorse(Text$)

Text$ =InputRequester("Morse coder","Enter text to send","Hello RosettaWorld!") Text$ =TextToMorse(Text$) If Not (InitSound() And PlayMorse(Text$))

 Text$=ReplaceString(Text$, ",","")
 MessageRequester("Morse EnCoded",Text$)

EndIf

-

Procedure PlayMorse(Code$)  ;- Beep() is normally only Ok on Windows_x86

 CompilerIf #PB_Compiler_Processor=#PB_Processor_x86 And #PB_Compiler_OS=#PB_OS_Windows
   Protected i, sign
   For i=1 To Len(Code$)
     sign=Asc(Mid(Code$,i,1))
     Select sign
       Case '.': Beep_(#Frequence,#Short): Delay(#Intergap)
       Case '-': Beep_(#Frequence,#Long) : Delay(#Intergap)
       Case ',': Delay(#LetterGap)
       Case ' ': Delay(#WordGap)
     EndSelect
   Next
   ProcedureReturn 1
 CompilerElse
   ProcedureReturn 0
 CompilerEndIf

EndProcedure

Procedure.s TextToMorse(InString$)

 Protected *p.Character=@InString$, CurrStr$, i=1
 Protected.s s1, s2, result
 Repeat
   If Not *p\c: Break: EndIf
   CurrStr$=UCase(PeekS(*p,1))
   *p+StringByteLength(">")
   Restore MorseCode
   Repeat
     Read.s s1
     If s1="Done"
       s2+s1+" " ; failed to find this coding
       Break 
     ElseIf Not s1=CurrStr$
       Continue
     EndIf
     Read.s s2
     result+s2
     If s2<>" "
       result+","  
     EndIf
   ForEver
 ForEver 
 ProcedureReturn result

EndProcedure

DataSection

 MorseCode:
 Data.s "A",  ".-"
 Data.s "B",  "-..."
 Data.s "C",  "-.-."
 Data.s "D",  "-.."
 Data.s "E",  "."
 Data.s "F",  "..-."
 Data.s "G",  "--."
 Data.s "H",  "...."
 Data.s "I",  ".."
 Data.s "J",  ".---"
 Data.s "K",  "-.-"
 Data.s "L",  ".-.."
 Data.s "M",  "--"
 Data.s "N",  "-."
 Data.s "O",  "---"
 Data.s "P",  ".--."
 Data.s "Q",  "--.-"
 Data.s "R",  ".-."
 Data.s "S",  "..."
 Data.s "T",  "-"
 Data.s "U",  "..-"
 Data.s "V",  "...-"
 Data.s "W",  ".--"
 Data.s "X",  "-..-"
 Data.s "Y",  "-.--"
 Data.s "Z",  "--.."
 Data.s "Á",  "--.-"
 Data.s "Ä",  ".-.-"
 Data.s "É",  "..-.."
 Data.s "Ñ",  "--.--"
 Data.s "Ö",  "---."
 Data.s "Ü",  "..--"
 Data.s "1",  ".----"
 Data.s "2",  "..---"
 Data.s "3",  "...--"
 Data.s "4",  "....-"
 Data.s "5",  "....."
 Data.s "6",  "-...."
 Data.s "7",  "--..."
 Data.s "8",  "---.."
 Data.s "9",  "----."
 Data.s "0",  "-----"
 Data.s ",",  "--..--"
 Data.s ".",  ".-.-.-"
 Data.s "?",  "..--.."
 Data.s ";",  "-.-.-"
 Data.s ":",  "---..."
 Data.s "/",  "-..-."
 Data.s "-",  "-....-"
 Data.s "'",  ".----."
 Data.s "+",  ".-.-."
 Data.s "-",  "-....-"
 Data.s #DOUBLEQUOTE$, ".-..-."
 Data.s "@",  ".--.-."
 Data.s "(",  "-.--."
 Data.s ")",  "-.--.-"
 Data.s "_",  "..--.-"
 Data.s "$",  "...-..-"
 Data.s "&",  ".-..."
 Data.s "=",  "---..."
 Data.s " ",  " "
 Data.s  "Done",""
 EndOfMorseCode:

EndDataSection</lang>

Python

<lang python>import time, winsound #, sys

char2morse = {

         "!": "---.",      "\"": ".-..-.",     "$": "...-..-",    "'": ".----.",  
         "(": "-.--.",      ")": "-.--.-",     "+": ".-.-.",      ",": "--..--", 
         "-": "-....-",     ".": ".-.-.-",     "/": "-..-.", 
         "0": "-----",      "1": ".----",      "2": "..---",      "3": "...--", 
         "4": "....-",      "5": ".....",      "6": "-....",      "7": "--...", 
         "8": "---..",      "9": "----.", 
         ":": "---...",     ";": "-.-.-.",     "=": "-...-",      "?": "..--..", 
         "@": ".--.-.", 
         "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": "--..", 
         "[": "-.--.",      "]": "-.--.-",     "_": "..--.-",
}

e = 50 # Element time in ms. one dit is on for e then off for e f = 1280 # Tone freq. in hertz chargap = 1 # Time between characters of a word, in units of e wordgap = 7 # Time between words, in units of e

def gap(n=1):

   time.sleep(n * e / 1000)

off = gap

def on(n=1):

   winsound.Beep(f, n * e)

def dit():

   on(); off()

def dah():

   on(3); off()

def bloop(n=3):

   winsound.Beep(f//2, n * e)

def windowsmorse(text):

   for word in text.strip().upper().split():
       for char in word:
           for element in char2morse.get(char, '?'):
               if element == '-':
                   dah()
               elif element == '.':
                   dit()
               else:
                   bloop()
           gap(chargap)
       gap(wordgap)
  1. Outputs its own source file as Morse. An audible quine!
  2. with open(sys.argv[0], 'r') as thisfile:
  3. windowsmorse(thisfile.read())

while True:

   windowsmorse(input('A string to change into morse: '))

</lang>

REXX

The   $MORSE   REXX program is included here ──► $MORSE.REX.
The help for the   $MORSE   REXX program is included here ──► $MORSE.HEL.

This program supports the International Morse code as well as the USA Morse code (which primarily was
used by the North American Railroads.   Some translation is done for unsupported characters such as
braces   {   }, brackets   [   ]   and the like.
Morse code words are shown one word to a line before sounding.

This REXX programs only works for Regina and PC/REXX, but other REXXes (specifically R4) will only display the Morse code, but not sound it.

The $MORSE REXX program makes use of $T REXX program which is used to display text and/or write the text to a file.
The $T REXX program is included here ──► $T.REX.
The help for the $T REXX program is included here ──► $T.HEL.

The $MORSE REXX program makes use of $ERR REXX program which is used to display error messages (via $T).
The $ERR REXX program is included here ──► $ERR.REX.
The help for the $ERR REXX program is included here ──► $ERR.HEL.

The $MORSE REXX program makes use of SOUND REXX program which is used to express sound (via the internal speaker).
The SOUND REXX program is included here ──► SOUND.REX.

Some older REXXes don't have a changestr bif, so one is included here ──► CHANGESTR.REX.

output when using the input of: ( cqd --- indicates the vessel sending is in distress and requires immediate assistance.

-∙-∙ --∙- -∙∙
-∙∙∙∙- -∙∙∙∙- -∙∙∙∙-
∙∙ -∙ -∙∙ ∙∙ -∙-∙ ∙- - ∙ ∙∙∙
- ∙∙∙∙ ∙
∙∙∙- ∙ ∙∙∙ ∙∙∙ ∙ ∙-∙∙
∙∙∙ ∙ -∙ -∙∙ ∙∙ -∙ --∙
∙∙ ∙∙∙
∙∙ -∙
-∙∙ ∙∙ ∙∙∙ - ∙-∙ ∙ ∙∙∙ ∙∙∙
∙- -∙ -∙∙
∙-∙ ∙ --∙- ∙∙- ∙∙ ∙-∙ ∙ ∙∙∙
∙∙ -- -- ∙ -∙∙ ∙∙ ∙- - ∙
∙- ∙∙∙ ∙∙∙ ∙∙ ∙∙∙ - ∙- -∙ -∙-∙ ∙ ∙-∙-∙-

Ruby

Works with: Ruby version 1.8.7+

(uses each_char)

Library: win32-utils

<lang ruby>require 'win32/sound'

class MorseCode

 MORSE = {
     "!" => "---.", "\"" => ".-..-.", "$" => "...-..-", "'" => ".----.",
     "(" => "-.--.", ")" => "-.--.-", "+" => ".-.-.", "," => "--..--",
     "-" => "-....-", "." => ".-.-.-", "/" => "-..-.", "0" => "-----",
     "1" => ".----", "2" => "..---", "3" => "...--", "4" => "....-", "5" => ".....",
     "6" => "-....", "7" => "--...", "8" => "---..", "9" => "----.", ":" => "---...",
     ";" => "-.-.-.", "=" => "-...-", "?" => "..--..", "@" => ".--.-.", "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" => "--..",
     "[" => "-.--.", "]" => "-.--.-", "_" => "..--.-",
 }
 T_UNIT = 75 # ms
 FREQ = 700
 DIT = 1 * T_UNIT
 DAH = 3 * T_UNIT
 CHARGAP = 1 * T_UNIT
 WORDGAP = 7 * T_UNIT
 def initialize(string)
   @message = string
   puts "your message is #{string.inspect}"
 end
 def send
   @message.strip.upcase.split.each do |word|
     word.each_char do |char|
       send_char char
       pause CHARGAP
       print " "
     end
     pause WORDGAP
     puts ""
   end
 end
 private
 def send_char(char)
   MORSE[char].each_char do |code|
     case code
     when '.' then beep DIT
     when '-' then beep DAH
     end
     pause CHARGAP
     print code
   end
 end
 def beep(ms)
   ::Win32::Sound.beep(FREQ, ms)
 end
 def pause(ms)
   sleep(ms.to_f/1000.0)
 end

end

MorseCode.new('sos').send MorseCode.new('this is a test.').send</lang>

outputs

your message is "sos"
... --- ...
your message is "this is a test."
- .... .. ...
.. ...
.-
- . ... - .-.-.-

sed

Translation of AWK: <lang sed>

  1. !/bin/sed -rf
  2. Convert to uppercase

s/.*/\U&/

  1. Add lookup table

s/$/\nA.-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--../

  1. Main loop
a

s/([A-Z])([^\n]*\n.*\1([-.]+))/\3 \2/ ta

  1. Remove lookup table

s/\n.*// </lang> Example: <lang bash> $ echo hello world! | ./morse.sed .... . .-.. .-.. --- .-- --- .-. .-.. -.. ! </lang>

Tcl

Library: Snack

<lang tcl># This uses the GUI-free part of the Snack library package require sound

  1. A simple pause while running the event loop, in terms of basic time units

proc pause n {

   global t
   after [expr {$t * $n}] set ok 1
   vwait ok

}

  1. Generate using a sine-wave filter

proc beep n {

   global frequency
   set f [snack::filter generator $frequency 30000 0.0 sine -1]
   set s [snack::sound -rate 22050]
   $s play -filter $f
   pause $n
   $s stop
   $s destroy
   $f destroy
   pause 1

}

  1. The dits and the dahs are just beeps of different lengths

interp alias {} dit {} beep 1 interp alias {} dah {} beep 3

set MORSE_CODE {

   "!" "---."	 "\"" ".-..-."	"$" "...-..-"	"'" ".----."
   "(" "-.--."	 ")" "-.--.-"	"+" ".-.-."	"," "--..--"
   "-" "-....-" "." ".-.-.-"	"/" "-..-."
   ":" "---..." ";" "-.-.-."	"=" "-...-"	"?" "..--.."
   "@" ".--.-." "[" "-.--."	"]" "-.--.-"	"_" "..--.-"
   "0" "-----"	 "1" ".----"	"2" "..---"	"3" "...--"
   "4" "....-"	 "5" "....."	"6" "-...."	"7" "--..."
   "8" "---.."	 "9" "----."
   "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" "--.."

}

  1. The code to translate text to morse code and play it

proc morse {str wpm} {

   global t MORSE_CODE
   set t [expr {1200 / $wpm}]
   # Backslash and space are special cases in various ways
   set map {"\\" {} " " {[pause 4]}}
   # Append each item in the code to the map, with an inter-letter pause after
   foreach {from to} $MORSE_CODE {lappend map $from "$to\[pause 3\]"}
   # Convert to dots and dashes
   set s [string map $map [string toupper $str]]
   # Play the dots and dashes by substituting commands for them
   subst [string map {"." [dit] "-" [dah]} $s]
   return

}

  1. We'll play at a fairly high pitch

set frequency 700

morse "Morse code with Tcl and Snack." 20</lang>

TUSCRIPT

<lang tuscript> $$ MODE TUSCRIPT MODE DATA $$ BUILD X_TABLE alfabet2moco =* "!"---."\".-..-."$"...-..-"'".----." "("-.--.")"-.--.-"+".-.-.","--..--" "-"-....-".".-.-.-"/"-..-." "0"-----"1".----"2"..---"3"...--" "4"....-"5"....."6"-...."7"--..." "8"---.."9"----." ":"---...";"-.-.-."="-...-"?"..--.." "@".--.-." "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"--.." "["-.--."]"-.--.-"_"..--.-" $$ BUILD X_TABLE moco2sound =* " "p2 " "-"a4 " "."a2 " $$ BUILD X_TABLE space2split=": :':" $$ MODE TUSCRIPT ASK "Please enter your sentence": mc="" PRINT "SEE your morsecode !" mc=EXCHANGE (mc,alfabet2moco) PRINT mc PRINT "HEAR your morsecode !" mc=EXCHANGE (mc,moco2sound) mc=EXCHANGE (mc,space2split) BEEP $mc </lang> Output:

Please enter your sentence >Hello World
SEE your morsecode !
......-...-..--- .-----.-..-..-..
HEAR your morsecode !
Please enter your sentence >SOS
SEE your morsecode !
...---...
HEAR your morsecode ! 

XPL0

This uses the Sound intrinsic, which has a minimum duration of one system tick (about 1/18th of a second) for a dit (.). The result is Morse code sent at about 20 words per minute. The Sound intrinsic also provides the delay for sound gaps when its volume parameter is set to zero.

<lang XPL0>code ChOut=8, CrLf=9, Sound=39; string 0; \use zero-terminated strings

proc Morse(Msg); \Output Msg string as audible International Morse code char Msg; char C, D; int Code, Vol; [Code:= [" ", \space

       ".-.-..",".-..-.", "    ", "...-..-","    ",    \!"#$%
       "----.", ".----.", "-.--.-", "---..",  "    ",  \&'()*
       ".-.-.", "--..--", "-....-", ".-.-.-", "-..-.", \+,-./
       "-----", ".----",  "..---",  "...--",  "....-", \01234
       ".....", "-....",  "--...",  "---..",  "----.", \56789
       "---...","-.-.-.", "    ",   "-...-",  "    ",  \:;<=>
       "..--..",".--.-.",                              \?@
       ".-",    "-...",   "-.-.",   "-..",    ".",     \ABCDE
       "..-.",  "--.",    "....",   "..",     ".---",  \FGHIJ
       "-.-",   ".-..",   "--",     "-.",     "---",   \KLMNO
       ".--.",  "--.-",   ".-.",    "...",    "-",     \PQRST
       "..-",   "...-",   ".--",    "-..-",   "-.--",  \UVWXY
       "--.."];                                        \Z

Sound(0, 1, 1); \sync, for consistent durations repeat C:= Msg(0); Msg:= Msg+1; \get character from message

       ChOut(0, C);                            \show the character
       if C>=^a & C<=^z then C:= C-$20;        \convert letters to uppercase
       Vol:= 1;                                \assume volume is on (not space)
       if C>=$21 & C<=^Z then D:= Code(C-$20)  \convert characters to code
       else [D:= Code(0);  Vol:= 0];           \space (or unsupported char)
       repeat  Sound(Vol, if D(0)=^- then 3 else 1, 1190000/600);      \600 Hz
               Sound(0, 1, 1);                 \gap between . and -
               ChOut(0, D(0));                 \show dots and dashes
               D:= D+1;                        \next dot or dash for character
       until   D(0) = 0;                       \string terminator
       Sound(0, 2, 1);                         \gap between letters (2+1=3)
       ChOut(0, ^ );                           \show gap

until Msg(0) = 0; \string terminator ];

[Morse("SOS SOS SOS "); \something easy to recognize CrLf(0); Morse("Hello, world!"); ]</lang>

Output:
S... O--- S...       S... O--- S...       S... O--- S...       
H.... e. l.-.. l.-.. o--- ,--..--       w.-- o--- r.-. l.-.. d-.. !.-.-..