Skip to content

Instantly share code, notes, and snippets.

@gkinsman
Last active August 29, 2015 14:27
Show Gist options
  • Save gkinsman/f7fb1c3a5ba60246faf2 to your computer and use it in GitHub Desktop.
Save gkinsman/f7fb1c3a5ba60246faf2 to your computer and use it in GitHub Desktop.
Solution to Liam's Challenge 22/08/2015
using System;
namespace SemitoneCalculator
{
public static class ToneExtensions
{
public static Tone Up(this Tone tone)
{
return tone.Name == ToneName.B
? new Tone(ToneName.C, tone.Octave + 1)
: new Tone(tone.Name - 1, tone.Octave);
}
public static Tone Down(this Tone tone)
{
return tone.Name == ToneName.C
? new Tone(ToneName.B, tone.Octave - 1)
: new Tone(tone.Name + 1, tone.Octave);
}
public static int GetIntervalTo(this Tone from, Tone to)
{
Func<Tone, Tone> action;
if (from.HigherThan(to)) action = tone => tone.Down();
else action = tone => tone.Up();
var count = 0;
while (!from.Equals(to))
{
count++;
from = action(from);
}
return count;
}
public static bool HigherThan(this Tone from, Tone to)
{
if (from.Octave == to.Octave) return from.Name > to.Name;
return from.Octave > to.Octave;
}
}
public enum ToneName
{
B = 1,
ASharp = 2,
A = 3,
GSharp = 4,
G = 5,
FSharp = 6,
F = 7,
E = 8,
DSharp = 9,
D = 10,
CSharp = 11,
C = 12,
}
public class Tone : IEquatable<Tone>
{
private static readonly double A440 = 440;
public static readonly Tone A4 = new Tone(ToneName.A, 4);
public Tone(ToneName name, int octave)
{
this.Name = name;
this.Octave = octave;
}
public ToneName Name { get; private set; }
public int Octave { get; private set; }
public double Frequency
{
get
{
var distanceToA4 = this.HigherThan(A4)
? this.GetIntervalTo(A4)
: -this.GetIntervalTo(A4);
var freq = A440*Math.Pow(Math.Pow(2,(1/12d)), distanceToA4);
return freq;
}
}
public override string ToString()
{
return Name.ToString() + Octave;
}
public bool Equals(Tone other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Name == other.Name && Octave == other.Octave;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Tone) obj);
}
}
}
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SemitoneCalculator;
namespace SemitoneCalculatorTests
{
[TestClass]
public class ToneDiffTests
{
[TestMethod]
public void Next_BPlusOneShouldBeCPlusOctave()
{
var tone = new Tone(ToneName.B, 1);
var next = tone.Up();
Assert.AreEqual(next.Octave, 2);
Assert.AreEqual(next.Name, ToneName.C);
}
[TestMethod]
public void Previous_CMinusOneShouldBeBMinusOctave()
{
var tone = new Tone(ToneName.C, 2);
var previous = tone.Down();
Assert.AreEqual(previous.Octave, 1);
Assert.AreEqual(previous.Name, ToneName.B);
}
[TestMethod]
public void HigherThan_CShouldBeHigherThanA()
{
var c = new Tone(ToneName.C, 1);
var a = new Tone(ToneName.A, 1);
Assert.IsTrue(c.HigherThan(a));
Assert.IsFalse(a.HigherThan(c));
}
[TestMethod]
public void HigherThan_C2ShouldBeHigherThanB1()
{
var c2 = new Tone(ToneName.C, 2);
var b1 = new Tone(ToneName.B, 1);
Assert.IsTrue(c2.HigherThan(b1));
Assert.IsFalse(b1.HigherThan(c2));
}
[TestMethod]
public void HigherThan_C1ShouldBeLowerThanF2()
{
var c1 = new Tone(ToneName.C, 1);
var f2 = new Tone(ToneName.F, 2);
Assert.IsTrue(f2.HigherThan(c1));
Assert.IsFalse(c1.HigherThan(f2));
}
[TestMethod]
public void GetDistanceTo_C2ToC1ShouldBe12()
{
var from = new Tone(ToneName.C, 2);
var to = new Tone(ToneName.C, 1);
Assert.AreEqual(12, from.GetIntervalTo(to));
}
[TestMethod]
public void GetDistanceTo_FSharp3ToASharp6ShouldBe40()
{
var from = new Tone(ToneName.FSharp, 3);
var to = new Tone(ToneName.ASharp, 6);
Assert.AreEqual(40, from.GetIntervalTo(to));
}
[TestMethod]
public void Frequency_A4FrequencyShouldBe440()
{
var a4 = new Tone(ToneName.A, 4);
Assert.AreEqual(440d, a4.Frequency);
}
[TestMethod]
public void Frequency_CSharp6ShouldBe1108Point73()
{
var csharp6 = new Tone(ToneName.CSharp, 6);
Assert.AreEqual(1108.73, Math.Round(csharp6.Frequency,2));
}
[TestMethod]
public void Frequency_BZeroShouldBe30Point87()
{
var b0 = new Tone(ToneName.B, 0);
Assert.AreEqual(30.87, Math.Round(b0.Frequency, 2));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment