Morse sound file generator
The Scilab program below generates a sound file (.wav) with the morse code representing a given text.
Three modes are implemented to "smooth the envelope": none, sine and gaussian.
Related information:
- Calculating Morse Code Speed
- A standard for morse timing using the Farnworth technique
- An Intuitive Explanation of CW Bandwidth
- Spectral Analysis of a CW keying pulse
- Video - Technique Of Hand Sending (1944)
- Video - INTERNATIONAL MORSE CODE, HAND SENDING
Here are the resulting graphs, bandwidth analysis and sound.
NO SMOOTHING | SINE | GAUSSIAN |
no smoothing | ||
Corresponding sound file Lots of "clicks"... | Corresponding sound file Smoother... | Corresponding sound file Smoother... |
//////////////////////////////////////////////////////////////////////////////
//
// Morse sound file generator
//
// Generate a .wav file playing user text in morse
//
// Copyright (C) 2012 Christophe DAVID (ON6ZQ)
// http://www.on6zq.be/Scilab/MorseSoundFileGenerator
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License version 2 as published
// by the Free Software Foundation (http://www.gnu.org/licenses/gpl.html).
//
//////////////////////////////////////////////////////////////////////////////
//
// This program was last tested with Scilab 5.5
// http://www.scilab.org
//
//////////////////////////////////////////////////////////////////////////////
// A semicolon at the end of an instruction causes the result not to be displayed.
errclear; // clear all errors
clear; // kill variables
clearglobal // kill global variables
clc; // clear command window
tohome; // move the cursor to the upper left corner of the Command Window
clf; // clear or reset the current graphic figure (window) to default values
// stacksize('max'); // increase the size of this stack to the maximum.
stacksize(1e8); //stacksize('max') does not appear to work on my system ;-(
//////////////////////////////////////////////////////////////////////////////
// text = "PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS "; // 1 minute at 12 WPM
// text = "The Quick Brown Fox Jumps Over The Lazy Dog 1234567890"; // all alphanumerical characters
text ="73 de ON6ZQ";
WordsPerMinute = 20;
OutputDirectory ='/home/cda/test/';
ToneFrequency = 600;// hertz
SamplingRate =22500;// samples per second
BitDepth = 16;// 8, 16, 24, 32 bits. number of bits each sample
EnvelopeSmoothingMethod =2// 0: none, 1: cosine, 2: Gaussian
RiseTime =0.010// second
FallTime =0.010// second
//////////////////////////////////////////////////////////////////////////////
function[OutputMatrix]= TextToMorse(InputString)
morse =[ // ASCII values and meanings
'', // 1
'', // 2
'', // 3
'', // 4
'', // 5
'', // 6
'', // 7
'', // 8
'', // 9
'', // 10
'', // 11
'', // 12
'', // 13
'', // 14
'', // 15
'', // 16
'', // 17
'', // 18
'', // 19
'', // 20
'', // 21
'', // 22
'', // 23
'', // 24
'', // 25
'', // 26
'', // 27
'', // 28
'', // 29
'', // 30
'', // 31
'', // 32
'', // 33 !
'', // 34 "
'', // 35 #
'', // 36 $
'', // 37 %
'', // 38 &
'', // 39 '
'', // 40 (
'', // 41 )
'', // 42 *
'', // 43 +
'', // 44 ,
'', // 45 -
'', // 46 .
'', // 47 /
'-----',// 48 0
'.----',// 49 1
'..---',// 50 2
'...--',// 51 3
'....-',// 52 4
'.....',// 53 5
'-....',// 54 6
'--...',// 55 7
'---..',// 56 8
'----.',// 57 9
'', // 58 :
'', // 59 ;
'', // 60 <
'', // 61 =
'', // 62 >
'', // 63 ?
'', // 64 @
'.-', // 65 A
'-...', // 66 B
'-.-.', // 67 C
'-..', // 68 D
'.', // 69 E
'..-.', // 70 F
'--.', // 71 G
'....', // 72 H
'..', // 73 I
'.---', // 74 J
'-.-', // 75 K
'.-..', // 76 L
'--', // 77 M
'-.', // 78 N
'---', // 79 O
'.--.', // 80 P
'--.-', // 81 Q
'.-.', // 82 R
'...', // 83 S
'-', // 84 T
'..-', // 85 U
'...-', // 86 V
'.--', // 87 W
'-..-', // 88 X
'-.--', // 89 Y
'--..', // 90 Z
'', // 91 [
'', // 92 \
'', // 93 ]
'', // 94 ^
'', // 95 _
'', // 96 `
'.-', // 97 a
'-...', // 98 b
'-.-.', // 99 c
'-..', // 100 d
'.', // 101 e
'..-.', // 102 f
'--.', // 103 g
'....', // 104 h
'..', // 105 i
'.---', // 106 j
'-.-', // 107 k
'.-..', // 108 l
'--', // 109 m
'-.', // 110 n
'---', // 111 o
'.--.', // 112 p
'--.-', // 113 q
'.-.', // 114 r
'...', // 115 s
'-', // 116 t
'..-', // 117 u
'...-', // 118 v
'.--', // 119 w
'-..-', // 120 x
'-.--', // 121 y
'--..', // 122 z
'', // 123 {
'', // 124 |
'', // 125 }
'', // 126 ~
'', // 127
];
OutputMatrix =[];
M =asciimat(InputString);
for i =1:length(M)
// printf("\n%03d %s %s", M(i), ascii(M(i)), morse(M(i)));
OutputMatrix(i)= morse(M(i));
end
endfunction
//////////////////////////////////////////////////////////////////////////////
function f = GaussDistribution(x, mu, s)
p1 =-.5 *((x - mu)/ s) .^ 2;
p2 =(s *sqrt(2*%pi));
f =exp(p1) ./ p2;
endfunction
//////////////////////////////////////////////////////////////////////////////
// http://www.kent-engineers.com/codespeed.htm
// http://www.arrl.org/files/file/Technology/x9004008.pdf
UnitsPerDit =1;
UnitsPerDah =3;
UnitsBetweenElements =1;
UnitsBetweenCharacters =3;
UnitsBetweenWords =7;
UnitDuration =1.2/ WordsPerMinute; // seconds
InterElements =zeros(1,(UnitsBetweenElements * UnitDuration * SamplingRate))
InterCharacters =zeros(1,(UnitsBetweenCharacters * UnitDuration * SamplingRate))
InterWords =zeros(1,(UnitsBetweenWords * UnitDuration * SamplingRate))
Dit =0:1:(UnitsPerDit * UnitDuration * SamplingRate);
Dah =0:1:(UnitsPerDah * UnitDuration * SamplingRate);
Dit = sind((Dit / SamplingRate)* ToneFrequency *360);
Dah = sind((Dah / SamplingRate)* ToneFrequency *360);
if EnvelopeSmoothingMethod >0then
// http://www.w8ji.com/cw_bandwidth_described.htm
// http://fermi.la.asu.edu/w9cf/articles/click/index.html
SmoothRise =1:1:(RiseTime * SamplingRate);
SmoothFall =(FallTime * SamplingRate):-1:1;
select EnvelopeSmoothingMethod
case1then
SmoothRise = sind((SmoothRise / SamplingRate)*(1/ RiseTime) *90);
SmoothFall = sind((SmoothFall / SamplingRate)*(1/ FallTime) *90);
case2then
mu = RiseTime * SamplingRate;
s =(RiseTime * SamplingRate)/2;
SmoothRise = GaussDistribution(SmoothRise, mu, s);
SmoothFall = GaussDistribution(SmoothFall, mu, s);
SmoothRise = SmoothRise *(1/max(SmoothRise));
SmoothFall = SmoothFall *(1/max(SmoothFall));
elseabort;
end
plot(SmoothRise,'g')
plot(SmoothFall,'r')
legend(['SmoothRise';'SmoothFall']);
FileName =sprintf("%sMorseSoundFileGenerator_Envelope_%da.jpg", OutputDirectory, EnvelopeSmoothingMethod);
xs2jpg(gcf(), FileName);// Export to a JPG file.
input('Check the graphic window then press enter to continue.')
clf;
[_rows, _columns]=size(SmoothRise);
Dit(1:_columns) = Dit(1:_columns) .* SmoothRise;
Dah(1:_columns) = Dah(1:_columns) .* SmoothRise;
[_rows, _columns]=size(SmoothFall);
Dit($ - _columns +1: $)= Dit($ - _columns +1: $) .* SmoothFall;
Dah($ - _columns +1: $)= Dah($ - _columns +1: $) .* SmoothFall;
end
plot(Dit)
FileName =sprintf("%sMorseSoundFileGenerator_Dit_%db.jpg", OutputDirectory, EnvelopeSmoothingMethod);
xs2jpg(gcf(), FileName);// Export to a JPG file.
input('Check the graphic window then press enter to continue.')
clf;
MorseText = TextToMorse(text)
[_rows, _columns] =size(MorseText);
SoundData =[];
for i =1: _rows
// printf("\n%s", MorseText(i));
if MorseText(i)==''then
SoundData =[SoundData, InterWords];
else
for j =1:length(MorseText(i))
// printf("\n%03d %03d %s", i, j, part(MorseText(i), j:j));
selectpart(MorseText(i), j:j),
case'-'then SoundData =[SoundData, Dah],
case'.'then SoundData =[SoundData, Dit],
elseprintf("\nCannot send %s",part(MorseText(i), j:j));
end
SoundData =[SoundData, InterElements]
end
SoundData =[SoundData, InterCharacters]
end
end
analyze(SoundData, ToneFrequency -300, ToneFrequency +300, SamplingRate,8192);
FileName =sprintf("%sMorseSoundFileGenerator_Analysis_%dc.jpg", OutputDirectory, EnvelopeSmoothingMethod);
xs2jpg(gcf(), FileName);// Export to a JPG file.
// wavwrite expects data arranged with one channel per column
FileName =sprintf("%sMorseSoundFileGenerator_%d.wav", OutputDirectory, EnvelopeSmoothingMethod);
wavwrite(SoundData', SamplingRate, BitDepth, FileName);
printf('Check the %s directory.', OutputDirectory)
//////////////////////////////////////////////////////////////////////////////
//
// Morse sound file generator
//
// Generate a .wav file playing user text in morse
//
// Copyright (C) 2012 Christophe DAVID (ON6ZQ)
// http://www.on6zq.be/Scilab/MorseSoundFileGenerator
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License version 2 as published
// by the Free Software Foundation (http://www.gnu.org/licenses/gpl.html).
//
//////////////////////////////////////////////////////////////////////////////
//
// This program was last tested with Scilab 5.5
// http://www.scilab.org
//
//////////////////////////////////////////////////////////////////////////////
// A semicolon at the end of an instruction causes the result not to be displayed.
errclear; // clear all errors
clear; // kill variables
clearglobal // kill global variables
clc; // clear command window
tohome; // move the cursor to the upper left corner of the Command Window
clf; // clear or reset the current graphic figure (window) to default values
// stacksize('max'); // increase the size of this stack to the maximum.
stacksize(1e8); //stacksize('max') does not appear to work on my system ;-(
//////////////////////////////////////////////////////////////////////////////
// text = "PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS PARIS "; // 1 minute at 12 WPM
// text = "The Quick Brown Fox Jumps Over The Lazy Dog 1234567890"; // all alphanumerical characters
text ="73 de ON6ZQ";
WordsPerMinute = 20;
OutputDirectory ='/home/cda/test/';
ToneFrequency = 600;// hertz
SamplingRate =22500;// samples per second
BitDepth = 16;// 8, 16, 24, 32 bits. number of bits each sample
EnvelopeSmoothingMethod =2// 0: none, 1: cosine, 2: Gaussian
RiseTime =0.010// second
FallTime =0.010// second
//////////////////////////////////////////////////////////////////////////////
function[OutputMatrix]= TextToMorse(InputString)
morse =[ // ASCII values and meanings
'', // 1
'', // 2
'', // 3
'', // 4
'', // 5
'', // 6
'', // 7
'', // 8
'', // 9
'', // 10
'', // 11
'', // 12
'', // 13
'', // 14
'', // 15
'', // 16
'', // 17
'', // 18
'', // 19
'', // 20
'', // 21
'', // 22
'', // 23
'', // 24
'', // 25
'', // 26
'', // 27
'', // 28
'', // 29
'', // 30
'', // 31
'', // 32
'', // 33 !
'', // 34 "
'', // 35 #
'', // 36 $
'', // 37 %
'', // 38 &
'', // 39 '
'', // 40 (
'', // 41 )
'', // 42 *
'', // 43 +
'', // 44 ,
'', // 45 -
'', // 46 .
'', // 47 /
'-----',// 48 0
'.----',// 49 1
'..---',// 50 2
'...--',// 51 3
'....-',// 52 4
'.....',// 53 5
'-....',// 54 6
'--...',// 55 7
'---..',// 56 8
'----.',// 57 9
'', // 58 :
'', // 59 ;
'', // 60 <
'', // 61 =
'', // 62 >
'', // 63 ?
'', // 64 @
'.-', // 65 A
'-...', // 66 B
'-.-.', // 67 C
'-..', // 68 D
'.', // 69 E
'..-.', // 70 F
'--.', // 71 G
'....', // 72 H
'..', // 73 I
'.---', // 74 J
'-.-', // 75 K
'.-..', // 76 L
'--', // 77 M
'-.', // 78 N
'---', // 79 O
'.--.', // 80 P
'--.-', // 81 Q
'.-.', // 82 R
'...', // 83 S
'-', // 84 T
'..-', // 85 U
'...-', // 86 V
'.--', // 87 W
'-..-', // 88 X
'-.--', // 89 Y
'--..', // 90 Z
'', // 91 [
'', // 92 \
'', // 93 ]
'', // 94 ^
'', // 95 _
'', // 96 `
'.-', // 97 a
'-...', // 98 b
'-.-.', // 99 c
'-..', // 100 d
'.', // 101 e
'..-.', // 102 f
'--.', // 103 g
'....', // 104 h
'..', // 105 i
'.---', // 106 j
'-.-', // 107 k
'.-..', // 108 l
'--', // 109 m
'-.', // 110 n
'---', // 111 o
'.--.', // 112 p
'--.-', // 113 q
'.-.', // 114 r
'...', // 115 s
'-', // 116 t
'..-', // 117 u
'...-', // 118 v
'.--', // 119 w
'-..-', // 120 x
'-.--', // 121 y
'--..', // 122 z
'', // 123 {
'', // 124 |
'', // 125 }
'', // 126 ~
'', // 127
];
OutputMatrix =[];
M =asciimat(InputString);
for i =1:length(M)
// printf("\n%03d %s %s", M(i), ascii(M(i)), morse(M(i)));
OutputMatrix(i)= morse(M(i));
end
endfunction
//////////////////////////////////////////////////////////////////////////////
function f = GaussDistribution(x, mu, s)
p1 =-.5 *((x - mu)/ s) .^ 2;
p2 =(s *sqrt(2*%pi));
f =exp(p1) ./ p2;
endfunction
//////////////////////////////////////////////////////////////////////////////
// http://www.kent-engineers.com/codespeed.htm
// http://www.arrl.org/files/file/Technology/x9004008.pdf
UnitsPerDit =1;
UnitsPerDah =3;
UnitsBetweenElements =1;
UnitsBetweenCharacters =3;
UnitsBetweenWords =7;
UnitDuration =1.2/ WordsPerMinute; // seconds
InterElements =zeros(1,(UnitsBetweenElements * UnitDuration * SamplingRate))
InterCharacters =zeros(1,(UnitsBetweenCharacters * UnitDuration * SamplingRate))
InterWords =zeros(1,(UnitsBetweenWords * UnitDuration * SamplingRate))
Dit =0:1:(UnitsPerDit * UnitDuration * SamplingRate);
Dah =0:1:(UnitsPerDah * UnitDuration * SamplingRate);
Dit = sind((Dit / SamplingRate)* ToneFrequency *360);
Dah = sind((Dah / SamplingRate)* ToneFrequency *360);
if EnvelopeSmoothingMethod >0then
// http://www.w8ji.com/cw_bandwidth_described.htm
// http://fermi.la.asu.edu/w9cf/articles/click/index.html
SmoothRise =1:1:(RiseTime * SamplingRate);
SmoothFall =(FallTime * SamplingRate):-1:1;
select EnvelopeSmoothingMethod
case1then
SmoothRise = sind((SmoothRise / SamplingRate)*(1/ RiseTime) *90);
SmoothFall = sind((SmoothFall / SamplingRate)*(1/ FallTime) *90);
case2then
mu = RiseTime * SamplingRate;
s =(RiseTime * SamplingRate)/2;
SmoothRise = GaussDistribution(SmoothRise, mu, s);
SmoothFall = GaussDistribution(SmoothFall, mu, s);
SmoothRise = SmoothRise *(1/max(SmoothRise));
SmoothFall = SmoothFall *(1/max(SmoothFall));
elseabort;
end
plot(SmoothRise,'g')
plot(SmoothFall,'r')
legend(['SmoothRise';'SmoothFall']);
FileName =sprintf("%sMorseSoundFileGenerator_Envelope_%da.jpg", OutputDirectory, EnvelopeSmoothingMethod);
xs2jpg(gcf(), FileName);// Export to a JPG file.
input('Check the graphic window then press enter to continue.')
clf;
[_rows, _columns]=size(SmoothRise);
Dit(1:_columns) = Dit(1:_columns) .* SmoothRise;
Dah(1:_columns) = Dah(1:_columns) .* SmoothRise;
[_rows, _columns]=size(SmoothFall);
Dit($ - _columns +1: $)= Dit($ - _columns +1: $) .* SmoothFall;
Dah($ - _columns +1: $)= Dah($ - _columns +1: $) .* SmoothFall;
end
plot(Dit)
FileName =sprintf("%sMorseSoundFileGenerator_Dit_%db.jpg", OutputDirectory, EnvelopeSmoothingMethod);
xs2jpg(gcf(), FileName);// Export to a JPG file.
input('Check the graphic window then press enter to continue.')
clf;
MorseText = TextToMorse(text)
[_rows, _columns] =size(MorseText);
SoundData =[];
for i =1: _rows
// printf("\n%s", MorseText(i));
if MorseText(i)==''then
SoundData =[SoundData, InterWords];
else
for j =1:length(MorseText(i))
// printf("\n%03d %03d %s", i, j, part(MorseText(i), j:j));
selectpart(MorseText(i), j:j),
case'-'then SoundData =[SoundData, Dah],
case'.'then SoundData =[SoundData, Dit],
elseprintf("\nCannot send %s",part(MorseText(i), j:j));
end
SoundData =[SoundData, InterElements]
end
SoundData =[SoundData, InterCharacters]
end
end
analyze(SoundData, ToneFrequency -300, ToneFrequency +300, SamplingRate,8192);
FileName =sprintf("%sMorseSoundFileGenerator_Analysis_%dc.jpg", OutputDirectory, EnvelopeSmoothingMethod);
xs2jpg(gcf(), FileName);// Export to a JPG file.
// wavwrite expects data arranged with one channel per column
FileName =sprintf("%sMorseSoundFileGenerator_%d.wav", OutputDirectory, EnvelopeSmoothingMethod);
wavwrite(SoundData', SamplingRate, BitDepth, FileName);
printf('Check the %s directory.', OutputDirectory)
//////////////////////////////////////////////////////////////////////////////