Can we generate morse code?

Greetings,

I’m learning a bit of C# for a unity game, and one of my first ideas is to generate morse code tone identifiers for parts.

I understand how to play audio clips, but I’d rather not use 36 or so clips to accomplish my goal. I’m looking to generate a series of tones based on a text identity or user data, add noise and a doppler shift if possible.

Can anyone provide some insight to help get me started?

Many thanks in advance.

-M

I think the easiest way would be to have two audio sources each playing a different clip, one for “dot” and one for “dash”. Then you could have a script that translates your message into a big List of dashes,dots, and pauses, (or maybe a string of ., -, and space, to make it easier to debug) and then pass that to a Coroutine that would call audioSource.Play and then Wait on the two audio sources as needed.

AudioSource: Unity - Scripting API: AudioSource
Coroutines: Unity - Scripting API: MonoBehaviour.StartCoroutine

Awesome stuff! Thanks

I’ve popped out a simple Morse code player for you. I was bored and this seemed like a fun little diversion. The full project is attached to this post.

C#

using UnityEngine;
using System.Collections;
using System.Text.RegularExpressions;

public class MorseCodePlayer : MonoBehaviour {
	public AudioClip dotSound;
	public AudioClip dashSound;
	public float spaceDelay;
	public float letterDelay;
	
	// International Morse Code Alphabet
	private string[] alphabet = 
    //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     
	 ".....", "-....", "--...", "---..", "----."};

	// Use this for initialization
	void Start () 
	{
		// H   E  L    L   O       W  O    R   L   D
		//.... . .-.. .-.. ---    .-- --- .-. .-.. -..
		PlayMorseCodeMessage("Hello World.");
	}
	
	public void PlayMorseCodeMessage(string message)
	{
		StartCoroutine("_PlayMorseCodeMessage", message);
	}
	
	private IEnumerator _PlayMorseCodeMessage(string message)
	{
		// Remove all characters that are not supported by Morse code...
		Regex regex = new Regex("[^A-z0-9 ]");
		message = regex.Replace(message.ToUpper(), "");
		
		// Convert the message into Morse code audio... 
		foreach(char letter in message)
		{
			if (letter == ' ')
				yield return new WaitForSeconds(spaceDelay);
			else
			{
				int index = letter - 'A';
				if (index < 0)
					index = letter - '0' + 26;
				string letterCode = alphabet[index];
				foreach(char bit in letterCode)
				{
					// Dot or Dash?
					AudioClip       sound = dotSound;
					if (bit == '-')	sound = dashSound;
					
					// Play the audio clip and wait for it to end before playing the next one.
					audio.PlayOneShot(sound);
					yield return new WaitForSeconds(sound.length + letterDelay);
				}
			}
		}
	}
}

Unity version 3.5.6
1055632–39213–$MorseCodeGenerator.zip (313 KB)

3 Likes

You’re incredible.

@ makeshiftwings and Brian Stone
Excellent Work!

I think there are so many ways to do that. But i like that logic u used for that.

Thanks for sharing.

sry for bumping. How can I make a lightbulb flash instead of sound? I am trying to do what battlefield 4 devs did in one of the easter eggs.

Wow! I was just looking into how I would go about building something like this to put into my K.I.T.T. project, you just saved me a butt load of time Brian Stone, Now all I need to do is hook it up to a virtual on screen keyboard so Morse code messages could be typed and then executed or played. This is AWESOME man, just AWESOME!! :smile:

Not really sure how to hook it up to a UI input field but it looks like it could be done??

I got it figured out to use a UI Text Input Field to type in the message and a display text to display what you are typing and then a Submit Button so that when you are done typing the message you hit submit and then the Morse Code Message plays, That’s pretty cool.
Working on a button that can set the message to “Repeat” like a Distress Beacon or something.
Now what I’m wondering is if the process can be reversed where the “Beeps” & “Dashes” can be converted back into text? That would be a very cool add to this I think.

Ok I have tweaked the script by Brian Stone,
To use a UI Input field to be abel to type in the text and I have added some button options to play the morse code, I have even added a “Repeat” option so that the morse code can be repeated like a Beacon after a specified time. I would not mind making that time a selectable time setting as I think that would be a useful feature with this awesome code.
But one of the things I’m really wondering about is if the process can be somehow reversed so that it could read back in the beep for dots and dashes and convert them back into UI text?
I was thinking something along the lines of using the AudioClip length to determine if each beep was either a dot or dash and then convert that back to the alphabet and render it out as text… like I say I have an idea of how I think it can be done but I’m not the greatest at code.

using UnityEngine;
using System.Collections;
using System.Text.RegularExpressions;
using UnityEngine.UI;

public class MorseCodePlayer : MonoBehaviour {

    public AudioSource beeper;

    public bool iSOn = false;

    public InputField inputFieldMCode;
    public    Text morseCodeDisplayText;

    public AudioClip dotSound;
    public AudioClip dashSound;
    public float spaceDelay;
    public float letterDelay;
   
    // International Morse Code Alphabet
    private string[] alphabet =
    //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    
     ".....", "-....", "--...", "---..", "----."};

    // Use this for initialization
        void Start ()
    {
        //morseCodeDisplayText = GameObject.Find ("MorseCodeDisplayText").GetComponent<Text> ();
        inputFieldMCode = GameObject.Find ("InputField").GetComponent<InputField> ();
       
        // H   E  L    L   O       W  O    R   L   D
        //.... . .-.. .-.. ---    .-- --- .-. .-.. -..
                //PlayMorseCodeMessage("Hello World.");
                PlayMorseCodeMessage ("");
    }

        public void MorseCodeField(string inputFieldString){
                morseCodeDisplayText.text = inputFieldString;
        }

        public void RepeatMessage (){
                iSOn = !iSOn;
                Debug.Log ("Checking if Bool is On");
                if (iSOn) {
                        InvokeRepeating ("repeat", 15f, 60f);
                        Debug.Log ("I will Invoke Repeat");
                }

                else {
                        Debug.Log ("Confirming Bool is Off!");
                        CancelInvoke("repeat");
                        Debug.Log ("I Am Cancelling Invoke Repeating");
                        beeper = GameObject.Find ("MorseCodeGenerator").GetComponent<AudioSource> ();
                        beeper.mute = true;
                }

               
        }

        //Repeat Message Being Sent after "X" time has passed.
        void repeat (){
                Debug.Log ("Activating Input Field");
                inputFieldMCode.Select();
                inputFieldMCode.ActivateInputField();
                Debug.Log ("I Have Activated Text Field");
                //submitText ();
                //Debug.Log ("I Am Calling messageToSend Function");
                message = inputFieldMCode.text;
                StartCoroutine("_PlayMorseCodeMessage", message);
        }
               

        public void readButton (string c){
               
                inputFieldMCode.text += c;

        }

        string message;
        public void submitText(){
        message = inputFieldMCode.text;
        //inputFieldMCode.Select();
        //inputFieldMCode.ActivateInputField();
        Debug.Log ("I Have Submitted Text");
        beeper = GameObject.Find ("MorseCodeGenerator").GetComponent<AudioSource> ();
        beeper.mute = false;
        StartCoroutine("_PlayMorseCodeMessage", message);
        }

   
   
        public void PlayMorseCodeMessage(string message)
    {
        StartCoroutine("_PlayMorseCodeMessage", message);
    }
   
        private IEnumerator _PlayMorseCodeMessage(string message)
    {
        // Remove all characters that are not supported by Morse code...
        Regex regex = new Regex("[^A-z0-9 ]");
        message = regex.Replace(message.ToUpper(), "");
       
        // Convert the message into Morse code audio...
        foreach(char letter in message)
        {
            if (letter == ' ')
                yield return new WaitForSeconds(spaceDelay);
            else
            {
                int index = letter - 'A';
                if (index < 0)
                    index = letter - '0' + 26;
                string letterCode = alphabet[index];
                foreach(char bit in letterCode)
                {
                    // Dot or Dash?
                    AudioClip       sound = dotSound;
                    if (bit == '-')    sound = dashSound;
                   
                    // Play the audio clip and wait for it to end before playing the next one.
                    GetComponent<AudioSource>().PlayOneShot(sound);
                    yield return new WaitForSeconds(sound.length + letterDelay);
                }
            }
        }
    }

    //Transmit Button
    public void Transmit (){
        //Transmit Code here - most likely Arduino Pulse fro external short wave transmitter

    }

    //Receive Transmitted Morse Code
    public void Receive (){

    }
}

I think the key to reversing the process is in here somewhere but not really sure how to go about making it so that it will read back in the two different beeps for dashes and dots and convert them back into a translated text… would be cool if it could be done as it would make for a cool 2 way morse code communicator.

// Convert the message into Morse code audio...
        foreach(char letter in message)
        {
            if (letter == ' ')
                yield return new WaitForSeconds(spaceDelay);
            else
            {
                int index = letter - 'A';
                if (index < 0)
                    index = letter - '0' + 26;
                string letterCode = alphabet[index];
                foreach(char bit in letterCode)
                {
                    // Dot or Dash?
                    AudioClip       sound = dotSound;
                    if (bit == '-') sound = dashSound;
                  
                    // Play the audio clip and wait for it to end before playing the next one.
                    audio.PlayOneShot(sound);
                    yield return new WaitForSeconds(sound.length + letterDelay);
                }
            }
        }
    }

Updated this a bit since audio wasn’t working.

using UnityEngine;
using System.Collections;
using System.Text.RegularExpressions;

public class MorseCodePlayer : MonoBehaviour {
    public AudioClip dotSound;
    public AudioClip dashSound;
    public float spaceDelay;
    public float letterDelay;
   
    // International Morse Code Alphabet
    private string[] alphabet =
    //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    
     ".....", "-....", "--...", "---..", "----."};

    // Use this for initialization
    void Start ()
    {
       
        // H   E  L    L   O       W  O    R   L   D
        //.... . .-.. .-.. ---    .-- --- .-. .-.. -..
        PlayMorseCodeMessage("CQ CQ DE NA1ISS");
    }
   
    public void PlayMorseCodeMessage(string message)
    {
        StartCoroutine("_PlayMorseCodeMessage", message);
    }
   
    private IEnumerator _PlayMorseCodeMessage(string message)
    {
        // Remove all characters that are not supported by Morse code...
        Regex regex = new Regex("[^A-z0-9 ]");
        message = regex.Replace(message.ToUpper(), "");
       
        // Convert the message into Morse code audio...
        foreach(char letter in message)
        {
            if (letter == ' ')
                yield return new WaitForSeconds(spaceDelay);
            else
            {
                int index = letter - 'A';
                if (index < 0)
                    index = letter - '0' + 26;
                string letterCode = alphabet[index];
                foreach(char bit in letterCode)
                {
                    // Dot or Dash?
                    AudioClip       sound = dotSound;
                    if (bit == '-')    sound = dashSound;

                    // Play the audio clip and wait for it to end before playing the next one.
                    //codeAudio.Play(sound);
                    gameObject.GetComponent<AudioSource>().PlayOneShot(sound);
                   
                    yield return new WaitForSeconds(sound.length + letterDelay);
                }
            }
        }
    }
}

Need to play around with the timing and/or the clips used, as the dot and dit timing is a bit odd and non standard CW. Excellent thread and original script though! Thank you.

I’m trying to build a morse code translator for a small project, but I’m new to Max and I don’t know where to start. Specifically, there will be a button that will be pushed for a certain amount of time, reading as either dots or dashes in morse code, then being displayed as letters, and then printing the message on the screen after an enter button is pushed. It’s almost like a morse code Instant Message. The users will have an alphabet to morse key next to them.
I know it sounds complicated, but I hope you guys can help, or at the very least offer suggestions on how I can approach something like this.
I’m currently using Max 6.1
Thanks

Audacity is not a Unity product, and as such this is not the place for questions about Audacity. I’m not sure how you found yourself here, but try https://forum.audacityteam.org/.

1 Like

Like Madgvox said, the Unity forum is for Unity related development question. Unity is a game engine. While it is theoretically possible to create most kinds of applications, this would be like shooting birds with cannons.

Apart from that the script Brian posted does convert text somewhat into audible morse code in Unity. Though, to be fair, not really practically. Morse code defines one unit of time as the time of a dot. The time between two morse code symbols is one unit of silence. The time between two letters is defined as 3 units of silence. Also the delay between words is defined as 7 units (so a space would have 6 units + the character spacing).

Though working with audio files like this is not that efficient or precise. A unit of time is not standardized. So you could send out 10 symbols per second or 1 per second. The timing just scales linearly up / down.

A better implementation would be this:

MorseCodeGen.cs

using System.Collections;
using UnityEngine;

[RequireComponent(typeof(AudioSource))]
public class MorseCodeGen : MonoBehaviour
{
    private static string[] 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
        "...-", ".--", "-..-", "-.--", "--.."};
    public SineGenerator sineGen;
    private Coroutine m_Coroutine;
    private AudioSource m_Source;

    #region Editor Testing
    // ================================
    // For editor testing only
    public string text = "This is a morse code test with english text";
    public float delay = 0.05f;
    public double frequency = 440;
    public double amplitude = 1;
    public bool startTrigger = false;
    private void Update()
    {
        if (startTrigger)
        {
            startTrigger = false;
            sineGen.Frequency = frequency;
            sineGen.Amplitude = amplitude;
            PlayMorseCode(text, delay);
        }
    }
    // ================================
    #endregion Editor Testing

    void Start()
    {
        m_Source = GetComponent<AudioSource>();
        if (!m_Source)
            m_Source = gameObject.AddComponent<AudioSource>();
        sineGen = new SineGenerator(AudioSettings.outputSampleRate);
    }

    public void PlayMorseCode(string aText, float aDelay = 0.05f)
    {
        Stop();
        m_Coroutine = StartCoroutine(_PlayMorseCode(aText, aDelay));
    }

    public void Stop()
    {
        if (m_Coroutine != null)
            StopCoroutine(m_Coroutine);
        sineGen.Generating = false;
        m_Source.Stop();
    }


    IEnumerator _PlayMorseCode(string aText, float aDelay)
    {
        m_Source.Play();
        yield return null;
        var dot = new WaitForSeconds(aDelay);
        var dash = new WaitForSeconds(aDelay * 2);
        aText = aText.ToUpper();
        foreach(var c in aText)
        {
            if (c == ' ')
                yield return dash; // 3 from last + 2 + 2
            else
            {
                int i = c - 48;
                if (i >= alphabet.Length)
                    continue;
                var str = alphabet[i];
                foreach (var s in str)
                {
                    sineGen.Generating = true;
                    yield return dot;
                    if (s == '-')
                        yield return dash;
                    sineGen.Generating = false;
                    yield return dot;
                }
            }
            yield return dash;
        }
        m_Source.Stop();
        m_Coroutine = null;
    }

    void OnAudioFilterRead(float[] aData, int aChannels)
    {
        for (int i = 0; i < aData.Length; i += aChannels)
        {
            aData[i] = (float)sineGen.GetSample();
            if (aChannels > 1)
                aData[i + 1] = aData[i];
        }
    }

    public class SineGenerator
    {
        private double m_Frequency;
        public double Frequency
        {
            get => m_Frequency;
            set
            {
                m_Frequency = value;
                f_r = System.Math.Cos(m_Frequency * System.Math.PI * 2d / samRate);
                f_i = System.Math.Sin(m_Frequency * System.Math.PI * 2d / samRate);
            }
        }
        public double Amplitude = 1f;
        public bool Generating = false;

        double r = 0;
        double i = 1;
        double f_r;
        double f_i;
        double samRate;
        public SineGenerator(double aSampleRate, double aFreq = 440, double aAmp = 1d)
        {
            samRate = aSampleRate;
            Frequency = aFreq;
            Amplitude = aAmp;
        }

        public double GetSample()
        {
            // continue until we reach a silent spot to avoid cracks
            if (!Generating && System.Math.Abs(r) < 0.01f)
            {
                r = 0;
                i = 1;
                return 0d;
            }
            // complex number multiplication
            double nr = r * f_r - i * f_i;
            double ni = r * f_i + i * f_r;
            // cheap normalizing to keep magnitude of 1.
            double norm = (3d - nr * nr - ni * ni) * 0.5d;
            r = nr * norm;
            i = ni * norm;
            return r * Amplitude;
        }
    }
}

Everything in one file. This contains a complex number “phaser” that generates a sine wave at the desired frequency that can be turned on / off without producing “cracks” due to a sudden stop (it completes the current wave until it reaches almost silence).

As long as you don’t use a crazy low delay this should work with any “dot” delay you want. This is meant to be used by script. So you can call PlayMorseCode with some text and Stop to stop playing. Though it has the “Editor Testing” section which adds some convenient testing variables.

This script automatically adds an AudioSource to the gameobject and handles everything else for you (in just under 160 lines). Just put it on a gameobject and have fun :slight_smile:

1 Like

Yes. Audacity already has the DTMF tone generator plugin… I would start there.

Go get the source code here:

https://github.com/audacity/audacity

and make a few changes so that instead of generating DTMF it generates Morse code.

Hi, I’m working on a project where the player needs to shoot the enemy using morse code according to the alphabet and the numbers labeled. to generate the morse code player must use the mouse key(short press = dot & long press = dash)

Can anyone provide some insight to help get me started?

I guess start by writing some kind of time-based morse code parser.

Once you have that reliably reading morse, then use the letters to control the player.

The first one is the hard one. :slight_smile:

This question was about generating morse code. Detecting user input morse code is significantly more difficult since the speed the user input the code may vary heavily. So an automatic morse code detector would need to determine the rough length of a short and long pulse as well as the length of the different pauses in between.

Dictating a certain speed would most likely fail if the user is too fast or too slow. Keep in mind that the alphabet is not unique and requires the pauses between characters. For example the characters J, P, L, W and 1 all start with an “A” dot - dash. So you need to have pauses in order to distinguish them. Now it highly depends on the desired gameplay. If you (the game) dictates what letter the player should “morse” out, it’s a lot simpler as you just have to record the mouse button signal over time and try to match the relative length of the pulses to the desired pattern. Much simpler, but still not trivial.

1 Like