diff --git a/tools/midi2piano/midi2piano.sln b/tools/midi2piano/midi2piano.sln new file mode 100644 index 00000000000..7636dd66d86 --- /dev/null +++ b/tools/midi2piano/midi2piano.sln @@ -0,0 +1,30 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual C# Express 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "midi2piano", "midi2piano\midi2piano.csproj", "{68C84B61-F710-491C-BEE8-5E362C167897}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {68C84B61-F710-491C-BEE8-5E362C167897}.Debug|Any CPU.ActiveCfg = Debug|x86 + {68C84B61-F710-491C-BEE8-5E362C167897}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {68C84B61-F710-491C-BEE8-5E362C167897}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {68C84B61-F710-491C-BEE8-5E362C167897}.Debug|x86.ActiveCfg = Debug|x86 + {68C84B61-F710-491C-BEE8-5E362C167897}.Debug|x86.Build.0 = Debug|x86 + {68C84B61-F710-491C-BEE8-5E362C167897}.Release|Any CPU.ActiveCfg = Release|x86 + {68C84B61-F710-491C-BEE8-5E362C167897}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {68C84B61-F710-491C-BEE8-5E362C167897}.Release|Mixed Platforms.Build.0 = Release|x86 + {68C84B61-F710-491C-BEE8-5E362C167897}.Release|x86.ActiveCfg = Release|x86 + {68C84B61-F710-491C-BEE8-5E362C167897}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/tools/midi2piano/midi2piano.v12.suo b/tools/midi2piano/midi2piano.v12.suo new file mode 100644 index 00000000000..b1f442d7eb4 Binary files /dev/null and b/tools/midi2piano/midi2piano.v12.suo differ diff --git a/tools/midi2piano/midi2piano/Form1.Designer.cs b/tools/midi2piano/midi2piano/Form1.Designer.cs new file mode 100644 index 00000000000..bac3a206301 --- /dev/null +++ b/tools/midi2piano/midi2piano/Form1.Designer.cs @@ -0,0 +1,135 @@ +namespace midi2piano +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.OutputTxt = new System.Windows.Forms.TextBox(); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.importMIDIToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.copyToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.importDlg = new System.Windows.Forms.OpenFileDialog(); + this.halpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.menuStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // OutputTxt + // + this.OutputTxt.Dock = System.Windows.Forms.DockStyle.Fill; + this.OutputTxt.Location = new System.Drawing.Point(0, 24); + this.OutputTxt.Multiline = true; + this.OutputTxt.Name = "OutputTxt"; + this.OutputTxt.ReadOnly = true; + this.OutputTxt.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.OutputTxt.Size = new System.Drawing.Size(284, 240); + this.OutputTxt.TabIndex = 0; + // + // menuStrip1 + // + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileToolStripMenuItem, + this.copyToolStripMenuItem, + this.halpToolStripMenuItem}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(284, 24); + this.menuStrip1.TabIndex = 1; + this.menuStrip1.Text = "menuStrip1"; + // + // fileToolStripMenuItem + // + this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.importMIDIToolStripMenuItem, + this.exitToolStripMenuItem}); + this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + this.fileToolStripMenuItem.Text = "&File"; + // + // importMIDIToolStripMenuItem + // + this.importMIDIToolStripMenuItem.Name = "importMIDIToolStripMenuItem"; + this.importMIDIToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.I))); + this.importMIDIToolStripMenuItem.Size = new System.Drawing.Size(184, 22); + this.importMIDIToolStripMenuItem.Text = "&Import MIDI..."; + this.importMIDIToolStripMenuItem.Click += new System.EventHandler(this.importMIDIToolStripMenuItem_Click); + // + // exitToolStripMenuItem + // + this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + this.exitToolStripMenuItem.Size = new System.Drawing.Size(184, 22); + this.exitToolStripMenuItem.Text = "E&xit"; + this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); + // + // copyToolStripMenuItem + // + this.copyToolStripMenuItem.Name = "copyToolStripMenuItem"; + this.copyToolStripMenuItem.Size = new System.Drawing.Size(47, 20); + this.copyToolStripMenuItem.Text = "&Copy"; + this.copyToolStripMenuItem.Click += new System.EventHandler(this.copyToolStripMenuItem_Click); + // + // importDlg + // + this.importDlg.Filter = "MIDI File|*.midi;*.mid"; + // + // halpToolStripMenuItem + // + this.halpToolStripMenuItem.Name = "halpToolStripMenuItem"; + this.halpToolStripMenuItem.Size = new System.Drawing.Size(44, 20); + this.halpToolStripMenuItem.Text = "&Halp"; + this.halpToolStripMenuItem.Click += new System.EventHandler(this.halpToolStripMenuItem_Click); + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(284, 264); + this.Controls.Add(this.OutputTxt); + this.Controls.Add(this.menuStrip1); + this.MainMenuStrip = this.menuStrip1; + this.Name = "Form1"; + this.Text = "MIDI2Piano"; + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox OutputTxt; + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem importMIDIToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; + private System.Windows.Forms.OpenFileDialog importDlg; + private System.Windows.Forms.ToolStripMenuItem copyToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem halpToolStripMenuItem; + } +} \ No newline at end of file diff --git a/tools/midi2piano/midi2piano/Form1.cs b/tools/midi2piano/midi2piano/Form1.cs new file mode 100644 index 00000000000..727b61f750b --- /dev/null +++ b/tools/midi2piano/midi2piano/Form1.cs @@ -0,0 +1,298 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +using Sanford.Multimedia; +using Sanford.Multimedia.Midi; + +namespace midi2piano +{ + public partial class Form1 : Form + { + [STAThread] + public static void Main() + { + Application.EnableVisualStyles(); + Application.Run(new Form1()); + } + + public Form1() + { + InitializeComponent(); + } + + private void exitToolStripMenuItem_Click(object sender, EventArgs e) + { + Close(); + } + + struct PNote + { + public float Length; + public string Note; + + public PNote(float length, string note) + { + Length = length; + Note = note; + } + + public static readonly PNote Default = new PNote(0, ""); + } + + private void importMIDIToolStripMenuItem_Click(object sender, EventArgs e) + { + if (importDlg.ShowDialog(this) + == System.Windows.Forms.DialogResult.Cancel) + return; + + List notes = new List(); + PNote curNote = PNote.Default; + float tempo = 1; + float timeSig = 4f; + + // first, we pull midi data + Sequence s = new Sequence(importDlg.FileName); + + // quickly see if there's a piano track first + // and get the tempo as well + int piano = -1; + for (int it = 0; it < s.Count; it++) + { + Track t = s[it]; + foreach (MidiEvent me in t.Iterator()) + { + switch (me.MidiMessage.MessageType) + { + case MessageType.Channel: + { + ChannelMessage m = (ChannelMessage)me.MidiMessage; + if (m.Command == ChannelCommand.ProgramChange) + if ((GeneralMidiInstrument)m.Data1 == GeneralMidiInstrument.AcousticGrandPiano) + { + piano = it; + } + } + break; + case MessageType.Meta: + { + MetaMessage m = (MetaMessage)me.MidiMessage; + if (m.MetaType == MetaType.Tempo) + tempo = (new TempoChangeBuilder(m)).Tempo; + else if (m.MetaType == MetaType.TimeSignature) + timeSig = new TimeSignatureBuilder(m).Denominator; + } + break; + } + if (piano >= 0) + break; + } + if (piano >= 0) + break; + } + + // didn't find one, so just try 0th track anyway + if (piano == -1) + piano = 0; + + // now, pull all notes (and tempo) + // and make sure it's a channel that has content + for (int it = piano; it < s.Count; it++) + { + Track t = s[it]; + + int delta = 0; + foreach (MidiEvent me in t.Iterator()) + { + delta += me.DeltaTicks; + + switch (me.MidiMessage.MessageType) + { + case MessageType.Channel: + { + ChannelMessage m = (ChannelMessage)me.MidiMessage; + switch (m.Command) + { + case ChannelCommand.NoteOn: + if (curNote.Note != "") + { + curNote.Length = delta / 1000F; + delta = 0; + notes.Add(curNote); + } + + curNote.Note = note2Piano(m.Data1); + break; + } + } + break; + case MessageType.Meta: + { + MetaMessage m = (MetaMessage)me.MidiMessage; + if (m.MetaType == MetaType.Tempo) + tempo = (new TempoChangeBuilder(m)).Tempo; + } + break; + } + } + + // make sure we get last note + if (curNote.Note != "") + { + curNote.Length = delta / 1000F; + notes.Add(curNote); + } + + // we found a track with content! + if (notes.Count > 0) + break; + } + + // compress redundant accidentals/octaves + char[] notemods = new char[7]; + int[] noteocts = new int[7]; + for (int i = 0; i < 7; i++) + { + notemods[i] = 'n'; + noteocts[i] = 3; + } + for (int i = 0; i < notes.Count; i++) + { + string noteStr = notes[i].Note; + int cur_note = noteStr[0] - 0x41; + char mod = noteStr[1]; + int oct = int.Parse(noteStr.Substring(2)); + + noteStr = noteStr.Substring(0, 1); + if (mod != notemods[cur_note]) + { + noteStr += new string(mod, 1); + notemods[cur_note] = mod; + } + if (oct != noteocts[cur_note]) + { + noteStr += oct.ToString(); + noteocts[cur_note] = oct; + } + + notes[i] = new PNote(notes[i].Length, noteStr); + } + + // now, we find what the "beat" length should be, + // by counting numbers of times for each length, and finding statistical mode + Dictionary scores = new Dictionary(); + foreach (PNote n in notes) + { + if (n.Length != 0) + if (scores.Keys.Contains(n.Length)) + scores[n.Length]++; + else + scores.Add(n.Length, 1); + } + float winner = 1; + int score = 0; + foreach (KeyValuePair kv in scores) + { + if (kv.Value > score) + { + winner = kv.Key; + score = kv.Value; + } + } + // realign all of them to match beat length + for (int i = 0; i < notes.Count; i++) + { + notes[i] = new PNote(notes[i].Length / winner, notes[i].Note); + } + + // compress chords down + for (int i = 0; i < notes.Count; i++) + { + if (notes[i].Length == 0 && i < notes.Count - 1) + { + notes[i + 1] = new PNote(notes[i + 1].Length, notes[i].Note + "-" + notes[i + 1].Note); + notes.RemoveAt(i); + i--; + } + } + + // add in time + for (int i = 0; i < notes.Count; i++) + { + float len = notes[i].Length; + notes[i] = new PNote(len, notes[i].Note + (len != 1 ? "/" + (1 / len).ToString("0.##") : "")); + } + + // what is the bpm, anyway? + int rpm = (int)(28800000 / tempo / winner); // 60 * 1,000,000 * .48 the .48 is because note lengths for some reason midi makes the beat note be .48 long + + // now, output! + string line = ""; + string output = ""; + int lineCount = 1; + foreach (PNote n in notes) + { + if (line.Length + n.Note.Length + 1 > 51) + { + output += line.Substring(0, line.Length - 1) + "\r\n"; + line = ""; + if (lineCount == 50) + break; + lineCount++; + } + line += n.Note + ","; + } + if (line.Length > 0) + output += line.Substring(0, line.Length - 1); + OutputTxt.Text = "BPM: " + rpm.ToString() + "\r\n" + output; + OutputTxt.SelectAll(); + } + + public enum NoteNames + { + C = 0, + D = 2, + E = 4, + F = 5, + G = 7, + A = 9, + B = 11 + } + + string note2Piano(int n) + { + string name, arg, octave; + name = Enum.GetName(typeof(NoteNames), (NoteNames)(n % 12)); + if (name == null) + { + name = Enum.GetName(typeof(NoteNames), (NoteNames)((n + 1) % 12)); + arg = "b"; + } + else + { + arg = "n"; + } + octave = (n / 12 - 1).ToString(); + + return name + arg + octave; + } + + private void copyToolStripMenuItem_Click(object sender, EventArgs e) + { + OutputTxt.SelectAll(); + OutputTxt.Copy(); + } + + private void halpToolStripMenuItem_Click(object sender, EventArgs e) + { + MessageBox.Show(this, + "This program prefers MIDIs that have a single track, otherwise it picks the first piano track it finds, else the first track. Songs with odd tempos may have their BPM's calculated wrong.", + "Halp", MessageBoxButtons.OK, MessageBoxIcon.Information); + } + } +} diff --git a/tools/midi2piano/midi2piano/Form1.resx b/tools/midi2piano/midi2piano/Form1.resx new file mode 100644 index 00000000000..b76de494d02 --- /dev/null +++ b/tools/midi2piano/midi2piano/Form1.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 132, 17 + + \ No newline at end of file diff --git a/tools/midi2piano/midi2piano/Properties/AssemblyInfo.cs b/tools/midi2piano/midi2piano/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..f968b018893 --- /dev/null +++ b/tools/midi2piano/midi2piano/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("midi2piano")] +[assembly: AssemblyProduct("midi2piano")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyCopyright("Copyright © 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. Only Windows +// assemblies support COM. +[assembly: ComVisible(false)] + +// On Windows, the following GUID is for the ID of the typelib if this +// project is exposed to COM. On other platforms, it unique identifies the +// title storage container when deploying this assembly to the device. +[assembly: Guid("9752c562-edc1-40da-8fa1-619df747e0f3")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("1.0.0.0")] diff --git a/tools/midi2piano/midi2piano/Sanford.Multimedia.Midi.dll b/tools/midi2piano/midi2piano/Sanford.Multimedia.Midi.dll new file mode 100644 index 00000000000..28a707657b3 Binary files /dev/null and b/tools/midi2piano/midi2piano/Sanford.Multimedia.Midi.dll differ diff --git a/tools/midi2piano/midi2piano/bin/x86/Release/Sanford.Multimedia.Midi.dll b/tools/midi2piano/midi2piano/bin/x86/Release/Sanford.Multimedia.Midi.dll new file mode 100644 index 00000000000..28a707657b3 Binary files /dev/null and b/tools/midi2piano/midi2piano/bin/x86/Release/Sanford.Multimedia.Midi.dll differ diff --git a/tools/midi2piano/midi2piano/bin/x86/Release/midi2piano.exe b/tools/midi2piano/midi2piano/bin/x86/Release/midi2piano.exe new file mode 100644 index 00000000000..7d718cde4db Binary files /dev/null and b/tools/midi2piano/midi2piano/bin/x86/Release/midi2piano.exe differ diff --git a/tools/midi2piano/midi2piano/midi2piano.csproj b/tools/midi2piano/midi2piano/midi2piano.csproj new file mode 100644 index 00000000000..c56cf02d57b --- /dev/null +++ b/tools/midi2piano/midi2piano/midi2piano.csproj @@ -0,0 +1,83 @@ + + + + Debug + x86 + WinExe + Properties + midi2piano + midi2piano + v4.0 + Client + + + true + full + false + bin\x86\Debug + DEBUG;TRACE;WINDOWS + prompt + 4 + true + false + x86 + + + pdbonly + true + bin\x86\Release + TRACE;WINDOWS + prompt + 4 + true + false + x86 + + + + + + + False + + + False + .\Sanford.Multimedia.Midi.dll + + + False + + + + + + False + + + 4.0 + False + + + 4.0 + False + + + False + + + + + Form + + + Form1.cs + + + + + + Form1.cs + + + + \ No newline at end of file diff --git a/tools/midi2piano/midi2piano/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/tools/midi2piano/midi2piano/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 00000000000..c1f743de53a Binary files /dev/null and b/tools/midi2piano/midi2piano/obj/x86/Debug/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/tools/midi2piano/README.txt b/tools/py-midi2piano/README.txt similarity index 100% rename from tools/midi2piano/README.txt rename to tools/py-midi2piano/README.txt diff --git a/tools/midi2piano/easygui/__init__.py b/tools/py-midi2piano/easygui/__init__.py similarity index 100% rename from tools/midi2piano/easygui/__init__.py rename to tools/py-midi2piano/easygui/__init__.py diff --git a/tools/midi2piano/midi/__init__.py b/tools/py-midi2piano/midi/__init__.py similarity index 100% rename from tools/midi2piano/midi/__init__.py rename to tools/py-midi2piano/midi/__init__.py diff --git a/tools/midi2piano/midi/midi.py b/tools/py-midi2piano/midi/midi.py similarity index 100% rename from tools/midi2piano/midi/midi.py rename to tools/py-midi2piano/midi/midi.py diff --git a/tools/midi2piano/midi2piano.py b/tools/py-midi2piano/midi2piano.py similarity index 100% rename from tools/midi2piano/midi2piano.py rename to tools/py-midi2piano/midi2piano.py diff --git a/tools/midi2piano/pyperclip/__init__.py b/tools/py-midi2piano/pyperclip/__init__.py similarity index 100% rename from tools/midi2piano/pyperclip/__init__.py rename to tools/py-midi2piano/pyperclip/__init__.py diff --git a/tools/midi2piano/pyperclip/clipboards.py b/tools/py-midi2piano/pyperclip/clipboards.py similarity index 100% rename from tools/midi2piano/pyperclip/clipboards.py rename to tools/py-midi2piano/pyperclip/clipboards.py diff --git a/tools/midi2piano/pyperclip/exceptions.py b/tools/py-midi2piano/pyperclip/exceptions.py similarity index 100% rename from tools/midi2piano/pyperclip/exceptions.py rename to tools/py-midi2piano/pyperclip/exceptions.py diff --git a/tools/midi2piano/pyperclip/windows.py b/tools/py-midi2piano/pyperclip/windows.py similarity index 100% rename from tools/midi2piano/pyperclip/windows.py rename to tools/py-midi2piano/pyperclip/windows.py