Thread: Morse simple audio decoder at C

  1. #1
    Registered User
    Join Date
    Feb 2021
    Posts
    3

    Morse simple audio decoder at C

    Hello,
    I'm new at coding and I just started a new project at C, I'm trying to make an encoder and decoder for Morse code (with audio) that's means I can put text and encode it to audio beeps (morse - dot and dash) as you can see below:

    Code:
    
    
    Code:
    #define _CRT_SECURE_NO_WARNINGS // To ingore VisualStudio C language errors
    #include <windows.h> // To use output of the computer (speakers)
    #include <utilapiset.h> // To use Beep function
    #include <stdio.h> // To use standard input/output functions
    #include <time.h> // To use time library of C 
    #include <stdlib.h> // To use more standard functions
    
    
    #include "delay.h" // To call delay.h function
    
    
    
    
    int main()
    {
    //----------------------------------Setting & Variables----------------------------------------//
    	system("cls");
    	char* alphamorse[] = { 
    	".-",//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 
    	};// All the alphabet in morse
    	char* nummorse[] = { 
    	"-----",//0
    	".----",//1
    	"..---",//2
    	"...--",//3
    	"....-",//4
    	".....",//5
    	"-....",//6
    	"--...",//7
    	"---..",//8
    	"----."//9
    	}; // All the numbers in morse
    	char T[100]; // Transmit 
    	//char R; // Receive
    	char enter[1]; // To recive enter input without interruption
    	int i = 0 ,j=0;
    	int count = 0;
    	unsigned int freq=550,dash=300,dot=100; // Recommended --> dash time x3 then dot
    	unsigned int delaydd = 0; // delay between dash/dots
    	unsigned int input = 0; //defult value of menu input 
    
    
    //---------------------------------------Menu UI-----------------------------------------------//
    
    
    Menu:
    	input = 0; count = 0; i = 0, j = 0; // reset the Setting & Variables values
    	system("cls"); //clear screen
    	printf("\nDaniel Hoasov Cohen\n");
    	printf("\nUltrasonic Data Transmit & Receive - POC\n \nChoose one of the following options by entering the number:\n" "\n1.Transmit\n" "2.Receive\n" "3.Configuration\n" "4.Exit\n"); //UI main menu
    	do
    	{
    		printf("\nyour choose:");
    		scanf_s("%d", &input); // scan --> user input
    	} while (input > 4 || input < 0);
    
    
    	system("cls"); //clear screen
    
    
    //---------------------------------------Main Menu---------------------------------------------//
    	switch (input)
    	{
    	case 1: goto Transmit; break; //if user input = 1 go to Transmit
    	//case 2: record(); break; //if user input = 2 go to Receive
    	case 3: goto Configuration; break; //if user input = 3 go to Configuration
    	case 4: exit(1); break; //if user input = 4 exit the software
    	}
    	return 0;
    
    
    //----------------------------------Configuration Option---------------------------------------//
    
    
    Configuration:
    
    
    	printf("\nCurrent configuration:"); // Configuration status
    	printf("\n Frequancy [Hz]: %d", freq);
    	printf("\n Dash time [msec]: %d", dash);
    	printf("\n Dot time [msec]: %d", dot); 
    	printf("\n Delay between Dash/Dot [msec]: %d", delaydd);
    
    
    
    
    	printf("\n\nConfiguration edit:"); // Configuration status
    	printf("\n Enter Frequancy in the range 37 through 25000 [Hz]: \n"); // UI --> frequancy (Default --> unsigned int freq=550)
    	scanf_s("%d", &freq); // To recive input from user keyboard
    	gets(enter);//recive the 'Enter' pressing from user without interruption for the gets below
    	printf("\n Dash ('-') duration [msec]: \n"); // UI --> dash duration (Default 300msec)
    	scanf_s("%d", &dash); // To recive input from user keyboard
    	gets(enter);//recive the 'Enter' pressing from user without interruption for the gets below
    	printf("\n Dot ('.') duration [msec]: \n"); // UI --> dash duration (Default 100msec)
    	scanf_s("%d", &dot); // To recive input from user keyboard
    	gets(enter);//recive the 'Enter' pressing from user without interruption for the gets below
    	printf("\n Delay between Dash/Dot duration [msec]: \n"); // UI --> delay duration (Default 0msec)
    	scanf_s("%d", &delaydd); // To recive input from user keyboard
    	gets(enter);//recive the 'Enter' pressing from user without interruption for the gets below
    
    
    	goto Menu;
    
    
    //------------------------------------Transmit Option------------------------------------------//
    
    
    Transmit:
    	system("cls"); //clear screen
    	gets(enter);//recive the 'Enter' pressing from user without interruption for the gets below
    	printf("\nEnter a sentence:\n"); // UI --> string
    	gets(T); // To recive input from user keyboard
    	printf("\nYour input:\n"); // UI --> string show
    	puts(T); // To check user input
    
    
    	printf("\nMorse convert in progress...\n"); // UI
    	while (T[i] != '\0') // Convert the string to morse (the end of the string '\0')
    	{
    		if (T[i] != ' ' && (!isdigit(T[i]))) // if user input isnt space or number
    		{
    			printf("%s ", alphamorse[toupper(T[i]) - 65]); // Print the a-z string
    			printf("\nMorse sound in progress...\n");
    			for (j = 0; alphamorse[toupper(T[i]) - 65][j] != '\0'; j++) // Counting the numbers of dash/dots in the word 
    				count++;
    			for (j = 0; j < count; j++)// j is the index of character 
    			{
    				if (alphamorse[toupper(T[i]) - 65][j] == '.') // if index j is dot
    				{
    					delaymilisec(delaydd); // Delay between Dash/Dot
    					Beep(freq, dot); // More information below
    				}
    				else if (alphamorse[toupper(T[i]) - 65][j] == '-')// if index j is dash
    				{
    					delaymilisec(delaydd); // Delay between Dash/Dot
    					Beep(freq, dash); // More information below
    				}
    			}
    		}
    
    
    		if (T[i] == ' ') // if string with space print the space
    		{
    			printf(" ");
    			delaysec(1);// Delay to separate between spaces 
    		}
    
    
    		if (isdigit(T[i]) && T[i] != ' ')// if user input is number but isnt space
    		{
    			printf("%s ", nummorse[T[i] - 48]);// print the 0-9 string
    			printf("\nMorse sound in progress...\n");
    			for (j = 0; nummorse[T[i] - 48][j] != '\0'; j++) // Counting the numbers of dash/dots in the word 
    				count++;
    			for (j = 0; j < count; j++)// j is the index of character 
    			{
    				if (nummorse[T[i] - 48][j] == '.') // if index j is dot
    				{
    					delaymilisec(delaydd); // Delay between Dash/Dot
    					Beep(freq, dot); // More information below
    				}
    				else if (nummorse[T[i] - 48][j] == '-')// if index j is dash
    				{
    					delaymilisec(delaydd); // Delay between Dash/Dot
    					Beep(freq, dash); // More information below
    				}
    			}
    		}
    		i++; // i is the index of the word
    	}
    	delaysec(1*count); // delay 2sec muliply counting the dash/dots
    	goto Menu;
    	}




    the all idea is to generate morse code with specific configuration by the user (Frequency, time of dot, time of dash, time of interval between dot and dash)

    but now I'm stuck at the decoder... I don't know what to do, I thought to open the microphone for live audio input and it will decode the morse code via the frequency of dot/dash and the time of them.

    I started to do this code to use the microphone of the laptop (took it from AudioPort --> Paex record example)

    Code:
    
    
    Code:
    /** @file paex_record.c
    	@ingroup examples_src
    	@brief Record input into an array; Save array to a file; Playback recorded data.
    	@author Phil Burk  http://www.softsynth.com
    */
    /*
     * $Id$
     *
     * This program uses the PortAudio Portable Audio Library.
     * For more information see: http://www.portaudio.com
     * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
     *
     * Permission is hereby granted, free of charge, to any person obtaining
     * a copy of this software and associated documentation files
     * (the "Software"), to deal in the Software without restriction,
     * including without limitation the rights to use, copy, modify, merge,
     * publish, distribute, sublicense, and/or sell copies of the Software,
     * and to permit persons to whom the Software is furnished to do so,
     * subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be
     * included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
     * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
     * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     */
    /*
     * The text above constitutes the entire PortAudio license; however, 
     * the PortAudio community also makes the following non-binding requests:
     *
     * Any person wishing to distribute modifications to the Software is
     * requested to send the modifications to the original developer so that
     * they can be incorporated into the canonical version. It is also 
     * requested that these non-binding requests be included along with the 
     * license above.
     */
    
    
    #define _CRT_SECURE_NO_WARNINGS // To ingore VisualStudio C language errors
    
    
    #include <stdio.h>// To use standard input/output functions
    #include <stdlib.h>// To use more standard functions
    #include <portaudio.h>// To use an external lib and include --> microphone input
    
    
     //-----------------------------------------Configuration-------------------------------------------------//
    
    
    #define SAMPLE_RATE  (44100) // #define SAMPLE_RATE  (17932) --> Test failure to open with this value.
    #define FRAMES_PER_BUFFER (512)
    #define NUM_SECONDS     (3)
    #define NUM_CHANNELS    (2)
    // #define DITHER_FLAG     (paDitherOff)
    #define DITHER_FLAG     (0) /**/
    #define WRITE_TO_FILE   (1) // Set to 1 if you want to capture the recording to a file.
    
    
    //---------------------------------------Select sample format---------------------------------------------//
    #if 1
    #define PA_SAMPLE_TYPE  paFloat32
    typedef float SAMPLE;
    #define SAMPLE_SILENCE  (0.0f)
    #define PRINTF_S_FORMAT "%.8f"
    #elif 1
    #define PA_SAMPLE_TYPE  paInt16
    typedef short SAMPLE;
    #define SAMPLE_SILENCE  (0)
    #define PRINTF_S_FORMAT "%d"
    #elif 0
    #define PA_SAMPLE_TYPE  paInt8
    typedef char SAMPLE;
    #define SAMPLE_SILENCE  (0)
    #define PRINTF_S_FORMAT "%d"
    #else
    #define PA_SAMPLE_TYPE  paUInt8
    typedef unsigned char SAMPLE;
    #define SAMPLE_SILENCE  (128)
    #define PRINTF_S_FORMAT "%d"
    #endif
    
    
    typedef struct
    {
        int          frameIndex;  // Index into sample array.
        int          maxFrameIndex;
        SAMPLE      *recordedSamples;
    }
    paTestData;
    
    
    /************************************************************************************ 
    ****This routine will be called by the PortAudio engine when audio is needed.    ****
    ****It may be called at interrupt level on some machines so don't do anything    ****
    ****that could mess up the system like calling malloc() or free().               ****
    ************************************************************************************/
    
    
    static int recordCallback( const void *inputBuffer, void *outputBuffer,
                               unsigned long framesPerBuffer,
                               const PaStreamCallbackTimeInfo* timeInfo,
                               PaStreamCallbackFlags statusFlags,
                               void *userData )
    {
        paTestData *data = (paTestData*)userData;
        const SAMPLE *rptr = (const SAMPLE*)inputBuffer;
        SAMPLE *wptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
        long framesToCalc;
        long i;
        int finished;
        unsigned long framesLeft = data->maxFrameIndex - data->frameIndex;
    
    
        (void) outputBuffer; // Prevent unused variable warnings. 
        (void) timeInfo;
        (void) statusFlags;
        (void) userData;
    
    
        if( framesLeft < framesPerBuffer )
        {
            framesToCalc = framesLeft;
            finished = paComplete;
        }
        else
        {
            framesToCalc = framesPerBuffer;
            finished = paContinue;
        }
    
    
        if( inputBuffer == NULL )
        {
            for( i=0; i<framesToCalc; i++ )
            {
                *wptr++ = SAMPLE_SILENCE;  // left 
                if( NUM_CHANNELS == 2 ) *wptr++ = SAMPLE_SILENCE;  // right 
            }
        }
        else
        {
            for( i=0; i<framesToCalc; i++ )
            {
                *wptr++ = *rptr++;  // left 
                if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++;  // right
            }
        }
        data->frameIndex += framesToCalc;
        return finished;
    }
    
    
    /**********************************************************************************
    ****This routine will be called by the PortAudio engine when audio is needed.  ****
    ****It may be called at interrupt level on some machines so don't do anything  ****
    ****that could mess up the system like calling malloc() or free().             ****   
    **********************************************************************************/
    
    
    static int playCallback( const void *inputBuffer, void *outputBuffer,
                             unsigned long framesPerBuffer,
                             const PaStreamCallbackTimeInfo* timeInfo,
                             PaStreamCallbackFlags statusFlags,
                             void *userData )
    {
        paTestData *data = (paTestData*)userData;
        SAMPLE *rptr = &data->recordedSamples[data->frameIndex * NUM_CHANNELS];
        SAMPLE *wptr = (SAMPLE*)outputBuffer;
        unsigned int i;
        int finished;
        unsigned int framesLeft = data->maxFrameIndex - data->frameIndex;
    
    
        (void) inputBuffer; // Prevent unused variable warnings.
        (void) timeInfo;
        (void) statusFlags;
        (void) userData;
    
    
        if( framesLeft < framesPerBuffer )
        {
            
            for( i=0; i<framesLeft; i++ ) // final buffer...
            {
                *wptr++ = *rptr++;  // left
                if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++;  // right 
            }
            for( ; i<framesPerBuffer; i++ )
            {
                *wptr++ = 0;  /* left */
                if( NUM_CHANNELS == 2 ) *wptr++ = 0;  // right 
            }
            data->frameIndex += framesLeft;
            finished = paComplete;
        }
        else
        {
            for( i=0; i<framesPerBuffer; i++ )
            {
                *wptr++ = *rptr++;  // left 
                if( NUM_CHANNELS == 2 ) *wptr++ = *rptr++;  // right 
            }
            data->frameIndex += framesPerBuffer;
            finished = paContinue;
        }
        return finished;
    }
    
    
    /*******************************************************************/
    int main(void);
    int main(void)
    {
        PaStreamParameters  inputParameters,
                            outputParameters;
        PaStream*           stream;
        PaError             err = paNoError;
        paTestData          data;
        int                 i;
        int                 totalFrames;
        int                 numSamples;
        int                 numBytes;
        SAMPLE              max, val;
        double              average;
        char input_user;
    
    
    
    
        //---------------------------------------Waiting for input to start---------------------------------------------//
    
    
        printf("\nPress any key to record\n"); 
        input_user=getch();
    
    
        //---------------------------------------------Prepare to record--------------------------------------------------//
    
    
        printf("patest_record.c\n"); fflush(stdout);
    
    
        data.maxFrameIndex = totalFrames = NUM_SECONDS * SAMPLE_RATE; // Record for a few seconds.
        data.frameIndex = 0;
        numSamples = totalFrames * NUM_CHANNELS;
        numBytes = numSamples * sizeof(SAMPLE);
        data.recordedSamples = (SAMPLE *) malloc( numBytes ); // From now on, recordedSamples is initialised.
        if( data.recordedSamples == NULL )
        {
            printf("Could not allocate record array.\n");
            goto done;
        }
        for( i=0; i<numSamples; i++ ) data.recordedSamples[i] = 0;
    
    
        err = Pa_Initialize();
        if( err != paNoError ) goto done;
    
    
        inputParameters.device = Pa_GetDefaultInputDevice(); // default input device 
        if (inputParameters.device == paNoDevice) {
            fprintf(stderr,"Error: No default input device.\n");
            goto done;
        }
        inputParameters.channelCount = 2; // stereo input 
        inputParameters.sampleFormat = PA_SAMPLE_TYPE;
        inputParameters.suggestedLatency = Pa_GetDeviceInfo( inputParameters.device )->defaultLowInputLatency;
        inputParameters.hostApiSpecificStreamInfo = NULL;
    
    
        //---------------------------------------------Start to record--------------------------------------------------//
        err = Pa_OpenStream(
                  &stream,
                  &inputParameters,
                  NULL,                  /* &outputParameters, */
                  SAMPLE_RATE,
                  FRAMES_PER_BUFFER,
                  paClipOff,      /* we won't output out of range samples so don't bother clipping them */
                  recordCallback,
                  &data );
        if( err != paNoError ) goto done;
    
    
        err = Pa_StartStream( stream );
        if( err != paNoError ) goto done;
        printf("\n=== Now recording. ===\n"); fflush(stdout);
    
    
        while( ( err = Pa_IsStreamActive( stream ) ) == 1 )
        {
            Pa_Sleep(1000);
            printf("index = %d\n", data.frameIndex ); fflush(stdout);
        }
        if( err < 0 ) goto done;
    
    
        err = Pa_CloseStream( stream );
        if( err != paNoError ) goto done;
    
    
        // Measure maximum peak amplitude.
        max = 0;
        average = 0.0;
        for( i=0; i<numSamples; i++ )
        {
            val = data.recordedSamples[i];
            if( val < 0 ) val = -val; // ABS
            if( val > max )
            {
                max = val;
            }
            average += val;
        }
    
    
        average = average / (double)numSamples;
    
    
        printf("sample max amplitude = "PRINTF_S_FORMAT"\n", max );
        printf("sample average = %lf\n", average );
    
    
        printf("\nPress any key to listen the playback\n");
        input_user = getch();
    
    
    #if WRITE_TO_FILE // Write recorded data to a file.
        {
            FILE  *fid;
            fid = fopen("recorded.raw", "wb");
            if( fid == NULL )
            {
                printf("Could not open file.");
            }
            else
            {
                fwrite( data.recordedSamples, NUM_CHANNELS * sizeof(SAMPLE), totalFrames, fid );
                fclose( fid );
                printf("Wrote data to 'recorded.raw'\n");
            }
        }
    #endif
    
    
        //---------------------------------------------Playback the record--------------------------------------------------//
        data.frameIndex = 0;
    
    
        outputParameters.device = Pa_GetDefaultOutputDevice(); // default output device
        if (outputParameters.device == paNoDevice) {
            fprintf(stderr,"Error: No default output device.\n");
            goto done;
        }
        outputParameters.channelCount = 2;                     /* stereo output */
        outputParameters.sampleFormat =  PA_SAMPLE_TYPE;
        outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
        outputParameters.hostApiSpecificStreamInfo = NULL;
    
    
        printf("\n=== Now playing back the record. ===\n"); fflush(stdout);
        err = Pa_OpenStream(
                  &stream,
                  NULL, // no input 
                  &outputParameters,
                  SAMPLE_RATE,
                  FRAMES_PER_BUFFER,
                  paClipOff,      /* we won't output out of range samples so don't bother clipping them */
                  playCallback,
                  &data );
        if( err != paNoError ) goto done;
    
    
        if( stream )
        {
            err = Pa_StartStream( stream );
            if( err != paNoError ) goto done;
            
            printf("Waiting for playback to finish.\n"); fflush(stdout);
    
    
            while( ( err = Pa_IsStreamActive( stream ) ) == 1 ) Pa_Sleep(100);
            if( err < 0 ) goto done;
            
            err = Pa_CloseStream( stream );
            if( err != paNoError ) goto done;
            
            printf("Done.\n"); fflush(stdout);
        }
    
    
    done:
        Pa_Terminate();
        if( data.recordedSamples ) // Sure it is NULL or valid.
            free( data.recordedSamples );
        if( err != paNoError )
        {
            fprintf( stderr, "An error occured while using the portaudio stream\n" );
            fprintf( stderr, "Error number: %d\n", err );
            fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
            err = 1; // Always return 0 or 1, but no other return codes.
        }
    
    
        printf("\nPress any key to exit\n");
        input_user = getch();
    
    
        return err;
    }


    how can I continue? I don't know what to do now.
    I really need your help.

  2. #2
    Registered User
    Join Date
    Feb 2019
    Posts
    886
    I really hope this is not a project you got by curiosity, because decoding an audio containing morse code isn't that easy.

    First, your code must be aware of "noise". One way to do it is to transform the audio to frequency domain using algorithms like fft (fast fourier transform) and using a specific range of frequencies to consider as a "valid" "on" signal. This can be hard since transmissions over long distances and/or different medium can distort the signal, affecting the central frequency.

    Second, your code need to be aware of timing... Manual morse code transmissions can vary in speed: A dot is, of course shorter than a dash, but for different people (and different in time too) the time of those symbols can be even equal (the software must decide).

    Third, Your software must do all this in runtime (I suppose)...

    And, finally, since you are using some begginer's coding tricks, I'm assuming you are not that experienced.

  3. #3
    Registered User
    Join Date
    Feb 2021
    Posts
    3
    Quote Originally Posted by flp1969 View Post
    I really hope this is not a project you got by curiosity, because decoding an audio containing morse code isn't that easy.

    First, your code must be aware of "noise". One way to do it is to transform the audio to frequency domain using algorithms like fft (fast fourier transform) and using a specific range of frequencies to consider as a "valid" "on" signal. This can be hard since transmissions over long distances and/or different medium can distort the signal, affecting the central frequency.

    Second, your code need to be aware of timing... Manual morse code transmissions can vary in speed: A dot is, of course shorter than a dash, but for different people (and different in time too) the time of those symbols can be even equal (the software must decide).

    Third, Your software must do all this in runtime (I suppose)…

    And, finally, since you are using some beginner's coding tricks, I'm assuming you are not that experienced.
    yes as I say, I'm new at coding and its my first project that i want to do.
    you are wright about the FFT but i think i want it even simple. I know that I using high frequency so the noise cant get in that frequencies, I know that dash its X time and dot its Y time and they are in some Z[kHz] frequency so i just want to know how can I take that data from the record

  4. #4
    Registered User
    Join Date
    Sep 2020
    Posts
    291
    I can help you with this if you want. It will be rather long and involved (with learning simple DSP involved too).

    Mayne email me (hamster@snap.net.nz) if you want to work on it together....

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Simple Manchester decoder
    By weez in forum C Programming
    Replies: 9
    Last Post: 01-14-2013, 05:53 PM
  2. Replies: 2
    Last Post: 04-09-2012, 02:01 PM
  3. Built simple audio player without gui
    By sauronnikko in forum C++ Programming
    Replies: 6
    Last Post: 02-01-2011, 01:09 AM
  4. Replies: 14
    Last Post: 11-23-2005, 08:53 AM
  5. C decoder for files encoded in Morse code
    By amsy in forum C Programming
    Replies: 5
    Last Post: 10-22-2004, 12:40 AM

Tags for this Thread