From 025977397e7ed386a993b138f0a258c85de4870d Mon Sep 17 00:00:00 2001 From: vince94 <12562322+vince94@users.noreply.github.com> Date: Mon, 29 Nov 2021 14:45:34 -0500 Subject: [PATCH] WIP - try to improve note length optimization --- PK Piano/Form1.cs | 164 +++++++++++++++++++++++++-------------- PK Piano/MPTColumn.cs | 3 +- PK Piano/PK Piano.csproj | 1 + PK Piano/SongPiece.cs | 104 +++++++++++++++++++++++++ 4 files changed, 213 insertions(+), 59 deletions(-) create mode 100644 PK Piano/SongPiece.cs diff --git a/PK Piano/Form1.cs b/PK Piano/Form1.cs index a4b3d93..5ce5b8c 100644 --- a/PK Piano/Form1.cs +++ b/PK Piano/Form1.cs @@ -4,6 +4,7 @@ using System.Text; using NAudio.Wave; using NAudio.Wave.SampleProviders; +using System.Collections.Generic; namespace PK_Piano { @@ -30,8 +31,7 @@ public Form1() byte echoDelay; byte echoFeedback; byte echoFilter; - - ToneGenerator.PlaybackThing playbackThing = new ToneGenerator.PlaybackThing(); + readonly ToneGenerator.PlaybackThing playbackThing = new ToneGenerator.PlaybackThing(); private void SendNote(byte input) { @@ -730,80 +730,130 @@ private void btnMPTconvert_Click(object sender, EventArgs e) private void btnC8eraser_Click(object sender, EventArgs e) { - int originalLength = 0x06; //TODO: write something that doesn't rely on this being here and just adds all lengths up - //VALIDATION - string input = Clipboard.GetText(); + //string clipboardContents = Clipboard.GetText(); + //if (string.IsNullOrWhiteSpace(clipboardContents)) return; //only continue if there's something there + //if (clipboardContents.Contains("Not enough rows to process")) return; + + //clipboardContents = clipboardContents.Replace("[", "").Replace("]", ""); //get rid of brackets + //var input = SongPiece.StringToBytes(clipboardContents); + var input = SongPiece.StringToBytes("0C 7F A7 C8 0C A7 C8"); + var goodVersion = SongPiece.StringToBytes("18 7F A7 18 A7"); + var optimizedBytes = ProcessBytes(input); + + RunTest(optimizedBytes, goodVersion); + //Clipboard.SetText(result.ToString()); + if (sfxEnabled) sfxEquipped.Play(); + } - if (string.IsNullOrWhiteSpace(input)) return; //only continue if there's something there - if (input.Contains("Not enough rows to process")) return; + public static List ProcessBytes(List bytesInput) + { + var result = new List(); + var piece = new SongPiece(); + bool lastByteWasANote = false; + bool lastByteWasANoteLength = false; + bool lastByteWasAnEffect = false; + int effectSkip = 0; + foreach (var b in bytesInput) + { + if (effectSkip > 0) //for effects with parameters, which shouldn't be processed + { + effectSkip--; + piece.tempNoteCollection.Add(b); //add the effect, byte by byte + continue; + } - //split the contents on spaces to a string array - input = input.Replace("[", "").Replace("]", ""); //get rid of brackets + if (lastByteWasAnEffect) + { + result.AddRange(piece.ParseHex()); + piece = new SongPiece(piece.noteLength, piece.noteLengthMultiplier); //keep the old note settings! + lastByteWasAnEffect = false; + } - //Clear out any lingering note length commands in the text itself - var firstCode = Convert.ToInt32(input.Substring(0, 2), 16); //convert the first code in the clipboard to an int - if (firstCode > 0xE0) return; //E0 to FF are all effect commands - if (firstCode < 0x80) //any code less than 80 (C-1) is a note length and should be removed - { - input = input.Replace(firstCode.ToString("X2") + " ", ""); - originalLength = firstCode; //if it's not 06, it should be set appropriately so later on the right value gets put at the end of the clipboard result + if (SongPiece.IsNoteLength(b)) + { + //Skip redundant note lengths + if (b == piece.noteLength && piece.noteLengthMultiplier == 1) + continue; + + if (lastByteWasANote) + { + //Add the contents of tempNoteCollection to the result + result.AddRange(piece.ParseHex()); + lastByteWasANote = false; + piece = new SongPiece(); //clear out everything and start from scratch + } + + if (lastByteWasANoteLength) + { + piece.noteStyle = b; //It's a note style, like 7F + lastByteWasANoteLength = false; + } + else + { + piece.noteLength = b; + lastByteWasANoteLength = true; + } + + continue; + } + + if (SongPiece.IsNote(b)) + { + lastByteWasANote = true; + lastByteWasANoteLength = false; //just in case + if (b == 0xC8) + { + piece.noteLengthMultiplier++; //keep track of how many C8s there have been + continue; + } + else + { + piece.tempNoteCollection.Add(b); //add the note to the collection + } + } + + if (SongPiece.IsEffect(b)) + { + effectSkip = SongPiece.GetNumberOfParameters(b); + piece.tempNoteCollection.Add(b); + lastByteWasAnEffect = true; + } } - //Make a string array with all of the notes in it - var notes = input.Split(' '); + //Add any remaining notes to result + result.AddRange(piece.ParseHex()); + return result; + } - if (notes.Length <= 1) + public static void RunTest(List resultBytes, List goodVersionBytes) + { + //DIY unit test + string result = ""; + foreach (var b in resultBytes) { - MessageBox.Show("This looks like it's just one note. No changes necessary here!"); - return; + result += b.ToString("X2") + " "; //convert the list back into a string } - //check to see that only the first one is not C8 - for (var i = 1; i < notes.Length; i++) + string goodVersion = ""; + foreach (var b in goodVersionBytes) { - if (notes[i] != "C8") - { - MessageBox.Show("Looks like there's more than one note in here...\r\nStripping multiple notes hasn't been implemented yet.\r\n(The note in question is " + notes[i] + ")"); - return; - } + goodVersion += b.ToString("X2") + " "; //convert the list back into a string } - var count = notes.Length; - - //returns [new length, appropriate multiplier] - var newLength = EBM_Note_Data.validateNoteLength(originalLength * count); - - var message = $"Number of notes: {count}\r\n" - + "Equivalent note length: "; + result = result.Trim(); + goodVersion = goodVersion.Trim(); - if (newLength[1] != 1) - message += $"[{newLength[0]:X2}], {getWrittenNumber(newLength[1])} times."; + var message = ""; + if (result == goodVersion) + message += "*** PASS ***\r\n"; else - message += $"[{newLength[0]:X2}]"; - + message += "*** FAIL ***\r\n"; + message += "Result:\r\n" + result + "\r\nDesired output:\r\n" + goodVersion; MessageBox.Show(message); - - //set the clipboard to the new length, whatever note is at the start of what was copied, and then the original length so when you paste it in, the rest of the column doesn't go out of whack - var result = new StringBuilder(); - result.Append(newLength[0].ToString("X2")); //the length that the new - result.Append(" "); - result.Append(notes[0]); //The note at the top of the clipboard contents - result.Append(" "); - - while (newLength[1] > 1) //this should only run if - { - result.Append("C8 "); //put in however many C8s are needed to make this the same size as the original clipboard contents - newLength[1]--; - } - result.Append(originalLength.ToString("X2")); //the 06 so things don't jump around when you paste it in - - Clipboard.SetText(result.ToString()); //paste this into EBMusEd for glorious ease of use - if (sfxEnabled) sfxEquipped.Play(); } - //Text blip stuff //If the sfxEnabled boolean value is set to true, then various parts of the UI will give audio feedback. //I'm getting kind of tired of it, though, so I'm going to make it toggleable in the future. diff --git a/PK Piano/MPTColumn.cs b/PK Piano/MPTColumn.cs index 5294d09..6adf6b5 100644 --- a/PK Piano/MPTColumn.cs +++ b/PK Piano/MPTColumn.cs @@ -3,10 +3,9 @@ namespace PK_Piano { - //These classes are a very recent addition. TODO: Move more of the logic to classes like this class MPTColumn { - public const string VALID = "Valid!"; + public const string VALID = "Valid!"; //TODO: make this validation thing less janky public static string GetEBMdata(string input) { diff --git a/PK Piano/PK Piano.csproj b/PK Piano/PK Piano.csproj index c3ef854..e75571e 100644 --- a/PK Piano/PK Piano.csproj +++ b/PK Piano/PK Piano.csproj @@ -131,6 +131,7 @@ + Form1.cs diff --git a/PK Piano/SongPiece.cs b/PK Piano/SongPiece.cs new file mode 100644 index 0000000..c4bd5e2 --- /dev/null +++ b/PK Piano/SongPiece.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; + +namespace PK_Piano +{ + class SongPiece + { + public byte noteLength; + public byte noteLengthMultiplier; + public List tempNoteCollection; + public byte noteStyle; + + public SongPiece() + { + noteLength = 0xFF; + noteLengthMultiplier = 1; + tempNoteCollection = new List(); + noteStyle = 0xFF; + } + + public SongPiece(byte length, byte multiplier) + { + noteLength = length; + noteLengthMultiplier = multiplier; + tempNoteCollection = new List(); + noteStyle = 0xFF; + } + + public List ParseHex() + { + var result = new List(); + result.Add((byte)(noteLength * noteLengthMultiplier)); + + if (noteStyle != 0xFF) + result.Add(noteStyle); + + result.AddRange(tempNoteCollection); + return result; + } + + public static bool IsNoteLength(byte b) => b <= 0x7F; + public static bool IsNote(byte b) => b >= 0x80 && b <= 0xDF; + public static bool IsEffect(byte b) => b >= 0xE0; + + public static int GetNumberOfParameters(byte b) + { + switch (b) + { + case 0xE4: + case 0xEC: + case 0xF3: + case 0xF6: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return 0; //these commands have no parameters + case 0xE0: + case 0xE1: + case 0xE5: + case 0xE7: + case 0xE9: + case 0xEA: + case 0xED: + case 0xF0: + case 0xF4: + case 0xFA: + return 1; + case 0xE2: + case 0xE6: + case 0xE8: + case 0xEE: + case 0xFB: + return 2; + case 0xE3: + case 0xEB: + case 0xF1: + case 0xF2: + case 0xF5: + case 0xF7: + case 0xF8: + case 0xF9: + return 3; + case 0xEF: + throw new Exception("The [EF] command is unimplemented.\r\nPlease use the *subroutine,count syntax instead."); + } + return 0; + } + + //TODO: Switch statement for returning an int of how many parameters an effect has in it! + + public static List StringToBytes(string input) + { + var bytes = input.Split(' '); + var bytesInput = new List(); + foreach (var b in bytes) + { + bytesInput.Add(Convert.ToByte(b, 16)); + } + + return bytesInput; + } + } +}