Initial commit

This commit is contained in:
jeremiah 2013-05-28 09:53:01 -07:00
parent e1a7e866b7
commit 8e3e24de75
10 changed files with 544 additions and 0 deletions

25
CWLibrary.sln Normal file
View file

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CWLibrary", "CWLibrary\CWLibrary.csproj", "{BB1D2263-20CB-4934-9F2B-5A2D1BDAF165}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3375E244-5EE5-45CE-B449-D3913B5CD78D}"
ProjectSection(SolutionItems) = preProject
README.txt = README.txt
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{BB1D2263-20CB-4934-9F2B-5A2D1BDAF165}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB1D2263-20CB-4934-9F2B-5A2D1BDAF165}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB1D2263-20CB-4934-9F2B-5A2D1BDAF165}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB1D2263-20CB-4934-9F2B-5A2D1BDAF165}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.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>
<MinimumVisualStudioVersion>10.0</MinimumVisualStudioVersion>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{BB1D2263-20CB-4934-9F2B-5A2D1BDAF165}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CWLibrary</RootNamespace>
<AssemblyName>CWLibrary</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Profile4</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<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' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
</ItemGroup>
<ItemGroup>
<Compile Include="Characters.cs" />
<Compile Include="DataChunk.cs" />
<Compile Include="FormatChunk.cs" />
<Compile Include="HeaderChunk.cs" />
<Compile Include="TextToMorse.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="WaveChunk.cs" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.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>

57
CWLibrary/Characters.cs Normal file
View file

@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CWLibrary
{
static class Characters
{
public static Dictionary<string, string> Symbols = new Dictionary<string, string>()
{
{ "a", ".-" },
{ "b", "-..." },
{ "c", "-.-." },
{ "d", "-.." },
{ "e", "." },
{ "f", "..-." },
{ "g", "--." },
{ "h", "...." },
{ "i", ".." },
{ "j", ".---" },
{ "k", "-.-" },
{ "l", ".-.." },
{ "m", "--" },
{ "n", "-." },
{ "o", "---" },
{ "p", ".--." },
{ "q", "--.-" },
{ "r", ".-." },
{ "s", "..." },
{ "t", "-" },
{ "u", "..-" },
{ "v", "...-" },
{ "w", ".--" },
{ "x", "-..-" },
{ "y", "-.--" },
{ "z", "--.." },
{ "0", "-----" },
{ "1", ".----" },
{ "2", "..---" },
{ "3", "...--" },
{ "4", "....-" },
{ "5", "....." },
{ "6", "-...." },
{ "7", "--..." },
{ "8", "---.." },
{ "9", "----." },
{ ".", ".-.-.-" },
{ ",", "--..--" },
{ "?", "..--.." },
{ "/", "-..-." },
{ "<AR>", ".-.-." },
{ "<BK>", "-...-.-" },
{ "<SK>", "...-.-" }
};
}
}

32
CWLibrary/DataChunk.cs Normal file
View file

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CWLibrary
{
class DataChunk : WaveChunk
{
public short[] ChunkData { get; set; }
public DataChunk(short[] data)
{
ChunkId = "data".ToCharArray();
ChunkSize = (uint)(data.Length * 2);
ChunkData = data;
}
public override byte[] ToBytes()
{
List<byte> bytes = new List<byte>();
bytes.AddRange(Encoding.UTF8.GetBytes(ChunkId));
bytes.AddRange(BitConverter.GetBytes(ChunkSize));
foreach (short datum in ChunkData)
bytes.AddRange(BitConverter.GetBytes(datum));
return bytes.ToArray<byte>();
}
}
}

45
CWLibrary/FormatChunk.cs Normal file
View file

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CWLibrary
{
class FormatChunk : WaveChunk
{
public short CompressionCode { get; set; }
public short NumChannels { get; set; }
public uint SampleRate { get; set; }
public uint BytesPerSecond { get; set; }
public short BlockAlign { get; set; }
public short SignificantBits { get; set; }
public FormatChunk()
{
ChunkId = "fmt ".ToCharArray();
ChunkSize = 16;
CompressionCode = 1;
NumChannels = 1;
SampleRate = 44100;
BytesPerSecond = 88200;
BlockAlign = 2;
SignificantBits = 16;
}
public override byte[] ToBytes()
{
List<byte> bytes = new List<byte>();
bytes.AddRange(Encoding.UTF8.GetBytes(ChunkId));
bytes.AddRange(BitConverter.GetBytes(ChunkSize));
bytes.AddRange(BitConverter.GetBytes(CompressionCode));
bytes.AddRange(BitConverter.GetBytes(NumChannels));
bytes.AddRange(BitConverter.GetBytes(SampleRate));
bytes.AddRange(BitConverter.GetBytes(BytesPerSecond));
bytes.AddRange(BitConverter.GetBytes(BlockAlign));
bytes.AddRange(BitConverter.GetBytes(SignificantBits));
return bytes.ToArray<byte>();
}
}
}

36
CWLibrary/HeaderChunk.cs Normal file
View file

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CWLibrary
{
class HeaderChunk : WaveChunk
{
public char[] RiffType { get; set; }
public FormatChunk FormatChunk { get; set; }
public DataChunk DataChunk { get; set; }
public HeaderChunk(FormatChunk formatChunk, DataChunk dataChunk)
{
ChunkId = "RIFF".ToCharArray();
RiffType = "WAVE".ToCharArray();
FormatChunk = formatChunk;
DataChunk = dataChunk;
ChunkSize = 36 + DataChunk.ChunkSize;
}
public override byte[] ToBytes()
{
List<byte> bytes = new List<byte>();
bytes.AddRange(Encoding.UTF8.GetBytes(ChunkId));
bytes.AddRange(BitConverter.GetBytes(ChunkSize));
bytes.AddRange(Encoding.UTF8.GetBytes(RiffType));
bytes.AddRange(FormatChunk.ToBytes());
bytes.AddRange(DataChunk.ToBytes());
return bytes.ToArray<byte>();
}
}
}

View file

@ -0,0 +1,30 @@
using System.Resources;
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("CWLibrary")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CWLibrary")]
[assembly: AssemblyCopyright("Copyright © 2013")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
// 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")]

171
CWLibrary/TextToMorse.cs Normal file
View file

@ -0,0 +1,171 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CWLibrary
{
public class TextToMorse
{
// Character speed in WPM
public int CharacterSpeed { get; set; }
// Overall speed in WPM (must be <= character speed)
public int WordSpeed { get; set; }
// Tone frequency
public double Frequency { get; set; }
public TextToMorse(int charSpeed, int wordSpeed, double frequency)
{
CharacterSpeed = charSpeed;
WordSpeed = wordSpeed;
Frequency = frequency;
}
public TextToMorse(int charSpeed, int wordSpeed) : this(charSpeed, wordSpeed, 600.0) { }
public TextToMorse(int wpm) : this(wpm, wpm) { }
public TextToMorse() : this(20) { }
// Return given number of seconds of sine wave
private short[] GetWave(double seconds)
{
short[] waveArray;
int samples = (int)(44100 * seconds);
waveArray = new short[samples];
for (int i = 0; i < samples; i++)
{
waveArray[i] = Convert.ToInt16(32760 * Math.Sin(i * 2 * Math.PI * Frequency / 44100));
}
return waveArray;
}
// Return given number of seconds of flatline. This could also be
// achieved with slnt chunks inside a wavl chunk, but the resulting
// file might not be universally readable. If saving space is that
// important, it would be better to compress the output as mp3 or ogg
// anyway.
private short[] GetSilence(double seconds)
{
short[] waveArray;
int samples = (int)(44100 * seconds);
waveArray = new short[samples];
return waveArray;
}
// Dot -- 1 unit long
private short[] GetDot()
{
return GetWave(1.2 / CharacterSpeed);
}
// Dash -- 3 units long
private short[] GetDash()
{
return GetWave(3.6 / CharacterSpeed);
}
// Inter-element space -- 1 unit long
private short[] GetInterEltSpace()
{
return GetSilence(1.2 / CharacterSpeed);
}
// Space between letters -- nominally 3 units, but adjusted for
// Farnsworth timing (if word speed is lower than character
// speed) based on ARRL's Morse code timing standard:
// http://www.arrl.org/files/file/Technology/x9004008.pdf
private short[] GetInterCharSpace()
{
double delay = (60.0 / WordSpeed) - (32.0 / CharacterSpeed);
double spaceLength = 3 * delay / 19;
return GetSilence(spaceLength);
}
// Space between words -- nominally 7 units, but adjusted for
// Farnsworth timing in case word speed is lower than character
// speed.
private short[] GetInterWordSpace()
{
double delay = (60.0 / WordSpeed) - (32.0 / CharacterSpeed);
double spaceLength = 7 * delay / 19;
return GetSilence(spaceLength);
}
// Return a single character as a waveform
private short[] GetCharacter(string character)
{
short[] space = GetInterEltSpace();
short[] dot = GetDot();
short[] dash = GetDash();
List<short> morseChar = new List<short>();
string morseSymbol = Characters.Symbols[character];
for (int i = 0; i < morseSymbol.Length; i++)
{
if (i > 0)
morseChar.AddRange(space);
if (morseSymbol[i] == '-')
morseChar.AddRange(dash);
else if (morseSymbol[i] == '.')
morseChar.AddRange(dot);
}
return morseChar.ToArray<short>();
}
// Return a word as a waveform
private short[] GetWord(string word)
{
List<short> data = new List<short>();
for (int i = 0; i < word.Length; i++)
{
if (i > 0)
data.AddRange(GetInterCharSpace());
data.AddRange(GetCharacter(word[i].ToString()));
}
return data.ToArray<short>();
}
// Return a string (lower case text only, unrecognized characters
// throw an exception -- see Characters.cs for the list of recognized
// characters) as a waveform wrapped in a DataChunk, ready to by added
// to a wave file.
private DataChunk GetText(string text)
{
List<short> data = new List<short>();
string[] words = text.Split(' ');
for (int i = 0; i < words.Length; i++)
{
if (i > 0)
data.AddRange(GetInterWordSpace());
data.AddRange(GetWord(words[i]));
}
// Pad the end with a little bit of silence. Otherwise the last
// character may sound funny in some media players.
data.AddRange(GetInterCharSpace());
DataChunk dataChunk = new DataChunk(data.ToArray<short>());
return dataChunk;
}
// Returns a byte array in the Wave file format containing the given
// text in morse code
public byte[] ConvertToMorse(string text)
{
DataChunk data = GetText(text.ToLower());
FormatChunk formatChunk = new FormatChunk();
HeaderChunk headerChunk = new HeaderChunk(formatChunk, data);
return headerChunk.ToBytes();
}
}
}

15
CWLibrary/WaveChunk.cs Normal file
View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace CWLibrary
{
abstract class WaveChunk
{
public char[] ChunkId { get; set; }
public uint ChunkSize { get; set; }
public abstract byte[] ToBytes();
}
}

78
README.txt Normal file
View file

@ -0,0 +1,78 @@
CW Library
Jeremiah Stoddard - AG6HC <ag6hc@arrl.net>
This is my personal library for working with Morse code. Presently it contains
the TextToMorse class which takes a string and returns a byte array in WAVE
file format containing the contents of the string translated into Morse code.
This is somewhat like what ebook2cw does, but not nearly as flexible (only
WAVE output rather than mp3 or ogg, a much more limited number of characters
supported). On the other hand, CWLibrary is written in C# using only the .NET
framework libraries, so if you're working on a .NET project it may be a better
fit for your needs...
Here is an example program that uses this library to write out a file named
"hello.wav" that plays "hello, world" in Morse code:
+-----------------------------------------------------------------------------+
using CWLibrary;
using System.IO;
class Program
{
static void Main(string[] args)
{
TextToMorse converter = new TextToMorse();
string text = "Hello, World";
byte[] outfile = converter.ConvertToMorse(text);
FileStream stream = new FileStream("hello.wav", FileMode.Create,
FileAccess.Write);
stream.Write(outfile, 0, outfile.Length);
stream.Close();
}
}
+-----------------------------------------------------------------------------+
The above program will generate 20 WPM Morse code with a pitch of 600Hz. If
you want 15 WPM, just pass it to the constructor:
TextToMorse converter = new TextToMorse(15);
If you want 5/18 WPM (character speed as if sending at 18 WPM, but spacing
between characters and words added for an overall speed of 5 WPM) you would
call the constructor thus:
TextToMorse converter = new TextToMorse(18, 5);
If you also wanted to raise the pitch to 800 Hz, the following would do so:
TextToMorse converter = new TextToMorse(18, 5, 800.0);
The code is licensed under the MIT license, so you can do pretty much whatever
you want with it.
LICENSE:
CW Library
Copyright © 2013 Jeremiah Stoddard
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.