Morse code

From Rosetta Code
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 175 years — longer than any other electronic encoding system.


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).

8086 Assembly

This program runs under MS-DOS and outputs to the PC speaker. It reads the text from standard input until EOF is reached.

	cpu	8086
	bits	16
	;;;	I/O ports
KBB:	equ	61h	; Keyboard controller port B (also controls speaker)
PITC2:	equ	42h	; Programmable Interrupt Timer, channel 2 (frequency)
PITCTL:	equ	43h	; PIT control port.
	;;;	Control bits
SPKR:	equ	3	; Lower two bits of KBB determine speaker on/off
CTR:	equ	6	; Counter select offset in PIT control byte
CBITS:	equ	4	; Size select offset in PIT control byte
B16:	equ	3	; 16-bit mode for the PIT counter
MODE:	equ	1	; Offset of mode in PIT control byte
SQWV:	equ	3	; Square wave mode
	;;;	Software interrupts
CLOCK:	equ	1Ah	; BIOS clock function interrupt
DOS:	equ	21h	; MS-DOS syscall interrupt
	;;;	MS-DOS syscalls
read:	equ	3Fh	; Read from file
section	.text
	org	100h
	;;;	Set up the PIT to generate a 'C' note
	cli
	mov	al,(2<<CTR)|(B16<<CBITS)|(SQWV<<MODE)
	out	PITCTL,al
	mov	ax,2280		; Divisor of main oscillator frequency
	out	PITC2,al	; Output low byte first,
	xchg	al,ah
	out	PITC2,al	; Then high byte.
	sti
	;;;	Read from stdin and sound as morse
input:	mov	ah,read		; Read
	xor	bx,bx		; from STDIN 
	mov	cx,buf.size	; into the buffer
	mov	dx,buf
	int	DOS
	jc	stop		; Carry set = error, stop
	test	ax,ax		; Did we get any characters?
	jnz	go		; If not, we're done, so stop
stop:	ret
go:	mov	cx,ax		; Loop counter = how many characters we have
	mov	si,buf		; Start at buffer size
dochar:	lodsb			; Get current character
	cmp	al,26		; End of file?
	je	stop		; Then stop
	push	cx		; Save pointer and counter
	push	si
	call	char		; Sound out this character
	pop	si		; Restore pointer and counter
	pop	cx
	loop	dochar		; If more characters, do the next one
	jmp	input		; Afterwards, try to get more input
	;;;	Sound ASCII character in AL
char:	and	al,127		; 7-bit ASCII
	sub	al,32		; Word separator? (<=32)
	ja	.snd		; If not, look up in table
	mov	bx,4		; Otherwise, 'sound' a word space
	jmp	delay		; (4 more ticks to form 7-tick delay)
.snd:	mov	bx,morse	; Find offset in morse pulse table
	xlatb
	test	al,al		; If it is zero, we want to ignore it
	jz	.out
	xor	ah,ah		; Otherwise, find its address
	mov	bx,ax
	lea	si,[bx+morse.P]	; and store it in SI.
	xor	bh,bh		; BX = BL in the following code
.byte:	lodsb			; Get pulse byte
	mov	cx,4		; Four pulses per byte
.pulse:	mov	bl,3		; Load low pulse in BL
	and	bl,al
	test	bl,bl		; If it is zero,
	jz	.out		; We're done
	mov	bp,ax		; Otherwise, keep AX
	call	pulse		; Sound the pulse
	mov	ax,bp		; Restore AX
	shr	al,1		; Next pulse
	shr 	al,1
	loop	.pulse
	jmp	.byte		; If no zero pulse seen yet, next byte
.out:	mov	bl,2		; 2 ticks more delay to form inter-char space 
	jmp	delay
	;;;	Sound morse code pulse w/delay
pulse:	cli			; Turn off interrupts
	in	al,KBB		; Read current configuration
	or	al,SPKR		; Turn speaker on
	out	KBB,al		; Write configuration back
	sti			; Turn interrupts back on
	call	delay		; Delay for BX ticks
	cli			; Turn off interrupts
	in	al,KBB		; Read current configuration
	and	al,~SPKR	; Turn speaker off
	out	KBB,al		; Write configuration back
	sti			; Turn interrupts back on
	mov	bx,1		; Intra-character delay = 1 tick
	;;;	Delay for BX ticks
delay:	push	bx		; Keep the registers we alter
	push	cx		
	push	dx
	xor	ah,ah		; Clock function 0 = get ticks
	int	CLOCK		; Get current ticks (in CX:DX)
	add	bx,dx		; BX = time for which to wait
.wait:	int	CLOCK		; Wait for that time to occur
	cmp	dx,bx		; Are we there yet?
	jbe	.wait		; If not, try again
	pop	dx		; Restore the registers
	pop	cx
	pop 	bx
	ret
section	.data
morse:	;;;	Printable ASCII to pulse mapping (32-122)
	db	.n_-.P,		.excl-.P,	.dquot-.P,	.n_-.P	; !"#
	db	.dolar-.P,	.n_-.P,		.amp-.P,	.quot-.P;$%&'
	db	.open-.P,	.close-.P,	.n_-.P,		.plus-.P;()*+
	db	.comma-.P,	.minus-.P,	.dot-.P,	.slsh-.P;,-./
	db	.n0-.P,		.n1-.P,		.n2-.P,		.n3-.P	;0123
	db	.n4-.P,		.n5-.P,		.n6-.P,		.n7-.P	;4567
	db	.n8-.P,		.n9-.P,		.colon-.P,	.semi-.P;89:;
	db	.n_-.P,		.eq-.P,		.n_-.P,		.qm-.P 	;<=>?
	db	.at-.P,		.a-.P,		.b-.P,		.c-.P	;@ABC
	db	.d-.P,		.e-.P,		.f-.P,		.g-.P	;DEFG
	db	.h-.P,		.i-.P,		.j-.P,		.k-.P	;HIJK
	db	.l-.P,		.m-.P,		.n-.P,		.o-.P	;LMNO
	db	.p-.P,		.q-.P,		.r-.P,		.s-.P	;PQRS
	db	.t-.P,		.u-.P,		.v-.P,		.w-.P	;TUVW
	db	.x-.P,		.y-.P,		.z-.P,		.n_-.P	;XYZ[
	db	.n_-.P,		.n_-.P,		.n_-.P,		.uscr-.P;\]^_
	db	.n_-.P,		.a-.P,		.b-.P,		.c-.P	;`abc
	db	.d-.P,		.e-.P,		.f-.P,		.g-.P	;defg
	db	.h-.P,		.i-.P,		.j-.P,		.k-.P	;hijk
	db	.l-.P,		.m-.P,		.n-.P,		.o-.P	;lmno
	db	.p-.P,		.q-.P,		.r-.P,		.s-.P	;pqrs
	db	.t-.P,		.u-.P,		.v-.P,		.w-.P	;tuvw
	db	.x-.P,		.y-.P,		.z-.P,		.n_-.P	;xyz{
	db	.n_-.P,		.n_-.P,		.n_-.P,		.n_-.P	;|}~
.P:	;;;	Morse pulses are stored four to a byte, lowest bits first
.n_:	db	0	; To ignore undefined characters
.a:	db	0Dh
.b:	db	57h,0
.c:	db	77h,0
.d:	db	17h
.e:	db	1h
.f:	db	75h,0
.g:	db	1Fh
.h:	db	55h,0
.i:	db	5h
.j:	db	0FDh,0
.k:	db	37h
.l:	db	5Dh,0
.m:	db	0Fh
.n:	db	7h
.o:	db	3Fh
.p:	db	7Dh,0
.q:	db	0DFh,0
.r:	db	1Dh
.s:	db	15h
.t:	db	3h
.u:	db	35h
.v:	db	0D5h,0
.w:	db	3Dh
.x:	db	0D7h,0
.y:	db	0F7h,0
.z:	db	5Fh,0
.n0:	db	0FFh,3
.n1:	db	0FDh,3
.n2:	db	0F5h,3
.n3:	db	0D5h,3
.n4:	db	55h,3
.n5:	db	55h,1
.n6:	db	57h,1
.n7:	db	5Fh,1
.n8:	db	7Fh,1
.n9:	db	0FFh,1
.dot:	db	0DDh,0Dh
.comma:	db	5Fh,0Fh
.qm:	db	0F5h,5
.quot:	db	0FDh,7
.excl:	db	77h,0Fh
.slsh:	db	0D7h,1
.open:	db	0F7h,1
.close:	db	0F7h,0Dh
.amp:	db	5Dh,1
.colon:	db	7Fh,5
.semi:	db	77h,7
.eq:	db	57h,3
.plus:	db	0DDh,1
.minus:	db	57h,0Dh
.uscr:	db	0F5h,0Dh
.dquot:	db	5Dh,7
.dolar:	db	0D5h,35h
.at:	db	7Dh,7
section	.bss
buf:	resb	1024		; 1K buffer
.size:	equ	$-buf

ABAP

REPORT morse_code.
TYPES: BEGIN OF y_morse_code,
         letter TYPE string,
         code   TYPE string,
       END OF y_morse_code,
       ty_morse_code TYPE STANDARD TABLE OF y_morse_code WITH EMPTY KEY.

cl_demo_output=>new(
          )->begin_section( |Morse Code|
          )->write( REDUCE stringtab( LET words = VALUE stringtab( ( |sos|                 )
                                                                   ( |   Hello     World!| )
                                                                   ( |Rosetta Code|        ) )
                                          morse_code = VALUE ty_morse_code( ( letter = 'A'   code = '.-     ' )
                                                                            ( letter = 'B'   code = '-...   ' )
                                                                            ( letter = 'C'   code = '-.-.   ' )
                                                                            ( letter = 'D'   code = '-..    ' )
                                                                            ( letter = 'E'   code = '.      ' )
                                                                            ( letter = 'F'   code = '..-.   ' )
                                                                            ( letter = 'G'   code = '--.    ' )
                                                                            ( letter = 'H'   code = '....   ' )
                                                                            ( letter = 'I'   code = '..     ' )
                                                                            ( letter = 'J'   code = '.---   ' )
                                                                            ( letter = 'K'   code = '-.-    ' )
                                                                            ( letter = 'L'   code = '.-..   ' )
                                                                            ( letter = 'M'   code = '--     ' )
                                                                            ( letter = 'N'   code = '-.     ' )
                                                                            ( letter = 'O'   code = '---    ' )
                                                                            ( letter = 'P'   code = '.--.   ' )
                                                                            ( letter = 'Q'   code = '--.-   ' )
                                                                            ( letter = 'R'   code = '.-.    ' )
                                                                            ( letter = 'S'   code = '...    ' )
                                                                            ( letter = 'T'   code = '-      ' )
                                                                            ( letter = 'U'   code = '..-    ' )
                                                                            ( letter = 'V'   code = '...-   ' )
                                                                            ( letter = 'W'   code = '.-   - ' )
                                                                            ( letter = 'X'   code = '-..-   ' )
                                                                            ( letter = 'Y'   code = '-.--   ' )
                                                                            ( letter = 'Z'   code = '--..   ' )
                                                                            ( letter = '0'   code = '-----  ' )
                                                                            ( letter = '1'   code = '.----  ' )
                                                                            ( letter = '2'   code = '..---  ' )
                                                                            ( letter = '3'   code = '...--  ' )
                                                                            ( letter = '4'   code = '....-  ' )
                                                                            ( letter = '5'   code = '.....  ' )
                                                                            ( letter = '6'   code = '-....  ' )
                                                                            ( letter = '7'   code = '--...  ' )
                                                                            ( letter = '8'   code = '---..  ' )
                                                                            ( letter = '9'   code = '----.  ' )
                                                                            ( letter = ''''  code = '.----. ' )
                                                                            ( letter = ':'   code = '---... ' )
                                                                            ( letter = ','   code = '--..-- ' )
                                                                            ( letter = '-'   code = '-....- ' )
                                                                            ( letter = '('   code = '-.--.- ' )
                                                                            ( letter = '.'   code = '.-.-.- ' )
                                                                            ( letter = '?'   code = '..--.. ' )
                                                                            ( letter = ';'   code = '-.-.-. ' )
                                                                            ( letter = '/'   code = '-..-.  ' )
                                                                            ( letter = '_'   code = '..--.- ' )
                                                                            ( letter = ')'   code = '---..  ' )
                                                                            ( letter = '='   code = '-...-  ' )
                                                                            ( letter = '@'   code = '.--.-. ' )
                                                                            ( letter = '\'   code = '.-..-. ' )
                                                                            ( letter = '+'   code = '.-.-.  ' )
                                                                            ( letter = ' '   code = '/'       ) )
                                      IN INIT word_coded_tab TYPE stringtab
                                          FOR word IN words
                                         NEXT word_coded_tab = VALUE #( BASE word_coded_tab ( REDUCE string( INIT word_coded TYPE string
                                                                                                              FOR index = 1 UNTIL index > strlen( word )
                                                                                                              LET _morse_code = VALUE #( morse_code[ letter = COND #( WHEN index = 1 THEN to_upper( word(index) )
                                                                                                                                                                      ELSE LET prev = index - 1 IN to_upper( word+prev(1) ) ) ]-code OPTIONAL )
                                                                                                               IN NEXT word_coded = |{ word_coded } { _morse_code }| ) ) ) )
          )->display( ).

Action!

DEFINE PTR="CARD"
DEFINE COUNT="$60"

PTR ARRAY code(COUNT)
BYTE
  PALNTSC=$D014,
  dotDuration,dashDuration,
  intraGapDuration,letterGapDuration,wordGapDuration

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

  IF PALNTSC=15 THEN
    dotDuration=6
  ELSE
    dotDuration=5
  FI
  dashDuration=2*dotDuration
  intraGapDuration=dotDuration
  letterGapDuration=3*intraGapDuration
  wordGapDuration=7*intraGapDuration
RETURN

PROC Wait(BYTE frames)
  BYTE RTCLOK=$14
  frames==+RTCLOK
  WHILE frames#RTCLOK DO OD
RETURN

PROC ProcessSound(CHAR ARRAY s BYTE last)
  BYTE i

  FOR i=1 TO s(0)
  DO
    Sound(0,30,10,10)
    IF s(i)='. THEN
      Wait(dotDuration)
    ELSE
      Wait(dashDuration)
    FI
    Sound(0,0,0,0)

    IF i<s(0) THEN
      Wait(intraGapDuration)
    FI
  OD
RETURN

PROC Process(CHAR ARRAY a)
  CHAR ARRAY seq,subs
  BYTE i,first,afterSpace
  CHAR c

  PrintE(a)
  first=1
  afterSpace=0
  FOR i=1 TO a(0)
  DO
    c=a(i)
    IF c>='a AND c<='z THEN
      c=c-'a+'A
    ELSEIF c>=COUNT THEN
      c=0
    FI
    seq=code(c)
    IF seq#0 THEN
      IF first=1 THEN
        first=0
      ELSE
        Put(' )
        IF afterSpace=0 THEN
          Wait(letterGapDuration)
        FI
      FI
      subs=code(c)
      Print(subs)
      ProcessSound(subs)
      afterSpace=0
    ELSEIF c='  THEN
      Print("  ")
      Wait(wordGapDuration)
      afterSpace=1
    ELSE
      afterSpace=0
    FI
  OD
  PutE() PutE()
  Wait(wordGapDuration)
RETURN

PROC Main()
  Init()
  Process("SOS")
  Process("Atari Action!")
  Process("www.rosettacode.org")
RETURN
Output:

Screenshot from Atari 8-bit computer

SOS
... --- ...

Atari Action!
.- - .- .-. ..   .- -.-. - .. --- -. -.-.--

www.rosettacode.org
.-- .-- .-- .-.-.- .-. --- ... . - - .- -.-. --- -.. . .-.-.- --- .-. --.

Ada

Conforms to Ada95. Works for Windows 32 XP , not for Vista since Beep is no longer effective.

Specification of the package :

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;
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;

Main program :

with Morse;            use Morse;
procedure Morse_Tx is
begin
   Morsebeep (Convert ("Science sans Conscience"));
end Morse_Tx;

AppleScript

This caters for non-diacritical English letters, numerals, and the punctuation found on the Wikipedia page and makes a burping noise for anything else. The morse sounds brisker and smoother if the script's run as an application or from Script Menu rather than in Script Editor or Script Debugger.

use AppleScript version "2.4" -- OS X 10.10 (Yosemite) or later
use framework "Foundation"
use framework "AppKit"

on morseCode(msg)
    script morse
        -- Unit duration in seconds and sounds used.
        property units : 0.075
        property morseSound : current application's class "NSSound"'s soundNamed:("Glass")
        property unknownCharacterSound : current application's class "NSSound"'s soundNamed:("Frog")
        -- Unicode IDs of in-range but uncatered-for punctuation characters.
        property unrecognisedPunctuation : {35, 37, 42, 60, 62, 91, 92, 93, 94}
        -- Dits and dahs for recognised characters, in units.
        property letters : {{1, 3}, {3, 1, 1, 1}, {3, 1, 3, 1}, {3, 1, 1}, {1}, {1, 1, 3, 1}, {3, 3, 1}, {1, 1, 1, 1}, {1, 1}, ¬
            {1, 3, 3, 3}, {3, 1, 3}, {1, 3, 1, 1}, {3, 3}, {3, 1}, {3, 3, 3}, {1, 3, 3, 1}, {3, 3, 1, 3}, {1, 3, 1}, {1, 1, 1}, ¬
            {3}, {1, 1, 3}, {1, 1, 1, 3}, {1, 3, 3}, {3, 1, 1, 3}, {3, 1, 3, 3}, {3, 3, 1, 1}}
        property underscore : {1, 1, 3, 3, 1, 3}
        property digitsAndPunctuation : {{3, 3, 3, 3, 3}, {1, 3, 3, 3, 3}, {1, 1, 3, 3, 3}, {1, 1, 1, 3, 3}, ¬
            {1, 1, 1, 1, 3}, {1, 1, 1, 1, 1}, {3, 1, 1, 1, 1}, {3, 3, 1, 1, 1}, {3, 3, 3, 1, 1}, {3, 3, 3, 3, 1}, ¬
            {3, 3, 3, 1, 1, 1}, {3, 1, 3, 1, 3, 1}, missing value, {3, 1, 1, 1, 3}, missing value, ¬
            {1, 1, 3, 3, 1, 1}, {1, 3, 3, 1, 3, 1}}
        property |punctuation| : {{3, 1, 3, 1, 3, 3}, {1, 3, 1, 1, 3, 1}, missing value, {1, 1, 1, 3, 1, 1, 3}, missing value, ¬
            {1, 3, 1, 1, 1}, {1, 3, 3, 3, 3, 1}, {3, 1, 3, 3, 1}, {3, 1, 3, 3, 1, 3}, missing value, ¬
            {1, 3, 1, 3, 1}, {3, 3, 1, 1, 3, 3}, {3, 1, 1, 1, 1, 3}, {1, 3, 1, 3, 1, 3}, {3, 1, 1, 3, 1}}
        -- Unicode IDs of the message's characters.
        property UnicodeIDs : (id of msg) as list
        
        on sendCharacter(ditsAndDahs)
            repeat with ditOrDah in ditsAndDahs
                tell morseSound to play()
                delay (ditOrDah * units)
                tell morseSound to stop()
                delay (1 * units)
            end repeat
            delay (2 * units) -- Previous 1 unit + 2 units = 3 units between characters.
        end sendCharacter
        
        on sendMessage()
            -- Play an extremely short sound to ensure the sound system's awake for the first morse beep.
            tell morse to sendCharacter({0})
            -- Output the message.
            repeat with i from 1 to (count UnicodeIDs)
                set thisID to item i of my UnicodeIDs
                if ((thisID > 122) or (thisID < 32) or (thisID is in unrecognisedPunctuation)) then
                    -- Character not catered for. Play alternative sound.
                    tell unknownCharacterSound to play()
                    delay (3 * units)
                    tell unknownCharacterSound to stop()
                    delay (3 * units)
                else if ((thisID > 64) and ((thisID < 91) or (thisID > 96))) then -- English letter.
                    sendCharacter(item (thisID mod 32) of my letters)
                else if (thisID is 95) then -- Underscore.
                    sendCharacter(underscore)
                else if (thisID > 47) then -- Digit, colon, semicolon, equals, or question mark.
                    sendCharacter(item (thisID - 47) of my digitsAndPunctuation)
                else if (thisID > 32) then -- Other recognised punctuation.
                    sendCharacter(item (thisID - 32) of my |punctuation|)
                else -- Space.
                    delay (4 * units) -- Previous 3 units + 4 units = 7 units between "words".
                end if
            end repeat
        end sendMessage
    end script
    
    tell morse to sendMessage()
end morseCode

-- Test code:
morseCode("Coded in AppleScrip†.")

Arturo

; set the morse code
 
code: #[
    ; letters
    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: "--.." 
    
    ; digits
    "0": "-----"
    "1": ".----"
    "2": "..---"
    "3": "...--"
    "4": "....-"
    "5": "....."
    "6": "-...."
    "7": "--..."
    "8": "---.."
    "9": "----."
]
 
; print an encoded message
 
str: "hello world 2019"
out: ""

loop split str 'ch [
    if not? whitespace? ch -> 'out ++ code\[ch]
    'out ++ " "
]
 
print out
Output:
.... . .-.. .-.. ---  .-- --- .-. .-.. -..  ..--- ----- .---- ----.

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. It uses the short form of if else that is implemented, to not have such a long script

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 using the more compact form of "if else", by var := x>y ? val1 : val2 which stores val1 in var if x > y, otherwise it stores val2, this can be used together to make a single line of if else
		morse .= A_LoopField = " " ?  "     " : A_LoopField = "a" ? ".- " : A_LoopField = "b" ? "-... " : A_LoopField = "c" ? "-.-. " : A_LoopField = "d" ? "-.. " : A_LoopField = "e" ? ". " : A_LoopField = "f" ? "..-. " : A_LoopField = "g" ? "--. " : A_LoopField = "h" ? ".... " : A_LoopField = "i" ? ".. " : A_LoopField = "j" ? ".--- " : A_LoopField = "k" ? "-.- " : A_LoopField = "l" ? ".-.. " : A_LoopField = "m" ? "-- " : A_LoopField = "n" ? "-. " : A_LoopField = "o" ? "--- " : A_LoopField = "p" ? ".--. " : A_LoopField = "q" ? "--.- " : A_LoopField = "r" ? ".-. " : A_LoopField = "s" ? "... " : A_LoopField = "t" ? "- " : A_LoopField = "u" ? "..- " : A_LoopField = "v" ? "...- " : A_LoopField = "w" ? ".-- " : A_LoopField = "x" ? "-..- " : A_LoopField = "y" ? "-.-- " : A_LoopField = "z" ? "--.. " : A_LoopField = "!" ? "---. " : A_LoopField = "\" ? ".-..-. " : A_LoopField = "$" ? "...-..- " : A_LoopField = "'" ? ".----. " : A_LoopField = "(" ? "-.--. " : A_LoopField = ")" ? "-.--.- " : A_LoopField = "+" ? ".-.-. " : A_LoopField = "," ? "--..-- " : A_LoopField = "-" ? "-....- " : A_LoopField = "." ? ".-.-.- " : A_LoopField = "/" ? "-..-. " : A_LoopField = "0" ? "----- " : A_LoopField = "1" ? ".---- " : A_LoopField = "2" ? "..--- " : A_LoopField = "3" ? "...-- " : A_LoopField = "4" ? "....- " : A_LoopField = "5" ? "..... " : A_LoopField = "6" ? "-.... " : A_LoopField = "7" ? "--... " : A_LoopField = "8" ? "---.. " : A_LoopField = "9" ? "----. " : A_LoopField = ":" ? "---... " : A_LoopField = ";" ? "-.-.-. " : A_LoopField = "=" ? "-...- " : A_LoopField = "?" ? "..--.. " : A_LoopField = "@" ? ".--.-. " : A_LoopField = "[" ? "-.--. " : A_LoopField = "]" ? "-.--.- " : A_LoopField = "_" ? "..--.- " : "ERROR" ; ---End conversion loop---
	Loop, Parse, morse
	{
		morsebeep := 120
		if (A_LoopField = ".")
			SoundBeep, 10*morsebeep, morsebeep		;Format: SoundBeep, frequency, duration
		If (A_LoopField = "-")
			SoundBeep, 10*morsebeep, 3*morsebeep	;Duration can be an expression
		If (A_LoopField = " ")
			Sleep, morsebeep					;Above, each character is followed by a space, and literal
	}									;Spaces are extended. Sleep pauses the script
	return morse								;Returns the text in morse code
} ;                                                         ---End Function Morse---

AWK

AWK cannot play sounds by itself, so here we just translate text to dits and dots:

# usage: awk -f morse.awk [inputfile]
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");
}
Output:
with input "sos sos titanic"
...|---|...||...|---|...||-|..|-|.-|-.|..|-.-.|

bash

IMC bash implementation

#!/bin/bash
# michaeltd	2019-11-29 https://github.com/michaeltd/dots/blob/master/dot.files/.bashrc.d/.var/morse.sh
# https://en.wikipedia.org/wiki/Morse_code
# International Morse Code
# 1. Length of dot is 1 unit
# 2. Length of dash is 3 units
# 3. The space between parts of the same letter is 1 unit
# 4. The space between letters is 3 units.
# 5. The space between words is 7 units.
################################################################################

alpha2morse() {
  local -A alpha_assoc=(    [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]='----.' )

  if [[ "${#}" -lt "1" ]]; then
     echo -ne "Usage: ${FUNCNAME[0]} arguments...\n \
        ${FUNCNAME[0]} is an IMC transmitter. \n \
        It'll transmit your messages to International Morse Code.\n" >&2
     return 1
  fi

  while [[ -n "${1}" ]]; do
        for (( i = 0; i < ${#1}; i++ )); do
            local letter="${1:${i}:1}"
            for (( y = 0; y < ${#alpha_assoc[${letter^^}]}; y++ )); do
                case "${alpha_assoc[${letter^^}]:${y}:1}" in
                     ".") echo -n "dot "; play -q -n -c2 synth .1 2> /dev/null || sleep .1 ;;
                     "-") echo -n "dash "; play -q -n -c2 synth .3 2> /dev/null || sleep .3 ;;
                esac
                sleep .1
            done
            echo
            sleep .3
        done
        echo
        sleep .7
        shift
  done
}

if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
   alpha2morse "${@}"
fi
Output:
(input
sos titanic sos):
./morse.sh sos titanic sos
dot dot dot
dash dash dash
dot dot dot

dash
dot dot
dash
dot dash
dash dot
dot dot
dash dot dash dot

dot dot dot
dash dash dash
dot dot dot

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.)

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
Output:
(2 runs)
 String to convert to Morse code: Rosetta Code
 .-. --- ... . - - .-   -.-. --- -.. .
 String to convert to Morse code: ~!@#$%^&*()_+
 # -.-.-- .--.-. # ...-..- # # .-... # -.--. -.--.- ..--.- .-.-.

BBC BASIC

      *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

Chipmunk Basic

Translation of: Yabasic
Works with: Chipmunk Basic version 3.6.4
10 rem Morse code
20 dim c$(54)
30 for f = 1 to 54
40  read l$ : read m$
50  d$ = d$+l$ : c$(f) = m$
60 next f
100 line input "Message? ";t$
105 t$ = ucase$(t$)
110 for f = 1 to len(t$)
120  p = instr(d$,mid$(t$,f,1))
130  if p > 0 then print c$(p); else print "?";
140 next f
150 print
160 goto 100
1000 data "A","._","B","_...","C","_._.","D","_..","E",".","F",".._."
1010 data "G","__.","H","....","I","..","J",".___","K","_._","L","._.."
1020 data "M","__","N","_.","O","___","P",".__.","Q","__._","R","._."
1030 data "S","...","T","_","U",".._","V","..._","W",".__","X","_.._"
1040 data "Y","_.__","Z","__..","1",".____","2","..___","3","...__"
1050 data "4","...._","5",".....","6","_....","7","__...","8","___.."
1060 data "9","____.","0","_____",".","._._._",",","__..__","?","..__.."
1070 data "'",".____.","!","_._.__","/","_.._.","(","_.__.",")","_.__._"
1080 data "&","._...",":","___...",";","_._._.","=","_..._","+","._._.","-","_...._"
1090 data "_","..__._","\","._.._.","$","..._.._","@",".__._."

IS-BASIC

100 PROGRAM "Morse.bas"
110 STRING TONE$(48 TO 90)*5,ST$*254
120 SET CHARACTER 46,0,0,0,0,24,24,0,0,0:SET CHARACTER 47,0,0,0,0,126,126,0,0,0
130 FOR I=48 TO 90
140   READ TONE$(I)
150 NEXT
160 DO
170   PRINT :PRINT "String to convert to Morse code: ":INPUT PROMPT ">":ST$
180   LET ST$=LTRIM$(RTRIM$(UCASE$(ST$)))
190   FOR I=1 TO LEN(ST$)
200     LET C=ORD(ST$(I:I))
210     IF C>47 AND C<91 THEN
220       PRINT TONE$(C);" ";
230       FOR J=1 TO LEN(TONE$(C))
240         SOUND PITCH 48,DURATION(ORD(TONE$(C)(J))-45)^3+4
250         SOUND PITCH 126,DURATION 8
260       NEXT 
270     ELSE
280       PRINT 
290       SOUND PITCH 126,DURATION 16
300     END IF
310     SOUND PITCH 126,DURATION 16
320   NEXT
330   PRINT
340 LOOP UNTIL ST$=""
350 CLEAR FONT
360 DATA .////,..///,...//,..../,.....,/....,//...,///..,////.,/////,"","","","","","",""
370 DATA ./,/...,/./.,/..,.,../.,//.,....,..,.///,/./,./..,//,/.,///,.//.,//./,./.,...,/,../,.../,.//,/../,/.//,//..

Commodore BASIC

Works with: Commodore BASIC version 7.0

This takes advantage of the PLAY statement in Commodore BASIC 7.0 on the C-128. Since we're in BASIC 7, it also uses some of its structured programming features, such as DO...LOOP and BEGIN...BEND.

100 DI$="IA" : REM DIT = EIGHTH NOTE
110 DA$="Q.A": REM DA = DOTTED QUARTER NOTE
120 IS$="IR" : REM SPACE BETWEEN  SYMS=1xDOT
130 IC$="QR" : REM EXTRA BETWEEN CHARS=2xDOT(TOTAL 3)
140 IW$="HR" : REM SPACE BETWEEN WORDS=4xDOT(TOTAL 7)
150 DIM MC$(127): REM READ CODE TABLE
160 DO
170 : READ C$
180 : IF C$="" THEN EXIT
190 : C=ASC(C$)
200 : READ C$
210 : MC$(C) = C$
220 LOOP
230 PRINT "ENTER MESSAGE:"
240 OPEN1,0:INPUT#1,M$:CLOSE1
250 TEMPO 128
260 P$="T7": REM ENVELOPE = ORGAN
270 FOR I=1 TO LEN(M$)
280 : A=ASC(MID$(M$,I))
290 : IF A=32 THEN BEGIN
300 :   P$=P$+IW$
310 : BEND:ELSE IF MC$(A)<>"" THEN BEGIN
320 :   C$=MC$(A)
330 :   FOR J=1 TO LEN(C$)
340 :     S$=MID$(C$,J,1)
350 :     IF S$="." THEN BEGIN
360 :       P$=P$+DI$
370 :     BEND:ELSE BEGIN
380 :       P$=P$+DA$
390 :     BEND
400 :     P$=P$+IS$
410 :   NEXT J
420 :   P$=P$+IC$
430 : BEND
440 NEXT I
450 PLAY P$
460 END
470 REM MORSE CODE TABLE DATA
500 DATA A,".-",   B,"-...", C,"-.-."
510 DATA D,"-..",  E,".",    F,"..-.",G,"--."
520 DATA H,"....", I,"..",   J,".---"
530 DATA K,"-.-",  L,".-..", M,"--",N,"-."
540 DATA O,"---",  P,".--.", Q,"--.-"
550 DATA R,".-.",  S,"...",  T,"-",U,"..-"
560 DATA V,"...-", W,".--",  X,"-..-"
570 DATA Y,"-.--", Z,"--.."
580 DATA 0,"-----",1,".----",2,"..---"
590 DATA 3,"...--",4,"....-",5,"....."
600 DATA 6,"-....",7,"--...",8,"---.."
610 DATA 9,"----."
620 DATA ".",".-.-.-", ",","--..--"
630 DATA "?","..--.."
640 DATA ""

Befunge

Since Befunge doesn't have a way to output sound, this implementation just generates a text representation of the morse. The string to convert is read from stdin.

>~>48*-:0\`#@_2*::"!"%\"!"/3+g75v
^v('_')v!:-*57g+3/"!"\%"!":+1\-*<
^$$,*84_\#!:#:2#-%#15#\9#/*#2+#,<
##X)P)##Z*##3(D)5);(##8(/)A)8)9(#
($(&(*(2(B(A(?(;(3([)M)##1(##V)L)
$%1'-')&$$.''&2'&%$'%&0'#%%%#&,''
'(&*&#$&&*'$&)'%'/'########6)##$%
1'-')&$$.''&2'&%$'%&0'#%%%#&,'''(
&*&#$&&*'$&)'%'/'################
Output:
Hello World!
.... . .-.. .-.. ---  .-- --- .-. .-.. -.. .-.-..

C

One could substitute another program for ubuntu beep command.

/*

  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.

 */

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#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;
}

C#

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);
            }
        }
    }
}

C++

/*
Michal Sikorski
06/07/2016
*/
#include <cstdlib>
#include <iostream>
#include <windows.h>
#include <string.h>
using namespace std;
int main(int argc, char *argv[])
{
    string inpt;
    char ascii[28] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ", lwcAscii[28] = " abcdefghijklmnopqrstuvwxyz";
    string morse[27] = {"  ", ".- ", "-... ", "-.-. ", "-.. ", ". ", "..-. ", "--. ", ".... ", ".. ", ".--- ", "-.- ", ".-.. ", "-- ", "-. ", "--- ", ".--.", "--.- ", ".-. ", "... ", "- ", "..- ", "...- ", ".-- ", "-..- ", "-.-- ", "--.. "};
    string outpt;
    getline(cin,inpt);
    int xx=0;
    int size = inpt.length();
    cout<<"Length:"<<size<<endl;
   
    xx=0;
    while(xx<inpt.length())
    {
                         int x=0;
                         bool working = false;
                         while(!working)
                         {
                                  if(ascii[x] != inpt[xx]&&lwcAscii[x] != inpt[xx])
                                  {      
                                        x++;
                                  }
                                  else
                                  {
                                        working = !working;
                                  }
                         }
                         
                         cout<<morse[x];
                         outpt = outpt + morse[x];
                         xx++;
    }
    
    xx=0;
    while(xx<outpt.length()+1)
    {
                         if(outpt[xx] == '.')
                         {
                                   Beep(1000,250);   
                         }
                         else
                         {
                             if(outpt[xx] == '-')
                             {
                                          Beep(1000,500);
                             }
                             else
                             {
                                 if(outpt[xx] == ' ')
                                 {
                                              Sleep(500);
                                 }   
                             }    
                         }
                         xx++; 
    }
    system("PAUSE");
    return EXIT_SUCCESS;
}

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 ,)))

CoffeeScript

class Morse
  constructor : (@unit=0.05, @freq=700) ->

    @cont = new AudioContext()
    @time = @cont.currentTime
    @alfabet = "..etianmsurwdkgohvf.l.pjbxcyzq..54.3...2.......16.......7...8.90"

  getCode : (letter) ->
    i = @alfabet.indexOf letter
    result = ""
    while i > 1
      result = ".-"[i%2] + result
      i //= 2
    result

  makecode : (data) ->
    for letter in data
      code = @getCode letter
      if code != undefined then @maketime code else @time += @unit * 7

  maketime : (data) ->
    for timedata in data
      timedata = @unit * ' . _'.indexOf timedata
      if timedata > 0
        @maketone timedata
        @time += timedata
        @time += @unit * 1
    @time += @unit * 2

  maketone : (data) ->
    start = @time
    stop = @time + data
    @gain.gain.linearRampToValueAtTime 0, start
    @gain.gain.linearRampToValueAtTime 1, start + @unit / 8
    @gain.gain.linearRampToValueAtTime 1, stop - @unit / 16
    @gain.gain.linearRampToValueAtTime 0, stop

  send : (text) -> 
    osci = @cont.createOscillator()
    osci.frequency.value = @freq
    @gain = @cont.createGain()
    @gain.gain.value = 0
    osci.connect @gain
    @gain.connect @cont.destination

    osci.start @time
    @makecode text
    @cont

morse = new Morse()
morse.send 'hello world 0123456789'

D

import std.conv;
import std.stdio;

immutable string[char] morsecode;

static this() {
    morsecode = [
        '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': "----."
    ];
}

void main(string[] args) {
    foreach (arg; args[1..$]) {
        writeln(arg);
        foreach (ch; arg) {
            if (ch in morsecode) {
                write(morsecode[ch]);
            }
            write(' ');
        }
        writeln();
    }
}

Delphi

program Morse;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.Generics.Collections,
  SysUtils,
  Windows;

const
  Codes: array[0..35, 0..1] of 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', '----.'));
var
  Dictionary: TDictionary<String, String>;

procedure InitCodes;
var
  i: Integer;
begin
  for i := 0 to High(Codes) do
    Dictionary.Add(Codes[i, 0], Codes[i, 1]);
end;

procedure SayMorse(const Word: String);
var
  s: String;
begin
  for s in Word do
    if s = '.' then
      Windows.Beep(1000, 250)
    else if s = '-' then
      Windows.Beep(1000, 750)
    else
      Windows.Beep(1000, 1000);
end;

procedure ParseMorse(const Word: String);
var
  s, Value: String;
begin
  for s in word do
    if Dictionary.TryGetValue(s, Value) then
    begin
      Write(Value + ' ');
      SayMorse(Value);
    end;
end;

begin
  Dictionary := TDictionary<String, String>.Create;
  try
    InitCodes;
    if ParamCount = 0 then
      ParseMorse('sos')
    else if ParamCount = 1 then
      ParseMorse(LowerCase(ParamStr(1)))
    else
      Writeln('Usage: Morse.exe anyword');

    Readln;
  finally
    Dictionary.Free;
  end;
end.

EasyLang

txt$ = "sos sos"
# 
chars$[] = strchars "abcdefghijklmnopqrstuvwxyz "
code$[] = [ ".-" "-..." "-.-." "-.." "." "..-." "--." "...." ".." ".---" "-.-" ".-.." "--" "-." "---" ".--." "--.-" ".-." "..." "-" "..-" "...-" ".--" "-..-" "-.--" "--.." " " ]
# 
proc morse ch$ . .
   ind = 1
   while ind <= len chars$[] and chars$[ind] <> ch$
      ind += 1
   .
   if ind <= len chars$[]
      write ch$ & " "
      sleep 0.4
      for c$ in strchars code$[ind]
         write c$
         if c$ = "."
            sound [ 440 0.2 ]
            sleep 0.4
         elif c$ = "-"
            sound [ 440 0.6 ]
            sleep 0.8
         elif c$ = " "
            sleep 0.8
         .
      .
      print ""
   .
.
for ch$ in strchars txt$
   morse ch$
.

EchoLisp

(require 'json)
(require 'hash)
(require 'timer)

;; json table from RUBY
(define morse-alphabet 
#'{"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":"--..","[":"-.--.","]":"-.--.-","_":"..--.-"," ":"|"}'#)

(define MORSE (json->hash (string->json morse-alphabet)))

;; translates a string into morse string
;; use "|" as letters separator
(define (string->morse str morse)
(apply append (map string->list
	(for/list [(a (string-diacritics str))]
		(string-append
		(or (hash-ref morse (string-upcase a)) "?") "|")))))
	
(define (play-morse)
	(when EMIT ;; else return #f which stops (at-every)
		(case  (first EMIT)
		((".") (play-sound 'digit) (write "dot"))
		(("-") (play-sound 'tick) (write "dash"))
		(else (writeln) (blink)))
		(set! EMIT (rest EMIT))))
Output:
(define EMIT (string->morse "Longtemps je me suis couché de bônne heure" MORSE))

    → ("." "-" "." "." "|" "-"  "." "-" "-" "." " [ ... ] "-" "." "|" "-" "." "|" "." "|" "|" "|" "." 
"." "." "." "|" "." "|" "." "." "-" "|" "." "-" "." "|" "." "|")

;; speakers ON
(at-every 300 'play-morse)

Elixir

defmodule Morse do
  @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" => "--..",
           "[" => "-.--.",      "]" => "-.--.-",     "_" => "..--.-" }
  def code(text) do
    String.upcase(text)
    |> String.codepoints
    |> Enum.map_join(" ", fn c -> Map.get(@morse, c, " ") end)
  end
end

IO.puts Morse.code("Hello, World!")
Output:
.... . .-.. .-.. --- --..--   .-- --- .-. .-.. -.. ---.

F#

open System
open System.Threading

let morse = Map.ofList
                [('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 beep c =
    match c with
    | '.' ->
        printf "."
        Console.Beep(1200, 250)
    | '_' ->
        printf "_"
        Console.Beep(1200, 1000)
    | _ ->
        printf " "
        Thread.Sleep(125)

let trim (s: string) = s.Trim()
let toMorse c = Map.find c morse
let lower (s: string) = s.ToLower()
let sanitize = String.filter Char.IsLetterOrDigit

let send = sanitize >> lower >> String.collect toMorse >> trim >> String.iter beep

send "Rosetta Code"
Output:
._. ___ ... . _ _ ._ _._. ___ _.. .

Factor

USE: morse
"Hello world!" play-as-morse

Forth

Starting with basic I/O operations, this demonstration creates a simple hardware driver for the PC speaker to generate tones. The Morse code generator could have taken the form of a look-up array with bit patterns cross referencing dits and dahs, but we instead used the Forth dictionary, which is functionally like a big case statement. By making each letter an executable routine in Forth we get a very succinct way to code our Morse. Proper sounding morse code requires precise time delays between the dits, dahs, letters and words. The program uses the Forth word "ms" which delays for n milliseconds and gives us real time control. This morse code sounds right. There is a lot accomplished in 75 lines of code for a "low level" language.

HEX
\ PC speaker hardware control (requires GIVEIO or DOSBOX for windows operation)
 042 constant fctrl
 043 constant tctrl
 061 constant sctrl
 0FC constant smask

\ PC@ is Port char fetch (Intel IN instruction).  PC! is port char store (Intel OUT instruction)
: speak     ( -- ) sctrl pc@               03 or  sctrl pc! ;
: silence   ( -- ) sctrl pc@   smask and   01 or  sctrl pc! ;

: tone     ( freq -- )                 \ freq is actually just a divisor value
            ?dup                       \ check for non-zero input
            if   0B6 tctrl pc!         \ enable PC speaker
                 dup fctrl pc!         \ set freq
                 8 rshift fctrl pc!
                 speak
            else
                 silence
            then ;

\ morse demonstration begins here
DECIMAL
1000 value freq                        \ arbitrary value that sounded ok
  90 value adit                        \ 1 dit will be 90 ms

: dit_dur      adit ms ;
: dah_dur      adit 3 * ms ;
: wordgap      adit 5 * ms ;
: off_dur      adit 2/ ms ;
: lettergap    dah_dur ;

: sound ( -- ) freq tone ;

: MORSE-EMIT  ( char -- )
        dup  bl =                      \ check for space character
        if
             wordgap drop              \ and delay if detected
        else
             pad C!                    \ write char to buffer
             pad 1 evaluate            \ evaluate 1 character
             lettergap                 \ pause for correct sounding morse code
        then ;

: TRANSMIT ( ADDR LEN -- )
           cr                          \ newline,
           bounds                      \ convert loop indices to address ranges
           do
              I C@ dup emit            \ dup and send char to console
              morse-emit               \ send the morse code
           loop ;

VOCABULARY MORSE                       \ prevent name conflicts with letters and numbers

MORSE DEFINITIONS                      \ the following definitions go into MORSE namespace
: .   ( -- ) sound  dit_dur  silence off_dur ;
: -   ( -- ) sound  dah_dur  silence off_dur ;

\ define morse letters as Forth words. They transmit when executed

: 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  - - - - . ;

: '  - . . - . ;     
: \  . - - - . ;    
: !  . - . - . ;
: ?  . . - - . . ;    
: ,  - - . . - - ;
: /   - . . - . ;     
: .  . - . - . - ;

 PREVIOUS DEFINITIONS                   \ go back to previous namespace
: TRANSMIT   MORSE  TRANSMIT  PREVIOUS ;

Test at the Forth console

S" CQ CQ CQ DE VE3CFW VE3CFW  K " TRANSMIT 
CQ CQ CQ DE VE3CFW VE3CFW  K  ok
MORSE  A B C D E F G  PREVIOUS ok

FreeBASIC

' FB 1.05.0 Win64

' Using Beep function in Win32 API
Dim As Any Ptr library = DyLibLoad("kernel32")
Dim Shared beep_ As Function (ByVal As ULong, ByVal As ULong) As Long
beep_ = DyLibSymbol(library, "Beep")

Sub playMorse(m As String)
  For i As Integer = 0 To Len(m) - 1
    If m[i] = 46 Then '' ascii code for dot
      beep_(1000, 250)
    Else '' must be ascii code for dash (45)
      beep_(1000, 750)
    End If
    Sleep 50
  Next
  Sleep 150
End Sub

Dim morse(0 To 35) As 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
}

Dim s As String = "The quick brown fox"
For i As Integer = 0 To Len(s) -1
  Select Case As Const s[i]
    Case 65 To 90  '' A - Z
      playMorse(morse(s[i] - 65))
    Case 97 To 122 '' a - z
      playMorse(morse(s[i] - 97))
    Case 48 To 57  '' 0 - 9
       playMorse(morse(s[i] - 22))
    Case Else
       '' ignore any other character
       Sleep 250
  End Select
Next

DyLibFree(library)
End


FutureBasic

When compiled with FB, this code produces a packaged, stand-alone 64-bit application that will run on either Intel or the newer M-series Macs. It translates a text string into Morse Code and plays back characters along with their respective sounds. The oscillator frequency and volume, along with letter and word spacing, can be adjusted with the definitions at the head of the file. While the code meets the Rosetta Code parameters, it's by no means exhaustive. For instance, it does not include translation of punctuation or prosigns, and the GUI is minimal. It's simply a proof of concept. It's been tested on Catalina (10.15.x) to Monterey (12.4.x) with Ventura still in beta at the time of this post. The free FB compiler is available on the FutureBasic home page.

include "NSLog.incl"
include "Tlbx AVKit.incl"

#define VOLUME         0.2
#define HZ_FREQUENCY 440.0
_ditDelay = 100
_dahDelay = 200
_spcDelay = 300


void local fn PlayCodeDone( ref as AVAudioPlayerNodeRef, userData as ptr )
  NSLog(@"%@\b",userData)
end fn


local fn PlayFrequency( frequency as float, amplitude as float, character as CFStringRef ) as AVAudioPlayerNodeRef
  static AVAudioEngineRef audioEngine
  
  audioEngine = fn AVAudioEngineInit
  AVAudioPlayerNodeRef player = fn AVAudioPlayerNodeInit
  
  AVAudioMixerNodeRef           mixer = fn AVAudioEngineMainMixerNode( audioEngine )
  float                    sampleRate = fn AVAudioFormatSampleRate( fn AVAudioNodeOutputFormatForBus( mixer, 0 ) )
  AVAudioFrameCount frameBufferLength = fn floorf( sampleRate / frequency ) * 1
  AVAudioPCMBufferRef          buffer = fn AVAudioPCMBufferWithFormat( fn AVAudioNodeOutputFormatForBus( player, 0 ), frameBufferLength )
  AVAudioPCMBufferSetFrameLength( buffer, frameBufferLength )
  NSInteger              channelCount = fn AVAudioFormatChannelCount( fn AVAudioNodeOutputFormatForBus( mixer, 0 ) )
  AVAudioFrameCount       frameLength = fn AVAudioPCMBufferFrameLength( buffer )
  
  ptr p = (ptr)fn AVAudioPCMBufferFloatChannelData(buffer)
  xref floatChannelData(100) as ^float
  floatChannelData = p
  
  long  i, channelNumber
  float theta, value
  ^float channelBuffer
  
  for i = 0 to frameLength - 1
    theta = frequency * i * 2.0 * M_PI / sampleRate
    value = fn sinf(theta)
    for channelNumber = 0 to channelCount - 1
      channelBuffer = floatChannelData(channelNumber)
      cln channelBuffer[i] = value * amplitude;
    next
  next
  
  AVAudioEngineAttachNode( audioEngine, player )
  AVAudioEngineConnect(    audioEngine, player, mixer, fn AVAudioNodeOutputFormatForBus( player, 0 ) )
  fn AVAudioEngineStart(   audioEngine, NULL )
  AVAudioPlayerNodePlay( player )
  AVAudioPlayerNodeScheduleBufferAtTime( player, buffer, NULL, AVAudioPlayerNodeBufferLoops, @fn PlayCodeDone, (ptr)character )
end fn = player


void local fn PlayMorseCode( play as BOOL, character as CFStringRef )
  static AVAudioPlayerNodeRef player
  
  select ( play )
    case YES : player = fn PlayFrequency( HZ_FREQUENCY, VOLUME, character )
    case NO  : AVAudioPlayerNodeStop( player )  // : AppRemoveAllProperties
  end select
end fn


local fn ParseCodeAndPlay( character as CFStringRef )
  dispatchmain
    if fn StringIsEqual( character, @"."  ) then timerbegin : fn PlayMorseCode( YES, character ) : delay _ditDelay : fn PlayMorseCode( NO, NULL ) : timerend
    if fn StringIsEqual( character, @"-"  ) then timerbegin : fn PlayMorseCode( YES, character ) : delay _dahDelay : fn PlayMorseCode( NO, NULL ) : timerend
    if fn StringIsEqual( character, @" "  ) then timerbegin : NSLog(@" \b") : delay _spcDelay : timerend
    if fn StringIsEqual( character, @"\n" ) then timerbegin : NSLog(@"") : timerend
  dispatchend
end fn


local fn MorseCharacter( letter as CFStringRef ) as CFStringRef
  CFDictionaryRef morseDict = @{
  @"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":@"----."}
end fn = morseDict[letter]


local fn BuildMorseString( asciiStr as CFStringRef ) as CFStringRef
  NSInteger i
  
  CFStringRef processStr = fn StringLowerCaseString( asciiStr )
  CFMutableStringRef mutStr = fn MutableStringWithCapacity(0)
  
  NSInteger length = len( processStr )
  for i = 0 to length - 1
    CFStringRef temp = mid( processStr, i, 1 )
    select ( temp )
      case @"\n", @"\r"
        MutableStringAppendString( mutStr, @"\n" )
      case else
        CFStringRef code = fn MorseCharacter( temp )
        if ( code != NULL )
          MutableStringAppendString( mutStr, fn StringByAppendingString( code, @" " ) )
        else
          MutableStringAppendString( mutStr, @" " )
        end if
    end select
  next
end fn = fn StringWithString( mutStr )


local fn MorseCodeAudio( morseStr as CFStringRef )
  NSInteger i
  
  NSInteger length = len( morseStr )
  for i = 0 to length - 1
    CFStringRef temp = mid( morseStr, i, 1 )
    fn ParseCodeAndPlay( temp )
  next
end fn


local fn OutputMorseCode( morseStr as CFStringRef )
  NSLog( @"\nSample ham radio Morse Code transmission:\n\n%@", morseStr )
  CFStringRef codeStr = fn BuildMorseString( morseStr )
  NSLog( @"\n\nMorse Code:\n\n%@", codeStr )
  
  NSLog( @"\n\nAudio playing...\n" )
  
  dispatchglobal
    fn MorseCodeAudio( codeStr )
  dispatchend
end fn

CFStringRef morseStr = @"CQ CQ CQ DE K1XYZ K1XYZ K TRANSMIT K1XYZ DE W1ZZZ TKS FOR CALL UR RST 479 479 NAME KEN KEN QTH NR KENTUCKY KENTUCKY G3ZZY DE W1ZZZ K"

fn OutputMorseCode( morseStr )

HandleEvents
Output:
Sample ham radio Morse Code transmission:

CQ CQ CQ DE K1XYZ K1XYZ K TRANSMIT K1XYZ DE W1ZZZ TKS FOR CALL UR RST 479 479 NAME KEN KEN QTH NR KENTUCKY KENTUCKY K1XYZ DE W1ZZZ K


Morse Code:

-.-. --.-  -.-. --.-  -.-. --.-  -.. .  -.- .---- -..- -.-- --..  -.- .---- -..- -.-- --..  -.-  - .-. .- -. ... -- .. -  -.- .---- -..- -.-- --..  -.. .  .-- .---- --.. --.. --..  - -.- ...  ..-. --- .-.  -.-. .- .-.. .-..  ..- .-.  .-. ... -  ....- --... ----.  ....- --... ----.  -. .- -- .  -.- . -.  -.- . -.  --.- - ....  -. .-.  -.- . -. - ..- -.-. -.- -.--  -.- . -. - ..- -.-. -.- -.--  --. ...-- --.. --.. -.--  -.. .  .-- .---- --.. --.. --..  -.- 


Audio playing...

-.-. --.-  -.-. --.-  -.-. --.-  -.. .  -.- .---- -..- -.

Gambas

'Requires component 'gb.sdl2.audio'

Public Sub Main()
Dim sMessage As String = "Hello World"
Dim sFile As String = File.Load("../morse.txt") 'Contains A,.- B,-... etc
Dim sChar As New String[]
Dim sMorse As New String[]
Dim sOutPut As New String[]
Dim sTemp As String
Dim siCount, siLoop As Short
Dim bTrigger As Boolean
Dim fDelay As Float = 0.4

If Not Exist("/tmp/dot.ogg") Then Copy "../dot.ogg" To "/tmp/dot.ogg"     'Sounds downloaded from 
If Not Exist("/tmp/dash.ogg") Then Copy "../dash.ogg" To "/tmp/dash.ogg"  'https://en.wikipedia.org/wiki/Morse_code#Letters.2C_numbers.2C_punctuation.2C_prosigns_for_Morse_code_and_non-English_variants

For Each sTemp In Split(sFile, gb.NewLine)
  sChar.add(Split(Trim(sTemp))[0])
  sMorse.add(Split(Trim(sTemp))[1])
Next

For siCount = 1 To Len(sMessage)
  For siLoop = 0 To sChar.Max
    If sChar[siLoop] = Mid(UCase(sMessage), siCount, 1) Then
      sOutPut.Add(sMorse[siLoop])
      bTrigger = True
      Break
    End If 
  Next
  If bTrigger = False Then sOutPut.Add(" ")
  bTrigger = False
Next

Print sOutPut.Join(" ")

For siCount = 0 To Len(sMessage) - 1
  For siLoop = 0 To Len(sOutPut[siCount]) - 1
    If Mid(sOutPut[siCount], siLoop + 1, 1) = "." Then
      Music.Load("/tmp/dot.ogg")
      Music.Play
      Wait fDelay
    Else If Mid(sOutPut[siCount], siLoop + 1, 1) = "-" Then
      Music.Load("/tmp/dash.ogg")
      Music.Play 
      Wait fDelay
    Else
      Wait fDelay
    Endif
  Next
Next

End

Output (plus sound):

.... . .-.. .-.. ---   .-- --- .-. .-.. -..

Go

// Command morse translates an input string into morse code,
// showing the output on the console, and playing it as sound.
// Only works on ubuntu.
package main

import (
	"flag"
	"fmt"
	"log"
	"regexp"
	"strings"
	"syscall"
	"time"
	"unicode"
)

// A key represents an action on the morse key.
// It's either on or off, for the given duration.
type key struct {
	duration int
	on       bool
	sym      string // for debug output
}

var (
	runeToKeys   = map[rune][]key{}
	interCharGap = []key{{1, false, ""}}
	punctGap     = []key{{7, false, " / "}}
	charGap      = []key{{3, false, " "}}
	wordGap      = []key{{7, false, " / "}}
)

const rawMorse = `
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:----.  &:.-...   @:.--.-.
`

func init() {
	// Convert the rawMorse table into a map of morse key actions.
	r := regexp.MustCompile("([^ ]):([.-]+)")
	for _, m := range r.FindAllStringSubmatch(rawMorse, -1) {
		c := m[1][0]
		keys := []key{}
		for i, dd := range m[2] {
			if i > 0 {
				keys = append(keys, interCharGap...)
			}
			if dd == '.' {
				keys = append(keys, key{1, true, "."})
			} else if dd == '-' {
				keys = append(keys, key{3, true, "-"})
			} else {
				log.Fatalf("found %c in morse for %c", dd, c)
			}
			runeToKeys[rune(c)] = keys
			runeToKeys[unicode.ToLower(rune(c))] = keys
		}
	}
}

// MorseKeys translates an input string into a series of keys.
func MorseKeys(in string) ([]key, error) {
	afterWord := false
	afterChar := false
	result := []key{}
	for _, c := range in {
		if unicode.IsSpace(c) {
			afterWord = true
			continue
		}
		morse, ok := runeToKeys[c]
		if !ok {
			return nil, fmt.Errorf("can't translate %c to morse", c)
		}
		if unicode.IsPunct(c) && afterChar {
			result = append(result, punctGap...)
		} else if afterWord {
			result = append(result, wordGap...)
		} else if afterChar {
			result = append(result, charGap...)
		}
		result = append(result, morse...)
		afterChar = true
		afterWord = false
	}
	return result, nil
}

func main() {
	var ditDuration time.Duration
	flag.DurationVar(&ditDuration, "d", 40*time.Millisecond, "length of dit")
	flag.Parse()
	in := "hello world."
	if len(flag.Args()) > 1 {
		in = strings.Join(flag.Args(), " ")
	}
	keys, err := MorseKeys(in)
	if err != nil {
		log.Fatalf("failed to translate: %s", err)
	}
	for _, k := range keys {
		if k.on {
			if err := note(true); err != nil {
				log.Fatalf("failed to play note: %s", err)
			}
		}
		fmt.Print(k.sym)
		time.Sleep(ditDuration * time.Duration(k.duration))
		if k.on {
			if err := note(false); err != nil {
				log.Fatalf("failed to stop note: %s", err)
			}
		}
	}
	fmt.Println()
}

// Implement sound on ubuntu. Needs permission to access /dev/console.

var consoleFD uintptr

func init() {
	fd, err := syscall.Open("/dev/console", syscall.O_WRONLY, 0)
	if err != nil {
		log.Fatalf("failed to get console device: %s", err)
	}
	consoleFD = uintptr(fd)
}

const KIOCSOUND = 0x4B2F
const clockTickRate = 1193180
const freqHz = 600

// note either starts or stops a note.
func note(on bool) error {
	arg := uintptr(0)
	if on {
		arg = clockTickRate / freqHz
	}
	_, _, errno := syscall.Syscall(syscall.SYS_IOCTL, consoleFD, KIOCSOUND, arg)
	if errno != 0 {
		return errno
	}
	return nil

}

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.

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

The module to convert text to Morse code symbols.

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)

The module to interpret Morse code symbols as sound.

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

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))

Example use:

   morse'this is an example'
- .... .. ...   .. ...   .- -.   . -..- .- -- .--. .-.. .
   playmorse'this is an example'
1
   12 playmorse'as is this'
1

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.

Java

Works with: java version 7
import java.util.*;

public class MorseCode {

    final static String[][] code = {
        {"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", "----.  "},
        {"'", ".----. "}, {":", "---... "}, {",", "--..-- "}, {"-", "-....- "},
        {"(", "-.--.- "}, {".", ".-.-.- "}, {"?", "..--.. "}, {";", "-.-.-. "},
        {"/", "-..-.  "}, {"-", "..--.- "}, {")", "---..  "}, {"=", "-...-  "},
        {"@", ".--.-. "}, {"\"", ".-..-."}, {"+", ".-.-.  "}, {" ", "/"}}; // cheat a little

    final static Map<Character, String> map = new HashMap<>();

    static {
        for (String[] pair : code)
            map.put(pair[0].charAt(0), pair[1].trim());
    }

    public static void main(String[] args) {
        printMorse("sos");
        printMorse("   Hello     World!");
        printMorse("Rosetta Code");
    }

    static void printMorse(String input) {
        System.out.printf("%s %n", input);

        input = input.trim().replaceAll("[ ]+", " ").toUpperCase();
        for (char c : input.toCharArray()) {
            String s = map.get(c);
            if (s != null)
                System.out.printf("%s ", s);
        }
        System.out.println("\n");
    }
}
sos 
... --- ... 

   Hello     World! 
.... . .-.. .-.. --- / .-   - --- .-. .-.. -.. 

Rosetta Code 
.-. --- ... . - - .- / -.-. --- -.. . 

JavaScript

This implementation utilises the fairly new Web Audio API in the browser for generating tones, as such it only uses one vendor implementation (WebKit). It is split into three modules; 1. translating the characters into morse code. 2. creating timings for the morse code. 3. creating tones with the timings.

var globalAudioContext = new webkitAudioContext();

function morsecode(text, unit, freq) {
	'use strict';

	// defaults
	unit = unit ? unit : 0.05;
	freq = freq ? freq : 700;
	var cont = globalAudioContext;
	var time = cont.currentTime;

	// morsecode
	var code = {
		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: '____.'
	};

	// generate code for text
	function makecode(data) {
		for (var i = 0; i <= data.length; i ++) {
			var codedata = data.substr(i, 1).toLowerCase();
			codedata = code[codedata];
			// recognised character
			if (codedata !== undefined) {
				maketime(codedata);
			}
			// unrecognised character
			else {
				time += unit * 7;
			}
		}
	}

	// generate time for code
	function maketime(data) {
		for (var i = 0; i <= data.length; i ++) {
			var timedata = data.substr(i, 1);
			timedata = (timedata === '.') ? 1 : (timedata === '_') ? 3 : 0;
			timedata *= unit;
			if (timedata > 0) {
				maketone(timedata);
				time += timedata;
				// tone gap
				time += unit * 1;
			}
		}
		// char gap
		time += unit * 2;
	}

	// generate tone for time
	function maketone(data) {
		var start = time;
		var stop = time + data;
		// filter: envelope the tone slightly
		gain.gain.linearRampToValueAtTime(0, start);
		gain.gain.linearRampToValueAtTime(1, start + (unit / 8));
		gain.gain.linearRampToValueAtTime(1, stop - (unit / 16));
		gain.gain.linearRampToValueAtTime(0, stop);
	}

	// create: oscillator, gain, destination
	var osci = cont.createOscillator();
	osci.frequency.value = freq;
	var gain = cont.createGainNode();
	gain.gain.value = 0;
	var dest = cont.destination;
	// connect: oscillator -> gain -> destination
	osci.connect(gain);
	gain.connect(dest);
	// start oscillator
	osci.start(time);

	// begin encoding: text -> code -> time -> tone
	makecode(text);

	// return web audio context for reuse / control
	return cont;
}

Usage:

morsecode('Hello World');
Output:

Live Version

Julia

Requires a sound card and the PortAudio libraries.

using PortAudio

const pstream = PortAudioStream(0, 2)
sendmorsesound(t, f) = write(pstream, SinSource(eltype(stream), samplerate(stream)*0.8, [f]), (t/1000)s)

char2morse = Dict[
      "!" => "---.", "\"" => ".-..-.", "$" => "...-..-", "'" => ".----.",
      "(" => "-.--.", ")" => "-.--.-", "+" => ".-.-.", "," => "--..--",
      "-" => "-....-", "." => ".-.-.-", "/" => "-..-.", "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" => "--..",
      "[" => "-.--.", "]" => "-.--.-", "_" => "..--.-"]
 
function sendmorsesound(freq, duration)
cpause() = sleep(0.080)
wpause = sleep(0.400)

dit() = sendmorsesound(0.070, 700)
dash() = sensmorsesound(0.210, 700)
sendmorsechar(c) = for d in char2morse(c) d == '.' ? dit(): dash() end end
sendmorseword(w) = for c in w sendmorsechar(c) cpause() end wpause() end
sendmorse(msg) = for word in uppercase(msg) sendmorseword(word) end

sendmorse("sos sos sos")
sendmorse("The case of letters in Morse coding is ignored."

Kotlin

Java does not have easy access to the beep method, so we need to create one using the Audio API it provides.

import javax.sound.sampled.AudioFormat
import javax.sound.sampled.AudioSystem

val morseCode = hashMapOf(
        'a' to ".-", 'b' to "-...", 'c' to "-.-.",
        'd' to "-..", 'e' to ".", 'f' to "..-.",
        'g' to "--.", 'h' to "....", 'i' to "..",
        'j' to ".---", 'k' to "-.-", 'l' to ".-..",
        'm' to "--", 'n' to "-.", 'o' to "---",
        'p' to ".--.", 'q' to "--.-", 'r' to ".-.",
        's' to "...", 't' to "-", 'u' to "..-",
        'v' to "...-", 'w' to ".--", 'x' to "-..-",
        'y' to "-.--", 'z' to "--..",

        '0' to ".....", '1' to "-....", '2' to "--...",
        '3' to "---..", '4' to "----.", '5' to "-----",
        '6' to ".----", '7' to "..---", '8' to "...--",
        '9' to "....-",

        ' ' to "/", ',' to "--..--", '!' to "-.-.--",
        '"' to ".-..-.", '.' to ".-.-.-", '?' to "..--..",
        '\'' to ".----.", '/' to "-..-.", '-' to "-....-",
        '(' to "-.--.-", ')' to "-.--.-"
)

val symbolDurationInMs = hashMapOf('.' to 200, '-' to 500, '/' to 1000)


fun toMorseCode(message: String) = message.filter { morseCode.containsKey(it) }
                                          .fold("") { acc, ch -> acc + morseCode[ch]!! }

fun playMorseCode(morseCode: String) = morseCode.forEach { symbol -> beep(symbolDurationInMs[symbol]!!) }

fun beep(durationInMs: Int) {
    val soundBuffer = ByteArray(durationInMs * 8)
    for ((i, _) in soundBuffer.withIndex()) {
        soundBuffer[i] = (Math.sin(i / 8.0 * 2.0 * Math.PI) * 80.0).toByte()
    }

    val audioFormat = AudioFormat(
            /*sampleRate*/ 8000F,
            /*sampleSizeInBits*/ 8,
            /*channels*/ 1,
            /*signed*/ true,
            /*bigEndian*/ false
    )
    with (AudioSystem.getSourceDataLine(audioFormat)!!) {
        open(audioFormat)

        start()
        write(soundBuffer, 0, soundBuffer.size)
        drain()

        close()
    }
}

fun main(args: Array<String>) {
    args.forEach {
        playMorseCode(toMorseCode(it.toLowerCase()))
    }
}

Liberty BASIC

'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

Lua

The following code is actual eLua code used to beep the speaker in Morse code n the Shenzhou III STM32F103ZET6 evaluation board. eLua is a Lua 5.1.4 implementation paired with libraries for low-level hardware access in embedded systems. The below code could easily be converted to any other Lua 5.n environment (including games), provided some kind of sound library has been installed. Only the functions buzz and pause would have to be modified.

local M = {}

-- module-local variables
local BUZZER = pio.PB_10
local dit_length, dah_length, word_length

-- module-local functions
local buzz, dah, dit, init, inter_element_gap, medium_gap, pause, sequence, short_gap

buzz = function(duration)
  pio.pin.output(BUZZER)
  pio.pin.setlow(BUZZER)
  tmr.delay(tmr.SYS_TIMER, duration)
  pio.pin.sethigh(BUZZER)
  pio.pin.input(BUZZER)
end

dah = function()
  buzz(dah_length)
end

dit = function()
  buzz(dit_length)
end

init = function(baseline)
  dit_length = baseline
  dah_length = 2 * baseline
  word_length = 4 * baseline
end

inter_element_gap = function()
  pause(dit_length)
end

medium_gap = function()
  pause(word_length)
end

pause = function(duration)
  tmr.delay(tmr.SYS_TIMER, duration)
end

sequence = function(codes)
  if codes then
    for _,f in ipairs(codes) do
      f()
      inter_element_gap()
    end
    short_gap()
  end
end

short_gap = function()
  pause(dah_length)
end

local morse = {
  a = { dit, dah }, b = { dah, dit, dit, dit }, c = { dah, dit, dah, dit },
  d = { dah, dit, dit }, e = { dit }, f = { dit, dit, dah, dit },
  g = { dah, dah, dit }, h = { dit, dit, dit ,dit }, i = { dit, dit },
  j = { dit, dah, dah, dah }, k = { dah, dit, dah }, l = { dit, dah, dit, dit },
  m = { dah, dah }, n = { dah, dit }, o = { dah, dah, dah },
  p = { dit, dah, dah, dit }, q = { dah, dah, dit, dah }, r = { dit, dah, dit },
  s = { dit, dit, dit }, t = { dah }, u = { dit, dit, dah },
  v = { dit, dit, dit, dah }, w = { dit, dah, dah }, x = { dah, dit, dit, dah },
  y = { dah, dit, dah, dah }, z = { dah, dah, dit, dit },

  ["0"] = { dah, dah, dah, dah, dah }, ["1"] = { dit, dah, dah, dah, dah },
  ["2"] = { dit, dit, dah, dah, dah }, ["3"] = { dit, dit, dit, dah, dah },
  ["4"] = { dit, dit, dit, dit, dah }, ["5"] = { dit, dit, dit, dit, dit },
  ["6"] = { dah, dit, dit, dit, dit }, ["7"] = { dah, dah, dit, dit, dit },
  ["8"] = { dah, dah, dah, dit, dit }, ["9"] = { dah, dah, dah, dah, dit },

  [" "] = { medium_gap }
}

-- public interface
M.beep = function(message)
  message = message:lower()
  for _,ch in ipairs { message:byte(1, #message) } do
    sequence(morse[string.char(ch)])
  end
end

M.set_dit = function(duration)
  init(duration)
end

-- initialization code
init(50000)

return M

Using this module is as simple as:

morse = require 'morse'
morse.beep "I am the very model of a modern major-general."

Mathematica/Wolfram Language

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.

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]
Text example:
morseCode["SOS   soS"]
morseDecode[%]
morseCode["s@os|"]
Output:
"... --- ...    ... --- ... "

"sos   sos"

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

MATLAB

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

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
Output:
This will play the audio automatically, because the playSound argument is "true".
>> text2morse('Call me Ishmael.',true)

ans =

-.-.|.-|.-..|.-..| |--|.| |..|...|....|--|.-|.|.-..|

Modula-2

MODULE MorseCode;
FROM Terminal IMPORT WriteString,WriteLn,ReadChar;

PROCEDURE WriteMorseCode(str : ARRAY OF CHAR);
VAR i : CARDINAL;
BEGIN
    WriteString(str);
    WriteLn;
    FOR i:=0 TO HIGH(str) DO
        CASE CAP(str[i]) OF
              'A': WriteString(".-");
            | 'B': WriteString("-...");
            | 'C': WriteString("-.-");
            | 'D': WriteString("-..");
            | 'E': WriteString(".");
            | 'F': WriteString("..-.");
            | 'G': WriteString("--.");
            | 'H': WriteString("....");
            | 'I': WriteString("..");
            | 'J': WriteString(".---");
            | 'K': WriteString("-.-");
            | 'L': WriteString(".-..");
            | 'M': WriteString("--");
            | 'N': WriteString("-.");
            | 'O': WriteString("---");
            | 'P': WriteString(".--.");
            | 'Q': WriteString("--.-");
            | 'R': WriteString(".-.");
            | 'S': WriteString("...");
            | 'T': WriteString("-");
            | 'U': WriteString("..-");
            | 'V': WriteString("...-");
            | 'W': WriteString(".--");
            | 'X': WriteString("-..-");
            | 'Y': WriteString("-.--");
            | 'Z': WriteString("--..");
            | '0': WriteString("-----");
            | '1': WriteString(".----");
            | '2': WriteString("..---");
            | '3': WriteString("...--");
            | '4': WriteString("....-");
            | '5': WriteString(".....");
            | '6': WriteString("-....");
            | '7': WriteString("--...");
            | '8': WriteString("---..");
            | '9': WriteString("----.");
            | ' ': WriteString("  ");
        ELSE
            IF (str[i] # 0C) THEN
                WriteString("?");
            END
        END;
        WriteString(" ");
    END;
END WriteMorseCode;

BEGIN
    WriteMorseCode("hello world");
    WriteLn;

    ReadChar;
END MorseCode.

Nim

Text mode only. The text to translate is provided in the command line.

import os, strutils, tables

const Morse = {'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': "----.",  '.': ".-.-.-", ',': "--..--",  '?': "..--..", '\'': ".----.",
               '!': "-.-.--", '/': "-..-.",  '(': "-.--.",   ')': "-.--.-", '&': ".-...",
               ':': "---...", ';': "-.-.-.", '=': "-...-",   '+': ".-.-.",  '-': "-....-",
               '_': "..--.-", '"': ".-..-.", '$': "...-..-", '@': ".--.-."}.toTable

proc morse(s: string): string =
  var r: seq[string]
  for c in s:
    r.add Morse.getOrDefault(c.toUpperAscii, "")
  result = r.join(" ")

var m: seq[string]
for arg in commandLineParams():
  m.add morse(arg)
echo m.join("   ")
Output:
./morse Hello World!
.... . .-.. .-.. ---   .-- --- .-. .-.. -.. -.-.--

OCaml

Using /dev/dsp:

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"

Ol

There are no portable way to play the sound cross different OS, so this code only translates the input string into dot-dash notation.

To simplify the example will be used only lower letter case.

(display "Please, enter the string in lower case bounded by \" sign: ")
(lfor
   (list->ff '(
     (#\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 . ".----")
     (#\2 . "..---") (#\3 . "...--") (#\4 . "....-")
     (#\5 . ".....") (#\6 . "-....") (#\7 . "--...")
     (#\8 . "---..") (#\9 . "----.") (#\0 . "-----")
     (#\space . " ") (#\. . "  ")))
  (str-iter (read))
  (lambda (codes char)
     (let ((out (getf codes char)))
        (if out (display out)))
     codes))

; ==> Please, enter the string in lower case bounded by " sign:
; <== "hello world"
; ==> ......-...-..--- .-----.-..-..-..

PARI/GP

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("...---...")

Pascal

Free Pascal in Delphi mode. This program uses OpenAL for cross-platform PCM audio.

{$mode delphi}
PROGRAM cw;
		// Output a string as Morse code and CW.
		// Cross-platform PCM audio uses OpenAL
USES 	OpenAL, HRTimer;

		// Intl. Morse codes in ASCII order
CONST  	Morse: ARRAY [32..95] OF STRING = (' ','-.-.--','.-..-.','#','...-..-','%','.-...','.----.','-.--.','-.--.-','*','.-.-.','--..--','-....-','.-.-.-','-..-.','-----','.----','..---','...--','....-','.....','-....','--...','---..','----.','---...','-.-.-.','>','-...-','<','..--..','.--.-.','.-','-...','-.-.','-..','.','..-.','--.','....','..','.---','-.-','.-..','--','-.','---','.--.','--.-','.-.','...','-','..-','...-','.--','-..-','-.--','--..','-.--.','\','-.--.-','~','..--.-');
		// lengthen dah by this fraction of dit: 
		// best = 0.4; also lengthens pauses
		doh = 0.4;  
		// an 0.05 sec dit is around 26 wpm
		dit = 0.05;
		dah = 3 * dit + doh * dit;

VAR 	// OpenAL variables
		buffer : TALuint;
		source : TALuint;
		sourcepos: ARRAY [0..2] OF TALfloat= ( 0.0, 0.0, 0.0 );
		sourcevel: ARRAY [0..2] OF TALfloat= ( 0.0, 0.0, 0.0 );

		argv: ARRAY OF PalByte;
		format: TALEnum;
		size: TALSizei;
		freq: TALSizei;
		loop: TALInt;
		data: TALVoid;

		// rewinding has an effect on the output:
		// <with> and <without> sound rather different
		rewind	: BOOLEAN = FALSE;
		// the high-res timer is from Wolfgang Ehrhardt
		// http://www.wolfgang-ehrhardt.de/misc_en.html
		t		: THRTimer;
		msg		: STRING = 'the quick brown fox jumps over the lazy dog.';
	

	PROCEDURE PlayS(s: Extended);
		BEGIN
			StartTimer(t);
			AlSourcePlay(source);			
			WHILE readseconds(t) < s DO BEGIN END; 
			IF rewind THEN AlSourceRewind(source);
			AlSourceStop(source);
		END;
		
	PROCEDURE Pause(s: Extended);
		BEGIN
			StartTimer(t);
			WHILE readseconds(t) < s DO BEGIN END; 
		END;
		
	PROCEDURE doDit;
		BEGIN
			PlayS(dit);
			Pause(dit);
		END;

	PROCEDURE doDah;
		BEGIN
			PlayS(dah);
			Pause(dit);
		END;
		
	// ASCII char to Morse CW
	FUNCTION AtoM(ch: CHAR): STRING;
		VAR i: Integer;
			u: CHAR;
		BEGIN
			u := ch;
			IF ch IN ['a'..'z'] THEN u := chr(ord(ch) AND $5F);
			result := Morse[ord(u)];
			FOR i := 1 TO Length(result) DO
				CASE result[i] OF
					'.': BEGIN doDit; Write('. ') END;
					'-': BEGIN doDah; Write('_ ') END;
				END;
			Pause(dah);
			Write('  ');
			IF u = ' ' THEN Write('  ');
		END;

	// ASCII string to Morse CW	
	PROCEDURE StoM(s: STRING);
		VAR i: Integer;
		BEGIN
			FOR i := 1 TO Length(s) DO AtoM(s[i]);
		END;
				
BEGIN
	// OpenAL preparation
	InitOpenAL;
	AlutInit(nil,argv);
	
	AlGenBuffers(1, @buffer);
	// load the 500 Hz 1 sec sine-wave file
	// get it from http://audiocheck.net
	AlutLoadWavFile('audiocheck.net_sin_500Hz_-3dBFS_1s.wav', format, data, size, freq, loop);
	AlBufferData(buffer, format, data, size, freq);
	AlutUnloadWav(format, data, size, freq);
	
	AlGenSources(1, @source);
	AlSourcei ( source, AL_BUFFER, buffer);
	AlSourcef ( source, AL_PITCH, 1.0 );
	AlSourcef ( source, AL_GAIN, 1.0 );
	AlSourcefv ( source, AL_POSITION, @sourcepos);
	AlSourcefv ( source, AL_VELOCITY, @sourcevel);
	AlSourcei ( source, AL_LOOPING, AL_TRUE);	
	
	// Sound and print the Morse
	StoM(msg);
	Pause(1.0);
	
	AlSourceRewind(source);
	AlSourceStop(source);
	
	// Clean up
	AlDeleteBuffers(1, @buffer);
	AlDeleteSources(1, @source);
	AlutExit();
END.

Perl

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

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

Phix

With Baden-Powell menemonics.

Library: Phix/pGUI
Library: Phix/online

You can run this online here.

--
-- demo\rosetta\Morse_code.exw
-- ===========================
--
with javascript_semantics
include pGUI.e
include builtins\beep.e

Ihandle input, canvas, output, vbox, dlg
cdCanvas cddbuffer, cdcanvas

constant title = "Morse code",
         help_text = """
Enter a message to convert to morse code.
Press Return to listen to the result.
Characters A-Z are shown with Baden-Powell menemonics.
(Note said are a memory aid, not perfectly readable.)
Obviously deliberately looking away and listening
would be the best way to use this as a learning aid.
"""

function show_help()
    IupMessage(title,help_text,bWrap:=false)
    IupSetFocus(input)
    return IUP_IGNORE -- (don't open the browser help!)
end function

constant morse_data = """
!-.-.--#".-..-.#$...-..-#&.-...#'.----.#(-.--.#)-.--.-#+.-.-.#,--..--#--....-#..-.-.-#/-..-.#=-...-#
0-----#1.----#2..---#3...--#4....-#5.....#6-....#7--...#8---..#9----.#:---...#;-.-.-.#?..--..#@.--.-.#
A.-51517525#B-...7177414144444747#C-.-.7121121277271616#D-..717741414747#E.7474#F..-.7171111174247777#
G--.712177271818#H....2121717127277777#I..51515757#J.---2121222558269668#K-.-742184854428#(-.--.#
L.-..7171727677771717#M--45118155#N-.71362727#O---712182861216#P.--.8181612164247777#)-.--.-#
Q--.-8286121677775818#R.-.717144261717#S...515154545757#T-7121#U..-818111117727#V...-8181111156567727#
W.--818187534317#X-..-8154333365654518#Y-.--8163545433115558#Z--..7121762277771717#_..--.-#     #"""

sequence morse = repeat(``,255), -- the eg "..."'s
        bdnpwl = repeat({},255) -- Baden-Powell mnemonics

procedure setMorse()
    -- I trust the characters and morse codes are all pretty evident in morse_data.
    -- Baden-Powell mnemonics are encoded as 4 points on a 9wx7h grid per dot/dash,
    -- counting (it just turned out that way) right to left and top to bottom, such
    -- that "9711" (=1197) is(/looks like) a forwardslash and "9117" a backslash.
    -- Each quite fiddly to set up - rather relieved there were only 26 of them!
    sequence data = split(substitute(morse_data,"\n",""),'#')
    for di in data do
        integer key = di[1]
        string bpm = trim_head(di[2..$],".- "),
              code = di[2..-length(bpm)-1]
        assert(length(bpm)=0 or length(bpm)=4*length(code))
        morse[key] = code   -- eg morse['S'] = "..."
        bdnpwl[key] = sq_sub('5',bpm)
    end for
    morse['['] = morse['(']
    morse[']'] = morse[')']
end procedure

setMorse()

function redraw_cb(Ihandle /*ih*/)
    string text = upper(IupGetAttribute(input,"VALUE")),
         outstr = ""
    integer {dw,dh} = IupGetIntInt(canvas, "DRAWSIZE"),
            {tw,th} = cdCanvasGetTextSize(cddbuffer,text)
    while tw>dw do
        if length(outstr) then outstr &= " " end if
        outstr &= morse[text[1]]
        text = text[2..$]
        {tw,th} = cdCanvasGetTextSize(cddbuffer,text)
    end while
    atom cw = tw/max(length(text),1),
         cx = dw/2,
         cy = dh/2

    cdCanvasActivate(cddbuffer)
    cdCanvasSetBackground(cddbuffer, CD_LIGHT_PARCHMENT)
    cdCanvasClear(cddbuffer)
    cdCanvasSetForeground(cddbuffer, #BBADA0)
    cdCanvasText(cddbuffer, cx, cy, text)
    cx -= cw*(length(text)/2-0.5)

    cdCanvasSetForeground(cddbuffer, CD_BLUE)
    cdCanvasSetLineWidth(cddbuffer,3)
    {} = cdCanvasMarkSize(cddbuffer,3)

    atom gw = cw*0.75, gh = th*0.285
    for ch in text do
        sequence bpm = bdnpwl[ch]
        if length(bpm) then
            for k=1 to length(bpm) by 4 do
                atom x1 = cx+gw*bpm[k+0]/8-1,
                     y1 = cy+gh*bpm[k+1]/4-3,
                     x2 = cx+gw*bpm[k+2]/8-1,
                     y2 = cy+gh*bpm[k+3]/4-3
                if x1=x2 and y1=y2 then
                    cdCanvasMark(cddbuffer, x1, y1)
                else
                    cdCanvasLine(cddbuffer, x1, y1, x2, y2)
                end if
            end for
        end if
        cx += cw
        if length(outstr) then outstr &= " " end if
        outstr &= morse[ch]
    end for
    cdCanvasFlush(cddbuffer)
    IupSetStrAttribute(output,"TITLE",outstr)
    return IUP_DEFAULT
end function

function map_cb(Ihandle ih)
    cdcanvas = cdCreateCanvas(CD_IUP, ih)
    cddbuffer = cdCreateCanvas(CD_DBUFFER, cdcanvas)
--  cdCanvasFont(cddbuffer,"Courier",CD_PLAIN,24)
    cdCanvasFont(cddbuffer,"Courier",CD_PLAIN,48)
    cdCanvasSetTextAlignment(cddbuffer, CD_CENTER) 
    return IUP_DEFAULT
end function

constant frequency = 1280,          -- in Hz, 37..32767
         wpm = 15,                  -- words per minute
         dit = 1200/wpm,            -- in milliseconds
         dah = 3*dit,
         lettergap = 3*dit,
         wordgap = 7*dit

function key_cb(Ihandle /*dlg*/, atom c)
    if c=K_ESC then return IUP_CLOSE end if -- (standard practice for me)
    if c=K_F5 then return IUP_DEFAULT end if -- (let browser reload work)
    if c=K_F1 then return show_help() end if
    if c=K_CR then
        string text = trim(upper(IupGetAttribute(input,"VALUE")))
        sequence durations = {}
        for ch in text do
            if ch=' ' then
                durations &= wordgap
            else
                if length(durations) then
                    durations &= lettergap
                end if
                string m = morse[ch]
                for i=1 to length(m) do
                    if i>1 then durations &= dit end if
                    durations &= iff(m[i]='.'?dit:dah)
                end for
            end if
        end for
        beep(frequency,durations,0.5)
        IupSetAttribute(input,"SELECTION","ALL")
    elsif find(c,"#") then
        beep()
        return IUP_IGNORE
    else
        IupUpdate(canvas)
    end if
    return IUP_CONTINUE
end function

IupOpen()
input = IupText("EXPAND=HORIZONTAL")
canvas = IupCanvas("RASTERSIZE=520x40")
output = IupLabel("","EXPAND=HORIZONTAL")
vbox = IupVbox({input,canvas,output}, "MARGIN=10x5, GAP=5")
dlg = IupDialog(vbox,`TITLE="%s",MINSIZE=440x140`,{title})
IupSetCallback(dlg,"KEY_CB",Icallback("key_cb"))
IupSetCallbacks(canvas, {"MAP_CB", Icallback("map_cb"),
                         "ACTION", Icallback("redraw_cb")})
IupShow(dlg)
IupSetAttribute(canvas, "RASTERSIZE", NULL)
IupSetAttributeHandle(NULL,"PARENTDIALOG",dlg)
if platform()!=JS then
    IupMainLoop()
    IupClose()
end if

PicoLisp

The following simply uses the 'beep' pc-speaker beeper utility.

# *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" "--..")
         ("[" "-.--.")     ("]" "-.--.-")    ("_" "..--.-") ) ) )

# Words per minute
(de wpm (N)
   (setq *Dit (*/ 1200 N)  *Dah (* 3 *Dit)) )

(wpm 20)

# 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!")

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;

PowerShell

This function is case insensitive, ignores all non-Morse characters and optionally displays the Morse code.

function Send-MorseCode
{
    [CmdletBinding()]
    [OutputType([string])]
    Param
    (
        [Parameter(Mandatory=$true,
                   ValueFromPipeline=$true,
                   Position=0)]
        [string]
        $Message,

        [switch]
        $ShowCode
    )

    Begin
    {
        $morseCode = @{
            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 = "----."
        }    
    }
    Process
    {
        foreach ($word in $Message)
        {
            $word.Split(" ",[StringSplitOptions]::RemoveEmptyEntries) | ForEach-Object {

                foreach ($char in $_.ToCharArray())
                {
                    if ($char -in $morseCode.Keys)
                    {
                        foreach ($code in ($morseCode."$char").ToCharArray())
                        {
                            if ($code -eq ".") {$duration = 250} else {$duration = 750}

                            [System.Console]::Beep(1000, $duration)
                            Start-Sleep -Milliseconds 50
                        }

                        if ($ShowCode) {Write-Host ("{0,-6}" -f ("{0,6}" -f $morseCode."$char")) -NoNewLine}
                    }
                }

                if ($ShowCode) {Write-Host}
            }

            if ($ShowCode) {Write-Host}
        }
    }
}
Send-MorseCode -Message "S.O.S" -ShowCode
Output:
...   ---   ...
"S.O.S", "Goodbye, cruel world!" | Send-MorseCode -ShowCode
Output:
   ...   ---   ...

   --.   ---   ---   -..  -...  -.--     .
  -.-.   .-.   ..-     .  .-..
   .--   ---   .-.  .-..   -..

Prolog

Runs in SWI Prolog, Edinburgh syntax

% convert text to morse
% query text2morse(Text, Morse)
% where
% Text is string to convert
% Morse is Morse representation
% There is a space between chars and double space between words
%
text2morse(Text, Morse) :-
	string_lower(Text, TextLower),			% rules are in lower case
	string_chars(TextLower, Chars),			% convert string into list of chars
	chars2morse(Chars, MorseChars),			% convert each char into morse
	string_chars(MorsePlusSpace, MorseChars),	% append returned string list into single string
	string_concat(Morse, ' ', MorsePlusSpace).	% Remove trailing space

chars2morse([], "").
chars2morse([H|CharTail], Morse) :-
	morse(H, M),
	chars2morse(CharTail, MorseTail),
	string_concat(M,' ', MorseSpace),
	string_concat(MorseSpace, MorseTail, Morse).

% space
morse(' ', " ").
% letters
morse('a', ".-").
morse('b', "-...").
morse('c', "-.-.").
morse('d', "-..").
morse('e', ".").
morse('f', "..-.").
morse('g', "--.").
morse('h', "....").
morse('i', "..").
morse('j', ".---").
morse('k', "-.-").
morse('l', ".-..").
morse('m', "--").
morse('n', "-.").
morse('o', "---").
morse('p', ".--.").
morse('q', "--.-").
morse('r', ".-.").
morse('s', "...").
morse('t', "-").
morse('u', "..-").
morse('v', "...-").
morse('w', ".--").
morse('x', "-..-").
morse('y', "-.--").
morse('z', "--..").
% numbers
morse('1', ".----").
morse('2', "..---").
morse('3', "...--").
morse('4', "....-").
morse('5', ".....").
morse('6', "-....").
morse('7', "--...").
morse('8', "---..").
morse('9', "----.").
morse('0', "-----").
% common punctuation
morse('.', ".-.-.-").
morse(',', "--..--").
morse('/', "-..-.").
morse('?', "..--..").
morse('=', "-...-").
morse('+', ".-.-.").
morse('-', "-....-").
morse('@', ".--.-.").
text2morse("Hello World", Morse).
Output:
Morse = ".... . .-.. .-.. ---   .-- --- .-. .-.. -.."

PureBasic

#BaseTime =50
#Frequence=1250
#Short    =   #BaseTime
#Long     =3* #BaseTime
#Intergap =   #BaseTime
#LetterGap=3* #BaseTime
#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

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)

# Outputs its own source file as Morse. An audible quine!
#with open(sys.argv[0], 'r') as thisfile:
#    windowsmorse(thisfile.read())
    
while True:
    windowsmorse(input('A string to change into morse: '))

Quackery

Mac specific as Quackery sits on top of Python, which is not strong with cross-platform audio.

  [ $ /
import subprocess
subprocess.run(["say", 
string_from_stack()])
      / python ]                  is speak   ( $ -->   )

  [ 3 times [ $ " " speak ] ]     is pause   (   -->   )

  [ [] swap witheach 
     [ char - = iff
         [ $ "dash " join ]
       else [ $ "dot " join ] ]
       space join ]               is dotdash ( $ --> $ )

  [ table ]                       is letter  ( n --> $ )
  $ ".- -... -.-. -.. . ..-.
     --. .... .. .--- -.-
     .-.. -- -. --- .--. 
     --.- .-. ... - ..- ...-
     .-- -..- -.-- --.."
  nest$ witheach 
    [ dotdash ' letter put ] 
    

  [ table ]                       is number  ( n --> $ )
  $ "----- .---- ..--- ...--
     ....- ..... -.... --...
     ---.. ----."
  nest$ witheach 
    [ dotdash ' number put ] 

  [ witheach
    [ dup space = iff 
        [ drop pause ] done 
      dup char 0 char 9 1+
      within iff
        [ char 0 -
          number speak ] done
      upper
      dup char A char Z 1+
      within iff
        [ char A -
          letter speak ] done
      drop ] ]                    is morse   ( $ -->   )

Racket

Using MIDI on Windows for the beeps.

#lang racket
(require ffi/unsafe ffi/unsafe/define)
(define-ffi-definer defmm (ffi-lib "Winmm"))
(defmm midiOutOpen (_fun [h : (_ptr o _int32)] [_int = -1] [_pointer = #f]
                         [_pointer = #f] [_int32 = 0] -> _void -> h))
(defmm midiOutShortMsg (_fun _int32 _int32 -> _void))
(define M (midiOutOpen))
(define (midi x y z) (midiOutShortMsg M (+ x (* 256 y) (* 65536 z))))

(define raw-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--..|1.----"
    "|2..---|3...--|4....-|5.....|6-....|7--...|8---..|9----.|0-----"))

(define codes
  (for/list ([x (regexp-split #rx"\\|" (string-append* raw-codes))])
    (cons (string-ref x 0) (substring x 1))))

(define (morse str [unit 0.1])
  (define (sound len)
    (midi #x90 72 127) (sleep (* len unit))
    (midi #x90 72 0)   (sleep unit))
  (define (play str)
    (midi #xC0 #x35 0) ; use a cute voice
    (for ([c str])
      (case c [(#\.) (sound 1)] [(#\-) (sound 3)] [(#\ ) (sleep (* 3 unit))])))
  (let* ([str (string-foldcase str)]
         [str (regexp-replace* #rx"[,:;]+" str " ")]
         [str (regexp-replace* #rx"[.!?]+" str ".")]
         [str (string-normalize-spaces str)])
    (for ([s (string-split str)])
      (define m
        (string-join
         (for/list ([c s])
           (cond [(assq c codes) => cdr]
                 [else (case c [(#\space) " "] [(#\.) "  "] [else ""])]))))
      (printf "~a: ~a\n" s m)
      (play (string-append m "  ")))))

(morse "Say something here")

Raku

(formerly Perl 6)

Works with: rakudo version 2015-09-24

Here we use the user as the audio device. Just read the output, leaving extra pauses where indicated by either whitespace or underscore.

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> " }

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 _

Red

The code must be compiled, because it contains red system code that is needed for the winapi calls so use "red.exe -c morse.red " from command line to compile with red runtime.dll or "red.exe -r morse.red " to compile to single .exe file
each character will be printed with its corresponding code before played

Red [ 
  file: %morse.red   ;; filename, could be ommited
]
; ";" is character for comment, i use double ones for better readability 

DIT: 100 ;; constant : 100 ms for short Beep
FREQ: 700 ;; frequency for Beep

;; exported code for red/system win api calls to Beep / Sleep:
#include %api.reds

;; string with morse codes for alphabet:
;; ( caution, u must use "str: copy ..."  if code ist to be executed multiple times ! )
str: "A.-B-...C-.-.D-..E.F..-.G--.H....I..J.---K-.-L.-..M--N-."
append str "O---P.--.Q--.-R.-.S...T-U..-V...-W.--X-..-Y-.--Z--.."
 
 delim: charset [#"A" - #"Z"]
 
 ;; use of parse to generate "mc" morse code series / array containing codes for A - Z
 ;; use characters only as delimiter for each code
 mc:  parse str [ thru "A"  collect some [ keep copy result to [delim | end ] skip ] ]
 
  ;;-------------------------------------------- 
 send-code: func ["function to play morse code for character " 
 ;;-------------------------------------------- 
           chr [char!]  ;; character A .. Z 
      ][
      sleep 500           ;; short break so u can read the character first
      ind:   to-integer chr - 64 ;; calculate index for morse array
      foreach sym mc/:ind [     ;; foreach symbol of code for character ...
        prin sym                  ;; prin(t) "." or "-"
        either sym = #"." [     ;; short beep
          beep FREQ DIT 
      ][
        beep FREQ 3 * DIT     ;; or long beep = 3 x short
      ] 
      sleep DIT                   ;; short break after each character
    ]
]
 ;;----------------------------------------------
 morse-text: func ["extract valid characters from sentence"
 ;;----------------------------------------------
        msg [string!]
][      
 foreach chr uppercase msg [
    prin  chr prin " "    ;; print character
  ;; valid character  A-Z ?
   either   (chr >= #"A") and (chr <= #"Z") [   
      send-code chr  
    ] [          ;; ... "else" word gap or unknown
      sleep 6 * DIT   ;; pause after word
    ]
    prin newline    ;; equal to :  print """ ,( prin prints without crlf )
  ]  
  sleep 6 * DIT  ;; pause after sentence
 ]
 ;;----------------------------------
 
morse-text "rosetta code"
morse-text "hello world"
Red/System [
  file: %api.reds ;; filename, could be ommited
]

; --- lib import -----
;; for winapi functions "Beep" and "Sleep"
#system [ #import [  "kernel32.dll" stdcall [
      wbeep: "Beep" [
           frequ    [integer!]
           dur [integer!]
          return: [integer!]
       ]
         wsleep: "Sleep" [ dur [integer!]  ]
] ] ] 

beep: routine [ 
        freq [integer!]
        duration [integer!] 
        return: [integer!]
      ]  [ wbeep freq duration  ]

sleep: routine [ 
        duration [integer!] 
      ]  [ wsleep duration  ]
;;----------------------------------------------

REXX

The   $MORSE.REX   REXX program is included here   ──►   $MORSE.REX.

This program supports the   International Morse code   as well as the   USA Morse code   (the later being primarily used by the North American Railroads).

Some translation is done for unsupported characters such as braces   {   }, brackets   [   ]   and the like.

This REXX program normally shows Morse code words 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.

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

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

Ring

 
 morsecode = [["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", "----."]]
strmorse = ""
str = "this is a test text"
for n = 1 to len(str)
     pos = 0
     for m = 1 to len(morsecode)
          if morsecode[m][1] = str[n]
             pos = m
          ok
     next
     if str[n] = " "
        strmorse = strmorse + " "
     else
        if pos > 0
           strmorse = strmorse + morsecode[pos][2] + "|"
        ok
      ok
next
strmorse = left(strmorse,len(strmorse)-1)
see strmorse + nl

Output:

-|....|..|...| ..|...| .-| -|.|...|-| -|.|-..-|-

RPL

We use here AWK's elegant way to store the Morse code.

"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--." 
   440 0.1 
   → morse freq beat
   ≪ -56 CF               @ to be replaced by 51 SF on HP-28s
     1 OVER SIZE FOR j
       morse OVER j DUP SUB
       IF POS THEN
          LAST
          WHILE 1 + ".-" morse 3 PICK DUP SUB POS REPEAT
             LAST beat * freq SWAP BEEP
             beat WAIT
          END
       DROP 3
       ELSE 5 END
       beat * WAIT
     NEXT
≫ ≫ 'EMIT' STO
"SOS" EMIT

Ruby

Works with: Ruby version 1.8.7+
(uses each_char)
Library: win32-utils
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
Output:
your message is "sos"
... --- ...
your message is "this is a test."
- .... .. ...
.. ...
.-
- . ... - .-.-.-

Rust

Original code can be found on GitHub.

morse_code/src/main.rs file:

//!
//! morse_code/src/main.rs
//!
//! Michael G. Cummings
//! 2019-08-26
//!
//! Since Rust doesn't have build-in audio support text output is used.
//!

use std::process;
use structopt::StructOpt;
use morse_code::{Config, Opt, run};

/// Core of the command-line binary.
///
/// By default expects input from stdin and outputs resulting morse code to stdout, but can also
/// read and/or write to files.
/// Use `morse_code --help` for more information about options.
fn main() {
    let opts = Opt::from_args();
    let mut config = Config::new(opts).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    if let Err(err) = run(&mut config) {
        eprintln!("Application error: {}", err);
        process::exit(2);
    }
}

morse_code/src/lib.rs file:

//!
//! morse_code/src/lib.rs
//!
//! Michael G. Cummings
//! 2019-08-26
//!

#[macro_use]
extern crate structopt;

use std::{fs, io};
use std::collections::HashMap;
use std::error::Error;
use std::path::PathBuf;

/// Main library function that does the actual work.
///
/// Each character has one space between them and there are two spaces between words.
/// Unknown characters in the input are replaced with a '#' in the output.
///
pub fn run(config: &mut Config) -> Result<(), Box<dyn Error>> {
    let mut contents = String::new();
    config.read.read_to_string(&mut contents)?;
    let morse_map = init_code_map();
    let mut result = String::new();
    for char in contents.trim().to_uppercase().chars() {
        match morse_map.get(&char) {
            Some(hash) => {
                result = result + *hash;
            }
            None => { result = result + "#" }
        }
        result = result + " ";
    }
    config.write.write(result.as_ref())?;
    Ok(())
}

/// Configuration structure for the input and output streams.
#[derive(Debug)]
pub struct Config {
    read: Box<dyn io::Read>,
    write: Box<dyn io::Write>,
}

impl Config {
    pub fn new(opts: Opt) -> Result<Config, &'static str> {
        let input: Box<dyn io::Read> = match opts.input {
            Some(p) => Box::new(fs::File::open(p).unwrap()),
            None => Box::new(io::stdin()),
        };
        let output: Box<dyn io::Write> = match opts.output {
            Some(p) => Box::new(fs::File::create(p).unwrap()),
            None => Box::new(io::stdout()),
        };
        Ok(Config { read: input, write: output })
    }
}

/// Structure used to hold command line opts(parameters) of binary.
///
/// Using StructOpt crate to parse command-line parameters/options.
///
#[derive(Debug, StructOpt)]
#[structopt(rename_all = "kebab-case", raw(setting = "structopt::clap::AppSettings::ColoredHelp"))]
pub struct Opt {
    /// Input file, stdin if not present
    #[structopt(short, long, parse(from_os_str))]
    input: Option<PathBuf>,
    /// Output file, stdout if not present
    #[structopt(short, long, parse(from_os_str))]
    output: Option<PathBuf>,
}

/// Initialize hash map of characters to morse code as string.
pub fn init_code_map() -> HashMap<char, &'static str> {
    let mut morse_map: HashMap<char, &str> = HashMap::with_capacity(37);
    morse_map.insert(' ', " ");
    morse_map.insert('A', "._");
    morse_map.insert('B', "_...");
    morse_map.insert('C', "_._.");
    morse_map.insert('D', "_..");
    morse_map.insert('E', ".");
    morse_map.insert('F', ".._.");
    morse_map.insert('G', "__.");
    morse_map.insert('H', "....");
    morse_map.insert('I', "..");
    morse_map.insert('J', ".___");
    morse_map.insert('K', "_._");
    morse_map.insert('L', "._..");
    morse_map.insert('M', "__");
    morse_map.insert('N', "_.");
    morse_map.insert('O', "___");
    morse_map.insert('P', ".__.");
    morse_map.insert('Q', "__._");
    morse_map.insert('R', "._.");
    morse_map.insert('S', "...");
    morse_map.insert('T', "_");
    morse_map.insert('U', ".._");
    morse_map.insert('V', "..._");
    morse_map.insert('W', ".__");
    morse_map.insert('X', "_.._");
    morse_map.insert('Y', "_.__");
    morse_map.insert('Z', "__..");
    morse_map.insert('1', ".____");
    morse_map.insert('2', "..___");
    morse_map.insert('3', "...__");
    morse_map.insert('4', "...._");
    morse_map.insert('5', ".....");
    morse_map.insert('6', "_....");
    morse_map.insert('7', "__...");
    morse_map.insert('8', "___..");
    morse_map.insert('9', "____.");
    morse_map.insert('0', "_____");
    morse_map
}

Scala

Output:
Best seen running in your browser either by ScalaFiddle (ES aka JavaScript, non JVM) or Scastie (remote JVM).
object MorseCode extends App {

  private val code = Map(
    ('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', "----.  "),
    ('\'', ".----."), (':', "---... "), (',', "--..-- "), ('-', "-....- "),
    ('(', "-.--.- "), ('.', ".-.-.- "), ('?', "..--.. "), (';', "-.-.-. "),
    ('/', "-..-.  "), ('-', "..--.- "), (')', "---..  "), ('=', "-...-  "),
    ('@', ".--.-. "), ('"', ".-..-. "), ('+', ".-.-.  "), (' ', "/")) // cheat a little

  private def printMorse(input: String): Unit = {
    println(input)
    println(input.trim.replaceAll("[ ]+", " ").toUpperCase
      .map(code.getOrElse(_, "").trim).mkString(" "))
  }

  printMorse("sos")
  printMorse("   Hello     World!")
  printMorse("Rosetta Code")

}

sed

Translation of AWK:

#!/bin/sed -rf
# Convert to uppercase
s/.*/\U&/
# 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--../
# Main loop
:a
s/([A-Z])([^\n]*\n.*\1([-.]+))/\3 \2/
ta
# Remove lookup table
s/\n.*//

Example:

$ echo hello world! | ./morse.sed 
.... . .-.. .-.. ---  .-- --- .-. .-.. -.. !

Tcl

Library: Snack
# This uses the GUI-free part of the Snack library
package require sound
 
# 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
}
# 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
}
# 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" "--.."
}

# 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
}

# We'll play at a fairly high pitch 
set frequency 700
 
morse "Morse code with Tcl and Snack." 20

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
Output:
Please enter your sentence >Hello World
SEE your morsecode !
......-...-..--- .-----.-..-..-..
HEAR your morsecode !
Output:
Please enter your sentence >SOS
SEE your morsecode !
...---...
HEAR your morsecode ! 

Ursa

Translation of: Python
decl ursa.util.sound snd
decl string<> chars
decl string<> morse

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

decl int e f chargap wordgap
# element time in ms. one dot is on for e then off for e
set e 50
# tone frequency in hertz
set f 1280
# time between characters of a word (in units of e)
set chargap 1
# time between words (in units of e)
set wordgap 7


def gap (int n)
	sleep (* n e)
end gap
decl function off
set off gap


def on (int n)
	 snd.beep f (/ (* n e) 1000)
end on


def dot ()
	on 1
	off 1
end dot


def dash ()
	on 3
	off 1
end dash


def bloop (int n)
	snd.beep (/ f 2) (/ (* n e) 1000)
end bloop


def encode_morse (string text)
	decl string<> words
	set words (split (upper (trim text)) " ")
	decl int i j k
	for () (< i (size words)) (inc i)
		for (set j 0) (< j (size words<i>)) (inc j)
			decl int loc
			set loc (locate words<i><j> chars)
			if (= loc -1)
				bloop 3
			else
				for (set k 0) (< k (size morse<loc>)) (inc k)
					if (= morse<loc><k> "-")
						dash
					elif (= morse<loc><k> ".")
						dot
					else
						bloop 3
					end if
				end for
			end if
			gap chargap
		end for
		gap wordgap
	end for
end encode_morse


# --- uncomment this block to output the source of this file as morse
# decl file src
# src.open args<0>
# encode_morse (src.readall)


while true
	out "A string to change into morse: " console
	encode_morse (in string console)
end while

VBA

Option Explicit

Private Declare Function Beep Lib "kernel32" (ByVal dwFreq As Long, ByVal dwDuration As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Private Const MORSE_ALPHA As String = ".-,-...,-.-.,-..,.,..-.,--.,....,..,.---,-.-,.-..,--,-.,---,.--.,--.-,.-.,...,-,..-,...-,.--,-..-,-.--,--.."
Private Const MORSE_NUMERIC As String = "-----,.----,..---,...--,....-,.....,-....,--...,---..,----."

Private Const ONE_UNIT As Integer = 100

Private Const BEEP_DOT As Integer = ONE_UNIT
Private Const BEEP_DASH As Integer = 3 * ONE_UNIT
Private Const BEEP_OTHER As Integer = 7 * ONE_UNIT
Private Const DELAY As Integer = ONE_UNIT
Private Const LETTERS_DELAY As Integer = 3 * ONE_UNIT
Private Const SPACE_DELAY As Integer = 7 * ONE_UNIT

Private Const FREQUENCY_CHARS As Integer = 1200
Private Const FREQUENCY_OTHERCHARS As Integer = 400

Sub Main()
Dim p$, temp$
    p = ToMorse("Hel/lo 123 world")
    temp = Replace(p, "+", "")
    Debug.Print Replace(temp, "_", "")
    PlayMorse p
End Sub

Private Function ToMorse(s As String) As String
Dim i&, t$, j&
    s = UCase(s)
    For i = 1 To Len(s)
        j = Asc(Mid(s, i, 1))
        Select Case j
            Case 65 To 90 'alpha
                t = t & Split(MORSE_ALPHA, ",")(j - 65) & "+" ' "+" ==> separate each characters
            Case 48 To 57 'numerics
                t = t & Split(MORSE_NUMERIC, ",")(j - 48) & "+"
            Case 32 'space
                t = t & " " & "+"
            Case Else 'others
                t = t & "_" & "+"
        End Select
    Next i
    ToMorse = t
End Function

Private Sub PlayMorse(s As String)
Dim i&
    For i = 1 To Len(s)
        Select Case Mid(s, i, 1)
            Case ".": Beep FREQUENCY_CHARS, BEEP_DOT
            Case "-": Beep FREQUENCY_CHARS, BEEP_DASH
            Case "_": Beep FREQUENCY_OTHERCHARS, BEEP_OTHER
            Case "+": Sleep LETTERS_DELAY
            Case " ": Sleep SPACE_DELAY
        End Select
        Sleep DELAY
    Next i
End Sub
Output:

Play the sound's morse and display :

......-...-..--- .----..---...-- .-----.-..-..-..

Visual Basic

Works with: Visual Basic version 6

The #VBA example works in VB6 as well, without any change.

Visual Basic .NET

Translation of: C#
Module Module1

    Sub Main()
        Dim word = "sos"
        Dim codes As New Dictionary(Of String, String) From {
            {"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", "----."}
        }

        For Each c In word.ToCharArray
            Dim rslt = codes(c).Trim
            For Each c2 In rslt.ToCharArray
                If c2 = "." Then
                    Console.Beep(1000, 250)
                Else
                    Console.Beep(1000, 750)
                End If
                System.Threading.Thread.Sleep(50)
            Next
        Next
    End Sub

End Module

Wren

Library: Wren-str
Library: Wren-sound

As Wren-cli doesn't have any built-in audio support, we instead build a .wav file which can then be played using a utility such as rhythmbox or SoX, having first printed the morse code to the terminal for comparison.

Any characters which do not appear in the Morse alphabet are simply ignored.

import "./str" for Str
import "./sound" for Wav

var charToMorse = {
    "!": "---.",   "\"": ".-..-.",  "$": "...-..-",  "'": ".----.",  
    "(": "-.--.",  ")": "-.--.-",   "+": ".-.-.",    ",": "--..--",
    "-": "-....-", ".": ".-.-.-",   "/": "-..-.",
    "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": "--..",
    "[": "-.--.",   "]": "-.--.-",   "_": "..--.-"
}

var textToMorse = Fn.new { |text|
    text = Str.upper(text)
    var morse = ""
    for (c in text) {
        if (c == " ") {
            morse = morse + (" " * 7)
        } else {
            var m = charToMorse[c]
            if (m) morse = morse + m.join(" ") + "   "
        }
    }
    return morse.trimEnd()
}

var morse = textToMorse.call("Hello world!")
System.print(morse) // print to terminal

// now create a .wav file
morse = morse.replace("-", "...") // replace 'dash' with 3 'dot's
var data = []
var sampleRate = 44100
var samples = 0.2 * sampleRate // number of samples assuming 'dot' takes 200 ms.
var freq = 500 // say
var omega = 2 * Num.pi * freq
for (c in morse) {
    if (c == ".") {
        for (s in 0...samples) {
            var value = (32 * (omega * s / sampleRate).sin).round & 255
            data.add(value)
        }
    } else {
        for (s in 0...samples) data.add(0)
    }
}
Wav.create("morse_code.wav", data, sampleRate)
Output:
. . . .   .   . - . .   . - . .   - - -          . - -   - - -   . - .   . - . .   - . .   - - - .


It's also possible to play .wav files which (preferably) have a sample rate of 44.1 kHz using DOME:

Library: DOME
import "audio" for AudioEngine

class Main {
    construct new() {}

    init() {
        AudioEngine.load("morse", "morse_code.wav")
        AudioEngine.play("morse")
    }

    update() {}

    draw(alpha) {}
}

var Game = Main.new()

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.

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!");
]
Output:
S... O--- S...       S... O--- S...       S... O--- S...       
H.... e. l.-.. l.-.. o--- ,--..--       w.-- o--- r.-. l.-.. d-.. !.-.-.. 

Yabasic

Mixin classic/modern style. In Yabasic, line number is not mandatory.

10 REM Morse code
20 DIM c$(54)
30 FOR f = 1 TO 54
40 READ l$, m$
50 LET d$ = d$ + l$ : LET c$(f) = m$
60 NEXT f

100 LINE INPUT "Message? " t$
105 t$ = upper$(t$)
110 FOR f = 1 TO LEN(t$)
120 p = INSTR(d$, mid$(t$, f, 1))
130 IF p > 0 then
        PRINT c$(p);
    else
        print "?";
    end if
140 NEXT f
print
goto 100


1000 DATA "A","._","B","_...","C","_._.","D","_..","E",".","F",".._."
1010 DATA "G","__.","H","....","I","..","J",".___","K","_._","L","._.."
1020 DATA "M","__","N","_.","O","___","P",".__.","Q","__._","R","._."
1030 DATA "S","...","T","_","U",".._","V","..._","W",".__","X","_.._"
1040 DATA "Y","_.__","Z","__..","1",".____","2","..___","3","...__"
1050 DATA "4","...._","5",".....","6","_....","7","__...","8","___.."
1060 DATA "9","____.","0","_____",".","._._._",",","__..__","?","..__.."
1070 DATA "'",".____.","!","_._.__","/","_.._.","(","_.__.",")","_.__._"
1080 DATA "&","._...",":","___...",";","_._._.","=","_..._","+","._._.","-","_...._"
1090 DATA "_","..__._","\"","._.._.","$","..._.._","@",".__._."