diff --git a/.editorconfig b/.editorconfig index 014564d..942f43b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,6 +2,7 @@ root = true [*] charset = utf-8 +end_of_line = lf indent_style = space indent_size = 4 insert_final_newline = true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..9710ffe --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,101 @@ +name: Build + +on: + push: + branches: + - master + pull_request: + release: + types: + - created + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + include: + - os: ubuntu-latest + dotnet-platform: linux-x64 + - os: windows-latest + dotnet-platform: win-x64 + - os: macos-latest + dotnet-platform: osx-x64 + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '5.0.x' + - name: Install dependencies + run: dotnet restore + - name: Build + run: dotnet build + + build-release: + if: github.event_name == 'release' + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + include: + - os: ubuntu-latest + dotnet-platform: linux-x64 + - os: windows-latest + dotnet-platform: win-x64 + - os: macos-latest + dotnet-platform: osx-x64 + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: '5.0.x' + - name: Build self-contained binary + run: dotnet publish -r ${{ matrix.dotnet-platform }} -p:PublishSingleFile=true,IncludeNativeLibrariesForSelfExtract=true --self-contained true + - name: Upload artifact + uses: actions/upload-artifact@v2 + with: + name: artifact_${{ matrix.dotnet-platform }} + path: | + ./**/${{ matrix.dotnet-platform }}/publish/* + !./**/*.pdb + if-no-files-found: error + + + publish-release: + needs: build-release + if: github.event_name == 'release' + runs-on: ubuntu-latest + strategy: + matrix: + dotnet-platform: [linux-x64, win-x64, osx-x64] + + steps: + - name: Download Artifact + uses: actions/download-artifact@v2 + with: + name: artifact_${{ matrix.dotnet-platform }} + path: artifact + - name: Get release + id: get_release + uses: bruceadams/get-release@v1.2.2 + env: + GITHUB_TOKEN: ${{ github.token }} + - name: Find artifact + id: find_artifact + run: echo "::set-output name=artifact_path::$(find artifact -type f)" + - name: Create zip archive + run: zip -j guitarprotomidi_${{ matrix.dotnet-platform }}.zip ${{ steps.find_artifact.outputs.artifact_path }} + - name: Upload release asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_path: guitarprotomidi_${{ matrix.dotnet-platform }}.zip + asset_name: guitarprotomidi_${{ matrix.dotnet-platform }}.zip + asset_content_type: application/zip diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9192ecc --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +bin/ +obj/ +.vscode/ diff --git a/GuitarProToMidi.sln b/GuitarProToMidi.sln new file mode 100644 index 0000000..6231dd8 --- /dev/null +++ b/GuitarProToMidi.sln @@ -0,0 +1,39 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26124.0 +MinimumVisualStudioVersion = 15.0.26124.0 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F27A2087-5E55-4AE6-AC9E-D3A1703E5FF3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GuitarProToMidi", "src\GuitarProToMidi.Console\GuitarProToMidi.csproj", "{CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Debug|x64.ActiveCfg = Debug|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Debug|x64.Build.0 = Debug|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Debug|x86.ActiveCfg = Debug|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Debug|x86.Build.0 = Debug|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Release|Any CPU.Build.0 = Release|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Release|x64.ActiveCfg = Release|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Release|x64.Build.0 = Release|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Release|x86.ActiveCfg = Release|Any CPU + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {CBC4E1FC-BCD4-41D4-B3CB-38B8EF75C34C} = {F27A2087-5E55-4AE6-AC9E-D3A1703E5FF3} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index b0c2c63..22d3434 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,9 @@ # GuitarPro-to-Midi -Simply import the unitypackage into your Unity program and run the only scene "call_gp_decoder". -Click on the button to open any GuitarPro file. -A file "output.mid" will be created in the home folder of your Unity project. +GuitarPro-to-Midi is a cross-platform command line application for converting GuitarPro files to MIDI files. -(The cs-files in this repo are just for quick access and needn't be downloaded.) +## Features -Features: - Reading GuitarPro 3 - 5 Files (based on the open python pyGuitarPro project) - Reading GuitarPro 6 Files (using a simple bitwise compression and an xml structure with dictionary and ids) - Reading GuitarPro 7 Files (packed like a normal zip-file and using a very large xml structure) @@ -26,3 +23,19 @@ Features: (I must mention that GuitarPro's native Midi export lacks far behind in this functionality!) Please enjoy and create some great software with this! + +## Getting started + +You can download a self-contained binary for Windows, MacOS and Linux in the release +section of this repository on github. + +Usage: `GuitarProToMidi path/to/GuitarProFile.gp` + +The above command should create a file `GuitarProFile.mid` in the same directory as +the input file. + +### Build and run from source + +- Install dotnet core SDK (preferably at least v5.0) +- Run `dotnet build` in the project root folder +- Run `dotnet run --project src/GuitarProToMidi.Console/GuitarProToMidi.csproj -- path/to/GuitarProFile.gp` diff --git a/gpxToMidi.unitypackage b/gpxToMidi.unitypackage deleted file mode 100644 index 26863d0..0000000 Binary files a/gpxToMidi.unitypackage and /dev/null differ diff --git a/open_gp_file.cs b/open_gp_file.cs deleted file mode 100755 index fe08278..0000000 --- a/open_gp_file.cs +++ /dev/null @@ -1,137 +0,0 @@ -using SFB; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Xml.Linq; -using UnityEngine; - - -public class open_gp_file : MonoBehaviour { - public string Title = ""; - public string FileName = ""; - public string Directory = ""; - public string Extension = ""; - public bool Multiselect = false; - - GPFile gpfile; - - public void open_file_dialog() - { - var paths = StandaloneFileBrowser.OpenFilePanel(Title, Directory, Extension, Multiselect); - if (paths.Length > 0) - { - StartCoroutine(OutputRoutine(new System.Uri(paths[0]).AbsoluteUri)); - } - } - - private IEnumerator OutputRoutine(string url) - { - var loader = new WWW(url); - yield return loader; - //Detect Version by Filename - int version = 7; - string fileEnding = Path.GetExtension(url); - if (fileEnding.Equals(".gp3")) version = 3; - if (fileEnding.Equals(".gp4")) version = 4; - if (fileEnding.Equals(".gp5")) version = 5; - if (fileEnding.Equals(".gpx")) version = 6; - if (fileEnding.Equals(".gp")) version = 7; - - - switch (version) - { - case 3: - gpfile = new GP3File(loader.bytes); - gpfile.readSong(); - break; - case 4: - gpfile = new GP4File(loader.bytes); - gpfile.readSong(); - break; - case 5: - gpfile = new GP5File(loader.bytes); - gpfile.readSong(); - - break; - case 6: - gpfile = new GP6File(loader.bytes); - gpfile.readSong(); - gpfile = gpfile.self; //Replace with transferred GP5 file - - break; - case 7: - string archiveName = url.Substring(8).Replace("%20"," "); - byte[] buffer = new byte[8200000]; - MemoryStream stream = new MemoryStream(buffer); - using (var unzip = new Unzip(archiveName)) - { - //Console.WriteLine("Listing files in the archive:"); - ListFiles(unzip); - - unzip.Extract("Content/score.gpif", stream); - stream.Position = 0; - var sr = new StreamReader(stream); - string gp7xml = sr.ReadToEnd(); - - gpfile = new GP7File(gp7xml); - gpfile.readSong(); - gpfile = gpfile.self; //Replace with transferred GP5 file - - } - break; - default: - Debug.Log("Unknown File Format"); - break; - } - Debug.Log("Done"); - - var song = new Native.NativeFormat(gpfile); - var midi = song.toMidi(); - List data = midi.createBytes(); - var dataArray = data.ToArray(); - using (var fs = new FileStream("output.mid", FileMode.OpenOrCreate, FileAccess.ReadWrite)) - { - fs.Write(dataArray, 0, dataArray.Length); - - } - //Create Example Track (delete) - /* - MidiExport.MidiExport midi = new MidiExport.MidiExport(); - midi.fileType = 2; - midi.ticksPerBeat = 960; - - MidiExport.MidiTrack track = new MidiExport.MidiTrack(); - track.messages.Add(new MidiExport.MidiMessage("track_name",new string[] { "untitled" },0)); - track.messages.Add(new MidiExport.MidiMessage("time_signature", new string[] {"4","4","24","8" }, 0)); - track.messages.Add(new MidiExport.MidiMessage("control_change", new string[] { "0","7", "100" }, 0)); - track.messages.Add(new MidiExport.MidiMessage("program_change", new string[] { "0", "48" }, 0)); - track.messages.Add(new MidiExport.MidiMessage("note_on", new string[] { "0", "55","91" }, 0)); - track.messages.Add(new MidiExport.MidiMessage("note_on", new string[] { "0", "55", "0" }, 256)); - track.messages.Add(new MidiExport.MidiMessage("end_of_track", new string[] { }, 0)); - midi.midiTracks.Add(track); - midi.createBytes(); - */ - - } - - private static void ListFiles(Unzip unzip) - { - var tab = unzip.Entries.Any(e => e.IsDirectory) ? "\t" : string.Empty; - - foreach (var entry in unzip.Entries.OrderBy(e => e.Name)) - { - if (entry.IsFile) - { - //Debug.Log(tab + entry.Name+": "+ entry.CompressedSize + " -> "+ entry.OriginalSize); - continue; - } - - //Debug.Log(entry.Name); - } - - //Debug.Log(""); - } - -} diff --git a/GP3File.cs b/src/GuitarProToMidi.Console/GP3File.cs old mode 100755 new mode 100644 similarity index 100% rename from GP3File.cs rename to src/GuitarProToMidi.Console/GP3File.cs diff --git a/GP4File.cs b/src/GuitarProToMidi.Console/GP4File.cs old mode 100755 new mode 100644 similarity index 100% rename from GP4File.cs rename to src/GuitarProToMidi.Console/GP4File.cs diff --git a/GP5File.cs b/src/GuitarProToMidi.Console/GP5File.cs old mode 100755 new mode 100644 similarity index 100% rename from GP5File.cs rename to src/GuitarProToMidi.Console/GP5File.cs diff --git a/GP6File.cs b/src/GuitarProToMidi.Console/GP6File.cs old mode 100755 new mode 100644 similarity index 98% rename from GP6File.cs rename to src/GuitarProToMidi.Console/GP6File.cs index 2e01f57..e117194 --- a/GP6File.cs +++ b/src/GuitarProToMidi.Console/GP6File.cs @@ -3,12 +3,11 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using UnityEngine; public class GP6File : GPFile { - + private byte[] udata; //uncompressed data private static List tempos = new List(); private static List chords = new List(); @@ -101,8 +100,8 @@ public static GP5File GP6NodeToGP5File(Node node) //node = GPIF tag //Page Layout Node nPageLayout = node.getSubnodeByName("Score", true).getSubnodeByName("PageSetup", true); file.pageSetup = new global::PageSetup(); - if (nPageLayout != null) { - + if (nPageLayout != null) { + file.pageSetup.pageSize = new Point(int.Parse(nPageLayout.subnodes[0].content), int.Parse(nPageLayout.subnodes[1].content)); file.pageSetup.pageMargin = new Padding(int.Parse(nPageLayout.subnodes[5].content), int.Parse(nPageLayout.subnodes[3].content), @@ -142,7 +141,7 @@ public static void transferBars(Node node, GP5File song) int barCnt = -1; foreach (Node nBar in nBars.subnodes) { - + var _bar = new Measure(); string clef = nBar.getSubnodeByName("Clef").content; if (clef.Equals("G2")) _bar.clef = MeasureClef.treble; @@ -169,7 +168,7 @@ public static void transferBars(Node node, GP5File song) if (nSimileMark.content.Equals("SecondOfDouble")) _bar.simileMark = SimileMark.secondOfDouble; } _bar.voices = new List(); - + foreach (string voice in voices) { if (int.Parse(voice) >= 0) _bar.voices.Add(transferVoice(node, int.Parse(voice), _bar)); @@ -177,7 +176,7 @@ public static void transferBars(Node node, GP5File song) song.tracks[(cnt - 1) % song.trackCount].addMeasure(_bar); } - + } private static int flipDuration(Duration d) @@ -248,7 +247,7 @@ public static Beat transferBeat(Node node, int index, Voice voice) { if (tempo.bar == currentMeasure && tempo.transferred == false) { - + if ((float)lengthPassed / totalLength > tempo.position) { //Place tempo value @@ -258,7 +257,7 @@ public static Beat transferBeat(Node node, int index, Voice voice) if (tempo.tempoType == 4) myTempo *= 2.0f; if (tempo.tempoType == 5) myTempo *= 3.0f; - + beat.effect.mixTableChange.tempo = new MixTableItem((int)myTempo, 0, true); tempo.transferred = true; } @@ -306,7 +305,7 @@ public static Beat transferBeat(Node node, int index, Voice voice) beat.effect.fadeIn = nBeat.getSubnodeByName("Fadding", true) != null && nBeat.getSubnodeByName("Fadding", true).content.Equals("FadeIn"); beat.effect.fadeOut = nBeat.getSubnodeByName("Fadding", true) != null && nBeat.getSubnodeByName("Fadding", true).content.Equals("FadeOut"); beat.effect.volumeSwell = nBeat.getSubnodeByName("Fadding", true) != null && nBeat.getSubnodeByName("Fadding", true).content.Equals("VolumeSwell"); - + if (nBeat.getSubnodeByName("FreeText",true) != null) { beat.text = new BeatText(nBeat.getSubnodeByName("FreeText", true).content); @@ -357,7 +356,7 @@ public static Beat transferBeat(Node node, int index, Voice voice) beat.effect.stroke.direction = bsd; searchBrushParams = true; //search in Xproperty } - + else if (nProperty.propertyValues[0].Equals("PickStroke")) { string direction = nProperty.subnodes[0].content; @@ -391,7 +390,7 @@ public static Beat transferBeat(Node node, int index, Voice voice) beat.effect.tremoloBar = new BendEffect(); beat.effect.tremoloBar.type = BendType.none; //Not defined in GP6 beat.effect.tremoloBar.points = new List(); - + beat.effect.tremoloBar.points.Add(new BendPoint(0.0f, whammyBarOriginValue)); beat.effect.tremoloBar.points.Add(new BendPoint(whammyBarOriginOffset, whammyBarOriginValue)); //Peak or Valley @@ -407,7 +406,7 @@ public static Beat transferBeat(Node node, int index, Voice voice) Node nWhammy = nBeat.getSubnodeByName("Whammy", true); if (nWhammy != null) - { + { beat.effect.tremoloBar = new BendEffect(); beat.effect.tremoloBar.type = BendType.none; //Not defined in GP6 beat.effect.tremoloBar.points = new List(); @@ -481,13 +480,13 @@ public static Beat transferBeat(Node node, int index, Voice voice) bool tapping; beat.notes.Add(transferNote(node, int.Parse(note), beat,velocity, graceEffect, tremolo, out tapping)); if (tapping) beat.effect.slapEffect = SlapEffect.tapping; - + } return beat; } - + public static Note transferNote(Node node, int index, Beat beat, int velocity, GraceEffect graceEffect, string tremolo, out bool tapping) { @@ -573,7 +572,7 @@ public static Note transferNote(Node node, int index, Beat beat, int velocity, G if ((flags >> 5) % 2 == 1) note.effect.slides.Add(SlideType.intoFromAbove); if ((flags >> 6) % 2 == 1) note.effect.slides.Add(SlideType.pickScrapeOutDownwards); if ((flags >> 7) % 2 == 1) note.effect.slides.Add(SlideType.pickScrapeOutUpwards); - + } else if (nProperty.propertyValues[0].Equals("LeftHandTapped")) { @@ -587,7 +586,7 @@ public static Note transferNote(Node node, int index, Beat beat, int velocity, G { tapping = true; } - + } if (hasBendEffect) @@ -615,12 +614,12 @@ public static Note transferNote(Node node, int index, Beat beat, int velocity, G if (harmonicFret != -1) { - + if (harmonicType.Equals("Natural") || harmonicType.Equals("")) { note.effect.harmonic = new NaturalHarmonic(); //Ignore the complicated GP3-5 settings - } else if (harmonicType.Equals("Artificial")) //There should be during playback a function that reads only fret and type and creates the harmonic + for GP3-5 files that transfers the old format - note.effect.harmonic = new ArtificialHarmonic(); + } else if (harmonicType.Equals("Artificial")) //There should be during playback a function that reads only fret and type and creates the harmonic + for GP3-5 files that transfers the old format + note.effect.harmonic = new ArtificialHarmonic(); else if (harmonicType.Equals("Pinch")) note.effect.harmonic = new PinchHarmonic(); else if (harmonicType.Equals("Tap")) note.effect.harmonic = new TappedHarmonic(); else if (harmonicType.Equals("Semi")) note.effect.harmonic = new SemiHarmonic(); @@ -635,7 +634,7 @@ public static Note transferNote(Node node, int index, Beat beat, int velocity, G int midiValue = getGP6DrumValue(element, variation); note.value = midiValue; note.str = 1; - } + } } @@ -658,7 +657,7 @@ public static Note transferNote(Node node, int index, Beat beat, int velocity, G int secondNote = int.Parse(nTrill.content); note.effect.trill = new TrillEffect(); note.effect.trill.fret = secondNote; - note.effect.trill.duration = new Duration(trillLength); + note.effect.trill.duration = new Duration(trillLength); } Node nVibrato = nNote.getSubnodeByName("Vibrato"); @@ -685,7 +684,7 @@ public static Note transferNote(Node node, int index, Beat beat, int velocity, G if (nNote.getSubnodeByName("Tie") != null && nNote.getSubnodeByName("Tie").propertyValues[1].Equals("true")) note.type = NoteType.tie; - if (!tremolo.Equals("")) { + if (!tremolo.Equals("")) { note.effect.tremoloPicking = new TremoloPickingEffect(); //1/2 = 8th, 1/4 = 16ths, 1/8 = 32nds note.effect.tremoloPicking.duration = new Duration(); @@ -794,7 +793,7 @@ public static List readRhythms(Node nRhythms) return ret_val; } - + public static List transferTracks(Node nTracks, GP5File song) { List ret_val = new List(); @@ -830,7 +829,7 @@ public static List transferTracks(Node nTracks, GP5File song) } _track.strings = new List(); - + Node nProperties = nTrack.getSubnodeByName("Properties"); if (nProperties != null) { @@ -845,7 +844,7 @@ public static List transferTracks(Node nTracks, GP5File song) } } } - if (nProperties != null) { + if (nProperties != null) { Node nCapoFret = nProperties.getSubnodeByProperty("name", "CapoFret"); Node nFretCount = nProperties.getSubnodeByProperty("name", "FretCount"); if (nCapoFret != null) _track.offset = int.Parse(nCapoFret.subnodes[0].content); @@ -860,9 +859,9 @@ public static List transferTracks(Node nTracks, GP5File song) } } _track.isPercussionTrack = _track.channel.channel == 9; - + _track.settings = new TrackSettings(); - + Node nPlaybackState = nTrack.getSubnodeByName("PlaybackState"); if (nPlaybackState != null) { @@ -877,7 +876,7 @@ public static List transferTracks(Node nTracks, GP5File song) return ret_val; } - + public static List transferMeasureHeaders(Node nMasterBars, GP5File song) { var ret_val = new List(); @@ -896,8 +895,8 @@ public static List transferMeasureHeaders(Node nMasterBars, GP5Fi _measureHeader.repeatClose = 0; if (nMasterBar.getSubnodeByName("Repeat", true) != null && nMasterBar.getSubnodeByName("Repeat", true).propertyValues[1].Equals("true")) _measureHeader.repeatClose = int.Parse(nMasterBar.getSubnodeByName("Repeat", true).propertyValues[2]); - - if (nMasterBar.getSubnodeByName("AlternateEndings", true) != null) { + + if (nMasterBar.getSubnodeByName("AlternateEndings", true) != null) { var _aes = nMasterBar.getSubnodeByName("AlternateEndings", true).content.Split(' '); foreach (string _ in _aes) { @@ -911,7 +910,7 @@ public static List transferMeasureHeaders(Node nMasterBars, GP5Fi _measureHeader.timeSignature.denominator = new Duration(); _measureHeader.timeSignature.denominator.value = int.Parse(timeSig[1]); - _measureHeader.tripletFeel = TripletFeel.none; + _measureHeader.tripletFeel = TripletFeel.none; if (nMasterBar.getSubnodeByName("TripletFeel",true) != null) { string feel = nMasterBar.getSubnodeByName("TripletFeel", true).content; @@ -933,7 +932,7 @@ public static List transferMeasureHeaders(Node nMasterBars, GP5Fi //Do I really need these: //_measureHeader.marker ? repeatGroup ? //_measureHeader.start ? realStart ? - //_measureHeader.tempo - useless, as the real tempo is saved as MixTableChange on the BeatEffect + //_measureHeader.tempo - useless, as the real tempo is saved as MixTableChange on the BeatEffect ret_val.Add(_measureHeader); } @@ -943,7 +942,7 @@ public static List transferMeasureHeaders(Node nMasterBars, GP5Fi public static List transferDirections(Node nDirections) { - + List ret_val = new List(); if (nDirections == null) return ret_val; foreach (Node nElement in nDirections.subnodes) @@ -954,7 +953,7 @@ public static List transferDirections(Node nDirections) } public static List transferFromDirections(Node nDirections) { - + List ret_val = new List(); if (nDirections == null) return ret_val; foreach (Node nElement in nDirections.subnodes) @@ -966,7 +965,7 @@ public static List transferFromDirections(Node nDirections) public static List transferLyrics(Node nTracks) { - + List ret_val = new List(); if (nTracks == null) return ret_val; foreach (Node nTrack in nTracks.subnodes) @@ -993,7 +992,7 @@ public static Node ParseGP6(string xml, int start) for (int x = 0; x < xml.Length-3; x++) { string sub = xml.Substring(x, 3); - + if (sub.Equals("")) skipMode = false; @@ -1149,7 +1148,7 @@ public class Node public List propertyValues = new List(); public string content; - + public Node(List subnodes, List propertyNames, List propertyValues, string name = "", string content="") { @@ -1183,7 +1182,7 @@ public Node getSubnodeByName(string name, bool directOnly = false) if (n.name.Equals(name)) return n; } return null; - } else { + } else { foreach (Node n in subnodes) { Node sub = n.getSubnodeByName(name); @@ -1486,7 +1485,7 @@ public byte GetByte() static private int[] powers = new int[] { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 }; public int GetBitsLE(int amount) - { //returns the number represented by the next n bits, starting with the least significant bit + { //returns the number represented by the next n bits, starting with the least significant bit int ret_val = 0; for (int x = 0; x < amount; x++) @@ -1498,7 +1497,7 @@ public int GetBitsLE(int amount) } public int GetBitsBE(int amount) - { //returns the number represented by the next n bits, starting with the most significant bit + { //returns the number represented by the next n bits, starting with the most significant bit int ret_val = 0; for (int x = 0; x < amount; x++) diff --git a/GP7File.cs b/src/GuitarProToMidi.Console/GP7File.cs old mode 100755 new mode 100644 similarity index 84% rename from GP7File.cs rename to src/GuitarProToMidi.Console/GP7File.cs index e72f393..8246c06 --- a/GP7File.cs +++ b/src/GuitarProToMidi.Console/GP7File.cs @@ -1,6 +1,4 @@ -using System.Collections; -using System.Collections.Generic; -using UnityEngine; +using System.Collections.Generic; public class GP7File : GPFile { @@ -21,6 +19,4 @@ public override void readSong() self = gp5file; self.versionTuple = new int[] { 7, 0 }; } - - } diff --git a/GPBase.cs b/src/GuitarProToMidi.Console/GPBase.cs old mode 100755 new mode 100644 similarity index 99% rename from GPBase.cs rename to src/GuitarProToMidi.Console/GPBase.cs index dd69e30..6a4c232 --- a/GPBase.cs +++ b/src/GuitarProToMidi.Console/GPBase.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using UnityEngine; abstract public class GPFile { @@ -27,7 +26,7 @@ abstract public class GPFile public string copyright ; public string tab_author ; public string instructional ; - public int[] versionTuple = new int[] { }; + public int[] versionTuple = new int[] { }; public string version = ""; public List lyrics = new List(); public List measureHeaders = new List(); @@ -152,13 +151,12 @@ public static string readIntByteSizeString() public static byte[] extract(int start, int length, bool advance_pointer) { if (length <= 0) - { + { return new byte[Math.Max(0, length)]; } if (length + start > data.Length) { return new byte[Math.Max(0, length)]; - return null; } byte[] ret = new byte[length]; @@ -274,7 +272,7 @@ public bool isDefault() pickStroke == def.pickStroke && fadeIn == def.fadeIn && vibrato == def.vibrato && tremoloBar == def.tremoloBar && slapEffect == def.slapEffect); - + } } @@ -288,7 +286,7 @@ public BeatStroke() { } public BeatStroke(BeatStrokeDirection d, int v, float s) { direction = d; - value = v; + value = v; startTime = s; } @@ -387,7 +385,7 @@ public BendPoint(int position = 0, int value = 0, bool vibrato = false) public BendPoint(float position, float value, bool isGP6Format = true) { - if (isGP6Format) { + if (isGP6Format) { //GP6 Format: position: 0-100, value: 100 = 1 whole tone up this.position = (int)(position * BendEffect.maxPosition / 100); this.value = (int)(value*2*BendEffect.semitoneLength / 100); @@ -398,7 +396,7 @@ public BendPoint(float position, float value, bool isGP6Format = true) this.position = (int)position; this.value = (int)value; GP6position = position * 100.0f / BendEffect.maxPosition; - GP6value = value * 50.0f / BendEffect.semitoneLength; + GP6value = value * 50.0f / BendEffect.semitoneLength; } } @@ -995,7 +993,7 @@ public PitchClass(int arg0i = 0, int arg1i = -1, string arg0s = "", string inton else { value = arg0i % 12; - + str = _notes_sharp[Math.Max(value,0)]; if (intonation.Equals("flat")) str = _notes_flat[value]; } @@ -1391,4 +1389,4 @@ public enum Octave public enum VoiceDirection { none = 0, up = 1, down = 2 -} \ No newline at end of file +} diff --git a/src/GuitarProToMidi.Console/GPFileParser.cs b/src/GuitarProToMidi.Console/GPFileParser.cs new file mode 100644 index 0000000..a841687 --- /dev/null +++ b/src/GuitarProToMidi.Console/GPFileParser.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.IO; +using NLog; + +namespace GuitarProToMidi +{ + public class GpFileParser { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + + private readonly string _title; + private readonly string _filePath; + private readonly string _extension; + private GPFile _gpfile; + + public GpFileParser(string filePath) + { + _title = Path.GetFileNameWithoutExtension(filePath); + _filePath = filePath; + _extension = Path.GetExtension(filePath); + } + + public void CreateMidiFile() + { + var loader = File.ReadAllBytes(_filePath); + //Detect Version by Filename + int version = 7; + string fileEnding = _extension; + if (fileEnding.Equals(".gp3")) version = 3; + if (fileEnding.Equals(".gp4")) version = 4; + if (fileEnding.Equals(".gp5")) version = 5; + if (fileEnding.Equals(".gpx")) version = 6; + if (fileEnding.Equals(".gp")) version = 7; + + switch (version) + { + case 3: + _gpfile = new GP3File(loader); + _gpfile.readSong(); + break; + case 4: + _gpfile = new GP4File(loader); + _gpfile.readSong(); + break; + case 5: + _gpfile = new GP5File(loader); + _gpfile.readSong(); + break; + case 6: + _gpfile = new GP6File(loader); + _gpfile.readSong(); + _gpfile = _gpfile.self; //Replace with transferred GP5 file + break; + case 7: + byte[] buffer = new byte[8200000]; + MemoryStream stream = new MemoryStream(buffer); + using (var unzip = new Unzip(_filePath)) + { + unzip.Extract("Content/score.gpif", stream); + stream.Position = 0; + var sr = new StreamReader(stream); + string gp7xml = sr.ReadToEnd(); + + _gpfile = new GP7File(gp7xml); + _gpfile.readSong(); + _gpfile = _gpfile.self; //Replace with transferred GP5 file + + } + break; + default: + Logger.Error("Unknown File Format"); + break; + } + Logger.Debug("Done"); + + var song = new NativeFormat(_gpfile); + var midi = song.toMidi(); + List data = midi.createBytes(); + var dataArray = data.ToArray(); + using (var fs = new FileStream(Path.Join(Path.GetDirectoryName(_filePath), $"{_title}.mid"), FileMode.OpenOrCreate, FileAccess.Write)) + { + fs.Write(dataArray, 0, dataArray.Length); + } + } + } +} diff --git a/src/GuitarProToMidi.Console/GuitarProToMidi.csproj b/src/GuitarProToMidi.Console/GuitarProToMidi.csproj new file mode 100644 index 0000000..61834f5 --- /dev/null +++ b/src/GuitarProToMidi.Console/GuitarProToMidi.csproj @@ -0,0 +1,14 @@ + + + Exe + netcoreapp5.0 + GuitarProToMidi + true + true + link + false + + + + + diff --git a/MidiExport.cs b/src/GuitarProToMidi.Console/MidiExport.cs old mode 100755 new mode 100644 similarity index 98% rename from MidiExport.cs rename to src/GuitarProToMidi.Console/MidiExport.cs index dc1a549..1615cd3 --- a/MidiExport.cs +++ b/src/GuitarProToMidi.Console/MidiExport.cs @@ -1,8 +1,7 @@ using System; using System.Collections.Generic; -using UnityEngine; -namespace MidiExport { +namespace MidiExport { public class MidiExport { public static System.Text.Encoding ascii = System.Text.Encoding.ASCII; @@ -133,7 +132,7 @@ public List createBytes() { List raw = new List(); raw = message.createBytes(); - + byte temp = raw[0]; if (statusByteSet && !message.is_meta && raw[0] < 0xf0 && raw[0] == runningStatusByte) { @@ -149,7 +148,7 @@ public List createBytes() } return MidiExport.writeChunk("MTrk",data); - } + } } public class MidiMessage @@ -240,7 +239,7 @@ public MidiMessage(string type, string[] args, int time, byte[] data = null) { is_meta = true; code = 0x58; numerator = int.Parse(args[0]); //4 - denominator = int.Parse(args[1]); //4 + denominator = int.Parse(args[1]); //4 clocks_per_click = int.Parse(args[2]); //24 notated_32nd_notes_per_beat = int.Parse(args[3]); //8 } @@ -327,14 +326,14 @@ public List createMetaBytes() data.InsertRange(0, MidiExport.encodeVariableInt(dataLength)); data.Insert(0, code); data.Insert(0, 0xff); - + return data; } public List createMessageBytes() { - + List data = new List(); /* if (type.Equals("note_off")) { code = 0x80; channel = int.Parse(args[0]); note = int.Parse(args[1]); velocity = int.Parse(args[2]); } if (type.Equals("note_on")) { code = 0x90; channel = int.Parse(args[0]); note = int.Parse(args[1]); velocity = int.Parse(args[2]); } @@ -344,15 +343,15 @@ public List createMessageBytes() if (type.Equals("aftertouch")) { code = 0xd0; channel = int.Parse(args[0]); value = int.Parse(args[1]); } if (type.Equals("pitchwheel")) { code = 0xe0; channel = int.Parse(args[0]); pitch = int.Parse(args[1]); } if (type.Equals("sysex")) { code = 0xf0; this.data = data; } - + */ if (type.Equals("note_off") || type.Equals("note_on")) { data.Add((byte)(code | (byte)channel)); data.Add((byte)note); data.Add((byte)velocity); } - - if (type.Equals("polytouch")) + + if (type.Equals("polytouch")) { data.Add((byte)(code | (byte)channel)); data.Add((byte)note); data.Add((byte)value); @@ -389,4 +388,4 @@ public List createMessageBytes() return data; } } -} \ No newline at end of file +} diff --git a/NativeFormat.cs b/src/GuitarProToMidi.Console/NativeFormat.cs old mode 100755 new mode 100644 similarity index 91% rename from NativeFormat.cs rename to src/GuitarProToMidi.Console/NativeFormat.cs index 4a8a79a..dca7e53 --- a/NativeFormat.cs +++ b/src/GuitarProToMidi.Console/NativeFormat.cs @@ -1,33 +1,28 @@ using System; -using System.Collections; using System.Collections.Generic; -using UnityEngine; +using NLog; -namespace Native { +namespace GuitarProToMidi { public class NativeFormat { + private readonly string _title; + private readonly string _subtitle; + private readonly string _artist; + private readonly string _album; + private readonly string _words; + private readonly string _music; + + private readonly List _annotations = new List(); + private readonly List _tempos; + private readonly List _barMaster; + private readonly List _tracks; + private readonly List _notesInMeasures = new List(); + public static readonly bool[] AvailableChannels = new bool[16]; - public string title = ""; - public string subtitle = ""; - public string artist = ""; - public string album = ""; - public string words = ""; - public string music = ""; - - public List directions = new List(); - public List annotations = new List(); - public List tempos = new List(); - public List barMaster = new List(); - public List tracks = new List(); - public List lyrics = new List(); - - - private List notesInMeasures = new List(); - public static bool[] availableChannels = new bool[16]; public MidiExport.MidiExport toMidi() { MidiExport.MidiExport mid = new MidiExport.MidiExport(); mid.midiTracks.Add(getMidiHeader()); //First, untitled track - foreach (Track track in tracks) + foreach (Track track in _tracks) { mid.midiTracks.Add(track.getMidi()); } @@ -37,23 +32,23 @@ public MidiExport.MidiExport toMidi() private MidiExport.MidiTrack getMidiHeader() { var midiHeader = new MidiExport.MidiTrack(); - //text(s) - name of song, artist etc., created by Gitaro + //text(s) - name of song, artist etc., created by Gitaro //copyright - by Gitaro - //midi port 0 + //midi port 0 //time signature //key signature //set tempo ///////marker text (will be seen in file) - also Gitaro copyright blabla //end_of_track midiHeader.messages.Add(new MidiExport.MidiMessage("track_name", new string[] { "untitled" }, 0)); - midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { title }, 0)); - midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { subtitle }, 0)); - midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { artist }, 0)); - midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { album }, 0)); - midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { words }, 0)); - midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { music }, 0)); + midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { _title }, 0)); + midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { _subtitle }, 0)); + midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { _artist }, 0)); + midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { _album }, 0)); + midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { _words }, 0)); + midiHeader.messages.Add(new MidiExport.MidiMessage("text", new string[] { _music }, 0)); midiHeader.messages.Add(new MidiExport.MidiMessage("copyright", new string[] { "Copyright 2017 by Gitaro" }, 0)); - midiHeader.messages.Add(new MidiExport.MidiMessage("marker", new string[] { title+" / "+artist+" - Copyright 2017 by Gitaro" }, 0)); + midiHeader.messages.Add(new MidiExport.MidiMessage("marker", new string[] { _title+" / "+_artist+" - Copyright 2017 by Gitaro" }, 0)); midiHeader.messages.Add(new MidiExport.MidiMessage("midi_port", new string[] { "0" }, 0)); //Get tempos from List tempos, get key_signature and time_signature from barMaster @@ -62,37 +57,37 @@ private MidiExport.MidiTrack getMidiHeader() var currentIndex = 0; var oldTimeSignature = ""; var oldKeySignature = ""; - if (tempos.Count == 0) tempos.Add(new Tempo()); - while (tempoIndex < tempos.Count || masterBarIndex < barMaster.Count) + if (_tempos.Count == 0) _tempos.Add(new Tempo()); + while (tempoIndex < _tempos.Count || masterBarIndex < _barMaster.Count) { //Compare next entry of both possible sources - if (tempoIndex == tempos.Count || tempos[tempoIndex].position >= barMaster[masterBarIndex].index) //next measure comes first + if (tempoIndex == _tempos.Count || _tempos[tempoIndex].position >= _barMaster[masterBarIndex].index) //next measure comes first { - if (!barMaster[masterBarIndex].keyBoth.Equals(oldKeySignature)) + if (!_barMaster[masterBarIndex].keyBoth.Equals(oldKeySignature)) { //Add Key-Sig to midiHeader - midiHeader.messages.Add(new MidiExport.MidiMessage("key_signature", new string[] { ""+ barMaster[masterBarIndex].key, ""+ barMaster[masterBarIndex].keyType }, barMaster[masterBarIndex].index - currentIndex)); - currentIndex = barMaster[masterBarIndex].index; + midiHeader.messages.Add(new MidiExport.MidiMessage("key_signature", new string[] { ""+ _barMaster[masterBarIndex].key, ""+ _barMaster[masterBarIndex].keyType }, _barMaster[masterBarIndex].index - currentIndex)); + currentIndex = _barMaster[masterBarIndex].index; - oldKeySignature = barMaster[masterBarIndex].keyBoth; + oldKeySignature = _barMaster[masterBarIndex].keyBoth; } - if (!barMaster[masterBarIndex].time.Equals(oldTimeSignature)) + if (!_barMaster[masterBarIndex].time.Equals(oldTimeSignature)) { //Add Time-Sig to midiHeader - midiHeader.messages.Add(new MidiExport.MidiMessage("time_signature", new string[] { "" + barMaster[masterBarIndex].num, "" + barMaster[masterBarIndex].den, "24", "8" }, barMaster[masterBarIndex].index - currentIndex)); - currentIndex = barMaster[masterBarIndex].index; + midiHeader.messages.Add(new MidiExport.MidiMessage("time_signature", new string[] { "" + _barMaster[masterBarIndex].num, "" + _barMaster[masterBarIndex].den, "24", "8" }, _barMaster[masterBarIndex].index - currentIndex)); + currentIndex = _barMaster[masterBarIndex].index; - oldTimeSignature = barMaster[masterBarIndex].time; + oldTimeSignature = _barMaster[masterBarIndex].time; } masterBarIndex++; } else //next tempo signature comes first { //Add Tempo-Sig to midiHeader - int _tempo = (int)(Math.Round((60 * 1000000) / tempos[tempoIndex].value)); - midiHeader.messages.Add(new MidiExport.MidiMessage("set_tempo", new string[] { "" + _tempo }, tempos[tempoIndex].position - currentIndex)); - currentIndex = tempos[tempoIndex].position; + int _tempo = (int)(Math.Round((60 * 1000000) / _tempos[tempoIndex].value)); + midiHeader.messages.Add(new MidiExport.MidiMessage("set_tempo", new string[] { "" + _tempo }, _tempos[tempoIndex].position - currentIndex)); + currentIndex = _tempos[tempoIndex].position; tempoIndex++; } } @@ -108,27 +103,25 @@ private MidiExport.MidiTrack getMidiHeader() public NativeFormat(GPFile fromFile) { - title = fromFile.title; - subtitle = fromFile.subtitle; - artist = fromFile.interpret; - album = fromFile.album; - words = fromFile.words; - music = fromFile.music; - tempos = retrieveTempos(fromFile); - directions = fromFile.directions; - barMaster = retrieveMasterBars(fromFile); - tracks = retrieveTracks(fromFile); - lyrics = fromFile.lyrics; + _title = fromFile.title; + _subtitle = fromFile.subtitle; + _artist = fromFile.interpret; + _album = fromFile.album; + _words = fromFile.words; + _music = fromFile.music; + _tempos = retrieveTempos(fromFile); + _barMaster = retrieveMasterBars(fromFile); + _tracks = retrieveTracks(fromFile); updateAvailableChannels(); } private void updateAvailableChannels() { - - for (int x = 0; x < 16; x++) { if (x != 9) { availableChannels[x] = true; } else { availableChannels[x] = false; } } - foreach (Track track in tracks) + + for (int x = 0; x < 16; x++) { if (x != 9) { AvailableChannels[x] = true; } else { AvailableChannels[x] = false; } } + foreach (Track track in _tracks) { - availableChannels[track.channel] = false; + AvailableChannels[track.channel] = false; } } @@ -191,7 +184,7 @@ public List getBendPoints(int index, int duration, BendEffect bend) return ret; } - + public List retrieveNotes(global::Track track, int[] tuning, Track myTrack) { @@ -213,13 +206,13 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack int notesInMeasure = 0; foreach (Measure m in track.measures) { - + notesInMeasure = 0; measureIndex++; bool skipVoice = false; if (m.simileMark == SimileMark.simple) //Repeat last measure { - int amountNotes = notesInMeasures[notesInMeasures.Count-1]; //misuse prohibited by guitarpro + int amountNotes = _notesInMeasures[_notesInMeasures.Count-1]; //misuse prohibited by guitarpro int endPoint = notes.Count; for (int x = endPoint - amountNotes; x < endPoint; x++) { @@ -233,8 +226,8 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack } if (m.simileMark == SimileMark.firstOfDouble || m.simileMark == SimileMark.secondOfDouble) //Repeat first or second of last two measures { - int secondAmount = notesInMeasures[notesInMeasures.Count - 1]; //misuse prohibited by guitarpro - int firstAmount = notesInMeasures[notesInMeasures.Count - 2]; + int secondAmount = _notesInMeasures[_notesInMeasures.Count - 1]; //misuse prohibited by guitarpro + int firstAmount = _notesInMeasures[_notesInMeasures.Count - 2]; int endPoint = notes.Count-secondAmount; for (int x = endPoint - firstAmount; x < endPoint; x++) { @@ -248,15 +241,15 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack } skipVoice = true; } - + foreach (Voice v in m.voices) { if (skipVoice) break; int subIndex = 0; foreach (Beat b in v.beats) { - - if (b.text != null && !b.text.value.Equals("")) annotations.Add(new Annotation(b.text.value,index+subIndex)); + + if (b.text != null && !b.text.value.Equals("")) _annotations.Add(new Annotation(b.text.value,index+subIndex)); if (b.effect.tremoloBar != null) addToTremoloBarList(index + subIndex, flipDuration(b.duration), b.effect.tremoloBar, myTrack); @@ -277,8 +270,8 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack temp.value = b.effect.stroke.value; int brushTotalDuration = flipDuration(temp); int beatTotalDuration = flipDuration(b.duration); - - + + brushIncrease = brushTotalDuration / (notesCnt); int startPos = index + subIndex + (int)((brushTotalDuration-brushIncrease) * (b.effect.stroke.startTime - 1)); int endPos = startPos + brushTotalDuration - brushIncrease; @@ -309,7 +302,7 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack note.isRHTapped = b.effect.slapEffect == SlapEffect.tapping; note.index = index + subIndex; note.duration = flipDuration(b.duration); - + //Note values note.fret = n.value; @@ -319,7 +312,7 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack note.isPalmMuted = n.effect.palmMute; note.isMuted = n.type == NoteType.dead; - if (n.effect.harmonic != null) { + if (n.effect.harmonic != null) { note.harmonicFret = n.effect.harmonic.fret; if (n.effect.harmonic.fret == 0) //older format.. { @@ -338,7 +331,7 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack break; } } - if (n.effect.slides != null) { + if (n.effect.slides != null) { foreach (SlideType sl in n.effect.slides) { note.slidesToNext = note.slidesToNext || sl == SlideType.shiftSlideTo || sl == SlideType.legatoSlideTo; @@ -352,37 +345,37 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack if (n.effect.bend != null) note.bendPoints = getBendPoints(index + subIndex, flipDuration(b.duration), n.effect.bend); //Ties - - bool dontAddNote = false; - + + bool dontAddNote = false; + if (n.type == NoteType.tie) { - - + + dontAddNote = true; //Find if note can simply be added to previous note - + var last = last_notes[Math.Max(0,note.str-1)]; - - - + + + if (last != null) { note.fret = last.fret; //For GP3 & GP4 if (last.harmonic != note.harmonic || last.harmonicFret != note.harmonicFret ) dontAddNote = false; - + if (dontAddNote) { note.connect = true; last.duration += note.duration; last.addBendPoints(note.bendPoints); - + } } - + } else // not a tie { - + last_was_tie[Math.Max(0, note.str - 1)] = false; } @@ -390,9 +383,9 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack //Triplet Feel - if (!barMaster[measureIndex].tripletFeel.Equals("none")) + if (!_barMaster[measureIndex].tripletFeel.Equals("none")) { - TripletFeel trip = barMaster[measureIndex].tripletFeel; + TripletFeel trip = _barMaster[measureIndex].tripletFeel; //Check if at regular 8th or 16th beat position bool is_8th_pos = subIndex % 480 == 0; bool is_16th_pos = subIndex % 240 == 0; @@ -507,13 +500,13 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack } else { graceNote.index -= graceNote.duration; - + } notes.Add(graceNote); //TODO: insert at correct position! notesInMeasure++; - - } else { + + } else { if (isOnBeat) // shorten next note @@ -523,10 +516,10 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack } else //Change previous note { if ( notes.Count > 0) - { + { note.index -= note.duration; //Can lead to negative indices. Midi should handle that subtractSubindex = note.duration; - + } } @@ -559,7 +552,7 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack } if (n.effect.accentuatedNote) note.velocity = (int)(note.velocity * 1.2f); if (n.effect.heavyAccentuatedNote) note.velocity = (int)(note.velocity * 1.4f); - + //Arpeggio / Brush if (hasBrush) { @@ -578,7 +571,7 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack } if (rememberedGrace) { subIndex -= graceLength; rememberGrace = false; rememberedGrace = false; } //After the change in duration for the second beat has been done - + subIndex -= subtractSubindex; subtractSubindex = 0; subIndex += flipDuration(b.duration); @@ -596,7 +589,7 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack for (int x = notes.Count - notesCnt; x < notes.Count; x++) { notes[x] = temp[temp.Length - (x - (notes.Count - notesCnt))-1]; - + } @@ -606,17 +599,17 @@ public List retrieveNotes(global::Track track, int[] tuning, Track myTrack break; //Consider only the first voice } int measureDuration = flipDuration(m.header.timeSignature.denominator) * m.header.timeSignature.numerator; - barMaster[measureIndex].duration = measureDuration; - barMaster[measureIndex].index = index; + _barMaster[measureIndex].duration = measureDuration; + _barMaster[measureIndex].index = index; index += measureDuration; - notesInMeasures.Add(notesInMeasure); + _notesInMeasures.Add(notesInMeasure); } - + return notes; } - + public int[] getTuning(List strings) { @@ -640,7 +633,7 @@ public List retrieveMasterBars(GPFile file) mb.num = mh.timeSignature.numerator; mb.den = mh.timeSignature.denominator.value; string keyFull = ""+(int)mh.keySignature; - if (!(keyFull.Length == 1)) { + if (!(keyFull.Length == 1)) { mb.keyType = int.Parse(keyFull.Substring(keyFull.Length - 1)); mb.key = int.Parse(keyFull.Substring(0,keyFull.Length-1)); } @@ -652,7 +645,7 @@ public List retrieveMasterBars(GPFile file) mb.keyBoth = keyFull; //Useful for midiExport later mb.tripletFeel = mh.tripletFeel; - + masterBars.Add(mb); } @@ -663,8 +656,8 @@ public List retrieveTempos(GPFile file) { List tempos = new List(); //Version < 4 -> look at Measure Headers, >= 4 look at mixtablechanges - - + + int version = file.versionTuple[0]; if (version < 4) //Look at MeasureHeaders { @@ -677,7 +670,7 @@ public List retrieveTempos(GPFile file) int pos = 0; float oldTempo = file.tempo; foreach (MeasureHeader mh in file.measureHeaders) - { + { Tempo t = new Tempo(); t.value = mh.tempo.value; t.position = pos; @@ -697,7 +690,7 @@ public List retrieveTempos(GPFile file) if (init.value != 0) tempos.Add(init); foreach (Measure m in file.tracks[0].measures) { - int smallPos = 0; //inner measure position + int smallPos = 0; //inner measure position if (m.voices.Count == 0) continue; foreach (Beat b in m.voices[0].beats){ @@ -747,7 +740,7 @@ private static int flipDuration(Duration d) int enters = d.tuplet.enters; int times = d.tuplet.times; - + //3:2 = standard triplet, 3 notes in the time of 2 result = (int)((result*times)/(float)enters); @@ -861,6 +854,8 @@ public enum HarmonicType public class Track { + private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); + public string name = ""; public int patch = 0; public int port = 0; @@ -880,7 +875,7 @@ public MidiExport.MidiTrack getMidi() midiTrack.messages.Add(new MidiExport.MidiMessage("track_name", new string[] { name }, 0)); midiTrack.messages.Add(new MidiExport.MidiMessage("program_change", new string[] { ""+channel,""+patch }, 0)); - + List noteOffs = new List(); List channelConnections = new List(); //For bending and trembar: [original Channel, artificial Channel, index at when to delete artificial] List activeBendingPlans = new List(); @@ -898,7 +893,7 @@ public MidiExport.MidiTrack getMidi() { noteOffs.Sort((x, y) => x[0].CompareTo(y[0])); - + //Check for active bendings in progress List currentBPs = findAndSortCurrentBendPoints(activeBendingPlans, n.index); @@ -925,7 +920,7 @@ public MidiExport.MidiTrack getMidi() //Check if there are active tremPoints to be adjusted for List _newTremPoints = new List(); - + foreach (TremoloPoint tp in tremoloPoints) { if (tp.index <= bp.index) //between last and this note, a note off event should occur @@ -994,7 +989,7 @@ public MidiExport.MidiTrack getMidi() } channelConnections = newChannelConnections; - NativeFormat.availableChannels[bpl.usedChannel] = true; + NativeFormat.AvailableChannels[bpl.usedChannel] = true; } } @@ -1011,7 +1006,7 @@ public MidiExport.MidiTrack getMidi() { var value = tp.value * 25.6f; value =Math.Min( Math.Max(value,-8192),8191); - foreach (int ch in activeChannels) { + foreach (int ch in activeChannels) { midiTrack.messages.Add( new MidiExport.MidiMessage("pitchwheel", new string[] { "" + ch, "" + (int)(value) }, tp.index - currentIndex)); @@ -1031,7 +1026,7 @@ public MidiExport.MidiTrack getMidi() foreach (int[] vc in volumeChanges) { if (vc[0] <= n.index) //between last and this note, a volume change event should occur - { + { foreach (int ch in activeChannels) { @@ -1055,7 +1050,7 @@ public MidiExport.MidiTrack getMidi() if (noteOff[0] <= n.index) //between last and this note, a note off event should occur { midiTrack.messages.Add( - new MidiExport.MidiMessage("note_off", + new MidiExport.MidiMessage("note_off", new string[] { "" + noteOff[2], "" + noteOff[1], "0" }, noteOff[0] - currentIndex)); currentIndex = noteOff[0]; } else @@ -1069,9 +1064,9 @@ public MidiExport.MidiTrack getMidi() int note; if (n.str == -2) break; //Last round - - if (n.str-1 < 0) Debug.Log("String was -1"); - if (n.str-1 >= tuning.Length && tuning.Length != 0) Debug.Log("String was higher than string amount (" + n.str + ")"); + + if (n.str - 1 < 0) Logger.Debug("String was -1"); + if (n.str-1 >= tuning.Length && tuning.Length != 0) Logger.Debug("String was higher than string amount (" + n.str + ")"); if (tuning.Length > 0) note = tuning[n.str - 1] + capo + n.fret; else { @@ -1089,7 +1084,7 @@ public MidiExport.MidiTrack getMidi() { int usedChannel = tryToFindChannel(); if (usedChannel == -1) usedChannel = channel; - NativeFormat.availableChannels[usedChannel] = false; + NativeFormat.AvailableChannels[usedChannel] = false; channelConnections.Add(new int[] {channel,usedChannel,n.index + n.duration }); midiTrack.messages.Add(new MidiExport.MidiMessage("program_change", new string[] { ""+usedChannel, ""+patch}, n.index - currentIndex)); noteChannel = usedChannel; @@ -1107,7 +1102,7 @@ public MidiExport.MidiTrack getMidi() if (n.fading != Fading.none) //Fading { volumeChanges = createVolumeChanges(n.index, n.duration, n.velocity, n.fading); - } + } midiTrack.messages.Add(new MidiExport.MidiMessage("note_on", new string[] { "" + noteChannel, "" + note, "" + n.velocity }, n.index - currentIndex)); currentIndex = n.index; @@ -1125,9 +1120,9 @@ public MidiExport.MidiTrack getMidi() noteOffs.Add(new int[] {n.index + n.duration, note , noteChannel}); } - - + + midiTrack.messages.Add(new MidiExport.MidiMessage("end_of_track", new string[] { }, 0)); return midiTrack; @@ -1141,7 +1136,7 @@ private List addSlidesToNotes(List notes) { index++; bool skipWrite = false; - + if ((n.slideInFromBelow && n.str > 1) || n.slideInFromAbove) { int myFret = n.fret; @@ -1230,7 +1225,7 @@ private List createVolumeChanges(int index, int duration, int velocity, F changes.Add(new int[] {x,Math.Min(127,Math.Max(0,val)) }); val += step; } - + } if (fading == Fading.volumeSwell) @@ -1240,7 +1235,7 @@ private List createVolumeChanges(int index, int duration, int velocity, F int times = 0; for (int x = index; x < index + duration; x += (duration / segments)) { - + changes.Add(new int[] { x, Math.Min(127, Math.Max(0, val)) }); val += step; if (times == segments/2) step = -step; @@ -1267,7 +1262,7 @@ private List getActiveChannels(List channelConnections) public int tryToFindChannel() { int cnt = 0; - foreach (bool available in NativeFormat.availableChannels) + foreach (bool available in NativeFormat.AvailableChannels) { if (available) return cnt; cnt++; @@ -1303,11 +1298,11 @@ public int getHarmonic(int baseTone, int fret, int capo, float harmonicFret, Har if (harmonicFret == 19f) val += 0; if (harmonicFret == 21.7f) val += 12; if (harmonicFret == 24f) val += 0; - + return Math.Min(val,127); } - + public List findAndSortCurrentBendPoints(List activeBendingPlans, int index) { List bps = new List(); @@ -1403,7 +1398,7 @@ public BendingPlan createBendingPlan(List bendPoints, int originalCha { if (isVibrato) bp.value += vibrato; bendingPoints.Add(bp); - + } old_pos = bp.index; old_value = bp.value; @@ -1434,13 +1429,13 @@ public BendingPlan(int originalChannel, int usedChannel, List bending //this.positions = positions; this.originalChannel = originalChannel; this.usedChannel = usedChannel; - + } } public class MasterBar { - + public string time = "4/4"; public int num = 4; public int den = 4; @@ -1464,4 +1459,4 @@ public class Tempo public int position = 0; //total position in song @ 960 ticks_per_beat } -} \ No newline at end of file +} diff --git a/src/GuitarProToMidi.Console/Program.cs b/src/GuitarProToMidi.Console/Program.cs new file mode 100644 index 0000000..412d2fd --- /dev/null +++ b/src/GuitarProToMidi.Console/Program.cs @@ -0,0 +1,30 @@ +using NLog; +using NLog.Config; +using NLog.Targets; + +namespace GuitarProToMidi +{ + class Program + { + public static void Main(string[] args) + { + ConfigureLogging(LogLevel.Info); + + var gpFile = new GpFileParser(args[0]); + + gpFile.CreateMidiFile(); + } + + private static void ConfigureLogging(LogLevel logLevel) + { + var config = new LoggingConfiguration(); + var logconsole = new ConsoleTarget + { + Name = "console", + Layout = "${message}" + }; + config.AddRule(logLevel, LogLevel.Fatal, logconsole); + LogManager.Configuration = config; + } + } +} diff --git a/Unzip.cs b/src/GuitarProToMidi.Console/Unzip.cs old mode 100755 new mode 100644 similarity index 100% rename from Unzip.cs rename to src/GuitarProToMidi.Console/Unzip.cs diff --git a/test.gp b/test.gp new file mode 100644 index 0000000..41b7aed Binary files /dev/null and b/test.gp differ diff --git a/test.mid b/test.mid new file mode 100644 index 0000000..dedc17f Binary files /dev/null and b/test.mid differ