Last active
August 29, 2015 14:27
-
-
Save gkinsman/f7fb1c3a5ba60246faf2 to your computer and use it in GitHub Desktop.
Solution to Liam's Challenge 22/08/2015
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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