diff --git a/.gitignore b/.gitignore index 28c249d..36b5970 100644 --- a/.gitignore +++ b/.gitignore @@ -9,14 +9,26 @@ *.user *.userosscache *.sln.docstates +.rmv/ +MidiParser/bin/Debug +MidiParser/bin/Old + +# Backups +*.csbak +*.bak + +# WIP cs (Not ready for build) +*.cswip # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results -#[Dd]ebug/ +[Dd]ebug/ +Debug/netcoreapp3.1/ +*.dev.json #[Dd]ebugPublic/ -[Rr]elease/ +#[Rr]elease/ [Rr]eleases/ x64/ x86/ @@ -27,6 +39,9 @@ x86/ [Oo]bj/ [Ll]og/ +# Shortcuts +#*.lnk + # Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot @@ -165,7 +180,7 @@ DocProject/Help/Html2 DocProject/Help/html # Click-Once directory -publish/ +# publish/ # Publish Web Output *.[Pp]ublish.xml diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..36c7486 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + "version": "0.2.0", + "configurations": [ + { + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "name": ".NET Core Launch (console)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.dll", + "args": [], + "cwd": "${workspaceFolder}/MidiParser", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..51ebb1f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "[rocket-mvbasic]": { + "editor.codeLens": false + }, + "files.associations": { + "**/.rmv/config/*.json": "jsonc" + }, + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + "**/.rmv/catalog/**": true, + "*/_*": false + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..09ee73f --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/MidiParser/MidiParser.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/MidiParser/MidiParser.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "--project", + "${workspaceFolder}/MidiParser/MidiParser.csproj" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/MidiParser.exe - Latest.lnk b/MidiParser.exe - Latest.lnk new file mode 100644 index 0000000..b6a79c1 Binary files /dev/null and b/MidiParser.exe - Latest.lnk differ diff --git a/MidiParser/AranaraN.cs b/MidiParser/AranaraN.cs new file mode 100644 index 0000000..24ec90a --- /dev/null +++ b/MidiParser/AranaraN.cs @@ -0,0 +1,75 @@ +using NAudio.Midi; +using System; +using System.Collections.Generic; +using System.Text; + +namespace MidiParser +{ + class AranaraN + { + private string ahex_note; + public string hex_note {get => ahex_note; private set => ahex_note = value;} + private string ahex_vel; + public string hex_vel {get => ahex_vel; private set => ahex_vel = value;} + private string ahex_ch; + public string hex_ch {get => ahex_ch; private set => ahex_ch = value;} + private string ahex_value; + public string hex_value {get => ahex_value; private set => ahex_value = value;} + private string ahex_time; + public string hex_time {get => ahex_time; private set => ahex_time = value;} + private string ahex_len; + public string hex_len {get => ahex_len; private set => ahex_len = value;} + private string ahex_type; + public string hex_type {get => ahex_type; private set => ahex_type = value;} + + public AranaraN (string htype, int hnote, int hvel, int hch, double htime, double hlen, int htpqn) + { + switch (htype){ //Len parameter for function AranaraN used for values for non-note events. + case "TR": //Track + hex_type = "F"; + hex_note = ""; //Unused Parameter for Track Headers + hex_vel = ""; + hex_ch = ""; + hex_value = Convert.ToInt32(hlen).ToString("X") + "|"; + hex_time = ""; + hex_len = ""; + break; + + case "TE": //Tempo + hex_type = "E"; + hex_note = ""; //Unused Parameter for Tempo Events + hex_vel = ""; + hex_ch = ""; + hex_value = Convert.ToInt32(hlen).ToString("X") + "|"; + hex_time = Convert.ToInt32(Math.Round(htime*htpqn,0)).ToString("X") + "|"; + hex_len = ""; + break; + + case "PC": //Program Change + hex_type = "D"; + hex_note = hnote.ToString("X2"); //Patch Value (Conveniently same range as note pitch) + hex_vel = ""; + hex_ch = hch.ToString("X"); + hex_value = ""; + hex_time = Convert.ToInt32(Math.Round(htime*htpqn,0)).ToString("X") + "|"; + hex_len = ""; + break; + + default: //Assume Note + hex_type = ""; + hex_note = hnote.ToString("X2"); + hex_vel = hvel.ToString("X2"); + hex_ch = hch.ToString("X"); + hex_value = ""; //Unused Parameter for Notes + hex_time = Convert.ToInt32(Math.Round(htime*htpqn,0)).ToString("X") + "|"; + hex_len = Convert.ToInt32(Math.Round(hlen*htpqn,0)).ToString("X") + "|"; + break; + } + } + + public static double ToSeconds (long time, TempoEvent lastTempoEvent, int ticksPerQuarterNote) + { + return (double)(((double)(time - lastTempoEvent.AbsoluteTime) / ticksPerQuarterNote) * lastTempoEvent.MicrosecondsPerQuarterNote + lastTempoEvent.AbsoluteTime) / 1000000; + } + } +} \ No newline at end of file diff --git a/MidiParser/MidiParser.csproj b/MidiParser/MidiParser.csproj index 1b45e44..a967a6f 100644 --- a/MidiParser/MidiParser.csproj +++ b/MidiParser/MidiParser.csproj @@ -1,12 +1,10 @@ - Exe netcoreapp3.1 - - + + - - + \ No newline at end of file diff --git a/MidiParser/Note.cs b/MidiParser/Note.cs deleted file mode 100644 index cc90f7f..0000000 --- a/MidiParser/Note.cs +++ /dev/null @@ -1,32 +0,0 @@ -using NAudio.Midi; -using System; -using System.Collections.Generic; -using System.Text; - -namespace MidiParser -{ - class Note - { - private int _notePitch; - public int NotePitch { get => _notePitch; private set => _notePitch = value; } - private double _timeStart; - public double TimeStart { get => _timeStart; private set => _timeStart = value; } - private double _length; - public double Length { get => _length; private set => _length = value; } - private int _velocity; - public int Velocity { get => _velocity; private set => _velocity = value; } - - public Note (int note, double timeStart, double length, int velocity) - { - NotePitch = note; - TimeStart = Math.Round(timeStart, 4); - Length = Math.Round(length, 4); - Velocity = velocity; - } - - public static double ToSeconds (long time, TempoEvent lastTempoEvent, int ticksPerQuarterNote) - { - return (double)(((double)(time - lastTempoEvent.AbsoluteTime) / ticksPerQuarterNote) * lastTempoEvent.MicrosecondsPerQuarterNote + lastTempoEvent.AbsoluteTime) / 1000000; - } - } -} diff --git a/MidiParser/Program.cs b/MidiParser/Program.cs index 925923f..5e46bcd 100644 --- a/MidiParser/Program.cs +++ b/MidiParser/Program.cs @@ -1,8 +1,10 @@ using NAudio.Midi; using System; using System.Collections.Generic; +using System.ComponentModel; using System.IO; using System.Linq; +using System.Runtime.InteropServices; using System.Text; //using System.Windows.Forms; using System.Threading; @@ -34,9 +36,14 @@ static void Main(string[] args) } break; default: + Console.WriteLine("Batch Convert function is partially supported."); + //Console.ReadLine(); ConvertAllFiles(args); break; } + //Post Operation. + Console.WriteLine("Finished process.\nIf this window does not close automatically, press enter to end."); + //Console.ReadLine(); //Leaving this here for the option to have the window not close automatically. } catch (Exception e) { @@ -45,16 +52,15 @@ static void Main(string[] args) } } + //An Attempt to make Batch Conversion Possible private static void ConvertAllFiles (string[] files) { - StringBuilder sb = new StringBuilder(); + int count = 1; foreach (string file in files) { - sb.AppendLine(ConvertFile(file)); + ConvertFile(file); + Console.WriteLine($"Finished file {count++}"); } - sb.Remove(sb.Length - 1, 1); - - File.WriteAllText(Path.Combine(Path.GetDirectoryName(files[0]), "__AllSongs.txt"), sb.ToString()); } private static string ConvertFile (string file) @@ -63,7 +69,7 @@ private static string ConvertFile (string file) if (File.Exists(file)) { string path = file; - string output = Path.Combine(Path.GetDirectoryName(path), "_" + Path.GetFileNameWithoutExtension(path) + " - Converted.txt"); + string output = Path.Combine(Path.GetDirectoryName(path), "!" + Path.GetFileNameWithoutExtension(path) + ".aramidi"); MidiFile mid; //If an error is thrown, notify user and continue looping @@ -71,9 +77,12 @@ private static string ConvertFile (string file) { mid = new MidiFile(path); int ticksPerQuarterNote = mid.DeltaTicksPerQuarterNote; - + //MIDI TPQ is 384, though is processed as TPH (Ticks per half-note) + int prctmp = 120; //Formula using 120 BPM: TPQ * (prctmp/60) + int outTPQ = 384 * (prctmp/60); //768 TPH + List tempoEvents = new List(); - tempoEvents.Add(new TempoEvent(500000, 0)); + tempoEvents.Add(new TempoEvent(60000000/prctmp, 0)); //Assumes Tempo is 120 BPM, converts to seconds at 120 BPM MidiEvent[][] midiEvents = new MidiEvent[mid.Events.Count()][]; @@ -82,7 +91,7 @@ private static string ConvertFile (string file) midiEvents[i] = mid.Events.ElementAt(i).ToArray(); } - List notes = new List(); + List notes = new List(); foreach (MidiEvent m in midiEvents[0]) { @@ -100,13 +109,19 @@ private static string ConvertFile (string file) } } + //Create a list containing default Expression, Volume, and Sustain values. 18 Entries in case. + int[] MIDIVol = Enumerable.Repeat(127, 16).ToArray(); + int[] MIDIExp = Enumerable.Repeat(127, 16).ToArray(); + //Iterate through every note press in the file for (int i = 0; i < midiEvents.Length; i++) { - //Skip drums - if (i == 9) - { - continue; + //Track Header + notes.Add(new AranaraN("TR",0,0,0,0,i,outTPQ)); + + //Assume no tempo events in track 0. Default tempo is 120 (0x07A120 ms per beat) + if (i == 0){ + notes.Add(new AranaraN("TE",0,0,0,0,500000,outTPQ)); } int currentTempoIndex = 0; @@ -126,19 +141,62 @@ private static string ConvertFile (string file) } } - //Only if a note press - if (midiEvent.CommandCode == MidiCommandCode.NoteOn) + //Test: Switch Cases + double timeInSeconds; + double lengthInSeconds; + switch(midiEvent.CommandCode) { - NoteOnEvent note = midiEvent as NoteOnEvent; - - //If not an off note - if (note.Velocity != 0) - { - double timeInSeconds = Note.ToSeconds(note.AbsoluteTime, tempoEvents[currentTempoIndex], ticksPerQuarterNote); - double lengthInSeconds = Note.ToSeconds(note.NoteLength, tempoEvents[currentTempoIndex], ticksPerQuarterNote); - //Add this note - notes.Add(new Note(note.NoteNumber, timeInSeconds, lengthInSeconds, note.Velocity)); - } + case MidiCommandCode.ControlChange: + ControlChangeEvent midicc = midiEvent as ControlChangeEvent; + int MIDICCRawValue = short.Parse(midicc.ControllerValue.ToString()); + int MIDICCChannel = short.Parse((midicc.Channel%16).ToString()); + switch(midicc.Controller.ToString()) + { + case "MainVolume": + MIDIVol[MIDICCChannel] = MIDICCRawValue; + break; + case "Expression": + MIDIExp[MIDICCChannel] = MIDICCRawValue; + break; + default: + break; + } + break; + case MidiCommandCode.PatchChange: + PatchChangeEvent midipc = midiEvent as PatchChangeEvent; + int MIDIPCRaw = ((short)midipc.Patch); + + + timeInSeconds = AranaraN.ToSeconds(midipc.AbsoluteTime, tempoEvents[currentTempoIndex], ticksPerQuarterNote); + lengthInSeconds = AranaraN.ToSeconds(0, tempoEvents[currentTempoIndex], ticksPerQuarterNote); //Not quite needed + + //Add this instrument change + notes.Add(new AranaraN("PC",MIDIPCRaw,0,midipc.Channel%16,timeInSeconds,0,outTPQ)); + break; + case MidiCommandCode.NoteOn: + NoteOnEvent note = midiEvent as NoteOnEvent; + + //If not an off note + if (note.Velocity != 0) + { + timeInSeconds = AranaraN.ToSeconds(note.AbsoluteTime, tempoEvents[currentTempoIndex], ticksPerQuarterNote); + lengthInSeconds = AranaraN.ToSeconds(note.NoteLength, tempoEvents[currentTempoIndex], ticksPerQuarterNote); + + //Add this note + notes.Add(new AranaraN("N",note.NoteNumber,Convert.ToInt32(note.Velocity * (MIDIVol[note.Channel%16] * MIDIExp[note.Channel%16]) / 16129),note.Channel%16,timeInSeconds,lengthInSeconds,outTPQ)); + } + break; + default: + //Tempo Event Detection + if (midiEvent is TempoEvent) + { + TempoEvent tempo = midiEvent as TempoEvent; + timeInSeconds = AranaraN.ToSeconds(tempo.AbsoluteTime, tempoEvents[currentTempoIndex], ticksPerQuarterNote); + lengthInSeconds = AranaraN.ToSeconds(0, tempoEvents[currentTempoIndex], ticksPerQuarterNote); + //Add Tempo Event + notes.Add(new AranaraN("TE",0,0,0,timeInSeconds,60000000/tempo.Tempo,outTPQ)); + } + break; } } catch (Exception e) @@ -148,31 +206,27 @@ private static string ConvertFile (string file) } } - //Sort by time in ascending order - Note[] sortedNotes = notes.OrderBy(o => o.TimeStart).ToArray(); + //Aranara MIDI File Parser + AranaraN[] events = notes.ToArray(); - char separateChar = '\\'; - //String to write to the text file - //Start with the file name without commas as the song title - StringBuilder info = new StringBuilder($"{Path.GetFileNameWithoutExtension(path).Replace(separateChar.ToString(), "")}{separateChar}1{separateChar}"); + char separateChar = '|'; //Only used for initialising file header. + string header = "[Aranara]█"; + StringBuilder info = new StringBuilder($"{header}{(Path.GetFileNameWithoutExtension(path) + $"{":"}{outTPQ}").Replace(separateChar.ToString(), "")}{separateChar}"); + - foreach (Note n in sortedNotes) + foreach (AranaraN n in events) { - //Every 4 items contains all the info for a note - info.Append(n.TimeStart.ToString()).Append(separateChar); - info.Append(n.NotePitch.ToString()).Append(separateChar); - info.Append(n.Length.ToString()).Append(separateChar); - info.Append(n.Velocity.ToString()).Append(separateChar); + info.Append(n.hex_type); + info.Append(n.hex_note); + info.Append(n.hex_vel); + info.Append(n.hex_ch); + info.Append(n.hex_value); + info.Append(n.hex_time); + info.Append(n.hex_len); } - - //Remove end comma - info.Remove(info.Length - 1, 1); - //Write to a text file File.WriteAllText(output, info.ToString()); - - //Console.WriteLine("\n\n****************\n\n" + info.ToString()); - //Clipboard.SetText(info.ToString()); + return info.ToString(); } catch (Exception e) @@ -189,4 +243,4 @@ private static string ConvertFile (string file) return ""; } } -} +} \ No newline at end of file diff --git a/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.dll b/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.dll index cc820f2..a8a551a 100644 Binary files a/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.dll and b/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.dll differ diff --git a/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.exe b/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.exe index 4d52042..184dc7c 100644 Binary files a/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.exe and b/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.exe differ diff --git a/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.runtimeconfig.dev.json b/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.runtimeconfig.dev.json deleted file mode 100644 index 607adfd..0000000 --- a/MidiParser/bin/Debug/netcoreapp3.1/MidiParser.runtimeconfig.dev.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "runtimeOptions": { - "additionalProbingPaths": [ - "C:\\Users\\Flynn\\.dotnet\\store\\|arch|\\|tfm|", - "C:\\Users\\Flynn\\.nuget\\packages", - "C:\\Microsoft\\Xamarin\\NuGet" - ] - } -} \ No newline at end of file diff --git a/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.deps.json b/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.deps.json new file mode 100644 index 0000000..0a69edf --- /dev/null +++ b/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.deps.json @@ -0,0 +1,102 @@ +{ + "runtimeTarget": { + "name": ".NETCoreApp,Version=v3.1", + "signature": "" + }, + "compilationOptions": {}, + "targets": { + ".NETCoreApp,Version=v3.1": { + "MidiParser/1.0.0": { + "dependencies": { + "NAudio": "1.10.0" + }, + "runtime": { + "MidiParser.dll": {} + } + }, + "Microsoft.NETCore.Platforms/3.1.0": {}, + "Microsoft.Win32.Registry/4.7.0": { + "dependencies": { + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" + } + }, + "NAudio/1.10.0": { + "dependencies": { + "Microsoft.Win32.Registry": "4.7.0", + "System.Resources.Extensions": "4.7.0" + }, + "runtime": { + "lib/netcoreapp3.0/NAudio.dll": { + "assemblyVersion": "1.10.0.0", + "fileVersion": "1.10.0.0" + } + } + }, + "System.Resources.Extensions/4.7.0": { + "runtime": { + "lib/netstandard2.0/System.Resources.Extensions.dll": { + "assemblyVersion": "4.0.1.0", + "fileVersion": "4.700.19.56404" + } + } + }, + "System.Security.AccessControl/4.7.0": { + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" + } + }, + "System.Security.Principal.Windows/4.7.0": {} + } + }, + "libraries": { + "MidiParser/1.0.0": { + "type": "project", + "serviceable": false, + "sha512": "" + }, + "Microsoft.NETCore.Platforms/3.1.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==", + "path": "microsoft.netcore.platforms/3.1.0", + "hashPath": "microsoft.netcore.platforms.3.1.0.nupkg.sha512" + }, + "Microsoft.Win32.Registry/4.7.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", + "path": "microsoft.win32.registry/4.7.0", + "hashPath": "microsoft.win32.registry.4.7.0.nupkg.sha512" + }, + "NAudio/1.10.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-y6VvwSPfJhElUllkzE2FHXHAADYFGLwMPt048KlPyuUXcq4E0VEi+4hWrFd+lBsrb+Q5DvPA9w86oRTvKRO9UQ==", + "path": "naudio/1.10.0", + "hashPath": "naudio.1.10.0.nupkg.sha512" + }, + "System.Resources.Extensions/4.7.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-Efd5jls31pR1MkEl/IlT/lOzG0nGRCoYnHctZTq597+absekOXaP53yUlv5U4DfcfImSAAupWVpTW1tE54lF7A==", + "path": "system.resources.extensions/4.7.0", + "hashPath": "system.resources.extensions.4.7.0.nupkg.sha512" + }, + "System.Security.AccessControl/4.7.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", + "path": "system.security.accesscontrol/4.7.0", + "hashPath": "system.security.accesscontrol.4.7.0.nupkg.sha512" + }, + "System.Security.Principal.Windows/4.7.0": { + "type": "package", + "serviceable": true, + "sha512": "sha512-ojD0PX0XhneCsUbAZVKdb7h/70vyYMDYs85lwEI+LngEONe/17A0cFaRFqZU+sOEidcVswYWikYOQ9PPfjlbtQ==", + "path": "system.security.principal.windows/4.7.0", + "hashPath": "system.security.principal.windows.4.7.0.nupkg.sha512" + } + } +} \ No newline at end of file diff --git a/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.dll b/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.dll new file mode 100644 index 0000000..a8a551a Binary files /dev/null and b/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.dll differ diff --git a/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.exe b/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.exe new file mode 100644 index 0000000..184dc7c Binary files /dev/null and b/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.exe differ diff --git a/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.runtimeconfig.json b/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.runtimeconfig.json new file mode 100644 index 0000000..bc456d7 --- /dev/null +++ b/MidiParser/bin/Release/netcoreapp3.1/publish/MidiParser.runtimeconfig.json @@ -0,0 +1,9 @@ +{ + "runtimeOptions": { + "tfm": "netcoreapp3.1", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "3.1.0" + } + } +} \ No newline at end of file diff --git a/MidiParser/bin/Release/netcoreapp3.1/publish/NAudio.dll b/MidiParser/bin/Release/netcoreapp3.1/publish/NAudio.dll new file mode 100644 index 0000000..38dcd81 Binary files /dev/null and b/MidiParser/bin/Release/netcoreapp3.1/publish/NAudio.dll differ diff --git a/MidiParser/bin/Release/netcoreapp3.1/publish/System.Resources.Extensions.dll b/MidiParser/bin/Release/netcoreapp3.1/publish/System.Resources.Extensions.dll new file mode 100644 index 0000000..83fe498 Binary files /dev/null and b/MidiParser/bin/Release/netcoreapp3.1/publish/System.Resources.Extensions.dll differ diff --git a/README.md b/README.md index 576011d..b14de2c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,51 @@ -# MidiParser -A program to convert MIDI files into a text file to copy into this Scratch project: https://scratch.mit.edu/projects/406337184/ +# MidiParser - Modded (Aranara Revision) +A program to convert MIDI files into a text file to copy originally into this Scratch project: https://scratch.mit.edu/projects/406337184/ + +This modded variant is intended for usage for the following Scratch projects: +1. Pen-Based Aranara MIDI Players +2. Pen-Based Aranara MIDI Render Toolkit + +You can access them [here](https://daniferous.github.io/aranara-midi-player-sb3). + +## About Mod +Initially, this program intended to add support for tempo events as the original code, made by [K9ShyGuy](https://scratch.mit.edu/users/K9shyguy/), did not include tempo changes. + +After multiple iterations in the past few years, the AraMIDI format, [which you can learn more about by clicking here](https://daniferous.github.io/aranara-midi-player-sb3/versions), now includes limited support for Control Change Events (such as Volume and Expression events), and full support for Program Change Events (or Instruments). However, do know that the Instruments only involve the standard Scratch library. + +An "upgraded" version of this format, [known as the Faelei MIDI Format](https://github.com/Daniferous/MidiParser/tree/Faelei), adds full Control Change and Pitch Bend Support, by using a plug-in made by [hevean](https://scratch.mit.edu/users/hevean_3/) which allows Scratch Projects to have MIDI Synth Support. + +This program has been modded to allow tempo events to be included in the file. This will make the code incompatible with the original Scratch project made by K9ShyGuy. This converter is only usable for Aranara MIDI Player or MIDI Players using Aranara MIDI Format. ## How to use -The compiled executable can be found [here](../master/MidiParser/bin/Debug/netcoreapp3.1): +The compiled executable can be found [here](../MidiParser/bin/Release/netcoreapp3.1/publish): There's a green button in the upper left area of the page. Click on that, then click "Download ZIP" Extract the zip file, then go to MidiParser >> bin >> Debug >> netcoreapp3.1 You can either run the exe file and paste in the MIDI file path, or you can drag MIDI files onto the exe, and convert them that way. I find the 2nd method easier. -A text file will appear, and you want to open the text file and copy everything in that text file. In Scratch, create a new item in the list, and paste the text you copied into the item. And there you go! You should be able to run the program, and your song will appear. Don't forget to remix the project, so I can see too! -If you drag multiple files onto the exe file, you'll get a file called \_\_AllSongs.txt You can import this file into the \_\_songs list in the Scratch program to add all the songs you converted. +Once finished, the converted MIDI will show up as `!(name) - converted.aramidi`. This file can now be imported to any Aranara MIDI Compatible Program in two methods. + +### Method 1 (File Drag and Drop) +This is by far the easiest method and is seen in the latest versions for [Aranara MIDI Player 2.3.8](https://daniferous.github.io/aranara-midi-player-sb3/amp/Aranara%20MIDI%20Player%202.3.8.html) and [Aranara MIDI Render Toolkit](https://daniferous.github.io/aranara-midi-player-sb3/amrt/Aranara%20MIDI%20Render%20Toolkit%201.6.html). + +Simply click the green `Import` button and a file selector will open. You can then navigate to the converted AraMIDI file and open it and have it loaded. If the file selector does not show, simply drag the converted AraMIDI file to the center of the program and it will open and load the AraMIDI file. + +### Method 2 (Open File) +For older Aranara MIDI Programs, it may rely on a text prompt to import your MIDIs. This is inefficient, as loading large MIDIs would be restrictive and lead to memory issues. Additionally, it lead to the reason for developing a [MIDI Splitter Tool](https://github.com/daniferous/PythonConvertedMIDISplitter) which made "slices" of each AraMIDI. + +Before opening the MIDI Program, navigate to the converted AraMIDI file and open it as if it were a text file. Copy everything in that text file. + +Once again, click the green `Import` button and a text prompt will open, requesting you enter or paste the AraMIDI file you wish to open. You then paste everything into the dialog box then press `Enter`. + +Alternatively, you can drag and drop the converted AraMIDI file to the dialog box and then press `Enter`. + +### Batch Conversion + +This modded version supports Batch Modification. Dropping multiple files will output multiple converted files. You may need to install DotNet Core Runtime: https://dotnet.microsoft.com/download/dotnet-core/thank-you/runtime-desktop-3.1.10-windows-x64-installer + +Also, if you have any inquiries regarding this modification, feel free to contact me through my email, my [Scratch Profile](https://scratch.mit.edu/users/OjasnGamer101/), and the [Daniferousity Discord Server](https://discord.gg/kTD8y6YDjJ). + +## Credits +[FlynnD273](https://github.com/FlynnD273/MidiParser) for the original code. + +Daniferous for the tempo events, MIDI channel, Volume and Expresion Control Change implementation, and Instrument Integration. \ No newline at end of file