Metodě předáváme dva parametry. Hrat (True/False) zda ma hrát, nebo nehrát (zastavit dosavadni hraní) a kód klávesy (code). V těle je pak volána zmíněná metoda PlaySound z externího dll souboru. V metodě Play je ještě jedna složitá věcička (jako by toho nebylo málo :-). Volá čistě naší metodu BinSearch(code);
Tato metoda dělá jednu jedinou věc: Vrací index do pole "NOTY" vstupuje kód stisknuté klávesy. Proměnná "NOTY" je pole tříd "TON". Za pozornost stojí jak metoda vyhledává index v poli "NOTY". Využívá tzv. binárního vyhledávání. Základem je, že pole je setříděné (máme setříděno právě podle kódu klavesy). Algoritmus je asi takový .
1. Skočíme do prostředka posloupnosti
2. Pokud není dále co půlit KONEC -> hodnota nenalezena
3. Pokud se hodnoty rovnají KONEC (nazenena hodnota) -> vrať index.
4. Pokud je hledaná hodnota menší než aktuální prvek opakujeme postup (1) v horní části posloupnosti
5. Pokud je hledaná hodnota větší než aktuální prvek opakujeme postup (1) v dolní části posloupnosti.
ad 4.
Co se týče časování, tak jsem nakonec použil nejjednodušší věc a to rozdíl dvou datumů. Starý datum uchováváme v promenně DT. V proměnné milisec vypočítáme rozdíl datumů v milisekundách. Hodnotu spolu s kódem klávesy ukládáme do globální poměnné. Data
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
using System.IO;
using System.Threading;
namespace SilentNight
{
/// <summary>
/// Summary description for Form4.
/// </summary>
public class Form4 : System.Windows.Forms.Form
{
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label INFO;
short[] Data = new short[4096];
short Data_IDX = 0;
int milisec = 0;
DateTime DT = DateTime.Now;
private System.Windows.Forms.PictureBox Osnova;
private System.Windows.Forms.Button KONEC;
private System.Windows.Forms.PictureBox Nota;
private System.Windows.Forms.Button bNew;
private System.Windows.Forms.ComboBox SKLADBY;
private System.Windows.Forms.Button bLoad;
private System.Windows.Forms.Button bSave;
struct TON
{
public byte id;
public string noteName;
public int keyCode;
public string fileName;
public int Hz;
// Konstruktor stuktury
public TON(byte ID, string NoteName, int KeyCode, string FileName, int _Hz)
{
id = ID;
noteName = NoteName;
keyCode = KeyCode;
fileName = FileName;
Hz = _Hz;
}
}
TON [] NOTY = {
new TON ( 3, "E"", 69, "e4.wav", 330 ),
new TON ( 8, "C""", 73, "c5.wav", 523 ),
new TON ( 9, "D""", 79, "d5.wav", 587 ),
new TON ( 10, "E""", 80, "e5.wav", 659 ),
new TON ( 1, "C"", 81, "c4.wav", 262 ),
new TON ( 4, "F"", 82, "f4.wav", 346 ),
new TON ( 5, "G"", 84, "g4.wav", 392 ),
new TON ( 7, "H"", 85, "b4.wav", 494 ),
new TON ( 2, "D"", 87, "d4.wav", 294 ),
new TON ( 6, "A"", 89, "a4.wav", 440 ),
new TON ( 6, "A"", 90, "a4.wav", 440 ),
new TON ( 11, "F""", 219, "f5.wav", 698 ),
new TON ( 13, "A""", 220, "a5.wav", 880 ),
new TON ( 12, "G""", 221, "g5.wav", 784 )
};
public enum SND
{
SND_SYNC = 0x0000 ,/* play synchronously (default) */
SND_ASYNC = 0x0001 , /* play asynchronously */
SND_NODEFAULT = 0x0002 , /* silence (!default) if sound not found */
SND_MEMORY = 0x0004 , /* pszSound points to a memory file */
SND_LOOP = 0x0008 , /* loop the sound until next sndPlaySound */
SND_NOSTOP = 0x0010 , /* don"t stop any currently playing sound */
SND_NOWAIT = 0x00002000, /* don"t wait if the driver is busy */
SND_ALIAS = 0x00010000 ,/* name is a registry alias */
SND_ALIAS_ID = 0x00110000, /* alias is a pre d ID */
SND_FILENAME = 0x00020000, /* name is file name */
SND_RESOURCE = 0x00040004, /* name is resource name or atom */
SND_PURGE = 0x0040, /* purge non-static events for task */
SND_APPLICATION = 0x0080 /* look for application specific association */
}
[DllImport("winmm.dll", SetLastError=true)]
private static extern bool PlaySound(string pszSound, uint hmod, uint fdwSound);
int AKT_KEY = 0;
public Form4()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(Form4));
this.label2 = new System.Windows.Forms.Label();
this.Osnova = new System.Windows.Forms.PictureBox();
this.KONEC = new System.Windows.Forms.Button();
this.INFO = new System.Windows.Forms.Label();
this.Nota = new System.Windows.Forms.PictureBox();
this.SKLADBY = new System.Windows.Forms.ComboBox();
this.bLoad = new System.Windows.Forms.Button();
this.bSave = new System.Windows.Forms.Button();
this.bNew = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// label2
//
this.label2.Location = new System.Drawing.Point(168, 88);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(96, 8);
this.label2.TabIndex = 1;
//
// Osnova
//
this.Osnova.Image = Image.FromFile("img/osnova.gif"@);
this.Osnova.Location = new System.Drawing.Point(8, 8);
this.Osnova.Name = "Osnova";
this.Osnova.Size = new System.Drawing.Size(160, 104);
this.Osnova.TabIndex = 5;
this.Osnova.TabStop = false;
//
// KONEC
//
this.KONEC.Location = new System.Drawing.Point(224, 120);
this.KONEC.Name = "KONEC";
this.KONEC.TabIndex = 6;
this.KONEC.Text = "KONEC";
this.KONEC.Click += new System.EventHandler(this.KONEC_Click);
//
// INFO
//
this.INFO.Location = new System.Drawing.Point(168, 8);
this.INFO.Name = "INFO";
this.INFO.Size = new System.Drawing.Size(128, 72);
this.INFO.TabIndex = 8;
//
// Nota
//
this.Nota.Image = Image.FromFile("img/nota.gif"@);
this.Nota.Location = new System.Drawing.Point(88, 80);
this.Nota.Name = "Nota";
this.Nota.Size = new System.Drawing.Size(11, 7);
this.Nota.TabIndex = 9;
this.Nota.TabStop = false;
this.Nota.Visible = false;
//
// SKLADBY
//
this.SKLADBY.DisplayMember = "1";
this.SKLADBY.Items.AddRange(new object[] {
"SKLADBA 0",
"SKLADBA 1",
"SKLADBA 2",
"SKLADBA 3",
"SKLADBA 4",
"SKLADBA 5",
"SKLADBA 6",
"SKLADBA 7",
"SKLADBA 8"});
this.SKLADBY.Location = new System.Drawing.Point(8, 120);
this.SKLADBY.Name = "SKLADBY";
this.SKLADBY.Size = new System.Drawing.Size(80, 21);
this.SKLADBY.TabIndex = 10;
this.SKLADBY.Text = "comboBox1";
//
// bLoad
//
this.bLoad.Location = new System.Drawing.Point(96, 120);
this.bLoad.Name = "bLoad";
this.bLoad.Size = new System.Drawing.Size(35, 23);
this.bLoad.TabIndex = 11;
this.bLoad.Text = "Hrej";
this.bLoad.Click += new System.EventHandler(this.bLoad_Click);
//
// bSave
//
this.bSave.Location = new System.Drawing.Point(136, 120);
this.bSave.Name = "bSave";
this.bSave.Size = new System.Drawing.Size(35, 23);
this.bSave.TabIndex = 12;
this.bSave.Text = "Ulož";
this.bSave.Click += new System.EventHandler(this.bSave_Click);
//
// bNew
//
this.bNew.Location = new System.Drawing.Point(176, 120);
this.bNew.Name = "bNew";
this.bNew.Size = new System.Drawing.Size(40, 23);
this.bNew.TabIndex = 13;
this.bNew.Text = "Nová";
this.bNew.Click += new System.EventHandler(this.bNew_Click);
//
// Form4
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(312, 149);
this.Controls.AddRange(new System.Windows.Forms.Control[] {
this.bNew,
this.bSave,
this.bLoad,
this.SKLADBY,
this.Nota,
this.INFO,
this.KONEC,
this.Osnova,
this.label2});
this.KeyPreview = true;
this.Name = "Form4";
this.Text = "Silent Night .NET";
this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.OnKeyDown);
this.KeyUp += new System.Windows.Forms.KeyEventHandler(this.OnKeyUp);
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form4());
}
private void OnKeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
int code = e.KeyValue;
// neji jiz jednou stisknuta ta sama klavesa ?
if (code != AKT_KEY)
{
milisec = ((int) (DateTime.Now - DT).TotalMilliseconds);
Data[Data_IDX] = (short) AKT_KEY;
Data[Data_IDX+1] = (short) milisec;
Data_IDX += 2;
// dosad kod zmackle klavesy
AKT_KEY = code;
DT = DateTime.Now;
// hraj ton
Play(true, code);
}
else
{
this.label2.Text += "|";
}
}
private void OnKeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
{
int code = e.KeyValue;
if (AKT_KEY == code)
{
milisec = ((int) (DateTime.Now - DT).TotalMilliseconds);
Data[Data_IDX] = (short) AKT_KEY;
Data[Data_IDX+1] = (short) milisec;
Data_IDX += 2;
Play(false, code);
AKT_KEY = 0;
DT = DateTime.Now;
this.label2.Text = "";
}
else
{
this.label2.Text = "";
}
}
private void Play(bool Hrat, int code)
{
if (Hrat)
{
// ziskej index do pole nami definovanych tonu
int IDX = BinSearch(code);
// nalezen ton odpovidaji kodu klavesy ?
if (IDX != -1)
{
TON ton = (TON) NOTY[IDX];
string FILE = "tony" + ton.fileName;@
INFO.Text = "TÓN: " + ton.noteName + "
Pořadí: " + ton.id + "
Frekvence: " + ton.Hz + " Hz";
Nota.Location = new System.Drawing.Point(85, 71 - ton.id * 4);
Nota.Visible = true;
PlaySound(FILE, 0, (int) (SND.SND_ASYNC | SND.SND_FILENAME | SND.SND_LOOP)).ToString();
}
else
{
INFO.Text = "tón nedefinován" + code;
Nota.Visible = false;
}
}
else
{
PlaySound(null, 0, (int) (SND.SND_ASYNC | SND.SND_FILENAME | SND.SND_LOOP)).ToString();
Nota.Visible = false;
}
}
private int BinSearch(int find)
{
// algoritmus binarniho vyhledavani
int s;
int a, b;
int code;
// nejnizsi index pole-1
a = -1;
// nejvyssi index pole+1
b = NOTY.Length;
while (a != b)
{
// v "s" bude stred dosavadniho intervalu
s = (b - a) / 2 + a;
code = ((TON) NOTY[s]).keyCode;
// porovnavame prostredni prvek s hledanou hodnotou
if (code == find)
{
return s;
}
// dale se rozhodujeme kterou z "delenych pulek" se vydame v dalsich krocich
if (code > find)
{
b = s;
}
else
{
a = s;
}
// nezbytna podminka k ukonceni (pokud by zustali indexy blizko sebe napr 4 a 5 celociselnym delenim by jsme nenasli prostredek pole)
if (a+1 == b) a++;
}
// prvek nenalezen
return -1;
}
private void KONEC_Click(object sender, System.EventArgs e)
{
Play(false, 0);
Application.Exit();
}
private void bNew_Click(object sender, System.EventArgs e)
{
Data_IDX = 0;
AKT_KEY = 0;
DT = DateTime.Now;
}
private void bLoad_Click(object sender, System.EventArgs e)
{
if (SKLADBY.SelectedIndex != -1)
{
string F = SKLADBY.SelectedIndex.ToString();
FileStream fs = File.Open("music"+ F +".sav", FileMode.Open);
Data_IDX = (short) (fs.Length / 2);
for (int i=0; i<Data_IDX; i++)
{
Data[i] = (short) (fs.ReadByte() | (fs.ReadByte() << 8));
}
fs.Close();
}
short code;
short time;
for (int i=0; i<Data_IDX; i+=2)
{
code = Data[i];
time = Data[i+1];
Play(true, code);
Thread.Sleep(time);
}
Play(false, 0);
}
private void bSave_Click(object sender, System.EventArgs e)
{
string F = SKLADBY.SelectedIndex.ToString();
FileStream fs = File.Create("music"+ F +".sav");
for (int i=0; i<Data_IDX; i++)
{
fs.WriteByte((byte) (Data[i] & 0xff));
fs.WriteByte((byte) (Data[i] >> 8));
}
fs.Close();
}
}
}
Tak dneska to bylo obtížné, ale snažil jsem se, popsat to co nejsrozumitelněji. Určitě je někde v programu chyba :-), takže to neberte za hotovou aplikaci ale spíše jako nakopnutí a pokračování. Využití programu je poněkud sporadické snad jenom pro začátečníky (v hudbě) nebo pro toho kdo nevydrží nehrát ani minutu a na cestě vlakem může místo pozorování přírody vytáhnou notesa a cvičit skladby. Přece jenom klasické klávesy mají více kláves než klávesnice počítače, více barev tonů (tony flétny, kytary, trumpety, ...) takže náhrada asi nehrozí, třebas se ale program někomu z Vás podaří dotáhnou dál...
zdrojové soubory
Užívejte vánoc a života vůbec.
Zdraví Michael