Переглянути джерело

Added some classes and the gitignore file

Oliver 9 роки тому
батько
коміт
250464a731

+ 52 - 0
.gitignore

@@ -0,0 +1,52 @@
+# Distribution
+dist
+
+# Build
+build
+
+# Logs
+logs
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directory
+# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
+node_modules
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
+
+# TypeScript compiler cache directory
+.tscache
+
+# TypeScript typings
+typings
+*.tmp.*
+.baseDir.ts
+.idea/
+.vscode/
+.idea/encodings.xml
+*.md

+ 346 - 0
src/Common/DataObjects/fraction.ts

@@ -0,0 +1,346 @@
+// TODO: implement operators!
+"use strict";
+export class Fraction /*implements IComparable, IComparer<Fraction> */{
+   constructor(numerator: number = 0, denominator: number = 1, simplify: boolean = true) {
+       this.numerator = numerator;
+       this.denominator = denominator;
+
+       if (simplify) {
+            this.simplify();
+       }
+       this.setRealValue();
+   }
+
+    private static maximumAllowedNumber: number = 46340;
+    private numerator: number = 0;
+    private denominator: number = 1;
+    private realValue: number;
+
+    public static CreateFractionFromFraction(fraction: Fraction): Fraction {
+        return new Fraction(fraction.numerator, fraction.denominator);
+    }
+
+    public static plus (f1: Fraction , f2: Fraction): Fraction {
+        let sum: Fraction = Fraction.CreateFractionFromFraction(f1);
+        sum.Add(f2);
+        return sum;
+    }
+
+    public static minus (f1: Fraction , f2: Fraction): Fraction {
+        let sum: Fraction = Fraction.CreateFractionFromFraction(f1);
+        sum.Sub(f2);
+        return sum;
+    }
+
+    private static greatestCommonDenominator(a: number, b: number): number {
+        if (a === 0) {
+            return b;
+        }
+
+        if (b === 1) {
+            return 1;
+        }
+
+        while (b !== 0) {
+            if (a > b) {
+                a -= b;
+            } else {
+                b -= a;
+            }
+        }
+
+        return a;
+    }
+
+    public get Numerator(): number {
+        return this.numerator;
+    }
+
+    public set Numerator(value: number) {
+        if (this.numerator !== value) {
+            this.numerator = value;
+            this.simplify();
+            this.setRealValue();
+        }
+    }
+
+    public get Denominator(): number {
+        return this.denominator;
+    }
+
+    public set Denominator(value: number) {
+        if (this.denominator !== value) {
+            this.denominator = value;
+            if (this.numerator !== 0) {
+                this.simplify();
+            }
+            this.setRealValue();
+        }
+    }
+
+    public get RealValue(): number {
+        return this.realValue;
+    }
+
+    public multiplyWithFactor(factor: number): void {
+        this.numerator *= factor;
+        this.denominator *= factor;
+    }
+
+    public multiplyDenominatorWithFactor(factor: number): void {
+        this.denominator *= factor;
+        this.setRealValue();
+    }
+
+    public Add(fraction: Fraction): void {
+        this.numerator = this.numerator * fraction.denominator + fraction.numerator * this.denominator;
+        this.denominator = this.denominator * fraction.denominator;
+        this.simplify();
+        this.setRealValue();
+
+    }
+
+    public Sub(fraction: Fraction): void {
+        this.numerator = this.numerator * fraction.denominator - fraction.numerator * this.denominator;
+        this.denominator = this.denominator * fraction.denominator;
+        this.simplify();
+        this.setRealValue();
+    }
+
+    public Quantize(maxAllowedDenominator: number): Fraction {
+        if (this.denominator <= maxAllowedDenominator) {
+            return this;
+        }
+
+        let upTestFraction: Fraction = new Fraction(this.numerator + 1, this.denominator);
+
+        while (upTestFraction.Denominator > maxAllowedDenominator) {
+            upTestFraction.Numerator++;
+        }
+
+        if (this.numerator > this.denominator) {
+            let downTestFraction: Fraction = new Fraction(this.numerator - 1, this.denominator);
+
+            while (downTestFraction.Denominator > maxAllowedDenominator) {
+                downTestFraction.Numerator--;
+            }
+
+            if (downTestFraction.Denominator < upTestFraction.Denominator) {
+                return downTestFraction;
+            }
+        }
+        return upTestFraction;
+    }
+
+    //public Equals(obj: Object): boolean {
+    //    if (ReferenceEquals(obj, null)) {
+    //        return false;
+    //    }
+    //    return this.Equals(__as__<Fraction>(obj, Fraction));
+    //}
+
+    //public Equals(f: Fraction): boolean {
+    //    if (ReferenceEquals(this, f))
+    //        return true;
+    //    if (ReferenceEquals(f, null))
+    //        return false;
+    //    return <number>this.numerator * f.denominator === <number>f.numerator * this.denominator;
+    //}
+
+    public GetInversion(): Fraction {
+        return new Fraction(this.Denominator, this.Numerator);
+    }
+
+    private setRealValue(): void {
+        this.realValue = this.numerator / this.denominator;
+    }
+
+    private simplify(): void {
+        if (this.numerator === 0) {
+            this.denominator = 1;
+            return;
+        }
+
+        let i: number = Fraction.greatestCommonDenominator(Math.abs(this.numerator), Math.abs(this.denominator));
+
+        this.numerator /= i;
+        this.denominator /= i;
+
+        if (this.denominator > Fraction.maximumAllowedNumber) {
+            let factor: number = this.denominator / Fraction.maximumAllowedNumber;
+            this.numerator = Math.round(this.numerator / factor);
+            this.denominator = Math.round(this.denominator / factor);
+        }
+        if (this.numerator > Fraction.maximumAllowedNumber) {
+            let factor: number = this.numerator / Fraction.maximumAllowedNumber;
+            this.numerator = Math.round(this.numerator / factor);
+            this.denominator = Math.round(this.denominator / factor);
+        }
+    }
+
+
+
+    //private static equals(f1: Fraction, f2: Fraction): boolean {
+    //    return <number>f1.numerator * f2.denominator === <number>f2.numerator * f1.denominator;
+    //}
+    //
+    //public static ApproximateFractionFromValue(value: number, epsilonForPrecision: number): Fraction {
+    //    let n: number = 1;
+    //    let d: number = 1;
+    //    let fraction: number = n / d;
+    //    while (Math.abs(fraction - value) > epsilonForPrecision) {
+    //        if (fraction < value) {
+    //            n++;
+    //        }
+    //        else {
+    //            d++;
+    //            n = <number>Math.round(value * d);
+    //        }
+    //        fraction = n / <number>d;
+    //    }
+    //    return new Fraction(n, d);
+    //}
+    //public static GetEarlierTimestamp(m1: Fraction, m2: Fraction): Fraction {
+    //    if (m1 < m2)
+    //        return m1;
+    //    else return m2;
+    //}
+    //public CompareTo(obj: Object): number {
+    //    if (this > <Fraction>obj)
+    //        return 1;
+    //    else if (this <<Fraction>obj)
+    //        return -1;
+    //    return 0;
+    //}
+    //public static getFraction(value: number, denominatorPrecision: number): Fraction {
+    //    let numerator: number = <number>Math.round(value / (1.0 / denominatorPrecision));
+    //    return new Fraction(numerator, denominatorPrecision);
+    //}
+    //public static fractionMin(f1: Fraction, f2: Fraction): Fraction {
+    //    if (f1 < f2)
+    //        return f1;
+    //    else return f2;
+    //}
+    //public static fractionMax(f1: Fraction, f2: Fraction): Fraction {
+    //    if (f1 > f2)
+    //        return f1;
+    //    else return f2;
+    //}
+    //public static GetMaxValue(): Fraction {
+    //    return new Fraction(Fraction.maximumAllowedNumber, 1);
+    //}
+    //public static get MaxAllowedNumerator(): number {
+    //    return Fraction.maximumAllowedNumber;
+    //}
+    //public static get MaxAllowedDenominator(): number {
+    //    return Fraction.maximumAllowedNumber;
+    //}
+    //public ToString(): string {
+    //    return this.numerator.ToString() + "/" + this.denominator.ToString();
+    //}
+    //public ToFloatingString(): string {
+    //    return this.RealValue.ToString();
+    //}
+    //public Compare(x: Fraction, y: Fraction): number {
+    //    if (x > y)
+    //        return 1;
+    //    if (x < y)
+    //        return -1;
+    //    return 0;
+    //}
+
+    //#region operators
+    //
+    //    // operator overloads must always come in pairs
+    //    // operator overload +
+    //    public static Fraction operator + (Fraction f1, Fraction f2)
+    //{
+    //    Fraction sum = new Fraction(f1);
+    //    sum.Add(f2);
+    //    return sum;
+    //}
+    //
+    //// operator overload -
+    //public static Fraction operator - (Fraction f1, Fraction f2)
+    //{
+    //    Fraction diff = new Fraction(f1);
+    //    diff.Sub(f2);
+    //    return diff;
+    //}
+    //
+    //// operator overloads must always come in pairs
+    //// operator overload >
+    //public static bool operator > (Fraction f1, Fraction f2)
+    //{
+    //    //return (long) f1.Numerator*f2._denominator > (long) f2._numerator*f1._denominator;
+    //    return f1.RealValue > f2.RealValue;
+    //}
+    //
+    //// operator overload <
+    //public static bool operator < (Fraction f1, Fraction f2)
+    //{
+    //    //return (long) f1._numerator*f2._denominator < (long) f2._numerator*f1._denominator;
+    //    return f1.RealValue < f2.RealValue;
+    //}
+    //
+    //// operator overload ==
+    //public static bool operator == (Fraction f1, Fraction f2)
+    //{
+    //    // code enhanced for performance
+    //    // System.Object.ReferenceEquals(f1, null) is better than if (f1 == null)
+    //    // and comparisons between booleans are quick
+    //    bool f1IsNull = System.Object.ReferenceEquals(f1, null);
+    //    bool f2IsNull = System.Object.ReferenceEquals(f2, null);
+    //
+    //    // method returns true when both are null, false when only the first is null, otherwise the result of equals
+    //    if (f1IsNull != f2IsNull)
+    //        return false;
+    //
+    //    if (f1IsNull /*&& f2IsNull*/)
+    //        return true;
+    //
+    //    return equals(f1, f2);
+    //}
+    //
+    //// operator overload !=
+    //public static bool operator != (Fraction f1, Fraction f2)
+    //{
+    //    return (!(f1 == f2));
+    //}
+    //
+    //// operator overload >=
+    //public static bool operator >= (Fraction f1, Fraction f2)
+    //{
+    //    return (!(f1 < f2));
+    //}
+    //
+    //// operator overload <=
+    //public static bool operator <= (Fraction f1,Fraction f2)
+    //{
+    //    return (!(f1 > f2));
+    //}
+    //
+    //public static Fraction operator / (Fraction f, int i)
+    //{
+    //    return new Fraction(f._numerator, f._denominator *= i);
+    //}
+    //
+    //public static Fraction operator / (Fraction f1, Fraction f2)
+    //{
+    //    var res = new Fraction(f1.Numerator*f2.Denominator, f1.Denominator*f2.Numerator);
+    //    return res.Denominator == 0 ? new Fraction(0, 1) : res;
+    //}
+    //
+    //public static Fraction operator * (Fraction f1, Fraction f2)
+    //{
+    //    return new Fraction(f1.Numerator*f2.Numerator, f1.Denominator*f2.Denominator);
+    //}
+    //
+    //public static Fraction operator % (Fraction f1, Fraction f2)
+    //{
+    //    var a = f1/f2;
+    //    return new Fraction(a.Numerator%a.Denominator, a.Denominator)*f2;
+    //}
+    //
+    //#endregion operators
+}

+ 267 - 0
src/Common/DataObjects/pitch.ts

@@ -0,0 +1,267 @@
+import {PSMath} from "../..//Util/psMath";
+import {CollectionUtil} from "../../Util/collectionUtil";
+
+export enum NoteEnum {
+    C = 0,
+    D = 2,
+    E = 4,
+    F = 5,
+    G = 7,
+    A = 9,
+    B = 11
+}
+
+export enum AccidentalEnum {
+    DOUBLEFLAT = -2,
+    FLAT = -1,
+    NONE = 0,
+    SHARP = 1,
+    DOUBLESHARP = 2
+}
+
+export class Pitch {
+
+    constructor(fundamentalNote: NoteEnum, octave: number, accidental: AccidentalEnum) {
+        this.fundamentalNote = fundamentalNote;
+        this.octave = octave;
+        this.accidental = accidental;
+        this.halfTone = <number>(fundamentalNote) + (octave + Pitch.octXmlDiff) * 12 + <number>accidental;
+        this.frequency = Pitch.calcFrequency(this);
+    }
+
+    public static pitchEnumValues: NoteEnum[] = [NoteEnum.C, NoteEnum.D, NoteEnum.E, NoteEnum.F, NoteEnum.G, NoteEnum.A, NoteEnum.B];
+
+    private static halftoneFactor: number = 12 / PSMath.log10(2);
+    private static octXmlDiff: number = 3;  // Pitch.octXmlDiff
+
+    // private _sourceOctave: number;
+    // private _sourceFundamentalNote: NoteEnum;
+    // private _sourceAccidental: AccidentalEnum = AccidentalEnum.NONE;
+    private octave: number;
+    private fundamentalNote: NoteEnum;
+    private accidental: AccidentalEnum = AccidentalEnum.NONE;
+    private frequency: number;
+    private halfTone: number;
+
+    /**
+     * @param the input pitch
+     * @param the number of halftones to transpose with
+     * @returns ret[0] = the transposed fundamental.
+     *          ret[1] = the octave shift (not the new octave!)
+     * @constructor
+     */
+    public static CalculateTransposedHalfTone(pitch: Pitch, transpose: number): number[] {
+        let newHalfTone: number = <number>pitch.fundamentalNote + <number>pitch.accidental + transpose;
+        return Pitch.WrapAroundCheck(newHalfTone, 12);
+    }
+
+    public static WrapAroundCheck(value: number, limit: number): number[] {
+        let overflow: number = 0;
+
+        while (value < 0) {
+            value += limit;
+            overflow--; // the octave change
+        }
+        while (value >= limit) {
+            value -= limit;
+            overflow++; // the octave change
+        }
+        return [value, overflow];
+    }
+
+    public static calcFrequency(pitch: Pitch): number;
+
+    public static calcFrequency(fractionalKey: number): number;
+
+    public static calcFrequency(pitch: any): number {
+        if (pitch instanceof Pitch) {
+            let octaveSteps: number = pitch.octave - 1;
+            let halftoneSteps: number = <number>pitch.fundamentalNote - <number>NoteEnum.A + <number>pitch.accidental;
+            let frequency: number = <number>(440.0 * Math.pow(2, octaveSteps) * Math.pow(2, halftoneSteps / 12.0));
+            return frequency;
+        } else if (typeof pitch === "number") {
+            let fractionalKey: number = pitch;
+            let frequency: number = <number>(440.0 * Math.pow(2, (fractionalKey - 57.0) / 12));
+            return frequency;
+        }
+    }
+
+    public static calcFractionalKey(frequency: number): number {
+        let halftoneFrequency: number = <number>((PSMath.log10(frequency / 440.0) * Pitch.halftoneFactor) + 57.0);
+        return halftoneFrequency;
+    }
+
+    public static getPitchFromFrequency(frequency: number): Pitch {
+        let key: number = Pitch.calcFractionalKey(frequency) + 0.5;
+        let octave: number = <number>Math.floor(key / 12) - Pitch.octXmlDiff;
+        let halftone: number = Math.floor(<number>(key)) % 12;
+        let fundamentalNote: NoteEnum = <NoteEnum>halftone;
+        let accidental: AccidentalEnum = AccidentalEnum.NONE;
+        if (!CollectionUtil.contains(this.pitchEnumValues, fundamentalNote)) {
+            fundamentalNote = <NoteEnum>(halftone - 1);
+            accidental = AccidentalEnum.SHARP;
+        }
+        return new Pitch(fundamentalNote, <number>octave, accidental);
+    }
+
+    public static getPitchFromHalftone(halftone: number): Pitch {
+        let octave: number = <number>Math.floor(<number>halftone / 12) - Pitch.octXmlDiff;
+        let halftoneInOctave: number = halftone % 12;
+        let fundamentalNote: NoteEnum = <NoteEnum>halftoneInOctave;
+        let accidental: AccidentalEnum = AccidentalEnum.NONE;
+        if (!CollectionUtil.contains(this.pitchEnumValues, fundamentalNote)) {
+            fundamentalNote = <NoteEnum>(halftoneInOctave - 1);
+            accidental = AccidentalEnum.SHARP;
+        }
+        return new Pitch(fundamentalNote, <number>octave, accidental);
+    }
+
+    public static ceiling(halftone: number): NoteEnum {
+        halftone = <number>(halftone) % 12;
+        let fundamentalNote: NoteEnum = <NoteEnum>halftone;
+        if (!CollectionUtil.contains(this.pitchEnumValues, fundamentalNote)) {
+            fundamentalNote = <NoteEnum>(halftone + 1);
+        }
+        return fundamentalNote;
+    }
+
+    public static floor(halftone: number): NoteEnum {
+        halftone = <number>(halftone) % 12;
+        let fundamentalNote: NoteEnum = <NoteEnum>halftone;
+        if (!CollectionUtil.contains(this.pitchEnumValues, fundamentalNote)) {
+            fundamentalNote = <NoteEnum>(halftone - 1);
+        }
+        return fundamentalNote;
+    }
+
+    public get Octave(): number {
+        return this.octave;
+    }
+
+    public get FundamentalNote(): NoteEnum {
+        return this.fundamentalNote;
+    }
+
+    public get Accidental(): AccidentalEnum {
+        return this.accidental;
+    }
+
+    public get Frequency(): number {
+        return this.frequency;
+    }
+
+    public static get OctaveXmlDifference(): number {
+        return Pitch.octXmlDiff;
+    }
+
+    public getHalfTone(): number {
+        return this.halfTone;
+    }
+
+    public getTransposedPitch(factor: number): Pitch {
+        if (factor > 12) {
+            throw new Error("rewrite this method to handle bigger octave changes or don't use is with bigger octave changes!");
+        }
+        if (factor > 0) {
+            return this.getHigherPitchByTransposeFactor(factor);
+        }
+        if (factor < 0) {
+            return this.getLowerPitchByTransposeFactor(-factor);
+        }
+        return this;
+    }
+
+    public DoEnharmonicChange(): void {
+        switch (this.accidental) {
+            case AccidentalEnum.FLAT:
+            case AccidentalEnum.DOUBLEFLAT:
+                this.fundamentalNote = this.getPreviousFundamentalNote(this.fundamentalNote);
+                this.accidental = <AccidentalEnum>(this.halfTone - (<number>(this.fundamentalNote) +
+                (this.octave + Pitch.octXmlDiff) * 12));
+                break;
+            case AccidentalEnum.SHARP:
+            case AccidentalEnum.DOUBLESHARP:
+                this.fundamentalNote = this.getNextFundamentalNote(this.fundamentalNote);
+                this.accidental = <AccidentalEnum>(this.halfTone - (<number>(this.fundamentalNote) +
+                (this.octave + Pitch.octXmlDiff) * 12));
+                break;
+            default:
+                return;
+        }
+    }
+
+    public ToString(): string {
+        return "Note: " + this.fundamentalNote + ", octave: " + this.octave.toString() + ", alter: " +
+            this.accidental;
+    }
+
+    public OperatorEquals(p2: Pitch): boolean {
+        let p1: Pitch = this;
+        // if (ReferenceEquals(p1, p2)) {
+        //     return true;
+        // }
+        if ((<Object>p1 === undefined) || (<Object>p2 === undefined)) {
+            return false;
+        }
+        return (p1.FundamentalNote === p2.FundamentalNote && p1.Octave === p2.Octave && p1.Accidental === p2.Accidental);
+    }
+
+    public OperatorNotEqual(p2: Pitch): boolean {
+        let p1: Pitch = this;
+        return !(p1 === p2);
+    }
+
+    private getHigherPitchByTransposeFactor(factor: number): Pitch {
+        let noteEnumIndex: number = Pitch.pitchEnumValues.indexOf(this.fundamentalNote);
+        let newOctave: number = this.octave;
+        let newNoteEnum: NoteEnum;
+        if (noteEnumIndex + factor > Pitch.pitchEnumValues.length - 1) {
+            newNoteEnum = Pitch.pitchEnumValues[noteEnumIndex + factor - Pitch.pitchEnumValues.length];
+            newOctave++;
+        } else {
+            newNoteEnum = Pitch.pitchEnumValues[noteEnumIndex + factor];
+        }
+        return new Pitch(newNoteEnum, newOctave, AccidentalEnum.NONE);
+    }
+
+    private getLowerPitchByTransposeFactor(factor: number): Pitch {
+        let noteEnumIndex: number = Pitch.pitchEnumValues.indexOf(this.fundamentalNote);
+        let newOctave: number = this.octave;
+        let newNoteEnum: NoteEnum;
+        if (noteEnumIndex - factor < 0) {
+            newNoteEnum = Pitch.pitchEnumValues[Pitch.pitchEnumValues.length + noteEnumIndex - factor];
+            newOctave--;
+        } else {
+            newNoteEnum = Pitch.pitchEnumValues[noteEnumIndex - factor];
+        }
+        return new Pitch(newNoteEnum, newOctave, AccidentalEnum.NONE);
+    }
+
+    private getNextFundamentalNote(fundamental: NoteEnum): NoteEnum {
+        let i: number = 0;
+        for (; i < Pitch.pitchEnumValues.length; i++) {
+            let note: NoteEnum = Pitch.pitchEnumValues[i];
+            if (note === fundamental) {
+                break;
+            }
+        }
+        i = ++i % Pitch.pitchEnumValues.length;
+        return Pitch.pitchEnumValues[i];
+    }
+
+    private getPreviousFundamentalNote(fundamental: NoteEnum): NoteEnum {
+        let i: number = 0;
+        for (; i < Pitch.pitchEnumValues.length; i++) {
+            let note: NoteEnum = Pitch.pitchEnumValues[i];
+            if (note === fundamental) {
+                break;
+            }
+        }
+        i--;
+        if (i < 0) {
+            i += Pitch.pitchEnumValues.length;
+        }
+        return Pitch.pitchEnumValues[i];
+    }
+}
+

+ 12 - 0
src/Util/collectionUtil.ts

@@ -0,0 +1,12 @@
+export class CollectionUtil {
+
+    public static contains(array: any[], object: any): boolean {
+        for (let i: number = 0; i < array.length; i++) {
+            if (array[i] === object) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+}

+ 35 - 0
src/Util/psMath.ts

@@ -0,0 +1,35 @@
+export class PSMath {
+
+    public static log(base: number, x: number): number {
+        return Math.log(x) / Math.log(base);
+    }
+
+    public static log10(x: number): number {
+        return PSMath.log(10, x);
+    }
+
+    public static meanSimple(values: number[]): number {
+        let sum: number = 0;
+        for (let i: number = 0; i < values.length; i++) {
+            sum += values[i];
+        }
+        return sum / values.length;
+    }
+
+    public static meanWeighted(values: number[], weights: number[]): number {
+        if (values.length !== weights.length || values.length === 0) {
+            return 0;
+        }
+        let sumWeigtedValues: number = 0;
+        let sumWeights: number = 0;
+        for (let i: number = 0; i < values.length; i++) {
+            let weight: number = weights[i];
+            sumWeigtedValues += values[i] * weight;
+            sumWeights += weight;
+        }
+        return <number>(sumWeigtedValues / sumWeights);
+    }
+}
+
+
+

+ 121 - 0
test/Common/DataObjects/Pitch_Test.ts

@@ -0,0 +1,121 @@
+import { Pitch, NoteEnum, AccidentalEnum } from "../../../src/Common/DataObjects/pitch";
+
+describe("Pitch Unit Tests:", () => {
+    describe("transpose Pitch", () => {
+        let pitch: Pitch = new Pitch(NoteEnum.A, 1, AccidentalEnum.NONE);
+        let transposedFundamentalAndOctave: number[] = Pitch.CalculateTransposedHalfTone(pitch, 12);
+        let higherTransposedFundamentalAndOctave: number[] = Pitch.CalculateTransposedHalfTone(pitch, 26);
+
+        it("should be 1 octave higher and same fundamental", (done: MochaDone) => {
+            chai.expect(transposedFundamentalAndOctave[1]).to.equal(1);
+            chai.expect(transposedFundamentalAndOctave[0]).to.equal(pitch.FundamentalNote);
+            chai.expect(higherTransposedFundamentalAndOctave[1]).to.equal(2);
+            chai.expect(higherTransposedFundamentalAndOctave[0]).to.equal(pitch.FundamentalNote + 2);
+            done();
+        });
+    });
+
+    describe("calculate Frequency from Pitch", () => {
+        let pitch1: Pitch = new Pitch(NoteEnum.A, 1, AccidentalEnum.NONE);
+        let pitch2: Pitch = new Pitch(NoteEnum.B, 1, AccidentalEnum.DOUBLEFLAT);
+        let pitch3: Pitch = new Pitch(NoteEnum.G, 1, AccidentalEnum.DOUBLESHARP);
+
+        let frequency1: number = Pitch.calcFrequency(Pitch.calcFractionalKey(pitch1.Frequency));
+        let frequency2: number = Pitch.calcFrequency(Pitch.calcFractionalKey(pitch2.Frequency));
+        let frequency3: number = Pitch.calcFrequency(Pitch.calcFractionalKey(pitch3.Frequency));
+
+        it("should be 440Hz", (done: MochaDone) => {
+            chai.expect(pitch1.Frequency).to.equal(440);
+            chai.expect(pitch2.Frequency).to.equal(440);
+            chai.expect(pitch3.Frequency).to.equal(440);
+            chai.expect(frequency1).to.equal(440);
+            chai.expect(frequency2).to.equal(440);
+            chai.expect(frequency3).to.equal(440);
+            done();
+        });
+    });
+
+    describe("calculate fractional key", () => {
+        // the values are validated against the C# output. TODO: ask mauz about the shift
+        let pitch1: Pitch = new Pitch(NoteEnum.C, 6, AccidentalEnum.SHARP);   // C#6 -> 109
+        let pitch2: Pitch = new Pitch(NoteEnum.B, 1, AccidentalEnum.NONE);    // B1 -> 59
+        let pitch3: Pitch = new Pitch(NoteEnum.F, 4, AccidentalEnum.DOUBLEFLAT);  // Fbb4 -> 87
+        let pitch4: Pitch = new Pitch(NoteEnum.E, -1, AccidentalEnum.DOUBLESHARP);    // E##-1 -> 30
+        let pitch5: Pitch = new Pitch(NoteEnum.A, 1, AccidentalEnum.NONE);    // A1 -> 57
+
+        let key1: number = Pitch.calcFractionalKey(pitch1.Frequency);
+        let key2: number = Pitch.calcFractionalKey(pitch2.Frequency);
+        let key3: number = Pitch.calcFractionalKey(pitch3.Frequency);
+        let key4: number = Pitch.calcFractionalKey(pitch4.Frequency);
+        let key5: number = Pitch.calcFractionalKey(pitch5.Frequency);
+
+        it("pitch key should equal midi key", (done: MochaDone) => {
+            chai.expect(key1).to.equal(109);
+            chai.expect(key2).to.equal(59);
+            chai.expect(key3).to.equal(87);
+            chai.expect(key4).to.equal(30);
+            chai.expect(key5).to.equal(57);
+            done();
+        });
+    });
+
+    describe("calculate Pitch from Frequency", () => {
+        let octave: number = 1;
+        let accidentals: number[] = [AccidentalEnum.DOUBLEFLAT,
+            AccidentalEnum.FLAT,
+            AccidentalEnum.NONE,
+            AccidentalEnum.SHARP,
+            AccidentalEnum.DOUBLESHARP,
+        ];
+
+        let pitch: Pitch;
+        let calcedPitch: Pitch;
+
+        for (let i: number = 0; i < Pitch.pitchEnumValues.length; i++) {
+            for (let j: number = 0; j < accidentals.length; j++) {
+                pitch = new Pitch(Pitch.pitchEnumValues[i], octave, accidentals[j]);
+                calcedPitch = Pitch.getPitchFromFrequency(pitch.Frequency);
+
+                it( "calcedPitch equals original, " +
+                    `note: ${pitch.FundamentalNote}, octave: ${pitch.Octave}, accidental; ${pitch.Accidental}`,
+                    (done: MochaDone) => {
+                        // compare the frequencies here -> only AccidentalEnum None and Sharp will lead to same note, octave and accidental
+                        chai.expect(pitch.Frequency).to.equal(calcedPitch.Frequency);
+                        done();
+                    });
+            }
+        }
+    });
+
+    describe("get Pitch from fractional key", () => {
+        let octave: number = 5;
+        let accidentals: number[] = [AccidentalEnum.DOUBLEFLAT,
+            AccidentalEnum.FLAT,
+            AccidentalEnum.NONE,
+            AccidentalEnum.SHARP,
+            AccidentalEnum.DOUBLESHARP,
+        ];
+
+        let pitch: Pitch;
+        let calcedPitch: Pitch;
+
+        for (let i: number = 0; i < Pitch.pitchEnumValues.length; i++) {
+            for (let j: number = 0; j < accidentals.length; j++) {
+                pitch = new Pitch(Pitch.pitchEnumValues[i], octave, accidentals[j]);
+                let halftone: number = pitch.getHalfTone();
+                calcedPitch = Pitch.getPitchFromHalftone(halftone);
+
+                it( "calcedPitch equals original, " +
+                    `note: ${pitch.FundamentalNote}, octave: ${pitch.Octave}, accidental; ${pitch.Accidental}`,
+                    (done: MochaDone) => {
+                        chai.expect(pitch.getHalfTone()).to.equal(calcedPitch.getHalfTone());
+                        done();
+                    });
+            }
+        }
+    });
+
+    // TODO: test ceiling and floor (needed for the music sheet transpose)
+    // TODO: test getTransposedPitch (or delete it -> seems to be a less powerful implementation of CalculateTransposedHalfTone)
+    // TODO: test DoEnharmonicEnchange (needed for the midi reader)
+});

+ 16 - 0
test/Common/DataObjects/fraction_Test.ts

@@ -0,0 +1,16 @@
+/**
+ * Created by Oliver on 16.03.2016.
+ */
+import { Fraction } from "../../../src/Common/DataObjects/fraction";
+
+describe("Fraction Unit Tests:", () => {
+    describe("Construct Fraction, check Properties", () => {
+        let f1: Fraction = new Fraction(2, 6);
+
+        it("Numerator and Denominator", (done: MochaDone) => {
+            chai.expect(f1.Numerator).to.equal(1);
+            chai.expect(f1.Denominator).to.equal(3);
+            done();
+        });
+    });
+});