├── .config └── dotnet-tools.json ├── .gitattributes ├── .github └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── MiniPlugins ├── .config │ └── dotnet-tools.json ├── MiniPluginInterface.cs ├── MiniPluginLayout.cs ├── MiniPlugins.cs └── MiniPlugins.csproj ├── README.md ├── SrcTextures ├── .gitignore ├── ImageSources │ ├── DialNumbered.knob │ ├── PowerOn.xcf │ └── PrefabText.xcf ├── Inter_18pt-Bold.ttf ├── OFL.txt └── UserInterface │ ├── .gitignore │ ├── ButtonPressed.png │ ├── ButtonUnpressed.png │ ├── DialBackground.png │ ├── DialPointer.png │ ├── FileOpen.svg │ ├── FxLoopChain.png │ ├── HoverTextOutline.png │ ├── InputChain.png │ ├── LevelDisplay.png │ ├── MoreButton.png │ ├── OutputChain.png │ ├── Play.png │ ├── PluginBackground.png │ ├── PopupBackground.png │ ├── PowerOff.png │ ├── PowerOn.png │ ├── Record.png │ ├── Restart.png │ ├── SingleWhitePixel.png │ ├── StompOutline.png │ ├── StompboxLogo.png │ ├── Stop.png │ ├── TunerPoint.png │ └── VerticalSlider.png ├── StompboxAndroid ├── .config │ └── dotnet-tools.json ├── Activity1.cs ├── AndroidManifest.xml ├── Resources │ ├── Values │ │ ├── Strings.xml │ │ └── ic_launcher_background.xml │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ ├── mipmap-ldpi │ │ └── ic_launcher.png │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ └── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png └── StompboxAndroid.csproj ├── StompboxClient ├── StompboxAPI │ ├── APIClient.cs │ ├── NativeAPI.cs │ ├── StompboxAPI.csproj │ ├── StompboxProcessor.cs │ └── UnmanagedAudioPlugin.cs ├── StompboxAPIBase │ ├── AudioPlugin.cs │ ├── PluginFactory.cs │ ├── StompboxAPIBase.projitems │ ├── StompboxClient.cs │ └── StompboxClientBase.shproj ├── StompboxAPITest │ ├── Program.cs │ └── StompboxAPITest.csproj └── StompboxRemoteClient │ ├── NetworkInterface.cs │ ├── ProtocolClient.cs │ ├── RemoteClient.cs │ ├── RemotePlugin.cs │ └── StompboxRemoteClient.csproj ├── StompboxContent └── Content │ └── Textures │ ├── ImageManifest.xml │ └── UISheet0.png ├── StompboxHost ├── Icon.ico ├── Program.cs ├── Properties │ └── launchSettings.json └── StompboxHost.csproj ├── StompboxImageProcessor ├── Program.cs └── StompboxImageProcessor.csproj ├── StompboxPlugin ├── Properties │ └── AssemblyInfo.cs ├── README.txt ├── StompboxPlugin.cs ├── StompboxPlugin.csproj └── Troubleshooting.md ├── StompboxRemote ├── .config │ └── dotnet-tools.json ├── Icon.ico ├── Program.cs ├── StompboxRemote.csproj └── app.manifest ├── StompboxRemoteGL ├── .config │ └── dotnet-tools.json ├── Icon.bmp ├── Icon.ico ├── Program.cs ├── StompboxRemoteGL.csproj └── app.manifest ├── StompboxShared ├── Content │ ├── Content.mgcb │ └── Textures │ │ └── .gitignore ├── Interface │ ├── AudioFilePlayerInterface.cs │ ├── AudioFileRecorderInterface.cs │ ├── DAWInterface.cs │ ├── InterfaceBase.cs │ ├── MobileInterface.cs │ ├── PedalboardInterface.cs │ ├── PluginInterface.cs │ ├── StompboxLayout.cs │ └── TunerInterface.cs ├── NoteUtil.cs ├── StompboxShared.projitems ├── StompboxShared.shproj └── SysExMapper.cs ├── StompboxUI.sln └── WioSerialDisplay ├── Free_Fonts.h └── WioSerialDisplay.ino /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-mgcb": { 6 | "version": "3.8.1.303", 7 | "commands": [ 8 | "mgcb" 9 | ] 10 | }, 11 | "dotnet-mgcb-editor": { 12 | "version": "3.8.1.303", 13 | "commands": [ 14 | "mgcb-editor" 15 | ] 16 | }, 17 | "dotnet-mgcb-editor-linux": { 18 | "version": "3.8.1.303", 19 | "commands": [ 20 | "mgcb-editor-linux" 21 | ] 22 | }, 23 | "dotnet-mgcb-editor-windows": { 24 | "version": "3.8.1.303", 25 | "commands": [ 26 | "mgcb-editor-windows" 27 | ] 28 | }, 29 | "dotnet-mgcb-editor-mac": { 30 | "version": "3.8.1.303", 31 | "commands": [ 32 | "mgcb-editor-mac" 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | [workflow_dispatch, push, pull_request] 5 | 6 | jobs: 7 | build-windows: 8 | name: Build Windows 9 | runs-on: windows-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | with: 13 | submodules: recursive 14 | 15 | - name: Configure stompbox 16 | working-directory: ${{github.workspace}}/Dependencies/stompbox/build 17 | run: | 18 | cmake.exe -G "Visual Studio 17 2022" -A x64 .. 19 | 20 | - name: Setup MSBuild and add to PATH 21 | uses: microsoft/setup-msbuild@v1.3.1 22 | 23 | - name: Restore NuGet Packages 24 | run: 25 | dotnet restore 26 | 27 | - name: Run Image Processor 28 | working-directory: ${{github.workspace}} 29 | run: | 30 | msbuild .\StompboxUI.sln /t:StompboxImageProcessor /p:Configuration="Release" 31 | cd StompboxImageProcessor\bin\Release\net8.0 32 | .\StompboxImageProcessor.exe 33 | 34 | - name: Run MSBuild for Plugin 35 | working-directory: ${{github.workspace}} 36 | run: msbuild .\StompboxUI.sln /t:StompboxPlugin /p:Configuration=Release 37 | 38 | - name: Create Plugin Artifact 39 | uses: actions/upload-artifact@v4 40 | with: 41 | name: StompboxVST3Plugin 42 | path: ${{github.workspace}}\StompboxPlugin\bin\Release\net8.0-windows 43 | 44 | - name: Run MSBuild for Windows Remote 45 | working-directory: ${{github.workspace}} 46 | run: msbuild .\StompboxUI.sln /t:StompboxRemote /p:Configuration=Release 47 | 48 | - name: Create Windows Remote Artifact 49 | uses: actions/upload-artifact@v4 50 | with: 51 | name: StompboxRemote 52 | path: ${{github.workspace}}\StompboxRemote\bin\Release\net8.0-windows 53 | 54 | - name: Setup Java 55 | uses: actions/setup-java@v3 56 | with: 57 | distribution: 'microsoft' 58 | java-version: '17' 59 | 60 | - name: run MSBuild for Android Remote 61 | run: | 62 | echo -n ${{ secrets.KEYSTORE }} | base64 --ignore-garbage --decode > ${{github.workspace}}\android.keystore 63 | msbuild .\StompboxAndroid\StompboxAndroid.csproj /p:Configuration=RELEASE /p:AndroidKeyStore=true /p:AndroidSigningKeyAlias=stompboxkeystore /p:AndroidSigningKeyStore="${{github.workspace}}\android.keystore" /p:AndroidSigningKeyPass="${{ secrets.KEYSTORE_PASS }}" /p:AndroidSigningStorePass="${{ secrets.KEYSTORE_PASS }}" 64 | 65 | - name: Create Android Remote Artifact 66 | uses: actions/upload-artifact@v4 67 | with: 68 | name: AndroidRemote 69 | path: ${{github.workspace}}\StompboxAndroid\bin\Release\net8.0-android\StompboxAndroid.StompboxAndroid-Signed.apk 70 | 71 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | create_release: 8 | name: Create release 9 | runs-on: ubuntu-latest 10 | outputs: 11 | upload_url: ${{steps.create_release.outputs.upload_url}} 12 | steps: 13 | - name: Check out repository 14 | uses: actions/checkout@v4 15 | with: 16 | submodules: recursive 17 | 18 | - name: Create release 19 | id: create_release 20 | uses: actions/create-release@v1 21 | env: 22 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 23 | with: 24 | draft: true 25 | tag_name: ${{github.ref}} 26 | release_name: Release ${{github.ref}} 27 | 28 | build-windows: 29 | name: Build Windows 30 | needs: create_release 31 | runs-on: windows-latest 32 | steps: 33 | - uses: actions/checkout@v3.3.0 34 | with: 35 | submodules: recursive 36 | 37 | - name: Configure stompbox 38 | working-directory: ${{github.workspace}}/Dependencies/stompbox/build 39 | run: | 40 | cmake.exe -G "Visual Studio 17 2022" -A x64 .. 41 | 42 | - name: Setup MSBuild and add to PATH 43 | uses: microsoft/setup-msbuild@v1.3.1 44 | 45 | - name: Restore NuGet Packages 46 | run: dotnet restore 47 | 48 | - name: Run Image Processor 49 | working-directory: ${{github.workspace}} 50 | run: | 51 | msbuild .\StompboxUI.sln /t:StompboxImageProcessor /p:Configuration="Release" 52 | cd StompboxImageProcessor\bin\Release\net8.0 53 | .\StompboxImageProcessor.exe 54 | 55 | - name: Run MSBuild for VST Plugin 56 | working-directory: ${{github.workspace}} 57 | run: msbuild .\StompboxUI.sln /t:StompboxPlugin /p:Configuration=Release 58 | 59 | - name: Add Plugin Archive 60 | working-directory: ${{github.workspace}} 61 | run: | 62 | mkdir plugin-build 63 | move StompboxPlugin\bin\Release\net8.0-windows plugin-build\StompboxPlugin 64 | cp StompboxPlugin\README.txt plugin-build 65 | Compress-Archive -Path plugin-build\* -Destination StompboxVST3Plugin.zip 66 | 67 | - name: Upload Plugin Asset 68 | uses: actions/upload-release-asset@v1 69 | env: 70 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 71 | with: 72 | upload_url: ${{ needs.create_release.outputs.upload_url }} 73 | asset_path: ./StompboxVST3Plugin.zip 74 | asset_name: StompboxVST3Plugin.zip 75 | asset_content_type: application/zip 76 | 77 | - name: Run MSBuild for Windows Remote 78 | working-directory: ${{github.workspace}} 79 | run: msbuild .\StompboxUI.sln /t:StompboxRemote /p:Configuration=Release 80 | 81 | - name: Add Windows Remote Archive 82 | run: Compress-Archive -Path ${{github.workspace}}\StompboxRemote\bin\Release\net8.0-windows\* -Destination StompboxRemote.zip 83 | 84 | - name: Upload Windows Remote Asset 85 | uses: actions/upload-release-asset@v1 86 | env: 87 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 88 | with: 89 | upload_url: ${{ needs.create_release.outputs.upload_url }} 90 | asset_path: ./StompboxRemote.zip 91 | asset_name: StompboxRemote.zip 92 | asset_content_type: application/zip 93 | 94 | - name: Setup Java 95 | uses: actions/setup-java@v3 96 | with: 97 | distribution: 'microsoft' 98 | java-version: '17' 99 | 100 | - name: run MSBuild for Android Remote 101 | run: | 102 | echo -n ${{ secrets.KEYSTORE }} | base64 --ignore-garbage --decode > ${{github.workspace}}\android.keystore 103 | msbuild .\StompboxAndroid\StompboxAndroid.csproj /p:Configuration=RELEASE /p:AndroidKeyStore=true /p:AndroidSigningKeyAlias=stompboxkeystore /p:AndroidSigningKeyStore="${{github.workspace}}\android.keystore" /p:AndroidSigningKeyPass="${{ secrets.KEYSTORE_PASS }}" /p:AndroidSigningStorePass="${{ secrets.KEYSTORE_PASS }}" 104 | 105 | - name: Upload Android Remote Asset 106 | uses: actions/upload-release-asset@v1 107 | env: 108 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 109 | with: 110 | upload_url: ${{ needs.create_release.outputs.upload_url }} 111 | asset_path: ${{github.workspace}}\StompboxAndroid\bin\Release\net8.0-android\StompboxAndroid.StompboxAndroid-Signed.apk 112 | asset_name: StompboxAndroid.StompboxAndroid-Signed.apk 113 | asset_content_type: application/zip 114 | 115 | - name: Publish Linux-x64 116 | working-directory: ${{github.workspace}} 117 | run: dotnet publish .\StompboxRemoteGL\StompboxRemoteGL.csproj --runtime linux-x64 -p:PublishSingleFile=true --self-contained true 118 | 119 | - name: Add Linux-x64 Archive 120 | working-directory: ${{github.workspace}} 121 | run: | 122 | mkdir linux-x64-build 123 | move StompboxRemoteGL\bin\Release\net8.0\linux-x64\publish linux-x64-build\StompboxRemote 124 | Compress-Archive -Path linux-x64-build\* -Destination StompboxRemoteLinux-X64.zip 125 | 126 | - name: Upload Linux-x64 Asset 127 | uses: actions/upload-release-asset@v1 128 | env: 129 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 130 | with: 131 | upload_url: ${{ needs.create_release.outputs.upload_url }} 132 | asset_path: ./StompboxRemoteLinux-x64.zip 133 | asset_name: StompboxRemoteLinux-x64.zip 134 | asset_content_type: application/zip 135 | 136 | - name: Publish Linux-arm64 137 | working-directory: ${{github.workspace}} 138 | run: dotnet publish .\StompboxRemoteGL\StompboxRemoteGL.csproj --runtime linux-arm64 -p:PublishSingleFile=true --self-contained true 139 | 140 | - name: Add Linux-arm64 Archive 141 | working-directory: ${{github.workspace}} 142 | run: | 143 | mkdir linux-arm64-build 144 | move StompboxRemoteGL\bin\Release\net8.0\linux-arm64\publish linux-arm64-build\StompboxRemote 145 | Compress-Archive -Path linux-arm64-build\* -Destination StompboxRemoteLinux-Arm64.zip 146 | 147 | - name: Upload Linux-arm64 Asset 148 | uses: actions/upload-release-asset@v1 149 | env: 150 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 151 | with: 152 | upload_url: ${{ needs.create_release.outputs.upload_url }} 153 | asset_path: ./StompboxRemoteLinux-Arm64.zip 154 | asset_name: StompboxRemoteLinux-Arm64.zip 155 | asset_content_type: application/zip 156 | 157 | - name: Publish OSX-x64 158 | working-directory: ${{github.workspace}} 159 | run: dotnet publish .\StompboxRemoteGL\StompboxRemoteGL.csproj --runtime osx-x64 -p:PublishSingleFile=true --self-contained true 160 | 161 | - name: Add OSX-x64 Archive 162 | working-directory: ${{github.workspace}} 163 | run: | 164 | mkdir osx-x64-build 165 | move StompboxRemoteGL\bin\Release\net8.0\osx-x64\publish osx-x64-build\StompboxRemote 166 | Compress-Archive -Path osx-x64-build\* -Destination StompboxRemoteMac-X64.zip 167 | 168 | - name: Upload OSX-x64 Asset 169 | uses: actions/upload-release-asset@v1 170 | env: 171 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 172 | with: 173 | upload_url: ${{ needs.create_release.outputs.upload_url }} 174 | asset_path: ./StompboxRemoteMac-X64.zip 175 | asset_name: StompboxRemoteMac-X64.zip 176 | asset_content_type: application/zip 177 | 178 | - name: Publish OSX-arm64 179 | working-directory: ${{github.workspace}} 180 | run: dotnet publish .\StompboxRemoteGL\StompboxRemoteGL.csproj --runtime osx-arm64 -p:PublishSingleFile=true --self-contained true 181 | 182 | - name: Add OSX-arm64 Archive 183 | working-directory: ${{github.workspace}} 184 | run: | 185 | mkdir osx-arm64-build 186 | move StompboxRemoteGL\bin\Release\net8.0\osx-arm64\publish osx-arm64-build\StompboxRemote 187 | Compress-Archive -Path osx-arm64-build\* -Destination StompboxRemoteMac-Arm64.zip 188 | 189 | - name: Upload OSX-arm64 Asset 190 | uses: actions/upload-release-asset@v1 191 | env: 192 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 193 | with: 194 | upload_url: ${{ needs.create_release.outputs.upload_url }} 195 | asset_path: ./StompboxRemoteMac-Arm64.zip 196 | asset_name: StompboxRemoteMac-Arm64.zip 197 | asset_content_type: application/zip 198 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Oo]ut/ 33 | [Ll]og/ 34 | [Ll]ogs/ 35 | 36 | # Visual Studio 2015/2017 cache/options directory 37 | .vs/ 38 | # Uncomment if you have tasks that create the project's static files in wwwroot 39 | #wwwroot/ 40 | 41 | # Visual Studio 2017 auto generated files 42 | Generated\ Files/ 43 | 44 | # MSTest test Results 45 | [Tt]est[Rr]esult*/ 46 | [Bb]uild[Ll]og.* 47 | 48 | # NUnit 49 | *.VisualState.xml 50 | TestResult.xml 51 | nunit-*.xml 52 | 53 | # Build Results of an ATL Project 54 | [Dd]ebugPS/ 55 | [Rr]eleasePS/ 56 | dlldata.c 57 | 58 | # Benchmark Results 59 | BenchmarkDotNet.Artifacts/ 60 | 61 | # .NET Core 62 | project.lock.json 63 | project.fragment.lock.json 64 | artifacts/ 65 | 66 | # ASP.NET Scaffolding 67 | ScaffoldingReadMe.txt 68 | 69 | # StyleCop 70 | StyleCopReport.xml 71 | 72 | # Files built by Visual Studio 73 | *_i.c 74 | *_p.c 75 | *_h.h 76 | *.ilk 77 | *.meta 78 | *.obj 79 | *.iobj 80 | *.pch 81 | *.pdb 82 | *.ipdb 83 | *.pgc 84 | *.pgd 85 | *.rsp 86 | *.sbr 87 | *.tlb 88 | *.tli 89 | *.tlh 90 | *.tmp 91 | *.tmp_proj 92 | *_wpftmp.csproj 93 | *.log 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio LightSwitch build output 298 | **/*.HTMLClient/GeneratedArtifacts 299 | **/*.DesktopClient/GeneratedArtifacts 300 | **/*.DesktopClient/ModelManifest.xml 301 | **/*.Server/GeneratedArtifacts 302 | **/*.Server/ModelManifest.xml 303 | _Pvt_Extensions 304 | 305 | # Paket dependency manager 306 | .paket/paket.exe 307 | paket-files/ 308 | 309 | # FAKE - F# Make 310 | .fake/ 311 | 312 | # CodeRush personal settings 313 | .cr/personal 314 | 315 | # Python Tools for Visual Studio (PTVS) 316 | __pycache__/ 317 | *.pyc 318 | 319 | # Cake - Uncomment if you are using it 320 | # tools/** 321 | # !tools/packages.config 322 | 323 | # Tabs Studio 324 | *.tss 325 | 326 | # Telerik's JustMock configuration file 327 | *.jmconfig 328 | 329 | # BizTalk build output 330 | *.btp.cs 331 | *.btm.cs 332 | *.odx.cs 333 | *.xsd.cs 334 | 335 | # OpenCover UI analysis results 336 | OpenCover/ 337 | 338 | # Azure Stream Analytics local run output 339 | ASALocalRun/ 340 | 341 | # MSBuild Binary and Structured Log 342 | *.binlog 343 | 344 | # NVidia Nsight GPU debugger configuration file 345 | *.nvuser 346 | 347 | # MFractors (Xamarin productivity tool) working folder 348 | .mfractor/ 349 | 350 | # Local History for Visual Studio 351 | .localhistory/ 352 | 353 | # BeatPulse healthcheck temp database 354 | healthchecksdb 355 | 356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 357 | MigrationBackup/ 358 | 359 | # Ionide (cross platform F# VS Code tools) working folder 360 | .ionide/ 361 | 362 | # Fody - auto-generated XML schema 363 | FodyWeavers.xsd -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Dependencies/stompbox"] 2 | path = Dependencies/stompbox 3 | url = https://github.com/mikeoliphant/stompbox 4 | [submodule "Dependencies/UILayout"] 5 | path = Dependencies/UILayout 6 | url = https://github.com/mikeoliphant/UILayout 7 | -------------------------------------------------------------------------------- /MiniPlugins/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-mgcb": { 6 | "version": "3.8.1.303", 7 | "commands": [ 8 | "mgcb" 9 | ] 10 | }, 11 | "dotnet-mgcb-editor": { 12 | "version": "3.8.1.303", 13 | "commands": [ 14 | "mgcb-editor" 15 | ] 16 | }, 17 | "dotnet-mgcb-editor-linux": { 18 | "version": "3.8.1.303", 19 | "commands": [ 20 | "mgcb-editor-linux" 21 | ] 22 | }, 23 | "dotnet-mgcb-editor-windows": { 24 | "version": "3.8.1.303", 25 | "commands": [ 26 | "mgcb-editor-windows" 27 | ] 28 | }, 29 | "dotnet-mgcb-editor-mac": { 30 | "version": "3.8.1.303", 31 | "commands": [ 32 | "mgcb-editor-mac" 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /MiniPlugins/MiniPluginInterface.cs: -------------------------------------------------------------------------------- 1 | using Stompbox; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using UILayout; 8 | 9 | namespace MiniPlugins 10 | { 11 | public class MiniPluginInterface : Dock 12 | { 13 | HorizontalStack pluginStack; 14 | 15 | public MiniPluginInterface() 16 | { 17 | pluginStack = new HorizontalStack() 18 | { 19 | HorizontalAlignment = EHorizontalAlignment.Center, 20 | VerticalAlignment = EVerticalAlignment.Center 21 | }; 22 | Children.Add(pluginStack); 23 | 24 | UpdateUI(); 25 | } 26 | 27 | public void UpdateUI() 28 | { 29 | pluginStack.Children.Clear(); 30 | 31 | string globalChain = StompboxClient.Instance.GetGlobalChain(); 32 | 33 | string[] split = globalChain.Split(' '); 34 | 35 | int num = (split.Length / 2) * 2; 36 | 37 | for (int pos = 0; pos < num; pos+=2) 38 | { 39 | if ((split[pos] == "Slot") || (split[pos] == "MasterSlot")) 40 | { 41 | string slotName = split[pos + 1]; 42 | 43 | AddPlugin(StompboxClient.Instance.PluginFactory.CreatePlugin(StompboxClient.Instance.GetSlotPlugin(split[pos + 1])), slotName); 44 | } 45 | else 46 | { 47 | foreach (IAudioPlugin plugin in StompboxClient.Instance.GetChain(split[pos])) 48 | { 49 | AddPlugin(plugin, null); 50 | } 51 | } 52 | } 53 | } 54 | 55 | void AddPlugin(IAudioPlugin plugin, string slotName) 56 | { 57 | if ((plugin.Name == "Input") || (plugin.Name == "Master")) 58 | { 59 | pluginStack.Children.Add(new GainPluginInterface(plugin) { VerticalAlignment = EVerticalAlignment.Stretch }); 60 | } 61 | else 62 | { 63 | pluginStack.Children.Add(new PluginInterface(plugin, slotName) { VerticalAlignment = EVerticalAlignment.Stretch }); 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /MiniPlugins/MiniPluginLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UILayout; 3 | using Stompbox; 4 | 5 | namespace MiniPlugins 6 | { 7 | public class MiniPluginLayout : MonoGameLayout 8 | { 9 | public static MiniPluginLayout Instance { get; private set; } 10 | public static float BaseScale { get; set; } = 1.0f; 11 | 12 | bool clientConnected = true; 13 | 14 | public MiniPluginLayout() 15 | { 16 | Instance = this; 17 | 18 | Scale = BaseScale; 19 | } 20 | 21 | public override void SetHost(MonoGameHost host) 22 | { 23 | base.SetHost(host); 24 | 25 | Host.InactiveSleepTime = TimeSpan.Zero; 26 | 27 | host.Window.Title = "stompbox"; 28 | 29 | LoadImageManifest("ImageManifest.xml"); 30 | 31 | GraphicsContext.SingleWhitePixelImage = GetImage("SingleWhitePixel"); 32 | GraphicsContext.SamplerState = new Microsoft.Xna.Framework.Graphics.SamplerState() 33 | { 34 | AddressU = Microsoft.Xna.Framework.Graphics.TextureAddressMode.Clamp, 35 | AddressV = Microsoft.Xna.Framework.Graphics.TextureAddressMode.Clamp, 36 | Filter = Microsoft.Xna.Framework.Graphics.TextureFilter.Anisotropic, 37 | MipMapLevelOfDetailBias = -0.8f 38 | }; 39 | 40 | DefaultFont = GetFont("MainFont"); 41 | DefaultFont.SpriteFont.Spacing = 1; 42 | 43 | GetFont("SmallFont").SpriteFont.Spacing = 0; 44 | 45 | DefaultForegroundColor = UIColor.White; 46 | 47 | DefaultOutlineNinePatch = GetImage("PopupBackground"); 48 | 49 | DefaultPressedNinePatch = GetImage("ButtonPressed"); 50 | DefaultUnpressedNinePatch = GetImage("ButtonUnpressed"); 51 | 52 | DefaultDragImage = GetImage("ButtonPressed"); 53 | 54 | Initialize(); 55 | 56 | RootUIElement = new MiniPluginInterface(); 57 | } 58 | 59 | void Initialize() 60 | { 61 | StompboxClient.Instance.SendCommand("SetGlobalChain MasterSlot InputGain Slot Amp Slot Tonestack Slot Cabinet MasterSlot MasterVolume"); 62 | 63 | StompboxClient.Instance.UpdateProgram(); 64 | 65 | StompboxClient.Instance.SendCommand("SetPluginSlot InputGain Input"); 66 | StompboxClient.Instance.SendCommand("SetPluginSlot MasterVolume Master"); 67 | 68 | StompboxClient.Instance.SetDefaultSlotPlugin("Amp", "NAM"); 69 | StompboxClient.Instance.SetDefaultSlotPlugin("Tonestack", "EQ-7"); 70 | StompboxClient.Instance.SetDefaultSlotPlugin("Cabint", "Cabinet"); 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /MiniPlugins/MiniPlugins.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | using AudioPlugSharp; 5 | using UILayout; 6 | 7 | namespace MiniPlugins 8 | { 9 | public unsafe class AmpPlugin : AudioPlugSharp.AudioPluginBase 10 | { 11 | public int CurrentProgram { get; private set; } 12 | 13 | public StompboxAPI.APIClient StompboxClient { get; private set; } 14 | public MonoGameHost GameHost { get; private set; } = null; 15 | 16 | FloatAudioIOPort monoInput; 17 | FloatAudioIOPort monoOutput; 18 | IntPtr bitConvertBuffer = IntPtr.Zero; 19 | uint bitConvertBufferSize = 0; 20 | 21 | public AmpPlugin() 22 | { 23 | Company = "Nostatic Software"; 24 | Website = "nostatic.org"; 25 | Contact = "contact@nostatic.org"; 26 | PluginName = "SB-Amp"; 27 | PluginCategory = "Fx"; 28 | PluginVersion = "0.0.1"; 29 | 30 | PluginID = 0x66D6F61F1DE14831; 31 | 32 | HasUserInterface = true; 33 | 34 | //Logger.ImmediateMode = true; 35 | 36 | //Logger.ImmediateMode = true; 37 | //Logger.WriteToStdErr = true; 38 | } 39 | 40 | public override void Initialize() 41 | { 42 | base.Initialize(); 43 | 44 | Debug("Initialize"); 45 | 46 | InputPorts = new AudioIOPort[] { monoInput = new FloatAudioIOPort("Mono Input", EAudioChannelConfiguration.Mono) }; 47 | OutputPorts = new AudioIOPort[] { monoOutput = new FloatAudioIOPort("Mono Output", EAudioChannelConfiguration.Mono) }; 48 | 49 | EditorWidth = 680; 50 | EditorHeight = 190; 51 | 52 | //GCSettings.LatencyMode = GCLatencyMode.LowLatency;// GCLatencyMode.Batch; // SustainedLowLatency; 53 | 54 | StompboxAPI.APIClient.DebugAction = Debug; 55 | 56 | StompboxClient = new StompboxAPI.APIClient(); 57 | 58 | StompboxClient.MidiCallback = SendMidiCommand; 59 | } 60 | 61 | public override void Stop() 62 | { 63 | base.Stop(); 64 | 65 | GC.Collect(); 66 | } 67 | 68 | public void DisposeDevice() 69 | { 70 | } 71 | 72 | public void Debug(String debugStr) 73 | { 74 | Logger.Log(debugStr); 75 | } 76 | 77 | 78 | public void ReportDSPLoad(float maxDSPLoad, float minDSPLoad) 79 | { 80 | } 81 | 82 | public virtual void UpdateUI() 83 | { 84 | StompboxClient.UpdateUI(); 85 | } 86 | 87 | IntPtr parentWindow; 88 | 89 | public override void ShowEditor(IntPtr parentWindow) 90 | { 91 | Logger.Log("Show Editor"); 92 | 93 | this.parentWindow = parentWindow; 94 | 95 | if (parentWindow == IntPtr.Zero) 96 | { 97 | RunGame(); 98 | } 99 | else 100 | { 101 | new Thread(new ThreadStart(RunGame)).Start(); 102 | } 103 | } 104 | 105 | void RunGame() 106 | { 107 | Logger.Log("Start game"); 108 | 109 | try 110 | { 111 | int screenWidth = (int)EditorWidth; 112 | int screenHeight = (int)EditorHeight; 113 | 114 | MiniPluginLayout layout = new MiniPluginLayout(); 115 | 116 | layout.Scale = 0.35f; 117 | 118 | using (GameHost = new MonoGameHost(parentWindow, screenWidth, screenHeight, fullscreen: false)) 119 | { 120 | GameHost.IsMouseVisible = true; 121 | 122 | GameHost.StartGame(layout); 123 | 124 | StompboxClient.NeedUIReload = true; 125 | } 126 | 127 | layout = null; 128 | } 129 | catch (Exception ex) 130 | { 131 | Logger.Log("Run game failed with: " + ex.ToString()); 132 | } 133 | } 134 | 135 | public override void ResizeEditor(uint newWidth, uint newHeight) 136 | { 137 | base.ResizeEditor(newWidth, newHeight); 138 | 139 | if (GameHost != null) 140 | { 141 | GameHost.RequestResize((int)newWidth, (int)newHeight); 142 | } 143 | } 144 | 145 | 146 | public override void HideEditor() 147 | { 148 | base.HideEditor(); 149 | 150 | GameHost.Exit(); 151 | } 152 | 153 | public override void InitializeProcessing() 154 | { 155 | base.InitializeProcessing(); 156 | 157 | Logger.Log("Sample rate is: " + Host.SampleRate); 158 | 159 | StompboxClient.Init((float)Host.SampleRate); 160 | } 161 | 162 | public override byte[] SaveState() 163 | { 164 | byte[] data = null; 165 | 166 | try 167 | { 168 | data = System.Text.Encoding.ASCII.GetBytes(StompboxClient.GetProgramState()); 169 | } 170 | catch (Exception ex) 171 | { 172 | Logger.Log("Save state failed with: " + ex.ToString()); 173 | } 174 | 175 | return data; 176 | } 177 | 178 | string[] lineSeparator = new string[] { "\r", "\n" }; 179 | 180 | public override void RestoreState(byte[] stateData) 181 | { 182 | String programString = System.Text.Encoding.ASCII.GetString(stateData); 183 | 184 | string[] commands = programString.Split(lineSeparator, 0); 185 | 186 | foreach (string command in commands) 187 | { 188 | if (!string.IsNullOrWhiteSpace(command)) 189 | { 190 | StompboxClient.SendCommand(command); 191 | } 192 | } 193 | 194 | UpdateUI(); 195 | } 196 | 197 | Stopwatch processWatch = new Stopwatch(); 198 | 199 | void SendMidiCommand(int midiCommand, int midiData1, int midiData2) 200 | { 201 | Logger.Log("Send midi: " + midiCommand + " " + midiData1 + " " + midiData2); 202 | 203 | switch (midiCommand) 204 | { 205 | case 0x80: 206 | Host.SendNoteOff(1, midiData1, (float)midiData2 / 127.0f, 0); 207 | break; 208 | case 0x90: 209 | Host.SendNoteOn(1, midiData1, (float)midiData2 / 127.0f, 0); 210 | break; 211 | } 212 | } 213 | 214 | public override void Process() 215 | { 216 | StompboxClient.Process(((float**)monoInput.GetAudioBufferPtrs())[0], ((float**)monoOutput.GetAudioBufferPtrs())[0], monoInput.CurrentBufferSize); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /MiniPlugins/MiniPlugins.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | Library 4 | net8.0-windows 5 | Major 6 | false 7 | false 8 | true 9 | true 10 | True 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # What is it? 2 | 3 | Stompbox is a guitar amplification and effects application, arranged as a digital version of a guitar pedalboard. 4 | 5 | This github repository is the front-end for the software (the core codebase is [stompbox](https://github.com/mikeoliphant/stompbox)), to be used either as a remote interface to an actual pedalboard implementation, or as a standalone VST plugin. 6 | 7 | This is what it looks like running a Windows/Linux remote or as a VST plugin: 8 | 9 | ![stompbox](https://github.com/mikeoliphant/StompboxUI/assets/6710799/dd6e9349-ff0d-4437-af42-ef62f1096496) 10 | 11 | This is what it looks like running as an Android remote: 12 | 13 | ![StompboxAndroid](https://github.com/mikeoliphant/StompboxUI/assets/6710799/3189e769-a28c-4e3b-8629-6846fb32de6c) 14 | 15 | # Features 16 | 17 | * Pedalboard-style layout (Amp/Cab with input chain and FX loop) 18 | * [Neural Amp Modeler](https://github.com/sdatkinson/neural-amp-modeler) and [RTNeural](https://github.com/jatinchowdhury18/RTNeural) model support 19 | * Cabinet impulse responses 20 | * Tonestack/EQ 21 | * Noise Gate 22 | * Drive (clean boost, tube screamer) 23 | * Time-based effects 24 | - Delay 25 | - Reverb (algorithmic and convolution) 26 | - Compressor 27 | - Chorus 28 | - Phaser 29 | - Flanger 30 | - Tremolo 31 | - Vibrato 32 | - Wah/Auto-Wah 33 | * Tuner 34 | * Audio file player 35 | 36 | # Platform Support 37 | 38 | Stompbox currently can be run in the following ways: 39 | 40 | * Headless on Linux (I use it on a [Raspberry Pi based pedalboard](https://www.youtube.com/watch?v=2I_bxxzQs2s)) 41 | * Remote UI on Windows, Linux or Android 42 | * Standalone app on Windows 43 | * VST3 plugin on Windows 44 | 45 | # Installation 46 | 47 | To run it as a VST3, you can simply download it from [the releases section of this repo](https://github.com/mikeoliphant/StompboxUI/releases/latest). 48 | 49 | Simply extract the .zip file and copy the resulting folder to "C:\Program Files\Common Files\VST3". 50 | 51 | # User Data Location 52 | 53 | Files, such as NAM models, impulse responses, etc. go in your local user roaming AppData folder. Go to "%APPDATA%" in file explorer, and you should see a "stompbox" folder after the VST has been loaded at least once. 54 | 55 | # Building From Source 56 | 57 | Make sure you clone this github repo recursively: 58 | 59 | ```bash 60 | git clone --recurse-submodules https://github.com/mikeoliphant/StompboxUI 61 | ``` 62 | 63 | Building should be straightforward using Visual Studio. 64 | 65 | **NOTE:** Build and run the "StompboxImageProcessor" project first - it creates texture assets that are required for the main build. 66 | 67 | -------------------------------------------------------------------------------- /SrcTextures/.gitignore: -------------------------------------------------------------------------------- 1 | tmp/ -------------------------------------------------------------------------------- /SrcTextures/ImageSources/DialNumbered.knob: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/ImageSources/DialNumbered.knob -------------------------------------------------------------------------------- /SrcTextures/ImageSources/PowerOn.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/ImageSources/PowerOn.xcf -------------------------------------------------------------------------------- /SrcTextures/ImageSources/PrefabText.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/ImageSources/PrefabText.xcf -------------------------------------------------------------------------------- /SrcTextures/Inter_18pt-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/Inter_18pt-Bold.ttf -------------------------------------------------------------------------------- /SrcTextures/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 The Inter Project Authors (https://github.com/rsms/inter) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | https://openfontlicense.org 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /SrcTextures/UserInterface/.gitignore: -------------------------------------------------------------------------------- 1 | DialPointerRotations.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/ButtonPressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/ButtonPressed.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/ButtonUnpressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/ButtonUnpressed.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/DialBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/DialBackground.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/DialPointer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/DialPointer.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/FileOpen.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /SrcTextures/UserInterface/FxLoopChain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/FxLoopChain.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/HoverTextOutline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/HoverTextOutline.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/InputChain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/InputChain.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/LevelDisplay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/LevelDisplay.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/MoreButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/MoreButton.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/OutputChain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/OutputChain.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/Play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/Play.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/PluginBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/PluginBackground.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/PopupBackground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/PopupBackground.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/PowerOff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/PowerOff.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/PowerOn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/PowerOn.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/Record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/Record.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/Restart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/Restart.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/SingleWhitePixel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/SingleWhitePixel.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/StompOutline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/StompOutline.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/StompboxLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/StompboxLogo.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/Stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/Stop.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/TunerPoint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/TunerPoint.png -------------------------------------------------------------------------------- /SrcTextures/UserInterface/VerticalSlider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/SrcTextures/UserInterface/VerticalSlider.png -------------------------------------------------------------------------------- /StompboxAndroid/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-mgcb": { 6 | "version": "3.8.1.303", 7 | "commands": [ 8 | "mgcb" 9 | ] 10 | }, 11 | "dotnet-mgcb-editor": { 12 | "version": "3.8.1.303", 13 | "commands": [ 14 | "mgcb-editor" 15 | ] 16 | }, 17 | "dotnet-mgcb-editor-linux": { 18 | "version": "3.8.1.303", 19 | "commands": [ 20 | "mgcb-editor-linux" 21 | ] 22 | }, 23 | "dotnet-mgcb-editor-windows": { 24 | "version": "3.8.1.303", 25 | "commands": [ 26 | "mgcb-editor-windows" 27 | ] 28 | }, 29 | "dotnet-mgcb-editor-mac": { 30 | "version": "3.8.1.303", 31 | "commands": [ 32 | "mgcb-editor-mac" 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /StompboxAndroid/Activity1.cs: -------------------------------------------------------------------------------- 1 | using Android.App; 2 | using Android.Content.PM; 3 | using Android.OS; 4 | using Android.Views; 5 | using Microsoft.Xna.Framework; 6 | using UILayout; 7 | using StompboxAPI; 8 | using Microsoft.Xna.Framework.Graphics; 9 | using Microsoft.Xna.Framework.Input; 10 | 11 | namespace Stompbox 12 | { 13 | [Activity( 14 | Label = "@string/app_name", 15 | MainLauncher = true, 16 | Icon = "@mipmap/ic_launcher", 17 | AlwaysRetainTaskState = true, 18 | LaunchMode = LaunchMode.SingleInstance, 19 | ScreenOrientation = ScreenOrientation.FullUser, 20 | ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.ScreenSize 21 | )] 22 | 23 | public class Activity1 : AndroidGameActivity 24 | { 25 | public static Activity1 Instance { get; private set; } 26 | 27 | View view; 28 | RemoteClient guitarClient; 29 | MonoGameHost GameHost; 30 | 31 | protected override void OnCreate(Bundle bundle) 32 | { 33 | base.OnCreate(bundle); 34 | 35 | Instance = this; 36 | 37 | //StompboxLayout.DAWMode = false; 38 | 39 | guitarClient = new RemoteClient(); 40 | 41 | InterfaceBase.InterfaceType = EStompboxInterfaceType.Mobile; 42 | 43 | StompboxLayout game = new StompboxLayout(); 44 | 45 | //game.SetScreenScale(scale, resizeScreen: true); 46 | 47 | GameHost = new MonoGameHost(0, 0, fullscreen: true); 48 | 49 | GameHost.IsMouseVisible = true; 50 | 51 | view = GameHost.Services.GetService(typeof(View)) as View; 52 | view.KeepScreenOn = true; 53 | 54 | SetContentView(view); 55 | 56 | GameHost.StartGame(game); 57 | } 58 | } 59 | 60 | public class Game1 : Game 61 | { 62 | private GraphicsDeviceManager _graphics; 63 | private SpriteBatch _spriteBatch; 64 | 65 | public Game1() 66 | { 67 | _graphics = new GraphicsDeviceManager(this); 68 | Content.RootDirectory = "Content"; 69 | IsMouseVisible = true; 70 | } 71 | 72 | protected override void Initialize() 73 | { 74 | // TODO: Add your initialization logic here 75 | 76 | base.Initialize(); 77 | } 78 | 79 | protected override void LoadContent() 80 | { 81 | _spriteBatch = new SpriteBatch(GraphicsDevice); 82 | 83 | // TODO: use this.Content to load your game content here 84 | } 85 | 86 | protected override void Update(GameTime gameTime) 87 | { 88 | if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape)) 89 | Exit(); 90 | 91 | // TODO: Add your update logic here 92 | 93 | base.Update(gameTime); 94 | } 95 | 96 | protected override void Draw(GameTime gameTime) 97 | { 98 | GraphicsDevice.Clear(Color.CornflowerBlue); 99 | 100 | // TODO: Add your drawing code here 101 | 102 | base.Draw(gameTime); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /StompboxAndroid/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /StompboxAndroid/Resources/Values/Strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Stompbox Remote 4 | 5 | -------------------------------------------------------------------------------- /StompboxAndroid/Resources/Values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000000 4 | -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-ldpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-ldpi/ic_launcher.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /StompboxAndroid/Resources/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxAndroid/Resources/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /StompboxAndroid/StompboxAndroid.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net8.0-android 4 | 23 5 | Exe 6 | com.companyname.StompboxAndroid 7 | 1 8 | 1.0 9 | 10 | 11 | $(DefineConstants);STOMPBOXREMOTE 12 | False 13 | 14 | 15 | $(DefineConstants);STOMPBOXREMOTE 16 | False 17 | apk 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPI/APIClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | using Stompbox; 4 | 5 | namespace StompboxAPI 6 | { 7 | public class APIClient : StompboxClient 8 | { 9 | StompboxProcessor processor; 10 | bool needPresetLoad = false; 11 | 12 | public override bool NeedUIReload 13 | { 14 | get 15 | { 16 | if (needPresetLoad && !processor.IsPresetLoading) 17 | { 18 | UpdateProgram(); 19 | 20 | needPresetLoad = false; 21 | 22 | return true; 23 | } 24 | 25 | return base.NeedUIReload; 26 | } 27 | 28 | set => base.NeedUIReload = value; 29 | } 30 | 31 | public override int CurrentPresetIndex 32 | { 33 | get => base.CurrentPresetIndex; 34 | 35 | set 36 | { 37 | base.CurrentPresetIndex = value; 38 | 39 | if (CurrentPresetIndex >= 0) 40 | { 41 | processor.LoadPreset(PresetNames[base.CurrentPresetIndex]); 42 | 43 | needPresetLoad = true; 44 | } 45 | } 46 | } 47 | 48 | public APIClient() 49 | : base() 50 | { 51 | if (!Directory.Exists(PluginPath)) 52 | { 53 | Directory.CreateDirectory(PluginPath); 54 | } 55 | 56 | processor = new StompboxProcessor(PluginPath, dawMode: true); 57 | 58 | InClientMode = false; 59 | 60 | //processorWrapper.SetMidiCallback(HandleMidi); 61 | 62 | //UpdateProgram(); 63 | } 64 | 65 | public void StartServer() 66 | { 67 | processor.StartServer(); 68 | } 69 | 70 | public override void UpdatePresets() 71 | { 72 | needPresetLoad = false; 73 | 74 | base.UpdatePresets(); 75 | 76 | SetPresetNames(processor.GetPresets()); 77 | 78 | SetSelectedPreset(processor.GetCurrentPreset()); 79 | } 80 | 81 | public override void UpdateProgram() 82 | { 83 | base.UpdateProgram(); 84 | 85 | UpdateUI(); 86 | } 87 | 88 | // FIXME: these should use direct commands 89 | 90 | public override void SaveCurrentPreset() 91 | { 92 | SendCommand("SavePreset " + PresetNames[CurrentPresetIndex]); 93 | } 94 | 95 | public override void SavePresetAs(string presetName) 96 | { 97 | SendCommand("SavePreset " + presetName); 98 | SendCommand("List Presets"); 99 | UpdateProgram(); 100 | } 101 | 102 | public override void DeleteCurrentPreset() 103 | { 104 | SendCommand("DeletePreset " + PresetNames[CurrentPresetIndex]); 105 | 106 | UpdateProgram(); 107 | } 108 | 109 | public override string GetGlobalChain() 110 | { 111 | return processor.GetGlobalChain(); 112 | } 113 | 114 | public override IEnumerable GetAllPluginNames() 115 | { 116 | return processor.GetAllPlugins(); 117 | } 118 | 119 | public override IAudioPlugin GetPluginDefinition(string pluginName) 120 | { 121 | return PluginFactory.GetPluginDefinition(pluginName); 122 | } 123 | 124 | public override IAudioPlugin CreatePlugin(string pluginName, string pluginID) 125 | { 126 | return processor.CreatePlugin(pluginID); 127 | } 128 | 129 | public override void SendCommand(string command) 130 | { 131 | processor.HandleCommand(command); 132 | } 133 | 134 | public override List GetChain(string chainName) 135 | { 136 | List chain = new(); 137 | 138 | chain.AddRange(processor.GetChainPlugins(chainName)); 139 | 140 | foreach (IAudioPlugin plugin in chain) 141 | { 142 | PluginFactory.RegisterPlugin(plugin); 143 | } 144 | 145 | return chain; 146 | } 147 | 148 | public override string GetSlotPlugin(string slotName) 149 | { 150 | return processor.GetPluginSlot(slotName); 151 | } 152 | 153 | public override void SetSlotPlugin(string slotName, string pluginID) 154 | { 155 | processor.SetPluginSlot(slotName, pluginID); 156 | } 157 | 158 | public override void Init(float sampleRate) 159 | { 160 | processor.Init(sampleRate); 161 | } 162 | 163 | public String GetProgramState() 164 | { 165 | String settingsString = processor.DumpSettings(); 166 | String programString = processor.DumpProgram(); 167 | 168 | return settingsString + programString; 169 | } 170 | 171 | public unsafe void Process(float* input, float* output, uint bufferSize) 172 | { 173 | processor.Process(input, output, bufferSize); 174 | } 175 | 176 | long samplePos = 0; 177 | 178 | public unsafe void SimulateAudio() 179 | { 180 | int bufferSize = 1024; 181 | int sampleRate = 44100; 182 | 183 | int sleepMS = sampleRate / 1000; 184 | 185 | Init(sampleRate); 186 | 187 | float* inBuf = (float*)Marshal.AllocHGlobal(bufferSize * sizeof(float)); 188 | float* outBuf = (float*)Marshal.AllocHGlobal(bufferSize * sizeof(float)); 189 | 190 | while (true) 191 | { 192 | if (StopSimulateAudio) 193 | break; 194 | 195 | for (int i = 0; i < bufferSize; i++) 196 | { 197 | inBuf[i] = 0; 198 | 199 | inBuf[i] += (float)Math.Sin(((float)samplePos / (float)sampleRate) * 440 * Math.PI * 2) * 0.25f; 200 | 201 | samplePos++; 202 | } 203 | 204 | processor.Process(inBuf, outBuf, (uint)bufferSize); 205 | 206 | Thread.Sleep(sleepMS); 207 | } 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPI/NativeAPI.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace StompboxAPI 9 | { 10 | class NativeApi 11 | { 12 | public const string STOMPBOX_LIB_NAME = "stompbox-capi"; 13 | 14 | [DllImport(STOMPBOX_LIB_NAME)] 15 | public static extern uint GetStringVectorSize(IntPtr strVec); 16 | 17 | [DllImport(STOMPBOX_LIB_NAME)] 18 | public static extern IntPtr GetStringVectorValue(IntPtr strVec, uint index); 19 | 20 | [DllImport(STOMPBOX_LIB_NAME)] 21 | public static extern IntPtr CreateProcessor([MarshalAs(UnmanagedType.LPWStr)] string dataPath, bool dawMode); 22 | 23 | [DllImport(STOMPBOX_LIB_NAME)] 24 | public static extern void DeleteProcessor(IntPtr processor); 25 | 26 | [DllImport(STOMPBOX_LIB_NAME)] 27 | public static extern void InitProcessor(IntPtr processor, float sampleRate); 28 | 29 | [DllImport(STOMPBOX_LIB_NAME)] 30 | public static extern void StartServer(IntPtr processor); 31 | 32 | [DllImport(STOMPBOX_LIB_NAME)] 33 | public static unsafe extern void Process(IntPtr processor, float* input, float* output, uint bufferSize); 34 | 35 | [DllImport(STOMPBOX_LIB_NAME)] 36 | [return: MarshalAs(UnmanagedType.I1)] 37 | public static extern bool IsPresetLoading(IntPtr processor); 38 | 39 | [DllImport(STOMPBOX_LIB_NAME)] 40 | public static extern void HandleCommand(IntPtr processor, [MarshalAs(UnmanagedType.LPStr)] string cmd); 41 | 42 | [DllImport(STOMPBOX_LIB_NAME)] 43 | [return: MarshalAs(UnmanagedType.I1)] 44 | public static extern bool HandleMidiCommand(IntPtr processor, int midiCommand, int midiData1, int midiData2); 45 | 46 | [DllImport(STOMPBOX_LIB_NAME)] 47 | [return: MarshalAs(UnmanagedType.LPWStr)] 48 | public static extern string GetDataPath(IntPtr processor); 49 | 50 | [DllImport(STOMPBOX_LIB_NAME)] 51 | public static extern IntPtr GetAllPlugins(IntPtr processor); 52 | 53 | [DllImport(STOMPBOX_LIB_NAME)] 54 | [return: MarshalAs(UnmanagedType.LPStr)] 55 | public static extern string GetGlobalChain(IntPtr processor); 56 | 57 | [DllImport(STOMPBOX_LIB_NAME)] 58 | public static extern IntPtr GetPluginSlot(IntPtr processor, [MarshalAs(UnmanagedType.LPStr)] string slotName); 59 | 60 | [DllImport(STOMPBOX_LIB_NAME)] 61 | public static extern void SetPluginSlot(IntPtr processor, [MarshalAs(UnmanagedType.LPStr)] string slotName, [MarshalAs(UnmanagedType.LPStr)] string pluginID); 62 | 63 | [DllImport(STOMPBOX_LIB_NAME)] 64 | public static extern uint GetPluginVectorSize(IntPtr plugVec); 65 | 66 | [DllImport(STOMPBOX_LIB_NAME)] 67 | public static extern IntPtr GetPluginVectorValue(IntPtr plugVec, uint index); 68 | 69 | [DllImport(STOMPBOX_LIB_NAME)] 70 | public static extern IntPtr GetChainPlugins(IntPtr processor, [MarshalAs(UnmanagedType.LPStr)] string chainName); 71 | 72 | [DllImport(STOMPBOX_LIB_NAME)] 73 | public static extern IntPtr CreatePlugin(IntPtr processor, [MarshalAs(UnmanagedType.LPStr)] string id); 74 | 75 | [DllImport(STOMPBOX_LIB_NAME)] 76 | public static extern IntPtr GetPresets(IntPtr processor); 77 | 78 | [DllImport(STOMPBOX_LIB_NAME)] 79 | public static extern IntPtr GetCurrentPreset(IntPtr processor); 80 | 81 | [DllImport(STOMPBOX_LIB_NAME)] 82 | public static extern void LoadPreset(IntPtr processor, [MarshalAs(UnmanagedType.LPStr)] string presetName); 83 | 84 | [DllImport(STOMPBOX_LIB_NAME)] 85 | [return: MarshalAs(UnmanagedType.LPStr)] 86 | public static extern string DumpSettings(IntPtr processor); 87 | 88 | [DllImport(STOMPBOX_LIB_NAME)] 89 | [return: MarshalAs(UnmanagedType.LPStr)] 90 | public static extern string DumpProgram(IntPtr processor); 91 | 92 | [DllImport(STOMPBOX_LIB_NAME)] 93 | public static extern IntPtr GetPluginName(IntPtr plugin); 94 | 95 | [DllImport(STOMPBOX_LIB_NAME)] 96 | public static extern IntPtr GetPluginID(IntPtr plugin); 97 | 98 | [DllImport(STOMPBOX_LIB_NAME)] 99 | public static extern IntPtr GetPluginDescription(IntPtr plugin); 100 | 101 | [DllImport(STOMPBOX_LIB_NAME)] 102 | public static extern IntPtr GetPluginBackgroundColor(IntPtr plugin); 103 | 104 | [DllImport(STOMPBOX_LIB_NAME)] 105 | public static extern IntPtr GetPluginForegroundColor(IntPtr plugin); 106 | 107 | [DllImport(STOMPBOX_LIB_NAME)] 108 | [return: MarshalAs(UnmanagedType.I1)] 109 | public static extern bool GetPluginIsUserSelectable(IntPtr plugin); 110 | 111 | [DllImport(STOMPBOX_LIB_NAME)] 112 | public static extern bool GetPluginEnabled(IntPtr plugin); 113 | 114 | [DllImport(STOMPBOX_LIB_NAME)] 115 | public static extern void SetPluginEnabled(IntPtr plugin, bool enabled); 116 | 117 | [DllImport(STOMPBOX_LIB_NAME)] 118 | public static extern float GetOutputValue(IntPtr plugin); 119 | 120 | [DllImport(STOMPBOX_LIB_NAME)] 121 | public static extern uint GetPluginNumParameters(IntPtr plugin); 122 | 123 | [DllImport(STOMPBOX_LIB_NAME)] 124 | public static extern IntPtr GetPluginParameter(IntPtr plugin, uint index); 125 | 126 | [DllImport(STOMPBOX_LIB_NAME)] 127 | public static extern float GetParameterValue(IntPtr parameter); 128 | 129 | [DllImport(STOMPBOX_LIB_NAME)] 130 | public static extern void SetParameterValue(IntPtr parameter, float value); 131 | 132 | [DllImport(STOMPBOX_LIB_NAME)] 133 | public static extern IntPtr GetParameterName(IntPtr parameter); 134 | 135 | [DllImport(STOMPBOX_LIB_NAME)] 136 | public static extern IntPtr GetParameterDescription(IntPtr parameter); 137 | 138 | [DllImport(STOMPBOX_LIB_NAME)] 139 | public static extern float GetParameterMinValue(IntPtr parameter); 140 | 141 | [DllImport(STOMPBOX_LIB_NAME)] 142 | public static extern float GetParameterMaxValue(IntPtr parameter); 143 | 144 | [DllImport(STOMPBOX_LIB_NAME)] 145 | public static extern float GetParameterDefaultValue(IntPtr parameter); 146 | 147 | [DllImport(STOMPBOX_LIB_NAME)] 148 | public static extern float GetParameterRangePower(IntPtr parameter); 149 | 150 | [DllImport(STOMPBOX_LIB_NAME)] 151 | public static extern int GetParameterType(IntPtr parameter); 152 | 153 | [DllImport(STOMPBOX_LIB_NAME)] 154 | public static extern IntPtr GetParameterEnumValues(IntPtr parameter); 155 | 156 | [DllImport(STOMPBOX_LIB_NAME)] 157 | [return: MarshalAs(UnmanagedType.I1)] 158 | public static extern bool GetParameterCanSyncToHostBPM(IntPtr parameter); 159 | 160 | [DllImport(STOMPBOX_LIB_NAME)] 161 | public static extern int GetParameterBPMSyncNumerator(IntPtr parameter); 162 | 163 | [DllImport(STOMPBOX_LIB_NAME)] 164 | public static extern int GetParameterBPMSyncDenominator(IntPtr parameter); 165 | 166 | [DllImport(STOMPBOX_LIB_NAME)] 167 | public static extern void SetParameterBPMSyncNumerator(IntPtr parameter, int numerator); 168 | 169 | [DllImport(STOMPBOX_LIB_NAME)] 170 | public static extern void SetParameterBPMSyncDenominator(IntPtr parameter, int denom); 171 | 172 | [DllImport(STOMPBOX_LIB_NAME)] 173 | [return: MarshalAs(UnmanagedType.I1)] 174 | public static extern bool GetParameterIsAdvanced(IntPtr parameter); 175 | 176 | [DllImport(STOMPBOX_LIB_NAME)] 177 | public static extern IntPtr GetParameterFilePath(IntPtr parameter); 178 | 179 | [DllImport(STOMPBOX_LIB_NAME)] 180 | public static extern IntPtr GetParameterDisplayFormat(IntPtr parameter); 181 | 182 | 183 | 184 | public static List GetListFromStringVector(IntPtr strVec) 185 | { 186 | if (strVec == IntPtr.Zero) 187 | return null; 188 | 189 | List list = new(); 190 | 191 | uint size = NativeApi.GetStringVectorSize(strVec); 192 | 193 | for (uint i = 0; i < size; i++) 194 | { 195 | list.Add(Marshal.PtrToStringAnsi(NativeApi.GetStringVectorValue(strVec, i))); 196 | } 197 | 198 | return list; 199 | } 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPI/StompboxAPI.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | True 8 | 9 | 10 | 11 | 12 | 13 | 14 | PreserveNewest 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPI/StompboxProcessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace StompboxAPI 5 | { 6 | public class StompboxProcessor 7 | { 8 | IntPtr nativeProcessor; 9 | 10 | public string DataPath { get { return NativeApi.GetDataPath(nativeProcessor); } } 11 | 12 | public StompboxProcessor(string dataPath, bool dawMode) 13 | { 14 | nativeProcessor = NativeApi.CreateProcessor(dataPath, dawMode); 15 | } 16 | 17 | public void Init(float sampleRate) 18 | { 19 | NativeApi.InitProcessor(nativeProcessor, sampleRate); 20 | } 21 | 22 | public UnmanagedAudioPlugin CreatePlugin(string id) 23 | { 24 | return CreatePluginFromNative(NativeApi.CreatePlugin(nativeProcessor, id)); 25 | 26 | } 27 | 28 | public bool IsPresetLoading { get { return NativeApi.IsPresetLoading(nativeProcessor); } } 29 | 30 | public void StartServer() 31 | { 32 | NativeApi.StartServer(nativeProcessor); 33 | } 34 | 35 | public void HandleCommand(string command) 36 | { 37 | NativeApi.HandleCommand(nativeProcessor, command); 38 | } 39 | 40 | public bool HandleMidiCommand(int midiCommand, int midiData1, int midiData2) 41 | { 42 | return NativeApi.HandleMidiCommand(nativeProcessor, midiCommand, midiData1, midiData2); 43 | } 44 | 45 | public List GetAllPlugins() 46 | { 47 | return NativeApi.GetListFromStringVector(NativeApi.GetAllPlugins(nativeProcessor)); 48 | } 49 | 50 | public string GetGlobalChain() 51 | { 52 | return NativeApi.GetGlobalChain(nativeProcessor); 53 | } 54 | 55 | public string GetPluginSlot(string slotName) 56 | { 57 | return Marshal.PtrToStringAnsi(NativeApi.GetPluginSlot(nativeProcessor, slotName)); 58 | } 59 | 60 | public void SetPluginSlot(string slotName, string pluginID) 61 | { 62 | NativeApi.SetPluginSlot(nativeProcessor, slotName, pluginID); 63 | } 64 | 65 | public List GetChainPlugins(string chainName) 66 | { 67 | return GetChainPlugins(NativeApi.GetChainPlugins(nativeProcessor, chainName)); 68 | } 69 | 70 | public List GetPresets() 71 | { 72 | return NativeApi.GetListFromStringVector(NativeApi.GetPresets(nativeProcessor)); 73 | } 74 | 75 | public string GetCurrentPreset() 76 | { 77 | IntPtr preset = NativeApi.GetCurrentPreset(nativeProcessor); 78 | 79 | if (preset == IntPtr.Zero) 80 | return null; 81 | 82 | return Marshal.PtrToStringAnsi(preset); 83 | } 84 | 85 | public void LoadPreset(string presetName) 86 | { 87 | NativeApi.LoadPreset(nativeProcessor, presetName); 88 | } 89 | 90 | public string DumpSettings() 91 | { 92 | return NativeApi.DumpSettings(nativeProcessor); 93 | } 94 | 95 | public string DumpProgram() 96 | { 97 | return NativeApi.DumpProgram(nativeProcessor); 98 | } 99 | 100 | public unsafe void Process(float* input, float* output, uint bufferSize) 101 | { 102 | NativeApi.Process(nativeProcessor, input, output, bufferSize); 103 | } 104 | 105 | UnmanagedAudioPlugin CreatePluginFromNative(IntPtr nativePtr) 106 | { 107 | if (nativePtr == IntPtr.Zero) 108 | return null; 109 | 110 | UnmanagedAudioPlugin newPlugin = new UnmanagedAudioPlugin(); 111 | 112 | newPlugin.SetNativePlugin(nativePtr); 113 | 114 | return newPlugin; 115 | } 116 | 117 | List GetChainPlugins(IntPtr plugVec) 118 | { 119 | if (plugVec == IntPtr.Zero) 120 | return null; 121 | 122 | List list = new(); 123 | 124 | uint size = NativeApi.GetPluginVectorSize(plugVec); 125 | 126 | for (uint i = 0; i < size; i++) 127 | { 128 | list.Add(CreatePluginFromNative(NativeApi.GetPluginVectorValue(plugVec, i))); 129 | } 130 | 131 | return list; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPI/UnmanagedAudioPlugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.ObjectModel; 4 | using System.Runtime.InteropServices; 5 | using Stompbox; 6 | 7 | namespace StompboxAPI 8 | { 9 | public class UnmanagedPluginParameter : PluginParameter 10 | { 11 | internal IntPtr nativeParameter; 12 | 13 | public override float Value 14 | { 15 | get 16 | { 17 | return NativeApi.GetParameterValue(nativeParameter); 18 | } 19 | 20 | set 21 | { 22 | if (SetValue != null) 23 | SetValue(value); 24 | 25 | NativeApi.SetParameterValue(nativeParameter, value); 26 | } 27 | } 28 | 29 | public IntPtr ParameterHandle { get; set; } 30 | } 31 | 32 | public class UnmanagedAudioPlugin : AudioPluginBase, IAudioPlugin 33 | { 34 | internal IntPtr nativePlugin; 35 | 36 | public override bool Enabled 37 | { 38 | get { return NativeApi.GetPluginEnabled(nativePlugin); } 39 | set { NativeApi.SetPluginEnabled(nativePlugin, value); } 40 | } 41 | 42 | public override float OutputValue 43 | { 44 | get 45 | { 46 | return NativeApi.GetOutputValue(nativePlugin); 47 | } 48 | } 49 | 50 | Dictionary cachedParameters = new Dictionary(); 51 | 52 | internal void SetNativePlugin(IntPtr nativePlugin) 53 | { 54 | this.nativePlugin = nativePlugin; 55 | 56 | ID = Marshal.PtrToStringAnsi(NativeApi.GetPluginID(nativePlugin)); 57 | 58 | foreach (PluginParameter parameter in Parameters) 59 | { 60 | cachedParameters[parameter.Name] = (parameter.Value - parameter.MinValue) / (parameter.MaxValue - parameter.MinValue); 61 | } 62 | 63 | ObservableCollection newParameters = new ObservableCollection(); 64 | 65 | if (String.IsNullOrEmpty(Name)) 66 | Name = Marshal.PtrToStringAnsi(NativeApi.GetPluginName(nativePlugin)); 67 | 68 | Description = Marshal.PtrToStringAnsi(NativeApi.GetPluginDescription(nativePlugin)); 69 | 70 | IsUserSelectable = NativeApi.GetPluginIsUserSelectable(nativePlugin); 71 | 72 | string backgroundColor = Marshal.PtrToStringAnsi(NativeApi.GetPluginBackgroundColor(nativePlugin)); 73 | 74 | if (!string.IsNullOrEmpty(backgroundColor)) 75 | { 76 | BackgroundColor = backgroundColor; 77 | //PaintColor = (System.Windows.Media.Color)System.Windows.Media.ColorConverter.ConvertFromString(backgroundColor); 78 | } 79 | 80 | string foregroundColor = Marshal.PtrToStringAnsi(NativeApi.GetPluginForegroundColor(nativePlugin)); 81 | 82 | if (!string.IsNullOrEmpty(foregroundColor)) 83 | { 84 | ForegroundColor = foregroundColor; 85 | } 86 | 87 | uint numParameters = NativeApi.GetPluginNumParameters(nativePlugin); 88 | 89 | for (uint i = 0; i < numParameters; i++) 90 | { 91 | IntPtr nativeParameter = NativeApi.GetPluginParameter(nativePlugin, i); 92 | 93 | string paramName = Marshal.PtrToStringAnsi(NativeApi.GetParameterName(nativeParameter)); 94 | EParameterType paramType = (EParameterType)NativeApi.GetParameterType(nativeParameter); 95 | 96 | string filePath = null; 97 | 98 | if (paramType == EParameterType.File) 99 | { 100 | filePath = Marshal.PtrToStringAnsi(NativeApi.GetParameterFilePath(nativeParameter)); 101 | } 102 | 103 | String[] enumValues = null; 104 | 105 | if ((paramType == EParameterType.Enum) || (paramType == EParameterType.File)) 106 | { 107 | enumValues = NativeApi.GetListFromStringVector(NativeApi.GetParameterEnumValues(nativeParameter)).ToArray(); 108 | } 109 | 110 | int[] intValues = null; 111 | 112 | if (paramType == EParameterType.Int) 113 | { 114 | int minVal = (int)NativeApi.GetParameterMinValue(nativeParameter); 115 | int maxVal = (int)NativeApi.GetParameterMaxValue(nativeParameter); 116 | 117 | if (minVal == maxVal) 118 | continue; 119 | 120 | intValues = new int[(maxVal - minVal) + 1]; 121 | 122 | for (int intVal = 0; intVal < intValues.Length; intVal++) 123 | { 124 | intValues[intVal] = minVal + intVal; 125 | } 126 | } 127 | 128 | PluginParameter parameter = new UnmanagedPluginParameter 129 | { 130 | Plugin = this, 131 | nativeParameter = nativeParameter, 132 | Name = paramName, 133 | Description = Marshal.PtrToStringAnsi(NativeApi.GetParameterDescription(nativeParameter)), 134 | MinValue = NativeApi.GetParameterMinValue(nativeParameter), 135 | MaxValue = NativeApi.GetParameterMaxValue(nativeParameter), 136 | DefaultValue = NativeApi.GetParameterDefaultValue(nativeParameter), 137 | RangePower = NativeApi.GetParameterRangePower(nativeParameter), 138 | ParameterType = paramType, 139 | IsAdvanced = NativeApi.GetParameterIsAdvanced(nativeParameter), 140 | CanSyncToHostBPM = NativeApi.GetParameterCanSyncToHostBPM(nativeParameter), 141 | HostBPMSyncNumerator = NativeApi.GetParameterBPMSyncNumerator(nativeParameter), 142 | HostBPMSyncDenominator = NativeApi.GetParameterBPMSyncDenominator(nativeParameter), 143 | ValueFormat = Marshal.PtrToStringAnsi(NativeApi.GetParameterDisplayFormat(nativeParameter)), 144 | FilePath = filePath, 145 | EnumValues = enumValues, 146 | IntValues = intValues, 147 | GetValue = delegate 148 | { 149 | return NativeApi.GetParameterValue(nativeParameter); 150 | }, 151 | }; 152 | 153 | if (parameter.CanSyncToHostBPM) 154 | { 155 | parameter.UpdateBPMSync = delegate 156 | { 157 | NativeApi.SetParameterBPMSyncNumerator(nativeParameter, parameter.HostBPMSyncNumerator); 158 | NativeApi.SetParameterBPMSyncDenominator(nativeParameter, parameter.HostBPMSyncDenominator); 159 | }; 160 | } 161 | 162 | if ((paramType == EParameterType.Enum) || (paramType == EParameterType.File)) 163 | { 164 | EnumParameter = parameter; 165 | } 166 | 167 | if (cachedParameters.ContainsKey(parameter.Name)) 168 | { 169 | parameter.Value = parameter.MinValue + ((parameter.MaxValue - parameter.MinValue) * cachedParameters[parameter.Name]); 170 | 171 | //Logger.Log("Set cached " + Name + "/" + unmanagedWrapper.GetParameterName(index) + " to " + parameter.Value ); 172 | } 173 | 174 | newParameters.Add(parameter); 175 | } 176 | 177 | Parameters = newParameters; 178 | 179 | // Currently just for EQ-7... 180 | if (Parameters.Count > 6) 181 | { 182 | ParameterTextSize = 9; 183 | } 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPIBase/AudioPlugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.ComponentModel; 4 | 5 | namespace Stompbox 6 | { 7 | public enum EParameterType 8 | { 9 | Knob, 10 | Bool, 11 | Int, 12 | VSlider, 13 | Enum, 14 | File 15 | }; 16 | 17 | public class BPMSync 18 | { 19 | public static List Timings; 20 | 21 | public string Name { get; set; } 22 | public int Numerator { get; set; } 23 | public int Denomenator { get; set; } 24 | 25 | static BPMSync() 26 | { 27 | Timings = new List 28 | { 29 | new BPMSync 30 | { 31 | Name = "Custom", 32 | Numerator = 0, 33 | Denomenator = 0 34 | }, 35 | new BPMSync 36 | { 37 | Name = "Half Note", 38 | Numerator = 2, 39 | Denomenator = 1 40 | }, 41 | new BPMSync 42 | { 43 | Name = "Dotted 1/4 Note", 44 | Numerator = 3, 45 | Denomenator = 2 46 | }, 47 | new BPMSync 48 | { 49 | Name = "1/4 Note", 50 | Numerator = 1, 51 | Denomenator = 1 52 | }, 53 | new BPMSync 54 | { 55 | Name = "Dotted 1/8th", 56 | Numerator = 3, 57 | Denomenator = 4 58 | }, 59 | new BPMSync 60 | { 61 | Name = "Triplet of Half", 62 | Numerator = 2, 63 | Denomenator = 3 64 | }, 65 | new BPMSync 66 | { 67 | Name = "1/8th Note", 68 | Numerator = 1, 69 | Denomenator = 2 70 | }, 71 | new BPMSync 72 | { 73 | Name = "Dotted 1/16th", 74 | Numerator = 3, 75 | Denomenator = 8 76 | }, 77 | new BPMSync 78 | { 79 | Name = "Triplet of Quarter", 80 | Numerator = 1, 81 | Denomenator = 3 82 | }, 83 | new BPMSync 84 | { 85 | Name = "16th Note", 86 | Numerator = 1, 87 | Denomenator = 4 88 | } 89 | }; 90 | } 91 | } 92 | 93 | public class PluginParameter 94 | { 95 | public IAudioPlugin Plugin { get; set; } 96 | public string Name { get; set; } 97 | public string Description { get; set; } 98 | public string ValueFormat { get; set; } 99 | public float MinValue { get; set; } 100 | public float MaxValue { get; set; } 101 | public float DefaultValue { get; set; } 102 | public float RangePower { get; set; } = 1; 103 | public EParameterType ParameterType { get; set; } 104 | public bool CanSyncToHostBPM { get; set; } 105 | public int HostBPMSyncNumerator { get; set; } 106 | public int HostBPMSyncDenominator { get; set; } 107 | public bool IsAdvanced { get; set; } 108 | public bool IsVisible { get; set; } 109 | public virtual float Value { get; set; } 110 | public float NormalizedValue 111 | { 112 | get 113 | { 114 | return GetNormalizedValue(Value); 115 | } 116 | set 117 | { 118 | Value = GetRangeValue(value); 119 | } 120 | } 121 | public string DisplayValue { get { return String.Format(ValueFormat, Value); } } 122 | public String FilePath { get; set; } = null; 123 | public String[] EnumValues { get; set; } 124 | public int[] IntValues { get; set; } 125 | public int IntValue 126 | { 127 | get 128 | { 129 | return (int)Value; 130 | } 131 | set 132 | { 133 | Value = value; 134 | } 135 | } 136 | public Func GetValue { get; set; } 137 | public Action SetValue { get; set; } 138 | public Action UpdateBPMSync { get; set; } 139 | 140 | double value; 141 | 142 | public PluginParameter() 143 | { 144 | IsVisible = true; 145 | ValueFormat = "{0:0.00}"; 146 | } 147 | 148 | public float GetNormalizedValue(float value) 149 | { 150 | float val = (value - MinValue) / (MaxValue - MinValue); 151 | 152 | return (float)((RangePower < 0) ? (1 - (Math.Pow(1 - val, 1 / -RangePower))) : Math.Pow(val, 1 / RangePower)); 153 | } 154 | 155 | public float GetRangeValue(float normalizedValue) 156 | { 157 | float val = (float)((RangePower < 0) ? (1 - Math.Pow(1 - normalizedValue, -RangePower)) : Math.Pow(normalizedValue, RangePower)); 158 | 159 | return MinValue + ((MaxValue - MinValue) * val); 160 | } 161 | 162 | public override string ToString() 163 | { 164 | return Name + ": " + DisplayValue; 165 | } 166 | 167 | public PluginParameter ShallowCopy() 168 | { 169 | return (PluginParameter)this.MemberwiseClone(); 170 | } 171 | } 172 | 173 | public interface IAudioPlugin 174 | { 175 | String Name { get; set; } 176 | String ID { get; } 177 | String Description { get; set; } 178 | bool Enabled { get; set; } 179 | float OutputValue { get; set; } 180 | bool EnabledIsSwitchable { get; set; } 181 | String BackgroundColor { get; set; } 182 | String ForegroundColor { get; set; } 183 | bool IsUserSelectable { get; set; } 184 | double ParameterTextSize { get; set; } 185 | string DialStyle { get; set; } 186 | ObservableCollection Parameters { get; set; } 187 | PluginParameter EnumParameter { get; set; } 188 | Action SetEnabled { get; set; } 189 | Action SetOutputValue { get; set; } 190 | 191 | PluginParameter GetParameter(string parameterName); 192 | } 193 | 194 | public class AudioPluginBase : IAudioPlugin 195 | { 196 | public String Name { get; set; } 197 | public String ID { get; set; } 198 | public String Description { get; set; } 199 | public virtual bool Enabled 200 | { 201 | get 202 | { 203 | return enabled; 204 | } 205 | set 206 | { 207 | enabled = value; 208 | 209 | if (SetEnabled != null) 210 | SetEnabled(enabled); 211 | } 212 | } 213 | public virtual float OutputValue 214 | { 215 | get 216 | { 217 | return outputValue; 218 | } 219 | set 220 | { 221 | outputValue = value; 222 | 223 | if (SetOutputValue != null) 224 | { 225 | SetOutputValue(value); 226 | } 227 | } 228 | } 229 | public String BackgroundColor { get; set; } 230 | public String ForegroundColor { get; set; } 231 | public bool IsUserSelectable { get; set; } 232 | public double ParameterTextSize { get; set; } 233 | public string DialStyle { get; set; } 234 | public bool EnabledIsSwitchable { get; set; } 235 | public ObservableCollection Parameters { get; set; } 236 | public PluginParameter EnumParameter { get; set; } 237 | public Action SetEnabled { get; set; } 238 | public Action SetOutputValue { get; set; } 239 | 240 | protected bool enabled; 241 | float outputValue; 242 | 243 | public event PropertyChangedEventHandler PropertyChanged; 244 | protected bool initialized = false; 245 | 246 | public AudioPluginBase() 247 | { 248 | EnabledIsSwitchable = true; 249 | Parameters = new ObservableCollection(); 250 | 251 | ForegroundColor = "#000000"; 252 | BackgroundColor = "#c9c9c9"; 253 | ParameterTextSize = 11; 254 | } 255 | 256 | public override string ToString() 257 | { 258 | return ID; 259 | } 260 | 261 | public PluginParameter GetParameter(string parameterName) 262 | { 263 | foreach (PluginParameter parameter in Parameters) 264 | { 265 | if (parameter.Name == parameterName) 266 | return parameter; 267 | } 268 | 269 | return null; 270 | } 271 | } 272 | } -------------------------------------------------------------------------------- /StompboxClient/StompboxAPIBase/PluginFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Stompbox 5 | { 6 | public class PluginFactory 7 | { 8 | Dictionary loadedPlugins = new Dictionary(); 9 | 10 | StompboxClient StompboxClient; 11 | 12 | public PluginFactory(StompboxClient stompboxClient) 13 | { 14 | this.StompboxClient = stompboxClient; 15 | } 16 | 17 | public void ClearPlugins() 18 | { 19 | loadedPlugins.Clear(); 20 | } 21 | 22 | public void RegisterPlugin(IAudioPlugin plugin) 23 | { 24 | loadedPlugins[plugin.ID] = plugin; 25 | } 26 | 27 | public IAudioPlugin GetPlugin(string id) 28 | { 29 | if (!loadedPlugins.ContainsKey(id)) 30 | return null; 31 | 32 | return loadedPlugins[id]; 33 | } 34 | 35 | public IAudioPlugin GetPluginDefinition(string name) 36 | { 37 | if (loadedPlugins.ContainsKey(name)) 38 | return loadedPlugins[name]; 39 | 40 | return StompboxClient.CreatePlugin(name, name); 41 | } 42 | 43 | public IAudioPlugin CreateNewPlugin(string name) 44 | { 45 | return CreatePlugin(name, null); 46 | } 47 | 48 | public IAudioPlugin CreatePlugin(string id) 49 | { 50 | if (id == null) 51 | return null; 52 | 53 | string name = id; 54 | string[] idName = id.Split('_'); 55 | 56 | if (idName.Length > 1) 57 | { 58 | name = idName[0]; 59 | } 60 | 61 | return CreatePlugin(name, id); 62 | } 63 | 64 | public IAudioPlugin CreatePlugin(string name, string id) 65 | { 66 | IAudioPlugin newPlugin = null; 67 | 68 | if (id == null) 69 | { 70 | id = name; 71 | int number = 1; 72 | 73 | while (loadedPlugins.ContainsKey(id)) 74 | { 75 | number++; 76 | 77 | id = name + "_" + number; 78 | } 79 | } 80 | else 81 | { 82 | if (loadedPlugins.ContainsKey(id)) 83 | return loadedPlugins[id]; 84 | } 85 | 86 | newPlugin = StompboxClient.CreatePlugin(name, id); 87 | 88 | loadedPlugins[id] = newPlugin; 89 | 90 | if (newPlugin != null) 91 | { 92 | StompboxClient.Debug("New plugin: " + newPlugin.Name + "[" + newPlugin.ID + "]"); 93 | } 94 | 95 | return newPlugin; 96 | } 97 | 98 | public void ReleasePlugin(IAudioPlugin plugin) 99 | { 100 | if (loadedPlugins.ContainsKey(plugin.ID)) 101 | { 102 | loadedPlugins.Remove(plugin.ID); 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPIBase/StompboxAPIBase.projitems: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | ef794465-7976-4ad3-b2cc-dea7a85a80ae 7 | 8 | 9 | StompboxAPIBase 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPIBase/StompboxClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | using System.Security.Cryptography; 6 | using System.Threading; 7 | using System.Xml.Linq; 8 | 9 | namespace Stompbox 10 | { 11 | public class MidiCCMapEntry 12 | { 13 | public int CCNumber { get; set; } 14 | public string PluginName { get; set; } 15 | public string PluginParameter { get; set; } 16 | } 17 | 18 | public class StompboxClient 19 | { 20 | public static StompboxClient Instance { get; private set; } 21 | 22 | public static Action DebugAction { get; set; } 23 | 24 | public PluginFactory PluginFactory { get; set; } 25 | public Action MidiCallback { get; set; } 26 | 27 | public virtual bool Connected { get { return false; } } 28 | public float BPM { get; set; } 29 | public bool InClientMode { get; protected set; } 30 | public bool AllowMidiMapping { get; set; } 31 | public string PluginPath { get; set; } 32 | public float MaxDSPLoad { get; private set; } 33 | public float MinDSPLoad { get; private set; } 34 | public List MidiCCMap { get; private set; } = new List(); 35 | public int MidiModeCC { get; set; } = -1; 36 | public Dictionary MidiStompCCMap { get; private set; } = new Dictionary(); 37 | public bool StopSimulateAudio { get; set; } 38 | public List PresetNames { get; private set; } 39 | 40 | public IEnumerable AllActivePlugins 41 | { 42 | get 43 | { 44 | yield break; 45 | } 46 | } 47 | 48 | public virtual int CurrentPresetIndex { get; set; } = -1; 49 | 50 | public virtual bool NeedUIReload { get; set; } 51 | 52 | bool needUIReload = false; 53 | 54 | public StompboxClient() 55 | { 56 | Instance = this; 57 | 58 | Debug("Creating StompboxClient."); 59 | 60 | AllowMidiMapping = true; 61 | BPM = 120; 62 | 63 | PluginPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "stompbox"); 64 | 65 | PluginFactory = new PluginFactory(this); 66 | 67 | PresetNames = new List(); 68 | } 69 | 70 | public virtual void Connect(string serverName, int port, Action connectCallback) 71 | { 72 | } 73 | 74 | public virtual void UpdatePresets() 75 | { 76 | } 77 | 78 | public virtual void UpdateProgram() 79 | { 80 | UpdatePresets(); 81 | } 82 | 83 | public virtual void SaveCurrentPreset() 84 | { 85 | 86 | } 87 | 88 | public virtual void SavePresetAs(string presetName) 89 | { 90 | 91 | } 92 | 93 | public virtual void DeleteCurrentPreset() 94 | { 95 | 96 | } 97 | 98 | public virtual string GetGlobalChain() 99 | { 100 | throw new NotImplementedException(); 101 | } 102 | 103 | public virtual IEnumerable GetAllPluginNames() 104 | { 105 | return null; 106 | } 107 | 108 | public IEnumerable GetAllPluginDefinitions() 109 | { 110 | foreach (string name in GetAllPluginNames()) 111 | { 112 | yield return GetPluginDefinition(name); 113 | } 114 | } 115 | 116 | public IEnumerable GetAllUserPluginDefinitions() 117 | { 118 | foreach (string name in GetAllPluginNames()) 119 | { 120 | IAudioPlugin plugin = GetPluginDefinition(name); 121 | 122 | if (plugin.IsUserSelectable) 123 | yield return plugin; 124 | } 125 | } 126 | 127 | public virtual IAudioPlugin GetPluginDefinition(string pluginName) 128 | { 129 | return null; 130 | } 131 | 132 | public virtual IAudioPlugin CreatePlugin(string pluginName, string pluginID) 133 | { 134 | return null; 135 | } 136 | 137 | public virtual void SetChain(string name, List plugins) 138 | { 139 | 140 | } 141 | 142 | public virtual List GetChain(string name) 143 | { 144 | throw new NotImplementedException(); 145 | } 146 | 147 | public virtual void SendCommand(string command) 148 | { 149 | } 150 | 151 | public void Debug(string debugStr) 152 | { 153 | if (DebugAction != null) 154 | DebugAction(debugStr); 155 | } 156 | 157 | 158 | public void ReportDSPLoad(float maxDSPLoad, float minDSPLoad) 159 | { 160 | MaxDSPLoad = maxDSPLoad; 161 | MinDSPLoad = minDSPLoad; 162 | } 163 | 164 | public void SetPresetNames(List presetNames) 165 | { 166 | PresetNames.Clear(); 167 | 168 | foreach (string presetName in presetNames) 169 | { 170 | PresetNames.Add(presetName); 171 | } 172 | } 173 | 174 | public void SetSelectedPreset(string presetName) 175 | { 176 | Debug("Set selected preset: " + presetName); 177 | 178 | CurrentPresetIndex = PresetNames.IndexOf(presetName); 179 | } 180 | 181 | public virtual void UpdateUI() 182 | { 183 | Debug("*** Update UI"); 184 | 185 | NeedUIReload = true; 186 | } 187 | 188 | protected virtual IAudioPlugin CreateSlotPlugin(string slotName, string defaultPlugin) 189 | { 190 | string pluginID = GetSlotPlugin(slotName); 191 | 192 | if (pluginID == null) 193 | { 194 | pluginID = defaultPlugin; 195 | } 196 | 197 | SetSlotPlugin(slotName, pluginID); 198 | 199 | return PluginFactory.CreatePlugin(pluginID); 200 | } 201 | 202 | public virtual void SetDefaultSlotPlugin(string slotName, string pluginID) 203 | { 204 | if (GetSlotPlugin(slotName) == null) 205 | SetSlotPlugin(slotName, pluginID); 206 | 207 | } 208 | 209 | public virtual void SetSlotPlugin(string slotName, string pluginID) 210 | { 211 | 212 | } 213 | 214 | public virtual string GetSlotPlugin(string slotName) 215 | { 216 | return null; 217 | } 218 | 219 | public virtual void UpdateChain(string chainName, List plugins) 220 | { 221 | string cmd = "SetChain " + chainName; 222 | 223 | foreach (IAudioPlugin plugin in plugins) 224 | { 225 | cmd += " " + plugin.ID; 226 | } 227 | 228 | StompboxClient.Instance.SendCommand(cmd); 229 | } 230 | 231 | public virtual void Init(float sampleRate) 232 | { 233 | } 234 | 235 | void HandleMidi(int midiCommand, int midiData1, int midiData2) 236 | { 237 | if (MidiCallback != null) 238 | MidiCallback(midiCommand, midiData1, midiData2); 239 | } 240 | 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPIBase/StompboxClientBase.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ef794465-7976-4ad3-b2cc-dea7a85a80ae 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPITest/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Runtime.InteropServices; 3 | using System.Threading; 4 | using StompboxAPI; 5 | 6 | namespace StompboxAPITest 7 | { 8 | internal class Program 9 | { 10 | static void Main(string[] args) 11 | { 12 | APIClient client = new(); 13 | 14 | client.Init(48000); 15 | 16 | Thread thread = new Thread(new ThreadStart(client.SimulateAudio)); 17 | thread.Start(); 18 | 19 | client.SetSelectedPreset("tmp"); 20 | 21 | Thread.Sleep(1000); 22 | 23 | string state = client.GetProgramState(); 24 | 25 | var tuner = client.PluginFactory.CreatePlugin("Tuner"); 26 | 27 | tuner.Enabled = true; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /StompboxClient/StompboxAPITest/StompboxAPITest.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | True 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /StompboxClient/StompboxRemoteClient/NetworkInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.Sockets; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace StompboxAPI 8 | { 9 | public class NetworkClient 10 | { 11 | public bool Running { get; private set; } 12 | public bool Connected { get; private set; } 13 | public string LastError { get; private set; } 14 | 15 | TcpClient client; 16 | NetworkStream stream; 17 | Thread runThread = null; 18 | 19 | public Action LineHandler { get; set; } 20 | public Action DebugAction { get; set; } 21 | 22 | Action resultCallback = null; 23 | 24 | public NetworkClient() 25 | { 26 | } 27 | 28 | public bool Start(string serverName, int port, Action resultCallback) 29 | { 30 | this.resultCallback = resultCallback; 31 | 32 | try 33 | { 34 | Debug("** Connecting to " + serverName + ":" + port); 35 | 36 | client = new TcpClient(); 37 | 38 | var connectionTask = client.ConnectAsync(serverName, port).ContinueWith(task => 39 | { 40 | return task.IsFaulted ? null : client; 41 | }, TaskContinuationOptions.ExecuteSynchronously); 42 | var timeoutTask = Task.Delay(5000) 43 | .ContinueWith(task => null, TaskContinuationOptions.ExecuteSynchronously); 44 | var resultTask = Task.WhenAny(connectionTask, timeoutTask).Unwrap(); 45 | 46 | var resultTcpClient = resultTask.GetAwaiter().GetResult(); 47 | 48 | if (client.Connected) 49 | { 50 | stream = client.GetStream(); 51 | 52 | Connected = true; 53 | 54 | Debug("** Connected!"); 55 | 56 | runThread = new Thread(RunServer); 57 | runThread.Start(); 58 | } 59 | 60 | resultCallback(client.Connected); 61 | 62 | return client.Connected; 63 | } 64 | catch (Exception ex) 65 | { 66 | LastError = ex.ToString(); 67 | 68 | Debug("** Connect failed with: " + LastError); 69 | 70 | resultCallback(false); 71 | } 72 | 73 | return false; 74 | } 75 | 76 | void Debug(string debugStr) 77 | { 78 | if (DebugAction != null) 79 | DebugAction(debugStr); 80 | } 81 | 82 | public void Stop() 83 | { 84 | client.Close(); 85 | 86 | if (runThread != null) 87 | runThread.Join(); 88 | } 89 | 90 | public void SendData(String data) 91 | { 92 | if (!Running) 93 | return; 94 | 95 | try 96 | { 97 | Byte[] byteData = System.Text.Encoding.ASCII.GetBytes(data); 98 | 99 | stream.Write(byteData, 0, data.Length); 100 | } 101 | catch 102 | { 103 | } 104 | } 105 | 106 | void RunServer() 107 | { 108 | try 109 | { 110 | using (StreamReader reader = new StreamReader(stream)) 111 | { 112 | Running = true; 113 | 114 | resultCallback(true); // Do this here to make sure we don't send any messages before we are ready to get the response 115 | 116 | do 117 | { 118 | string line = reader.ReadLine(); 119 | 120 | if (String.IsNullOrEmpty(line)) 121 | { 122 | if (client.Client.Poll(0, SelectMode.SelectRead)) 123 | { 124 | byte[] buff = new byte[1]; 125 | 126 | if (client.Client.Receive(buff, SocketFlags.Peek) == 0) 127 | { 128 | break; 129 | } 130 | } 131 | } 132 | else 133 | { 134 | if (LineHandler != null) 135 | LineHandler(line); 136 | } 137 | } 138 | while (true); 139 | } 140 | } 141 | catch (Exception ex) 142 | { 143 | } 144 | 145 | Debug("** Connection ended"); 146 | 147 | Running = false; 148 | Connected = false; 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /StompboxClient/StompboxRemoteClient/RemoteClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Stompbox; 5 | 6 | namespace StompboxAPI 7 | { 8 | public class RemoteClient : StompboxClient 9 | { 10 | public static new RemoteClient Instance { get { return StompboxClient.Instance as RemoteClient; } } 11 | 12 | public bool SuppressCommandUpdates { get; set; } 13 | 14 | Dictionary slotPlugins = new(); 15 | Dictionary> chains = new(); 16 | 17 | NetworkClient networkClient; 18 | ProtocolClient protocolClient; 19 | 20 | public override bool Connected 21 | { 22 | get 23 | { 24 | return networkClient.Connected; 25 | } 26 | } 27 | 28 | public override int CurrentPresetIndex 29 | { 30 | get => base.CurrentPresetIndex; 31 | 32 | set 33 | { 34 | base.CurrentPresetIndex = value; 35 | 36 | if ((CurrentPresetIndex >= 0) && !SuppressCommandUpdates) 37 | { 38 | SendCommand("LoadPreset " + PresetNames[base.CurrentPresetIndex]); 39 | } 40 | } 41 | } 42 | 43 | public RemoteClient() 44 | : base() 45 | { 46 | InClientMode = true; 47 | 48 | protocolClient = new ProtocolClient(this); 49 | 50 | networkClient = new NetworkClient(); 51 | networkClient.LineHandler = HandleCommand; 52 | networkClient.DebugAction = delegate (string debug) 53 | { 54 | Debug(debug); 55 | }; 56 | } 57 | 58 | Action connectCallback; 59 | 60 | public override void Connect(string serverName, int port, Action connectCallback) 61 | { 62 | if (InClientMode) 63 | { 64 | this.connectCallback = connectCallback; 65 | 66 | Debug("Connect to server: " + serverName); 67 | 68 | networkClient.Start(serverName, 24639, ConnectCallback); 69 | } 70 | } 71 | 72 | public void Disconnect() 73 | { 74 | if (InClientMode) 75 | { 76 | networkClient.Stop(); 77 | } 78 | } 79 | 80 | void ConnectCallback(bool result) 81 | { 82 | if (result) 83 | { 84 | Debug("Connected"); 85 | 86 | networkClient.LineHandler = HandleCommand; 87 | 88 | RequestConfigDump(); 89 | 90 | connectCallback(true); 91 | } 92 | else 93 | { 94 | Debug("Connect failed"); 95 | 96 | connectCallback(false); 97 | } 98 | } 99 | 100 | public override void UpdatePresets() 101 | { 102 | base.UpdatePresets(); 103 | 104 | SendCommand("List Presets"); 105 | } 106 | 107 | public override void UpdateProgram() 108 | { 109 | base.UpdateProgram(); 110 | 111 | SendCommand("Dump Program"); 112 | } 113 | 114 | public override void SaveCurrentPreset() 115 | { 116 | SendCommand("SavePreset " + PresetNames[CurrentPresetIndex]); 117 | } 118 | 119 | public override void SavePresetAs(string presetName) 120 | { 121 | SendCommand("SavePreset " + presetName); 122 | SendCommand("List Presets"); 123 | UpdateProgram(); 124 | } 125 | 126 | public override void DeleteCurrentPreset() 127 | { 128 | SendCommand("DeletePreset " + PresetNames[CurrentPresetIndex]); 129 | 130 | UpdateProgram(); 131 | } 132 | 133 | public void RequestConfigDump() 134 | { 135 | //Thread.Sleep(500); 136 | 137 | SendCommand("Dump Config"); 138 | SendCommand("List Presets"); 139 | 140 | if (InClientMode) 141 | SendCommand("PluginOutputOn"); 142 | 143 | UpdateProgram(); 144 | } 145 | 146 | public override IEnumerable GetAllPluginNames() 147 | { 148 | return protocolClient.PluginNames; 149 | } 150 | 151 | public override IAudioPlugin GetPluginDefinition(string pluginName) 152 | { 153 | return protocolClient.GetPluginDefinition(pluginName); 154 | } 155 | 156 | public override IAudioPlugin CreatePlugin(string pluginName, string pluginID) 157 | { 158 | return protocolClient.CreateNewPlugin(pluginName, pluginID); 159 | } 160 | 161 | string[] lineSeparator = new string[] { "\r", "\n" }; 162 | 163 | public void HandleCommand(string commandStr) 164 | { 165 | if (InClientMode) 166 | { 167 | string[] commands = commandStr.Split(lineSeparator, 0); 168 | 169 | foreach (string command in commands) 170 | { 171 | if (!string.IsNullOrWhiteSpace(command)) 172 | { 173 | protocolClient.HandleCommand(command); 174 | } 175 | } 176 | } 177 | } 178 | 179 | public override void SendCommand(string command) 180 | { 181 | Debug("Send command: " + command); 182 | 183 | if (SuppressCommandUpdates) 184 | { 185 | Debug("*** Command suppressed"); 186 | } 187 | else 188 | { 189 | if (networkClient != null) 190 | { 191 | if (!networkClient.Connected) 192 | { 193 | Debug("*** Network client not connected"); 194 | } 195 | else 196 | { 197 | Debug("Actullay sending"); 198 | networkClient.SendData(command + "\r\n"); 199 | } 200 | } 201 | } 202 | } 203 | 204 | public override void SetSlotPlugin(string slotName, string pluginID) 205 | { 206 | slotPlugins[slotName] = pluginID; 207 | 208 | string cmd = "SetPluginSlot " + slotName + " " + pluginID; 209 | 210 | StompboxClient.Instance.SendCommand(cmd); 211 | } 212 | 213 | public override string GetSlotPlugin(string slotName) 214 | { 215 | string pluginID; 216 | 217 | if (slotPlugins.TryGetValue(slotName, out pluginID)) 218 | { 219 | return pluginID; 220 | } 221 | 222 | return null; 223 | } 224 | 225 | public override void SetChain(string name, List plugins) 226 | { 227 | chains[name] = plugins; 228 | } 229 | 230 | public override List GetChain(string name) 231 | { 232 | if (chains.ContainsKey(name)) 233 | { 234 | return chains[name]; 235 | } 236 | 237 | return new List(); 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /StompboxClient/StompboxRemoteClient/RemotePlugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Stompbox; 3 | 4 | namespace StompboxAPI 5 | { 6 | public class RemoteParameter : PluginParameter 7 | { 8 | public override float Value 9 | { 10 | get => base.Value; 11 | 12 | set 13 | { 14 | if (SetValue != null) 15 | SetValue(value); 16 | 17 | base.Value = value; 18 | 19 | if (!RemoteClient.Instance.SuppressCommandUpdates) 20 | { 21 | if (CanSyncToHostBPM && (HostBPMSyncNumerator != 0) && (HostBPMSyncDenominator != 0)) 22 | { 23 | RemoteClient.Instance.SendCommand("SetParam " + Plugin.ID + " " + Name + " " + HostBPMSyncNumerator + "/" + HostBPMSyncDenominator); 24 | } 25 | else 26 | { 27 | RemoteClient.Instance.SendCommand("SetParam " + Plugin.ID + " " + Name + " " + 28 | (((ParameterType == EParameterType.Enum) || (ParameterType == EParameterType.File)) ? ((EnumValues.Length > 0) ? EnumValues[(int)Value] : "") : Value.ToString())); 29 | } 30 | } 31 | } 32 | } 33 | } 34 | 35 | public class RemotePlugin : AudioPluginBase 36 | { 37 | public override bool Enabled 38 | { 39 | get => base.Enabled; 40 | 41 | set 42 | { 43 | base.Enabled = value; 44 | 45 | if (!RemoteClient.Instance.SuppressCommandUpdates) 46 | { 47 | RemoteClient.Instance.SendCommand("SetParam " + ID + " Enabled " + (enabled ? "1" : "0")); 48 | } 49 | } 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /StompboxClient/StompboxRemoteClient/StompboxRemoteClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /StompboxContent/Content/Textures/UISheet0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxContent/Content/Textures/UISheet0.png -------------------------------------------------------------------------------- /StompboxHost/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxHost/Icon.ico -------------------------------------------------------------------------------- /StompboxHost/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using AudioPlugSharpHost; 3 | 4 | namespace Stompbox 5 | { 6 | class Program 7 | { 8 | [STAThread] 9 | static void Main(string[] args) 10 | { 11 | //StompboxPlugin plugin = new(); 12 | //var host = new WindowsFormsHost(plugin); 13 | 14 | MiniPlugins.AmpPlugin plugin = new(); 15 | var host = new WindowsFormsHost(plugin); 16 | 17 | host.Run(); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /StompboxHost/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "StompboxSimHost": { 4 | "commandName": "Project", 5 | "nativeDebugging": true 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /StompboxHost/StompboxHost.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net8.0-windows 5 | Icon.ico 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /StompboxImageProcessor/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using System.IO; 4 | using System.Reflection; 5 | 6 | namespace StompboxImageProcessor 7 | { 8 | class StompboxImageProcessor : ImageSheetProcessor.ImageSheetProcessor 9 | { 10 | public void RenderImages(string destPath) 11 | { 12 | BeginRenderImages(destPath); 13 | Render(); 14 | 15 | EndRenderImages(); 16 | } 17 | 18 | public void Render() 19 | { 20 | MaxImageSheetSize = 2048; 21 | 22 | BeginSpriteSheetGroup("UISheet"); 23 | 24 | string font = Path.Combine(SrcPath, "Inter_18pt-Bold.ttf"); 25 | 26 | AddFont("MainFont", font, 36); 27 | AddFont("SmallFont", font, 26); 28 | 29 | PushDirectory("UserInterface"); 30 | 31 | Add("SingleWhitePixel"); 32 | 33 | //CreateRotations("DialPointer", "DialPointerRotations", 32); 34 | 35 | //DoSquaredSprites = true; 36 | //processor.Scale("DialPointerRotations", 128, 128); 37 | //processor.DoSquaredSprites = false; 38 | 39 | Add("StompboxLogo"); 40 | 41 | Add("InputChain"); 42 | Add("OutputChain"); 43 | Add("FxLoopChain"); 44 | 45 | AddWithShadow("HoverTextOutline"); 46 | 47 | Add("DialBackground"); 48 | Add("DialPointer"); 49 | 50 | Add("PowerOff"); 51 | Add("PowerOn"); 52 | 53 | Add("PopupBackground"); 54 | Add("PluginBackground"); 55 | Add("ButtonPressed"); 56 | Add("ButtonUnpressed"); 57 | 58 | Add("LevelDisplay"); 59 | 60 | Add("TunerPoint"); 61 | 62 | Add("VerticalSlider"); 63 | 64 | Add("MoreButton"); 65 | 66 | Add("StompOutline"); 67 | 68 | Add("Play"); 69 | Add("Stop"); 70 | Add("Restart"); 71 | Add("Record"); 72 | 73 | PopDirectory(); 74 | 75 | EndSpriteSheetGroup(); 76 | } 77 | 78 | void CreateRotations(string srcImage, string destImage, int numRotations) 79 | { 80 | string srcPath = GetSourcePath(srcImage); 81 | string destPath = GetSourcePath(destImage); 82 | 83 | Bitmap srcBitmap = (Bitmap)Bitmap.FromFile(srcPath); 84 | srcBitmap.SetResolution(96, 96); 85 | 86 | Bitmap destBitmap = new Bitmap(srcBitmap.Width * numRotations, srcBitmap.Height); 87 | 88 | Bitmap tmpBitmap = new Bitmap(srcBitmap.Width, srcBitmap.Height); 89 | 90 | for (int i = 0; i < numRotations; i++) 91 | { 92 | float angle = ((float)i / (float)numRotations) * 360f; 93 | 94 | Rectangle srcRect = new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height); 95 | 96 | using (Graphics gt = Graphics.FromImage(tmpBitmap)) 97 | { 98 | using (Graphics g = Graphics.FromImage(destBitmap)) 99 | { 100 | gt.Clear(Color.Transparent); 101 | 102 | //move rotation point to center of image 103 | gt.TranslateTransform((float)srcBitmap.Width / 2, (float)srcBitmap.Height / 2); 104 | //rotate 105 | gt.RotateTransform(angle); 106 | //move image back 107 | gt.TranslateTransform(-(float)srcBitmap.Width / 2, -(float)srcBitmap.Height / 2); 108 | //draw passed in image onto graphics object 109 | gt.DrawImage(srcBitmap, 0, 0, srcRect, GraphicsUnit.Pixel); 110 | 111 | g.DrawImage(tmpBitmap, i * srcBitmap.Width, 0, srcRect, GraphicsUnit.Pixel); 112 | } 113 | } 114 | } 115 | 116 | destBitmap.Save(destPath, System.Drawing.Imaging.ImageFormat.Png); 117 | } 118 | } 119 | 120 | 121 | class Program 122 | { 123 | static void Main(string[] args) 124 | { 125 | var processor = new StompboxImageProcessor(); 126 | 127 | string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @"..\..\..\.."); 128 | 129 | processor.ForceRegen = false; 130 | 131 | processor.SrcPath = Path.Combine(path, "SrcTextures"); 132 | 133 | processor.RenderImages(Path.Combine(path, @"StompboxShared\Content\Textures")); 134 | } 135 | 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /StompboxImageProcessor/StompboxImageProcessor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /StompboxPlugin/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Windows; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("StompboxPlugin")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("StompboxPlugin")] 13 | [assembly: AssemblyCopyright("Copyright © 2020")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7bc972c1-afea-4dfd-be4d-71dc5a544274")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /StompboxPlugin/README.txt: -------------------------------------------------------------------------------- 1 | Stompbox 2 | 3 | Copyright (c) 2024-2025 Mike Oliphant 4 | 5 | Source code and license information can be found here: 6 | 7 | https://github.com/mikeoliphant/StompboxUI 8 | 9 | This is a VST3 plugin, and as such must be run within a VST host such as 10 | a DAW (Digital Audio Workstation) application. 11 | 12 | For your host to see the plugin, the "Stompbox" folder must be copied to your 13 | VST3 path - usually "C:\Program Files\Common Files\VST3". 14 | 15 | **Important Note**: You must have .NET 8.0 or later installed: 16 | 17 | https://dotnet.microsoft.com/en-us/download/dotnet/8.0 18 | 19 | If you are still having trouble getting the plugin to work, please check out 20 | troubleshooting instructions here: 21 | 22 | https://github.com/mikeoliphant/StompboxUI/blob/master/StompboxPlugin/Troubleshooting.md 23 | -------------------------------------------------------------------------------- /StompboxPlugin/StompboxPlugin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Threading; 4 | using AudioPlugSharp; 5 | using UILayout; 6 | 7 | namespace Stompbox 8 | { 9 | public unsafe class StompboxPlugin : AudioPlugSharp.AudioPluginBase 10 | { 11 | public int CurrentProgram { get; private set; } 12 | 13 | public StompboxAPI.APIClient StompboxClient { get; private set; } 14 | public MonoGameHost GameHost { get; private set; } = null; 15 | 16 | FloatAudioIOPort monoInput; 17 | FloatAudioIOPort monoOutput; 18 | IntPtr bitConvertBuffer = IntPtr.Zero; 19 | uint bitConvertBufferSize = 0; 20 | 21 | public StompboxPlugin() 22 | { 23 | Company = "Nostatic Software"; 24 | Website = "nostatic.org"; 25 | Contact = "contact@nostatic.org"; 26 | PluginName = "stompbox"; 27 | PluginCategory = "Fx"; 28 | PluginVersion = "0.1.14"; 29 | 30 | PluginID = 0x43D53D93648B49CA; 31 | 32 | HasUserInterface = true; 33 | 34 | //Logger.ImmediateMode = true; 35 | 36 | //Logger.ImmediateMode = true; 37 | //Logger.WriteToStdErr = true; 38 | } 39 | 40 | public override void Initialize() 41 | { 42 | base.Initialize(); 43 | 44 | Debug("Initialize"); 45 | 46 | InputPorts = new AudioIOPort[] { monoInput = new FloatAudioIOPort("Mono Input", EAudioChannelConfiguration.Mono) }; 47 | OutputPorts = new AudioIOPort[] { monoOutput = new FloatAudioIOPort("Mono Output", EAudioChannelConfiguration.Mono) }; 48 | 49 | EditorWidth = 1000; 50 | EditorHeight = 540; 51 | 52 | //GCSettings.LatencyMode = GCLatencyMode.LowLatency;// GCLatencyMode.Batch; // SustainedLowLatency; 53 | 54 | StompboxAPI.APIClient.DebugAction = Debug; 55 | 56 | StompboxClient = new StompboxAPI.APIClient(); 57 | 58 | StompboxClient.MidiCallback = SendMidiCommand; 59 | } 60 | 61 | public override void Stop() 62 | { 63 | base.Stop(); 64 | 65 | GC.Collect(); 66 | } 67 | 68 | public void DisposeDevice() 69 | { 70 | } 71 | 72 | public void Debug(String debugStr) 73 | { 74 | Logger.Log(debugStr); 75 | } 76 | 77 | 78 | public void ReportDSPLoad(float maxDSPLoad, float minDSPLoad) 79 | { 80 | } 81 | 82 | public virtual void UpdateUI() 83 | { 84 | StompboxClient.UpdateUI(); 85 | } 86 | 87 | IntPtr parentWindow; 88 | 89 | public override void ShowEditor(IntPtr parentWindow) 90 | { 91 | Logger.Log("Show Editor"); 92 | 93 | this.parentWindow = parentWindow; 94 | 95 | if (parentWindow == IntPtr.Zero) 96 | { 97 | RunGame(); 98 | } 99 | else 100 | { 101 | new Thread(new ThreadStart(RunGame)).Start(); 102 | } 103 | } 104 | 105 | void RunGame() 106 | { 107 | Logger.Log("Start game"); 108 | 109 | try 110 | { 111 | int screenWidth = (int)EditorWidth; 112 | int screenHeight = (int)EditorHeight; 113 | 114 | StompboxLayout game = new StompboxLayout(); 115 | 116 | game.Scale = 0.35f; 117 | 118 | using (GameHost = new MonoGameHost(parentWindow, screenWidth, screenHeight, fullscreen: false)) 119 | { 120 | GameHost.IsMouseVisible = true; 121 | 122 | GameHost.StartGame(game); 123 | 124 | StompboxClient.NeedUIReload = true; 125 | } 126 | 127 | game = null; 128 | } 129 | catch (Exception ex) 130 | { 131 | Logger.Log("Run game failed with: " + ex.ToString()); 132 | } 133 | } 134 | 135 | public override void ResizeEditor(uint newWidth, uint newHeight) 136 | { 137 | base.ResizeEditor(newWidth, newHeight); 138 | 139 | if (GameHost != null) 140 | { 141 | GameHost.RequestResize((int)newWidth, (int)newHeight); 142 | } 143 | } 144 | 145 | 146 | public override void HideEditor() 147 | { 148 | base.HideEditor(); 149 | 150 | GameHost.Exit(); 151 | } 152 | 153 | public override void InitializeProcessing() 154 | { 155 | base.InitializeProcessing(); 156 | 157 | Logger.Log("Sample rate is: " + Host.SampleRate); 158 | 159 | StompboxClient.Init((float)Host.SampleRate); 160 | } 161 | 162 | public override byte[] SaveState() 163 | { 164 | byte[] data = null; 165 | 166 | try 167 | { 168 | data = System.Text.Encoding.ASCII.GetBytes(StompboxClient.GetProgramState()); 169 | } 170 | catch (Exception ex) 171 | { 172 | Logger.Log("Save state failed with: " + ex.ToString()); 173 | } 174 | 175 | return data; 176 | } 177 | 178 | string[] lineSeparator = new string[] { "\r", "\n" }; 179 | 180 | public override void RestoreState(byte[] stateData) 181 | { 182 | String programString = System.Text.Encoding.ASCII.GetString(stateData); 183 | 184 | string[] commands = programString.Split(lineSeparator, 0); 185 | 186 | foreach (string command in commands) 187 | { 188 | if (!string.IsNullOrWhiteSpace(command)) 189 | { 190 | StompboxClient.SendCommand(command); 191 | } 192 | } 193 | 194 | UpdateUI(); 195 | } 196 | 197 | Stopwatch processWatch = new Stopwatch(); 198 | 199 | void SendMidiCommand(int midiCommand, int midiData1, int midiData2) 200 | { 201 | Logger.Log("Send midi: " + midiCommand + " " + midiData1 + " " + midiData2); 202 | 203 | switch (midiCommand) 204 | { 205 | case 0x80: 206 | Host.SendNoteOff(1, midiData1, (float)midiData2 / 127.0f, 0); 207 | break; 208 | case 0x90: 209 | Host.SendNoteOn(1, midiData1, (float)midiData2 / 127.0f, 0); 210 | break; 211 | } 212 | } 213 | 214 | public override void Process() 215 | { 216 | StompboxClient.Process(((float**)monoInput.GetAudioBufferPtrs())[0], ((float**)monoOutput.GetAudioBufferPtrs())[0], monoInput.CurrentBufferSize); 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /StompboxPlugin/StompboxPlugin.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net8.0-windows 4 | Library 5 | false 6 | true 7 | true 8 | true 9 | AllDisabledByDefault 10 | AnyCPU 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | TRACE;XNA 29 | 30 | 31 | TRACE;XNA 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /StompboxPlugin/Troubleshooting.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## Installing 4 | 5 | Make sure that the ChartPlayer folder is installed where your DAW looks for VST3 plugins. Normally this is "**C:\Program Files\Common Files\VST3**". 6 | 7 | Also ensure that you have .NET 8.0 installed on your system. You can get it here: 8 | 9 | https://dotnet.microsoft.com/en-us/download/dotnet/8.0 10 | 11 | ## Stompbox Plugin Doesn't Scan in DAW 12 | 13 | If your DAW isn't loading the plugin, first make sure that you had it re-scan plugins after installing .net 8.0 14 | 15 | If you are still having trouble, please check for log files here: 16 | 17 | "**%appdata%\AudioPlugSharp**" 18 | 19 | Error messages here can help track down the problem. 20 | 21 | ## Reporting Problems 22 | 23 | If you are having trouble, please get in touch. The best way is through opening an issue or a discussion here on GitHub. 24 | -------------------------------------------------------------------------------- /StompboxRemote/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-mgcb": { 6 | "version": "3.8.1.303", 7 | "commands": [ 8 | "mgcb" 9 | ] 10 | }, 11 | "dotnet-mgcb-editor": { 12 | "version": "3.8.1.303", 13 | "commands": [ 14 | "mgcb-editor" 15 | ] 16 | }, 17 | "dotnet-mgcb-editor-linux": { 18 | "version": "3.8.1.303", 19 | "commands": [ 20 | "mgcb-editor-linux" 21 | ] 22 | }, 23 | "dotnet-mgcb-editor-windows": { 24 | "version": "3.8.1.303", 25 | "commands": [ 26 | "mgcb-editor-windows" 27 | ] 28 | }, 29 | "dotnet-mgcb-editor-mac": { 30 | "version": "3.8.1.303", 31 | "commands": [ 32 | "mgcb-editor-mac" 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /StompboxRemote/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxRemote/Icon.ico -------------------------------------------------------------------------------- /StompboxRemote/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Stompbox; 3 | using StompboxAPI; 4 | using UILayout; 5 | 6 | RemoteClient client = new(); 7 | 8 | InterfaceBase.InterfaceType = EStompboxInterfaceType.DAW; 9 | 10 | StompboxLayout game = new StompboxLayout(); 11 | 12 | int width = 1000; 13 | int height = 540; 14 | 15 | if (InterfaceBase.InterfaceType == EStompboxInterfaceType.Mobile) 16 | { 17 | width = 540; 18 | height = 960; 19 | } 20 | else if (InterfaceBase.InterfaceType == EStompboxInterfaceType.Pedalboard) 21 | { 22 | width = 960; 23 | height = 540; 24 | } 25 | 26 | using (MonoGameHost GameHost = new MonoGameHost(width, height, fullscreen: false)) 27 | { 28 | GameHost.IsMouseVisible = true; 29 | GameHost.StartGame(game); 30 | 31 | client.Disconnect(); 32 | } 33 | 34 | -------------------------------------------------------------------------------- /StompboxRemote/StompboxRemote.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | WinExe 4 | net8.0-windows 5 | Major 6 | false 7 | false 8 | true 9 | 10 | 11 | app.manifest 12 | Icon.ico 13 | True 14 | 15 | 16 | $(DefineConstants);STOMPBOXREMOTE 17 | 18 | 19 | $(DefineConstants);STOMPBOXREMOTE 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /StompboxRemote/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | true/pm 39 | permonitorv2,permonitor 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /StompboxRemoteGL/.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "dotnet-mgcb": { 6 | "version": "3.8.1.303", 7 | "commands": [ 8 | "mgcb" 9 | ] 10 | }, 11 | "dotnet-mgcb-editor": { 12 | "version": "3.8.1.303", 13 | "commands": [ 14 | "mgcb-editor" 15 | ] 16 | }, 17 | "dotnet-mgcb-editor-linux": { 18 | "version": "3.8.1.303", 19 | "commands": [ 20 | "mgcb-editor-linux" 21 | ] 22 | }, 23 | "dotnet-mgcb-editor-windows": { 24 | "version": "3.8.1.303", 25 | "commands": [ 26 | "mgcb-editor-windows" 27 | ] 28 | }, 29 | "dotnet-mgcb-editor-mac": { 30 | "version": "3.8.1.303", 31 | "commands": [ 32 | "mgcb-editor-mac" 33 | ] 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /StompboxRemoteGL/Icon.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxRemoteGL/Icon.bmp -------------------------------------------------------------------------------- /StompboxRemoteGL/Icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mikeoliphant/StompboxUI/8dc3bdc26e1513bb0ef88e4f925863b903655937/StompboxRemoteGL/Icon.ico -------------------------------------------------------------------------------- /StompboxRemoteGL/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Stompbox; 3 | using StompboxAPI; 4 | using UILayout; 5 | 6 | RemoteClient client = new RemoteClient(); 7 | 8 | //client.MidiCallback = SendMidiCommand; 9 | 10 | StompboxLayout game = new StompboxLayout(); 11 | 12 | //game.SetScreenScale(scale, resizeScreen: true); 13 | 14 | //int width = 540; 15 | //int height = 960; 16 | int width = 1024; 17 | int height = 640; 18 | 19 | using (MonoGameHost GameHost = new MonoGameHost(width, height, fullscreen: false)) 20 | { 21 | GameHost.IsMouseVisible = true; 22 | GameHost.StartGame(game); 23 | 24 | client.Disconnect(); 25 | } 26 | -------------------------------------------------------------------------------- /StompboxRemoteGL/StompboxRemoteGL.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | WinExe 4 | net8.0 5 | Major 6 | false 7 | false 8 | 9 | 10 | app.manifest 11 | Icon.ico 12 | 13 | 14 | $(DefineConstants);STOMPBOXREMOTE 15 | 16 | 17 | $(DefineConstants);STOMPBOXREMOTE 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /StompboxRemoteGL/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | true/pm 39 | permonitorv2,permonitor 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /StompboxShared/Content/Content.mgcb: -------------------------------------------------------------------------------- 1 | 2 | #----------------------------- Global Properties ----------------------------# 3 | 4 | /outputDir:bin/$(Platform) 5 | /intermediateDir:obj/$(Platform) 6 | /platform:Windows 7 | /config: 8 | /profile:Reach 9 | /compress:False 10 | 11 | #-------------------------------- References --------------------------------# 12 | 13 | 14 | #---------------------------------- Content ---------------------------------# 15 | 16 | #begin Textures/ImageManifest.xml 17 | /copy:Textures/ImageManifest.xml 18 | 19 | #begin Textures/UISheet0.png 20 | /importer:TextureImporter 21 | /processor:TextureProcessor 22 | /processorParam:ColorKeyColor=255,0,255,255 23 | /processorParam:ColorKeyEnabled=True 24 | /processorParam:GenerateMipmaps=True 25 | /processorParam:PremultiplyAlpha=True 26 | /processorParam:ResizeToPowerOfTwo=False 27 | /processorParam:MakeSquare=False 28 | /processorParam:TextureFormat=Color 29 | /build:Textures/UISheet0.png 30 | 31 | -------------------------------------------------------------------------------- /StompboxShared/Content/Textures/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore -------------------------------------------------------------------------------- /StompboxShared/Interface/AudioFilePlayerInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UILayout; 3 | using Stompbox; 4 | 5 | namespace Stompbox 6 | { 7 | public class AudioFilePlayerInterface : PluginInterface 8 | { 9 | public AudioFilePlayerInterface(IAudioPlugin plugin) 10 | : base(plugin) 11 | { 12 | 13 | } 14 | 15 | protected override void AddControls(Dock dock) 16 | { 17 | VerticalStack vStack = new VerticalStack { HorizontalAlignment = EHorizontalAlignment.Stretch, VerticalAlignment = EVerticalAlignment.Stretch, ChildSpacing = 5 }; 18 | dock.Children.Add(vStack); 19 | 20 | vStack.Children.Add(new TextBlock(Plugin.Name) { HorizontalAlignment = EHorizontalAlignment.Center, TextColor = foregroundColor }); 21 | 22 | vStack.Children.Add(CreateFileControl(Plugin.Parameters[0])); 23 | 24 | UIElement levelControl = CreateControl(Plugin.Parameters[1]); 25 | levelControl.HorizontalAlignment = EHorizontalAlignment.Center; 26 | 27 | vStack.Children.Add(levelControl); 28 | 29 | HorizontalStack controlStack = new HorizontalStack { HorizontalAlignment = EHorizontalAlignment.Center, ChildSpacing = 5 }; 30 | vStack.Children.Add(controlStack); 31 | 32 | ImageToggleButton playButton = new ImageToggleButton("Stop", "Play") 33 | { 34 | HorizontalAlignment = EHorizontalAlignment.Center, 35 | }; 36 | 37 | playButton.PressAction = delegate 38 | { 39 | Plugin.Enabled = playButton.IsPressed; 40 | Plugin.GetParameter("Playing").Value = playButton.IsPressed ? 1 : 0; 41 | }; 42 | 43 | playButton.SetPressed(Plugin.GetParameter("Playing").Value == 1); 44 | 45 | playButton.ImageColor = foregroundColor; 46 | 47 | controlStack.Children.Add(playButton); 48 | 49 | controlStack.Children.Add(new ImageButton("Restart") 50 | { 51 | ImageColor = foregroundColor, 52 | PressAction = delegate 53 | { 54 | StompboxClient.Instance.SendCommand("SetParam " + Plugin.ID + " Position 0"); 55 | } 56 | }); 57 | 58 | controlStack.Children.Add(new ImageButton("Record") 59 | { 60 | ImageColor = UIColor.Red, 61 | PressAction = delegate 62 | { 63 | StompboxClient.Instance.SendCommand("SendCommand " + Plugin.ID + " ArmRecord"); 64 | } 65 | }); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /StompboxShared/Interface/AudioFileRecorderInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Timers; 3 | using UILayout; 4 | using Stompbox; 5 | 6 | namespace Stompbox 7 | { 8 | public class AudioFileRecorderInterface : PluginInterface 9 | { 10 | TextBlock recordTimeText; 11 | Timer recordTimer; 12 | DateTime startTime; 13 | 14 | public AudioFileRecorderInterface(IAudioPlugin plugin) 15 | : base(plugin) 16 | { 17 | recordTimer = new Timer(1000); 18 | recordTimer.Elapsed += RecordTimer_Elapsed; 19 | recordTimer.Start(); 20 | 21 | startTime = DateTime.Now; 22 | } 23 | 24 | protected override void AddControls(Dock dock) 25 | { 26 | VerticalStack vStack = new VerticalStack { HorizontalAlignment = EHorizontalAlignment.Stretch, VerticalAlignment = EVerticalAlignment.Stretch, ChildSpacing = 5 }; 27 | dock.Children.Add(vStack); 28 | 29 | vStack.Children.Add(new TextBlock(Plugin.Name) { HorizontalAlignment = EHorizontalAlignment.Center, TextColor = foregroundColor }); 30 | 31 | recordTimeText = new TextBlock("-----") { HorizontalAlignment = EHorizontalAlignment.Center, VerticalAlignment = EVerticalAlignment.Stretch, TextColor = foregroundColor }; 32 | vStack.Children.Add(recordTimeText); 33 | 34 | HorizontalStack controlStack = new HorizontalStack { HorizontalAlignment = EHorizontalAlignment.Center, VerticalAlignment = EVerticalAlignment.Stretch, ChildSpacing = 5 }; 35 | vStack.Children.Add(controlStack); 36 | 37 | controlStack.Children.Add(new ImageButton("Record") 38 | { 39 | VerticalAlignment = EVerticalAlignment.Center, 40 | ImageColor = UIColor.Red, 41 | PressAction = delegate 42 | { 43 | StompboxClient.Instance.SendCommand("SendCommand " + Plugin.ID + " Save"); 44 | 45 | startTime = DateTime.Now; 46 | } 47 | }); 48 | } 49 | 50 | private void RecordTimer_Elapsed(object sender, ElapsedEventArgs e) 51 | { 52 | int recordSecondsElapsed = (int)(e.SignalTime - startTime).TotalSeconds; 53 | 54 | recordTimeText.Text = (recordSecondsElapsed / 60).ToString("00") + ":" + (recordSecondsElapsed % 60).ToString("00"); 55 | 56 | UpdateContentLayout(); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /StompboxShared/Interface/DAWInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration.Internal; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using UILayout; 7 | 8 | namespace Stompbox 9 | { 10 | public class DAWInterface : InterfaceBase 11 | { 12 | PluginChainDisplay inputChainDisplay; 13 | PluginChainDisplay fxLoopDisplay; 14 | PluginChainDisplay outputChainDisplay; 15 | 16 | HorizontalStack topUIStack; 17 | VerticalStack programStack; 18 | HorizontalStack ampStack; 19 | EnumInterface currentProgramInterface; 20 | TextToggleButton midiToggleButton; 21 | StringBuilderTextBlock dspLoadText; 22 | UIElementWrapper tunerWrapper; 23 | UIElementWrapper audioFilePlayerWrapper; 24 | 25 | public DAWInterface() 26 | { 27 | Padding = new LayoutPadding(5); 28 | BackgroundColor = new UIColor(60, 60, 60); 29 | 30 | VerticalStack vStack = new VerticalStack 31 | { 32 | ChildSpacing = 10, 33 | HorizontalAlignment = EHorizontalAlignment.Stretch, 34 | VerticalAlignment = EVerticalAlignment.Stretch 35 | }; 36 | Children.Add(vStack); 37 | 38 | topUIStack = new HorizontalStack { HorizontalAlignment = EHorizontalAlignment.Stretch }; 39 | vStack.Children.Add(topUIStack); 40 | 41 | programStack = new VerticalStack { HorizontalAlignment = EHorizontalAlignment.Stretch, VerticalAlignment = EVerticalAlignment.Center }; 42 | 43 | programStack.Children.Add(new ImageElement("StompboxLogo") 44 | { 45 | HorizontalAlignment = EHorizontalAlignment.Center, 46 | VerticalAlignment = EVerticalAlignment.Center 47 | }); 48 | 49 | HorizontalStack programHStack = new HorizontalStack() { ChildSpacing = 10, HorizontalAlignment = EHorizontalAlignment.Stretch }; 50 | programStack.Children.Add(programHStack); 51 | 52 | currentProgramInterface = new EnumInterface(new List(), UIColor.Black) 53 | { 54 | NoSelectionText = "-- No Preset --", 55 | HorizontalAlignment = EHorizontalAlignment.Stretch, 56 | SelectionChangedAction = delegate (int index) 57 | { 58 | int currentPreset = currentProgramInterface.SelectedIndex; 59 | 60 | StompboxClient.Instance.CurrentPresetIndex = currentPreset; 61 | 62 | //if (currentPreset != -1) 63 | //{ 64 | // StompboxClient.Instance.SendCommand("LoadPreset " + currentProgramInterface.EnumValues[currentPreset]); 65 | 66 | // StompboxClient.Instance.UpdateProgram(); 67 | //} 68 | } 69 | }; 70 | programHStack.Children.Add(currentProgramInterface); 71 | 72 | HorizontalStack programHStack2 = new HorizontalStack() { ChildSpacing = 10, HorizontalAlignment = EHorizontalAlignment.Center }; ; 73 | programStack.Children.Add(programHStack2); 74 | 75 | programHStack2.Children.Add(new TextButton("Save") 76 | { 77 | ClickAction = delegate 78 | { 79 | if (currentProgramInterface.SelectedIndex == -1) 80 | DoSavePresetAs(); 81 | else 82 | StompboxClient.Instance.SaveCurrentPreset(); 83 | } 84 | }); 85 | programHStack2.Children.Add(new TextButton("Save As") 86 | { 87 | ClickAction = delegate 88 | { 89 | DoSavePresetAs(); 90 | } 91 | }); 92 | programHStack2.Children.Add(new TextButton("Delete") 93 | { 94 | ClickAction = delegate 95 | { 96 | if (currentProgramInterface.SelectedIndex != -1) 97 | { 98 | Layout.Current.ShowConfirmationPopup("Are you sure you want to\ndelete this preset?", 99 | delegate 100 | { 101 | StompboxClient.Instance.DeleteCurrentPreset(); 102 | }); 103 | } 104 | } 105 | }); 106 | 107 | HorizontalStack programHStack3 = new HorizontalStack() { ChildSpacing = 10, HorizontalAlignment = EHorizontalAlignment.Center }; ; 108 | programStack.Children.Add(programHStack3); 109 | 110 | programHStack3.Children.Add(new TextButton("Save Settings") 111 | { 112 | ClickAction = delegate 113 | { 114 | StompboxClient.Instance.SendCommand("SaveSettings"); 115 | } 116 | }); 117 | 118 | 119 | HorizontalStack buttonStack = new HorizontalStack { HorizontalAlignment = EHorizontalAlignment.Center }; 120 | programStack.Children.Add(buttonStack); 121 | 122 | midiToggleButton = new TextToggleButton("Midi On", "Midi Off") 123 | { 124 | PressAction = ToggleMidi 125 | }; 126 | 127 | midiToggleButton.SetPressed(true); 128 | 129 | buttonStack.Children.Add(midiToggleButton); 130 | 131 | 132 | ampStack = new HorizontalStack { HorizontalAlignment = EHorizontalAlignment.Center }; //, BackgroundColor = UIColor.FromHex("#b5893c") }; 133 | 134 | HorizontalStack row2Stack = new HorizontalStack { HorizontalAlignment = EHorizontalAlignment.Stretch }; 135 | vStack.Children.Add(row2Stack); 136 | 137 | row2Stack.Children.Add(tunerWrapper = new UIElementWrapper()); 138 | 139 | row2Stack.Children.Add(inputChainDisplay = new PluginChainDisplay("Input", miniMode: false) { HorizontalAlignment = EHorizontalAlignment.Center }); 140 | 141 | HorizontalStack row3Stack = new HorizontalStack { HorizontalAlignment = EHorizontalAlignment.Stretch }; 142 | vStack.Children.Add(row3Stack); 143 | 144 | row3Stack.Children.Add(audioFilePlayerWrapper = new UIElementWrapper()); 145 | 146 | HorizontalStack loopOutputStack = new HorizontalStack { HorizontalAlignment = EHorizontalAlignment.Center, ChildSpacing = 50 }; 147 | row3Stack.Children.Add(loopOutputStack); 148 | 149 | loopOutputStack.Children.Add(fxLoopDisplay = new PluginChainDisplay("FxLoop", miniMode: false) { HorizontalAlignment = EHorizontalAlignment.Left }); 150 | loopOutputStack.Children.Add(outputChainDisplay = new PluginChainDisplay("Output", miniMode: false) { HorizontalAlignment = EHorizontalAlignment.Left }); 151 | 152 | if (StompboxClient.Instance.InClientMode) 153 | { 154 | dspLoadText = new StringBuilderTextBlock("---") { HorizontalAlignment = EHorizontalAlignment.Left, VerticalAlignment = EVerticalAlignment.Bottom }; 155 | Children.Add(dspLoadText); 156 | 157 | Children.Add(new ExtraOptionsButton(EStompboxInterfaceType.DAW) 158 | { 159 | HorizontalAlignment = EHorizontalAlignment.Right, 160 | VerticalAlignment = EVerticalAlignment.Bottom, 161 | }); 162 | } 163 | } 164 | 165 | protected override void DrawContents() 166 | { 167 | base.DrawContents(); 168 | 169 | float desiredScale = (Layout.Current as MonoGameLayout).UnscaledBounds.Height / 1600.0f; 170 | 171 | if (StompboxLayout.Instance.Scale != desiredScale) 172 | StompboxLayout.Instance.Scale = desiredScale; 173 | 174 | if (StompboxClient.Instance.NeedUIReload) 175 | { 176 | UpdateUI(); 177 | 178 | StompboxClient.Instance.NeedUIReload = false; 179 | } 180 | 181 | if (StompboxClient.Instance.InClientMode) 182 | { 183 | UpdateDSPLoad(StompboxClient.Instance.MaxDSPLoad, StompboxClient.Instance.MinDSPLoad); 184 | } 185 | } 186 | 187 | void ToggleMidi() 188 | { 189 | StompboxClient.Instance.SendCommand("MidiLock" + (midiToggleButton.IsPressed ? "On" : "Off")); 190 | } 191 | 192 | public void UpdateUI() 193 | { 194 | ampStack.Children.Clear(); 195 | 196 | AddAmpPlugin("Amp"); 197 | AddAmpPlugin("Tonestack"); 198 | AddAmpPlugin("Cabinet"); 199 | 200 | topUIStack.Children.Clear(); 201 | 202 | topUIStack.Children.Add(programStack); 203 | 204 | topUIStack.Children.Add(new GainPluginInterface(StompboxClient.Instance.PluginFactory.CreatePlugin("Input"))); 205 | 206 | topUIStack.Children.Add(ampStack); 207 | 208 | topUIStack.Children.Add(new GainPluginInterface(StompboxClient.Instance.PluginFactory.CreatePlugin("Master"))); 209 | 210 | tunerWrapper.Child = new TunerInterface(StompboxClient.Instance.PluginFactory.CreatePlugin("Tuner")); 211 | audioFilePlayerWrapper.Child = new AudioFilePlayerInterface(StompboxClient.Instance.PluginFactory.CreatePlugin("AudioFilePlayer")); 212 | 213 | inputChainDisplay.SetChain(StompboxClient.Instance.GetChain("Input")); 214 | fxLoopDisplay.SetChain(StompboxClient.Instance.GetChain("FxLoop")); 215 | outputChainDisplay.SetChain(StompboxClient.Instance.GetChain("Output")); 216 | 217 | currentProgramInterface.SetEnumValues(StompboxClient.Instance.PresetNames); 218 | currentProgramInterface.SetSelectedIndex(StompboxClient.Instance.CurrentPresetIndex); 219 | 220 | UpdateContentLayout(); 221 | } 222 | 223 | void AddAmpPlugin(string slotName) 224 | { 225 | IAudioPlugin plugin = StompboxClient.Instance.PluginFactory.CreatePlugin(StompboxClient.Instance.GetSlotPlugin(slotName)); 226 | 227 | if (plugin == null) 228 | return; 229 | 230 | ampStack.Children.Add(new PluginInterface(plugin, slotName) { VerticalAlignment = EVerticalAlignment.Stretch }); 231 | } 232 | 233 | public void UpdateDSPLoad(float maxDSPLoad, float minDSPLoad) 234 | { 235 | maxDSPLoad *= 100; 236 | minDSPLoad *= 100; 237 | 238 | dspLoadText.StringBuilder.Clear(); 239 | 240 | int maxInt = (int)maxDSPLoad; 241 | 242 | dspLoadText.StringBuilder.AppendNumber(maxInt); 243 | dspLoadText.StringBuilder.Append('.'); 244 | dspLoadText.StringBuilder.AppendNumber((int)Math.Round((maxDSPLoad - (float)maxInt) * 10)); 245 | dspLoadText.StringBuilder.Append("%/"); 246 | 247 | int minInt = (int)minDSPLoad; 248 | 249 | dspLoadText.StringBuilder.AppendNumber(maxInt); 250 | dspLoadText.StringBuilder.Append('.'); 251 | dspLoadText.StringBuilder.AppendNumber((int)Math.Round((minDSPLoad - (float)minInt) * 10)); 252 | dspLoadText.StringBuilder.Append('%'); 253 | } 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /StompboxShared/Interface/InterfaceBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading.Tasks; 5 | using UILayout; 6 | 7 | namespace Stompbox 8 | { 9 | public enum EStompboxInterfaceType 10 | { 11 | DAW, 12 | Mobile, 13 | Pedalboard 14 | } 15 | 16 | public class InterfaceBase : Dock 17 | { 18 | public static EStompboxInterfaceType InterfaceType { get; set; } = EStompboxInterfaceType.DAW; 19 | 20 | protected void DoSavePresetAs() 21 | { 22 | Layout.Current.ShowTextInputPopup("File Name:", null, delegate (string presetName) 23 | { 24 | if (!string.IsNullOrEmpty(presetName)) 25 | { 26 | StompboxClient.Instance.SavePresetAs(presetName); 27 | } 28 | }); 29 | } 30 | 31 | public static InterfaceBase GetInterface() 32 | { 33 | switch (InterfaceType) 34 | { 35 | case EStompboxInterfaceType.DAW: 36 | return new DAWInterface(); 37 | case EStompboxInterfaceType.Mobile: 38 | #if ANDROID 39 | Activity1.Instance.RequestedOrientation = Android.Content.PM.ScreenOrientation.UserPortrait; 40 | #endif 41 | return new MobileInterface(); 42 | case EStompboxInterfaceType.Pedalboard: 43 | #if ANDROID 44 | Activity1.Instance.RequestedOrientation = Android.Content.PM.ScreenOrientation.UserLandscape; 45 | #endif 46 | return new PedalboardInterface(); 47 | } 48 | 49 | throw new InvalidOperationException(); 50 | } 51 | 52 | public static void SetInterfaceType(EStompboxInterfaceType interfaceType) 53 | { 54 | InterfaceType = interfaceType; 55 | 56 | StompboxLayout.Instance.RootUIElement = GetInterface(); 57 | } 58 | } 59 | 60 | public class ExtraOptionsButton : ImageButton 61 | { 62 | public ExtraOptionsButton(EStompboxInterfaceType currentInterfaceType) 63 | : base("MoreButton") 64 | { 65 | ClickAction = delegate 66 | { 67 | Layout.Current.ShowPopup(new ExtraOptionsMenu(currentInterfaceType), ContentBounds.Center); 68 | }; 69 | } 70 | } 71 | 72 | public class ExtraOptionsMenu : Menu 73 | { 74 | public ExtraOptionsMenu(EStompboxInterfaceType currentInterfaceType) 75 | { 76 | List menuItems = new(); 77 | 78 | foreach (EStompboxInterfaceType type in Enum.GetValues(typeof(EStompboxInterfaceType))) 79 | { 80 | if (type != currentInterfaceType) 81 | { 82 | menuItems.Add(new ContextMenuItem() 83 | { 84 | Text = "Switch to " + type.ToString() + " interface", 85 | AfterCloseAction = delegate 86 | { 87 | InterfaceBase.SetInterfaceType(type); 88 | StompboxClient.Instance.NeedUIReload = true; 89 | } 90 | }); 91 | } 92 | } 93 | 94 | menuItems.Add(new ContextMenuItem() 95 | { 96 | Text = "Shut down stompbox", 97 | AfterCloseAction = delegate 98 | { 99 | Layout.Current.ShowConfirmationPopup("Shut down!\n\nAre you sure?", 100 | delegate 101 | { 102 | StompboxClient.Instance.SendCommand("Shutdown"); 103 | }); 104 | } 105 | }); 106 | 107 | SetMenuItems(menuItems); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /StompboxShared/Interface/PedalboardInterface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using UILayout; 6 | 7 | namespace Stompbox 8 | { 9 | public class PedalboardInterface : InterfaceBase 10 | { 11 | TextBlock presetText; 12 | HorizontalStack stompStack; 13 | 14 | public PedalboardInterface() 15 | { 16 | BackgroundColor = UIColor.Black; 17 | 18 | presetText = new TextBlock("-- No Preset --") 19 | { 20 | TextColor = UIColor.White, 21 | HorizontalAlignment = EHorizontalAlignment.Center, 22 | VerticalAlignment = EVerticalAlignment.Top 23 | }; 24 | children.Add(presetText); 25 | 26 | stompStack = new HorizontalStack() 27 | { 28 | HorizontalAlignment = EHorizontalAlignment.Stretch, 29 | VerticalAlignment = EVerticalAlignment.Bottom 30 | }; 31 | children.Add(stompStack); 32 | } 33 | 34 | protected override void DrawContents() 35 | { 36 | base.DrawContents(); 37 | 38 | float desiredScale = (Layout.Current as MonoGameLayout).UnscaledBounds.Width / 640.0f; 39 | 40 | if (StompboxLayout.Instance.Scale != desiredScale) 41 | StompboxLayout.Instance.Scale = desiredScale; 42 | 43 | if (StompboxClient.Instance.NeedUIReload) 44 | { 45 | UpdateUI(); 46 | 47 | StompboxClient.Instance.NeedUIReload = false; 48 | } 49 | } 50 | 51 | public void UpdateUI() 52 | { 53 | if (StompboxClient.Instance.CurrentPresetIndex == -1) 54 | { 55 | presetText.Text = "-- No Preset --"; 56 | } 57 | else 58 | { 59 | presetText.Text = StompboxClient.Instance.PresetNames[StompboxClient.Instance.CurrentPresetIndex]; 60 | } 61 | 62 | stompStack.Children.Clear(); 63 | 64 | MidiCCMapEntry[] stomps = new MidiCCMapEntry[3]; 65 | 66 | foreach (var stompMap in StompboxClient.Instance.MidiStompCCMap) 67 | { 68 | foreach (MidiCCMapEntry ccMap in StompboxClient.Instance.MidiCCMap) 69 | { 70 | if (ccMap.CCNumber == stompMap.Key) 71 | { 72 | stomps[stompMap.Value - 1] = ccMap; 73 | } 74 | } 75 | } 76 | 77 | foreach (MidiCCMapEntry ccMap in stomps) 78 | { 79 | bool addedStomp = false; 80 | 81 | if (ccMap != null) 82 | { 83 | IAudioPlugin plugin = StompboxClient.Instance.AllActivePlugins.Where(p => (p.ID == ccMap.PluginName)).FirstOrDefault(); 84 | 85 | if (plugin != null) 86 | { 87 | PluginParameter parameter = plugin.Parameters.Where(p => (p.Name == ccMap.PluginParameter)).FirstOrDefault(); 88 | 89 | if (((parameter != null) && (parameter.ParameterType == EParameterType.Bool)) || (ccMap.PluginParameter == "Enabled")) 90 | { 91 | NinePatchButton button = new NinePatchButton(Layout.Current.GetImage("StompOutline")) 92 | { 93 | HorizontalAlignment = EHorizontalAlignment.Stretch 94 | }; 95 | 96 | button.IsToggleButton = true; 97 | 98 | Dock dock = new Dock() 99 | { 100 | HorizontalAlignment = EHorizontalAlignment.Stretch, 101 | VerticalAlignment = EVerticalAlignment.Stretch 102 | }; 103 | 104 | dock.Children.Add(GetStack(plugin, ccMap.PluginParameter)); 105 | dock.Children.Add(new UIElement() 106 | { 107 | HorizontalAlignment = EHorizontalAlignment.Stretch, 108 | VerticalAlignment = EVerticalAlignment.Stretch, 109 | BackgroundColor = new UIColor(0, 0, 0, 200) 110 | }); 111 | 112 | button.SetElements(GetStack(plugin, ccMap.PluginParameter), dock); 113 | 114 | if (parameter == null) // "Enabled" 115 | { 116 | button.SetPressed(plugin.Enabled); 117 | 118 | button.ClickAction = delegate 119 | { 120 | plugin.Enabled = !plugin.Enabled; 121 | }; 122 | 123 | plugin.SetEnabled = delegate (bool enabled) 124 | { 125 | button.SetPressed(enabled); 126 | }; 127 | } 128 | else 129 | { 130 | button.SetPressed(parameter.Value == 1.0f); 131 | 132 | button.ClickAction = delegate 133 | { 134 | parameter.Value = 1.0f - parameter.Value; 135 | }; 136 | 137 | parameter.SetValue = delegate (float value) 138 | { 139 | button.SetPressed(value == 1.0f); 140 | }; 141 | } 142 | 143 | stompStack.Children.Add(button); 144 | 145 | addedStomp = true; 146 | } 147 | } 148 | } 149 | 150 | if (!addedStomp) 151 | { 152 | stompStack.Children.Add(new UIElement { HorizontalAlignment = EHorizontalAlignment.Stretch }); 153 | } 154 | } 155 | 156 | UpdateContentLayout(); 157 | } 158 | 159 | UIElement GetStack(IAudioPlugin plugin, string parameterName) 160 | { 161 | VerticalStack stack = new VerticalStack() 162 | { 163 | HorizontalAlignment = EHorizontalAlignment.Stretch, 164 | VerticalAlignment = EVerticalAlignment.Stretch 165 | }; 166 | 167 | stack.BackgroundColor = UIColor.FromHex(plugin.BackgroundColor); 168 | 169 | UIColor foregroundColor = UIColor.FromHex(plugin.ForegroundColor); 170 | 171 | stack.Children.Add(new TextBlock(plugin.Name) 172 | { 173 | TextColor = foregroundColor, 174 | HorizontalAlignment = EHorizontalAlignment.Center 175 | }); 176 | 177 | stack.Children.Add(new TextBlock(parameterName) 178 | { 179 | TextColor = foregroundColor, 180 | HorizontalAlignment = EHorizontalAlignment.Center 181 | }); 182 | 183 | return stack; 184 | } 185 | 186 | public override bool HandleTouch(in Touch touch) 187 | { 188 | if (!base.HandleTouch(touch)) 189 | { 190 | if (IsDoubleTap(touch)) 191 | { 192 | Layout.Current.ShowPopup(new ExtraOptionsMenu(EStompboxInterfaceType.Pedalboard), ContentBounds.Center); 193 | } 194 | } 195 | 196 | return true; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /StompboxShared/Interface/StompboxLayout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UILayout; 3 | using StompboxAPI; 4 | 5 | namespace Stompbox 6 | { 7 | public class StompboxLayout : MonoGameLayout 8 | { 9 | public static StompboxLayout Instance { get; private set; } 10 | public static float BaseScale { get; set; } = 1.0f; 11 | 12 | bool clientConnected = true; 13 | 14 | public StompboxLayout() 15 | { 16 | Instance = this; 17 | 18 | Scale = BaseScale; 19 | } 20 | 21 | public override void SetHost(MonoGameHost host) 22 | { 23 | base.SetHost(host); 24 | 25 | Host.InactiveSleepTime = TimeSpan.Zero; 26 | 27 | host.Window.Title = "stompbox"; 28 | 29 | LoadImageManifest("ImageManifest.xml"); 30 | 31 | GraphicsContext.SingleWhitePixelImage = GetImage("SingleWhitePixel"); 32 | GraphicsContext.SamplerState = new Microsoft.Xna.Framework.Graphics.SamplerState() 33 | { 34 | AddressU = Microsoft.Xna.Framework.Graphics.TextureAddressMode.Clamp, 35 | AddressV = Microsoft.Xna.Framework.Graphics.TextureAddressMode.Clamp, 36 | Filter = Microsoft.Xna.Framework.Graphics.TextureFilter.Anisotropic, 37 | MipMapLevelOfDetailBias = -0.8f 38 | }; 39 | 40 | DefaultFont = GetFont("MainFont"); 41 | DefaultFont.SpriteFont.Spacing = 1; 42 | 43 | GetFont("SmallFont").SpriteFont.Spacing = 0; 44 | 45 | DefaultForegroundColor = UIColor.White; 46 | 47 | DefaultOutlineNinePatch = GetImage("PopupBackground"); 48 | 49 | DefaultPressedNinePatch = GetImage("ButtonPressed"); 50 | DefaultUnpressedNinePatch = GetImage("ButtonUnpressed"); 51 | 52 | DefaultDragImage = GetImage("ButtonPressed"); 53 | 54 | InputManager.AddMapping("StompMode", new KeyMapping(InputKey.D1)); 55 | InputManager.AddMapping("Stomp1", new KeyMapping(InputKey.D2)); 56 | InputManager.AddMapping("Stomp2", new KeyMapping(InputKey.D3)); 57 | InputManager.AddMapping("Stomp3", new KeyMapping(InputKey.D4)); 58 | 59 | InterfaceBase.SetInterfaceType(InterfaceBase.InterfaceType); 60 | 61 | if (!StompboxClient.Instance.InClientMode) 62 | { 63 | Initialize(); 64 | } 65 | } 66 | 67 | public void Connect() 68 | { 69 | clientConnected = false; 70 | 71 | string serverName = "raspberrypi"; 72 | 73 | StompboxClient.Instance.Connect(serverName, 24639, ConnectCallback); 74 | } 75 | 76 | void ConnectCallback(bool connected) 77 | { 78 | if (!connected) 79 | { 80 | Layout.Current.ShowTextInputPopup("Enter server:", "raspberrypi", delegate (string serverName) 81 | { 82 | try 83 | { 84 | StompboxClient.Instance.Connect(serverName, 24639, ConnectCallback); 85 | } 86 | catch { } 87 | }); 88 | } 89 | else 90 | { 91 | clientConnected = true; 92 | 93 | Initialize(); 94 | } 95 | } 96 | 97 | void Initialize() 98 | { 99 | StompboxClient.Instance.SendCommand("SetGlobalChain MasterChain MasterIn Chain Input Slot Amp Slot Tonestack Chain FxLoop Slot Cabinet Chain Output MasterChain MasterOut"); 100 | 101 | StompboxClient.Instance.UpdateProgram(); 102 | 103 | StompboxClient.Instance.SendCommand("SetChain MasterIn AudioRecorder Tuner Input"); 104 | StompboxClient.Instance.SendCommand("SetChain MasterOut AudioFilePlayer Master"); 105 | 106 | StompboxClient.Instance.SetDefaultSlotPlugin("Amp", "NAM"); 107 | StompboxClient.Instance.SetDefaultSlotPlugin("Tonestack", "EQ-7"); 108 | StompboxClient.Instance.SetDefaultSlotPlugin("Cabint", "Cabinet"); 109 | } 110 | 111 | public override void Update(float secondsElapsed) 112 | { 113 | base.Update(secondsElapsed); 114 | 115 | if (StompboxClient.Instance.InClientMode) 116 | { 117 | if (clientConnected && !StompboxClient.Instance.Connected) 118 | { 119 | Connect(); 120 | } 121 | } 122 | 123 | if (InputManager.WasClicked("StompMode", this)) 124 | { 125 | StompboxClient.Instance.SendCommand("HandleMidi " + (0xb0).ToString() + " 32 127"); 126 | } 127 | else if (InputManager.WasClicked("Stomp1", this)) 128 | { 129 | StompboxClient.Instance.SendCommand("HandleMidi " + (0xb0).ToString() + " 33 127"); 130 | } 131 | else if (InputManager.WasClicked("Stomp2", this)) 132 | { 133 | StompboxClient.Instance.SendCommand("HandleMidi " + (0xb0).ToString() + " 34 127"); 134 | } 135 | else if (InputManager.WasClicked("Stomp3", this)) 136 | { 137 | StompboxClient.Instance.SendCommand("HandleMidi " + (0xb0).ToString() + " 35 127"); 138 | } 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /StompboxShared/NoteUtil.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | 5 | namespace Stompbox 6 | { 7 | public enum ENoteName 8 | { 9 | C, 10 | [Description("Cs/Df")] 11 | CsDf, 12 | D, 13 | [Description("Ds/Ef")] 14 | DsEf, 15 | E, 16 | F, 17 | [Description("Fs/Gf")] 18 | FsGf, 19 | G, 20 | [Description("Gs/Af")] 21 | GsAf, 22 | A, 23 | [Description("As/Bf")] 24 | AsBf, 25 | B 26 | } 27 | 28 | public class NoteUtil 29 | { 30 | private static Dictionary scales = new Dictionary(); 31 | private static double HalfStepRatio = Math.Pow(2.0, (1.0 / 12.0)); 32 | private static double A4Frequency = 440.0; 33 | private static int A4MidiNoteNum = 57; 34 | 35 | public static Dictionary Scales 36 | { 37 | get { return NoteUtil.scales; } 38 | set { NoteUtil.scales = value; } 39 | } 40 | 41 | static NoteUtil() 42 | { 43 | scales[EChordType.Maj] = new int[] { 0, 2, 4, 5, 7, 9, 11 }; 44 | scales[EChordType.Min] = new int[] { 0, 2, 3, 5, 7, 8, 11 }; 45 | } 46 | 47 | public static bool TryParseNoteFrequency(string noteStr, out double frequency) 48 | { 49 | ENoteName note; 50 | int octave; 51 | 52 | frequency = 0; 53 | 54 | if (TryParseNoteName(noteStr, out note, out octave)) 55 | { 56 | frequency = GetMidiNoteFrequency(GetMidiNoteNumber(note, octave)); 57 | 58 | return true; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | public static bool TryParseNoteName(string noteStr, out ENoteName note, out int octave) 65 | { 66 | note = ENoteName.C; 67 | octave = 0; 68 | 69 | string lowerStr = noteStr.ToLower().Trim(); 70 | 71 | if (lowerStr.StartsWith("cs") || lowerStr.StartsWith("df")) 72 | { 73 | note = ENoteName.CsDf; 74 | 75 | return int.TryParse(lowerStr.Substring(2, 1), out octave); 76 | } 77 | 78 | if (lowerStr.StartsWith("ds") || lowerStr.StartsWith("ef")) 79 | { 80 | note = ENoteName.DsEf; 81 | 82 | return int.TryParse(lowerStr.Substring(2, 1), out octave); 83 | } 84 | 85 | if (lowerStr.StartsWith("fs") || lowerStr.StartsWith("gf")) 86 | { 87 | note = ENoteName.FsGf; 88 | 89 | return int.TryParse(lowerStr.Substring(2, 1), out octave); 90 | } 91 | 92 | if (lowerStr.StartsWith("gs") || lowerStr.StartsWith("af")) 93 | { 94 | note = ENoteName.GsAf; 95 | 96 | return int.TryParse(lowerStr.Substring(2, 1), out octave); 97 | } 98 | 99 | if (lowerStr.StartsWith("as") || lowerStr.StartsWith("bf")) 100 | { 101 | note = ENoteName.AsBf; 102 | 103 | return int.TryParse(lowerStr.Substring(2, 1), out octave); 104 | } 105 | 106 | if (lowerStr.StartsWith("c")) 107 | { 108 | note = ENoteName.C; 109 | 110 | return int.TryParse(lowerStr.Substring(1, 1), out octave); 111 | } 112 | 113 | if (lowerStr.StartsWith("d")) 114 | { 115 | note = ENoteName.D; 116 | 117 | return int.TryParse(lowerStr.Substring(1, 1), out octave); 118 | } 119 | 120 | if (lowerStr.StartsWith("e")) 121 | { 122 | note = ENoteName.E; 123 | 124 | return int.TryParse(lowerStr.Substring(1, 1), out octave); 125 | } 126 | 127 | if (lowerStr.StartsWith("f")) 128 | { 129 | note = ENoteName.F; 130 | 131 | return int.TryParse(lowerStr.Substring(1, 1), out octave); 132 | } 133 | 134 | 135 | if (lowerStr.StartsWith("g")) 136 | { 137 | note = ENoteName.G; 138 | 139 | return int.TryParse(lowerStr.Substring(1, 1), out octave); 140 | } 141 | 142 | if (lowerStr.StartsWith("a")) 143 | { 144 | note = ENoteName.A; 145 | 146 | return int.TryParse(lowerStr.Substring(1, 1), out octave); 147 | } 148 | 149 | 150 | if (lowerStr.StartsWith("b")) 151 | { 152 | note = ENoteName.B; 153 | 154 | return int.TryParse(lowerStr.Substring(1, 1), out octave); 155 | } 156 | 157 | return false; 158 | } 159 | 160 | public static double GetMidiNoteFrequency(int midiNoteNum) 161 | { 162 | int noteDiff = A4MidiNoteNum - midiNoteNum; 163 | 164 | return A4Frequency / Math.Pow(HalfStepRatio, noteDiff); 165 | } 166 | 167 | public static ENoteName GetNoteName(int midiNoteNum) 168 | { 169 | return (ENoteName)(midiNoteNum % 12); 170 | } 171 | 172 | public static int GetNoteOctave(int midiNoteNum) 173 | { 174 | return (midiNoteNum / 12) - 1; 175 | } 176 | 177 | public static int GetMidiNoteNumber(ENoteName note, int octave) 178 | { 179 | return ((octave + 1) * 12) + (int)note; 180 | } 181 | } 182 | 183 | public enum EChordType 184 | { 185 | Maj, 186 | Min 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /StompboxShared/StompboxShared.projitems: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | 6efba8a9-a407-4029-bdd0-651d22a87386 7 | 8 | 9 | Stompbox 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /StompboxShared/StompboxShared.shproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | a0cdbd0e-3a41-42b7-9425-a838bc2c7383 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /StompboxShared/SysExMapper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Stompbox 4 | { 5 | public class SysExCC 6 | { 7 | public int SysExID { get; set; } 8 | public int Controller { get; set; } 9 | public int MaxValue { get; set; } 10 | 11 | public SysExCC(int sysExID, int controller, int maxValue) 12 | { 13 | this.SysExID = sysExID; 14 | this.Controller = controller; 15 | this.MaxValue = maxValue; 16 | } 17 | } 18 | 19 | public class SysExMapper 20 | { 21 | public byte[] SysExHeader { get; set; } 22 | 23 | Dictionary ccMap = new Dictionary(); 24 | 25 | public SysExMapper() 26 | { 27 | SysExHeader = new byte[] { 0xF0, 0x41, 0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x60, 0x00, 0x06 }; 28 | } 29 | 30 | public void AddCC(SysExCC cc) 31 | { 32 | ccMap[cc.SysExID] = cc; 33 | } 34 | 35 | public bool HandleMidiMessage(byte[] message, out int controller, out double value) 36 | { 37 | controller = 0; 38 | value = 0; 39 | 40 | if (message.Length >= (SysExHeader.Length + 2)) 41 | { 42 | for (int i = 0; i < SysExHeader.Length; i++) 43 | { 44 | if (message[i] != SysExHeader[i]) 45 | return false; 46 | } 47 | 48 | int sysExID = message[SysExHeader.Length]; 49 | int sysExValue = message[SysExHeader.Length + 1]; 50 | 51 | if (!ccMap.ContainsKey(sysExID)) 52 | return false; 53 | 54 | SysExCC cc = ccMap[sysExID]; 55 | 56 | controller = cc.Controller; 57 | value = (double)sysExValue / (double)cc.MaxValue; 58 | 59 | return true; 60 | } 61 | 62 | return false; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /WioSerialDisplay/Free_Fonts.h: -------------------------------------------------------------------------------- 1 | // Attach this header file to your sketch to use the GFX Free Fonts. You can write 2 | // sketches without it, but it makes referencing them easier. 3 | 4 | // This calls up ALL the fonts but they only get loaded if you actually 5 | // use them in your sketch. 6 | // 7 | // No changes are needed to this header file unless new fonts are added to the 8 | // library "Fonts/GFXFF" folder. 9 | // 10 | // To save a lot of typing long names, each font can easily be referenced in the 11 | // sketch in three ways, either with: 12 | // 13 | // 1. Font file name with the & in front such as &FreeSansBoldOblique24pt7b 14 | // an example being: 15 | // 16 | // tft.setFreeFont(&FreeSansBoldOblique24pt7b); 17 | // 18 | // 2. FF# where # is a number determined by looking at the list below 19 | // an example being: 20 | // 21 | // tft.setFreeFont(FF32); 22 | // 23 | // 3. An abbreviation of the file name. Look at the list below to see 24 | // the abbreviations used, for example: 25 | // 26 | // tft.setFreeFont(FSSBO24) 27 | // 28 | // Where the letters mean: 29 | // F = Free font 30 | // M = Mono 31 | // SS = Sans Serif (double S to distinguish is form serif fonts) 32 | // S = Serif 33 | // B = Bold 34 | // O = Oblique (letter O not zero) 35 | // I = Italic 36 | // # = point size, either 9, 12, 18 or 24 37 | // 38 | // Setting the font to NULL will select the GLCD font: 39 | // 40 | // tft.setFreeFont(NULL); // Set font to GLCD 41 | 42 | #define LOAD_GFXFF 43 | 44 | #ifdef LOAD_GFXFF // Only include the fonts if LOAD_GFXFF is defined in User_Setup.h 45 | 46 | // Use these when printing or drawing text in GLCD and high rendering speed fonts 47 | #define GFXFF 1 48 | #define GLCD 0 49 | #define FONT2 2 50 | #define FONT4 4 51 | #define FONT6 6 52 | #define FONT7 7 53 | #define FONT8 8 54 | 55 | // Use the following when calling setFont() 56 | // 57 | // Reserved for GLCD font // FF0 58 | // 59 | 60 | #define TT1 &TomThumb 61 | 62 | #define FM9 &FreeMono9pt7b 63 | #define FM12 &FreeMono12pt7b 64 | #define FM18 &FreeMono18pt7b 65 | #define FM24 &FreeMono24pt7b 66 | 67 | #define FMB9 &FreeMonoBold9pt7b 68 | #define FMB12 &FreeMonoBold12pt7b 69 | #define FMB18 &FreeMonoBold18pt7b 70 | #define FMB24 &FreeMonoBold24pt7b 71 | 72 | #define FMO9 &FreeMonoOblique9pt7b 73 | #define FMO12 &FreeMonoOblique12pt7b 74 | #define FMO18 &FreeMonoOblique18pt7b 75 | #define FMO24 &FreeMonoOblique24pt7b 76 | 77 | #define FMBO9 &FreeMonoBoldOblique9pt7b 78 | #define FMBO12 &FreeMonoBoldOblique12pt7b 79 | #define FMBO18 &FreeMonoBoldOblique18pt7b 80 | #define FMBO24 &FreeMonoBoldOblique24pt7b 81 | 82 | #define FSS9 &FreeSans9pt7b 83 | #define FSS12 &FreeSans12pt7b 84 | #define FSS18 &FreeSans18pt7b 85 | #define FSS24 &FreeSans24pt7b 86 | 87 | #define FSSB9 &FreeSansBold9pt7b 88 | #define FSSB12 &FreeSansBold12pt7b 89 | #define FSSB18 &FreeSansBold18pt7b 90 | #define FSSB24 &FreeSansBold24pt7b 91 | 92 | #define FSSO9 &FreeSansOblique9pt7b 93 | #define FSSO12 &FreeSansOblique12pt7b 94 | #define FSSO18 &FreeSansOblique18pt7b 95 | #define FSSO24 &FreeSansOblique24pt7b 96 | 97 | #define FSSBO9 &FreeSansBoldOblique9pt7b 98 | #define FSSBO12 &FreeSansBoldOblique12pt7b 99 | #define FSSBO18 &FreeSansBoldOblique18pt7b 100 | #define FSSBO24 &FreeSansBoldOblique24pt7b 101 | 102 | #define FS9 &FreeSerif9pt7b 103 | #define FS12 &FreeSerif12pt7b 104 | #define FS18 &FreeSerif18pt7b 105 | #define FS24 &FreeSerif24pt7b 106 | 107 | #define FSI9 &FreeSerifItalic9pt7b 108 | #define FSI12 &FreeSerifItalic12pt7b 109 | #define FSI19 &FreeSerifItalic18pt7b 110 | #define FSI24 &FreeSerifItalic24pt7b 111 | 112 | #define FSB9 &FreeSerifBold9pt7b 113 | #define FSB12 &FreeSerifBold12pt7b 114 | #define FSB18 &FreeSerifBold18pt7b 115 | #define FSB24 &FreeSerifBold24pt7b 116 | 117 | #define FSBI9 &FreeSerifBoldItalic9pt7b 118 | #define FSBI12 &FreeSerifBoldItalic12pt7b 119 | #define FSBI18 &FreeSerifBoldItalic18pt7b 120 | #define FSBI24 &FreeSerifBoldItalic24pt7b 121 | 122 | #define FF0 NULL //ff0 reserved for GLCD 123 | #define FF1 &FreeMono9pt7b 124 | #define FF2 &FreeMono12pt7b 125 | #define FF3 &FreeMono18pt7b 126 | #define FF4 &FreeMono24pt7b 127 | 128 | #define FF5 &FreeMonoBold9pt7b 129 | #define FF6 &FreeMonoBold12pt7b 130 | #define FF7 &FreeMonoBold18pt7b 131 | #define FF8 &FreeMonoBold24pt7b 132 | 133 | #define FF9 &FreeMonoOblique9pt7b 134 | #define FF10 &FreeMonoOblique12pt7b 135 | #define FF11 &FreeMonoOblique18pt7b 136 | #define FF12 &FreeMonoOblique24pt7b 137 | 138 | #define FF13 &FreeMonoBoldOblique9pt7b 139 | #define FF14 &FreeMonoBoldOblique12pt7b 140 | #define FF15 &FreeMonoBoldOblique18pt7b 141 | #define FF16 &FreeMonoBoldOblique24pt7b 142 | 143 | #define FF17 &FreeSans9pt7b 144 | #define FF18 &FreeSans12pt7b 145 | #define FF19 &FreeSans18pt7b 146 | #define FF20 &FreeSans24pt7b 147 | 148 | #define FF21 &FreeSansBold9pt7b 149 | #define FF22 &FreeSansBold12pt7b 150 | #define FF23 &FreeSansBold18pt7b 151 | #define FF24 &FreeSansBold24pt7b 152 | 153 | #define FF25 &FreeSansOblique9pt7b 154 | #define FF26 &FreeSansOblique12pt7b 155 | #define FF27 &FreeSansOblique18pt7b 156 | #define FF28 &FreeSansOblique24pt7b 157 | 158 | #define FF29 &FreeSansBoldOblique9pt7b 159 | #define FF30 &FreeSansBoldOblique12pt7b 160 | #define FF31 &FreeSansBoldOblique18pt7b 161 | #define FF32 &FreeSansBoldOblique24pt7b 162 | 163 | #define FF33 &FreeSerif9pt7b 164 | #define FF34 &FreeSerif12pt7b 165 | #define FF35 &FreeSerif18pt7b 166 | #define FF36 &FreeSerif24pt7b 167 | 168 | #define FF37 &FreeSerifItalic9pt7b 169 | #define FF38 &FreeSerifItalic12pt7b 170 | #define FF39 &FreeSerifItalic18pt7b 171 | #define FF40 &FreeSerifItalic24pt7b 172 | 173 | #define FF41 &FreeSerifBold9pt7b 174 | #define FF42 &FreeSerifBold12pt7b 175 | #define FF43 &FreeSerifBold18pt7b 176 | #define FF44 &FreeSerifBold24pt7b 177 | 178 | #define FF45 &FreeSerifBoldItalic9pt7b 179 | #define FF46 &FreeSerifBoldItalic12pt7b 180 | #define FF47 &FreeSerifBoldItalic18pt7b 181 | #define FF48 &FreeSerifBoldItalic24pt7b 182 | 183 | // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 184 | // Now we define "s"tring versions for easy printing of the font name so: 185 | // tft.println(sFF5); 186 | // will print 187 | // Mono bold 9 188 | // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 189 | 190 | #define sFF0 "GLCD" 191 | #define sTT1 "Tom Thumb" 192 | #define sFF1 "Mono 9" 193 | #define sFF2 "Mono 12" 194 | #define sFF3 "Mono 18" 195 | #define sFF4 "Mono 24" 196 | 197 | #define sFF5 "Mono bold 9" 198 | #define sFF6 "Mono bold 12" 199 | #define sFF7 "Mono bold 18" 200 | #define sFF8 "Mono bold 24" 201 | 202 | #define sFF9 "Mono oblique 9" 203 | #define sFF10 "Mono oblique 12" 204 | #define sFF11 "Mono oblique 18" 205 | #define sFF12 "Mono oblique 24" 206 | 207 | #define sFF13 "Mono bold oblique 9" 208 | #define sFF14 "Mono bold oblique 12" 209 | #define sFF15 "Mono bold oblique 18" 210 | #define sFF16 "Mono bold oblique 24" // Full text line is too big for 480 pixel wide screen 211 | 212 | #define sFF17 "Sans 9" 213 | #define sFF18 "Sans 12" 214 | #define sFF19 "Sans 18" 215 | #define sFF20 "Sans 24" 216 | 217 | #define sFF21 "Sans bold 9" 218 | #define sFF22 "Sans bold 12" 219 | #define sFF23 "Sans bold 18" 220 | #define sFF24 "Sans bold 24" 221 | 222 | #define sFF25 "Sans oblique 9" 223 | #define sFF26 "Sans oblique 12" 224 | #define sFF27 "Sans oblique 18" 225 | #define sFF28 "Sans oblique 24" 226 | 227 | #define sFF29 "Sans bold oblique 9" 228 | #define sFF30 "Sans bold oblique 12" 229 | #define sFF31 "Sans bold oblique 18" 230 | #define sFF32 "Sans bold oblique 24" 231 | 232 | #define sFF33 "Serif 9" 233 | #define sFF34 "Serif 12" 234 | #define sFF35 "Serif 18" 235 | #define sFF36 "Serif 24" 236 | 237 | #define sFF37 "Serif italic 9" 238 | #define sFF38 "Serif italic 12" 239 | #define sFF39 "Serif italic 18" 240 | #define sFF40 "Serif italic 24" 241 | 242 | #define sFF41 "Serif bold 9" 243 | #define sFF42 "Serif bold 12" 244 | #define sFF43 "Serif bold 18" 245 | #define sFF44 "Serif bold 24" 246 | 247 | #define sFF45 "Serif bold italic 9" 248 | #define sFF46 "Serif bold italic 12" 249 | #define sFF47 "Serif bold italic 18" 250 | #define sFF48 "Serif bold italic 24" 251 | 252 | #else // LOAD_GFXFF not defined so setup defaults to prevent error messages 253 | 254 | // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 255 | // Free fonts are not loaded in User_Setup.h so we must define all as font 1 256 | // to prevent compile error messages 257 | // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 258 | 259 | #define GFXFF 1 260 | #define GLCD 1 261 | #define FONT2 2 262 | #define FONT4 4 263 | #define FONT6 6 264 | #define FONT7 7 265 | #define FONT8 8 266 | 267 | #define FF0 1 268 | #define FF1 1 269 | #define FF2 1 270 | #define FF3 1 271 | #define FF4 1 272 | #define FF5 1 273 | #define FF6 1 274 | #define FF7 1 275 | #define FF8 1 276 | #define FF9 1 277 | #define FF10 1 278 | #define FF11 1 279 | #define FF12 1 280 | #define FF13 1 281 | #define FF14 1 282 | #define FF15 1 283 | #define FF16 1 284 | #define FF17 1 285 | #define FF18 1 286 | #define FF19 1 287 | #define FF20 1 288 | #define FF21 1 289 | #define FF22 1 290 | #define FF23 1 291 | #define FF24 1 292 | #define FF25 1 293 | #define FF26 1 294 | #define FF27 1 295 | #define FF28 1 296 | #define FF29 1 297 | #define FF30 1 298 | #define FF31 1 299 | #define FF32 1 300 | #define FF33 1 301 | #define FF34 1 302 | #define FF35 1 303 | #define FF36 1 304 | #define FF37 1 305 | #define FF38 1 306 | #define FF39 1 307 | #define FF40 1 308 | #define FF41 1 309 | #define FF42 1 310 | #define FF43 1 311 | #define FF44 1 312 | #define FF45 1 313 | #define FF46 1 314 | #define FF47 1 315 | #define FF48 1 316 | 317 | #define FM9 1 318 | #define FM12 1 319 | #define FM18 1 320 | #define FM24 1 321 | 322 | #define FMB9 1 323 | #define FMB12 1 324 | #define FMB18 1 325 | #define FMB24 1 326 | 327 | #define FMO9 1 328 | #define FMO12 1 329 | #define FMO18 1 330 | #define FMO24 1 331 | 332 | #define FMBO9 1 333 | #define FMBO12 1 334 | #define FMBO18 1 335 | #define FMBO24 1 336 | 337 | #define FSS9 1 338 | #define FSS12 1 339 | #define FSS18 1 340 | #define FSS24 1 341 | 342 | #define FSSB9 1 343 | #define FSSB12 1 344 | #define FSSB18 1 345 | #define FSSB24 1 346 | 347 | #define FSSO9 1 348 | #define FSSO12 1 349 | #define FSSO18 1 350 | #define FSSO24 1 351 | 352 | #define FSSBO9 1 353 | #define FSSBO12 1 354 | #define FSSBO18 1 355 | #define FSSBO24 1 356 | 357 | #define FS9 1 358 | #define FS12 1 359 | #define FS18 1 360 | #define FS24 1 361 | 362 | #define FSI9 1 363 | #define FSI12 1 364 | #define FSI19 1 365 | #define FSI24 1 366 | 367 | #define FSB9 1 368 | #define FSB12 1 369 | #define FSB18 1 370 | #define FSB24 1 371 | 372 | #define FSBI9 1 373 | #define FSBI12 1 374 | #define FSBI18 1 375 | #define FSBI24 1 376 | 377 | #endif // LOAD_GFXFF 378 | -------------------------------------------------------------------------------- /WioSerialDisplay/WioSerialDisplay.ino: -------------------------------------------------------------------------------- 1 | #include "Free_Fonts.h" // Include the header file attached to this sketch 2 | 3 | #include "SPI.h" 4 | #include "TFT_eSPI.h" 5 | 6 | // Use hardware SPI 7 | TFT_eSPI tft = TFT_eSPI(); 8 | TFT_eSprite screen = TFT_eSprite(&tft); 9 | 10 | #define BACKGROUND_COLOR TFT_BLACK 11 | 12 | void setup(void) 13 | { 14 | tft.begin(); 15 | tft.setRotation(3); 16 | 17 | screen.createSprite(tft.width(), tft.height()); 18 | 19 | screen.fillScreen(BACKGROUND_COLOR); 20 | 21 | screen.pushSprite(0, 0); 22 | } 23 | 24 | void loop() 25 | { 26 | handleSerialCommand(); 27 | } 28 | 29 | void handleSerialCommand() 30 | { 31 | char *line = readLine(); 32 | 33 | if (line != 0) 34 | { 35 | Serial.println(line); 36 | 37 | char *word = strtok(line, " "); 38 | 39 | if (strcmp(word, "Reset") == 0) 40 | { 41 | screen.fillRect(0, 0, tft.width(), tft.height(), BACKGROUND_COLOR); 42 | } 43 | else if (strcmp(word, "Set") == 0) 44 | { 45 | word = strtok(NULL, " "); 46 | 47 | if (strcmp(word, "Preset") == 0) 48 | { 49 | char *presetName = strtok(NULL, " "); 50 | 51 | char *textColor = strtok(NULL, ""); 52 | 53 | setPresetName(presetName, (textColor != NULL) ? (uint)strtol(textColor, 0, 16) : TFT_WHITE); 54 | } 55 | else if (strcmp(word, "Stomp") == 0) 56 | { 57 | word = strtok(NULL, " "); 58 | 59 | if (word) 60 | { 61 | int stompNumber = word[0] - '1'; 62 | 63 | if ((stompNumber >= 0) && (stompNumber < 3)) 64 | { 65 | char *text1 = strtok(NULL, " "); 66 | 67 | if (!text1) 68 | { 69 | setStomp(stompNumber, NULL, NULL, 0, 0); 70 | } 71 | else 72 | { 73 | char *text2 = strtok(NULL, " "); 74 | 75 | char *bgColor = strtok(NULL, " "); 76 | char *fgColor = strtok(NULL, ""); 77 | 78 | if (bgColor && fgColor) 79 | { 80 | setStomp(stompNumber, text1, text2, (uint)strtol(bgColor, 0, 16), (uint)strtol(fgColor, 0, 16)); 81 | } 82 | } 83 | } 84 | } 85 | } 86 | else if (strcmp(word, "Tuner") == 0) 87 | { 88 | char *noteName = strtok(NULL, " "); 89 | 90 | if (noteName != NULL) 91 | { 92 | setTuner(noteName, atoi(strtok(NULL, ""))); 93 | } 94 | else 95 | { 96 | setTuner("\0", -1000); 97 | } 98 | } 99 | } 100 | 101 | screen.pushSprite(0, 0); 102 | } 103 | } 104 | 105 | const int numChars = 256; 106 | char receivedChars[numChars]; 107 | 108 | char *readLine() 109 | { 110 | static byte ndx = 0; 111 | char rc; 112 | 113 | while (Serial.available() > 0) 114 | { 115 | rc = Serial.read(); 116 | 117 | if (rc != '\n') 118 | { 119 | receivedChars[ndx] = rc; 120 | ndx++; 121 | 122 | if (ndx >= numChars) 123 | { 124 | ndx = numChars - 1; 125 | } 126 | } 127 | else 128 | { 129 | receivedChars[ndx] = '\0'; // terminate the string 130 | ndx = 0; 131 | 132 | return receivedChars; 133 | } 134 | } 135 | 136 | return 0; 137 | } 138 | 139 | int patchBoxStartY = 0; 140 | int patchBoxHeight = 50; 141 | 142 | void setPresetName(char *presetName, uint textColor) 143 | { 144 | screen.fillRect(0, patchBoxStartY, 320, patchBoxHeight, BACKGROUND_COLOR); 145 | 146 | screen.setTextColor(textColor); 147 | screen.setTextDatum(MC_DATUM); 148 | 149 | screen.setFreeFont(FSSB18); 150 | screen.drawString(presetName, 160, patchBoxStartY + (patchBoxHeight / 2), GFXFF); 151 | } 152 | 153 | int stompBoxHeight = 70; 154 | int stompBoxStartY = 240 - stompBoxHeight; 155 | 156 | void setStomp(int stompNumber, char *text1, char *text2, uint bgColor, uint fgColor) 157 | { 158 | int stompWidth = screen.width() / 3; 159 | 160 | if (!text1) 161 | { 162 | screen.fillRect((320 / 3) * stompNumber, stompBoxStartY, 320 / 3, stompBoxHeight, BACKGROUND_COLOR); 163 | 164 | return; 165 | } 166 | 167 | screen.fillRect(stompWidth * stompNumber, stompBoxStartY, stompWidth, stompBoxHeight, bgColor); 168 | screen.drawRect(stompWidth * stompNumber, stompBoxStartY, stompWidth, stompBoxHeight, fgColor); 169 | 170 | screen.setTextColor(fgColor); 171 | screen.setTextDatum(MC_DATUM); 172 | 173 | int xCenter = stompWidth * (stompNumber + 0.5); 174 | 175 | screen.setFreeFont(FSSB12); 176 | 177 | int textWidth = max(screen.textWidth(text1), screen.textWidth(text2)); 178 | 179 | if(textWidth > (stompWidth - 4)) 180 | { 181 | screen.setFreeFont(FSSB9); 182 | } 183 | 184 | screen.drawString(text1, xCenter, stompBoxStartY + (stompBoxHeight / 4), GFXFF); 185 | screen.drawString(text2, xCenter, stompBoxStartY + (stompBoxHeight * .75), GFXFF); 186 | } 187 | 188 | char lastNote = 0; 189 | int lastDelta = -1000; 190 | 191 | void setTuner(char *noteName, int delta) 192 | { 193 | if ((lastNote == *noteName) && (lastDelta == delta)) 194 | return; 195 | 196 | lastNote = *noteName; 197 | lastDelta = delta; 198 | 199 | int xCenter = screen.width() / 2; 200 | int yCenter = screen.height() / 2; 201 | 202 | int tunerHeight = 30; 203 | int centerWidth = 35; 204 | int deltaWidth = 10; 205 | 206 | screen.fillRect(0, yCenter - tunerHeight, tft.width(), tunerHeight * 2, BACKGROUND_COLOR); 207 | 208 | if (*noteName == 0) 209 | return; 210 | 211 | if ((delta >= -1) && (delta <= 1)) 212 | { 213 | screen.fillRect(xCenter - centerWidth, yCenter - tunerHeight, centerWidth * 2, tunerHeight * 2, TFT_BLUE); 214 | } 215 | 216 | if (delta < 0) 217 | { 218 | for (int i = 0; i > delta; i--) 219 | { 220 | screen.fillRect(xCenter - centerWidth + (deltaWidth * (i - 1) * 2), 221 | yCenter - tunerHeight, (deltaWidth - 2) * 2, tunerHeight * 2, TFT_RED); 222 | } 223 | } 224 | else if (delta > 0) 225 | { 226 | for (int i = 0; i < delta; i++) 227 | { 228 | screen.fillRect(xCenter + centerWidth + (deltaWidth * i * 2), 229 | yCenter - tunerHeight, (deltaWidth - 2) * 2, tunerHeight * 2, TFT_YELLOW); 230 | } 231 | } 232 | 233 | screen.setTextColor(TFT_WHITE); 234 | screen.setTextDatum(MC_DATUM); 235 | 236 | screen.setFreeFont(FSSB24); 237 | 238 | screen.drawString(noteName, xCenter, yCenter, GFXFF); 239 | } 240 | --------------------------------------------------------------------------------