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




 
{$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.
 