Donnerstag, 30. Juli 2015

.Net und C# Alternative zu Matlab: ILNumerics.

Hinweis: Für diesen Post wurde ich von der ILNumerics GmbH bezahlt, konnte und sollte aber den Inhalt selber frei bestimmen.

Im heutigen Post möchte ich die Klassenbibliothek ILNumerics vorstellen und meine Erfahrungen damit teilen.
ILNumerics ist eine Klassenbibliothek für .Net mit dem Ziel numerische Berechnungen, Plots etc. einfacher und schneller in .Net implementieren zu können. Natürlich könnte man dies alles prinzipiell auch mit .Net Mitteln umsetzen, was allerdings sehr umständlich sein kann (man denke allein an die Darstellung von Vektoren, Matrix Multiplikation etc.). Mit ILNumerics steht nun eine Matlab sehr ähnliche Spracherweiterung bereit, mit welcher komplexere Algorithmen effizient implementiert werden können. Im folgenden werde ich nun, wie gesagt, eine kleine Einführung in dieses Thema geben und das kostenpflichtige Produkt testen, welches mir insgesamt sehr gut gefallen hat.

Fangen wir mit der Installation an: Heruntergeladen werden kann die Bibliothek über die Homepage. Erhältlich ist kostenlos nur eine Trial Version, diese Tatsache und der Preis ist der einzige Negativpunkt meiner Meinung nach. Dieser ist relativ hoch, anfangend von 89 Euro pro Monat, also wahrscheinlich für den Freizeitnutzer nicht geeignet. Sehr wohl jedoch für Universitäten, Firmen o.ä., hier kann ich mir ILNumerics als Alternative zu Matlab vorstellen, insbesondere da ich persönlich sehr gerne in C# und .Net programmiere. Eine kostenlose Version für Studenten wäre sehr zu begrüßen.
Nach der Installation kann die Trial Version getestet werden bzw. die Version aktiviert werden.
Weitere Informationen hierzu gibt es auf der Support Seite. Auf dieser gibt es auch ein Tutorial und eine genauere Dokumentation, welche auch mir als Referenz diente.

Die Bibliothek beinhaltet im Wesentlichen zwei Komponenten: Die Computation und die Visualization Engine. Zuerst werde ich die Verwendung der Computation Engine erläutern, dann die der Visualization Engine.

Um die Computation Engine ganz einfach einzubinden, legen wir ein neues Windows Projekt an und klicken dann im Menü auf Projekt - Neues Element hinzufügen. Im sich öffnenden Fenster wählen wir "Computing Module" aus und fügen damit eine neue .cs Datei in Form eines Computing Modules dem Projekt hinzu. In dieser sind bereits einige Beispielmethoden implementiert, die die Verwendung von ILNumerics demonstrieren, welche aber natürlich gelöscht werden können. Die Main - Methode lassen wir aber, da wir das Computing Module als Einstiegspunkt in das Programm verwenden möchten. Dies müssen wir dem Programm mitteilen, da auch die angelegte Form Klasse eine Main - Methode besitzt. Dies tun wir, in dem wir unter Projekteigenschaften - Anwendung Startobjekt auf das Computation Module setzen.
Der Code der Datei Computing Module1.cs sollte so aussehen:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ILNumerics;
using System.IO;

namespace IlNumericsTest
{

    public class Computing_Module1 : ILMath
    {
        public static void Main(params string[] parameter)
        {
        }
    }

}

Fangen wir mit ein paar leichten Operationen zur Eingewöhnung an.
Matrizen werden über die ILNumerics Klasse ILArray verwaltet. Mit diesen lassen sich mit +. -, *, / die erwarteten Rechenoperationen durchführen, nämlich Matrixaddition / -subtraktion und komponentenweise Multiplikation / Division. Der folgende Code erzeugt 3 Matrizen der Größe 10 x 10, die erste wird mit Einsen gefüllt, die zweite mit Nullen und die dritte mit zufälligen Werten zwischen 0 und 1. Dann wird auf der Konsole A + C ausgegeben sowie A * (-1).

ILArray<double> A = ones(10, 10);
ILArray<double> B = zeros(10, 10);
ILArray<double> C = rand(10, 10);
Console.WriteLine(A + C);
Console.WriteLine(-A);

Nun ein paar etwas komplexere Beispiele, ich möchte ein paar Operationen auf Graphen und Netzwerken zeigen. Wer Graphen nicht kennt sei auf Wikipedia verwiesen, diese sind jedenfalls eine oft benutzte Datenstruktur in der Informatik. Das Fachgebiet Web Science befasst sich insbesondere mit der Erforschung und Analyse von Netzwerken (wie dem Internet), dabei werden Graphen zur Repräsentation der Daten verwendet, welche riesige Ausmaße annehmen können. Auf Grund der Themenrelevanz (oft wird dabei z.B. mit Matlab gearbeitet) habe ich mich entschieden, aus diesem Bereich ein paar Beispiele zu zeigen, wie zum Beispiel den PageRank Algorithmus von Google.
Zuerst aber eine Funktion zum Einlesen von Daten:

            var file = File.ReadAllText("Graph.txt");
            ILArray AdjMatrix = csvread(file);

In der ersten Zeile wird die Datei in einen String gelesen, welcher dann mit der ILNumerics Funktion csvread() in eine Matrix gelesen wird. csvread() trennt die eingelesenen Werte (zum Beispiel) nach Komma. Die Eingabedatei soll die kommagetrennte Adjazenzmatrix eines Graphen enthalten, welche eine 1 an Position i, j besitzt falls die Knoten i und j mit einer Kante verbunden sind, ansonsten 0. Für einen Graph mit 4 Knoten könnte die Datei so aussehen:

0, 1, 0, 0
1, 0, 1, 1
0, 1, 0, 1
0, 1, 1, 0

Nun möchten wir aus der Adjazenzmatrix die Laplace Matrix bestimmen, welche den Grad von Knoten i im i-ten Diagonaleintrag enthält und -1 an Position i, j falls die Knoten i, j verbunden sind.
Dafür legen wir eine neue Funktion an. Da ILNumerics sehr auf Performance / Speichermanagement achtet (achten muss wenn es große Daten handhaben will), sind hier ein paar Dinge zu beachten. Zum einen gibt es nicht nur den vorgestellten Typ ILArray, sondern auch noch ILInArray, ILOutArray und ILRetArray (und generell noch andere Typen als Array). Für genaueres sei auf die Dokumentation verwiesen, hier nur kurz, ILInArray wird zum Beispiel für Eingabeparameter verwendet, ist nicht änderbar und wird direkt nach Verlassen des entsprechenden Scopes gelöscht (eben aus Speicher / Performance Gründen). Dafür müssen wir auch jede Funktion mit einem neuen Scope umschließen, welcher die Input Parameter enthält.
Die Funktion zur Berechnung der Laplace Matrix sieht so aus:

public static ILRetArray<double> CalcLaplacian(ILInArray<double> AdjMatrix)
{
    using (ILScope.Enter(AdjMatrix))
    {
        ILArray<double> Degrees = sum(AdjMatrix);
        ILArray<double> LaplacianMatrix = Degrees * eye(AdjMatrix.Length, AdjMatrix.Length);
        LaplacianMatrix = LaplacianMatrix - AdjMatrix;
        return LaplacianMatrix;
    }
}

Der Eingabeparameter ist die Adjazenzmatrix. In der ersten Zeile im Scope wird der Grad der Knoten ausgerechnet, was mit der Funktion sum() getan wird. Diese summiert alle Spalten der Matrix auf und gibt einen Vektor mit dem Ergebnis aus, welcher genau der Gradfolge entspricht. eye() erzeugt eine Matrix mit Einsen auf der Diagonalen, Multiplizieren mit dem Gradvektor und Subtrahieren von der Adjazenzmatrix liefert genau die gewünschte Matrix.

Als nächstes Beispiel berechnen wir die Dichte eines Netzwerks. Diese ist eine wichtige Kennzahl in Netzwerken, sie berechnet sich als Anzahl der vorhandenen Kanten durch die Anzahl der möglichen Kanten und gibt somit eben an, wie dicht bzw. zentralisiert ein Netzwerk ist. Durch eine doppelte Summation über die Matrixdimension lässt sich die Dichte ganz leicht berechnen:

public static ILRetArray<double> CalcDensity(ILInArray<double> AdjMatrix)
{
    using (ILScope.Enter(AdjMatrix))
    {
        ILArray<double> NrLinks = sum(sum(AdjMatrix));
        return (double)NrLinks / (AdjMatrix.Length * (AdjMatrix.Length - 1));
    }
}

Nun zu einem Algorithmus, welcher seine Erfinder reich und weltberühmt gemacht hat, im Kern im Prinzip aber so einfach ist, dass wir ihn hier jetzt umsetzen (im Kern, natürlich gehört noch viel mehr dazu als die paar Zeilen Code). Die Rede ist von Googles Pagerank Algorithmus, für lange Zeit die Basis für das Ranking von Webseiten bei der Google Suche. Der Algorithmus weist jeder Seite einen Pagerank (eine Popularität) zu, welcher sich aus der Summe der Pageranks der zu der Seite verlinkenden Seiten zusammensetzt. Die Formel zur Berechnung ist im verlinkten Wikipedia Artikel erklärt, in der Praxis wird der Pagerank jedoch iterativ wie folgt approximiert: Starte mit einem beliebigen Pagerank Vektor P, dann berechnet sich der neue Pagerank zu P = (1 - d) * e * A'T*P. Hierbei ist d ein Dämpfungsfaktor (z.B. 0.5), e der Einheitsvektor und A' die Matrix die aus der Adjazenzmatrix A entsteht, indem alle Zeilen mit nur Nullen durch Zeilen mit Einträgen 1/n (n = Anzahl der Knoten) ersetzt wird. Dies wiederholen wir so oft, bis sich neuer und alter Pagerank genügend nah angenährt haben, das Verfahren also pro Iteration nicht mehr viel verändert. In ILNumerics sieht das wie folgt aus:

        public static ILRetArray<double> CalcPageRank(ILArray<double> AdjMatrix)
        {
            using (ILScope.Enter(AdjMatrix))
            {
                ILArray<double> Degrees = sum(AdjMatrix.T);
                double epsilon = 0.00001;
                double d = 0.5;
                for (int i = 0; i < AdjMatrix.Length; i++)
                {
                    for (int j = 0; j < AdjMatrix.Length; j++)
                    {
                        if (Degrees[i] != 0)
                            AdjMatrix[i, j] /= Degrees[i];
                    }
                    if (AdjMatrix["0", ":"].Equals(zeros(1, AdjMatrix.Length)))
                        AdjMatrix["0", ":"] = ones(AdjMatrix.Length) / AdjMatrix.Length;
                }
                ILArray<double> POld = zeros(AdjMatrix.Length);
                ILArray<double> PNew = ones(AdjMatrix.Length);

                do
                {
                    POld = PNew;
                    PNew = (1 - d) * ones(AdjMatrix.Length) + d * multiply(AdjMatrix.T, POld);
                }
                while (norm(POld - PNew, 1) > epsilon);

                return PNew;
            }
        }

Mit der vorher beschriebenen Methode muss also zuerst die Adjazenzmatrix eines beliebigen Graphen eingelesen werden, die Funktion berechnet dann für jeden Knoten im Graph den Pagerank und gibt den Vektor der Pageranks zurück. Wie man sieht, ist auch die Auswahl von Untermatrizen wie in Matlab möglich. Matrix["a:b", "c:d"] wählt die Untermatrix aus, welche aus den Zeilen a - b und den Spalten c - d von Matrix besteht.

Allerdings sollte man die Pagerank Berechnung so nicht in ILNumerics implementieren. Ich habe den Code nur erst in diesem Stil geschrieben, um einen intuitiven Einstieg zu geben, und damit aufzuzeigen, wie man die Fähigkeiten von ILNumerics besser ausnutzt.
Generell empfiehlt sich die Verwendung von ILNumerics eigenen Funktionen und die Vermeidung von selber gebauten, wie for - Schleifen über große Matrizen etc. So verwenden wir statt dem iterieren durch die Matrix und Testen der Zeilen auf 0 den ILLogical Datentyp. Dieser liefert uns einen Vektor, welcher angibt, welche Elemente aus einem gegebenen Vektor eine bestimmte Bedingung erfüllen. Wir wählen als Bedingung Degrees = 0, wählen dann die Untermatrix mit den entsprechenden Zeilen aus und ersetzen diese durch 1/n. Das gleiche tun wir, um Einträge zu Knoten mit Grad ungleich 0 durch n zu teilen. Desweiteren haben wir unnötige Rechenoperationen aus der Rechenschleife herausgezogen, wie das Generieren eines Vektors von Einsen und das Transponieren der Matrix. Der entstandene, deutlich schlankere und schnellere Code sieht so aus:

        public static ILRetArray<double> CalcPageRank(ILArray<double> AdjMatrix)
        {
            using (ILScope.Enter(AdjMatrix))
            {
                ILArray<double> Degrees = sum(AdjMatrix, 1);
                double epsilon = 0.00001;
                double d = 0.5;

                ILLogical dummy = Degrees == 0;

                AdjMatrix[dummy, full] = 1.0 / AdjMatrix.Length;
                dummy = Degrees != 0;

                AdjMatrix[dummy, full] = AdjMatrix[dummy, full] / Degrees[dummy];
                AdjMatrix = AdjMatrix.T;

                ILArray<double> POld = zeros(AdjMatrix.Length);
                ILArray<double> PNew = ones(AdjMatrix.Length);
                ILArray<double> ILOnes = (1.0 - d) * ones(AdjMatrix.Length);

                do
                {
                    POld = PNew;
                    PNew = ILOnes + d * multiply(AdjMatrix, POld);
                }
                while (norm(POld - PNew, 1) > epsilon);

                return PNew;
            }
        }

Dieses Verfahren in Matlab implementiert benötigt bei einem 20 MB großen Graphen (ca. 3000 Knoten) etwa 5s, ILNumerics ebenfalls. Bei so geringen Laufzeiten lassen sich natürlich keine verlässlichen Performancemessungen durchführen, aber ich denke diese liegt im vergleichbaren Bereich. Laut Angaben der Hersteller hat ILNumerics einen etwas größeren Overhead, soll aber bei längeren Ausführungszeiten wesentlich schneller sein.

Um die Computing Engine abzuschließen, hier noch ein Beispiel wie sich ein lineares Gleichungssystem lösen lässt:

            ILArray<double> A = ILMath.zeros(3, 3);
            A["0;:"] = new double[] { 1, 2, 1 };
            A["1;:"] = new double[] { 3, 4, 0 };
            A["2;:"] = new double[] { -1, 0, 1 };

            ILArray<double> B = new double[] { 5, 4, 7};

            ILArray<double> x = ILMath.linsolve(A, B);

Kommen wir nun zur Visualization Engine. Hierbei gibt es natürlich unglaublich viele Möglichkeiten und Einstellungen, den gewünschten Plot zu erzeugen, deswegen werde ich hier nur eine kurze Einführung geben und für den Rest auf die Online Dokumentation verweisen.
Wie bei der Computation Engine folgen wir hier dem Quick Start Guide und legen ein neues Windows-Form Projekt an. Dann fügen wir mittels Projekt - Neues Element hinzufügen eine Plotting Form hinzu. In der Datei Program.cs ändern wir die Zeile Application.Run(new Form1()); zu Application.Run(new Plotting_Form1());, um dem Programm mitzuteilen, dass es das Plotting Formular starten soll.
Dieses enthält bereits Demo Plots, wir löschen aber erst einmal den Code um von Grund auf einen neuen Plot zu erstellen. Die Plotting Form stellt ein Steuerelement names ilPanel1 bereit, in dessen Load() Funktion zeichnen wir beim Starten des Formulars einen einfachen Linienplot mit 6 Punkten:

        private void ilPanel1_Load(object sender, EventArgs e)
        {
            var scene = new ILScene();
            // create some data

            ILArray<float> A = new float[] { 1, 2, 3, 4, -1, -2 };
            // add a plot cube
            scene.Add(new ILPlotCube {
  new ILLinePlot(A)
});
            ilPanel1.Scene = scene;
        }

Grundbestandteil der Plots sind sogennante scenes. Wir legen hier eine neue an und fügen dieser dann einen neuen Linienplot mit den angegebenen Daten hinzu. Das Ergebnis sieht so aus:


Standardmäßig kann der Zeichenbereich verschoben, gezoomt etc. werden. Hierfür muss der Plot per Code neugezeichnet werden.









Dies soll schon genügen, hier noch ein paar interessante Plots von der Homepage, die sich auch sehr leicht implementieren lassen:



Als Zusammenfassung kann ich noch einmal sagen, dass ich als Manko den Preis sehe, mir aber das Produkt insgesamt sehr gut gefallen hat, es bietet ein schnelles und einfach zu bedienendes Rechen- und Visualisierungstoolkit und ich auch von der Geschwindigkeit des Codes überzeugt bin. Ich sehe darin eine echte Alternative zu Matlab und würde mich freuen, wenn diese öfter eingesetzt wird.

Samstag, 18. Juli 2015

Gitarren Tab abspielen

In diesem Post möchte ich zeigen, wie man Gitarren Tabs auslesen kann und so quasi automatisiert Lieder mit C# und dem MIDI Format spielen kann. Sogenannte Tabs sind bei Gitarrenspielern eine beliebte und einfache Variante, Lieder zu notieren. Im Textformat werden einfach die 6 Gitarrenseiten durch ---- dargestellt, an Stellern, an welchen die Seite angeschlagen wird wird eine Zahl geschrieben, welche angibt, in welchem Bund die Seite gegriffen werden muss. So lässt sich ein Lied schon ziemlich gut angeben, die Töne können auf jeden Fall alle beschrieben werden, lediglich die Tonlänge kann nicht dargestellt werden, aber oft geben Striche zwischen den Zahlen Pausen an.
Da es in diesem Format quasi alle Lieder (frei zugänglich) gibt habe ich ein kleines Programm geschrieben, welches die Tabs einliest und das interpretierte Lied dann mit den Techniken aus den vorigen Posts spielt.
Zuerst wird die Funktion ReadTab() mit einer URL aufgerufen, wessen Quellcode dann mit einem Webclient heruntergeladen wird. Enthalten 6 aufeinanderfolgende Zeilen mindestens 3 "-", interpretieren wir die Zeilen als Tab und speichern sie. Anschließend schreiben wir sie in eine Datei (pro Zeile eine Gitarrenseite).
Dann wird die Funktion ReadSong() aufgerufen. In dieser wird die Datei gelesen und die 6 Spuren gleichzeitig gelesen. Für jede Zeiteinheit wird entweder eine Pause eingefügt, wenn auf keiner der Seiten eine Note gespielt wird, oder eine Liste mit allen angeschlagenen Noten. Dabei berechnen wir die Nummer der Klaviertaste, welche dem Ton entspricht. Dies tun wir, in dem wir ausgehend von der Aufzählung Tone, welche die zu den 6 Seiten entsprechenden Tasten speichert, die Zahl aus dem Tab addieren, denn dies ist genau die Anzahl der Halbtöne (Tasten), um welche der Ursprungston erhöht wird.
Anschließend übergeben wir diese Liste von Listen von Tastennummern an die Funktion Play(). Diese geht alle Zeitpunkte durch und spielt entweder die Liste von Tönen gleichzeitig oder eine Pause ab, wie im vorigen Post beschrieben.

Der Code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.IO;
using Midi;
using System.Threading;

namespace TabReader
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ReadTab("http://tabs.ultimate-guitar.com/m/metallica/master_of_puppets_tab.htm");    
        }

        int NoteDuration = 70;

        protected enum Tone
        {
            E2 = 20,
            A = 25,
            D = 30,
            G = 35,
            B = 39,
            E4 = 44,
        }

        public void ReadTab(string url)
        {
            System.Net.WebClient wc = new System.Net.WebClient();
            string HTML = wc.DownloadString(url);
            string[] Lines = HTML.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
            string[] Tabs = new string[6];

            for (int i = 0; i < Lines.Length - 6; i++)
            {
                bool Found = true;
                for (int j = 0; j < 6; j++)
                {
                    if (Lines[i + j].Count(f => f == '-') <= 3)
                    {
                        Found = false;
                        break;
                    }
                }
                if (Found)
                {
                    for (int j = 0; j < 6; j++)
                    {
                        Tabs[j] += Lines[i + j];
                    }
                    i += 6;
                }
            }

            string FileName = url.Replace("/", "").Replace("http", "").Replace(":", "");
            StreamWriter sw = new StreamWriter(FileName);
            for (int i = 0; i < 6; i++)
            {
                sw.WriteLine(Tabs[i]);
            }
            sw.Close();

            List<List<int>> Result = ReadSong(FileName);
            Play(Result);
        }

        public List<List<int>> ReadSong(string path)
        {
            System.IO.StreamReader sr = new System.IO.StreamReader(path);
            string[] Strings = new string[6];
            for (int i = 0; i < 6; i++)
            {
                Strings[i] = "";
            }

            string Temp = "";
            while ((Temp = sr.ReadLine()) != null)
            {
                Strings[5] += Temp;
                for (int i = 4; i >= 0; i--)
                {
                    Strings[i] += sr.ReadLine();
                }
            }
            sr.Close();

            List<List<int>> Song = new List<List<int>>();

            for (int i = 0; i < Strings[0].Length; i++)
            {

                bool ToneFound = false;

                List<int> Current = new List<int>();

                for (int j = 0; j < 6; j++)
                {
                    if (Strings[j][i] != '-' && Strings[j][i] != '/' && Strings[j][i] != '|')
                    {
                        int Dummy;

                        if (int.TryParse(Strings[j][i].ToString(), out Dummy))
                        {
                            Current.Add(((int)((Tone[])(Enum.GetValues(typeof(Tone))))[j] + int.Parse(Strings[j][i].ToString())));
                            ToneFound = true;
                        }
                    }

                }

                if (!ToneFound && Strings[0][i] == '-')
                    Song.Add(null);
                else
                    Song.Add(Current);
            }

            Play(Song);
            return Song;
        }

        public void Play(List<List<int>> keys)
        {
            OutputDevice outputDevice = OutputDevice.InstalledDevices[0];
            outputDevice.Open();

            foreach (List<int> t in keys)
            {
                if (t == null)
                    Thread.Sleep(NoteDuration);
                else
                {
                    foreach (int s in t)
                    {
                        outputDevice.SendNoteOn(Channel.Channel1, GetFreq(s), 80);
                    }
                    Thread.Sleep(NoteDuration);
                }
            }
        }

        public Pitch GetFreq(int key)
        {
            return ((Pitch[])Enum.GetValues(typeof(Pitch)))[key + 21];
        }
    }
}

Als Beispiel habe ich hier das Lied Master of Puppets von Metallica aufgenommen. Wie gesagt, natürlich stimmt der Rythmus oft nicht und die Melodie wirkt manchmal leicht daneben, aber ich finde es doch faszinierend wie gut und genau zum Beispiel das Einleitungsriff getroffen wird und wie leicht wir solche Musikausgaben erzeugen können.
Außerdem gibt es hier noch Yesterday und Wonderwall.

Freitag, 17. Juli 2015

MIDI Töne mit C# erzeugen

Nachdem ich in den vorigen Posts das Erzeugen grundlegender musikalischer Töne mit Beep erklärt habe, möchte ich heute nun den Klang deutlich verbessern, was wir mit dem MIDI Format tun.
MIDI ist ein Standard zum Austausch musikalischer Steuerinformationen. Hauptsächlich wurde es zur Kommunikation von Synthesizern entworfen, es besteht aus Nachrichten die das Spielen bestimmter Töne beschreiben. Mit einer externen Bibliothek können wir in C# ganz leicht MIDI Töne abspielen, welche auch wirklich gut klingen, was mich sehr überrascht und fasziniert hat - mit ein paar Codezeilen kann man wirklich Lieder abspielen.

Wir verwenden dafür die MIDI Bibliothek midi-dot-net. Diese muss heruntergeladen werden, und dann ist die Datei "midi.dll" ins Projekt einzubinden (Projekt - Verweis hinzufügen). Nach using Midi; kann dann ein OutputDevice angelegt werden, welches wir mit InstalledDevices[0] auf unseren Standardlautsprecher schalten. Mittels SendNoteOn() können wir dann eine Note spielen. Hierzu müssen wir als ersten Parameter einen Channel übergeben, als zweiten die Note und als dritten einen int - Wert für die Anschlagstärke der gedachten Keyboard Taste. Die Note wird als Objekt von der Aufzählung Pitch angegeben, welche ein breites Spektrum von Tönen enthält.
Ich fand jedoch den Ansatz aus dem letzten Post der Benutzung der Klaviertasten ganz schön, deswegen benutzen wir das hier: Wir konvertieren uns die Aufzählung in ein Pitch Array und suchen uns dann den zu der gedrückten Taste gehörigen Ton heraus (der gewünschte Ton der Taste n ist der Eintrag n + 21).
Der Ton wird solange gespielt, bis ein anderer Befehl kommt, z.B. können wir mit SendNoteOff() den Ton ausschalten. Wir warten einfach mittels Sleep() die entsprechende Tonlänge und spielen dann den nächsten.
Das folgende Programm spielt die beidem im vorigen Post gezeigten Hymnen mit MIDI Tönen ab:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using Midi;
using System.Threading;

namespace Beep
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            PlayAmericanAnthem();
            PlayGermanAnthem();
        }

        OutputDevice outputDevice;

        public void PlayNote(int key, double duration)
        {
            Pitch Note = ((Pitch[])Enum.GetValues(typeof(Pitch)))[key + 21];
            outputDevice.SendNoteOn(Channel.Channel1, Note, 80);
            Thread.Sleep((int)duration);
        }

        public void PlayGermanAnthem()
        {
            outputDevice = OutputDevice.InstalledDevices[0];
            outputDevice.Open();

            PlayNote(31, 700);
            PlayNote(33, 300);

            PlayNote(35, 500);
            PlayNote(33, 500);
            PlayNote(36, 500);
            PlayNote(35, 500);

            PlayNote(33, 250);
            PlayNote(30, 250);
            PlayNote(31, 500);
            PlayNote(40, 500);
            PlayNote(38, 500);

            PlayNote(36, 500);
            PlayNote(35, 500);
            PlayNote(33, 500);
            PlayNote(35, 250);
            PlayNote(31, 250);

            PlayNote(38, 800);
            outputDevice.Close();
        }

        public void PlayAmericanAnthem()
        {
            outputDevice = OutputDevice.InstalledDevices[0];
            outputDevice.Open();

            PlayNote(20, 300 * 1.3);
            PlayNote(17, 170 * 1.3);

            PlayNote(13, 333 * 1.3);
            PlayNote(17, 333 * 1.3);
            PlayNote(20, 333 * 1.3);

            PlayNote(25, 666 * 1.3);
            PlayNote(29, 200 * 1.3);
            PlayNote(27, 130 * 1.3);

            PlayNote(25, 333 * 1.3);
            PlayNote(17, 333 * 1.3);
            PlayNote(19, 333 * 1.3);

            PlayNote(20, 666 * 1.3);
            PlayNote(20, 170 * 1.3);
            PlayNote(20, 170 * 1.3);

            PlayNote(29, 450 * 1.3);
            PlayNote(27, 220 * 1.3);
            PlayNote(25, 333 * 1.3);

            PlayNote(24, 666 * 1.3);
            PlayNote(22, 200 * 1.3);
            PlayNote(24, 100 * 1.3);

            PlayNote(25, 333 * 1.3);
            PlayNote(25, 333 * 1.3);
            PlayNote(20, 333 * 1.3);

            PlayNote(17, 333 * 1.3);
            PlayNote(13, 333 * 1.3);
            outputDevice.Close();
        }
    }
}

Donnerstag, 16. Juli 2015

Klavier Tastatur

Nachdem ich im vorigen Post erklärt habe, wie man musikalische Töne mittels Beep() erzeugt, möchte ich heute einfach ein kleines Programm zeigen, welche eine rudimentäre spielbare Klaviertastatur darstellt. Die Töne werden einfach nach der Formel aus dem vorigen Post berechnet, die Oberfläche des Programms sieht so aus:










Und hier ist der Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace KeyboardTone
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void PlayKey(int key)
        {
            Console.Beep((int)(Math.Pow(Math.Pow(2, 1 / (double)12), key - 49) * 440), 500);
        }

        private void button_Click(object sender, EventArgs e)
        {
            PlayKey(int.Parse(((Button)sender).Name.Substring(6)));
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            button13.Click += button_Click;
            button14.Click += button_Click;
            button15.Click += button_Click;
            button16.Click += button_Click;
            button17.Click += button_Click;
            button18.Click += button_Click;
            button19.Click += button_Click;
            button20.Click += button_Click;
            button21.Click += button_Click;
            button22.Click += button_Click;
            button23.Click += button_Click;
            button24.Click += button_Click;
            button25.Click += button_Click;
            button26.Click += button_Click;
            button27.Click += button_Click;
            button28.Click += button_Click;
            button29.Click += button_Click;
            button30.Click += button_Click;
            button31.Click += button_Click;
            button32.Click += button_Click;
            button33.Click += button_Click;
            button34.Click += button_Click;
            button35.Click += button_Click;
            button36.Click += button_Click;
            button37.Click += button_Click;
            button38.Click += button_Click;
            button39.Click += button_Click;
            button40.Click += button_Click;
            button41.Click += button_Click;
            button42.Click += button_Click;
            button43.Click += button_Click;
            button44.Click += button_Click;
            button45.Click += button_Click;
            button46.Click += button_Click;
            button47.Click += button_Click;
            button48.Click += button_Click;
            button49.Click += button_Click;
            button50.Click += button_Click;
            button51.Click += button_Click;
            button52.Click += button_Click;
            button53.Click += button_Click;
            button54.Click += button_Click;
            button55.Click += button_Click;
            button56.Click += button_Click;
            button57.Click += button_Click;
            button58.Click += button_Click;
            button59.Click += button_Click;
            button60.Click += button_Click;
        }

    }
}
Und hier der Inhalt der Datei Form1.Designer.cs:
namespace KeyboardTone
{
    partial class Form1
    {
        /// <summary>
       /// Erforderliche Designervariable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
       /// Verwendete Ressourcen bereinigen.
        /// </summary>
        /// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Vom Windows Form-Designer generierter Code

        /// <summary>
       /// Erforderliche Methode für die Designerunterstützung.
       /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
        /// </summary>
        private void InitializeComponent()
        {
            this.button13 = new System.Windows.Forms.Button();
            this.button15 = new System.Windows.Forms.Button();
            this.button14 = new System.Windows.Forms.Button();
            this.button16 = new System.Windows.Forms.Button();
            this.button17 = new System.Windows.Forms.Button();
            this.button18 = new System.Windows.Forms.Button();
            this.button19 = new System.Windows.Forms.Button();
            this.button20 = new System.Windows.Forms.Button();
            this.button21 = new System.Windows.Forms.Button();
            this.button22 = new System.Windows.Forms.Button();
            this.button23 = new System.Windows.Forms.Button();
            this.button24 = new System.Windows.Forms.Button();
            this.button35 = new System.Windows.Forms.Button();
            this.button36 = new System.Windows.Forms.Button();
            this.button33 = new System.Windows.Forms.Button();
            this.button34 = new System.Windows.Forms.Button();
            this.button31 = new System.Windows.Forms.Button();
            this.button32 = new System.Windows.Forms.Button();
            this.button30 = new System.Windows.Forms.Button();
            this.button28 = new System.Windows.Forms.Button();
            this.button29 = new System.Windows.Forms.Button();
            this.button26 = new System.Windows.Forms.Button();
            this.button27 = new System.Windows.Forms.Button();
            this.button25 = new System.Windows.Forms.Button();
            this.button47 = new System.Windows.Forms.Button();
            this.button48 = new System.Windows.Forms.Button();
            this.button45 = new System.Windows.Forms.Button();
            this.button46 = new System.Windows.Forms.Button();
            this.button43 = new System.Windows.Forms.Button();
            this.button44 = new System.Windows.Forms.Button();
            this.button42 = new System.Windows.Forms.Button();
            this.button40 = new System.Windows.Forms.Button();
            this.button41 = new System.Windows.Forms.Button();
            this.button38 = new System.Windows.Forms.Button();
            this.button39 = new System.Windows.Forms.Button();
            this.button37 = new System.Windows.Forms.Button();
            this.button59 = new System.Windows.Forms.Button();
            this.button60 = new System.Windows.Forms.Button();
            this.button57 = new System.Windows.Forms.Button();
            this.button58 = new System.Windows.Forms.Button();
            this.button55 = new System.Windows.Forms.Button();
            this.button56 = new System.Windows.Forms.Button();
            this.button54 = new System.Windows.Forms.Button();
            this.button52 = new System.Windows.Forms.Button();
            this.button53 = new System.Windows.Forms.Button();
            this.button50 = new System.Windows.Forms.Button();
            this.button51 = new System.Windows.Forms.Button();
            this.button49 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            //
            // button13
            //
            this.button13.BackColor = System.Drawing.Color.White;
            this.button13.Location = new System.Drawing.Point(17, 42);
            this.button13.Name = "button13";
            this.button13.Size = new System.Drawing.Size(21, 113);
            this.button13.TabIndex = 0;
            this.button13.UseVisualStyleBackColor = false;
            //
            // button15
            //
            this.button15.BackColor = System.Drawing.Color.White;
            this.button15.Location = new System.Drawing.Point(38, 42);
            this.button15.Name = "button15";
            this.button15.Size = new System.Drawing.Size(21, 113);
            this.button15.TabIndex = 1;
            this.button15.UseVisualStyleBackColor = false;
            //
            // button14
            //
            this.button14.BackColor = System.Drawing.Color.Black;
            this.button14.Location = new System.Drawing.Point(29, 22);
            this.button14.Name = "button14";
            this.button14.Size = new System.Drawing.Size(17, 87);
            this.button14.TabIndex = 2;
            this.button14.UseVisualStyleBackColor = false;
            //
            // button16
            //
            this.button16.BackColor = System.Drawing.Color.Black;
            this.button16.Location = new System.Drawing.Point(50, 22);
            this.button16.Name = "button16";
            this.button16.Size = new System.Drawing.Size(17, 87);
            this.button16.TabIndex = 4;
            this.button16.UseVisualStyleBackColor = false;
            //
            // button17
            //
            this.button17.BackColor = System.Drawing.Color.White;
            this.button17.Location = new System.Drawing.Point(59, 42);
            this.button17.Name = "button17";
            this.button17.Size = new System.Drawing.Size(21, 113);
            this.button17.TabIndex = 3;
            this.button17.UseVisualStyleBackColor = false;
            //
            // button18
            //
            this.button18.BackColor = System.Drawing.Color.White;
            this.button18.Location = new System.Drawing.Point(80, 42);
            this.button18.Name = "button18";
            this.button18.Size = new System.Drawing.Size(21, 113);
            this.button18.TabIndex = 5;
            this.button18.UseVisualStyleBackColor = false;
            //
            // button19
            //
            this.button19.BackColor = System.Drawing.Color.Black;
            this.button19.Location = new System.Drawing.Point(92, 22);
            this.button19.Name = "button19";
            this.button19.Size = new System.Drawing.Size(17, 87);
            this.button19.TabIndex = 7;
            this.button19.UseVisualStyleBackColor = false;
            //
            // button20
            //
            this.button20.BackColor = System.Drawing.Color.White;
            this.button20.Location = new System.Drawing.Point(101, 42);
            this.button20.Name = "button20";
            this.button20.Size = new System.Drawing.Size(21, 113);
            this.button20.TabIndex = 6;
            this.button20.UseVisualStyleBackColor = false;
            //
            // button21
            //
            this.button21.BackColor = System.Drawing.Color.Black;
            this.button21.Location = new System.Drawing.Point(112, 22);
            this.button21.Name = "button21";
            this.button21.Size = new System.Drawing.Size(17, 87);
            this.button21.TabIndex = 9;
            this.button21.UseVisualStyleBackColor = false;
            //
            // button22
            //
            this.button22.BackColor = System.Drawing.Color.White;
            this.button22.Location = new System.Drawing.Point(121, 42);
            this.button22.Name = "button22";
            this.button22.Size = new System.Drawing.Size(21, 113);
            this.button22.TabIndex = 8;
            this.button22.UseVisualStyleBackColor = false;
            //
            // button23
            //
            this.button23.BackColor = System.Drawing.Color.Black;
            this.button23.Location = new System.Drawing.Point(133, 22);
            this.button23.Name = "button23";
            this.button23.Size = new System.Drawing.Size(17, 87);
            this.button23.TabIndex = 11;
            this.button23.UseVisualStyleBackColor = false;
            //
            // button24
            //
            this.button24.BackColor = System.Drawing.Color.White;
            this.button24.Location = new System.Drawing.Point(142, 42);
            this.button24.Name = "button24";
            this.button24.Size = new System.Drawing.Size(21, 113);
            this.button24.TabIndex = 10;
            this.button24.UseVisualStyleBackColor = false;
            //
            // button35
            //
            this.button35.BackColor = System.Drawing.Color.Black;
            this.button35.Location = new System.Drawing.Point(285, 22);
            this.button35.Name = "button35";
            this.button35.Size = new System.Drawing.Size(17, 87);
            this.button35.TabIndex = 23;
            this.button35.UseVisualStyleBackColor = false;
            //
            // button36
            //
            this.button36.BackColor = System.Drawing.Color.White;
            this.button36.Location = new System.Drawing.Point(294, 42);
            this.button36.Name = "button36";
            this.button36.Size = new System.Drawing.Size(21, 113);
            this.button36.TabIndex = 22;
            this.button36.UseVisualStyleBackColor = false;
            //
            // button33
            //
            this.button33.BackColor = System.Drawing.Color.Black;
            this.button33.Location = new System.Drawing.Point(264, 22);
            this.button33.Name = "button33";
            this.button33.Size = new System.Drawing.Size(17, 87);
            this.button33.TabIndex = 21;
            this.button33.UseVisualStyleBackColor = false;
            //
            // button34
            //
            this.button34.BackColor = System.Drawing.Color.White;
            this.button34.Location = new System.Drawing.Point(273, 42);
            this.button34.Name = "button34";
            this.button34.Size = new System.Drawing.Size(21, 113);
            this.button34.TabIndex = 20;
            this.button34.UseVisualStyleBackColor = false;
            //
            // button31
            //
            this.button31.BackColor = System.Drawing.Color.Black;
            this.button31.Location = new System.Drawing.Point(244, 22);
            this.button31.Name = "button31";
            this.button31.Size = new System.Drawing.Size(17, 87);
            this.button31.TabIndex = 19;
            this.button31.UseVisualStyleBackColor = false;
            //
            // button32
            //
            this.button32.BackColor = System.Drawing.Color.White;
            this.button32.Location = new System.Drawing.Point(253, 42);
            this.button32.Name = "button32";
            this.button32.Size = new System.Drawing.Size(21, 113);
            this.button32.TabIndex = 18;
            this.button32.UseVisualStyleBackColor = false;
            //
            // button30
            //
            this.button30.BackColor = System.Drawing.Color.White;
            this.button30.Location = new System.Drawing.Point(232, 42);
            this.button30.Name = "button30";
            this.button30.Size = new System.Drawing.Size(21, 113);
            this.button30.TabIndex = 17;
            this.button30.UseVisualStyleBackColor = false;
            //
            // button28
            //
            this.button28.BackColor = System.Drawing.Color.Black;
            this.button28.Location = new System.Drawing.Point(202, 22);
            this.button28.Name = "button28";
            this.button28.Size = new System.Drawing.Size(17, 87);
            this.button28.TabIndex = 16;
            this.button28.UseVisualStyleBackColor = false;
            //
            // button29
            //
            this.button29.BackColor = System.Drawing.Color.White;
            this.button29.Location = new System.Drawing.Point(211, 42);
            this.button29.Name = "button29";
            this.button29.Size = new System.Drawing.Size(21, 113);
            this.button29.TabIndex = 15;
            this.button29.UseVisualStyleBackColor = false;
            //
            // button26
            //
            this.button26.BackColor = System.Drawing.Color.Black;
            this.button26.Location = new System.Drawing.Point(181, 22);
            this.button26.Name = "button26";
            this.button26.Size = new System.Drawing.Size(17, 87);
            this.button26.TabIndex = 14;
            this.button26.UseVisualStyleBackColor = false;
            //
            // button27
            //
            this.button27.BackColor = System.Drawing.Color.White;
            this.button27.Location = new System.Drawing.Point(190, 42);
            this.button27.Name = "button27";
            this.button27.Size = new System.Drawing.Size(21, 113);
            this.button27.TabIndex = 13;
            this.button27.UseVisualStyleBackColor = false;
            //
            // button25
            //
            this.button25.BackColor = System.Drawing.Color.White;
            this.button25.Location = new System.Drawing.Point(169, 42);
            this.button25.Name = "button25";
            this.button25.Size = new System.Drawing.Size(21, 113);
            this.button25.TabIndex = 12;
            this.button25.UseVisualStyleBackColor = false;
            //
            // button47
            //
            this.button47.BackColor = System.Drawing.Color.Black;
            this.button47.Location = new System.Drawing.Point(439, 22);
            this.button47.Name = "button47";
            this.button47.Size = new System.Drawing.Size(17, 87);
            this.button47.TabIndex = 35;
            this.button47.UseVisualStyleBackColor = false;
            //
            // button48
            //
            this.button48.BackColor = System.Drawing.Color.White;
            this.button48.Location = new System.Drawing.Point(448, 42);
            this.button48.Name = "button48";
            this.button48.Size = new System.Drawing.Size(21, 113);
            this.button48.TabIndex = 34;
            this.button48.UseVisualStyleBackColor = false;
            //
            // button45
            //
            this.button45.BackColor = System.Drawing.Color.Black;
            this.button45.Location = new System.Drawing.Point(418, 22);
            this.button45.Name = "button45";
            this.button45.Size = new System.Drawing.Size(17, 87);
            this.button45.TabIndex = 33;
            this.button45.UseVisualStyleBackColor = false;
            //
            // button46
            //
            this.button46.BackColor = System.Drawing.Color.White;
            this.button46.Location = new System.Drawing.Point(427, 42);
            this.button46.Name = "button46";
            this.button46.Size = new System.Drawing.Size(21, 113);
            this.button46.TabIndex = 32;
            this.button46.UseVisualStyleBackColor = false;
            //
            // button43
            //
            this.button43.BackColor = System.Drawing.Color.Black;
            this.button43.Location = new System.Drawing.Point(398, 22);
            this.button43.Name = "button43";
            this.button43.Size = new System.Drawing.Size(17, 87);
            this.button43.TabIndex = 31;
            this.button43.UseVisualStyleBackColor = false;
            //
            // button44
            //
            this.button44.BackColor = System.Drawing.Color.White;
            this.button44.Location = new System.Drawing.Point(407, 42);
            this.button44.Name = "button44";
            this.button44.Size = new System.Drawing.Size(21, 113);
            this.button44.TabIndex = 30;
            this.button44.UseVisualStyleBackColor = false;
            //
            // button42
            //
            this.button42.BackColor = System.Drawing.Color.White;
            this.button42.Location = new System.Drawing.Point(386, 42);
            this.button42.Name = "button42";
            this.button42.Size = new System.Drawing.Size(21, 113);
            this.button42.TabIndex = 29;
            this.button42.UseVisualStyleBackColor = false;
            //
            // button40
            //
            this.button40.BackColor = System.Drawing.Color.Black;
            this.button40.Location = new System.Drawing.Point(356, 22);
            this.button40.Name = "button40";
            this.button40.Size = new System.Drawing.Size(17, 87);
            this.button40.TabIndex = 28;
            this.button40.UseVisualStyleBackColor = false;
            //
            // button41
            //
            this.button41.BackColor = System.Drawing.Color.White;
            this.button41.Location = new System.Drawing.Point(365, 42);
            this.button41.Name = "button41";
            this.button41.Size = new System.Drawing.Size(21, 113);
            this.button41.TabIndex = 27;
            this.button41.UseVisualStyleBackColor = false;
            //
            // button38
            //
            this.button38.BackColor = System.Drawing.Color.Black;
            this.button38.Location = new System.Drawing.Point(335, 22);
            this.button38.Name = "button38";
            this.button38.Size = new System.Drawing.Size(17, 87);
            this.button38.TabIndex = 26;
            this.button38.UseVisualStyleBackColor = false;
            //
            // button39
            //
            this.button39.BackColor = System.Drawing.Color.White;
            this.button39.Location = new System.Drawing.Point(344, 42);
            this.button39.Name = "button39";
            this.button39.Size = new System.Drawing.Size(21, 113);
            this.button39.TabIndex = 25;
            this.button39.UseVisualStyleBackColor = false;
            //
            // button37
            //
            this.button37.BackColor = System.Drawing.Color.White;
            this.button37.Location = new System.Drawing.Point(323, 42);
            this.button37.Name = "button37";
            this.button37.Size = new System.Drawing.Size(21, 113);
            this.button37.TabIndex = 24;
            this.button37.UseVisualStyleBackColor = false;
            //
            // button59
            //
            this.button59.BackColor = System.Drawing.Color.Black;
            this.button59.Location = new System.Drawing.Point(594, 22);
            this.button59.Name = "button59";
            this.button59.Size = new System.Drawing.Size(17, 87);
            this.button59.TabIndex = 47;
            this.button59.UseVisualStyleBackColor = false;
            //
            // button60
            //
            this.button60.BackColor = System.Drawing.Color.White;
            this.button60.Location = new System.Drawing.Point(603, 42);
            this.button60.Name = "button60";
            this.button60.Size = new System.Drawing.Size(21, 113);
            this.button60.TabIndex = 46;
            this.button60.UseVisualStyleBackColor = false;
            //
            // button57
            //
            this.button57.BackColor = System.Drawing.Color.Black;
            this.button57.Location = new System.Drawing.Point(573, 22);
            this.button57.Name = "button57";
            this.button57.Size = new System.Drawing.Size(17, 87);
            this.button57.TabIndex = 45;
            this.button57.UseVisualStyleBackColor = false;
            //
            // button58
            //
            this.button58.BackColor = System.Drawing.Color.White;
            this.button58.Location = new System.Drawing.Point(582, 42);
            this.button58.Name = "button58";
            this.button58.Size = new System.Drawing.Size(21, 113);
            this.button58.TabIndex = 44;
            this.button58.UseVisualStyleBackColor = false;
            //
            // button55
            //
            this.button55.BackColor = System.Drawing.Color.Black;
            this.button55.Location = new System.Drawing.Point(553, 22);
            this.button55.Name = "button55";
            this.button55.Size = new System.Drawing.Size(17, 87);
            this.button55.TabIndex = 43;
            this.button55.UseVisualStyleBackColor = false;
            //
            // button56
            //
            this.button56.BackColor = System.Drawing.Color.White;
            this.button56.Location = new System.Drawing.Point(562, 42);
            this.button56.Name = "button56";
            this.button56.Size = new System.Drawing.Size(21, 113);
            this.button56.TabIndex = 42;
            this.button56.UseVisualStyleBackColor = false;
            //
            // button54
            //
            this.button54.BackColor = System.Drawing.Color.White;
            this.button54.Location = new System.Drawing.Point(541, 42);
            this.button54.Name = "button54";
            this.button54.Size = new System.Drawing.Size(21, 113);
            this.button54.TabIndex = 41;
            this.button54.UseVisualStyleBackColor = false;
            //
            // button52
            //
            this.button52.BackColor = System.Drawing.Color.Black;
            this.button52.Location = new System.Drawing.Point(511, 22);
            this.button52.Name = "button52";
            this.button52.Size = new System.Drawing.Size(17, 87);
            this.button52.TabIndex = 40;
            this.button52.UseVisualStyleBackColor = false;
            //
            // button53
            //
            this.button53.BackColor = System.Drawing.Color.White;
            this.button53.Location = new System.Drawing.Point(520, 42);
            this.button53.Name = "button53";
            this.button53.Size = new System.Drawing.Size(21, 113);
            this.button53.TabIndex = 39;
            this.button53.UseVisualStyleBackColor = false;
            //
            // button50
            //
            this.button50.BackColor = System.Drawing.Color.Black;
            this.button50.Location = new System.Drawing.Point(490, 22);
            this.button50.Name = "button50";
            this.button50.Size = new System.Drawing.Size(17, 87);
            this.button50.TabIndex = 38;
            this.button50.UseVisualStyleBackColor = false;
            //
            // button51
            //
            this.button51.BackColor = System.Drawing.Color.White;
            this.button51.Location = new System.Drawing.Point(499, 42);
            this.button51.Name = "button51";
            this.button51.Size = new System.Drawing.Size(21, 113);
            this.button51.TabIndex = 37;
            this.button51.UseVisualStyleBackColor = false;
            //
            // button49
            //
            this.button49.BackColor = System.Drawing.Color.White;
            this.button49.Location = new System.Drawing.Point(478, 42);
            this.button49.Name = "button49";
            this.button49.Size = new System.Drawing.Size(21, 113);
            this.button49.TabIndex = 36;
            this.button49.UseVisualStyleBackColor = false;
            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(652, 180);
            this.Controls.Add(this.button59);
            this.Controls.Add(this.button60);
            this.Controls.Add(this.button57);
            this.Controls.Add(this.button58);
            this.Controls.Add(this.button55);
            this.Controls.Add(this.button56);
            this.Controls.Add(this.button54);
            this.Controls.Add(this.button52);
            this.Controls.Add(this.button53);
            this.Controls.Add(this.button50);
            this.Controls.Add(this.button51);
            this.Controls.Add(this.button49);
            this.Controls.Add(this.button47);
            this.Controls.Add(this.button48);
            this.Controls.Add(this.button45);
            this.Controls.Add(this.button46);
            this.Controls.Add(this.button43);
            this.Controls.Add(this.button44);
            this.Controls.Add(this.button42);
            this.Controls.Add(this.button40);
            this.Controls.Add(this.button41);
            this.Controls.Add(this.button38);
            this.Controls.Add(this.button39);
            this.Controls.Add(this.button37);
            this.Controls.Add(this.button35);
            this.Controls.Add(this.button36);
            this.Controls.Add(this.button33);
            this.Controls.Add(this.button34);
            this.Controls.Add(this.button31);
            this.Controls.Add(this.button32);
            this.Controls.Add(this.button30);
            this.Controls.Add(this.button28);
            this.Controls.Add(this.button29);
            this.Controls.Add(this.button26);
            this.Controls.Add(this.button27);
            this.Controls.Add(this.button25);
            this.Controls.Add(this.button23);
            this.Controls.Add(this.button24);
            this.Controls.Add(this.button21);
            this.Controls.Add(this.button22);
            this.Controls.Add(this.button19);
            this.Controls.Add(this.button20);
            this.Controls.Add(this.button18);
            this.Controls.Add(this.button16);
            this.Controls.Add(this.button17);
            this.Controls.Add(this.button14);
            this.Controls.Add(this.button15);
            this.Controls.Add(this.button13);
            this.Name = "Form1";
            this.Text = "Piano";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.Button button13;
        private System.Windows.Forms.Button button15;
        private System.Windows.Forms.Button button14;
        private System.Windows.Forms.Button button16;
        private System.Windows.Forms.Button button17;
        private System.Windows.Forms.Button button18;
        private System.Windows.Forms.Button button19;
        private System.Windows.Forms.Button button20;
        private System.Windows.Forms.Button button21;
        private System.Windows.Forms.Button button22;
        private System.Windows.Forms.Button button23;
        private System.Windows.Forms.Button button24;
        private System.Windows.Forms.Button button35;
        private System.Windows.Forms.Button button36;
        private System.Windows.Forms.Button button33;
        private System.Windows.Forms.Button button34;
        private System.Windows.Forms.Button button31;
        private System.Windows.Forms.Button button32;
        private System.Windows.Forms.Button button30;
        private System.Windows.Forms.Button button28;
        private System.Windows.Forms.Button button29;
        private System.Windows.Forms.Button button26;
        private System.Windows.Forms.Button button27;
        private System.Windows.Forms.Button button25;
        private System.Windows.Forms.Button button47;
        private System.Windows.Forms.Button button48;
        private System.Windows.Forms.Button button45;
        private System.Windows.Forms.Button button46;
        private System.Windows.Forms.Button button43;
        private System.Windows.Forms.Button button44;
        private System.Windows.Forms.Button button42;
        private System.Windows.Forms.Button button40;
        private System.Windows.Forms.Button button41;
        private System.Windows.Forms.Button button38;
        private System.Windows.Forms.Button button39;
        private System.Windows.Forms.Button button37;
        private System.Windows.Forms.Button button59;
        private System.Windows.Forms.Button button60;
        private System.Windows.Forms.Button button57;
        private System.Windows.Forms.Button button58;
        private System.Windows.Forms.Button button55;
        private System.Windows.Forms.Button button56;
        private System.Windows.Forms.Button button54;
        private System.Windows.Forms.Button button52;
        private System.Windows.Forms.Button button53;
        private System.Windows.Forms.Button button50;
        private System.Windows.Forms.Button button51;
        private System.Windows.Forms.Button button49;
    }
}