Skip to content

Instantly share code, notes, and snippets.

@Bioblaze
Created May 26, 2024 23:29
Show Gist options
  • Save Bioblaze/c2061964c6ca0044643f4d8b80c47c45 to your computer and use it in GitHub Desktop.
Save Bioblaze/c2061964c6ca0044643f4d8b80c47c45 to your computer and use it in GitHub Desktop.
Morse Code Generator/Reader
public static class MorseCodeConverter
{
private static readonly Dictionary<char, string> _morseCodeDictionary = new Dictionary<char, 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', "----." }, { ' ', "/" }
};
public static string TextToMorse(string input)
{
var output = new List<string>();
foreach (var character in input.ToUpper())
{
if (_morseCodeDictionary.ContainsKey(character))
{
output.Add(_morseCodeDictionary[character]);
}
else
{
throw new ArgumentException($"Character '{character}' cannot be converted to Morse code.");
}
}
return string.Join(" ", output);
}
public static string MorseToText(string morseCode)
{
var morseCodeWords = morseCode.Split(' ');
var output = new List<char>();
foreach (var morseCodeWord in morseCodeWords)
{
var character = _morseCodeDictionary.FirstOrDefault(x => x.Value == morseCodeWord).Key;
if (character != '\0')
{
output.Add(character);
}
else
{
throw new ArgumentException($"Morse code '{morseCodeWord}' cannot be converted to text.");
}
}
return new string(output.ToArray());
}
}
public class MorseCodeAudioGenerator
{
private const int SampleRate = 44100; // 44.1kHz
private const int DitDuration = 100; // Duration of a "dit" in milliseconds
private const int Frequency = 1000; // Frequency of the tone in Hz
public static void GenerateMorseCodeWav(string text, string filePath)
{
var morseCode = MorseCodeConverter.TextToMorse(text);
var samples = GenerateMorseCodeSamples(morseCode);
WriteWavFile(samples, filePath);
}
private static short[] GenerateMorseCodeSamples(string morseCode)
{
var samples = new List<short>();
int ditSamples = SampleRate * DitDuration / 1000;
int dahSamples = 3 * ditSamples;
int intraCharSpaceSamples = ditSamples;
int interCharSpaceSamples = 3 * ditSamples;
int interWordSpaceSamples = 7 * ditSamples;
foreach (var symbol in morseCode)
{
switch (symbol)
{
case '.':
Console.WriteLine($"Generating dot of length: {ditSamples} samples");
samples.AddRange(GenerateTone(ditSamples));
samples.AddRange(GenerateSilence(intraCharSpaceSamples));
break;
case '-':
Console.WriteLine($"Generating dash of length: {dahSamples} samples");
samples.AddRange(GenerateTone(dahSamples));
samples.AddRange(GenerateSilence(intraCharSpaceSamples));
break;
case ' ':
Console.WriteLine($"Generating inter-character space of length: {interCharSpaceSamples} samples");
samples.AddRange(GenerateSilence(interCharSpaceSamples));
break;
case '/':
Console.WriteLine($"Generating inter-word space of length: {interWordSpaceSamples} samples");
samples.AddRange(GenerateSilence(interWordSpaceSamples));
break;
}
}
// Ensure trailing silence for the end of the last character or word
samples.AddRange(GenerateSilence(interWordSpaceSamples));
return samples.ToArray();
}
private static IEnumerable<short> GenerateTone(int durationSamples)
{
double theta = 2 * Math.PI * Frequency / SampleRate;
for (int i = 0; i < durationSamples; i++)
{
yield return (short)(32760 * Math.Sin(theta * i));
}
Console.WriteLine($"Generated tone of length: {durationSamples} samples");
}
private static IEnumerable<short> GenerateSilence(int durationSamples)
{
for (int i = 0; i < durationSamples; i++)
{
yield return 0;
}
Console.WriteLine($"Generated silence of length: {durationSamples} samples");
}
private static void WriteWavFile(short[] samples, string filePath)
{
using (var writer = new BinaryWriter(File.Create(filePath)))
{
// Write WAV header
writer.Write(new char[] { 'R', 'I', 'F', 'F' });
writer.Write(36 + samples.Length * sizeof(short));
writer.Write(new char[] { 'W', 'A', 'V', 'E' });
writer.Write(new char[] { 'f', 'm', 't', ' ' });
writer.Write(16);
writer.Write((short)1);
writer.Write((short)1);
writer.Write(SampleRate);
writer.Write(SampleRate * sizeof(short));
writer.Write((short)sizeof(short));
writer.Write((short)16);
writer.Write(new char[] { 'd', 'a', 't', 'a' });
writer.Write(samples.Length * sizeof(short));
// Write samples
foreach (var sample in samples)
{
writer.Write(sample);
}
}
}
}
public class MorseCodeAudioReader
{
private const int SampleRate = 44100; // Same as in Generator
private const int DitDuration = 100; // Dit duration in milliseconds
private const int Frequency = 1000; // Frequency used for tone generation
private const int AmplitudeThreshold = 5000; // Threshold to distinguish tone from silence
public static string ReadMorseCodeFromWav(string filePath)
{
short[] samples = ReadWavFile(filePath);
string morseCode = DecodeMorseCode(samples);
return MorseCodeConverter.MorseToText(morseCode);
}
private static short[] ReadWavFile(string filePath)
{
using (var reader = new BinaryReader(File.OpenRead(filePath)))
{
reader.ReadBytes(44); // Skip the WAV header
var samples = new List<short>();
while (reader.BaseStream.Position != reader.BaseStream.Length)
{
samples.Add(reader.ReadInt16());
}
return samples.ToArray();
}
}
private static string DecodeMorseCode(short[] samples)
{
var morseCode = new StringBuilder();
int ditSamples = SampleRate * DitDuration / 1000;
int dahSamples = 3 * ditSamples;
int intraCharSpaceSamples = ditSamples;
int interCharSpaceSamples = 3 * ditSamples;
int interWordSpaceSamples = 7 * ditSamples;
int index = 0;
int amplitudeThreshold = 1000; // Lower the threshold to better isolate tones
int minDuration = 50; // Lower the minimum duration to filter out noise
Console.WriteLine($"Dit samples: {ditSamples}");
Console.WriteLine($"Dah samples: {dahSamples}");
Console.WriteLine($"Intra-char space samples: {intraCharSpaceSamples}");
Console.WriteLine($"Inter-char space samples: {interCharSpaceSamples}");
Console.WriteLine($"Inter-word space samples: {interWordSpaceSamples}");
while (index < samples.Length)
{
int toneLength = 0;
while (index < samples.Length && Math.Abs(samples[index]) > amplitudeThreshold)
{
toneLength++;
index++;
}
if (toneLength >= minDuration)
{
Console.WriteLine($"Tone length detected: {toneLength} samples");
if (toneLength >= ditSamples * 0.9 && toneLength <= ditSamples * 1.1)
{
Console.WriteLine("Detected a dot");
morseCode.Append(".");
}
else if (toneLength >= dahSamples * 0.9 && toneLength <= dahSamples * 1.1)
{
Console.WriteLine("Detected a dash");
morseCode.Append("-");
}
else
{
Console.WriteLine("Warning: Tone length does not match expected dot or dash length.");
}
}
int silenceLength = 0;
while (index < samples.Length && Math.Abs(samples[index]) <= amplitudeThreshold)
{
silenceLength++;
index++;
}
if (silenceLength >= minDuration)
{
Console.WriteLine($"Silence length detected: {silenceLength} samples");
if (silenceLength >= interWordSpaceSamples * 0.75)
{
Console.WriteLine("Detected a word space");
morseCode.Append(" / ");
}
else if (silenceLength >= interCharSpaceSamples * 0.75)
{
Console.WriteLine("Detected a character space");
morseCode.Append(" ");
}
else if (silenceLength >= intraCharSpaceSamples * 0.75)
{
// Intra-character space, do nothing
}
else
{
Console.WriteLine("Warning: Silence length below expected intra-char space.");
}
}
}
Console.WriteLine($"Decoded Morse Code: {morseCode.ToString()}");
return morseCode.ToString().Trim();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment