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