diff --git a/src/backend-8svx.c b/src/backend-8svx.c deleted file mode 100644 index 94e1262..0000000 --- a/src/backend-8svx.c +++ /dev/null @@ -1,588 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "main.h" -#include "morsegen.h" -#include "backend-count.h" - - -struct SvxMorseGen -{ - struct MorseGen mg; - LONG *smg_Metrics; - LONG smg_SamplingRate; - LONG smg_TonePitch; - LONG smg_WordsPerMinute; - LONG smg_RealWordsPerMinute; /* Farnsworth timing */ - LONG smg_SamplesPerDot; - LONG smg_SamplesPerCharPause; /* Farnsworth timing */ - LONG smg_SamplesPerWordPause; /* Farnsworth timing */ - LONG smg_AttackTime; /* miliseconds */ - LONG smg_ReleaseTime; /* miliseconds */ - BYTE *smg_DotTone; - BYTE *smg_DashTone; - BYTE *smg_Silence; /* long enough for word pause, including Farnsworth */ - STRPTR smg_OutputPath; - BPTR smg_OutputFile; - struct IFFHandle* smg_OutputIFF; - BOOL smg_IFFOpened; - BOOL smg_FormPushed; - BOOL smg_BodyPushed; -}; - - -#define ID_8SVX MAKE_ID('8','S','V','X') -#define ID_VHDR MAKE_ID('V','H','D','R') -#define ID_BODY MAKE_ID('B','O','D','Y') - -struct Vhdr -{ - ULONG OneShotHiSamples; - ULONG RepeatHiSamples; - ULONG SamplesPerHiCycle; - UWORD SamplingRate; - UBYTE Octave; - UBYTE Compression; - ULONG Volume; -}; - - - -/****i* backend-8svx/SvxSetup ************************************************ -* -* NAME -* SvxSetup -- Prepares Morse 8SVX backend to work. -* -* SYNOPSIS -* success = SvxSetup(morsegen, taglist) -* BOOL SvxSetup(struct MorseGen*); -* -* FUNCTION -* Parses the taglist of parameters, overwriting default values. Checks if -* values of these parameters are within allowed ranges. Calculates number of -* audio samples per dot symbol. Allocates two audio buffers, one with -* silence, one with tone. -* -* Control tags: -* MA_SamplingRate - sampling frequency of generated audio file in Hz. -* Default value is 8000 Hz. Allowed range: 1000 to 65535 Hz. -* MA_TonePitch - tone pitch of Morse symbols. Default value is 500 Hz. -* Allowed range is 100 to 8000 Hz with additional requirement, that -* pitch cannot be higher than half of the sampling rate. -* MA_WordsPerMinute - Morse code speed based on PARIS word. Defaults to -* 20 wpm. Allowed range: 5 to 100 wpm. -* -* INPUTS -* morsegen - control structure. -* taglist - parameters. -* -* RESULT -* May be FALSE in following conditions: -* - parameter(s)out of range -* - pitch is higher than half of sampling rate, so sampling theorem is not -* fulfilled -* - failed buffer allocation -* -* NOTES -* Upper sampling rate limit of 65535 Hz is IFF 8SVX format limitation. -* -****************************************************************************** -*/ - -static void ParseTags(struct SvxMorseGen *mg, struct TagItem *taglist) -{ - struct TagItem *tag, *tagptr = taglist; - - while (tag = NextTagItem(&tagptr)) - { - switch (tag->ti_Tag) - { - case MA_SamplingRate: mg->smg_SamplingRate = tag->ti_Data; break; - case MA_TonePitch: mg->smg_TonePitch = tag->ti_Data; break; - case MA_WordsPerMinute: mg->smg_WordsPerMinute = tag->ti_Data; break; - case MA_OutputFile: mg->smg_OutputPath = (STRPTR)tag->ti_Data; break; - case MA_MorseMetrics: mg->smg_Metrics = (LONG*)tag->ti_Data; break; - case MA_EnvAttack: mg->smg_AttackTime = tag->ti_Data; break; - case MA_EnvRelease: mg->smg_ReleaseTime = tag->ti_Data; break; - case MA_RealWordsPerMinute: mg->smg_RealWordsPerMinute = tag->ti_Data; break; - } - } -} - - -static BOOL CreateBody(struct SvxMorseGen *mg, LONG samples) -{ - if (PushChunk(mg->smg_OutputIFF, ID_8SVX, ID_BODY, samples) == 0) - { - mg->smg_BodyPushed = TRUE; - return TRUE; - } - - return FALSE; -} - - -static BOOL WriteHeader(struct SvxMorseGen *mg, LONG samples) -{ - struct Vhdr h; - - h.OneShotHiSamples = samples; - h.RepeatHiSamples = 0; - h.SamplesPerHiCycle = 0; - h.SamplingRate = (UWORD)mg->smg_SamplingRate; - h.Octave = 1; - h.Compression = 0; - h.Volume = 0x10000; - - if (PushChunk(mg->smg_OutputIFF, ID_8SVX, ID_FORM, IFFSIZE_UNKNOWN) == 0) - { - mg->smg_FormPushed = TRUE; - - if (PushChunk(mg->smg_OutputIFF, ID_8SVX, ID_VHDR, sizeof(struct Vhdr)) == 0) - { - if (WriteChunkBytes(mg->smg_OutputIFF, &h, sizeof(struct Vhdr)) == sizeof(struct Vhdr)) - { - if (PopChunk(mg->smg_OutputIFF) == 0) return CreateBody(mg, samples); - } - } - } - - return FALSE; -} - - -static BOOL CalculateAudioSize(struct SvxMorseGen *mg) -{ - LONG samples = 0, samples_per_dash; - - samples_per_dash = (mg->smg_SamplesPerDot << 1) + mg->smg_SamplesPerDot; - samples += SMult32(mg->smg_Metrics[COUNTER_DOTS], mg->smg_SamplesPerDot); - samples += SMult32(mg->smg_Metrics[COUNTER_DASHES], samples_per_dash); - samples += SMult32(mg->smg_Metrics[COUNTER_SYMBOL_PAUSES], mg->smg_SamplesPerDot); - samples += SMult32(mg->smg_Metrics[COUNTER_CHAR_PAUSES], mg->smg_SamplesPerCharPause); - samples += SMult32(mg->smg_Metrics[COUNTER_WORD_PAUSES], mg->smg_SamplesPerWordPause); - Printf("%ld audio samples total.\n", samples); - return WriteHeader(mg, samples); -} - - -static BOOL OpenOutputIFF(struct SvxMorseGen *mg) -{ - if (mg->smg_OutputIFF = AllocIFF()) - { - mg->smg_OutputIFF->iff_Stream = mg->smg_OutputFile; - InitIFFasDOS(mg->smg_OutputIFF); - - if (OpenIFF(mg->smg_OutputIFF, IFFF_WRITE) == 0) - { - mg->smg_IFFOpened = TRUE; - return CalculateAudioSize(mg); - } - else SetErr(RETURN_ERROR, 400); - } - else SetErr(RETURN_ERROR, ERROR_NO_FREE_STORE); - - return FALSE; -} - - -static BOOL OpenOutputFile(struct SvxMorseGen *mg) -{ - if (mg->smg_OutputPath) - { - if (mg->smg_OutputFile = Open(mg->smg_OutputPath, MODE_NEWFILE)) - { - Printf("File $%08lx opened.\n", (LONG)mg->smg_OutputFile); - return OpenOutputIFF(mg); - } - else SetErr(RETURN_ERROR, IoErr()); - } - else SetErr(RETURN_ERROR, ERROR_REQUIRED_ARG_MISSING); - - return FALSE; -} - - -void GenerateTone(BYTE *buffer, LONG count, LONG samprate, LONG pitch) -{ - LONG i; - FLOAT step, fpitch; - - step = IEEESPFlt(samprate); - step = IEEESPDiv(6.28318531f, step); - fpitch = IEEESPFlt(pitch); - step = IEEESPMul(step, fpitch); - - for (i = 0; i < count; i++) - { - FLOAT x; - - x = IEEESPFlt(i); - x = IEEESPMul(x, step); - x = IEEESPSin(x); - x = IEEESPMul(x, 127.0f); - *buffer++ = (BYTE)IEEESPFix(x); - } -} - - -void ApplyEnvelope(BYTE *buffer, LONG count, LONG attack, LONG release) -{ - LONG i; - BYTE *p, s; - - for (p = buffer, i = 0; i < attack; i++) - { - s = *p; - *p++ = div16(mul16(s, i), attack); - } - - for (p = buffer + count - release, i = release - 1; i >= 0; i--) - { - s = *p; - *p++ = div16(mul16(s, i), release); - } -} - -/*--------------------------------------------------------------------------------------------*/ -/* Function is prepared to be moved out of this file, as will be used with more backends. */ -/*--------------------------------------------------------------------------------------------*/ -/* Note that MorsConv knows Morse code metrics in advance, so it could fit RealWPM perfectly. */ -/* However it calculates Farnsworth timing based on PARIS word, the same as ARRL paper. */ -/* It calculates two values, so they are returned via pointers. */ -/*--------------------------------------------------------------------------------------------*/ - -void CalculateFarnsworthPauses(LONG samplesperdot, WORD charwpm, WORD realwpm, LONG samplerate, -LONG *charpause, LONG *wordpause) -{ - LONG alpha, beta, cpauseplus, wpauseplus; - - alpha = SMult32(SMult32(60, samplerate), charwpm - realwpm); - beta = mul16(charwpm, realwpm); /* max 10000 */ - beta = mul16(beta, 19); - cpauseplus = SDivMod32(SMult32(3, alpha), beta); - wpauseplus = SDivMod32(SMult32(7, alpha), beta); - *charpause = SMult32(samplesperdot, 3) + cpauseplus; - *wordpause = SMult32(samplesperdot, 7) + wpauseplus; -} - - -static BOOL PrepareBuffers(struct SvxMorseGen *mg) -{ - LONG attack_samples, release_samples, samples_per_dash; - - mg->smg_SamplesPerDot = SDivMod32(SMult32(mg->smg_SamplingRate, 6), SMult32(mg->smg_WordsPerMinute, 5)); - samples_per_dash = (mg->smg_SamplesPerDot << 1) + mg->smg_SamplesPerDot; - CalculateFarnsworthPauses(mg->smg_SamplesPerDot, mg->smg_WordsPerMinute, mg->smg_RealWordsPerMinute, - mg->smg_SamplingRate, &mg->smg_SamplesPerCharPause, &mg->smg_SamplesPerWordPause); - attack_samples = SDivMod32(SMult32(mg->smg_SamplingRate, mg->smg_AttackTime), 1000); - release_samples = SDivMod32(SMult32(mg->smg_SamplingRate, mg->smg_ReleaseTime), 1000); - - if (mg->smg_DotTone = AllocVec(mg->smg_SamplesPerDot + samples_per_dash + mg->smg_SamplesPerWordPause, - MEMF_ANY | MEMF_CLEAR)) - { - mg->smg_DashTone = mg->smg_DotTone + mg->smg_SamplesPerDot; - mg->smg_Silence = mg->smg_DashTone + samples_per_dash; - GenerateTone(mg->smg_DotTone, mg->smg_SamplesPerDot, mg->smg_SamplingRate, mg->smg_TonePitch); - ApplyEnvelope(mg->smg_DotTone, mg->smg_SamplesPerDot, attack_samples, release_samples); - GenerateTone(mg->smg_DashTone, samples_per_dash, mg->smg_SamplingRate, mg->smg_TonePitch); - ApplyEnvelope(mg->smg_DashTone, samples_per_dash, attack_samples, release_samples); - return OpenOutputFile(mg); - } - - SetErr(RETURN_ERROR, ERROR_NO_FREE_STORE); - return FALSE; -} - - -static BOOL RangeChecks(struct SvxMorseGen *mg) -{ - BOOL success = TRUE; - - /* if RealWPM has been not specified in taglist, it defaults to WPM */ - - if (mg->smg_RealWordsPerMinute == 0) mg->smg_RealWordsPerMinute = mg->smg_WordsPerMinute; - - if (mg->smg_SamplingRate < 1000) success = FALSE; - if (mg->smg_SamplingRate > 65535) success = FALSE; - if (mg->smg_TonePitch < 100) success = FALSE; - if (mg->smg_TonePitch > 8000) success = FALSE; - if ((mg->smg_TonePitch << 2) > mg->smg_SamplingRate) success = FALSE; - if (mg->smg_WordsPerMinute < 5) success = FALSE; - if (mg->smg_WordsPerMinute > 100) success = FALSE; - if (mg->smg_RealWordsPerMinute < 5) success = FALSE; - if (mg->smg_RealWordsPerMinute > 100) success = FALSE; - if (mg->smg_RealWordsPerMinute > mg->smg_WordsPerMinute) success = FALSE; - if (mg->smg_AttackTime < 0) success = FALSE; - if (mg->smg_AttackTime > 50) success = FALSE; - if (mg->smg_ReleaseTime < 0) success = FALSE; - if (mg->smg_ReleaseTime > 50) success = FALSE; - if (success) return PrepareBuffers(mg); - - SetErr(RETURN_ERROR, ERROR_BAD_NUMBER); - return FALSE; -} - - -static BOOL SvxSetup(struct MorseGen *mg, struct TagItem *taglist) -{ - struct SvxMorseGen *smg = (struct SvxMorseGen*)mg; - - ParseTags(smg, taglist); - - if (smg->smg_Metrics) - { - return RangeChecks(smg); - } - else SetErr(RETURN_ERROR, ERROR_REQUIRED_ARG_MISSING); - - return FALSE; -} - - -/****i* backend-8svx/SvxCleanup ********************************************** -* -* NAME -* SvxCleanup -- Frees 8SVX backend resources and disposes it. -* -* SYNOPSIS -* SvxCleanup(morsegen) -* void SvxCleanup(struct MorseGen*); -* -* FUNCTION -* - frees IFF handle, -* - closes the output file, -* - frees audio buffers, -* - frees MorseGen structure. -* -* INPUTS -* morsegen - control structure. Don't call with NULL. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void SvxCleanup(struct MorseGen *mg) -{ - struct SvxMorseGen *smg = (struct SvxMorseGen*)mg; - - if (smg->smg_BodyPushed) PopChunk(smg->smg_OutputIFF); /* BODY */ - if (smg->smg_FormPushed) PopChunk(smg->smg_OutputIFF); /* FORM */ - if (smg->smg_IFFOpened) CloseIFF(smg->smg_OutputIFF); - if (smg->smg_OutputIFF) FreeIFF(smg->smg_OutputIFF); - if (smg->smg_OutputFile) Close(smg->smg_OutputFile); - if (smg->smg_DotTone) FreeVec(smg->smg_DotTone); - FreeMem(mg, sizeof(struct SvxMorseGen)); -} - - - -/****i* backend-8svx/SvxIntraSymbolPause ************************************* -* -* NAME -* SvxIntraSymbolPause -- Generates Morse intra-symbol pause for -* 8SVX backend. -* -* SYNOPSIS -* SvxIntraSymbolPause(morsegen) -* void SvxIntraSymbolPause(struct MorseGen*); -* -* FUNCTION -* Writes silence of dot length to 8SVX file. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void SvxIntraSymbolPause(struct MorseGen *mg) -{ - struct SvxMorseGen *smg = (struct SvxMorseGen*)mg; - - WriteChunkBytes(smg->smg_OutputIFF, smg->smg_Silence, smg->smg_SamplesPerDot); -} - - -/****i* backend-8svx/SvxInterSymbolPause ************************************* -* -* NAME -* SvxInterSymbolPause -- Generates Morse inter-symbol pause for -* 8SVX backend. -* -* SYNOPSIS -* SvxInterSymbolPause(morsegen) -* void SvxInterSymbolPause(struct MorseGen*); -* -* FUNCTION -* Writes silence of inter-symbol lenght to 8SVX file. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void SvxInterSymbolPause(struct MorseGen *mg) -{ - struct SvxMorseGen *smg = (struct SvxMorseGen*)mg; - - WriteChunkBytes(smg->smg_OutputIFF, smg->smg_Silence, smg->smg_SamplesPerCharPause); -} - - -/****i* backend-8svx/SvxInterWordPause *************************************** -* -* NAME -* SvxInterWordPause -- Generates Morse inter-word pause for 8SVX backend. -* -* SYNOPSIS -* SvxInterWordPause(morsegen) -* void SvxInterWordPause(struct MorseGen*); -* -* FUNCTION -* Writes silence of inter-word pause length to 8SVX file. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void SvxInterWordPause(struct MorseGen *mg) -{ - struct SvxMorseGen *smg = (struct SvxMorseGen*)mg; - - WriteChunkBytes(smg->smg_OutputIFF, smg->smg_Silence, smg->smg_SamplesPerWordPause); -} - - -/****i* backend-8svx/SvxShortTone ******************************************** -* -* NAME -* SvxShortTone -- Generates Morse short tone ("dot") for 8SVX backend. -* -* SYNOPSIS -* SvxShortTone(morsegen) -* void SvxShortTone(struct MorseGen*); -* -* FUNCTION -* Writes sinusoidal tone of dot length to 8SVX file. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void SvxShortTone(struct MorseGen *mg) -{ - struct SvxMorseGen *smg = (struct SvxMorseGen*)mg; - - WriteChunkBytes(smg->smg_OutputIFF, smg->smg_DotTone, smg->smg_SamplesPerDot); -} - - -/****i* backend-8svx/SvxLongTone ********************************************* -* -* NAME -* SvxLongTone -- Generates Morse long tone ("dash") for 8SVX backend. -* -* SYNOPSIS -* SvxLongTone(morsegen) -* void SvxLongTone(struct MorseGen*); -* -* FUNCTION -* Writes sinusoidal tone of dash length to 8SVX file. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void SvxLongTone(struct MorseGen *mg) -{ - struct SvxMorseGen *smg = (struct SvxMorseGen*)mg; - LONG samples_per_dash; - - samples_per_dash = (smg->smg_SamplesPerDot << 1) + smg->smg_SamplesPerDot; - WriteChunkBytes(smg->smg_OutputIFF, smg->smg_DashTone, samples_per_dash); -} - - - -/****i* backend-8svx/CreateSvxBackend **************************************** -* -* NAME -* CreateSvxBackend -- Allocates and initializes MorseGen for 8SVX backend. -* -* SYNOPSIS -* morsegen = CreateSvxBackend() -* struct MorseGen* CreateSvxBackend(void); -* -* FUNCTION -* Allocates MorseGen structure and fills it with stdout-backend callbacks. -* Sets default values of audio attributes. Sets audio buffer pointers to -* NULL. -* -* INPUTS -* None. -* -* RESULT -* Pointer to initialized MorseGen structure or NULL if memory allocation -* failed. -* -* SEE ALSO -* SvxCleanup, SvxSetup -* -****************************************************************************** -*/ - -struct MorseGen* CreateSvxBackend(void) -{ - struct SvxMorseGen *smg; - - smg = (struct SvxMorseGen*)AllocMem(sizeof(struct SvxMorseGen), MEMF_ANY | MEMF_CLEAR); - - if (smg) - { - smg->mg.mg_Setup = SvxSetup; - smg->mg.mg_Cleanup = SvxCleanup; - smg->mg.mg_IntraSymbolPause = SvxIntraSymbolPause; - smg->mg.mg_InterSymbolPause = SvxInterSymbolPause; - smg->mg.mg_InterWordPause = SvxInterWordPause; - smg->mg.mg_ShortTone = SvxShortTone; - smg->mg.mg_LongTone = SvxLongTone; - smg->mg.mg_NeedsMetrics = TRUE; - smg->smg_SamplingRate = 8000; - smg->smg_TonePitch = 500; - smg->smg_WordsPerMinute = 20; - smg->smg_ReleaseTime = 1; - } - - return &smg->mg; -} diff --git a/src/backend-8svx.cpp b/src/backend-8svx.cpp new file mode 100644 index 0000000..08c0d5a --- /dev/null +++ b/src/backend-8svx.cpp @@ -0,0 +1,162 @@ +#include +#include + +#include "main.h" +#include "backend-8svx.h" +#include "backend-count.h" + +#define ID_8SVX MAKE_ID('8','S','V','X') +#define ID_VHDR MAKE_ID('V','H','D','R') +#define ID_BODY MAKE_ID('B','O','D','Y') + +struct Vhdr +{ + long OneShotHiSamples; + long RepeatHiSamples; + long SamplesPerHiCycle; + unsigned short SamplingRate; + unsigned char Octave; + unsigned char Compression; + long Volume; +}; + +/*--------------------------------------------------------------------------------------------*/ + +SvxMorseGen::SvxMorseGen(long srate, long pitch, long wpm, long rwpm, long attack, long release, +const char* path, long *metrics) : AudioMorseGen(srate, pitch, wpm, rwpm, attack, release, +metrics) +{ + OutputPath = path; + File = NULL; + Iff = NULL; + IffOpened = FALSE; + DotTone = NULL; + FormPushed = FALSE; + BodyPushed = FALSE; + + if (!Initialized) return; + + Initialized = FALSE; + + if ((SampleRate < 1000) || (SampleRate > 65535)) + { + PutStr("agruments check: sampling rate out of range (1000 - 65535 Hz)\n"); + return; + } + + if (!OutputPath) + { + PutStr("arguments check: no output file specified (missing 'TO' argument)\n"); + return; + } + + if (!PrepareBuffers()) return; + if (!InitStream()) return; + if (!WriteHeader(CalculateAudioSize())) return; + + Initialized = TRUE; +} + +/*--------------------------------------------------------------------------------------------*/ + +short SvxMorseGen::InitStream() +{ + if (File = Open(OutputPath, MODE_NEWFILE)) + { + if (Iff = AllocIFF()) + { + long ifferr; + Iff->iff_Stream = File; + InitIFFasDOS(Iff); + + if ((ifferr = OpenIFF(Iff, IFFF_WRITE)) == 0) + { + IffOpened = TRUE; + return TRUE; + } + else Printf("IFF writer: iffparse error %ld\n", ifferr); + } + else PutStr("IFF writer: out of memory\n"); + } + else PrintFault(IoErr(), "IFF writer"); + + return FALSE; +} + +/*--------------------------------------------------------------------------------------------*/ + +short SvxMorseGen::PrepareBuffers() +{ + LONG attack_samples, release_samples, samples_per_dash; + + samples_per_dash = SamplesPerDot * 3; + attack_samples = SampleRate * AttackTime / 1000; + release_samples = SampleRate * ReleaseTime / 1000; + + if (DotTone = (signed char*)AllocVec(SamplesPerDot + samples_per_dash + SamplesPerWPause, + MEMF_ANY | MEMF_CLEAR)) + { + DashTone = DotTone + SamplesPerDot; + Silence = DashTone + samples_per_dash; + GenerateTone8(DotTone, SamplesPerDot, SampleRate, TonePitch); + ApplyEnvelope8(DotTone, SamplesPerDot, attack_samples, release_samples); + GenerateTone8(DashTone, samples_per_dash, SampleRate, TonePitch); + ApplyEnvelope8(DashTone, samples_per_dash, attack_samples, release_samples); + return TRUE; + } + + PutStr("audio generator: out of memory\n"); + return FALSE; +} + +/*--------------------------------------------------------------------------------------------*/ + +short SvxMorseGen::WriteHeader(long samples) +{ + struct Vhdr h; + + h.OneShotHiSamples = samples; + h.RepeatHiSamples = 0; + h.SamplesPerHiCycle = 0; + h.SamplingRate = (unsigned short)SampleRate; + h.Octave = 1; + h.Compression = 0; + h.Volume = 0x10000; + + if (PushChunk(Iff, ID_8SVX, ID_FORM, IFFSIZE_UNKNOWN) == 0) + { + FormPushed = TRUE; + + if (PushChunk(Iff, ID_8SVX, ID_VHDR, sizeof(Vhdr)) == 0) + { + if (WriteChunkBytes(Iff, &h, sizeof(Vhdr)) == sizeof(Vhdr)) + { + if (PopChunk(Iff) == 0) + { + if (PushChunk(Iff, ID_8SVX, ID_BODY, samples) == 0) + { + BodyPushed = TRUE; + return TRUE; + } + } + } + } + } + + return FALSE; +} + +/*--------------------------------------------------------------------------------------------*/ + + +SvxMorseGen::~SvxMorseGen() +{ + if (BodyPushed) PopChunk(Iff); + if (FormPushed) PopChunk(Iff); + if (DotTone) FreeVec (DotTone); + if (IffOpened) CloseIFF(Iff); + if (Iff) FreeIFF(Iff); + if (File) Close(File); +} + + \ No newline at end of file diff --git a/src/backend-8svx.h b/src/backend-8svx.h index a622b73..7a2edaf 100644 --- a/src/backend-8svx.h +++ b/src/backend-8svx.h @@ -1,3 +1,32 @@ -#include "morsegen.h" +#include -struct MorseGen* CreateSvxBackend(void); +#include "main.h" +#include "morsegenaudio.h" + +class SvxMorseGen : public AudioMorseGen +{ + const char *OutputPath; + IFFHandle *Iff; + BPTR File; + signed char *DotTone; + signed char *DashTone; + signed char *Silence; + short IffOpened; + short FormPushed; + short BodyPushed; + + short InitStream(); + short PrepareBuffers(); + short WriteHeader(long samples); + + public: + + SvxMorseGen(long srate, long pitch, long wpm, long rwpm, long attack, long release, const char* path, + long *metrics); + void IntraSymbolPause() { WriteChunkBytes(Iff, Silence, SamplesPerDot); } + void InterSymbolPause() { WriteChunkBytes(Iff, Silence, SamplesPerCPause); } + void InterWordPause() { WriteChunkBytes(Iff, Silence, SamplesPerWPause); } + void ShortTone() { WriteChunkBytes(Iff, DotTone, SamplesPerDot); } + void LongTone() { WriteChunkBytes(Iff, DashTone, SamplesPerDot * 3); } + ~SvxMorseGen(); +}; diff --git a/src/backend-count.c b/src/backend-count.c deleted file mode 100644 index a154ea6..0000000 --- a/src/backend-count.c +++ /dev/null @@ -1,318 +0,0 @@ -#include -#include -#include - -#include "main.h" -#include "morsegen.h" - -struct CountMorseGen -{ - struct MorseGen mg; - LONG cmg_Dots; - LONG cmg_Dashes; - LONG cmg_SymbolPauses; - LONG cmg_CharPauses; - LONG cmg_WordPauses; - LONG *cmg_CounterStorage; - BOOL cmg_Print; -}; - - -/****i* backend-count/CountSetup ********************************************* -* -* NAME -* CountSetup -- Prepares Morse count backend to work. -* -* SYNOPSIS -* success = CountSetup(morsegen, taglist) -* BOOL CountSetup(struct MorseGen*); -* -* FUNCTION -* Control tags: -* MA_CounterStorage - a pointer to array of 5 LONGs, where values of -* counters are stored during cleanup. Default is NULL (counters are not -* stored anywhere). Order of counters is: dots, dashes, symbol pauses, -* character pauses, word pauses. One can use predefined constants -* COUNTER_xxx as indexes to the array. -* MA_CounterPrint - boolean. If TRUE, values of counters are printed -* to standard output separated by spaces with newline at end. Order of -* counters is the same as described in MA_CounterStorage. Defaults to -* FALSE. -* -* INPUTS -* morsegen - control structure. -* taglist - parameters. -* -* RESULT -* -****************************************************************************** -*/ - -static void ParseTags(struct CountMorseGen *mg, struct TagItem *taglist) -{ - struct TagItem *tag, *tagptr = taglist; - - while (tag = NextTagItem(&tagptr)) - { - switch (tag->ti_Tag) - { - case MA_CounterStorage: mg->cmg_CounterStorage = (LONG*)tag->ti_Data; break; - case MA_CounterPrint: mg->cmg_Print = (BOOL)tag->ti_Data; break; - } - } -} - - -static BOOL CountSetup(struct MorseGen *mg, struct TagItem *taglist) -{ - struct CountMorseGen *cmg = (struct CountMorseGen*)mg; - - ParseTags(cmg, taglist); - return TRUE; -} - - -/****i* backend-count/CountCleanup ******************************************* -* -* NAME -* CountCleanup -- Frees count backend resources and disposes it. -* -* SYNOPSIS -* CountCleanup(morsegen) -* void CountCleanup(struct MorseGen*); -* -* FUNCTION -* - Copies counters to storage array if MA_CounterStorage has been set. -* - Prints values of counters to stdout if MA_CounterPrint has been set. -* - Frees MorseGen structure. -* -* INPUTS -* morsegen - control structure. Don't call with NULL. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void CountCleanup(struct MorseGen *mg) -{ - struct CountMorseGen *cmg = (struct CountMorseGen*)mg; - - if (cmg->cmg_CounterStorage) - { - cmg->cmg_CounterStorage[COUNTER_DOTS] = cmg->cmg_Dots; - cmg->cmg_CounterStorage[COUNTER_DASHES] = cmg->cmg_Dashes; - cmg->cmg_CounterStorage[COUNTER_SYMBOL_PAUSES] = cmg->cmg_SymbolPauses; - cmg->cmg_CounterStorage[COUNTER_CHAR_PAUSES] = cmg->cmg_CharPauses; - cmg->cmg_CounterStorage[COUNTER_WORD_PAUSES] = cmg->cmg_WordPauses; - } - - if (cmg->cmg_Print) - { - Printf("%ld %ld %ld %ld %ld\n", cmg->cmg_Dots, cmg->cmg_Dashes, cmg->cmg_SymbolPauses, - cmg->cmg_CharPauses, cmg->cmg_WordPauses); - } - - FreeMem(mg, sizeof(struct CountMorseGen)); -} - - - -/****i* backend-count/CountIntraSymbolPause ********************************** -* -* NAME -* CountIntraSymbolPause -- Generates Morse intra-symbol pause for -* count backend. -* -* SYNOPSIS -* CountIntraSymbolPause(morsegen) -* void CountIntraSymbolPause(struct MorseGen*); -* -* FUNCTION -* Increments counter of symbol pauses. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void CountIntraSymbolPause(struct MorseGen *mg) -{ - struct CountMorseGen *cmg = (struct CountMorseGen*)mg; - - cmg->cmg_SymbolPauses++; - return; -} - - -/****i* backend-count/CountInterSymbolPause ********************************** -* -* NAME -* CountInterSymbolPause -- Generates Morse inter-symbol pause for -* count backend. -* -* SYNOPSIS -* CountInterSymbolPause(morsegen) -* void CountInterSymbolPause(struct MorseGen*); -* -* FUNCTION -* Increments counter of character pauses. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void CountInterSymbolPause(struct MorseGen *mg) -{ - struct CountMorseGen *cmg = (struct CountMorseGen*)mg; - - cmg->cmg_CharPauses++; - return; -} - - -/****i* backend-count/CountInterWordPause ************************************ -* -* NAME -* CountInterWordPause -- Generates Morse inter-word pause for count backend. -* -* SYNOPSIS -* CountInterWordPause(morsegen) -* void CountInterWordPause(struct MorseGen*); -* -* FUNCTION -* Increments counter of word pauses. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void CountInterWordPause(struct MorseGen *mg) -{ - struct CountMorseGen *cmg = (struct CountMorseGen*)mg; - - cmg->cmg_WordPauses++; - return; -} - - -/****i* backend-count/CountShortTone ***************************************** -* -* NAME -* CountShortTone -- Generates Morse short tone ("dot") for count backend. -* -* SYNOPSIS -* CountShortTone(morsegen) -* void CountShortTone(struct MorseGen*); -* -* FUNCTION -* Increments counter of dots. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void CountShortTone(struct MorseGen *mg) -{ - struct CountMorseGen *cmg = (struct CountMorseGen*)mg; - - cmg->cmg_Dots++; -} - - -/****i* backend-count/CountLongTone ****************************************** -* -* NAME -* CountLongTone -- Generates Morse long tone ("dash") for count backend. -* -* SYNOPSIS -* CountLongTone(morsegen) -* void CountLongTone(struct MorseGen*); -* -* FUNCTION -* Increments counter of dashes. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void CountLongTone(struct MorseGen *mg) -{ - struct CountMorseGen *cmg = (struct CountMorseGen*)mg; - - cmg->cmg_Dashes++; - return; -} - - -/****i* backend-count/CreateCountBackend **************************************** -* -* NAME -* CreateCountBackend -- Allocates and initializes MorseGen for count backend. -* -* SYNOPSIS -* morsegen = CreateCountBackend() -* struct MorseGen* CreateCountBackend(void); -* -* FUNCTION -* Allocates MorseGen structure and fills it with count-backend callbacks. -* Counters are cleared automatically due to MEMF_CLEAR flag for allocation. -* -* INPUTS -* None. -* -* RESULT -* Pointer to initialized MorseGen structure or NULL if memory allocation -* failed. -* -* SEE ALSO -* CountCleanup, CountSetup -* -****************************************************************************** -*/ - -struct MorseGen* CreateCountBackend(void) -{ - struct CountMorseGen *cmg; - - cmg = (struct CountMorseGen*)AllocMem(sizeof(struct CountMorseGen), MEMF_ANY | MEMF_CLEAR); - - if (cmg) - { - cmg->mg.mg_Setup = CountSetup; - cmg->mg.mg_Cleanup = CountCleanup; - cmg->mg.mg_IntraSymbolPause = CountIntraSymbolPause; - cmg->mg.mg_InterSymbolPause = CountInterSymbolPause; - cmg->mg.mg_InterWordPause = CountInterWordPause; - cmg->mg.mg_ShortTone = CountShortTone; - cmg->mg.mg_LongTone = CountLongTone; - cmg->mg.mg_NeedsMetrics = FALSE; - } - - return &cmg->mg; -} diff --git a/src/backend-count.cpp b/src/backend-count.cpp new file mode 100644 index 0000000..a3dd82a --- /dev/null +++ b/src/backend-count.cpp @@ -0,0 +1,36 @@ +#include +#include + +#include "main.h" +#include "backend-count.h" + + +CountMorseGen::CountMorseGen(short print, long *metrics) : MorseGen() +{ + Dots = 0; + Dashes = 0; + SymbolPauses = 0; + CharPauses = 0; + WordPauses = 0; + Storage = metrics; + Print = print; + Initialized = TRUE; +}; + + +CountMorseGen::~CountMorseGen() +{ + if (Storage) + { + Storage[COUNTER_DOTS] = Dots; + Storage[COUNTER_DASHES] = Dashes; + Storage[COUNTER_SYMBOL_PAUSES] = SymbolPauses; + Storage[COUNTER_CHAR_PAUSES] = CharPauses; + Storage[COUNTER_WORD_PAUSES] = WordPauses; + } + + if (Print) + { + Printf("%ld %ld %ld %ld %ld\n", Dots, Dashes, SymbolPauses, CharPauses, WordPauses); + } +} diff --git a/src/backend-count.h b/src/backend-count.h index 3e0f9d9..b7cba27 100644 --- a/src/backend-count.h +++ b/src/backend-count.h @@ -1,3 +1,24 @@ +#include #include "morsegen.h" -struct MorseGen* CreateCountBackend(void); + +class CountMorseGen : public MorseGen +{ + int Dots; + int Dashes; + int SymbolPauses; + int CharPauses; + int WordPauses; + long *Storage; + short Print; + + public: + + CountMorseGen(short print, long *metrics); + void IntraSymbolPause() { SymbolPauses++; }; + void InterSymbolPause() { CharPauses++; }; + void InterWordPause() { WordPauses++; }; + void ShortTone() { Dots++; }; + void LongTone() { Dashes++; }; + ~CountMorseGen(); +}; diff --git a/src/backend-stdout.c b/src/backend-stdout.c deleted file mode 100644 index 8c2f7b6..0000000 --- a/src/backend-stdout.c +++ /dev/null @@ -1,337 +0,0 @@ -#include -#include -#include - -#include "main.h" -#include "morsegen.h" - -struct StdOutMorseGen -{ - struct MorseGen mg; - STRPTR somg_ShortString; - STRPTR somg_LongString; - STRPTR somg_SymbolPauseString; - STRPTR somg_CharPauseString; - STRPTR somg_WordPauseString; -}; - - -/****i* backend-stdout/StdOutSetup ******************************************* -* -* NAME -* StdOutSetup -- Prepares Morse stdout-backend to work. -* -* SYNOPSIS -* success = StdOutSetup(morsegen, taglist) -* BOOL StdOutSetup(struct MorseGen*); -* -* FUNCTION -* For stdout-backend setup function sets parameters of generated outputs -* from the taglist. Following tags are recognized. -* MA_DotText - text emmited for a dot. Default value is ".". -* MA_DashText - text emmited for a dash. Default value is "-". -* -* INPUTS -* morsegen - control structure. -* taglist - parameters. -* -* RESULT -* Always TRUE. -* -****************************************************************************** -*/ - -/*----------------------------------------------------------------- - Replaces escape sequences with characters. - %n -> $0A - %t -> $09 - %% -> % - Unescaped string is always shorter, so it may be done in-place. - Unknown escape sequences are discarded completely. ------------------------------------------------------------------*/ - -static void Unescape(STRPTR s) -{ - STRPTR d = s; - UBYTE c; - - while (c = *s++) - { - if (c == '%') - { - switch (*s++) - { - case 'n': *d++ = 0x0A; break; - case 't': *d++ = 0x09; break; - case '%': *d++ = '%'; break; - } - } - else *d++ = c; - } - - *d = 0x00; -} - - -static BOOL StdOutSetup(struct MorseGen *mg, struct TagItem *taglist) -{ - struct StdOutMorseGen *somg = (struct StdOutMorseGen*)mg; - struct TagItem *tag, *tagptr = taglist; - - while (tag = NextTagItem(&tagptr)) - { - switch (tag->ti_Tag) - { - case MA_DotText: somg->somg_ShortString = (STRPTR)tag->ti_Data; break; - case MA_DashText: somg->somg_LongString = (STRPTR)tag->ti_Data; break; - case MA_SymbolPause: somg->somg_SymbolPauseString = (STRPTR)tag->ti_Data; break; - case MA_CharPause: somg->somg_CharPauseString = (STRPTR)tag->ti_Data; break; - case MA_WordPause: somg->somg_WordPauseString = (STRPTR)tag->ti_Data; break; - } - } - - Unescape(somg->somg_ShortString); - Unescape(somg->somg_LongString); - Unescape(somg->somg_SymbolPauseString); - Unescape(somg->somg_CharPauseString); - Unescape(somg->somg_WordPauseString); - return TRUE; -} - - - -/****i* backend-stdout/StdOutCleanup ***************************************** -* -* NAME -* StdOutCleanup -- Frees stdout-backend resources and disposes it. -* -* SYNOPSIS -* StdOutCleanup(morsegen) -* void StdOutCleanup(struct MorseGen*); -* -* FUNCTION -* StdOutCleanup sends a newline, then just deallocates MorseGen structure. -* -* INPUTS -* morsegen - control structure. Don't call with NULL. -* -* RESULT -* None. -* -******************************************************************************* -*/ - -static void StdOutCleanup(struct MorseGen *mg) -{ - WriteChars("\n", 1); - FreeMem(mg, sizeof(struct StdOutMorseGen)); -} - - - -/****i* backend-stdout/StdOutIntraSymbolPause ******************************** -* -* NAME -* StdOutIntraSymbolPause -- Generates Morse intra-symbol pause for -* stdout-backend. -* -* SYNOPSIS -* StdOutIntraSymbolPause(morsegen) -* void StdOutIntraSymbolPause(struct MorseGen*); -* -* FUNCTION -* Intra-symbol pause is ignored in this backend. Textual representation of -* Morse code provides intra-symbol pauses as separation between characters. -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void StdOutIntraSymbolPause(struct MorseGen *mg) -{ - struct StdOutMorseGen *somg = (struct StdOutMorseGen*)mg; - - PutStr(somg->somg_SymbolPauseString); - return; -} - - -/****i* backend-stdout/StdOutInterSymbolPause ******************************** -* -* NAME -* StdOutInterSymbolPause -- Generates Morse inter-symbol pause for -* stdout-backend. -* -* SYNOPSIS -* StdOutInterSymbolPause(morsegen) -* void StdOutInterSymbolPause(struct MorseGen*); -* -* FUNCTION -* Inter-symbol pause is represented by this backend as a space. It is sent -* to the standard output by WriteChars(). -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void StdOutInterSymbolPause(struct MorseGen *mg) -{ - struct StdOutMorseGen *somg = (struct StdOutMorseGen*)mg; - - PutStr(somg->somg_CharPauseString); - return; -} - - -/****i* backend-stdout/StdOutInterWordPause ********************************** -* -* NAME -* StdOutInterWordPause -- Generates Morse inter-word pause for -* stdout-backend. -* -* SYNOPSIS -* StdOutInterWordPause(morsegen) -* void StdOutInterWordPause(struct MorseGen*); -* -* FUNCTION -* Inter-word pause is represented by this backend as three spaces. They -* are sent to the standard output by WriteChars(). -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void StdOutInterWordPause(struct MorseGen *mg) -{ - struct StdOutMorseGen *somg = (struct StdOutMorseGen*)mg; - - PutStr(somg->somg_WordPauseString); - return; -} - - -/****i* backend-stdout/StdOutShortTone *************************************** -* -* NAME -* StdOutShortTone -- Generates Morse short tone ("dit") for stdout-backend. -* -* SYNOPSIS -* StdOutShortTone(morsegen) -* void StdOutShortTone(struct MorseGen*); -* -* FUNCTION -* StdOut backend emits a specified character (default character is '.') for -* a short tone. Character is sent to stdout using WriteChars(). -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void StdOutShortTone(struct MorseGen *mg) -{ - struct StdOutMorseGen *somg = (struct StdOutMorseGen*)mg; - PutStr(somg->somg_ShortString); -} - - -/****i* backend-stdout/StdOutLongTone **************************************** -* -* NAME -* StdOutLongTone -- Generates Morse long tone ("dat") for stdout-backend. -* -* SYNOPSIS -* StdOutLongTone(morsegen) -* void StdOutLongTone(struct MorseGen*); -* -* FUNCTION -* StdOut backend emits a string (default is "-") for a long tone. String is -* sent to stdout using PutStr(). -* -* INPUTS -* morsegen - control structure. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -static void StdOutLongTone(struct MorseGen *mg) -{ - struct StdOutMorseGen *somg = (struct StdOutMorseGen*)mg; - PutStr(somg->somg_LongString); -} - - - -/****i* backend-stdout/CreateStdOutBackend *********************************** -* -* NAME -* CreateStdOutBackend -- Allocates and initializes MorseGen for stdout -* backend. -* -* SYNOPSIS -* morsegen = CreateStdOutBackend() -* struct MorseGen* CreateStdOutBackend(void); -* -* FUNCTION -* Allocates MorseGen structure and fills it with stdout-backend callbacks. -* -* INPUTS -* None. -* -* RESULT -* Pointer to initialized MorseGen structure or NULL if memory allocation -* failed. -* -* SEE ALSO -* StdOutCleanup, StdOutSetup, StdOutPause, StdOutTone -* -****************************************************************************** -*/ - -struct MorseGen* CreateStdOutBackend(void) -{ - struct StdOutMorseGen *somg; - - somg = (struct StdOutMorseGen*)AllocMem(sizeof(struct StdOutMorseGen), MEMF_ANY); - - if (somg) - { - somg->mg.mg_Setup = StdOutSetup; - somg->mg.mg_Cleanup = StdOutCleanup; - somg->mg.mg_IntraSymbolPause = StdOutIntraSymbolPause; - somg->mg.mg_InterSymbolPause = StdOutInterSymbolPause; - somg->mg.mg_InterWordPause = StdOutInterWordPause; - somg->mg.mg_ShortTone = StdOutShortTone; - somg->mg.mg_LongTone = StdOutLongTone; - somg->mg.mg_NeedsMetrics = FALSE; - somg->somg_ShortString = "."; - somg->somg_LongString = "-"; - somg->somg_SymbolPauseString = ""; - somg->somg_CharPauseString = " "; - somg->somg_WordPauseString = " "; - } - - return &somg->mg; -} diff --git a/src/backend-stdout.cpp b/src/backend-stdout.cpp new file mode 100644 index 0000000..3380d59 --- /dev/null +++ b/src/backend-stdout.cpp @@ -0,0 +1,54 @@ +#include "backend-stdout.h" + +#include +#include +#include + +/*--------------------------------------------------------------------------------------------*/ + +StdOutMorseGen::StdOutMorseGen(char* shstr, char* lnstr, char* spstr, char *cpstr, char *wpstr) +: MorseGen() +{ + ShortString = shstr; + LongString = lnstr; + SymbolPauseString = spstr; + CharPauseString = cpstr; + WordPauseString = wpstr; + Unescape(ShortString); + Unescape(LongString); + Unescape(SymbolPauseString); + Unescape(CharPauseString); + Unescape(WordPauseString); + Initialized = TRUE; +} + +/*--------------------------------------------------------------------------------------------*/ +/* Replaces escape sequences with characters. */ +/* %n -> $0A */ +/* %t -> $09 */ +/* %% -> % */ +/* Unescaped string is always shorter, so it may be done in-place. */ +/* Unknown escape sequences are discarded completely. */ +/*--------------------------------------------------------------------------------------------*/ + +void StdOutMorseGen::Unescape(char *s) +{ + STRPTR d = s; + UBYTE c; + + while (c = *s++) + { + if (c == '%') + { + switch (*s++) + { + case 'n': *d++ = 0x0A; break; + case 't': *d++ = 0x09; break; + case '%': *d++ = '%'; break; + } + } + else *d++ = c; + } + + *d = 0x00; +} diff --git a/src/backend-stdout.h b/src/backend-stdout.h index e33601f..96a5c47 100644 --- a/src/backend-stdout.h +++ b/src/backend-stdout.h @@ -1,3 +1,26 @@ +#include +#include + +#include "main.h" #include "morsegen.h" -struct MorseGen* CreateStdOutBackend(void); +class StdOutMorseGen : public MorseGen +{ + char *ShortString; + char *LongString; + char *SymbolPauseString; + char *CharPauseString; + char *WordPauseString; + + void Unescape(char *s); + + public: + + StdOutMorseGen(char* shstr, char* lnstr, char* spstr, char *cpstr, char *wpstr); + void IntraSymbolPause() { PutStr(SymbolPauseString); } + void InterSymbolPause() { PutStr(CharPauseString); } + void InterWordPause() { PutStr(WordPauseString); } + void ShortTone() { PutStr(ShortString); } + void LongTone() { PutStr(LongString); } + ~StdOutMorseGen() { WriteChars("\n", 1); } +}; diff --git a/src/backend-wave.cpp b/src/backend-wave.cpp new file mode 100644 index 0000000..fac3e1f --- /dev/null +++ b/src/backend-wave.cpp @@ -0,0 +1,133 @@ +#include +#include +#include + +#include "main.h" +#include "backend-wave.h" + +struct WaveHeader +{ + long Riff; + long RiffLen; + long Wave; + long Fmt; + long FmtLen; + short Format; + short Channels; + long SampleRate; + long ByteRate; + short BlockAlign; + short BitsPerSample; + long Data; + long DataLen; +}; + +#define ID_RIFF MAKE_ID('R','I','F','F') +#define ID_WAVE MAKE_ID('W','A','V','E') +#define ID_FMT MAKE_ID('f','m','t',' ') +#define ID_DATA MAKE_ID('d','a','t','a') + +/*--------------------------------------------------------------------------------------------*/ + +WaveMorseGen::WaveMorseGen(long srate, long pitch, long wpm, long rwpm, long attack, long release, +const char* path, long *metrics) : AudioMorseGen(srate, pitch, wpm, rwpm, attack, release, +metrics) +{ + OutputPath = path; + File = NULL; + DotTone = NULL; + + if (!Initialized) return; + + Initialized = FALSE; + + if ((SampleRate < 1000) || (SampleRate > 192000)) + { + PutStr("agruments check: sampling rate out of range (1000 - 192000 Hz)\n"); + return; + } + + if (!OutputPath) + { + PutStr("arguments check: no output file specified (missing 'TO' argument)\n"); + return; + } + + if (!PrepareBuffers()) return; + if (!InitStream()) return; + if (!WriteHeader(CalculateAudioSize())) return; + + Initialized = TRUE; +} + +/*--------------------------------------------------------------------------------------------*/ + +short WaveMorseGen::InitStream() +{ + if (File = Open(OutputPath, MODE_NEWFILE)) return TRUE; + PrintFault(IoErr(), "WAVE writer"); + return FALSE; +} + +/*--------------------------------------------------------------------------------------------*/ + +short WaveMorseGen::PrepareBuffers() +{ + long attack_samples, release_samples, samples_per_dash; + + samples_per_dash = SamplesPerDot * 3; + attack_samples = SampleRate * AttackTime / 1000; + release_samples = SampleRate * ReleaseTime / 1000; + + if (DotTone = (short*)AllocVec((SamplesPerDot + samples_per_dash + + SamplesPerWPause) * sizeof(short), MEMF_ANY | MEMF_CLEAR)) + { + DashTone = DotTone + SamplesPerDot; + Silence = DashTone + samples_per_dash; + GenerateTone16(DotTone, SamplesPerDot, SampleRate, TonePitch); + ApplyEnvelope16(DotTone, SamplesPerDot, attack_samples, release_samples); + ByteSwap16(DotTone, SamplesPerDot); + GenerateTone16(DashTone, samples_per_dash, SampleRate, TonePitch); + ApplyEnvelope16(DashTone, samples_per_dash, attack_samples, release_samples); + ByteSwap16(DashTone, samples_per_dash); + return TRUE; + } + + PutStr("audio generator: out of memory\n"); + return FALSE; +} + +/*--------------------------------------------------------------------------------------------*/ + +short WaveMorseGen::WriteHeader(long samples) +{ + WaveHeader wh; + long bytes = samples << 1; + + wh.Wave = ID_WAVE; + wh.Data = ID_DATA; + wh.DataLen = bswap32(bytes); + wh.Fmt = ID_FMT; + wh.FmtLen = 0x10000000; + wh.Riff = ID_RIFF; + wh.RiffLen = bswap32(bytes + 36); + wh.Format = 0x0100; + wh.Channels = 0x0100; + wh.SampleRate = bswap32(SampleRate); + wh.ByteRate = bswap32(SampleRate << 1); + wh.BlockAlign = 0x0200; + wh.BitsPerSample = 0x1000; + + return FWrite(File, &wh, sizeof(WaveHeader), 1); +} + +/*--------------------------------------------------------------------------------------------*/ + + +WaveMorseGen::~WaveMorseGen() +{ + if (DotTone) FreeVec(DotTone); + if (File) Close(File); +} + + \ No newline at end of file diff --git a/src/backend-wave.h b/src/backend-wave.h new file mode 100644 index 0000000..044d5c4 --- /dev/null +++ b/src/backend-wave.h @@ -0,0 +1,28 @@ +#include + +#include "main.h" +#include "morsegenaudio.h" + +class WaveMorseGen : public AudioMorseGen +{ + const char *OutputPath; + BPTR File; + short *DotTone; /* byteswapped */ + short *DashTone; /* byteswapped */ + short *Silence; /* byteswapped ;-) */ + + short InitStream(); + short PrepareBuffers(); + short WriteHeader(long samples); + + public: + + WaveMorseGen(long srate, long pitch, long wpm, long rwpm, long attack, long release, const char* path, + long *metrics); + void IntraSymbolPause() { FWrite(File, Silence, SamplesPerDot << 1, 1); } + void InterSymbolPause() { FWrite(File, Silence, SamplesPerCPause << 1, 1); } + void InterWordPause() { FWrite(File, Silence, SamplesPerWPause << 1, 1); } + void ShortTone() { FWrite(File, DotTone, SamplesPerDot << 1, 1); } + void LongTone() { FWrite(File, DashTone, SamplesPerDot * 6, 1); } + ~WaveMorseGen(); +}; diff --git a/src/main.c b/src/main.c deleted file mode 100644 index 3a9526f..0000000 --- a/src/main.c +++ /dev/null @@ -1,235 +0,0 @@ -#include -#include -#include - -#include "main.h" -#include "backend-stdout.h" -#include "backend-8svx.h" -#include "backend-count.h" - -ULONG MorErr; /* global error var, return code in upper 16 bits, error code in lower 16 bits */ - - -struct Library - *IntuitionBase, - *UtilityBase, - *MathIeeeSingBasBase, - *MathIeeeSingTransBase, - *IFFParseBase; - - -struct LibHandle -{ - STRPTR lih_Name; - ULONG lih_MinVer; - struct Library **lih_Ptr; -}; - - -struct LibHandle Libraries[] = { - { "intuition.library", 39, &IntuitionBase }, - { "utility.library", 39, &UtilityBase }, - { "mathieeesingbas.library", 37, &MathIeeeSingBasBase }, - { "mathieeesingtrans.library", 37, &MathIeeeSingTransBase }, - { "iffparse.library", 39, &IFFParseBase }, - { NULL, 0, NULL } -}; - -/*----------------------------------------------------------------------------*/ - -BOOL OpenLibs(void) -{ - struct LibHandle *lih = Libraries; - BOOL success = TRUE; - - while (lih->lih_Name) - { - struct Library *lb; - - lb = OpenLibrary(lih->lih_Name, lih->lih_MinVer); - *lih->lih_Ptr = lb; - if (!lb) success = FALSE; - lih++; - } - - return success; -} - -/*----------------------------------------------------------------------------*/ - -void CloseLibs(void) -{ - struct LibHandle *lih = Libraries; - - while (lih->lih_Name) - { - if (*lih->lih_Ptr) CloseLibrary(*lih->lih_Ptr); - *lih->lih_Ptr = NULL; - lih++; - } -} - -/*----------------------------------------------------------------------------*/ - -struct MorsMode -{ - STRPTR mm_Name; - struct MorseGen*(*mm_Creator)(void); -}; - -const struct MorsMode Modes[] = -{ - { "CON", CreateStdOutBackend }, - { "8SVX", CreateSvxBackend }, - { "COUNT", CreateCountBackend }, - { NULL, NULL } -}; - -static struct MorseGen* CreateGenerator(LONG *argvals) -{ - STRPTR modename = (STRPTR)argvals[1]; - const struct MorsMode *mode; - struct MorseGen* generator = NULL; - - for (mode = Modes; mode->mm_Name; mode++) - { - if (Stricmp(mode->mm_Name, modename) == 0) - { - generator = mode->mm_Creator(); - break; - } - } - - /* Set error if mode hasn't been found. */ - - if (!mode->mm_Name) SetErr(RETURN_ERROR, ERROR_NOT_IMPLEMENTED); - return generator; -} - - - -/*----------------------------------------------------------------------------*/ - -LONG PrintMyFault() -{ - LONG err = MorErr & 0xFFFF; - LONG res = MorErr >> 16; - - if (res > 0) - { - if (err > 0) PrintFault(err, "MorsConv"); - else Printf("MorsConv: unknown error %ld\n", err); - } - - return res; -} - -/*----------------------------------------------------------------------------*/ - -BOOL GenerateMetrics(STRPTR text, LONG *metrics) -{ - struct MorseGen *counter; - - if (counter = CreateCountBackend()) - { - BOOL result = FALSE; - - if (MorseGenSetup(counter, - MA_CounterStorage, (ULONG)metrics, - TAG_END)) - { - MorseText(counter, text); - result = TRUE; - } - - counter->mg_Cleanup(counter); - return result; - } - else SetErr(RETURN_ERROR, ERROR_NO_FREE_STORE); - - return FALSE; -} - -/*----------------------------------------------------------------------------*/ - -ULONG Main(void) -{ - LONG result; - - SetErr(RETURN_OK, 0); - - if (OpenLibs()) - { - struct RDArgs *args; - - LONG numdefs[6] = { - 8000, /* SAMPLERATE */ - 500, /* PITCH */ - 20, /* WPM */ - 0, /* ATTACK */ - 1, /* RELEASE */ - 0, /* REALWPM */ - }; - - LONG argvals[14] = { - 0, /* TEXT */ - (LONG)"CON", /* MODE */ - (LONG)".", /* DOT */ - (LONG)"-", /* DASH */ - (LONG)&numdefs[0], /* SAMPLERATE */ - (LONG)&numdefs[1], /* PITCH */ - (LONG)&numdefs[2], /* WPM */ - 0, /* TO */ - (LONG)&numdefs[3], /* ATTACK */ - (LONG)&numdefs[4], /* RELEASE */ - (LONG)"", /* SPAUSE */ - (LONG)" ", /* CPAUSE */ - (LONG)" ", /* WPAUSE */ - (LONG)&numdefs[5] /* REALWPM */ - }; - - if (args = ReadArgs("TEXT/A,MODE/K,DOT/K,DASH/K,SAMPLERATE=SR/K/N,PITCH/K/N,WPM/K/N,TO/K,ATTACK/K/N,RELEASE/K/N,SPAUSE/K,CPAUSE/K,WPAUSE/K,REALWPM/K/N", argvals, NULL)) - { - struct MorseGen *mg; - - if (mg = CreateGenerator(argvals)) - { - LONG metrics[5]; - - if (!mg->mg_NeedsMetrics || GenerateMetrics((STRPTR)argvals[0], metrics)) - { - if (MorseGenSetup(mg, - MA_DotText, argvals[2], - MA_DashText, argvals[3], - MA_SamplingRate, *(LONG*)argvals[4], - MA_TonePitch, *(LONG*)argvals[5], - MA_WordsPerMinute, *(LONG*)argvals[6], - MA_RealWordsPerMinute, *(LONG*)argvals[13], - MA_OutputFile, argvals[7], - MA_CounterPrint, TRUE, - MA_MorseMetrics, (ULONG)metrics, - MA_EnvAttack, *(LONG*)argvals[8], - MA_EnvRelease, *(LONG*)argvals[9], - MA_SymbolPause, argvals[10], - MA_CharPause, argvals[11], - MA_WordPause, argvals[12], - TAG_END)) - { - MorseText(mg, (STRPTR)argvals[0]); - } - } - - mg->mg_Cleanup(mg); - } - - FreeArgs(args); - } - else SetErr(RETURN_ERROR, IoErr()); - - result = PrintMyFault(); - } - else result = RETURN_FAIL; - - CloseLibs(); - return result; -} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..bcff85c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,242 @@ +#include +#include +#include + +#include +#include +#include + +#include "main.h" +#include "backend-stdout.h" +#include "backend-count.h" +#include "backend-8svx.h" +#include "backend-wave.h" + +/* indexes of commandline arguments array */ + +#define MCARG_TEXT 0 +#define MCARG_MODE 1 +#define MCARG_DOT 2 +#define MCARG_DASH 3 +#define MCARG_SAMPRATE 4 +#define MCARG_PITCH 5 +#define MCARG_WPM 6 +#define MCARG_TO 7 +#define MCARG_ATTACK 8 +#define MCARG_RELEASE 9 +#define MCARG_SPAUSE 10 +#define MCARG_CPAUSE 11 +#define MCARG_WPAUSE 12 +#define MCARG_REALWPM 13 + + +extern Library *SysBase, *DOSBase; + +Library *UtilityBase; +Library *MathIeeeSingBasBase; +Library *MathIeeeSingTransBase; +Library *IFFParseBase; + + +struct LibHandle +{ + char *lih_Name; + ULONG lih_MinVer; + Library **lih_Ptr; +}; + +struct LibHandle Libraries[] = { +// { "intuition.library", 39, &IntuitionBase }, + { "utility.library", 39, &UtilityBase }, + { "mathieeesingbas.library", 37, &MathIeeeSingBasBase }, + { "mathieeesingtrans.library", 37, &MathIeeeSingTransBase }, + { "iffparse.library", 39, &IFFParseBase }, + { NULL, 0, NULL } +}; + +struct GenDesc +{ + char *type; + long metrics; +}; + +#define GENERATOR_COUNT 0 +#define GENERATOR_CON 1 +#define GENERATOR_8SVX 2 +#define GENERATOR_WAVE 3 + +#define GENERATORS 4 + +GenDesc Generators[GENERATORS] = { + { "COUNT", FALSE }, + { "CON", FALSE }, + { "8SVX", TRUE }, + { "WAVE", TRUE } +}; + + +BOOL OpenLibs() +{ + LibHandle *lih = Libraries; + BOOL success = TRUE; + + while (lih->lih_Name) + { + Library *lb; + + lb = OpenLibrary(lih->lih_Name, lih->lih_MinVer); + *lih->lih_Ptr = lb; + if (!lb) success = FALSE; + lih++; + } + + return success; +} + +/*----------------------------------------------------------------------------*/ + +void CloseLibs() +{ + LibHandle *lih = Libraries; + + while (lih->lih_Name) + { + if (*lih->lih_Ptr) CloseLibrary(*lih->lih_Ptr); + *lih->lih_Ptr = NULL; + lih++; + } +} + +/*----------------------------------------------------------------------------*/ + +static MorseGen* CreateGenerator(long mode, long *argvals, long *metrics) +{ + MorseGen *generator = NULL; + + switch (mode) + { + case GENERATOR_COUNT: + generator = new CountMorseGen(TRUE, NULL); + break; + + case GENERATOR_CON: + generator = new StdOutMorseGen( + (char*)argvals[MCARG_DOT], + (char*)argvals[MCARG_DASH], + (char*)argvals[MCARG_SPAUSE], + (char*)argvals[MCARG_CPAUSE], + (char*)argvals[MCARG_WPAUSE]); + break; + + case GENERATOR_8SVX: + generator = new SvxMorseGen( + *(long*)argvals[MCARG_SAMPRATE], + *(long*)argvals[MCARG_PITCH], + *(long*)argvals[MCARG_WPM], + *(long*)argvals[MCARG_REALWPM], + *(long*)argvals[MCARG_ATTACK], + *(long*)argvals[MCARG_RELEASE], + (char*)argvals[MCARG_TO], + metrics); + break; + + case GENERATOR_WAVE: + generator = new WaveMorseGen( + *(long*)argvals[MCARG_SAMPRATE], + *(long*)argvals[MCARG_PITCH], + *(long*)argvals[MCARG_WPM], + *(long*)argvals[MCARG_REALWPM], + *(long*)argvals[MCARG_ATTACK], + *(long*)argvals[MCARG_RELEASE], + (char*)argvals[MCARG_TO], + metrics); + break; + } + + return generator; +} + +/*---------------------------------------------------------------------------*/ + +void ParseCommandline() +{ + RDArgs *args; + long metrics[5]; + + long numdefs[6] = { + 8000, /* SAMPLERATE */ + 500, /* PITCH */ + 20, /* WPM */ + 0, /* ATTACK */ + 1, /* RELEASE */ + 0, /* REALWPM */ + }; + + long argvals[14] = { + 0, /* [00] TEXT */ + (int)"CON", /* [01] MODE */ + (int)".", /* [02] DOT */ + (int)"-", /* [03] DASH */ + (int)&numdefs[0], /* [04] SAMPLERATE */ + (int)&numdefs[1], /* [05] PITCH */ + (int)&numdefs[2], /* [06] WPM */ + 0, /* [07] TO */ + (int)&numdefs[3], /* [08] ATTACK */ + (int)&numdefs[4], /* [09] RELEASE */ + (int)"", /* [10] SPAUSE */ + (int)" ", /* [11] CPAUSE */ + (int)" ", /* [12] WPAUSE */ + (int)&numdefs[5] /* [13] REALWPM */ + }; + + if (args = ReadArgs("TEXT/A,MODE/K,DOT/K,DASH/K,SAMPLERATE=SR/K/N,PITCH/K/N,WPM/K/N,TO/K," + "ATTACK/K/N,RELEASE/K/N,SPAUSE/K,CPAUSE/K,WPAUSE/K,REALWPM/K/N", argvals, NULL)) + { + long mode; + + for (mode = 0; mode < GENERATORS; mode++) + { + if (Stricmp((const char*)argvals[MCARG_MODE], Generators[mode].type) == 0) break; + } + + if (mode < GENERATORS) + { + MorseGen *generator; + + if (Generators[mode].metrics) + { + CountMorseGen counter(FALSE, metrics); + counter.MorseText((const char*)argvals[MCARG_TEXT]); + } + + if (generator = CreateGenerator(mode, argvals, metrics)) + { + if (generator->Initialized) + { + generator->MorseText((const char*)argvals[MCARG_TEXT]); + } + + delete generator; + } + } + else Printf("arguments check: no such mode '%s'\n", (long)argvals[MCARG_MODE]); + + FreeArgs(args); + } + else PrintFault(IoErr(), "argument parser"); +} + +/*---------------------------------------------------------------------------*/ + +LONG Main(WBStartup *wbmsg) +{ + LONG result = RETURN_FAIL; + + if (OpenLibs()) + { + ParseCommandline(); + } + + CloseLibs(); + return result; +} diff --git a/src/main.h b/src/main.h index 0b7877f..c1e157a 100644 --- a/src/main.h +++ b/src/main.h @@ -1,3 +1,6 @@ +#ifndef MORSCONV_MAIN_H +#define MORSCONV_MAIN_H + extern struct Library *SysBase, *DOSBase, @@ -6,27 +9,31 @@ extern struct Library *MathIeeeSingTransBase, *IFFParseBase; -extern ULONG MorErr; - -static inline SetErr(LONG retval, LONG ecode) -{ - MorErr = (retval << 16) | ecode; -} #ifdef __mc68000__ #define mul16(a,b) ({ \ -LONG _r; \ -WORD _a = (a), _b = (b); \ +long _r; \ +short _a = (a), _b = (b); \ asm("MULS.W %2,%0": "=d" (_r): "0" (_a), "dmi" (_b): "cc"); \ _r;}) #define div16(a,b) ({ \ -WORD _r, _b = (b); \ -LONG _a = (a); \ +short _r, _b = (b); \ +long _a = (a); \ asm("DIVS.W %2,%0": "=d" (_r): "0" (_a), "dmi" (_b): "cc"); \ _r;}) +#define bswap16(a) ({ \ +short _r, _a = (a); \ +asm ("ROR.W #8,%0": "=d" (_r) : "0" (_a) : "cc"); \ +_r;}) + +#define bswap32(a) ({ \ +long _r, _a = (a); \ +asm("ROR.W #8,%0; SWAP %0; ROR.W #8,%0" : "=d" (_r) : "0" (_a) : "cc"); \ +_r;}) + #else #define mul16(a,b) a * b @@ -34,3 +41,4 @@ _r;}) #endif +#endif /* MORSCONV_MAIN_H */ diff --git a/src/makefile b/src/makefile index f0a1fbc..f50e529 100644 --- a/src/makefile +++ b/src/makefile @@ -1,10 +1,10 @@ -CC = gcc -LD = ld -CFLAGS = -noixemul -nostdlib -O2 -fomit-frame-pointer -m68000 -mregparm=2 -D__NOLIBBASE__ -DUSE_INLINE_STDARG -LDFLAGS = -noixemul -nostartfiles -nostdlib -LIBS = -OBJS = start.o main.o morsegen.o backend-stdout.o backend-8svx.o backend-count.o -SRCS = start.c main.c morsegen.c morsegen.h backend-stdout.c backend-8svx.c backend-count.c +CC = g++ +LD = g++ +CFLAGS = -nostdlib -O2 -fbaserel -fomit-frame-pointer -mregparm -fno-exceptions -fno-rtti -D__NOLIBBASE__ +LDFLAGS = -nostdlib -fbaserel -fomit-frame-pointer -nostartfiles +LIBS = -lminteger -lmfloat -lmtransfloat +OBJS = start.o main.o morsegen.o morsegenaudio.o backend-count.o backend-stdout.o backend-8svx.o \ + backend-wave.o purevirtual.o EXE = MorsConv all: $(OBJS) @@ -12,28 +12,31 @@ all: $(OBJS) @$(LD) $(LDFLAGS) -o $(EXE).db $^ $(LIBS) @strip $(EXE).db -o $(EXE) --strip-unneeded @Protect $(EXE) +E + List $(EXE) clean: rm -vf *.o $(EXE) $(EXE).db -devdoc: - Join $(SRCS) TO T:$(EXE).sources - RoboDoc T:$(EXE).sources $(EXE)-Dev.guide GUIDE TOC SORT INTERNAL - Delete T:$(EXE).sources - -start.o: start.c +start.o: start.cpp @echo "Compiling $@..." @$(CC) $(CFLAGS) -fwritable-strings -c -o $@ $< -%.o: %.c +purevirtual.o: purevirtual.c + @echo "Compiling $@..." + @gcc $(CFLAGS) -c -o $@ $< + +%.o: %.cpp @echo "Compiling $@..." @$(CC) $(CFLAGS) -c -o $@ $< # dependencies -backend-8svx.o: backend-8svx.c main.h morsegen.h -backend-count.o: backend-count.c main.h morsegen.h -backend-stdout.o: backend-stdout.c main.h morsegen.h -main.o: main.c main.h backend-stdout.h morsegen.h -morsegen.o: morsegen.c main.h morsegen.h -start.o: start.c +backend-8svx.o: backend-8svx.cpp main.h backend-8svx.h morsegenaudio.h morsegen.h backend-count.h +backend-count.o: backend-count.cpp main.h backend-count.h morsegen.h +backend-stdout.o: backend-stdout.cpp backend-stdout.h main.h morsegen.h +backend-wave.o: backend-wave.cpp main.h backend-wave.h morsegenaudio.h morsegen.h +main.o: main.cpp main.h backend-stdout.h morsegen.h backend-count.h backend-8svx.h morsegenaudio.h \ + backend-wave.h +morsegen.o: morsegen.cpp morsegen.h main.h +morsegenaudio.o: morsegenaudio.cpp main.h morsegenaudio.h morsegen.h +start.o: start.cpp \ No newline at end of file diff --git a/src/morsegen.c b/src/morsegen.c deleted file mode 100644 index 8ffc3bd..0000000 --- a/src/morsegen.c +++ /dev/null @@ -1,259 +0,0 @@ -#include -#include - -#include "main.h" -#include "morsegen.h" - -#include /* for tests */ - -/* Morse code contains no characters with ASCII less than 32, so indexes in */ -/* 'morse' table are (ASCII - 32). To make the table human readable, Morse */ -/* sequences are given as strings, where DIT is '.', DAT is '-' and short */ -/* (intra-letter) pause is just space. */ - -const UBYTE* const Morse[] = { - NULL, /* 32, ' ', no representation */ - "- . - . - -", /* 33, '!' */ - ". - . . - .", /* 34, '"' */ - NULL, /* 35, '#', no representation */ - ". . . - . . -", /* 36, '$', non-standard */ - NULL, /* 37, '%', no representation */ - ". - . . .", /* 38, '&', non-standard */ - ". - - - - .", /* 39, ''' */ - "- . - - .", /* 40, '(' */ - "- . - - . -", /* 41, ')' */ - NULL, /* 42, '*', no representation */ - ". - . - .", /* 43, '+' */ - "- - . . - -", /* 44, ',' */ - "- . . . . -", /* 45, '-' */ - ". - . - . -", /* 46, '.' */ - "- . . - .", /* 47, '/' */ - "- - - - -", /* 48, '0' */ - ". - - - -", /* 49, '1' */ - ". . - - -", /* 50, '2' */ - ". . . - -", /* 51, '3' */ - ". . . . -", /* 52, '4' */ - ". . . . .", /* 53, '5' */ - "- . . . .", /* 54, '6' */ - "- - . . .", /* 55, '7' */ - "- - - . .", /* 56, '8' */ - "- - - - .", /* 57, '9' */ - "- - - . . .", /* 58, ':' */ - "- . - . - .", /* 59, ';' */ - NULL, /* 60, '<', no representation */ - "- . . . -", /* 61, '=' */ - NULL, /* 62, '>', no representation */ - ". . - - . .", /* 63, '?' */ - ". - - . - .", /* 64, '@' */ - ". -", /* 65, 'A' */ - "- . . .", /* 66, 'B' */ - "- . - .", /* 67, 'C' */ - "- . .", /* 68, 'D' */ - ".", /* 69, 'E' */ - ". . - .", /* 70, 'F' */ - "- - .", /* 71, 'G' */ - ". . . .", /* 72, 'H' */ - ". .", /* 73, 'I' */ - ". - - -", /* 74, 'J' */ - "- . -", /* 75, 'K' */ - ". - . .", /* 76, 'L' */ - "- -", /* 77, 'M' */ - "- .", /* 78, 'N' */ - "- - -", /* 80, 'O' */ - ". - - .", /* 81, 'P' */ - "- - . -", /* 82, 'Q' */ - ". - .", /* 83, 'R' */ - ". . .", /* 84, 'S' */ - "-", /* 85, 'T' */ - ". . -", /* 86, 'U' */ - ". . . -", /* 87, 'V' */ - ". - -", /* 88, 'W' */ - "- . . -", /* 89, 'X' */ - "- . - -", /* 90, 'Y' */ - "- - . .", /* 91, 'Z' */ - NULL, /* 92, '[', no representation */ - NULL, /* 93, '\', no representation */ - NULL, /* 94, ']', no representation */ - ". . - - . -" /* 95, '_', non-standard */ -}; - - -/****i* morsegen/ToMorse() ************************************************* -* -* NAME -* ToMorse() -- Converts ASCII code to source Morse string -* -* SYNOPSIS -* string = ToMorse(code) -* const UBYTE* ToMorse(UBYTE); -* -* FUNCTION -* Returns Morse source string for passed ASCII code. NULL is returned for -* ASCII codes not representable in Morse code. Codes lower than 32 and -* higher than 95 are rejected in code. For others non-representable codes -* Morse[] contains NULLs. -* -* INPUTS -* code - ASCII code to be converted. -* -* RESULT -* Pointer to source Morse string. In such a string "dits" are represented -* by '.', "dats" are represented by '-'. Intra-symbol pauses are -* represented by spaces. Pointer may be NULL, if passed ASCII code has no -* representation in Morse code. -* -* NOTES -* - Small letters are treated as non-representable. Letters must be -* capitalized before passing to this function. -* -****************************************************************************** -*/ - -const UBYTE* ToMorse(UBYTE ascii) -{ - if (ascii < 32) return NULL; - if (ascii > 95) return NULL; - return Morse[ascii - 32]; -} - - - -/* Converts words per minute to dit length in miliseconds (based on CODEX, */ -/* 20 wpm = 50 msec unit). */ - -LONG WpmToMsec(LONG wpm) -{ - return SDivMod32(1000, wpm); -} - - -/****i* morsegen/MorseLetter() ********************************************** -* -* NAME -* MorseLetter() -- Emits a source Morse string -* -* SYNOPSIS -* MorseLetter(generator, morsestring) -* VOID MorseLetter(struct MorseGen*, UBYTE*); -* -* FUNCTION -* Emits a NULL-terminated source Morse string using callbacks of generator -* specified. A dot in the string is emitted as one unit long tone. A dash -* in the string is emitted as a three units long tone. A space in the -* string is emitted as one unit long pause. -* -* INPUTS -* generator - properly initialized MorseGen structure. NULL is not checked -* and will be fatal. -* morsestring - source string of a letter (using '.', '-' and space). NULL -* is not checked and will be fatal. -* -* RESULT -* None. -* -****************************************************************************** -*/ - -void MorseLetter(struct MorseGen *mg, const UBYTE *morse) -{ - UBYTE m; - - while (m = *morse++) - { - switch (m) - { - case ' ': mg->mg_IntraSymbolPause(mg); break; - case '.': mg->mg_ShortTone(mg); break; - case '-': mg->mg_LongTone(mg); break; - } - } -} - - -/****i* morsegen/MorseText() ************************************************ -* -* NAME -* MorseText() -- Emits an ASCII string as Morse -* -* SYNOPSIS -* skipped = MorseText(generator, text) -* ULONG MorseText(struct MorseGen*, STRPTR); -* -* FUNCTION -* A string is emitted as Morse code using specified generator. The string -* is treated as a series of words separated by spaces. All the characters -* except the first one in each word are prefixed by inter-symbol pause. -* Space character generates inter-word pause. ToUpper() from utility.library -* is called on every character. Then characters not representable in Morse -* code are skipped, counter of skipped characters is updated. -* -* INPUTS -* generator - properly initialized MorseGen structure. NULL is not checked -* and will be fatal. -* text - NULL-terminated string. NULL is not checked and will be fatal. -* -* RESULT -* Count of skipped symbols (having no representation in Morse code). -* -****************************************************************************** -*/ - -LONG MorseText(struct MorseGen *mg, STRPTR text) -{ - UBYTE current_letter; - LONG skipped = 0; - BOOL start_of_word = TRUE; - - while (current_letter = *text++) - { - const UBYTE *morse; - - if (current_letter == ' ') - { - mg->mg_InterWordPause(mg); - start_of_word = TRUE; - } - else if (morse = ToMorse(ToUpper(current_letter))) - { - if (start_of_word) start_of_word = FALSE; - else mg->mg_InterSymbolPause(mg); - MorseLetter(mg, morse); - } - else skipped++; - } - - return skipped; -} - - -/****i* morsegen/MorseGenSetup() ********************************************* -* -* NAME -* MorseGenSetup() -- prepare generator and initialize parameters -* -* SYNOPSIS -* success = MorseGenSetup(generator, tag, ...) -* BOOL MorseGenSetup(struct MorseGen*, Tag, ...); -* -* FUNCTION -* Prepares Morse generator to run. Allocates resources needed. Sets -* parameters of the output according to passed tags. Function creates a -* taglist from variadic arguments, then calls mg_Setup() of the generator -* with the taglist. -* -* INPUTS -* generator - MorseGen structure. -* tag - inlined TagList of parameters. -* -* RESULT -* Result of called mg_Setup() is returned. -* -****************************************************************************** -*/ - -BOOL MorseGenSetup(struct MorseGen *mg, Tag tag0, ...) -{ - struct TagItem *taglist = (struct TagItem*)&tag0; - - return mg->mg_Setup(mg, taglist); -} \ No newline at end of file diff --git a/src/morsegen.cpp b/src/morsegen.cpp new file mode 100644 index 0000000..50498b1 --- /dev/null +++ b/src/morsegen.cpp @@ -0,0 +1,134 @@ +#include +#include +#include + +#include "morsegen.h" +#include "main.h" + + +/* Morse code contains no characters with ASCII less than 32, so indexes in */ +/* 'morse' table are (ASCII - 32). To make the table human readable, Morse */ +/* sequences are given as strings, where DIT is '.', DAT is '-' and short */ +/* (intra-letter) pause is space. */ + +const char* const MorseGen::morsedata[] = +{ + NULL, /* 32, ' ', no representation */ + "- . - . - -", /* 33, '!' */ + ". - . . - .", /* 34, '"' */ + NULL, /* 35, '#', no representation */ + ". . . - . . -", /* 36, '$', non-standard */ + NULL, /* 37, '%', no representation */ + ". - . . .", /* 38, '&', non-standard */ + ". - - - - .", /* 39, ''' */ + "- . - - .", /* 40, '(' */ + "- . - - . -", /* 41, ')' */ + NULL, /* 42, '*', no representation */ + ". - . - .", /* 43, '+' */ + "- - . . - -", /* 44, ',' */ + "- . . . . -", /* 45, '-' */ + ". - . - . -", /* 46, '.' */ + "- . . - .", /* 47, '/' */ + "- - - - -", /* 48, '0' */ + ". - - - -", /* 49, '1' */ + ". . - - -", /* 50, '2' */ + ". . . - -", /* 51, '3' */ + ". . . . -", /* 52, '4' */ + ". . . . .", /* 53, '5' */ + "- . . . .", /* 54, '6' */ + "- - . . .", /* 55, '7' */ + "- - - . .", /* 56, '8' */ + "- - - - .", /* 57, '9' */ + "- - - . . .", /* 58, ':' */ + "- . - . - .", /* 59, ';' */ + NULL, /* 60, '<', no representation */ + "- . . . -", /* 61, '=' */ + NULL, /* 62, '>', no representation */ + ". . - - . .", /* 63, '?' */ + ". - - . - .", /* 64, '@' */ + ". -", /* 65, 'A' */ + "- . . .", /* 66, 'B' */ + "- . - .", /* 67, 'C' */ + "- . .", /* 68, 'D' */ + ".", /* 69, 'E' */ + ". . - .", /* 70, 'F' */ + "- - .", /* 71, 'G' */ + ". . . .", /* 72, 'H' */ + ". .", /* 73, 'I' */ + ". - - -", /* 74, 'J' */ + "- . -", /* 75, 'K' */ + ". - . .", /* 76, 'L' */ + "- -", /* 77, 'M' */ + "- .", /* 78, 'N' */ + "- - -", /* 80, 'O' */ + ". - - .", /* 81, 'P' */ + "- - . -", /* 82, 'Q' */ + ". - .", /* 83, 'R' */ + ". . .", /* 84, 'S' */ + "-", /* 85, 'T' */ + ". . -", /* 86, 'U' */ + ". . . -", /* 87, 'V' */ + ". - -", /* 88, 'W' */ + "- . . -", /* 89, 'X' */ + "- . - -", /* 90, 'Y' */ + "- - . .", /* 91, 'Z' */ + NULL, /* 92, '[', no representation */ + NULL, /* 93, '\', no representation */ + NULL, /* 94, ']', no representation */ + ". . - - . -" /* 95, '_', non-standard */ +}; + + +const char* MorseGen::ToMorse(char ascii) +{ + if (ascii < 32) return NULL; + if (ascii > 95) return NULL; + return morsedata[ascii - 32]; +} + + +void MorseGen::MorseLetter(const char *morse) +{ + char m; + + while (m = *morse++) + { + switch (m) + { + case ' ': IntraSymbolPause(); break; + case '.': ShortTone(); break; + case '-': LongTone(); break; + } + } +} + + +LONG MorseGen::MorseText(const char *text) +{ + char current_letter; + LONG skipped = 0; + BOOL start_of_word = TRUE; + + while (current_letter = *text++) + { + const char *morse; + + if (current_letter == ' ') + { + InterWordPause(); + start_of_word = TRUE; + } + else if (morse = ToMorse(ToUpper(current_letter))) + { + if (start_of_word) start_of_word = FALSE; + else InterSymbolPause(); + MorseLetter(morse); + } + else skipped++; + } + + return skipped; +} + + +MorseGen::~MorseGen() {} diff --git a/src/morsegen.h b/src/morsegen.h index 5138537..b0867ee 100644 --- a/src/morsegen.h +++ b/src/morsegen.h @@ -1,85 +1,28 @@ -#ifndef TELEGRAPHIST_MORSEGEN_H -#define TELEGRAPHIST_MORSEGEN_H +#ifndef MORSCONV_MORSEGEN_H +#define MORSCONV_MORSEGEN_H #include - -/****i* morsegen/MorseGen **************************************************** -* -* NAME -* MorseGen -- Structure controlling Morse generator -* -* SYNOPSIS -* struct MorseGen -* { -* BOOL(*mg_Setup)(struct MorseGen*, struct TagItem*); -* void(*mg_Cleanup)(struct MorseGen*); -* void(*mg_IntraSymbolPause)(struct MorseGen*); -* void(*mg_InterSymbolPause)(struct MorseGen*); -* void(*mg_InterWordPause)(struct MorseGen*); -* void(*mg_ShortTone)(struct MorseGen*); -* void(*mg_LongTone)(struct MorseGen*); -* BOOL mg_NeedsMetrics; -* }; -* -* FUNCTION -* The structure is the master control structure of a Morse generator. The -* application creates it and fills with data. Meaning of structure fields: -* - mg_Setup, points to generator setup function. Should not be called -* before each text, it is assumed to be called once in application init. -* - mg_Cleanup, points to generator cleanup function, usually called once -* at application exit. -* - mg_IntraSymbolPause, is a callback called by generator for emitting -* pauses inside a symbol. -* - mg_InterSymbolPause, is a callback called by generator for emitting -* pauses between symbols in a word. -* - mg_InterWordPause, is a callback called by generator for emitting -* pauses between words (spaces). -* - mg_ShortTone, is a callback called by generator for emitting "dit" -* tone. -* - mg_LongTone, is a callback called by generator for emitting "dat" tone. -* - mg_NeedsMetrics, boolean flag set to TRUE if given backend has to -* precalculate its output size based on text to be converted later. -* -****************************************************************************** -*/ - -struct MorseGen +class MorseGen { - BOOL(*mg_Setup)(struct MorseGen*, struct TagItem*); - void(*mg_Cleanup)(struct MorseGen*); - void(*mg_IntraSymbolPause)(struct MorseGen*); - void(*mg_InterSymbolPause)(struct MorseGen*); - void(*mg_InterWordPause)(struct MorseGen*); - void(*mg_ShortTone)(struct MorseGen*); - void(*mg_LongTone)(struct MorseGen*); - BOOL mg_NeedsMetrics; + static const char* const morsedata[]; + const char* ToMorse(char ascii); + void MorseLetter(const char *morse); + + public: + + short Initialized; + MorseGen() { Initialized = FALSE; } + virtual void IntraSymbolPause() = 0; + virtual void InterSymbolPause() = 0; + virtual void InterWordPause() = 0; + virtual void ShortTone() = 0; + virtual void LongTone() = 0; + long MorseText(const char *text); + virtual ~MorseGen(); }; -LONG MorseText(struct MorseGen*, STRPTR); -BOOL MorseGenSetup(struct MorseGen*, Tag tag0, ...); - -/*-----------------------------------*/ -/* control tags for Morse generators */ -/*-----------------------------------*/ - -#define MA_DotText 0x80000001 /* CON */ -#define MA_DashText 0x80000002 /* CON */ -#define MA_SamplingRate 0x80000003 /* 8SVX */ -#define MA_TonePitch 0x80000004 /* 8SVX */ -#define MA_WordsPerMinute 0x80000005 /* 8SVX */ -#define MA_OutputFile 0x80000006 /* 8SVX */ -#define MA_CounterStorage 0x80000007 /* COUNT */ -#define MA_CounterPrint 0x80000008 /* COUNT */ -#define MA_MorseMetrics 0x80000009 /* 8SVX */ -#define MA_EnvAttack 0x8000000A /* 8SVX */ -#define MA_EnvRelease 0x8000000B /* 8SVX */ -#define MA_SymbolPause 0x8000000C /* CON */ -#define MA_CharPause 0x8000000D /* CON */ -#define MA_WordPause 0x8000000E /* CON */ -#define MA_RealWordsPerMinute 0x8000000F /* 8SVX */ - -/* indexes for the counter array (count module) */ +/* indexes for the metrics array (count module) */ #define COUNTER_DOTS 0 #define COUNTER_DASHES 1 @@ -87,5 +30,4 @@ BOOL MorseGenSetup(struct MorseGen*, Tag tag0, ...); #define COUNTER_CHAR_PAUSES 3 #define COUNTER_WORD_PAUSES 4 - -#endif /* TELEGRAPHIST_MORSEGEN_H */ +#endif /* MORSCONV_MORSEGEN_H */ diff --git a/src/morsegenaudio.cpp b/src/morsegenaudio.cpp new file mode 100644 index 0000000..d895376 --- /dev/null +++ b/src/morsegenaudio.cpp @@ -0,0 +1,230 @@ +#include +#include +#include + +#include "main.h" +#include "morsegenaudio.h" + +#define TWO_PI 6.28318531f + +char* AudioMorseGen::modname_argcheck = "arguments check"; + +AudioMorseGen::AudioMorseGen(long srate, long pitch, long wpm, long rwpm, long attack, +long release, long *metrics) : MorseGen() +{ + SampleRate = srate; + TonePitch = pitch; + Wpm = wpm; + RealWpm = rwpm; + Metrics = metrics; + AttackTime = attack; + ReleaseTime = release; + + if (RealWpm == 0) RealWpm = Wpm; + + if ((TonePitch < 100) || (TonePitch > 8000)) + { + Printf("%s: tone pitch out of range (100 - 8000 Hz)\n", modname_argcheck); + return; + } + + if ((TonePitch << 2) > SampleRate) + { + Printf("%s: tone pitch must not be higher than 1/4 of sampling rate\n", modname_argcheck); + return; + } + + if ((Wpm < 5) || (Wpm > 100)) + { + Printf("%s: WPM out of range (5 - 100)\n", modname_argcheck); + return; + } + + if ((RealWpm < 5) || (RealWpm > 100)) + { + Printf("%s: real WPM out of range (5 - 100)\n", modname_argcheck); + return; + } + + if (RealWpm > Wpm) + { + Printf("%s: real WPM must not be higher than symbol WPM\n", modname_argcheck); + return; + } + + if ((AttackTime < 0) || (AttackTime > 50)) + { + Printf("%s: attack time out of range (0 - 50 ms)\n", modname_argcheck); + return; + } + + if ((ReleaseTime < 0) || (ReleaseTime > 50)) + { + Printf("%s: release time out of range (0 - 50 ms)\n", modname_argcheck); + return; + } + + SamplesPerDot = (SampleRate * 6) / (Wpm * 5); + CalculateFarnsworthPauses(); + Initialized = TRUE; + +} + +/*--------------------------------------------------------------------------------------------*/ + +long AudioMorseGen::CalculateAudioSize() +{ + long samples; + long samples_per_dash = SamplesPerDot * 3; + + samples = Metrics[COUNTER_DOTS] * SamplesPerDot; + samples += Metrics[COUNTER_DASHES] * samples_per_dash; + samples += Metrics[COUNTER_SYMBOL_PAUSES] * SamplesPerDot; + samples += Metrics[COUNTER_CHAR_PAUSES] * SamplesPerCPause; + samples += Metrics[COUNTER_WORD_PAUSES] * SamplesPerWPause; + return samples; +} + +/*--------------------------------------------------------------------------------------------*/ +/* Note that MorsConv knows Morse code metrics in advance, so it could fit RealWpm perfectly. */ +/* However it calculates Farnsworth timing based on PARIS word, the same as ARRL paper. */ +/*--------------------------------------------------------------------------------------------*/ + +void AudioMorseGen::CalculateFarnsworthPauses() +{ + long alpha, beta, cpauseplus, wpauseplus; + + alpha = SampleRate * 60 * (Wpm - RealWpm); + beta = mul16(mul16(Wpm, RealWpm), 19); + cpauseplus = alpha * 3 / beta; + wpauseplus = alpha * 7 / beta; + SamplesPerCPause = SamplesPerDot * 3 + cpauseplus; + SamplesPerWPause = SamplesPerDot * 7 + wpauseplus; +} + +/*--------------------------------------------------------------------------------------------*/ + +void AudioMorseGen::GenerateTone8(signed char *buffer, long count, long samprate, long pitch) +{ + long i; + float step; + + step = TWO_PI / samprate * pitch; + + for (i = 0; i < count; i++) + { + float x; + + x = sinf(i * step) * 127.0f; + *buffer++ = (signed char)x; + } +} + +/*--------------------------------------------------------------------------------------------*/ + +void AudioMorseGen::GenerateTone16(short *buffer, long count, long samprate, long pitch) +{ + long i; + float step; + + step = TWO_PI / samprate * pitch; + + for (i = 0; i < count; i++) + { + float x; + + x = sinf(i * step) * 32767.0f; + *buffer++ = (short)x; + } +} + +/*--------------------------------------------------------------------------------------------*/ + +void AudioMorseGen::ApplyEnvelope8(signed char *buffer, long count, short attack, short release) +{ + short i; + signed char *p, s; + + /*----------------------------------------------*/ + /* limit attack and release zones to audio size */ + /*----------------------------------------------*/ + + if (attack > count) attack = count; + if (release > count) release = count; + + /*--------------*/ + /* apply attack */ + /*--------------*/ + + for (p = buffer, i = 0; i < attack; i++) + { + s = *p; + *p++ = div16(mul16(s, i), attack); + } + + /*---------------*/ + /* apply release */ + /*---------------*/ + + for (p = buffer + count - release, i = release - 1; i >= 0; i--) + { + s = *p; + *p++ = div16(mul16(s, i), release); + } +} + +/*--------------------------------------------------------------------------------------------*/ + +void AudioMorseGen::ApplyEnvelope16(short *buffer, long count, short attack, short release) +{ + short i; + short *p, s; + + /*----------------------------------------------*/ + /* limit attack and release zones to audio size */ + /*----------------------------------------------*/ + + if (attack > count) attack = count; + if (release > count) release = count; + + /*--------------*/ + /* apply attack */ + /*--------------*/ + + for (p = buffer, i = 0; i < attack; i++) + { + s = *p; + *p++ = div16(mul16(s, i), attack); + } + + /*---------------*/ + /* apply release */ + /*---------------*/ + + for (p = buffer + count - release, i = release - 1; i >= 0; i--) + { + s = *p; + *p++ = div16(mul16(s, i), release); + } +} + + +/*--------------------------------------------------------------------------------------------*/ + +void AudioMorseGen::ByteSwap16(short *buffer, long count) +{ + unsigned short x, *p = (unsigned short*)buffer; + long i; + + for (i = 0; i < count; i++) + { + x = *p; + *p++ = bswap16(x); + } +} + +/*--------------------------------------------------------------------------------------------*/ + + +AudioMorseGen::~AudioMorseGen() {} + diff --git a/src/morsegenaudio.h b/src/morsegenaudio.h new file mode 100644 index 0000000..3d66e3f --- /dev/null +++ b/src/morsegenaudio.h @@ -0,0 +1,41 @@ +#ifndef MORSCONV_MORSEGENAUDIO_H +#define MORSCONV_MORSEGENAUDIO_H + +#include +#include + +#include "main.h" +#include "morsegen.h" + +class AudioMorseGen : public MorseGen +{ + static char* modname_argcheck; + + protected: + + long SampleRate; + long SamplesPerDot; + long SamplesPerCPause; + long SamplesPerWPause; + long *Metrics; + short TonePitch; + short Wpm; + short RealWpm; + short AttackTime; + short ReleaseTime; + + long CalculateAudioSize(); + void CalculateFarnsworthPauses(); + void GenerateTone8(signed char *buffer, long count, long samprate, long pitch); + void ApplyEnvelope8(signed char *buffer, long count, short attack, short release); + void GenerateTone16(short *buffer, long count, long samprate, long pitch); + void ApplyEnvelope16(short *buffer, long count, short attack, short release); + void ByteSwap16(short *buffer, long count); + + public: + + AudioMorseGen(long srate, long pitch, long wpm, long rwpm, long attack, long release, long *metrics); + ~AudioMorseGen(); +}; + +#endif /* MORSCONV_MORSEGENAUDIO_H */ diff --git a/src/purevirtual.c b/src/purevirtual.c new file mode 100644 index 0000000..9a7f4d9 --- /dev/null +++ b/src/purevirtual.c @@ -0,0 +1,8 @@ +#include + +extern struct Library *DOSBase; + +void __pure_virtual(void) +{ + PutStr("pure virtual\n"); +} diff --git a/src/start.c b/src/start.c deleted file mode 100644 index 12f95eb..0000000 --- a/src/start.c +++ /dev/null @@ -1,50 +0,0 @@ -/*---------------------------------*/ -/* Minimal startup for AmigaOS 3.x */ -/* RastPort, 2020 */ -/*---------------------------------*/ - -#include -#include -#include -#include - -struct Library *SysBase; -struct Library *DOSBase; - -extern ULONG Main(void); - -ULONG Start(void) -{ - struct Process *myproc = NULL; - struct Message *wbmsg = NULL; - BOOL have_shell = FALSE; - ULONG result = RETURN_OK; - - SysBase = *(struct Library**)4L; - myproc = (struct Process*)FindTask(NULL); - if (myproc->pr_CLI) have_shell = TRUE; - - if (!have_shell) - { - WaitPort(&myproc->pr_MsgPort); - wbmsg = GetMsg(&myproc->pr_MsgPort); - } - - if (DOSBase = OpenLibrary("dos.library", 39)) - { - result = Main(); - CloseLibrary(DOSBase); - } - else result = RETURN_FAIL; - - if (wbmsg) - { - Forbid(); - ReplyMsg(wbmsg); - } - - return result; -} - - -__attribute__((section(".text"))) UBYTE VString[] = "$VER: MorsConv 2.2 (10.01.2024)\r\n"; diff --git a/src/start.cpp b/src/start.cpp new file mode 100644 index 0000000..91a6f7d --- /dev/null +++ b/src/start.cpp @@ -0,0 +1,94 @@ +/*-------------------------------------*/ +/* Minimal C++ startup for AmigaOS 3.x */ +/* RastPort, 2024 */ +/*-------------------------------------*/ + +#include +#include +#include + +Library *SysBase; +Library *DOSBase; +APTR TaskPool = NULL; + +extern ULONG Main(WBStartup *wbmsg); + + +__saveds ULONG Start(void) +{ + Process *myproc = NULL; + WBStartup *wbmsg = NULL; + BOOL have_shell = FALSE; + ULONG result = RETURN_OK; + + SysBase = *(Library**)4L; + myproc = (Process*)FindTask(NULL); + + if (myproc->pr_CLI) have_shell = TRUE; + + if (!have_shell) + { + WaitPort(&myproc->pr_MsgPort); + wbmsg = (WBStartup*)GetMsg(&myproc->pr_MsgPort); + } + + result = RETURN_FAIL; + + if (DOSBase = OpenLibrary("dos.library", 39)) + { + if (TaskPool = CreatePool(MEMF_ANY, 4096, 2048)) + { + result = Main(wbmsg); + DeletePool(TaskPool); + } + + CloseLibrary(DOSBase); + } + + if (wbmsg) + { + Forbid(); + ReplyMsg(&wbmsg->sm_Message); + } + + return (ULONG)result; +} + + +__attribute__((section(".text"))) UBYTE VString[] = "$VER: MorsConv 2.3 (03.04.2024)\r\n"; + + + +APTR operator new(ULONG size) throw() +{ + ULONG *m; + + size += 4; + + if (m = (ULONG*)AllocPooled(TaskPool, size)) + { + *m = size; + return m + 1; + } + else return NULL; +} + + +APTR operator new[](ULONG size) +{ + return operator new(size); +} + + +void operator delete(APTR memory) +{ + ULONG *m = (ULONG*)memory - 1; + + FreePooled(TaskPool, m, *m); +} + + +void operator delete[](APTR memory) +{ + operator delete(memory); +}