mirror of
https://github.com/MarkAHamann/MorseTrainer.git
synced 2025-12-06 03:42:02 +01:00
Initial commit.
This commit is contained in:
parent
93399784ba
commit
88f596fb0b
215
.gitignore
vendored
Normal file
215
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
x64/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
|
||||
# Roslyn cache directories
|
||||
*.ide/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
#NUNIT
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_i.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# JustCode is a .NET coding addin-in
|
||||
.JustCode
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
## TODO: Comment the next line if you want to checkin your
|
||||
## web deploy settings but do note that will include unencrypted
|
||||
## passwords
|
||||
#*.pubxml
|
||||
|
||||
# NuGet Packages Directory
|
||||
packages/*
|
||||
## TODO: If the tool you use requires repositories.config
|
||||
## uncomment the next line
|
||||
#!packages/repositories.config
|
||||
|
||||
# Enable "build/" folder in the NuGet Packages folder since
|
||||
# NuGet packages use it for MSBuild targets.
|
||||
# This line needs to be after the ignore of the build folder
|
||||
# (and the packages folder if the line above has been uncommented)
|
||||
!packages/build/
|
||||
|
||||
# Windows Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Windows Store app package directory
|
||||
AppPackages/
|
||||
|
||||
# Others
|
||||
sql/
|
||||
*.Cache
|
||||
ClientBin/
|
||||
[Ss]tyle[Cc]op.*
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
node_modules/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# LightSwitch generated files
|
||||
GeneratedArtifacts/
|
||||
_Pvt_Extensions/
|
||||
ModelManifest.xml
|
||||
|
||||
# Lattice
|
||||
work/
|
||||
synwork/
|
||||
syntmp/
|
||||
synlog/
|
||||
|
||||
# GVIM
|
||||
*.v~
|
||||
*.swp
|
||||
|
||||
*.rel
|
||||
*.sym
|
||||
*.lst
|
||||
*.asm
|
||||
*.adb
|
||||
*.ihx
|
||||
*.lk
|
||||
*.map
|
||||
*.mem
|
||||
*.noi
|
||||
*.d
|
||||
|
||||
*.lib
|
||||
*.rst
|
||||
sta[0-9][0-9][0-9][0-9][0-9]
|
||||
22
MorseTrainer.sln
Normal file
22
MorseTrainer.sln
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 14
|
||||
VisualStudioVersion = 14.0.24720.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MorseTrainer", "MorseTrainer\MorseTrainer.csproj", "{48E042F6-B6A3-4ABF-B272-A76F9533FDF9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{48E042F6-B6A3-4ABF-B272-A76F9533FDF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{48E042F6-B6A3-4ABF-B272-A76F9533FDF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{48E042F6-B6A3-4ABF-B272-A76F9533FDF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{48E042F6-B6A3-4ABF-B272-A76F9533FDF9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
54
MorseTrainer/Analyzer.cs
Normal file
54
MorseTrainer/Analyzer.cs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
public class Analyzer
|
||||
{
|
||||
public Analyzer(System.Windows.Forms.RichTextBox resultsRTB)
|
||||
{
|
||||
_resultsRTB = resultsRTB;
|
||||
}
|
||||
|
||||
public void Analyze(String sent, String recorded)
|
||||
{
|
||||
_resultsRTB.Clear();
|
||||
MorseCompareResults results = Comparer.Compare(sent, recorded);
|
||||
Write("I sent : ");
|
||||
ResultsDisplayFlags flags = ResultsDisplayFlags.Valid | ResultsDisplayFlags.Dropped;
|
||||
foreach (MorseSubstring substring in results.SubStrings)
|
||||
{
|
||||
Write(substring.Str(flags), substring.Color);
|
||||
}
|
||||
Write(Environment.NewLine);
|
||||
|
||||
Write("You typed: ");
|
||||
flags = ResultsDisplayFlags.Valid | ResultsDisplayFlags.Extra;
|
||||
foreach (MorseSubstring substring in results.SubStrings)
|
||||
{
|
||||
Write(substring.Str(flags), substring.Color);
|
||||
}
|
||||
Write(Environment.NewLine);
|
||||
|
||||
}
|
||||
|
||||
private void Write(String text)
|
||||
{
|
||||
_resultsRTB.AppendText(text);
|
||||
}
|
||||
|
||||
private void Write(String text, System.Drawing.Color color)
|
||||
{
|
||||
_resultsRTB.SelectionStart = _resultsRTB.TextLength;
|
||||
_resultsRTB.SelectionLength = 0;
|
||||
_resultsRTB.SelectionColor = color;
|
||||
_resultsRTB.AppendText(text);
|
||||
_resultsRTB.SelectionColor = _resultsRTB.ForeColor;
|
||||
}
|
||||
|
||||
private System.Windows.Forms.RichTextBox _resultsRTB;
|
||||
}
|
||||
}
|
||||
6
MorseTrainer/App.config
Normal file
6
MorseTrainer/App.config
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
149
MorseTrainer/CharGenerator.cs
Normal file
149
MorseTrainer/CharGenerator.cs
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
public class CharGenerator
|
||||
{
|
||||
public const int STRING_LENGTH_MIN = 2;
|
||||
public const int STRING_LENGTH_MAX = 7;
|
||||
|
||||
public enum Method
|
||||
{
|
||||
Koch,
|
||||
Custom
|
||||
};
|
||||
|
||||
public CharGenerator()
|
||||
{
|
||||
_randomizer = new Random();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the generation method
|
||||
/// </summary>
|
||||
public Method GenerationMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return _method;
|
||||
}
|
||||
set
|
||||
{
|
||||
_method = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the index into the Koch order. Must be at least 1
|
||||
/// </summary>
|
||||
public int KochIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return _kochIndex;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < 0 || value > Koch.Length)
|
||||
{
|
||||
throw new ArgumentException("KochIndex");
|
||||
}
|
||||
_kochIndex = Math.Max(value, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get or set whether to favor new characters in Koch order
|
||||
/// </summary>
|
||||
public bool FavorNew
|
||||
{
|
||||
get
|
||||
{
|
||||
return _favorNew;
|
||||
}
|
||||
set
|
||||
{
|
||||
_favorNew = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom string for custom. Characters are pulled from
|
||||
/// this string randomly in the custom method.
|
||||
/// </summary>
|
||||
public String Custom
|
||||
{
|
||||
get
|
||||
{
|
||||
return _custom;
|
||||
}
|
||||
set
|
||||
{
|
||||
_custom = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a random string. The caller is responsible for spaces
|
||||
/// </summary>
|
||||
/// <returns>A stringf containing characters to send</returns>
|
||||
public String CreateRandomString()
|
||||
{
|
||||
int size = 2 + _randomizer.Next() % (STRING_LENGTH_MAX - STRING_LENGTH_MIN);
|
||||
StringBuilder cc = new StringBuilder();
|
||||
for (int i = 0; i < size; ++i)
|
||||
{
|
||||
cc.Append(CreateRandomChar());
|
||||
}
|
||||
return cc.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a random character
|
||||
/// </summary>
|
||||
/// <returns>A character</returns>
|
||||
private char CreateRandomChar()
|
||||
{
|
||||
int rangeStart;
|
||||
int rangeLength;
|
||||
int index;
|
||||
char c;
|
||||
String s;
|
||||
|
||||
if (_method == Method.Koch)
|
||||
{
|
||||
rangeStart = 0;
|
||||
rangeLength = _kochIndex+1;
|
||||
if (_favorNew)
|
||||
{
|
||||
// reduce the range to the upper part once in a while
|
||||
if (_randomizer.Next() % 4 == 0)
|
||||
{
|
||||
rangeStart = Math.Max(0, rangeLength - 4);
|
||||
rangeLength -= rangeStart;
|
||||
}
|
||||
}
|
||||
s = Koch.Order;
|
||||
}
|
||||
else
|
||||
{
|
||||
rangeStart = 0;
|
||||
rangeLength = _custom.Length;
|
||||
s = _custom;
|
||||
}
|
||||
index = rangeStart + _randomizer.Next() % rangeLength;
|
||||
c = s[index];
|
||||
return c;
|
||||
}
|
||||
|
||||
private Method _method;
|
||||
private int _kochIndex;
|
||||
private String _custom;
|
||||
private bool _favorNew;
|
||||
private Random _randomizer;
|
||||
}
|
||||
}
|
||||
140
MorseTrainer/Comparer.cs
Normal file
140
MorseTrainer/Comparer.cs
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
public class Comparer
|
||||
{
|
||||
private static readonly int[] matchLengthThresholdArray = { 5, 3, 2 };
|
||||
|
||||
public static MorseCompareResults Compare(string sent, string recorded)
|
||||
{
|
||||
const int SEARCH_AMOUNT = 20;
|
||||
|
||||
int sentStart = 0;
|
||||
int recordedStart = 0;
|
||||
int sentLength = sent.Length;
|
||||
int recordedLength = recorded.Length;
|
||||
int sum = 0;
|
||||
int sentOffset = 0;
|
||||
int recordedOffset = 0;
|
||||
int matched = 0;
|
||||
int threshold;
|
||||
int matchedTotal = 0;
|
||||
|
||||
List<MorseSubstring> substringList = new List<MorseSubstring>();
|
||||
|
||||
// As long as there are unprocessed characters
|
||||
while (sentStart < sentLength || recordedStart < recordedLength)
|
||||
{
|
||||
bool searching = true;
|
||||
int sentRemaining = sentLength - sentStart;
|
||||
int recordedRemaining = recordedLength - recordedStart;
|
||||
int maxSum = sentRemaining + recordedRemaining;
|
||||
|
||||
// start with a higher threshold to filter false positives, then loosen
|
||||
for (int thresholdIndex = 0; searching && (thresholdIndex < matchLengthThresholdArray.Length); ++thresholdIndex)
|
||||
{
|
||||
// a match is considered a match if it is contains at least 'threshold' contiguous matching characters
|
||||
threshold = matchLengthThresholdArray[thresholdIndex];
|
||||
int searchAmount = Math.Min(SEARCH_AMOUNT, maxSum);
|
||||
|
||||
// The algorithm here is to match positive offsets on each string
|
||||
// starting from (0,0) (0,1) (1,0) (0,2) (1,1) (2,0) (0,3) (1,2) (2,1) (3,0) (0,4) ...
|
||||
// until (0,searchAmount) ... (searchAmount,0)
|
||||
for (sum = 0; searching && (sum < searchAmount); ++sum)
|
||||
{
|
||||
for (sentOffset = 0; sentOffset <= sum; ++sentOffset)
|
||||
{
|
||||
// get the respective offsets from the start
|
||||
recordedOffset = sum - sentOffset;
|
||||
|
||||
// number of matching characters starting at the respective offsets
|
||||
matched = matchCount(sent, recorded, sentStart + sentOffset, recordedStart + recordedOffset);
|
||||
|
||||
// got matched, leave sentOffset for loop
|
||||
if (matched >= threshold)
|
||||
{
|
||||
// found!
|
||||
searching = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At this point we have matched and the offsets
|
||||
if (searching)
|
||||
{
|
||||
// didn't find a match--punt and just put everything into the mismatch output
|
||||
sentOffset = sentLength - sentStart;
|
||||
recordedOffset = recordedLength - recordedStart;
|
||||
}
|
||||
|
||||
// sum > 0 means that there was either extra or dropped characters detected before a substring match
|
||||
if (sum > 0)
|
||||
{
|
||||
if (sentOffset > 0)
|
||||
{
|
||||
// at least one character was dropped
|
||||
MorseSubstring dropped = new MorseDropped(sent.Substring(sentStart, sentOffset));
|
||||
substringList.Add(dropped);
|
||||
|
||||
// skip over the dropped characters to the start of the match
|
||||
sentStart += sentOffset;
|
||||
}
|
||||
if (recordedOffset > 0)
|
||||
{
|
||||
// at least one unsent character was detected before a substring match
|
||||
MorseSubstring extra = new MorseExtra(recorded.Substring(recordedStart, recordedOffset));
|
||||
substringList.Add(extra);
|
||||
|
||||
// skip over the extra characters to the start of the match
|
||||
recordedStart += recordedOffset;
|
||||
}
|
||||
}
|
||||
if (matched > 0)
|
||||
{
|
||||
// there was a match
|
||||
MorseSubstring valid = new MorseValid(sent.Substring(sentStart, matched));
|
||||
substringList.Add(valid);
|
||||
|
||||
// skip over the match in each string
|
||||
recordedStart += matched;
|
||||
sentStart += matched;
|
||||
|
||||
// keep track of the matched total
|
||||
matchedTotal += matched;
|
||||
}
|
||||
}
|
||||
|
||||
MorseCompareResults results = new MorseCompareResults(substringList, sent, recorded);
|
||||
return results;
|
||||
}
|
||||
|
||||
private static int matchCount(string string1, string string2, int str1Offset, int str2Offset)
|
||||
{
|
||||
if (str1Offset >= string1.Length || str2Offset >= string2.Length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int str1Comparable = string1.Length - str1Offset;
|
||||
int str2Comparable = string2.Length - str2Offset;
|
||||
|
||||
int maxCount = Math.Min(str1Comparable, str2Comparable);
|
||||
int match = 0;
|
||||
for (; match < maxCount; ++match)
|
||||
{
|
||||
if (string1[str1Offset + match] != string2[str2Offset + match])
|
||||
{
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
}
|
||||
}
|
||||
218
MorseTrainer/Config.cs
Normal file
218
MorseTrainer/Config.cs
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
[Serializable]
|
||||
public class Config
|
||||
{
|
||||
public static readonly Config Default;
|
||||
|
||||
static Config()
|
||||
{
|
||||
Default = new Config();
|
||||
Default._frequency = 1000;
|
||||
Default._wpm = 20;
|
||||
Default._farnsworthWpm = 13;
|
||||
Default._duration = 60;
|
||||
Default._startDelay = 2;
|
||||
Default._stopDelay = 2;
|
||||
Default._volume = 1.0f;
|
||||
|
||||
Default._method = CharGenerator.Method.Koch;
|
||||
Default._kochIndex = 1;
|
||||
Default._favorNew = true;
|
||||
Default._custom = "";
|
||||
}
|
||||
|
||||
private UInt16 _frequency;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the frequency in Hz
|
||||
/// </summary>
|
||||
public UInt16 Frequency
|
||||
{
|
||||
get
|
||||
{
|
||||
return _frequency;
|
||||
}
|
||||
set
|
||||
{
|
||||
_frequency = value;
|
||||
}
|
||||
}
|
||||
|
||||
private float _wpm;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the frequency in WPM in 0.5 increments
|
||||
/// </summary>
|
||||
public float WPM
|
||||
{
|
||||
get
|
||||
{
|
||||
return _wpm;
|
||||
}
|
||||
set
|
||||
{
|
||||
_wpm = value;
|
||||
}
|
||||
}
|
||||
|
||||
private float _farnsworthWpm;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the frequency in Farnsworth WPM in 0.5 increments
|
||||
/// </summary>
|
||||
public float FarnsworthWPM
|
||||
{
|
||||
get
|
||||
{
|
||||
return _farnsworthWpm;
|
||||
}
|
||||
set
|
||||
{
|
||||
_farnsworthWpm = value;
|
||||
}
|
||||
}
|
||||
|
||||
private UInt16 _duration;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the running duration in seconds increments
|
||||
/// </summary>
|
||||
public UInt16 Duration
|
||||
{
|
||||
get
|
||||
{
|
||||
return _duration;
|
||||
}
|
||||
set
|
||||
{
|
||||
_duration = value;
|
||||
}
|
||||
}
|
||||
|
||||
private UInt16 _startDelay;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start delay in seconds
|
||||
/// </summary>
|
||||
public UInt16 StartDelay
|
||||
{
|
||||
get
|
||||
{
|
||||
return _startDelay;
|
||||
}
|
||||
set
|
||||
{
|
||||
_startDelay = value;
|
||||
}
|
||||
}
|
||||
|
||||
private UInt16 _stopDelay;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stop delay in seconds
|
||||
/// </summary>
|
||||
public UInt16 StopDelay
|
||||
{
|
||||
get
|
||||
{
|
||||
return _stopDelay;
|
||||
}
|
||||
set
|
||||
{
|
||||
_stopDelay = value;
|
||||
}
|
||||
}
|
||||
|
||||
private float _volume;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the volume in 0.0f - 1.0f
|
||||
/// </summary>
|
||||
public float Volume
|
||||
{
|
||||
get
|
||||
{
|
||||
return _volume;
|
||||
}
|
||||
set
|
||||
{
|
||||
_volume = value;
|
||||
}
|
||||
}
|
||||
|
||||
private UInt16 _kochIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Koch order index
|
||||
/// </summary>
|
||||
public UInt16 KochIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
return _kochIndex;
|
||||
}
|
||||
set
|
||||
{
|
||||
_kochIndex = value;
|
||||
}
|
||||
}
|
||||
|
||||
private CharGenerator.Method _method;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the generation method (Koch or Custom)
|
||||
/// </summary>
|
||||
public CharGenerator.Method GenerationMethod
|
||||
{
|
||||
get
|
||||
{
|
||||
return _method;
|
||||
}
|
||||
set
|
||||
{
|
||||
_method = value;
|
||||
}
|
||||
}
|
||||
|
||||
private String _custom;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the custom string generator
|
||||
/// </summary>
|
||||
public String Custom
|
||||
{
|
||||
get
|
||||
{
|
||||
return _custom;
|
||||
}
|
||||
set
|
||||
{
|
||||
_custom = value;
|
||||
}
|
||||
}
|
||||
|
||||
private bool _favorNew;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to favor newly learned Koch characters
|
||||
/// </summary>
|
||||
public bool FavorNew
|
||||
{
|
||||
get
|
||||
{
|
||||
return _favorNew;
|
||||
}
|
||||
set
|
||||
{
|
||||
_favorNew = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
437
MorseTrainer/Form1.Designer.cs
generated
Normal file
437
MorseTrainer/Form1.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
namespace MorseTrainer
|
||||
{
|
||||
partial class Form1
|
||||
{
|
||||
/// <summary>
|
||||
/// Required designer variable.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
/// <summary>
|
||||
/// Clean up any resources being used.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && (components != null))
|
||||
{
|
||||
components.Dispose();
|
||||
|
||||
// Manually entered disposals
|
||||
if (_player != null)
|
||||
{
|
||||
_player.Dispose();
|
||||
_player = null;
|
||||
}
|
||||
if (_runner != null)
|
||||
{
|
||||
_runner.Dispose();
|
||||
_runner = null;
|
||||
}
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#region Windows Form Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// Required method for Designer support - do not modify
|
||||
/// the contents of this method with the code editor.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
this.label1 = new System.Windows.Forms.Label();
|
||||
this.label2 = new System.Windows.Forms.Label();
|
||||
this.label3 = new System.Windows.Forms.Label();
|
||||
this.label4 = new System.Windows.Forms.Label();
|
||||
this.sliderFrequency = new System.Windows.Forms.TrackBar();
|
||||
this.sliderWPM = new System.Windows.Forms.TrackBar();
|
||||
this.sliderDuration = new System.Windows.Forms.TrackBar();
|
||||
this.sliderFarnsworth = new System.Windows.Forms.TrackBar();
|
||||
this.txtAnalysis = new System.Windows.Forms.RichTextBox();
|
||||
this.label5 = new System.Windows.Forms.Label();
|
||||
this.label6 = new System.Windows.Forms.Label();
|
||||
this.label7 = new System.Windows.Forms.Label();
|
||||
this.sliderStartDelay = new System.Windows.Forms.TrackBar();
|
||||
this.sliderStopDelay = new System.Windows.Forms.TrackBar();
|
||||
this.txtFrequency = new System.Windows.Forms.TextBox();
|
||||
this.txtWPM = new System.Windows.Forms.TextBox();
|
||||
this.txtFarnsworth = new System.Windows.Forms.TextBox();
|
||||
this.txtDuration = new System.Windows.Forms.TextBox();
|
||||
this.btnStartStop = new System.Windows.Forms.Button();
|
||||
this.btnKoch = new System.Windows.Forms.RadioButton();
|
||||
this.btnCustom = new System.Windows.Forms.RadioButton();
|
||||
this.cmbKoch = new System.Windows.Forms.ComboBox();
|
||||
this.txtCustom = new System.Windows.Forms.TextBox();
|
||||
this.chkFavorNew = new System.Windows.Forms.CheckBox();
|
||||
this.sliderVolume = new System.Windows.Forms.TrackBar();
|
||||
this.label8 = new System.Windows.Forms.Label();
|
||||
this.txtStartDelay = new System.Windows.Forms.TextBox();
|
||||
this.txtStopDelay = new System.Windows.Forms.TextBox();
|
||||
this.txtVolume = new System.Windows.Forms.TextBox();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderFrequency)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderWPM)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderDuration)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderFarnsworth)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderStartDelay)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderStopDelay)).BeginInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderVolume)).BeginInit();
|
||||
this.SuspendLayout();
|
||||
//
|
||||
// label1
|
||||
//
|
||||
this.label1.AutoSize = true;
|
||||
this.label1.Location = new System.Drawing.Point(16, 23);
|
||||
this.label1.Name = "label1";
|
||||
this.label1.Size = new System.Drawing.Size(84, 20);
|
||||
this.label1.TabIndex = 0;
|
||||
this.label1.Text = "Frequency";
|
||||
//
|
||||
// label2
|
||||
//
|
||||
this.label2.AutoSize = true;
|
||||
this.label2.Location = new System.Drawing.Point(16, 62);
|
||||
this.label2.Name = "label2";
|
||||
this.label2.Size = new System.Drawing.Size(47, 20);
|
||||
this.label2.TabIndex = 1;
|
||||
this.label2.Text = "WPM";
|
||||
//
|
||||
// label3
|
||||
//
|
||||
this.label3.AutoSize = true;
|
||||
this.label3.Location = new System.Drawing.Point(16, 135);
|
||||
this.label3.Name = "label3";
|
||||
this.label3.Size = new System.Drawing.Size(112, 20);
|
||||
this.label3.TabIndex = 3;
|
||||
this.label3.Text = "Send Duration";
|
||||
//
|
||||
// label4
|
||||
//
|
||||
this.label4.AutoSize = true;
|
||||
this.label4.Location = new System.Drawing.Point(16, 96);
|
||||
this.label4.Name = "label4";
|
||||
this.label4.Size = new System.Drawing.Size(131, 20);
|
||||
this.label4.TabIndex = 2;
|
||||
this.label4.Text = "Farnsworth WPM";
|
||||
//
|
||||
// sliderFrequency
|
||||
//
|
||||
this.sliderFrequency.LargeChange = 100;
|
||||
this.sliderFrequency.Location = new System.Drawing.Point(229, 25);
|
||||
this.sliderFrequency.Maximum = 1000;
|
||||
this.sliderFrequency.Minimum = 400;
|
||||
this.sliderFrequency.Name = "sliderFrequency";
|
||||
this.sliderFrequency.Size = new System.Drawing.Size(637, 69);
|
||||
this.sliderFrequency.SmallChange = 50;
|
||||
this.sliderFrequency.TabIndex = 4;
|
||||
this.sliderFrequency.TickFrequency = 50;
|
||||
this.sliderFrequency.Value = 700;
|
||||
this.sliderFrequency.Scroll += new System.EventHandler(this.sliderFrequency_Scroll);
|
||||
//
|
||||
// sliderWPM
|
||||
//
|
||||
this.sliderWPM.Location = new System.Drawing.Point(229, 62);
|
||||
this.sliderWPM.Maximum = 80;
|
||||
this.sliderWPM.Minimum = 8;
|
||||
this.sliderWPM.Name = "sliderWPM";
|
||||
this.sliderWPM.Size = new System.Drawing.Size(637, 69);
|
||||
this.sliderWPM.TabIndex = 5;
|
||||
this.sliderWPM.Value = 20;
|
||||
this.sliderWPM.Scroll += new System.EventHandler(this.sliderWPM_Scroll);
|
||||
//
|
||||
// sliderDuration
|
||||
//
|
||||
this.sliderDuration.Location = new System.Drawing.Point(229, 137);
|
||||
this.sliderDuration.Minimum = 1;
|
||||
this.sliderDuration.Name = "sliderDuration";
|
||||
this.sliderDuration.Size = new System.Drawing.Size(637, 69);
|
||||
this.sliderDuration.TabIndex = 7;
|
||||
this.sliderDuration.Value = 2;
|
||||
this.sliderDuration.Scroll += new System.EventHandler(this.sliderDuration_Scroll);
|
||||
//
|
||||
// sliderFarnsworth
|
||||
//
|
||||
this.sliderFarnsworth.Location = new System.Drawing.Point(229, 100);
|
||||
this.sliderFarnsworth.Maximum = 80;
|
||||
this.sliderFarnsworth.Minimum = 8;
|
||||
this.sliderFarnsworth.Name = "sliderFarnsworth";
|
||||
this.sliderFarnsworth.Size = new System.Drawing.Size(637, 69);
|
||||
this.sliderFarnsworth.TabIndex = 6;
|
||||
this.sliderFarnsworth.Value = 13;
|
||||
this.sliderFarnsworth.Scroll += new System.EventHandler(this.sliderFarnsworth_Scroll);
|
||||
//
|
||||
// txtAnalysis
|
||||
//
|
||||
this.txtAnalysis.Location = new System.Drawing.Point(20, 288);
|
||||
this.txtAnalysis.Name = "txtAnalysis";
|
||||
this.txtAnalysis.ReadOnly = true;
|
||||
this.txtAnalysis.Size = new System.Drawing.Size(955, 177);
|
||||
this.txtAnalysis.TabIndex = 8;
|
||||
this.txtAnalysis.Text = "";
|
||||
//
|
||||
// label5
|
||||
//
|
||||
this.label5.AutoSize = true;
|
||||
this.label5.Location = new System.Drawing.Point(22, 247);
|
||||
this.label5.Name = "label5";
|
||||
this.label5.Size = new System.Drawing.Size(182, 20);
|
||||
this.label5.TabIndex = 9;
|
||||
this.label5.Text = "Your Recording/Analysis";
|
||||
//
|
||||
// label6
|
||||
//
|
||||
this.label6.AutoSize = true;
|
||||
this.label6.Location = new System.Drawing.Point(16, 186);
|
||||
this.label6.Name = "label6";
|
||||
this.label6.Size = new System.Drawing.Size(88, 20);
|
||||
this.label6.TabIndex = 10;
|
||||
this.label6.Text = "Start Delay";
|
||||
//
|
||||
// label7
|
||||
//
|
||||
this.label7.AutoSize = true;
|
||||
this.label7.Location = new System.Drawing.Point(428, 186);
|
||||
this.label7.Name = "label7";
|
||||
this.label7.Size = new System.Drawing.Size(87, 20);
|
||||
this.label7.TabIndex = 11;
|
||||
this.label7.Text = "Stop Delay";
|
||||
//
|
||||
// sliderStartDelay
|
||||
//
|
||||
this.sliderStartDelay.Location = new System.Drawing.Point(229, 175);
|
||||
this.sliderStartDelay.Maximum = 5;
|
||||
this.sliderStartDelay.Name = "sliderStartDelay";
|
||||
this.sliderStartDelay.Size = new System.Drawing.Size(135, 69);
|
||||
this.sliderStartDelay.TabIndex = 12;
|
||||
this.sliderStartDelay.Value = 2;
|
||||
this.sliderStartDelay.Scroll += new System.EventHandler(this.sliderStartDelay_Scroll);
|
||||
//
|
||||
// sliderStopDelay
|
||||
//
|
||||
this.sliderStopDelay.Location = new System.Drawing.Point(521, 175);
|
||||
this.sliderStopDelay.Maximum = 5;
|
||||
this.sliderStopDelay.Name = "sliderStopDelay";
|
||||
this.sliderStopDelay.Size = new System.Drawing.Size(128, 69);
|
||||
this.sliderStopDelay.TabIndex = 13;
|
||||
this.sliderStopDelay.Value = 2;
|
||||
this.sliderStopDelay.Scroll += new System.EventHandler(this.sliderStopDelay_Scroll);
|
||||
//
|
||||
// txtFrequency
|
||||
//
|
||||
this.txtFrequency.Location = new System.Drawing.Point(887, 27);
|
||||
this.txtFrequency.Name = "txtFrequency";
|
||||
this.txtFrequency.Size = new System.Drawing.Size(88, 26);
|
||||
this.txtFrequency.TabIndex = 14;
|
||||
this.txtFrequency.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtFrequency_KeyPress);
|
||||
//
|
||||
// txtWPM
|
||||
//
|
||||
this.txtWPM.Location = new System.Drawing.Point(887, 64);
|
||||
this.txtWPM.Name = "txtWPM";
|
||||
this.txtWPM.Size = new System.Drawing.Size(88, 26);
|
||||
this.txtWPM.TabIndex = 15;
|
||||
this.txtWPM.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtWPM_KeyPress);
|
||||
//
|
||||
// txtFarnsworth
|
||||
//
|
||||
this.txtFarnsworth.Location = new System.Drawing.Point(887, 102);
|
||||
this.txtFarnsworth.Name = "txtFarnsworth";
|
||||
this.txtFarnsworth.Size = new System.Drawing.Size(88, 26);
|
||||
this.txtFarnsworth.TabIndex = 16;
|
||||
this.txtFarnsworth.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtFarnsworth_KeyPress);
|
||||
//
|
||||
// txtDuration
|
||||
//
|
||||
this.txtDuration.Location = new System.Drawing.Point(887, 137);
|
||||
this.txtDuration.Name = "txtDuration";
|
||||
this.txtDuration.Size = new System.Drawing.Size(88, 26);
|
||||
this.txtDuration.TabIndex = 17;
|
||||
this.txtDuration.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.txtDuration_KeyPress);
|
||||
//
|
||||
// btnStartStop
|
||||
//
|
||||
this.btnStartStop.Location = new System.Drawing.Point(229, 239);
|
||||
this.btnStartStop.Name = "btnStartStop";
|
||||
this.btnStartStop.Size = new System.Drawing.Size(118, 43);
|
||||
this.btnStartStop.TabIndex = 18;
|
||||
this.btnStartStop.Text = "Start";
|
||||
this.btnStartStop.UseVisualStyleBackColor = true;
|
||||
this.btnStartStop.Click += new System.EventHandler(this.btnStartStop_Click);
|
||||
//
|
||||
// btnKoch
|
||||
//
|
||||
this.btnKoch.AutoSize = true;
|
||||
this.btnKoch.Location = new System.Drawing.Point(24, 484);
|
||||
this.btnKoch.Name = "btnKoch";
|
||||
this.btnKoch.Size = new System.Drawing.Size(70, 24);
|
||||
this.btnKoch.TabIndex = 19;
|
||||
this.btnKoch.TabStop = true;
|
||||
this.btnKoch.Text = "Koch";
|
||||
this.btnKoch.UseVisualStyleBackColor = true;
|
||||
this.btnKoch.Click += new System.EventHandler(this.btnKoch_Click);
|
||||
//
|
||||
// btnCustom
|
||||
//
|
||||
this.btnCustom.AutoSize = true;
|
||||
this.btnCustom.Location = new System.Drawing.Point(24, 522);
|
||||
this.btnCustom.Name = "btnCustom";
|
||||
this.btnCustom.Size = new System.Drawing.Size(89, 24);
|
||||
this.btnCustom.TabIndex = 20;
|
||||
this.btnCustom.TabStop = true;
|
||||
this.btnCustom.Text = "Custom";
|
||||
this.btnCustom.UseVisualStyleBackColor = true;
|
||||
this.btnCustom.Click += new System.EventHandler(this.btnCustom_Click);
|
||||
//
|
||||
// cmbKoch
|
||||
//
|
||||
this.cmbKoch.FormattingEnabled = true;
|
||||
this.cmbKoch.Location = new System.Drawing.Point(139, 483);
|
||||
this.cmbKoch.Name = "cmbKoch";
|
||||
this.cmbKoch.Size = new System.Drawing.Size(100, 28);
|
||||
this.cmbKoch.TabIndex = 21;
|
||||
this.cmbKoch.SelectedIndexChanged += new System.EventHandler(this.cmbKoch_SelectedIndexChanged);
|
||||
//
|
||||
// txtCustom
|
||||
//
|
||||
this.txtCustom.Location = new System.Drawing.Point(139, 522);
|
||||
this.txtCustom.Name = "txtCustom";
|
||||
this.txtCustom.Size = new System.Drawing.Size(510, 26);
|
||||
this.txtCustom.TabIndex = 22;
|
||||
//
|
||||
// chkFavorNew
|
||||
//
|
||||
this.chkFavorNew.AutoSize = true;
|
||||
this.chkFavorNew.Location = new System.Drawing.Point(259, 484);
|
||||
this.chkFavorNew.Name = "chkFavorNew";
|
||||
this.chkFavorNew.Size = new System.Drawing.Size(192, 24);
|
||||
this.chkFavorNew.TabIndex = 23;
|
||||
this.chkFavorNew.Text = "Favor New Characters";
|
||||
this.chkFavorNew.UseVisualStyleBackColor = true;
|
||||
this.chkFavorNew.CheckStateChanged += new System.EventHandler(this.chkFavorNew_CheckStateChanged);
|
||||
//
|
||||
// sliderVolume
|
||||
//
|
||||
this.sliderVolume.LargeChange = 10;
|
||||
this.sliderVolume.Location = new System.Drawing.Point(805, 175);
|
||||
this.sliderVolume.Name = "sliderVolume";
|
||||
this.sliderVolume.Size = new System.Drawing.Size(116, 69);
|
||||
this.sliderVolume.TabIndex = 25;
|
||||
this.sliderVolume.TickFrequency = 2;
|
||||
this.sliderVolume.Value = 10;
|
||||
this.sliderVolume.Scroll += new System.EventHandler(this.sliderVolume_Scroll);
|
||||
//
|
||||
// label8
|
||||
//
|
||||
this.label8.AutoSize = true;
|
||||
this.label8.Location = new System.Drawing.Point(712, 186);
|
||||
this.label8.Name = "label8";
|
||||
this.label8.Size = new System.Drawing.Size(63, 20);
|
||||
this.label8.TabIndex = 24;
|
||||
this.label8.Text = "Volume";
|
||||
//
|
||||
// txtStartDelay
|
||||
//
|
||||
this.txtStartDelay.Location = new System.Drawing.Point(370, 180);
|
||||
this.txtStartDelay.Name = "txtStartDelay";
|
||||
this.txtStartDelay.Size = new System.Drawing.Size(35, 26);
|
||||
this.txtStartDelay.TabIndex = 26;
|
||||
//
|
||||
// txtStopDelay
|
||||
//
|
||||
this.txtStopDelay.Location = new System.Drawing.Point(642, 180);
|
||||
this.txtStopDelay.Name = "txtStopDelay";
|
||||
this.txtStopDelay.Size = new System.Drawing.Size(35, 26);
|
||||
this.txtStopDelay.TabIndex = 27;
|
||||
//
|
||||
// txtVolume
|
||||
//
|
||||
this.txtVolume.Location = new System.Drawing.Point(927, 180);
|
||||
this.txtVolume.Name = "txtVolume";
|
||||
this.txtVolume.Size = new System.Drawing.Size(35, 26);
|
||||
this.txtVolume.TabIndex = 28;
|
||||
//
|
||||
// Form1
|
||||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(991, 631);
|
||||
this.Controls.Add(this.txtVolume);
|
||||
this.Controls.Add(this.txtStopDelay);
|
||||
this.Controls.Add(this.txtStartDelay);
|
||||
this.Controls.Add(this.sliderVolume);
|
||||
this.Controls.Add(this.label8);
|
||||
this.Controls.Add(this.chkFavorNew);
|
||||
this.Controls.Add(this.txtCustom);
|
||||
this.Controls.Add(this.cmbKoch);
|
||||
this.Controls.Add(this.btnCustom);
|
||||
this.Controls.Add(this.btnKoch);
|
||||
this.Controls.Add(this.btnStartStop);
|
||||
this.Controls.Add(this.txtDuration);
|
||||
this.Controls.Add(this.txtFarnsworth);
|
||||
this.Controls.Add(this.txtWPM);
|
||||
this.Controls.Add(this.txtFrequency);
|
||||
this.Controls.Add(this.sliderStopDelay);
|
||||
this.Controls.Add(this.sliderStartDelay);
|
||||
this.Controls.Add(this.label7);
|
||||
this.Controls.Add(this.label6);
|
||||
this.Controls.Add(this.label5);
|
||||
this.Controls.Add(this.txtAnalysis);
|
||||
this.Controls.Add(this.sliderDuration);
|
||||
this.Controls.Add(this.sliderFarnsworth);
|
||||
this.Controls.Add(this.sliderWPM);
|
||||
this.Controls.Add(this.sliderFrequency);
|
||||
this.Controls.Add(this.label3);
|
||||
this.Controls.Add(this.label4);
|
||||
this.Controls.Add(this.label2);
|
||||
this.Controls.Add(this.label1);
|
||||
this.KeyPreview = true;
|
||||
this.Name = "Form1";
|
||||
this.Text = "Morse Code Trainer";
|
||||
this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.Form1_FormClosed);
|
||||
this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.Form1_KeyPress);
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderFrequency)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderWPM)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderDuration)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderFarnsworth)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderStartDelay)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderStopDelay)).EndInit();
|
||||
((System.ComponentModel.ISupportInitialize)(this.sliderVolume)).EndInit();
|
||||
this.ResumeLayout(false);
|
||||
this.PerformLayout();
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private System.Windows.Forms.Label label1;
|
||||
private System.Windows.Forms.Label label2;
|
||||
private System.Windows.Forms.Label label3;
|
||||
private System.Windows.Forms.Label label4;
|
||||
private System.Windows.Forms.TrackBar sliderFrequency;
|
||||
private System.Windows.Forms.TrackBar sliderWPM;
|
||||
private System.Windows.Forms.TrackBar sliderDuration;
|
||||
private System.Windows.Forms.TrackBar sliderFarnsworth;
|
||||
private System.Windows.Forms.RichTextBox txtAnalysis;
|
||||
private System.Windows.Forms.Label label5;
|
||||
private System.Windows.Forms.Label label6;
|
||||
private System.Windows.Forms.Label label7;
|
||||
private System.Windows.Forms.TrackBar sliderStartDelay;
|
||||
private System.Windows.Forms.TrackBar sliderStopDelay;
|
||||
private System.Windows.Forms.TextBox txtFrequency;
|
||||
private System.Windows.Forms.TextBox txtWPM;
|
||||
private System.Windows.Forms.TextBox txtFarnsworth;
|
||||
private System.Windows.Forms.TextBox txtDuration;
|
||||
private System.Windows.Forms.Button btnStartStop;
|
||||
private System.Windows.Forms.RadioButton btnKoch;
|
||||
private System.Windows.Forms.RadioButton btnCustom;
|
||||
private System.Windows.Forms.ComboBox cmbKoch;
|
||||
private System.Windows.Forms.TextBox txtCustom;
|
||||
private System.Windows.Forms.CheckBox chkFavorNew;
|
||||
private System.Windows.Forms.TrackBar sliderVolume;
|
||||
private System.Windows.Forms.Label label8;
|
||||
private System.Windows.Forms.TextBox txtStartDelay;
|
||||
private System.Windows.Forms.TextBox txtStopDelay;
|
||||
private System.Windows.Forms.TextBox txtVolume;
|
||||
}
|
||||
}
|
||||
|
||||
1263
MorseTrainer/Form1.cs
Normal file
1263
MorseTrainer/Form1.cs
Normal file
File diff suppressed because it is too large
Load diff
120
MorseTrainer/Form1.resx
Normal file
120
MorseTrainer/Form1.resx
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
66
MorseTrainer/Koch.cs
Normal file
66
MorseTrainer/Koch.cs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
public class Koch
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the
|
||||
/// </summary>
|
||||
/// <param name="c"></param>
|
||||
/// <returns>A string with the characters</returns>
|
||||
static public String CharsUpToAndIncluding(Char c)
|
||||
{
|
||||
int end = IndexOf(c);
|
||||
return CharsUpToAndIncluding(end);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns>A string with the characters</returns>
|
||||
static public String CharsUpToAndIncluding(int index)
|
||||
{
|
||||
String ret = null;
|
||||
if (index > 0 && index < Length)
|
||||
{
|
||||
ret = Order.Substring(0, index);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static public Char[] RecentFromChar(Char c, int numberOfChars)
|
||||
{
|
||||
int end = IndexOf(c);
|
||||
int start = Math.Max(0, end - numberOfChars);
|
||||
return Order.Substring(start, numberOfChars).ToCharArray();
|
||||
}
|
||||
|
||||
static public Char[] RecentFromIndex(Char c, int numberOfChars)
|
||||
{
|
||||
int end = IndexOf(c);
|
||||
int start = Math.Max(0, end - numberOfChars);
|
||||
return Order.Substring(start, numberOfChars).ToCharArray();
|
||||
}
|
||||
|
||||
static public int IndexOf(Char c)
|
||||
{
|
||||
return Order.IndexOf(c);
|
||||
}
|
||||
|
||||
static Koch()
|
||||
{
|
||||
Order = String.Concat("KMRSUAPTLOWI.NJEF0Y,VG5/Q9ZH38B?427C1D6X",
|
||||
"\x80\x81\x82");
|
||||
Length = Order.Length;
|
||||
}
|
||||
|
||||
static public readonly String Order;
|
||||
static public readonly int Length;
|
||||
}
|
||||
}
|
||||
156
MorseTrainer/MorseCompareResults.cs
Normal file
156
MorseTrainer/MorseCompareResults.cs
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
[Flags]
|
||||
public enum ResultsDisplayFlags
|
||||
{
|
||||
Valid = 0x01,
|
||||
Dropped = 0x02,
|
||||
Extra = 0x04,
|
||||
All = 0x07
|
||||
}
|
||||
|
||||
public class MorseCompareResults
|
||||
{
|
||||
public MorseCompareResults(IEnumerable<MorseSubstring> substrings, String sent, String recorded)
|
||||
{
|
||||
_substringList = new List<MorseSubstring>(substrings);
|
||||
_sent = sent;
|
||||
_recorded = recorded;
|
||||
}
|
||||
|
||||
public String Sent
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sent;
|
||||
}
|
||||
}
|
||||
|
||||
public String Recorded
|
||||
{
|
||||
get
|
||||
{
|
||||
return _recorded;
|
||||
}
|
||||
}
|
||||
|
||||
public IList<MorseSubstring> SubStrings
|
||||
{
|
||||
get
|
||||
{
|
||||
return _substringList.AsReadOnly();
|
||||
}
|
||||
}
|
||||
|
||||
private String _sent;
|
||||
private String _recorded;
|
||||
private List<MorseSubstring> _substringList;
|
||||
}
|
||||
|
||||
public abstract class MorseSubstring
|
||||
{
|
||||
protected MorseSubstring() { }
|
||||
protected MorseSubstring(String str)
|
||||
{
|
||||
_str = str;
|
||||
}
|
||||
|
||||
public String Chars
|
||||
{
|
||||
get
|
||||
{
|
||||
return _str;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract System.Drawing.Color Color
|
||||
{
|
||||
get;
|
||||
}
|
||||
public abstract String Str(ResultsDisplayFlags flags = ResultsDisplayFlags.All);
|
||||
|
||||
protected string _str;
|
||||
}
|
||||
|
||||
public class MorseValid : MorseSubstring
|
||||
{
|
||||
public MorseValid(String str) : base(str)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Str(ResultsDisplayFlags flags)
|
||||
{
|
||||
String ret = "";
|
||||
if ((flags & ResultsDisplayFlags.Valid) != 0)
|
||||
{
|
||||
ret = MorseInfo.ExpandProsigns(_str);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override Color Color
|
||||
{
|
||||
get
|
||||
{
|
||||
return Color.Black;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MorseDropped : MorseSubstring
|
||||
{
|
||||
public MorseDropped(String str) : base(str)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Str(ResultsDisplayFlags flags)
|
||||
{
|
||||
String ret = "";
|
||||
if ((flags & ResultsDisplayFlags.Dropped) != 0)
|
||||
{
|
||||
ret = MorseInfo.ExpandProsigns(_str);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override Color Color
|
||||
{
|
||||
get
|
||||
{
|
||||
return Color.Red;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MorseExtra : MorseSubstring
|
||||
{
|
||||
public MorseExtra(String str) : base(str)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Str(ResultsDisplayFlags flags)
|
||||
{
|
||||
String ret = "";
|
||||
if ((flags & ResultsDisplayFlags.Extra) != 0)
|
||||
{
|
||||
ret = MorseInfo.ExpandProsigns(_str);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public override Color Color
|
||||
{
|
||||
get
|
||||
{
|
||||
return Color.Orange;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
149
MorseTrainer/MorseInfo.cs
Normal file
149
MorseTrainer/MorseInfo.cs
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
public class MorseInfo
|
||||
{
|
||||
public const char PROSIGN_BT = '\x80';
|
||||
public const char PROSIGN_SK = '\x81';
|
||||
public const char PROSIGN_AR = '\x82';
|
||||
|
||||
static MorseInfo()
|
||||
{
|
||||
// save the morse conversions into an array
|
||||
__conversions = new String[256];
|
||||
__conversions['A'] = ".-";
|
||||
__conversions['B'] = "-...";
|
||||
__conversions['C'] = "-.-.";
|
||||
__conversions['D'] = "-..";
|
||||
__conversions['E'] = ".";
|
||||
__conversions['F'] = "..-.";
|
||||
__conversions['G'] = "--.";
|
||||
__conversions['H'] = "....";
|
||||
__conversions['I'] = "..";
|
||||
__conversions['J'] = ".---";
|
||||
__conversions['K'] = "-.-";
|
||||
__conversions['L'] = ".-..";
|
||||
__conversions['M'] = "--";
|
||||
__conversions['N'] = "-.";
|
||||
__conversions['O'] = "---";
|
||||
__conversions['P'] = ".--.";
|
||||
__conversions['Q'] = "--.-";
|
||||
__conversions['R'] = ".-.";
|
||||
__conversions['S'] = "...";
|
||||
__conversions['T'] = "-";
|
||||
__conversions['U'] = "..-";
|
||||
__conversions['V'] = "...-";
|
||||
__conversions['W'] = ".--";
|
||||
__conversions['X'] = "";
|
||||
__conversions['Y'] = "-.--";
|
||||
__conversions['Z'] = "--..";
|
||||
|
||||
__conversions['0'] = "-----";
|
||||
__conversions['1'] = ".----";
|
||||
__conversions['2'] = "..---";
|
||||
__conversions['3'] = "...--";
|
||||
__conversions['4'] = "....-";
|
||||
__conversions['5'] = ".....";
|
||||
__conversions['6'] = "-....";
|
||||
__conversions['7'] = "--...";
|
||||
__conversions['8'] = "---..";
|
||||
__conversions['9'] = "----.";
|
||||
|
||||
__conversions['.'] = ".-.-.-";
|
||||
__conversions[','] = "--..--";
|
||||
__conversions['?'] = "..--..";
|
||||
__conversions['/'] = "-..-.";
|
||||
|
||||
__conversions[PROSIGN_BT] = "-...-";
|
||||
__conversions[PROSIGN_SK] = "...-.-";
|
||||
__conversions[PROSIGN_AR] = ".-.-.";
|
||||
|
||||
__prosignExpansionToValue = new Dictionary<string, char>();
|
||||
__prosignExpansionToValue.Add("<BT>", PROSIGN_BT);
|
||||
__prosignExpansionToValue.Add("<SK>", PROSIGN_SK);
|
||||
__prosignExpansionToValue.Add("<AR>", PROSIGN_AR);
|
||||
|
||||
__prosignValueToExpansion = new Dictionary<char, string>();
|
||||
foreach (KeyValuePair<String, char> kv in __prosignExpansionToValue)
|
||||
{
|
||||
__prosignValueToExpansion.Add(kv.Value, kv.Key);
|
||||
}
|
||||
|
||||
|
||||
// save the number of elements--needed to calculate Farnsworth timing
|
||||
__elements = new int[256];
|
||||
for (int i = 0; i < __elements.Length; ++i)
|
||||
{
|
||||
int elements = 0;
|
||||
String s = __conversions[i];
|
||||
if (!String.IsNullOrEmpty(s))
|
||||
{
|
||||
foreach (char c in s)
|
||||
{
|
||||
if (c == '.')
|
||||
{
|
||||
elements += 1;
|
||||
}
|
||||
else if (c == '-')
|
||||
{
|
||||
elements += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
__elements[i] = elements;
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<String, char> __prosignExpansionToValue;
|
||||
private static Dictionary<char, String> __prosignValueToExpansion;
|
||||
private static String[] __conversions;
|
||||
private static int[] __elements;
|
||||
|
||||
public static int ToElements(Char c)
|
||||
{
|
||||
if (c < 0 || c >= __elements.Length)
|
||||
{
|
||||
throw new ArgumentException("c");
|
||||
}
|
||||
return __elements[c];
|
||||
}
|
||||
|
||||
public static String ToMorse(Char c)
|
||||
{
|
||||
if (c < 0 || c >= __elements.Length)
|
||||
{
|
||||
throw new ArgumentException("c");
|
||||
}
|
||||
String s = __conversions[c];
|
||||
return s;
|
||||
}
|
||||
|
||||
public static String ReplaceProsigns(String expandedProsigns)
|
||||
{
|
||||
String replaced = expandedProsigns;
|
||||
foreach (KeyValuePair<String, char> kv in __prosignExpansionToValue)
|
||||
{
|
||||
String expansion = kv.Key;
|
||||
String value = kv.Value.ToString();
|
||||
replaced = replaced.Replace(expansion, value);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
public static String ExpandProsigns(String valuedProsigns)
|
||||
{
|
||||
String replaced = valuedProsigns;
|
||||
foreach (KeyValuePair<String, char> kv in __prosignExpansionToValue)
|
||||
{
|
||||
String expansion = kv.Key;
|
||||
String value = kv.Value.ToString();
|
||||
replaced = replaced.Replace(value, expansion);
|
||||
}
|
||||
return replaced;
|
||||
}
|
||||
}
|
||||
}
|
||||
103
MorseTrainer/MorseTrainer.csproj
Normal file
103
MorseTrainer/MorseTrainer.csproj
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{48E042F6-B6A3-4ABF-B272-A76F9533FDF9}</ProjectGuid>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>MorseTrainer</RootNamespace>
|
||||
<AssemblyName>MorseTrainer</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Deployment" />
|
||||
<Reference Include="System.Drawing" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Analyzer.cs" />
|
||||
<Compile Include="CharGenerator.cs" />
|
||||
<Compile Include="Config.cs" />
|
||||
<Compile Include="MorseInfo.cs" />
|
||||
<Compile Include="Comparer.cs" />
|
||||
<Compile Include="Form1.cs">
|
||||
<SubType>Form</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Form1.Designer.cs">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Koch.cs" />
|
||||
<Compile Include="MorseCompareResults.cs" />
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Runner.cs" />
|
||||
<Compile Include="SoundPlayerAsync.cs" />
|
||||
<Compile Include="ToneGenerator.cs" />
|
||||
<Compile Include="WaveBuilder.cs" />
|
||||
<Compile Include="WaveStream.cs" />
|
||||
<Compile Include="WordToToneBuilder.cs" />
|
||||
<EmbeddedResource Include="Form1.resx">
|
||||
<DependentUpon>Form1.cs</DependentUpon>
|
||||
</EmbeddedResource>
|
||||
<EmbeddedResource Include="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
<SubType>Designer</SubType>
|
||||
</EmbeddedResource>
|
||||
<Compile Include="Properties\Resources.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<None Include="Properties\Settings.settings">
|
||||
<Generator>SettingsSingleFileGenerator</Generator>
|
||||
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||
</None>
|
||||
<Compile Include="Properties\Settings.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Settings.settings</DependentUpon>
|
||||
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
</Target>
|
||||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
22
MorseTrainer/Program.cs
Normal file
22
MorseTrainer/Program.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
static class Program
|
||||
{
|
||||
/// <summary>
|
||||
/// The main entry point for the application.
|
||||
/// </summary>
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
Application.EnableVisualStyles();
|
||||
Application.SetCompatibleTextRenderingDefault(false);
|
||||
Application.Run(new Form1());
|
||||
}
|
||||
}
|
||||
}
|
||||
36
MorseTrainer/Properties/AssemblyInfo.cs
Normal file
36
MorseTrainer/Properties/AssemblyInfo.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("MorseTrainer")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("MorseTrainer")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2016")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("48e042f6-b6a3-4abf-b272-a76f9533fdf9")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
71
MorseTrainer/Properties/Resources.Designer.cs
generated
Normal file
71
MorseTrainer/Properties/Resources.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace MorseTrainer.Properties
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Resources
|
||||
{
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Resources()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager
|
||||
{
|
||||
get
|
||||
{
|
||||
if ((resourceMan == null))
|
||||
{
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MorseTrainer.Properties.Resources", typeof(Resources).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture
|
||||
{
|
||||
get
|
||||
{
|
||||
return resourceCulture;
|
||||
}
|
||||
set
|
||||
{
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
117
MorseTrainer/Properties/Resources.resx
Normal file
117
MorseTrainer/Properties/Resources.resx
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
</root>
|
||||
30
MorseTrainer/Properties/Settings.Designer.cs
generated
Normal file
30
MorseTrainer/Properties/Settings.Designer.cs
generated
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.42000
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace MorseTrainer.Properties
|
||||
{
|
||||
|
||||
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||
{
|
||||
|
||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||
|
||||
public static Settings Default
|
||||
{
|
||||
get
|
||||
{
|
||||
return defaultInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
MorseTrainer/Properties/Settings.settings
Normal file
7
MorseTrainer/Properties/Settings.settings
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
|
||||
<Profiles>
|
||||
<Profile Name="(Default)" />
|
||||
</Profiles>
|
||||
<Settings />
|
||||
</SettingsFile>
|
||||
377
MorseTrainer/Runner.cs
Normal file
377
MorseTrainer/Runner.cs
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The Runner class handles the timing of the morse code runnning.
|
||||
/// It tracks 5 basic states:
|
||||
/// Idle: no tests being run
|
||||
/// StartDelay: countdown to starting the morse code
|
||||
/// Sending: Sending morse code
|
||||
/// SendFinished: Send time is up, waiting for last word to finish up
|
||||
/// StopDelay: Still allowing user to enter characters
|
||||
/// </summary>
|
||||
public class Runner : IDisposable
|
||||
{
|
||||
private enum State
|
||||
{
|
||||
// waiting
|
||||
Idle,
|
||||
|
||||
// waiting
|
||||
StartRequest,
|
||||
|
||||
// user has started and delay is counting
|
||||
StartDelay,
|
||||
|
||||
// sending morse
|
||||
Sending,
|
||||
|
||||
// send is done, but finishing up the rest of the word
|
||||
SendFinished,
|
||||
|
||||
// send is
|
||||
StopDelay,
|
||||
|
||||
// stop requested
|
||||
StopRequest
|
||||
}
|
||||
|
||||
public const int MIN_DURATION = 30;
|
||||
public const int MAX_DURATION = 60*10;
|
||||
public const int MIN_START_DELAY = 0;
|
||||
public const int MAX_START_DELAY = 5;
|
||||
public const int MIN_STOP_DELAY = 0;
|
||||
public const int MAX_STOP_DELAY = 5;
|
||||
|
||||
public Runner()
|
||||
{
|
||||
_timer = new System.Timers.Timer();
|
||||
_timer.Enabled = true;
|
||||
_timer.AutoReset = false;
|
||||
_timer.Elapsed += _timer_Elapsed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the runner is idle (false) or in one of the running states (true)
|
||||
/// </summary>
|
||||
public bool IsRunning
|
||||
{
|
||||
get
|
||||
{
|
||||
return _state != State.Idle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets is the runner is in a mode where the user input should be recorded.
|
||||
/// These states are Sending, SendFinished, and StopDelay
|
||||
/// </summary>
|
||||
public bool IsListenMode
|
||||
{
|
||||
get
|
||||
{
|
||||
return _state == State.Sending ||
|
||||
_state == State.SendFinished ||
|
||||
_state == State.StopDelay;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bool that indicate if the morse is being sent and a new word
|
||||
/// should be sent to the tone generator.
|
||||
/// </summary>
|
||||
public bool ContinueMorse
|
||||
{
|
||||
get
|
||||
{
|
||||
return _state == State.Sending;
|
||||
}
|
||||
}
|
||||
|
||||
private void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
State nextState = StateExit();
|
||||
StateEnter(nextState);
|
||||
}
|
||||
|
||||
private State StateExit()
|
||||
{
|
||||
State nextState = _state;
|
||||
switch (_state)
|
||||
{
|
||||
case State.StartDelay:
|
||||
_timer.Stop();
|
||||
OnStartDelayExit();
|
||||
nextState = State.Sending;
|
||||
break;
|
||||
case State.Sending:
|
||||
_timer.Stop();
|
||||
OnMorseExit();
|
||||
nextState = State.SendFinished;
|
||||
break;
|
||||
case State.StopDelay:
|
||||
_timer.Stop();
|
||||
OnStopDelayExit();
|
||||
nextState = State.Idle;
|
||||
break;
|
||||
}
|
||||
return nextState;
|
||||
}
|
||||
|
||||
|
||||
private void StateEnter(State nextState)
|
||||
{
|
||||
_state = nextState;
|
||||
switch (_state)
|
||||
{
|
||||
case State.StartDelay:
|
||||
_timer.Interval = _startDelay * 1000;
|
||||
_timer.Start();
|
||||
OnStartDelayEnter();
|
||||
break;
|
||||
case State.Sending:
|
||||
_timer.Interval = _sendDuration * 1000;
|
||||
_timer.Start();
|
||||
OnMorseEnter();
|
||||
break;
|
||||
case State.StopDelay:
|
||||
_timer.Interval = _stopDelay * 1000;
|
||||
_timer.Start();
|
||||
OnStopDelayEnter();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the start delay in seconds. This is the time for the user
|
||||
/// to recover from pressing the start button to positioning their hands
|
||||
/// for input, for example.
|
||||
/// </summary>
|
||||
public int StartDelay
|
||||
{
|
||||
get
|
||||
{
|
||||
return _startDelay;
|
||||
}
|
||||
set
|
||||
{
|
||||
_startDelay = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the stop delay in seconds. This is the time after the last word
|
||||
/// was sent to let the user input the remaining letters.
|
||||
/// </summary>
|
||||
public int StopDelay
|
||||
{
|
||||
get
|
||||
{
|
||||
return _stopDelay;
|
||||
}
|
||||
set
|
||||
{
|
||||
_stopDelay = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the send duration in seconds.
|
||||
/// </summary>
|
||||
public int SendDuration
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sendDuration;
|
||||
}
|
||||
set
|
||||
{
|
||||
_sendDuration = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts the runner.
|
||||
/// </summary>
|
||||
public void RequestStart()
|
||||
{
|
||||
State startState = (_startDelay > 0) ? State.StartDelay : State.Sending;
|
||||
StateEnter(startState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exits the current state and then fires the Abort event. The abort event
|
||||
/// is only fired if the runner is not running
|
||||
/// </summary>
|
||||
public void RequestStop()
|
||||
{
|
||||
bool abort = _state != State.Idle;
|
||||
_state = StateExit();
|
||||
|
||||
// stop request during start delay
|
||||
if (_state == State.Sending)
|
||||
{
|
||||
_state = State.Idle;
|
||||
}
|
||||
if (abort)
|
||||
{
|
||||
OnAbort();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used when the SendMorseEnd is sent while sending is still occurring.
|
||||
/// Call this in the ToneGenerator.CharactersSent event
|
||||
/// </summary>
|
||||
public void AcknowledgeSendEnd()
|
||||
{
|
||||
if (_state == State.SendFinished)
|
||||
{
|
||||
StateEnter(State.StopDelay);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Countdown to morse code sending has started
|
||||
/// </summary>
|
||||
public event EventHandler StartDelayEnter;
|
||||
protected void OnStartDelayEnter()
|
||||
{
|
||||
EventHandler handler = StartDelayEnter;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The countdown delay has ended
|
||||
/// </summary>
|
||||
public event EventHandler StartDelayExit;
|
||||
protected void OnStartDelayExit()
|
||||
{
|
||||
EventHandler handler = StartDelayExit;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Start morse code sending.
|
||||
/// </summary>
|
||||
public event EventHandler MorseEnter;
|
||||
protected void OnMorseEnter()
|
||||
{
|
||||
EventHandler handler = MorseEnter;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop feeding tone generator with characters and let it finish up
|
||||
/// </summary>
|
||||
public event EventHandler MorseExit;
|
||||
protected void OnMorseExit()
|
||||
{
|
||||
EventHandler handler = MorseExit;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Morse code has stopped, but the user is still allowed to type in keys
|
||||
/// </summary>
|
||||
public event EventHandler StopDelayEnter;
|
||||
protected void OnStopDelayEnter()
|
||||
{
|
||||
EventHandler handler = StopDelayEnter;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Post sending delay is over, time to analyze
|
||||
/// </summary>
|
||||
public event EventHandler StopDelayExit;
|
||||
protected void OnStopDelayExit()
|
||||
{
|
||||
EventHandler handler = StopDelayExit;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Abort
|
||||
/// </summary>
|
||||
public event EventHandler Abort;
|
||||
protected void OnAbort()
|
||||
{
|
||||
EventHandler handler = Abort;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private State _state;
|
||||
private System.Timers.Timer _timer;
|
||||
private int _startDelay;
|
||||
private int _sendDuration;
|
||||
private int _stopDelay;
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_timer != null)
|
||||
{
|
||||
_timer.Stop();
|
||||
_timer.Dispose();
|
||||
_timer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||
// TODO: set large fields to null.
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
|
||||
// ~Runner() {
|
||||
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
// Dispose(false);
|
||||
// }
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(true);
|
||||
// TODO: uncomment the following line if the finalizer is overridden above.
|
||||
// GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
230
MorseTrainer/SoundPlayerAsync.cs
Normal file
230
MorseTrainer/SoundPlayerAsync.cs
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
public class SoundPlayerAsync : IDisposable
|
||||
{
|
||||
public SoundPlayerAsync()
|
||||
{
|
||||
_mediaSoundPlayer = new System.Media.SoundPlayer();
|
||||
_queue = new Queue<WaveStream>();
|
||||
_sentString = new StringBuilder();
|
||||
|
||||
_stopThread = false;
|
||||
_thread = new System.Threading.Thread(ThreadMain);
|
||||
_thread.Name = "Async Player";
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
private void ThreadMain()
|
||||
{
|
||||
while (!_stopThread)
|
||||
{
|
||||
WaveStream waveToPlay = Dequeue();
|
||||
if (waveToPlay != null)
|
||||
{
|
||||
_mediaSoundPlayer.Stream = waveToPlay.Stream;
|
||||
_mediaSoundPlayer.Load();
|
||||
_mediaSoundPlayer.PlaySync();
|
||||
_sentString.Append(waveToPlay.Text);
|
||||
_sentString.Append(' ');
|
||||
// All done
|
||||
if (Count == 0)
|
||||
{
|
||||
OnPlayingFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
lock(this)
|
||||
{
|
||||
_stopped = true;
|
||||
System.Threading.Monitor.Pulse(this);
|
||||
}
|
||||
}
|
||||
|
||||
private WaveStream Dequeue()
|
||||
{
|
||||
WaveStream wave = null;
|
||||
lock (this)
|
||||
{
|
||||
while (_queue.Count == 0 && !_stopThread)
|
||||
{
|
||||
System.Threading.Monitor.Wait(this);
|
||||
}
|
||||
if (_queue.Count > 0)
|
||||
{
|
||||
wave = _queue.Dequeue();
|
||||
if (_queue.Count == 0)
|
||||
{
|
||||
OnQueueEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
return wave;
|
||||
}
|
||||
|
||||
public void Start(WaveStream wave)
|
||||
{
|
||||
Enqueue(wave);
|
||||
_sentString.Clear();
|
||||
}
|
||||
|
||||
public String Sent
|
||||
{
|
||||
get
|
||||
{
|
||||
return _sentString.ToString().TrimEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Give the SoundPlayerAsync a wave to play immediately (if noty busy) or
|
||||
/// following the currently playing/enqueued waves
|
||||
/// </summary>
|
||||
/// <param name="wave"></param>
|
||||
public void Enqueue(WaveStream wave)
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
_queue.Enqueue(wave);
|
||||
System.Threading.Monitor.Pulse(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clear all waves from the queue when aborting
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
_queue.Clear();
|
||||
System.Threading.Monitor.Pulse(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of enqueued waves
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
return _queue.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Playing has finished and no more waves are enqueued
|
||||
/// </summary>
|
||||
public event EventHandler PlayingFinished;
|
||||
protected void OnPlayingFinished()
|
||||
{
|
||||
EventHandler handler = PlayingFinished;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The queue has been emptied. Action in this should be quick
|
||||
/// </summary>
|
||||
public event EventHandler QueueEmpty;
|
||||
protected void OnQueueEmpty()
|
||||
{
|
||||
EventHandler handler = QueueEmpty;
|
||||
if (handler != null)
|
||||
{
|
||||
handler(this, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the thread.
|
||||
/// </summary>
|
||||
public void CloseAndJoin()
|
||||
{
|
||||
lock(this)
|
||||
{
|
||||
if (_thread != null && !_stopThread)
|
||||
{
|
||||
_stopThread = true;
|
||||
_stopped = false;
|
||||
_queue.Clear();
|
||||
System.Threading.Monitor.Pulse(this);
|
||||
if (!_stopped)
|
||||
{
|
||||
System.Threading.Monitor.Wait(this);
|
||||
}
|
||||
_thread.Join();
|
||||
_thread = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private System.Threading.Thread _thread;
|
||||
private bool _stopThread;
|
||||
private bool _stopped;
|
||||
private Queue<WaveStream> _queue;
|
||||
private System.Media.SoundPlayer _mediaSoundPlayer;
|
||||
private StringBuilder _sentString;
|
||||
|
||||
#region IDisposable Support
|
||||
private bool disposedValue = false; // To detect redundant calls
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// TODO: dispose managed state (managed objects).
|
||||
CloseAndJoin();
|
||||
|
||||
if (_queue != null)
|
||||
{
|
||||
_queue.Clear();
|
||||
_queue = null;
|
||||
}
|
||||
|
||||
if (_mediaSoundPlayer != null)
|
||||
{
|
||||
_mediaSoundPlayer.Stop();
|
||||
_mediaSoundPlayer.Dispose();
|
||||
_mediaSoundPlayer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
|
||||
// TODO: set large fields to null.
|
||||
|
||||
disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
|
||||
// ~SoundPlayerAsync() {
|
||||
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
// Dispose(false);
|
||||
// }
|
||||
|
||||
// This code added to correctly implement the disposable pattern.
|
||||
public void Dispose()
|
||||
{
|
||||
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
|
||||
Dispose(true);
|
||||
// TODO: uncomment the following line if the finalizer is overridden above.
|
||||
// GC.SuppressFinalize(this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
297
MorseTrainer/ToneGenerator.cs
Normal file
297
MorseTrainer/ToneGenerator.cs
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Media;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
public class ToneGenerator
|
||||
{
|
||||
public const UInt16 MIN_FREQUENCY = 300;
|
||||
public const UInt16 MAX_FREQUENCY = 1200;
|
||||
public const float MIN_WPM = 3.0f;
|
||||
public const float MAX_WPM = 40.0f;
|
||||
public const float MIN_FARNSWORTH_WPM = 3.0f;
|
||||
public const float MAX_FARNSWORTH_WPM = 40.0f;
|
||||
public const float MIN_VOLUME = 0.0f;
|
||||
public const float MAX_VOLUME = 1.0f;
|
||||
|
||||
public const Int32 SAMPLES_PER_SECOND = 8000;
|
||||
|
||||
public ToneGenerator()
|
||||
{
|
||||
_frequency = 0;
|
||||
_WPM = 0;
|
||||
_farnsworthWPM = 0;
|
||||
_volume = 0.0f;
|
||||
}
|
||||
|
||||
private float WpmToMillesecPerElement(float wpm)
|
||||
{
|
||||
float millisecPerElement =
|
||||
1000.0f /* ms per sec */
|
||||
* 60.0f /* Sec / min */
|
||||
/ wpm
|
||||
/ 50; /* elem per word */
|
||||
return millisecPerElement;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// changing tone frequency or WPM will cause the tones to be regenerated
|
||||
if (_frequency == 0 || _frequency != _newFrequency || _newWPM != _WPM || _newVolume != _volume)
|
||||
{
|
||||
_volume = _newVolume;
|
||||
_frequency = _newFrequency;
|
||||
_WPM = _newWPM;
|
||||
// ms per element = WPM * 1000 ms per sec / 60 words per second / 50 elements
|
||||
_msPerElement = WpmToMillesecPerElement(_WPM);
|
||||
|
||||
float samplesPerCycle = SAMPLES_PER_SECOND / _frequency;
|
||||
|
||||
_samplesPerCycle = (UInt16)(samplesPerCycle + 0.5);
|
||||
CreateTones();
|
||||
}
|
||||
if (_farnsworthWPM == 0 || _farnsworthWPM != _newFarnsworthWPM)
|
||||
{
|
||||
_farnsworthWPM = _newFarnsworthWPM;
|
||||
_msPerFarnsworthElement = WpmToMillesecPerElement(_farnsworthWPM);
|
||||
}
|
||||
}
|
||||
|
||||
public UInt16 CurrentFrequency
|
||||
{
|
||||
get
|
||||
{
|
||||
return _frequency;
|
||||
}
|
||||
}
|
||||
|
||||
public UInt16 Frequency
|
||||
{
|
||||
get
|
||||
{
|
||||
return _newFrequency;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < MIN_FREQUENCY || value > MAX_FREQUENCY)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Frequency");
|
||||
}
|
||||
_newFrequency = value;
|
||||
}
|
||||
}
|
||||
|
||||
public float Volume
|
||||
{
|
||||
get
|
||||
{
|
||||
return _newVolume;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < MIN_VOLUME || value > MAX_VOLUME)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Volume");
|
||||
}
|
||||
_newVolume = value;
|
||||
}
|
||||
}
|
||||
public float CurrentVolume
|
||||
{
|
||||
get
|
||||
{
|
||||
return _volume;
|
||||
}
|
||||
}
|
||||
|
||||
public float WPM
|
||||
{
|
||||
get
|
||||
{
|
||||
return _newWPM;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < MIN_WPM || value > MAX_WPM)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("WPM");
|
||||
}
|
||||
_newWPM = (float)Math.Round(value * 2) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public float CurrentFarnsworthWPM
|
||||
{
|
||||
get
|
||||
{
|
||||
return _farnsworthWPM;
|
||||
}
|
||||
}
|
||||
|
||||
public float FarnsworthWPM
|
||||
{
|
||||
get
|
||||
{
|
||||
return _newFarnsworthWPM;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value < MIN_WPM || value > MAX_WPM)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("WPM");
|
||||
}
|
||||
_newFarnsworthWPM = (float)Math.Round(value * 2) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
public UInt32 SamplesPerCycle
|
||||
{
|
||||
get
|
||||
{
|
||||
return _samplesPerCycle;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateTones()
|
||||
{
|
||||
System.IO.MemoryStream streamDot = new System.IO.MemoryStream();
|
||||
System.IO.MemoryStream streamDash = new System.IO.MemoryStream();
|
||||
|
||||
_dotToneWaveform = CreateTone(_msPerElement);
|
||||
_dashToneWaveform = CreateTone(_msPerElement*3);
|
||||
_dotSpaceWaveform = CreateSpace(_msPerElement);
|
||||
_letterSpaceWaveform = CreateSpace(_msPerElement*3);
|
||||
_wordSpaceWaveform = CreateSpace(_msPerElement * 7);
|
||||
}
|
||||
|
||||
private Int16[] CreateSpace(float millisec)
|
||||
{
|
||||
float samples = SAMPLES_PER_SECOND * millisec / 1000;
|
||||
|
||||
UInt32 actualSamples = (UInt32)samples;
|
||||
|
||||
Int16[] waveform = new Int16[actualSamples];
|
||||
|
||||
// Fill in the space
|
||||
for (UInt32 sample = 0; sample < actualSamples; ++sample)
|
||||
{
|
||||
waveform[sample] = 0;
|
||||
}
|
||||
|
||||
return waveform;
|
||||
}
|
||||
|
||||
private Int16[] CreateTone(float millisec)
|
||||
{
|
||||
float samples = SAMPLES_PER_SECOND * millisec / 1000;
|
||||
|
||||
// Nyquist check
|
||||
if (samples < _samplesPerCycle/2)
|
||||
{
|
||||
throw new ArgumentException("samples");
|
||||
}
|
||||
|
||||
// Create stream
|
||||
// Get a number of cyucles to make the tone end at the end of a sinewave to prevent a pop
|
||||
UInt32 actualSamples = (UInt32)samples;
|
||||
actualSamples = actualSamples - (actualSamples % _samplesPerCycle) + 1;
|
||||
|
||||
Int16[] waveform = new Int16[actualSamples];
|
||||
|
||||
uint fade = _samplesPerCycle;
|
||||
// Fill in the tone
|
||||
for (UInt32 sample = 0; sample < actualSamples; ++sample)
|
||||
{
|
||||
float envelope = 1.0f;
|
||||
if (sample < fade)
|
||||
{
|
||||
envelope = (float)sample / (float)fade;
|
||||
}
|
||||
else if (sample > actualSamples - fade)
|
||||
{
|
||||
envelope = (float)(actualSamples - sample) / (float)fade;
|
||||
}
|
||||
float instantaneous = (float)Math.Sin((float)(sample % _samplesPerCycle) / _samplesPerCycle * (2 * Math.PI )) * _volume * envelope;
|
||||
waveform[sample] = (Int16)(32767 * instantaneous);
|
||||
}
|
||||
|
||||
return waveform;
|
||||
}
|
||||
|
||||
public Int16[] DotToneWaveform
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dotToneWaveform;
|
||||
}
|
||||
}
|
||||
|
||||
public Int16[] DashToneWaveform
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dashToneWaveform;
|
||||
}
|
||||
}
|
||||
|
||||
public Int16[] DotSpaceWaveform
|
||||
{
|
||||
get
|
||||
{
|
||||
return _dotSpaceWaveform;
|
||||
}
|
||||
}
|
||||
|
||||
public Int16[] LetterSpaceWaveform
|
||||
{
|
||||
get
|
||||
{
|
||||
return _letterSpaceWaveform;
|
||||
}
|
||||
}
|
||||
|
||||
public Int16[] WordSpaceWaveform
|
||||
{
|
||||
get
|
||||
{
|
||||
return _wordSpaceWaveform;
|
||||
}
|
||||
}
|
||||
|
||||
public Int16[] FarnsworthSpacingWaveform(Char c)
|
||||
{
|
||||
// Get the equivalent dots
|
||||
int elements = MorseInfo.ToElements(c);
|
||||
|
||||
// Farnsworth timing stretches time between characters.
|
||||
// Character has already been sent WPM and now we have to
|
||||
// wait as if it were sent FarnsworthWPM
|
||||
float wpmTime = _msPerElement * elements;
|
||||
float farnsworthTime = _msPerFarnsworthElement * elements;
|
||||
float difference = farnsworthTime - wpmTime;
|
||||
|
||||
return CreateSpace(difference);
|
||||
}
|
||||
|
||||
private UInt16 _frequency;
|
||||
private UInt16 _newFrequency;
|
||||
private float _WPM;
|
||||
private float _newWPM;
|
||||
private float _farnsworthWPM;
|
||||
private float _newFarnsworthWPM;
|
||||
private float _volume;
|
||||
private float _newVolume;
|
||||
private float _msPerElement;
|
||||
private float _msPerFarnsworthElement;
|
||||
private UInt32 _samplesPerCycle;
|
||||
private Int16[] _dotToneWaveform;
|
||||
private Int16[] _dashToneWaveform;
|
||||
private Int16[] _dotSpaceWaveform;
|
||||
private Int16[] _letterSpaceWaveform;
|
||||
private Int16[] _wordSpaceWaveform;
|
||||
}
|
||||
}
|
||||
83
MorseTrainer/WaveBuilder.cs
Normal file
83
MorseTrainer/WaveBuilder.cs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
public class WaveBuilder
|
||||
{
|
||||
public WaveBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
public Int16[] Dot
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public Int16[] Dash
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public Int16[] DotSpace
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public Int16[] DashSpace
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public UInt16[] WordSpace
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
set
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void Append(String morse)
|
||||
{
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
}
|
||||
|
||||
public WaveStream GetFinalStream()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
159
MorseTrainer/WaveStream.cs
Normal file
159
MorseTrainer/WaveStream.cs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
public class WaveStream
|
||||
{
|
||||
public WaveStream(String text, Int16[] waveform, UInt32 sampleRate, UInt32 samplesPerCycle)
|
||||
{
|
||||
_text = text;
|
||||
SetupStream(waveform, sampleRate, samplesPerCycle);
|
||||
}
|
||||
|
||||
public WaveStream(String text, IEnumerable<Int16[]> waveforms, UInt32 sampleRate, UInt32 samplesPerCycle)
|
||||
{
|
||||
_text = text;
|
||||
SetupStream(waveforms, sampleRate, samplesPerCycle);
|
||||
}
|
||||
|
||||
public String Text
|
||||
{
|
||||
get
|
||||
{
|
||||
return _text;
|
||||
}
|
||||
}
|
||||
|
||||
public System.IO.Stream Stream
|
||||
{
|
||||
get
|
||||
{
|
||||
return _stream;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a stream
|
||||
// Header
|
||||
// 4 bytes "RIFF"
|
||||
// 4 bytes chunkSize
|
||||
// 4 bytes "WAVE"
|
||||
//
|
||||
// Subchunk 1
|
||||
// 4 bytes subchunk ID ("fmt ")
|
||||
// 4 bytes subchunk size (16)
|
||||
// 2 bytes audio format (1)
|
||||
// 2 bytes num channels (1)
|
||||
// 4 bytes sample rate (8000)
|
||||
// 4 bytes byte rate = SampleRate * NumChannels * BitsPerSample/8 ()
|
||||
// 2 bytes block align = NumChannels * BitsPerSample/8 (2)
|
||||
// 2 bytes bits per sample (16)
|
||||
//
|
||||
// Subchunk 2
|
||||
// 4 bytes subchunk ID ("data")
|
||||
// 4 bytes subchunk size (2*waveform.Length)
|
||||
// copy of waveform
|
||||
private void SetupStream(
|
||||
Int16[] waveform,
|
||||
UInt32 sampleRate,
|
||||
UInt32 samplesPerCycle
|
||||
)
|
||||
{
|
||||
List<Int16[]> waveforms = new List<Int16[]>();
|
||||
waveforms.Add(waveform);
|
||||
SetupStream(waveforms, sampleRate, samplesPerCycle);
|
||||
}
|
||||
|
||||
private void SetupStream(
|
||||
IEnumerable<Int16[]> waveforms,
|
||||
UInt32 sampleRate,
|
||||
UInt32 samplesPerCycle
|
||||
)
|
||||
{
|
||||
// Create stream
|
||||
_stream = new System.IO.MemoryStream();
|
||||
|
||||
UInt32 totalWaveformLength = GetSize(waveforms);
|
||||
|
||||
_stream.Write(StringToBytes("RIFF"), 0, 4);
|
||||
_stream.Write(LittleEndian(36 + 2* totalWaveformLength, 4), 0, 4);
|
||||
_stream.Write(StringToBytes("WAVE"), 0, 4);
|
||||
|
||||
_stream.Write(StringToBytes("fmt "), 0, 4);
|
||||
_stream.Write(LittleEndian(16, 4), 0, 4); // chunk1Size
|
||||
_stream.Write(LittleEndian(1, 2), 0, 2); // uncompressed linear
|
||||
_stream.Write(LittleEndian(1, 2), 0, 2); // 1 channel
|
||||
_stream.Write(LittleEndian(sampleRate, 4), 0, 4); // sampleRate
|
||||
_stream.Write(LittleEndian(sampleRate * 2, 4), 0, 4); // sampleRate * channels * bitrate / 8
|
||||
_stream.Write(LittleEndian(2, 2), 0, 2); // block align
|
||||
_stream.Write(LittleEndian(16, 2), 0, 2); // bits per sample
|
||||
|
||||
// Fill in the tone
|
||||
_stream.Write(StringToBytes("data"), 0, 4);
|
||||
_stream.Write(LittleEndian(totalWaveformLength * 2, 4), 0, 4);
|
||||
foreach (Int16[] waveform in waveforms)
|
||||
{
|
||||
foreach (Int16 sample in waveform)
|
||||
{
|
||||
_stream.Write(LittleEndian(sample, 2), 0, 2);
|
||||
}
|
||||
}
|
||||
_stream.Seek(0, System.IO.SeekOrigin.Begin);
|
||||
}
|
||||
|
||||
private UInt32 GetSize(IEnumerable<Int16[]> partials)
|
||||
{
|
||||
UInt32 count = 0;
|
||||
foreach (Int16[] waveform in partials)
|
||||
{
|
||||
count += (UInt32)waveform.Length;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private void FillWaveForms(IEnumerable<UInt16[]> partials)
|
||||
{
|
||||
foreach (UInt16[] waveform in partials)
|
||||
{
|
||||
FillWaveForm(waveform);
|
||||
}
|
||||
}
|
||||
|
||||
private void FillWaveForm(UInt16[] waveform)
|
||||
{
|
||||
foreach (Int16 sample in waveform)
|
||||
{
|
||||
_stream.Write(LittleEndian(sample, 2), 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] StringToBytes(String str)
|
||||
{
|
||||
return System.Text.UTF7Encoding.ASCII.GetBytes(str);
|
||||
}
|
||||
|
||||
private byte[] LittleEndian(UInt32 n, int byteCount)
|
||||
{
|
||||
byte[] bytes = new byte[byteCount];
|
||||
int i = 0;
|
||||
while (i < byteCount)
|
||||
{
|
||||
bytes[i] = (byte)n;
|
||||
i++;
|
||||
n = n >> 8;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private byte[] LittleEndian(Int32 n, int byteCount)
|
||||
{
|
||||
return LittleEndian((UInt32)n, byteCount);
|
||||
}
|
||||
|
||||
private String _text;
|
||||
private System.IO.MemoryStream _stream;
|
||||
}
|
||||
}
|
||||
147
MorseTrainer/WordToToneBuilder.cs
Normal file
147
MorseTrainer/WordToToneBuilder.cs
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MorseTrainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The WordToToneBuilder class builds a tone in a worker
|
||||
/// thread and makes it available.
|
||||
/// </summary>
|
||||
public class WordToToneBuilder
|
||||
{
|
||||
public WordToToneBuilder(ToneGenerator toneGenerator)
|
||||
{
|
||||
_toneGenerator = toneGenerator;
|
||||
}
|
||||
|
||||
public IAsyncResult StartBuildAsync(String word, AsyncCallback callback)
|
||||
{
|
||||
BuildWaverformAsync asyncResult = new BuildWaverformAsync(word, callback);
|
||||
WaitCallback cb = new WaitCallback(MakeWaveform);
|
||||
if (System.Threading.ThreadPool.QueueUserWorkItem(cb, asyncResult))
|
||||
{
|
||||
}
|
||||
return asyncResult;
|
||||
}
|
||||
|
||||
private void MakeWaveform(object state)
|
||||
{
|
||||
BuildWaverformAsync buildInfo = (BuildWaverformAsync)state;
|
||||
List<Int16[]> soundsList = new List<short[]>();
|
||||
_toneGenerator.Update();
|
||||
foreach (Char c in buildInfo.Word)
|
||||
{
|
||||
String morse = MorseInfo.ToMorse(c);
|
||||
bool first = true;
|
||||
foreach (Char d in morse)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
soundsList.Add(_toneGenerator.DotSpaceWaveform);
|
||||
}
|
||||
switch (d)
|
||||
{
|
||||
case '.':
|
||||
soundsList.Add(_toneGenerator.DotToneWaveform);
|
||||
break;
|
||||
case '-':
|
||||
soundsList.Add(_toneGenerator.DashToneWaveform);
|
||||
break;
|
||||
case ' ':
|
||||
soundsList.Add(_toneGenerator.WordSpaceWaveform);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
soundsList.Add(_toneGenerator.LetterSpaceWaveform);
|
||||
|
||||
// Farnsworth timing
|
||||
if (_toneGenerator.FarnsworthWPM < _toneGenerator.WPM)
|
||||
{
|
||||
soundsList.Add(_toneGenerator.FarnsworthSpacingWaveform(c));
|
||||
}
|
||||
}
|
||||
soundsList.Add(_toneGenerator.WordSpaceWaveform);
|
||||
WaveStream stream = new WaveStream(buildInfo.Word, soundsList, ToneGenerator.SAMPLES_PER_SECOND, _toneGenerator.SamplesPerCycle);
|
||||
buildInfo.SetWaveform(stream);
|
||||
buildInfo.Callback();
|
||||
}
|
||||
|
||||
private ToneGenerator _toneGenerator;
|
||||
}
|
||||
|
||||
public class BuildWaverformAsync : IAsyncResult
|
||||
{
|
||||
public BuildWaverformAsync(String word, AsyncCallback callback)
|
||||
{
|
||||
_word = word;
|
||||
_callback = callback;
|
||||
_waveStream = null;
|
||||
}
|
||||
|
||||
public void SetWaveform(WaveStream wavestream)
|
||||
{
|
||||
_waveStream = wavestream;
|
||||
}
|
||||
|
||||
public object AsyncState
|
||||
{
|
||||
get
|
||||
{
|
||||
return _waveStream;
|
||||
}
|
||||
}
|
||||
|
||||
public WaitHandle AsyncWaitHandle
|
||||
{
|
||||
get
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CompletedSynchronously
|
||||
{
|
||||
get
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
return _waveStream != null;
|
||||
}
|
||||
}
|
||||
|
||||
public String Word
|
||||
{
|
||||
get
|
||||
{
|
||||
return _word;
|
||||
}
|
||||
}
|
||||
|
||||
public void Callback()
|
||||
{
|
||||
if (_callback != null)
|
||||
{
|
||||
_callback(this);
|
||||
}
|
||||
}
|
||||
|
||||
private String _word;
|
||||
private AsyncCallback _callback;
|
||||
private WaveStream _waveStream;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue