Created
May 26, 2024 23:29
-
-
Save Bioblaze/c2061964c6ca0044643f4d8b80c47c45 to your computer and use it in GitHub Desktop.
Morse Code Generator/Reader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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