I just made this, a few days ago I got interested in morse code. I decided to make myself a teaching tool, I lost interest in morse code while programming this. If I ever get into a horrible car crash and become paralyzed with the only way left of communicating is through a blinking light controlled by my brain waves, well I'm fucked.
The -t / -m options work:
PS K:\>.\morse.exe -t "Hello Evilzone"
str:
Hello Evilzone
morse:
.... . .-.. .-.. --- . ...- .. .-.. --.. --- -. .
Although morse to text, isn't finished. As the morse code is spelled out, you'll here the dit-dit-dah-dit of the morse code.
The -i option I added was an attempt at an interactive mode, tapping the spacebar as morse code input. My attempt at reproducing above "Hello Evilzone":
PS K:\>.\morse.exe -i
....[H] .[E] .-..[L] .[E] -[T] .
Needless to say I suck at it, and have lost all interest to learn. To change speed is still handled with macros, didn't bother with morse code way of "wpm".
You will need GCC(MinGW, DevCpp) to compile it, as MSVC++ doesn't have <getopt.h>. Also for audio, winmm is required to be linked.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include <windows.h>
#define DIT_BUFFSIZE (1024)
#define DAH_BUFFSIZE (3 * 1024)
/* defaults */
#define GAP_PAUSE 40 // ms
#define FREQ 700 // Hz
typedef struct
{
char ascii;
char* morse;
} morse_key;
#define TABLE_LEN (sizeof(morse_table) / sizeof(morse_key))
#define MAX_MORSE 10
const morse_key morse_table[] =
{
{'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',"--.."},
{'1',".----"},
{'2',"..---"},
{'3',"...--"},
{'4',"....-"},
{'5',"....."},
{'6',"-...."},
{'7',"--..."},
{'8',"---.."},
{'9',"----."},
{'0',"-----"},
{'.',".-.-.-"},
{',',"--..--"},
{'?',"..--.."},
{'\'',".----."},
{'!',"-.-.--"},
{'/',"-..-."},
{'(',"-.--."},
{')',"-.--.-"},
{'&',".-..."},
{':',"---..."},
{';',"-.-.-."},
{'=',"-...-"},
{'+',".-.-."},
{'-',"-....-"},
{'_',"..--.-"},
{'"',".-..-."},
{'$',"...-..-"},
{'@',".--.-."},
};
#define HELPMSG \
"use:\r\n" \
" morse.exe [ -i | [-t <text> | -m <morse>]] OPTIONS\r\n" \
"options:\r\n" \
" -f <freq> sound frequency(Hz)\r\n"
#define err(str) \
do { perror(str); ExitProcess(1); } while(0)
#define eprintf(str,...) \
fprintf(stderr, str,##__VA_ARGS__)
#define die(str,...) \
do { eprintf(str,##__VA_ARGS__); ExitProcess(1); } while(0)
#define help() \
die(HELPMSG)
// custom malloc functions, with error checking
inline void *xcalloc(ssize_t size)
{
register void *ret;
if((ret = calloc(size, 1)) == NULL)
err("calloc");
return ret;
}
inline void *xrealloc(void *ptr, ssize_t size)
{
register void *ret;
if((ret = realloc(ptr, size)) == NULL)
err("realloc");
return ret;
}
void args(int, char**);
LRESULT CALLBACK LowLevelKeyboardProc(int, WPARAM, LPARAM);
int mchar_to_char(char*, char*);
int char_to_mchar(char, char*, int);
int str_to_mstr(const char*, int);
void play_morse(char*, int);
HWAVEOUT hWaveOut;
WAVEFORMATEX WaveFormat;
WAVEHDR WaveHeader;
HANDLE callback;
union
{
char dit[DIT_BUFFSIZE]; // .
char dah[DAH_BUFFSIZE]; // -
} audio_buff;
char *mcode, *morse_code = NULL, *text = NULL;
int mcode_len = 0, freq = FREQ, sleep_gap = GAP_PAUSE, interactive_mode = 0;
int main(int argc, char **argv)
{
args(argc, argv);
int i, morse_code_len;
HHOOK hhk;
MSG msg;
WaveFormat.wFormatTag = WAVE_FORMAT_PCM; // PCM
WaveFormat.nChannels = 1; // Mono
WaveFormat.nSamplesPerSec = 11025; // 11.025 kHz
WaveFormat.wBitsPerSample = 8;
WaveFormat.nBlockAlign = 1;
WaveFormat.nAvgBytesPerSec = WaveFormat.nSamplesPerSec * WaveFormat.nBlockAlign;
WaveFormat.cbSize = 0;
WaveHeader.dwFlags = 0;
WaveHeader.dwLoops = 0;
for(i = 0; i < DAH_BUFFSIZE; i++)
audio_buff.dah[i] = (char)(127 * sin(i * 2 * M_PI * FREQ / WaveFormat.nSamplesPerSec) + 128);
if((callback = CreateEvent(0, 0, 0, 0)) == NULL)
err("CreateEvent");
if(waveOutOpen(&hWaveOut, 0, &WaveFormat, (DWORD)callback, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR)
err("waveOutOpen");
if(interactive_mode)
{
if((hhk = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, NULL, 0)) == NULL)
err("SetWindowsHookEx");
while(GetMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST))
;
UnhookWindowsHookEx(hhk);
}
else
{
if(morse_code == NULL)
{
mcode = xcalloc(1);
morse_code_len = str_to_mstr(text, strlen(text));
morse_code = mcode;
}
else
{
if(text == NULL)
{
// todo: convert morse code to text
text = "HAI!";
}
morse_code_len = strlen(morse_code);
}
printf("str:\r\n %s\r\nmorse:\r\n", text);
play_morse(morse_code, morse_code_len);
printf("\r\n");
}
waveOutClose(hWaveOut);
CloseHandle(callback);
if(mcode_len)
free(mcode);
return 0;
}
void args(int argc, char **argv)
{
int c;
while((c = getopt(argc, argv, "m:t:f:s:i")) != -1)
switch(c)
{
case 'm': morse_code = optarg; break;
case 't': text = optarg; break;
case 'f': freq = atoi(optarg); break;
case 's': sleep_gap = atoi(optarg); break;
case 'i': interactive_mode++; break;
default: break;
}
if(morse_code != NULL && text != NULL)
eprintf("warning: both -m (morse code) & -t (text) selected, using -m ...\r\n");
if(!interactive_mode && morse_code == NULL && text == NULL)
help();
}
#define CLOCKS_CHAR (0.2 * CLOCKS_PER_SEC)
#define CLOCKS_SPACE (0.8 * CLOCKS_PER_SEC)
LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wParam, LPARAM lParam)
{
static clock_t downtime = 0, uptime = 0;
static int keyisdown = 0, morse_len = 0;
static char morse[MAX_MORSE], *p = morse;
char c;
if(((PKBDLLHOOKSTRUCT)lParam)->vkCode == VK_SPACE)
{
if(wParam == WM_KEYDOWN && !keyisdown)
{
keyisdown = 1;
downtime = clock();
if((clock() - uptime) >= CLOCKS_SPACE && morse_len)
{
*p++ = 0;
if(mchar_to_char(morse, &c) == 0)
printf("[%c] ", c);
else
printf("<UN> ");
morse_len = 0;
p = morse;
}
}
else if(wParam == WM_KEYUP && downtime != 0)
{
keyisdown = 0;
uptime = clock();
if(morse_len == (MAX_MORSE - 3))
{
printf("<TL> ");
morse_len = 0;
p = morse;
}
*p = ((clock() - downtime) >= CLOCKS_CHAR) ? '-' : '.' ;
play_morse(p, 1);
p++; morse_len++;
}
}
return CallNextHookEx(NULL, code, wParam, lParam);
}
int mchar_to_char(char *morse, char *c)
{
int i;
for(i = 0; i < TABLE_LEN; i++)
if(strcmp(morse, morse_table[i].morse) == 0)
{
*c = morse_table[i].ascii;
return 0;
}
return 1;
}
int char_to_mchar(char ascii, char *buff, int len)
{
int i, l;
for(i = 0; i < TABLE_LEN; i++)
if(morse_table[i].ascii == ascii)
if((l = strlen(morse_table[i].morse)) < len)
{
strcpy(buff, morse_table[i].morse);
return l;
}
return -1;
}
// feel free to take an axe to this function
int str_to_mstr(const char *str, int len)
{
int i, l;
char buff[MAX_MORSE], c;
mcode_len = 0;
for(i = 0; i < len; i++)
{
c = toupper(str[i]);
if(c != ' ')
{
if((l = char_to_mchar(c, buff, MAX_MORSE)) != -1)
{
mcode_len += l + 1;
mcode = xrealloc(mcode, mcode_len + 1);
strcat(mcode, buff); strcat(mcode, " ");
}
else eprintf("warning: unreconized morse char '%c', skipping ...\r\n", c);
}
else
{
mcode_len += 2;
mcode = xrealloc(mcode, mcode_len + 1);
strcat(mcode, " ");
}
}
return mcode_len;
}
void play_morse(char *morse, int len)
{
int i;
for(i = 0; i < len; i++)
{
printf("%c", morse[i]);
switch(morse[i])
{
case '.':
WaveHeader.lpData = audio_buff.dit;
WaveHeader.dwBufferLength = DIT_BUFFSIZE;
break;
case '-':
WaveHeader.lpData = audio_buff.dah;
WaveHeader.dwBufferLength = DAH_BUFFSIZE;
break;
case ' ':
Sleep(sleep_gap * 3);
continue;
default: break;
}
Sleep(sleep_gap);
if(waveOutPrepareHeader(hWaveOut, &WaveHeader, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
err("waveOutPrepareHeader");
ResetEvent(callback);
if(waveOutWrite(hWaveOut, &WaveHeader, sizeof(WaveHeader)) != MMSYSERR_NOERROR)
err("waveOutWrite");
if(WaitForSingleObject(callback, INFINITE) == WAIT_FAILED)
err("WaitForSingleObject");
if(waveOutUnprepareHeader(hWaveOut, &WaveHeader, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
err("waveOutUnprepareHeader");
}
}