diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 37cfc99..61f156b 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -1,53 +1,154 @@ -name: Build and Publish Artifacts +name: Build Drum Midi Remapper (Windows/macOS/Linux) on: push: - branches: [ main ] tags: - - 'v*' - -permissions: - contents: write + - 'v*.*.*' + pull_request: + branches: [ main ] jobs: - build: + version: + name: 🏷️ Generate Version runs-on: ubuntu-latest - strategy: - matrix: - runtime: [win-x64, osx-x64, osx-arm64, linux-x64] - + outputs: + version: ${{ steps.set-version.outputs.version }} steps: - - name: Checkout repository - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - - name: Setup .NET 9 SDK - uses: actions/setup-dotnet@v3 + - name: 🏷️ Generate semantic version from Git tag + id: set-version + run: | + TAG=$(git describe --tags --abbrev=0) + VERSION_BASE=${TAG#v} + COUNT=$(git rev-list --count ${TAG}..HEAD) + VERSION="$VERSION_BASE.$COUNT" + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "📦 Using version: $VERSION" + + build-windows: + name: Build Windows Desktop + CLI + runs-on: windows-latest + needs: version + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-dotnet@v4 with: - dotnet-version: '9.0.x' + dotnet-version: 8.0.x + + - name: Install MAUI workloads + run: | + dotnet workload update + dotnet workload install maui + dotnet workload install maui-windows - name: Restore dependencies run: dotnet restore - - name: Publish single-file executable for ${{ matrix.runtime }} + - name: Build Windows MSIX + run: | + dotnet publish ./src/GUI/GUI.csproj -f net8.0-windows10.0.19041.0 -c Release ` + -p:Version=${{ needs.version.outputs.version }} ` + -p:WindowsPackageType=MSIX ` + -p:GenerateAppxPackageOnBuild=true + + - name: Build CLI + run: dotnet publish ./src/CLI/CLI.csproj -c Release -r win-x64 --self-contained true -p:Version=${{ needs.version.outputs.version }} -o ./cli-publish + + - name: Package Windows MSIX + shell: pwsh run: | - dotnet publish src/CLI \ - -c Release \ - -r ${{ matrix.runtime }} \ - --self-contained true \ - /p:PublishSingleFile=true \ - /p:IncludeAllContentForSelfExtract=true \ - -o ./publish/${{ matrix.runtime }} - - - name: Upload artifact for ${{ matrix.runtime }} - uses: actions/upload-artifact@v4 + $msix = Get-ChildItem -Path ./src/GUI/bin/Release/** -Recurse -Include "*.msix" | Select-Object -First 1 + Compress-Archive -Path $msix.FullName -DestinationPath DrumMidiRemapper-Windows-${{ needs.version.outputs.version }}.zip + + - uses: actions/upload-artifact@v4 + with: + name: DrumMidiRemapper-Windows-${{ needs.version.outputs.version }} + path: | + DrumMidiRemapper-Windows-${{ needs.version.outputs.version }}.zip + ./cli-publish/** + + build-macos: + name: Build macOS Desktop + CLI + runs-on: macos-latest + needs: version + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-dotnet@v4 with: - name: DrumMidiRemapper-${{ matrix.runtime }} - path: ./publish/${{ matrix.runtime }} + dotnet-version: 8.0.x - - name: Upload Release Asset - if: startsWith(github.ref, 'refs/tags/v') - uses: softprops/action-gh-release@v2 + - uses: maxim-lobanov/setup-xcode@v1 with: - files: ./publish/${{ matrix.runtime }}/CLI* - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + xcode-version: 'latest' + + - name: Install MAUI workloads + run: | + dotnet workload update + dotnet workload install maui + dotnet workload install maui-maccatalyst + + - name: Restore dependencies + run: dotnet workload restore + + - name: Build MacCatalyst App + run: dotnet publish ./src/GUI/GUI.csproj -f net8.0-maccatalyst -c Release -p:Version=${{ needs.version.outputs.version }} + + - name: Create DMG Installer + run: | + brew install create-dmg + APP_PATH=$(find ./src/GUI/bin/Release/net8.0-maccatalyst -name "*.app" | head -n 1) + echo "Found app: $APP_PATH" + + if [[ "$APP_PATH" == *" "* ]]; then + APP_DIR=$(dirname "$APP_PATH") + APP_NEW="$APP_DIR/DrumMidiRemapper.app" + echo "Renaming app to: $APP_NEW" + mv "$APP_PATH" "$APP_NEW" + APP_PATH="$APP_NEW" + fi + + create-dmg \ + --volname "DrumMidiRemapper" \ + --window-pos 200 120 \ + --window-size 600 400 \ + --no-internet-enable \ + --skip-jenkins \ + --icon-size 100 \ + --icon "$APP_PATH" 175 190 \ + --app-drop-link 425 190 \ + "DrumMidiRemapper-${{ needs.version.outputs.version }}.dmg" \ + "$APP_PATH" + + - name: Build CLI + run: dotnet publish ./src/CLI/CLI.csproj -c Release -r osx-x64 --self-contained true -p:Version=${{ needs.version.outputs.version }} -o ./cli-publish + + - uses: actions/upload-artifact@v4 + with: + name: DrumMidiRemapper-macOS-${{ needs.version.outputs.version }} + path: | + DrumMidiRemapper-${{ needs.version.outputs.version }}.dmg + ./cli-publish/** + + # build-linux: + # name: Build Linux CLI only + # runs-on: ubuntu-latest + # needs: version + # steps: + # - uses: actions/checkout@v4 + # - uses: actions/setup-dotnet@v4 + # with: + # dotnet-version: 8.0.x + + # - name: Restore dependencies + # run: dotnet restore ./src/CLI/CLI.csproj + + # - name: Build CLI Linux + # run: dotnet publish ./src/CLI/CLI.csproj -c Release --framework net8.0 --self-contained true -p:Version=${{ needs.version.outputs.version }} -o ./cli-publish --no-restore + + # - uses: actions/upload-artifact@v4 + # with: + # name: DrumMidiRemapper-LinuxCLI-${{ needs.version.outputs.version }} + # path: ./cli-publish/** \ No newline at end of file diff --git a/.github/workflows/pr-validation.yml b/.github/workflows/pr-validation.yml deleted file mode 100644 index 5a19686..0000000 --- a/.github/workflows/pr-validation.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: PR Validation - -on: - pull_request: - -jobs: - build-and-validate: - name: Build, Test & Validate - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '9.0.x' - - - name: Restore dependencies - run: dotnet restore - - - name: Check code formatting - run: dotnet format --verify-no-changes - - - name: Build with analyzers - run: dotnet build --configuration Release -warnaserror - - - name: Run unit tests - run: dotnet test --configuration Release --verbosity normal - - - name: Validate mapping JSON files - run: | - for file in $(find ./Services/Resources/Maps -name "*.json"); do - echo "Validating $file" - jq empty "$file" - done - - - name: Check for vulnerable packages - run: dotnet list package --vulnerable \ No newline at end of file diff --git a/.gitignore b/.gitignore index 083aead..7820328 100644 --- a/.gitignore +++ b/.gitignore @@ -483,4 +483,4 @@ $RECYCLE.BIN/ # Vim temporary swap files *.swp -midis/** +**/midis/** diff --git a/.vscode/launch.json b/.vscode/launch.json index 574dcbd..e3ac9b9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,29 +1,30 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { - "name": ".NET Core Attach", - "type": "coreclr", - "request": "attach" - }, - { - "name": ".NET Core Launch (console)", + "name": "Launch CLI", "type": "coreclr", "request": "launch", - "preLaunchTask": "build", - "program": "${workspaceFolder}/src/CLI/bin/Debug/net9.0/CLI.dll", - "args": [], + "program": "${workspaceFolder}/src/CLI/bin/Debug/net8.0/CLI", "cwd": "${workspaceFolder}/src/CLI", - "console": "internalConsole", - "stopAtEntry": false + "args": [ + "GuitarPro", + "StevenSlate", + "${workspaceFolder}/midis/test.mid" + ], + "console": "integratedTerminal" }, { - "name": ".NET Core Attach", + "name": "Launch GUI (Mac Catalyst)", "type": "coreclr", - "request": "attach" + "request": "launch", + "preLaunchTask": "build GUI (Mac Catalyst)", + "program": "${workspaceFolder}/src/GUI/bin/Debug/net8.0-maccatalyst/maccatalyst-arm64/DrumMidiRemapper.app/Contents/MacOS/DrumMidiRemapper", + "cwd": "${workspaceFolder}/src/GUI", + "internalConsoleOptions": "openOnSessionStart", + "env": { + "DOTNET_ROOT": "/usr/local/share/dotnet" + } } ] } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index f99d588..a021bb2 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -2,38 +2,14 @@ "version": "2.0.0", "tasks": [ { - "label": "build", + "label": "build GUI (Mac Catalyst)", "command": "dotnet", "type": "process", "args": [ "build", - "${workspaceFolder}/drum-midi-remapper.sln", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary;ForceNoAlign" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "publish", - "command": "dotnet", - "type": "process", - "args": [ - "publish", - "${workspaceFolder}/drum-midi-remapper.sln", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary;ForceNoAlign" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "watch", - "command": "dotnet", - "type": "process", - "args": [ - "watch", - "run", - "--project", - "${workspaceFolder}/drum-midi-remapper.sln" + "${workspaceFolder}/src/GUI/GUI.csproj", + "-f", + "net8.0-maccatalyst" ], "problemMatcher": "$msCompile" } diff --git a/README.md b/README.md index 1e1673b..5174499 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Drum MIDI Remapper -A **.NET 9** cross-platform tool for remapping MIDI drum notes between different standards and custom mappings. Designed to help musicians and producers adapt MIDI drum tracks for compatibility with various drum kits, DAWs, and hardware. +A **.NET 8** cross-platform tool for remapping MIDI drum notes between different standards and custom mappings. Designed to help musicians and producers adapt MIDI drum tracks for compatibility with various drum kits, DAWs, and hardware. ![Build Status](https://img.shields.io/github/actions/workflow/status/Abstractize/drum-midi-remapper/ci.yml?branch=main) ![License](https://img.shields.io/github/license/Abstractize/drum-midi-remapper) @@ -22,7 +22,7 @@ dotnet run --project src/CLI -- GuitarPro StevenSlate midis/test.mid ## Requirements -- [.NET 9.0 SDK or newer](https://dotnet.microsoft.com/download) +- [.NET 8.0 SDK or newer](https://dotnet.microsoft.com/download) - Compatible with Windows, macOS, and Linux --- @@ -32,7 +32,7 @@ dotnet run --project src/CLI -- GuitarPro StevenSlate midis/test.mid - Remap MIDI drum notes using customizable JSON mapping files - Support for popular drum mapping standards (e.g., GuitarPro, StevenSlate, LogicPro, ProTools) - Batch processing of MIDI files via CLI -- Cross-platform support powered by .NET 9 +- Cross-platform support powered by .NET 8 - Modular architecture with Dependency Injection for easy extensibility --- @@ -112,7 +112,7 @@ dotnet run --project src/CLI -- GuitarPro StevenSlate midis/test.mid ## Troubleshooting & FAQ -- **Build errors:** Ensure .NET 9 SDK is installed and your environment is configured correctly. +- **Build errors:** Ensure .NET 8 SDK is installed and your environment is configured correctly. - **Mapping not found:** Verify spelling and that JSON mapping files exist in **Services/Resources/Maps/**. - **MIDI file issues:** Confirm your input file is a valid MIDI file and accessible. diff --git a/drum-midi-remapper.sln b/drum-midi-remapper.sln index 1f8712f..9958861 100644 --- a/drum-midi-remapper.sln +++ b/drum-midi-remapper.sln @@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Models.Tests", "tests\Model EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services.Tests", "tests\Services.Tests\Services.Tests.csproj", "{3CECB972-EFFD-422B-9109-4D0D5AB6441E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GUI", "src\GUI\GUI.csproj", "{06266691-6BB7-4ADC-9F06-0AA0553CF26D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -115,6 +117,18 @@ Global {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Release|x64.Build.0 = Release|Any CPU {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Release|x86.ActiveCfg = Release|Any CPU {3CECB972-EFFD-422B-9109-4D0D5AB6441E}.Release|x86.Build.0 = Release|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Debug|x64.ActiveCfg = Debug|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Debug|x64.Build.0 = Debug|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Debug|x86.ActiveCfg = Debug|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Debug|x86.Build.0 = Debug|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Release|Any CPU.Build.0 = Release|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Release|x64.ActiveCfg = Release|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Release|x64.Build.0 = Release|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Release|x86.ActiveCfg = Release|Any CPU + {06266691-6BB7-4ADC-9F06-0AA0553CF26D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -127,5 +141,6 @@ Global {04A8B018-1954-48B8-BAAF-E17385114845} = {0AB3BF05-4346-4AA6-1389-037BE0695223} {C3C581BD-E5DE-4CC4-B9A5-9F76602C69A4} = {0AB3BF05-4346-4AA6-1389-037BE0695223} {3CECB972-EFFD-422B-9109-4D0D5AB6441E} = {0AB3BF05-4346-4AA6-1389-037BE0695223} + {06266691-6BB7-4ADC-9F06-0AA0553CF26D} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} EndGlobalSection EndGlobal diff --git a/src/CLI/CLI.csproj b/src/CLI/CLI.csproj index 060ef51..73c3f37 100644 --- a/src/CLI/CLI.csproj +++ b/src/CLI/CLI.csproj @@ -6,9 +6,14 @@ Exe - net9.0 + net8.0 enable enable + + drummidi + DrumMidiRemapper.CLI + Drum Midi Remapper CLI + 1.0.0 - + \ No newline at end of file diff --git a/src/Managers/Contracts/ICliArgumentManager.cs b/src/CLI/Managers/Contracts/ICliArgumentManager.cs similarity index 75% rename from src/Managers/Contracts/ICliArgumentManager.cs rename to src/CLI/Managers/Contracts/ICliArgumentManager.cs index 6f9e270..afaaf8a 100644 --- a/src/Managers/Contracts/ICliArgumentManager.cs +++ b/src/CLI/Managers/Contracts/ICliArgumentManager.cs @@ -1,6 +1,6 @@ using Models; -namespace Managers.Contracts; +namespace CLI.Managers.Contracts; public interface ICliArgumentManager { diff --git a/src/Managers/Implementations/CliArgumentsManager.cs b/src/CLI/Managers/Implementation/CliArgumentsManager.cs similarity index 61% rename from src/Managers/Implementations/CliArgumentsManager.cs rename to src/CLI/Managers/Implementation/CliArgumentsManager.cs index 25d4cbf..b0cb3a0 100644 --- a/src/Managers/Implementations/CliArgumentsManager.cs +++ b/src/CLI/Managers/Implementation/CliArgumentsManager.cs @@ -1,7 +1,7 @@ -using Managers.Contracts; +using CLI.Managers.Contracts; using Models; -namespace Managers.Implementations; +namespace CLI.Managers.Implementations; public class CliArgumentManager : ICliArgumentManager { @@ -23,22 +23,10 @@ public Task Execute(string[] args) var targetArg = args[1]; var midiPath = args[2]; - if (!Enum.TryParse(sourceArg, true, out var sourceMapType)) - { - PrintAvailableMapTypes(); - throw new ArgumentException($"Invalid source map: '{sourceArg}'"); - } - - if (!Enum.TryParse(targetArg, true, out var targetMapType)) - { - PrintAvailableMapTypes(); - throw new ArgumentException($"Invalid target map: '{targetArg}'"); - } - return Task.Run(() => new RemapVariables { - SourceMapType = sourceMapType, - TargetMapType = targetMapType, + SourceMapType = sourceArg, + TargetMapType = targetArg, MidiPath = midiPath }); } @@ -46,7 +34,7 @@ public Task Execute(string[] args) private static void PrintAvailableMapTypes() { Console.WriteLine(AVAILABLE_MAPS); - foreach (string name in Enum.GetNames()) + foreach (string name in Enum.GetNames()) { Console.WriteLine($"- {name}"); } diff --git a/src/CLI/Managers/ServiceCollectionEx.cs b/src/CLI/Managers/ServiceCollectionEx.cs new file mode 100644 index 0000000..356a26b --- /dev/null +++ b/src/CLI/Managers/ServiceCollectionEx.cs @@ -0,0 +1,15 @@ +using CLI.Managers.Contracts; +using CLI.Managers.Implementations; +using Microsoft.Extensions.DependencyInjection; + +namespace CLI.Managers; + +public static class ServiceCollectionEx +{ + public static IServiceCollection AddCLIManagers(this IServiceCollection services) + { + services.AddTransient(); + + return services; + } +} \ No newline at end of file diff --git a/src/CLI/Program.cs b/src/CLI/Program.cs index ed3fa00..97465f5 100644 --- a/src/CLI/Program.cs +++ b/src/CLI/Program.cs @@ -2,6 +2,8 @@ using Managers.Contracts; using Services; using Managers; +using CLI.Managers; +using CLI.Managers.Contracts; namespace CLI; @@ -15,6 +17,7 @@ private static async Task Main(string[] args) services.AddServices(); services.AddManagers(); + services.AddCLIManagers(); ServiceProvider provider = services.BuildServiceProvider(); @@ -25,7 +28,7 @@ private static async Task Main(string[] args) { Models.RemapVariables variables = await argumentManager.Execute(args); - await manager.RemapMidi(variables); + await manager.RemapMidi(variables.SourceMapType, variables.TargetMapType, variables.MidiPath); Console.WriteLine(SUCCESS_MESSAGE); } catch (Exception ex) diff --git a/src/GUI/App.xaml b/src/GUI/App.xaml new file mode 100644 index 0000000..ba90a5f --- /dev/null +++ b/src/GUI/App.xaml @@ -0,0 +1,76 @@ + + + + + + #6366f1 + + #4f46e5 + #f8f9fa + + #ffffff + + #f9fafb + + #212529 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/GUI/App.xaml.cs b/src/GUI/App.xaml.cs new file mode 100644 index 0000000..66b01d5 --- /dev/null +++ b/src/GUI/App.xaml.cs @@ -0,0 +1,22 @@ +using GUI.Views; +using Microsoft.Maui; +using Microsoft.Maui.Controls; + +namespace GUI; + +public partial class App : Application +{ + private readonly MainPage _mainPage; + + public App(MainPage mainPage) + { + InitializeComponent(); + + _mainPage = mainPage; + } + + protected override Window CreateWindow(IActivationState? activationState) + { + return new Window(new NavigationPage(_mainPage)); + } +} \ No newline at end of file diff --git a/src/GUI/AppShell.xaml b/src/GUI/AppShell.xaml new file mode 100644 index 0000000..33d21e0 --- /dev/null +++ b/src/GUI/AppShell.xaml @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/GUI/AppShell.xaml.cs b/src/GUI/AppShell.xaml.cs new file mode 100644 index 0000000..33d4585 --- /dev/null +++ b/src/GUI/AppShell.xaml.cs @@ -0,0 +1,11 @@ +using Microsoft.Maui.Controls; + +namespace GUI; + +public partial class AppShell : Shell +{ + public AppShell() + { + InitializeComponent(); + } +} diff --git a/src/GUI/GUI.csproj b/src/GUI/GUI.csproj new file mode 100644 index 0000000..431bb74 --- /dev/null +++ b/src/GUI/GUI.csproj @@ -0,0 +1,57 @@ + + + + net8.0-maccatalyst;net8.0-windows10.0.19041.0 + Exe + true + enable + true + enable + true + + Drum Midi Remapper + DrumMidiRemapper + DrumMidiRemapper + com.abstractize.drumremapper + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + runtime;native;build;contentfiles + + + + + true + None + + + \ No newline at end of file diff --git a/src/GUI/MauiProgram.cs b/src/GUI/MauiProgram.cs new file mode 100644 index 0000000..431a4c2 --- /dev/null +++ b/src/GUI/MauiProgram.cs @@ -0,0 +1,31 @@ +using GUI.ViewModels; +using GUI.Views; +using Managers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Maui.Controls.Hosting; +using Microsoft.Maui.Hosting; +using Services; +using CommunityToolkit.Maui; + +namespace GUI; + +public static class MauiProgram +{ + public static MauiApp CreateMauiApp() + { + var builder = MauiApp.CreateBuilder(); + builder + .UseMauiApp() + .UseMauiCommunityToolkit() + .ConfigureFonts(fonts => { fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); }); + + builder.Services.AddServices(); + builder.Services.AddManagers(); + + // ViewModels + builder.Services.AddTransient(); + builder.Services.AddTransient(); + + return builder.Build(); + } +} \ No newline at end of file diff --git a/src/GUI/Platforms/MacCatalyst/AppDelegate.cs b/src/GUI/Platforms/MacCatalyst/AppDelegate.cs new file mode 100644 index 0000000..dd6386a --- /dev/null +++ b/src/GUI/Platforms/MacCatalyst/AppDelegate.cs @@ -0,0 +1,11 @@ +using Foundation; +using Microsoft.Maui; +using Microsoft.Maui.Hosting; + +namespace GUI; + +[Register("AppDelegate")] +public class AppDelegate : MauiUIApplicationDelegate +{ + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); +} diff --git a/src/GUI/Platforms/MacCatalyst/Entitlements.plist b/src/GUI/Platforms/MacCatalyst/Entitlements.plist new file mode 100644 index 0000000..e3af334 --- /dev/null +++ b/src/GUI/Platforms/MacCatalyst/Entitlements.plist @@ -0,0 +1,20 @@ + + + + + + + com.apple.security.app-sandbox + + + com.apple.security.network.client + + + com.apple.security.files.downloads.read-write + + com.apple.security.files.user-selected.read-write + + + \ No newline at end of file diff --git a/src/GUI/Platforms/MacCatalyst/Info.plist b/src/GUI/Platforms/MacCatalyst/Info.plist new file mode 100644 index 0000000..df8aae5 --- /dev/null +++ b/src/GUI/Platforms/MacCatalyst/Info.plist @@ -0,0 +1,55 @@ + + + + + + ITSAppUsesNonExemptEncryption + + + + LSApplicationCategoryType + public.app-category.music + + + LSSupportsOpeningDocumentsInPlace + + UIFileSharingEnabled + + + + NSDocumentsFolderUsageDescription + This app requires access to your Documents folder to open and save MIDI files. + NSDownloadsFolderUsageDescription + This app requires access to your Downloads folder to open and save MIDI files. + + + UIDeviceFamily + + 2 + + UIRequiredDeviceCapabilities + + arm64 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + XSAppIconAssets + Assets.xcassets/appicon.appiconset + + UIBackgroundModes + + fetch + + + \ No newline at end of file diff --git a/src/GUI/Platforms/MacCatalyst/Program.cs b/src/GUI/Platforms/MacCatalyst/Program.cs new file mode 100644 index 0000000..4c1a9cd --- /dev/null +++ b/src/GUI/Platforms/MacCatalyst/Program.cs @@ -0,0 +1,15 @@ +using ObjCRuntime; +using UIKit; + +namespace GUI; + +public class Program +{ + // This is the main entry point of the application. + static void Main(string[] args) + { + // if you want to use a different Application Delegate class from "AppDelegate" + // you can specify it here. + UIApplication.Main(args, null, typeof(AppDelegate)); + } +} diff --git a/src/GUI/Platforms/Windows/App.xaml b/src/GUI/Platforms/Windows/App.xaml new file mode 100644 index 0000000..56e418e --- /dev/null +++ b/src/GUI/Platforms/Windows/App.xaml @@ -0,0 +1,8 @@ + + + diff --git a/src/GUI/Platforms/Windows/App.xaml.cs b/src/GUI/Platforms/Windows/App.xaml.cs new file mode 100644 index 0000000..833431f --- /dev/null +++ b/src/GUI/Platforms/Windows/App.xaml.cs @@ -0,0 +1,24 @@ +using Microsoft.UI.Xaml; + +// To learn more about WinUI, the WinUI project structure, +// and more about our project templates, see: http://aka.ms/winui-project-info. + +namespace GUI.WinUI; + +/// +/// Provides application-specific behavior to supplement the default Application class. +/// +public partial class App : MauiWinUIApplication +{ + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + public App() + { + this.InitializeComponent(); + } + + protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); +} + diff --git a/src/GUI/Platforms/Windows/Package.appxmanifest b/src/GUI/Platforms/Windows/Package.appxmanifest new file mode 100644 index 0000000..40d53dd --- /dev/null +++ b/src/GUI/Platforms/Windows/Package.appxmanifest @@ -0,0 +1,46 @@ + + + + + + + + + $placeholder$ + User Name + $placeholder$.png + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/GUI/Platforms/Windows/app.manifest b/src/GUI/Platforms/Windows/app.manifest new file mode 100644 index 0000000..e508f66 --- /dev/null +++ b/src/GUI/Platforms/Windows/app.manifest @@ -0,0 +1,15 @@ + + + + + + + + true/PM + PerMonitorV2, PerMonitor + + + diff --git a/src/GUI/Properties/launchSettings.json b/src/GUI/Properties/launchSettings.json new file mode 100644 index 0000000..f4c6c8d --- /dev/null +++ b/src/GUI/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Windows Machine": { + "commandName": "Project", + "nativeDebugging": false + } + } +} \ No newline at end of file diff --git a/src/GUI/Resources/AppIcon/appicon.svg b/src/GUI/Resources/AppIcon/appicon.svg new file mode 100644 index 0000000..5f04fcf --- /dev/null +++ b/src/GUI/Resources/AppIcon/appicon.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/GUI/Resources/AppIcon/appiconfg.svg b/src/GUI/Resources/AppIcon/appiconfg.svg new file mode 100644 index 0000000..62d66d7 --- /dev/null +++ b/src/GUI/Resources/AppIcon/appiconfg.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/GUI/Resources/Fonts/OpenSans-Regular.ttf b/src/GUI/Resources/Fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000..33b3e0d Binary files /dev/null and b/src/GUI/Resources/Fonts/OpenSans-Regular.ttf differ diff --git a/src/GUI/Resources/Fonts/OpenSans-Semibold.ttf b/src/GUI/Resources/Fonts/OpenSans-Semibold.ttf new file mode 100644 index 0000000..a1f8571 Binary files /dev/null and b/src/GUI/Resources/Fonts/OpenSans-Semibold.ttf differ diff --git a/src/GUI/Resources/Images/dotnet_bot.png b/src/GUI/Resources/Images/dotnet_bot.png new file mode 100644 index 0000000..1d1b981 Binary files /dev/null and b/src/GUI/Resources/Images/dotnet_bot.png differ diff --git a/src/GUI/Resources/Raw/AboutAssets.txt b/src/GUI/Resources/Raw/AboutAssets.txt new file mode 100644 index 0000000..f22d3bf --- /dev/null +++ b/src/GUI/Resources/Raw/AboutAssets.txt @@ -0,0 +1,15 @@ +Any raw assets you want to be deployed with your application can be placed in +this directory (and child directories). Deployment of the asset to your application +is automatically handled by the following `MauiAsset` Build Action within your `.csproj`. + + + +These files will be deployed with your package and will be accessible using Essentials: + + async Task LoadMauiAsset() + { + using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt"); + using var reader = new StreamReader(stream); + + var contents = reader.ReadToEnd(); + } diff --git a/src/GUI/Resources/Splash/splash.svg b/src/GUI/Resources/Splash/splash.svg new file mode 100644 index 0000000..62d66d7 --- /dev/null +++ b/src/GUI/Resources/Splash/splash.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/GUI/Resources/Styles/Colors.xaml b/src/GUI/Resources/Styles/Colors.xaml new file mode 100644 index 0000000..22f0a67 --- /dev/null +++ b/src/GUI/Resources/Styles/Colors.xaml @@ -0,0 +1,45 @@ + + + + + + + #512BD4 + #ac99ea + #242424 + #DFD8F7 + #9880e5 + #2B0B98 + + White + Black + #D600AA + #190649 + #1f1f1f + + #E1E1E1 + #C8C8C8 + #ACACAC + #919191 + #6E6E6E + #404040 + #212121 + #141414 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/GUI/Resources/Styles/Styles.xaml b/src/GUI/Resources/Styles/Styles.xaml new file mode 100644 index 0000000..f6319b1 --- /dev/null +++ b/src/GUI/Resources/Styles/Styles.xaml @@ -0,0 +1,451 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/GUI/ViewModels/MainViewModel.cs b/src/GUI/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..a5c23fd --- /dev/null +++ b/src/GUI/ViewModels/MainViewModel.cs @@ -0,0 +1,108 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using CommunityToolkit.Maui.Storage; +using Managers.Contracts; +using Models; + +namespace GUI.ViewModels; + +public partial class MainViewModel : ObservableObject +{ + private readonly IMidiMapManager _midiManager; + + public MainViewModel(IMidiMapManager midiManager) + { + _midiManager = midiManager; + MapTypes = [.. Enum.GetValues()]; + } + + [ObservableProperty] + private DrumMapTypes sourceMap; + + [ObservableProperty] + private DrumMapTypes targetMap; + + [ObservableProperty] + private Stream? selectedFile; + [ObservableProperty] + private string? fileName; + + public List MapTypes { get; } + + + [RelayCommand] + private async Task PickFile() + { + var options = new PickOptions + { + PickerTitle = "Select a MIDI file", + FileTypes = new FilePickerFileType(new Dictionary> + { + { DevicePlatform.MacCatalyst, ["public.audio"] }, + { DevicePlatform.WinUI, [".mid", ".midi"] } + }) + }; + + FileResult? result = await FilePicker.Default.PickAsync(options); + if (result != null) + { + SelectedFile = await result.OpenReadAsync(); + fileName = result.FileName; + } + else + { + await Application.Current!.Windows[0].Page!.DisplayAlert("Error", "No file selected.", "OK"); + } + + } + + [RelayCommand] + private async Task Remap() + { + try + { + if (SelectedFile == null) + { + await Application.Current!.Windows[0].Page!.DisplayAlert("Error", "Please select a MIDI file.", "OK"); + return; + } + + Console.WriteLine($"Source Map: {SourceMap}, Target Map: {TargetMap}"); + + Stream tempFile = await _midiManager.RemapMidi(SourceMap.ToString(), TargetMap.ToString(), SelectedFile); + + var saveResult = await FileSaver.Default.SaveAsync("remapped.mid", tempFile); + + if (saveResult.IsSuccessful) + await Application.Current!.Windows[0].Page!.DisplayAlert("Success", "✅ MIDI remapped and saved!", "OK"); + } + catch (Exception ex) + { + Console.WriteLine($"Error during remapping: {ex.Message}"); + await Application.Current!.Windows[0].Page!.DisplayAlert("Error", $"An error occurred: {ex.Message}", "OK"); + } + } + + private static string GetDownloadsPath() + { + string downloadsPath = string.Empty; + if (OperatingSystem.IsWindows()) + { + downloadsPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "\\Downloads"; + } + else if (OperatingSystem.IsMacOS()) + { + downloadsPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/Downloads"; + } + else if (OperatingSystem.IsLinux()) + { + downloadsPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + "/Downloads"; + } + else + { + throw new PlatformNotSupportedException("Unsupported platform for Downloads path."); + } + + return downloadsPath; + } +} \ No newline at end of file diff --git a/src/GUI/Views/MainPage.xaml b/src/GUI/Views/MainPage.xaml new file mode 100644 index 0000000..f5133bb --- /dev/null +++ b/src/GUI/Views/MainPage.xaml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + +