├── .gitattributes
├── .github
└── workflows
│ └── build.yml
├── .gitignore
├── .gitmodules
├── AudioPlugSharp.sln
├── AudioPlugSharp
├── AudioIOPort.cs
├── AudioPlugSharp.csproj
├── AudioPluginBase.cs
├── AudioPluginParameter.cs
├── AudioPluginSaveState.cs
├── IAudioHost.cs
├── IAudioPlugin.cs
├── IAudioPluginEditor.cs
├── IAudioPluginProcessor.cs
├── Logger.cs
├── PluginLoader.cs
└── build
│ └── AudioPlugSharp.targets
├── AudioPlugSharpHost
├── Asio
│ ├── ASIOStructures.cs
│ ├── AsioDriver.cs
│ ├── AsioDriverCapability.cs
│ ├── AsioInterop.cs
│ ├── AsioSampleType.cs
│ └── README.md
├── AudioPlugSharpHost.cs
├── AudioPlugSharpHost.csproj
├── AudioSettingsForm.Designer.cs
├── AudioSettingsForm.cs
├── AudioSettingsForm.resx
├── README.md
└── WindowsFormsHost.cs
├── AudioPlugSharpJack
├── AudioPlugSharpJack.csproj
└── JackHost.cs
├── AudioPlugSharpVst
├── AudioPlugSharpController.cpp
├── AudioPlugSharpController.h
├── AudioPlugSharpEditor.cpp
├── AudioPlugSharpEditor.h
├── AudioPlugSharpEntryPoints.cpp
├── AudioPlugSharpFactory.cpp
├── AudioPlugSharpFactory.h
├── AudioPlugSharpHost.cpp
├── AudioPlugSharpHost.h
├── AudioPlugSharpParameter.cpp
├── AudioPlugSharpParameter.h
├── AudioPlugSharpProcessor.cpp
├── AudioPlugSharpProcessor.h
├── AudioPlugSharpVst.vcxproj
├── AudioPlugSharpVst.vcxproj.filters
├── Ijwhost.xml
└── desktop.runtimeconfig.json
├── AudioPlugSharpWPF
├── AudioPlugSharpWPF.csproj
├── AudioPluginWPF.cs
├── Dial.cs
├── EditorView.xaml
├── EditorView.xaml.cs
├── EditorWindow.cs
├── Images
│ ├── DialBackground.png
│ ├── DialBackgroundWhite.png
│ ├── DialPointer.png
│ ├── DialPointerBlack.png
│ ├── PowerOff.png
│ └── PowerOn.png
├── ParameterDisplay.xaml
├── ParameterDisplay.xaml.cs
├── Themes
│ └── Generic.xaml
├── TypeConverter.cs
└── build
│ └── AudioPlugSharpWPF.targets
├── Dist
└── .gitignore
├── JackHostTest
├── JackHostTest.csproj
└── Program.cs
├── LICENSE
├── MakeDist.bat
├── MidiExample
├── MidiExample.csproj
└── MidiExamplePlugin.cs
├── README.md
├── SimpleExample
├── Properties
│ └── launchSettings.json
├── SimpleExample.csproj
└── SimpleExamplePlugin.cs
├── WPFExample
├── Properties
│ └── launchSettings.json
├── WPFExample.csproj
└── WPFExamplePlugin.cs
└── vstbuild
└── .gitignore
/.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, pull_request]
5 |
6 | jobs:
7 | build:
8 | name: Build Windows
9 | runs-on: windows-latest
10 |
11 | steps:
12 | - name: Checkout code
13 | uses: actions/checkout@v3
14 | with:
15 | submodules: recursive
16 |
17 | - name: Setup MSBuild and add to PATH
18 | uses: microsoft/setup-msbuild@v1.3.1
19 |
20 | - name: Build VST3 SDK
21 | working-directory: ${{github.workspace}}/vstbuild
22 | run:
23 | cmake.exe -G "Visual Studio 17 2022" -A x64 ../vst3sdk
24 |
25 | - name: Run MSBuild
26 | working-directory: ${{github.workspace}}
27 | run: msbuild .\AudioPlugSharp.sln
28 |
--------------------------------------------------------------------------------
/.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 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | [Rr]eleases/
21 | x64/
22 | x86/
23 | [Aa][Rr][Mm]/
24 | [Aa][Rr][Mm]64/
25 | bld/
26 | [Bb]in/
27 | [Oo]bj/
28 | [Ll]og/
29 |
30 | # Visual Studio 2015/2017 cache/options directory
31 | .vs/
32 | # Uncomment if you have tasks that create the project's static files in wwwroot
33 | #wwwroot/
34 |
35 | # Visual Studio 2017 auto generated files
36 | Generated\ Files/
37 |
38 | # MSTest test Results
39 | [Tt]est[Rr]esult*/
40 | [Bb]uild[Ll]og.*
41 |
42 | # NUNIT
43 | *.VisualState.xml
44 | TestResult.xml
45 |
46 | # Build Results of an ATL Project
47 | [Dd]ebugPS/
48 | [Rr]eleasePS/
49 | dlldata.c
50 |
51 | # Benchmark Results
52 | BenchmarkDotNet.Artifacts/
53 |
54 | # .NET Core
55 | project.lock.json
56 | project.fragment.lock.json
57 | artifacts/
58 |
59 | # StyleCop
60 | StyleCopReport.xml
61 |
62 | # Files built by Visual Studio
63 | *_i.c
64 | *_p.c
65 | *_h.h
66 | *.ilk
67 | *.meta
68 | *.obj
69 | *.iobj
70 | *.pch
71 | *.pdb
72 | *.ipdb
73 | *.pgc
74 | *.pgd
75 | *.rsp
76 | *.sbr
77 | *.tlb
78 | *.tli
79 | *.tlh
80 | *.tmp
81 | *.tmp_proj
82 | *_wpftmp.csproj
83 | *.log
84 | *.vspscc
85 | *.vssscc
86 | .builds
87 | *.pidb
88 | *.svclog
89 | *.scc
90 |
91 | # Chutzpah Test files
92 | _Chutzpah*
93 |
94 | # Visual C++ cache files
95 | ipch/
96 | *.aps
97 | *.ncb
98 | *.opendb
99 | *.opensdf
100 | *.sdf
101 | *.cachefile
102 | *.VC.db
103 | *.VC.VC.opendb
104 |
105 | # Visual Studio profiler
106 | *.psess
107 | *.vsp
108 | *.vspx
109 | *.sap
110 |
111 | # Visual Studio Trace Files
112 | *.e2e
113 |
114 | # TFS 2012 Local Workspace
115 | $tf/
116 |
117 | # Guidance Automation Toolkit
118 | *.gpState
119 |
120 | # ReSharper is a .NET coding add-in
121 | _ReSharper*/
122 | *.[Rr]e[Ss]harper
123 | *.DotSettings.user
124 |
125 | # JustCode is a .NET coding add-in
126 | .JustCode
127 |
128 | # TeamCity is a build add-in
129 | _TeamCity*
130 |
131 | # DotCover is a Code Coverage Tool
132 | *.dotCover
133 |
134 | # AxoCover is a Code Coverage Tool
135 | .axoCover/*
136 | !.axoCover/settings.json
137 |
138 | # Visual Studio code coverage results
139 | *.coverage
140 | *.coveragexml
141 |
142 | # NCrunch
143 | _NCrunch_*
144 | .*crunch*.local.xml
145 | nCrunchTemp_*
146 |
147 | # MightyMoose
148 | *.mm.*
149 | AutoTest.Net/
150 |
151 | # Web workbench (sass)
152 | .sass-cache/
153 |
154 | # Installshield output folder
155 | [Ee]xpress/
156 |
157 | # DocProject is a documentation generator add-in
158 | DocProject/buildhelp/
159 | DocProject/Help/*.HxT
160 | DocProject/Help/*.HxC
161 | DocProject/Help/*.hhc
162 | DocProject/Help/*.hhk
163 | DocProject/Help/*.hhp
164 | DocProject/Help/Html2
165 | DocProject/Help/html
166 |
167 | # Click-Once directory
168 | publish/
169 |
170 | # Publish Web Output
171 | *.[Pp]ublish.xml
172 | *.azurePubxml
173 | # Note: Comment the next line if you want to checkin your web deploy settings,
174 | # but database connection strings (with potential passwords) will be unencrypted
175 | *.pubxml
176 | *.publishproj
177 |
178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
179 | # checkin your Azure Web App publish settings, but sensitive information contained
180 | # in these scripts will be unencrypted
181 | PublishScripts/
182 |
183 | # NuGet Packages
184 | *.nupkg
185 | # The packages folder can be ignored because of Package Restore
186 | **/[Pp]ackages/*
187 | # except build/, which is used as an MSBuild target.
188 | !**/[Pp]ackages/build/
189 | # Uncomment if necessary however generally it will be regenerated when needed
190 | #!**/[Pp]ackages/repositories.config
191 | # NuGet v3's project.json files produces more ignorable files
192 | *.nuget.props
193 | *.nuget.targets
194 |
195 | # Microsoft Azure Build Output
196 | csx/
197 | *.build.csdef
198 |
199 | # Microsoft Azure Emulator
200 | ecf/
201 | rcf/
202 |
203 | # Windows Store app package directories and files
204 | AppPackages/
205 | BundleArtifacts/
206 | Package.StoreAssociation.xml
207 | _pkginfo.txt
208 | *.appx
209 |
210 | # Visual Studio cache files
211 | # files ending in .cache can be ignored
212 | *.[Cc]ache
213 | # but keep track of directories ending in .cache
214 | !?*.[Cc]ache/
215 |
216 | # Others
217 | ClientBin/
218 | ~$*
219 | *~
220 | *.dbmdl
221 | *.dbproj.schemaview
222 | *.jfm
223 | *.pfx
224 | *.publishsettings
225 | orleans.codegen.cs
226 |
227 | # Including strong name files can present a security risk
228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
229 | #*.snk
230 |
231 | # Since there are multiple workflows, uncomment next line to ignore bower_components
232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
233 | #bower_components/
234 |
235 | # RIA/Silverlight projects
236 | Generated_Code/
237 |
238 | # Backup & report files from converting an old project file
239 | # to a newer Visual Studio version. Backup files are not needed,
240 | # because we have git ;-)
241 | _UpgradeReport_Files/
242 | Backup*/
243 | UpgradeLog*.XML
244 | UpgradeLog*.htm
245 | ServiceFabricBackup/
246 | *.rptproj.bak
247 |
248 | # SQL Server files
249 | *.mdf
250 | *.ldf
251 | *.ndf
252 |
253 | # Business Intelligence projects
254 | *.rdl.data
255 | *.bim.layout
256 | *.bim_*.settings
257 | *.rptproj.rsuser
258 | *- Backup*.rdl
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
338 |
339 | # BeatPulse healthcheck temp database
340 | healthchecksdb
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "vst3sdk"]
2 | path = vst3sdk
3 | url = https://github.com/steinbergmedia/vst3sdk
4 |
--------------------------------------------------------------------------------
/AudioPlugSharp.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.2.32616.157
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AudioPlugSharpVst", "AudioPlugSharpVst\AudioPlugSharpVst.vcxproj", "{99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AudioPlugSharp", "AudioPlugSharp\AudioPlugSharp.csproj", "{9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "vstsdk", "vstsdk", "{BF51B0B5-4A48-449C-B888-0560E85F3D70}"
11 | EndProject
12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdk", "vstbuild\public.sdk\sdk.vcxproj", "{0E457A07-0E59-35DD-BA1C-904D39085C52}"
13 | EndProject
14 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pluginterfaces", "vstbuild\pluginterfaces\pluginterfaces.vcxproj", "{61B1304B-AA25-31A3-83D3-9DE105658B7B}"
15 | EndProject
16 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base", "vstbuild\base\base.vcxproj", "{D47CA71A-67FB-39FC-A8D8-C42E728E322D}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleExample", "SimpleExample\SimpleExample.csproj", "{4CAC752E-03D5-4D47-AC65-A53ADD73BED9}"
19 | ProjectSection(ProjectDependencies) = postProject
20 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30} = {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}
21 | {EE48EC6C-2833-458A-8155-8249502F7613} = {EE48EC6C-2833-458A-8155-8249502F7613}
22 | EndProjectSection
23 | EndProject
24 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdk_common", "vstbuild\public.sdk\sdk_common.vcxproj", "{D842966E-FE62-3586-A148-214BA9825EE3}"
25 | EndProject
26 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AudioPlugSharpWPF", "AudioPlugSharpWPF\AudioPlugSharpWPF.csproj", "{EE48EC6C-2833-458A-8155-8249502F7613}"
27 | ProjectSection(ProjectDependencies) = postProject
28 | {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77} = {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}
29 | EndProjectSection
30 | EndProject
31 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExamplePlugins", "ExamplePlugins", "{F427CAA0-B75D-4186-9B6D-3E97BB729B4C}"
32 | EndProject
33 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WPFExample", "WPFExample\WPFExample.csproj", "{E3D78C8F-93BB-43B4-8774-0A3F08594024}"
34 | ProjectSection(ProjectDependencies) = postProject
35 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30} = {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}
36 | EndProjectSection
37 | EndProject
38 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MidiExample", "MidiExample\MidiExample.csproj", "{518B2E52-C5C6-4204-A8E4-47F486748947}"
39 | ProjectSection(ProjectDependencies) = postProject
40 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30} = {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}
41 | {EE48EC6C-2833-458A-8155-8249502F7613} = {EE48EC6C-2833-458A-8155-8249502F7613}
42 | EndProjectSection
43 | EndProject
44 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AudioPlugSharpHost", "AudioPlugSharpHost\AudioPlugSharpHost.csproj", "{060D54F3-D6E3-4334-89AC-E1914F478D1C}"
45 | EndProject
46 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AudioPlugSharpJack", "AudioPlugSharpJack\AudioPlugSharpJack.csproj", "{4C129057-1BB9-4A4C-9CBF-8AF8562B6E5D}"
47 | EndProject
48 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "HostTests", "HostTests", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
49 | EndProject
50 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JackHostTest", "JackHostTest\JackHostTest.csproj", "{FC568608-D05A-4F61-AF60-B1A6AC3A62FA}"
51 | EndProject
52 | Global
53 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
54 | Debug|Any CPU = Debug|Any CPU
55 | Debug|x64 = Debug|x64
56 | Release|Any CPU = Release|Any CPU
57 | Release|x64 = Release|x64
58 | EndGlobalSection
59 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
60 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}.Debug|Any CPU.ActiveCfg = Debug|x64
61 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}.Debug|Any CPU.Build.0 = Debug|x64
62 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}.Debug|x64.ActiveCfg = Debug|x64
63 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}.Debug|x64.Build.0 = Debug|x64
64 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}.Release|Any CPU.ActiveCfg = Release|x64
65 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}.Release|Any CPU.Build.0 = Release|x64
66 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}.Release|x64.ActiveCfg = Release|x64
67 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}.Release|x64.Build.0 = Release|x64
68 | {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
69 | {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}.Debug|Any CPU.Build.0 = Debug|Any CPU
70 | {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}.Debug|x64.ActiveCfg = Debug|Any CPU
71 | {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}.Debug|x64.Build.0 = Debug|Any CPU
72 | {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}.Release|Any CPU.ActiveCfg = Release|Any CPU
73 | {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}.Release|Any CPU.Build.0 = Release|Any CPU
74 | {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}.Release|x64.ActiveCfg = Release|Any CPU
75 | {9418F8D2-A4BB-4A69-A2DC-7FD312E49D77}.Release|x64.Build.0 = Release|Any CPU
76 | {0E457A07-0E59-35DD-BA1C-904D39085C52}.Debug|Any CPU.ActiveCfg = Debug|x64
77 | {0E457A07-0E59-35DD-BA1C-904D39085C52}.Debug|Any CPU.Build.0 = Debug|x64
78 | {0E457A07-0E59-35DD-BA1C-904D39085C52}.Debug|x64.ActiveCfg = Debug|x64
79 | {0E457A07-0E59-35DD-BA1C-904D39085C52}.Debug|x64.Build.0 = Debug|x64
80 | {0E457A07-0E59-35DD-BA1C-904D39085C52}.Release|Any CPU.ActiveCfg = Release|x64
81 | {0E457A07-0E59-35DD-BA1C-904D39085C52}.Release|Any CPU.Build.0 = Release|x64
82 | {0E457A07-0E59-35DD-BA1C-904D39085C52}.Release|x64.ActiveCfg = Release|x64
83 | {0E457A07-0E59-35DD-BA1C-904D39085C52}.Release|x64.Build.0 = Release|x64
84 | {61B1304B-AA25-31A3-83D3-9DE105658B7B}.Debug|Any CPU.ActiveCfg = Debug|x64
85 | {61B1304B-AA25-31A3-83D3-9DE105658B7B}.Debug|Any CPU.Build.0 = Debug|x64
86 | {61B1304B-AA25-31A3-83D3-9DE105658B7B}.Debug|x64.ActiveCfg = Debug|x64
87 | {61B1304B-AA25-31A3-83D3-9DE105658B7B}.Debug|x64.Build.0 = Debug|x64
88 | {61B1304B-AA25-31A3-83D3-9DE105658B7B}.Release|Any CPU.ActiveCfg = Release|x64
89 | {61B1304B-AA25-31A3-83D3-9DE105658B7B}.Release|Any CPU.Build.0 = Release|x64
90 | {61B1304B-AA25-31A3-83D3-9DE105658B7B}.Release|x64.ActiveCfg = Release|x64
91 | {61B1304B-AA25-31A3-83D3-9DE105658B7B}.Release|x64.Build.0 = Release|x64
92 | {D47CA71A-67FB-39FC-A8D8-C42E728E322D}.Debug|Any CPU.ActiveCfg = Debug|x64
93 | {D47CA71A-67FB-39FC-A8D8-C42E728E322D}.Debug|Any CPU.Build.0 = Debug|x64
94 | {D47CA71A-67FB-39FC-A8D8-C42E728E322D}.Debug|x64.ActiveCfg = Debug|x64
95 | {D47CA71A-67FB-39FC-A8D8-C42E728E322D}.Debug|x64.Build.0 = Debug|x64
96 | {D47CA71A-67FB-39FC-A8D8-C42E728E322D}.Release|Any CPU.ActiveCfg = Release|x64
97 | {D47CA71A-67FB-39FC-A8D8-C42E728E322D}.Release|Any CPU.Build.0 = Release|x64
98 | {D47CA71A-67FB-39FC-A8D8-C42E728E322D}.Release|x64.ActiveCfg = Release|x64
99 | {D47CA71A-67FB-39FC-A8D8-C42E728E322D}.Release|x64.Build.0 = Release|x64
100 | {4CAC752E-03D5-4D47-AC65-A53ADD73BED9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
101 | {4CAC752E-03D5-4D47-AC65-A53ADD73BED9}.Debug|Any CPU.Build.0 = Debug|Any CPU
102 | {4CAC752E-03D5-4D47-AC65-A53ADD73BED9}.Debug|x64.ActiveCfg = Debug|Any CPU
103 | {4CAC752E-03D5-4D47-AC65-A53ADD73BED9}.Debug|x64.Build.0 = Debug|Any CPU
104 | {4CAC752E-03D5-4D47-AC65-A53ADD73BED9}.Release|Any CPU.ActiveCfg = Release|Any CPU
105 | {4CAC752E-03D5-4D47-AC65-A53ADD73BED9}.Release|Any CPU.Build.0 = Release|Any CPU
106 | {4CAC752E-03D5-4D47-AC65-A53ADD73BED9}.Release|x64.ActiveCfg = Release|Any CPU
107 | {4CAC752E-03D5-4D47-AC65-A53ADD73BED9}.Release|x64.Build.0 = Release|Any CPU
108 | {D842966E-FE62-3586-A148-214BA9825EE3}.Debug|Any CPU.ActiveCfg = Debug|x64
109 | {D842966E-FE62-3586-A148-214BA9825EE3}.Debug|Any CPU.Build.0 = Debug|x64
110 | {D842966E-FE62-3586-A148-214BA9825EE3}.Debug|x64.ActiveCfg = Debug|x64
111 | {D842966E-FE62-3586-A148-214BA9825EE3}.Debug|x64.Build.0 = Debug|x64
112 | {D842966E-FE62-3586-A148-214BA9825EE3}.Release|Any CPU.ActiveCfg = Release|x64
113 | {D842966E-FE62-3586-A148-214BA9825EE3}.Release|Any CPU.Build.0 = Release|x64
114 | {D842966E-FE62-3586-A148-214BA9825EE3}.Release|x64.ActiveCfg = Release|x64
115 | {D842966E-FE62-3586-A148-214BA9825EE3}.Release|x64.Build.0 = Release|x64
116 | {EE48EC6C-2833-458A-8155-8249502F7613}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
117 | {EE48EC6C-2833-458A-8155-8249502F7613}.Debug|Any CPU.Build.0 = Debug|Any CPU
118 | {EE48EC6C-2833-458A-8155-8249502F7613}.Debug|x64.ActiveCfg = Debug|Any CPU
119 | {EE48EC6C-2833-458A-8155-8249502F7613}.Debug|x64.Build.0 = Debug|Any CPU
120 | {EE48EC6C-2833-458A-8155-8249502F7613}.Release|Any CPU.ActiveCfg = Release|Any CPU
121 | {EE48EC6C-2833-458A-8155-8249502F7613}.Release|Any CPU.Build.0 = Release|Any CPU
122 | {EE48EC6C-2833-458A-8155-8249502F7613}.Release|x64.ActiveCfg = Release|Any CPU
123 | {EE48EC6C-2833-458A-8155-8249502F7613}.Release|x64.Build.0 = Release|Any CPU
124 | {E3D78C8F-93BB-43B4-8774-0A3F08594024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
125 | {E3D78C8F-93BB-43B4-8774-0A3F08594024}.Debug|Any CPU.Build.0 = Debug|Any CPU
126 | {E3D78C8F-93BB-43B4-8774-0A3F08594024}.Debug|x64.ActiveCfg = Debug|Any CPU
127 | {E3D78C8F-93BB-43B4-8774-0A3F08594024}.Debug|x64.Build.0 = Debug|Any CPU
128 | {E3D78C8F-93BB-43B4-8774-0A3F08594024}.Release|Any CPU.ActiveCfg = Release|Any CPU
129 | {E3D78C8F-93BB-43B4-8774-0A3F08594024}.Release|Any CPU.Build.0 = Release|Any CPU
130 | {E3D78C8F-93BB-43B4-8774-0A3F08594024}.Release|x64.ActiveCfg = Release|Any CPU
131 | {E3D78C8F-93BB-43B4-8774-0A3F08594024}.Release|x64.Build.0 = Release|Any CPU
132 | {518B2E52-C5C6-4204-A8E4-47F486748947}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
133 | {518B2E52-C5C6-4204-A8E4-47F486748947}.Debug|Any CPU.Build.0 = Debug|Any CPU
134 | {518B2E52-C5C6-4204-A8E4-47F486748947}.Debug|x64.ActiveCfg = Debug|Any CPU
135 | {518B2E52-C5C6-4204-A8E4-47F486748947}.Debug|x64.Build.0 = Debug|Any CPU
136 | {518B2E52-C5C6-4204-A8E4-47F486748947}.Release|Any CPU.ActiveCfg = Release|Any CPU
137 | {518B2E52-C5C6-4204-A8E4-47F486748947}.Release|Any CPU.Build.0 = Release|Any CPU
138 | {518B2E52-C5C6-4204-A8E4-47F486748947}.Release|x64.ActiveCfg = Release|Any CPU
139 | {518B2E52-C5C6-4204-A8E4-47F486748947}.Release|x64.Build.0 = Release|Any CPU
140 | {060D54F3-D6E3-4334-89AC-E1914F478D1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
141 | {060D54F3-D6E3-4334-89AC-E1914F478D1C}.Debug|Any CPU.Build.0 = Debug|Any CPU
142 | {060D54F3-D6E3-4334-89AC-E1914F478D1C}.Debug|x64.ActiveCfg = Debug|Any CPU
143 | {060D54F3-D6E3-4334-89AC-E1914F478D1C}.Debug|x64.Build.0 = Debug|Any CPU
144 | {060D54F3-D6E3-4334-89AC-E1914F478D1C}.Release|Any CPU.ActiveCfg = Release|Any CPU
145 | {060D54F3-D6E3-4334-89AC-E1914F478D1C}.Release|Any CPU.Build.0 = Release|Any CPU
146 | {060D54F3-D6E3-4334-89AC-E1914F478D1C}.Release|x64.ActiveCfg = Release|Any CPU
147 | {060D54F3-D6E3-4334-89AC-E1914F478D1C}.Release|x64.Build.0 = Release|Any CPU
148 | {4C129057-1BB9-4A4C-9CBF-8AF8562B6E5D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
149 | {4C129057-1BB9-4A4C-9CBF-8AF8562B6E5D}.Debug|Any CPU.Build.0 = Debug|Any CPU
150 | {4C129057-1BB9-4A4C-9CBF-8AF8562B6E5D}.Debug|x64.ActiveCfg = Debug|Any CPU
151 | {4C129057-1BB9-4A4C-9CBF-8AF8562B6E5D}.Debug|x64.Build.0 = Debug|Any CPU
152 | {4C129057-1BB9-4A4C-9CBF-8AF8562B6E5D}.Release|Any CPU.ActiveCfg = Release|Any CPU
153 | {4C129057-1BB9-4A4C-9CBF-8AF8562B6E5D}.Release|Any CPU.Build.0 = Release|Any CPU
154 | {4C129057-1BB9-4A4C-9CBF-8AF8562B6E5D}.Release|x64.ActiveCfg = Release|Any CPU
155 | {4C129057-1BB9-4A4C-9CBF-8AF8562B6E5D}.Release|x64.Build.0 = Release|Any CPU
156 | {FC568608-D05A-4F61-AF60-B1A6AC3A62FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
157 | {FC568608-D05A-4F61-AF60-B1A6AC3A62FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
158 | {FC568608-D05A-4F61-AF60-B1A6AC3A62FA}.Debug|x64.ActiveCfg = Debug|Any CPU
159 | {FC568608-D05A-4F61-AF60-B1A6AC3A62FA}.Debug|x64.Build.0 = Debug|Any CPU
160 | {FC568608-D05A-4F61-AF60-B1A6AC3A62FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
161 | {FC568608-D05A-4F61-AF60-B1A6AC3A62FA}.Release|Any CPU.Build.0 = Release|Any CPU
162 | {FC568608-D05A-4F61-AF60-B1A6AC3A62FA}.Release|x64.ActiveCfg = Release|Any CPU
163 | {FC568608-D05A-4F61-AF60-B1A6AC3A62FA}.Release|x64.Build.0 = Release|Any CPU
164 | EndGlobalSection
165 | GlobalSection(SolutionProperties) = preSolution
166 | HideSolutionNode = FALSE
167 | EndGlobalSection
168 | GlobalSection(NestedProjects) = preSolution
169 | {0E457A07-0E59-35DD-BA1C-904D39085C52} = {BF51B0B5-4A48-449C-B888-0560E85F3D70}
170 | {61B1304B-AA25-31A3-83D3-9DE105658B7B} = {BF51B0B5-4A48-449C-B888-0560E85F3D70}
171 | {D47CA71A-67FB-39FC-A8D8-C42E728E322D} = {BF51B0B5-4A48-449C-B888-0560E85F3D70}
172 | {4CAC752E-03D5-4D47-AC65-A53ADD73BED9} = {F427CAA0-B75D-4186-9B6D-3E97BB729B4C}
173 | {D842966E-FE62-3586-A148-214BA9825EE3} = {BF51B0B5-4A48-449C-B888-0560E85F3D70}
174 | {E3D78C8F-93BB-43B4-8774-0A3F08594024} = {F427CAA0-B75D-4186-9B6D-3E97BB729B4C}
175 | {518B2E52-C5C6-4204-A8E4-47F486748947} = {F427CAA0-B75D-4186-9B6D-3E97BB729B4C}
176 | {FC568608-D05A-4F61-AF60-B1A6AC3A62FA} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
177 | EndGlobalSection
178 | GlobalSection(ExtensibilityGlobals) = postSolution
179 | SolutionGuid = {80717CF5-9E50-46E6-8E47-04E88EB429BD}
180 | EndGlobalSection
181 | EndGlobal
182 |
--------------------------------------------------------------------------------
/AudioPlugSharp/AudioPlugSharp.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | Library
6 | 0.6.10
7 | Mike Oliphant
8 | Easily create VST (VST3) audio plugins in C# .NET.
9 | MIT
10 | true
11 | true
12 | snupkg
13 | true
14 | true
15 | README.md
16 |
17 |
18 |
19 | true
20 |
21 |
22 |
23 | true
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/AudioPlugSharp/AudioPluginBase.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 | using System.Xml.Serialization;
6 |
7 | namespace AudioPlugSharp
8 | {
9 | public class AudioPluginBase : IAudioPlugin, IAudioPluginProcessor, IAudioPluginEditor
10 | {
11 | //
12 | // IAudioPlugin Properties
13 | //
14 |
15 | public string Company { get; protected set; }
16 | public string Website { get; protected set; }
17 | public string Contact { get; protected set; }
18 | public string PluginName { get; protected set; }
19 | public string PluginCategory { get; protected set; }
20 | public string PluginVersion { get; protected set; }
21 | public ulong PluginID { get; protected set; }
22 |
23 | public bool CacheLoadContext { get; protected set; } = false;
24 |
25 | public IAudioHost Host { get; set; }
26 |
27 | public IAudioPluginProcessor Processor { get { return this; } }
28 | public IAudioPluginEditor Editor { get { return this; } }
29 |
30 |
31 | //
32 | // IAudioPluginProcessor Properties
33 | //
34 |
35 | public AudioIOPort[] InputPorts { get; protected set; }
36 | public AudioIOPort[] OutputPorts { get; protected set; }
37 | public EAudioBitsPerSample SampleFormatsSupported { get; protected set; }
38 | public IReadOnlyList Parameters { get; private set; }
39 |
40 | List parameterList = new List();
41 | Dictionary parameterDict = new Dictionary();
42 | Dictionary parameterCCDict = new Dictionary();
43 |
44 | public AudioPluginSaveState SaveStateData { get; protected set; }
45 |
46 | //
47 | // IAudioPluginEditor Properties
48 | //
49 |
50 | public bool HasUserInterface { get; protected set; }
51 | public uint EditorWidth { get; protected set; }
52 | public uint EditorHeight { get; protected set; }
53 |
54 | public AudioPluginBase()
55 | {
56 | SaveStateData = new AudioPluginSaveState();
57 |
58 | InputPorts = new AudioIOPort[0];
59 | OutputPorts = new AudioIOPort[0];
60 |
61 | SampleFormatsSupported = EAudioBitsPerSample.Bits32 | EAudioBitsPerSample.Bits64;
62 |
63 | HasUserInterface = false;
64 | EditorWidth = 400;
65 | EditorHeight = 200;
66 | }
67 |
68 | //
69 | // IAudioPluginProcessor Methods
70 | //
71 |
72 | public virtual void Initialize()
73 | {
74 | Logger.Log("Initializing processor");
75 |
76 | Parameters = parameterList.AsReadOnly();
77 | }
78 |
79 | public void AddParameter(AudioPluginParameter parameter)
80 | {
81 | parameter.Editor = this;
82 | parameter.ParameterIndex = parameterList.Count;
83 |
84 | parameterList.Add(parameter);
85 | parameterDict[parameter.ID] = parameter;
86 |
87 | parameter.ProcessValue = parameter.DefaultValue;
88 | parameter.EditValue = parameter.DefaultValue;
89 | }
90 |
91 | public AudioPluginParameter GetParameter(string paramID)
92 | {
93 | return parameterDict[paramID];
94 | }
95 |
96 | public void AddMidiControllerMapping(AudioPluginParameter parameter, uint ccNumber)
97 | {
98 | parameterCCDict[ccNumber] = parameter;
99 | }
100 |
101 | public AudioPluginParameter GetParameterByMidiController(uint ccNumber)
102 | {
103 | if (!parameterCCDict.ContainsKey(ccNumber))
104 | return null;
105 |
106 | return parameterCCDict[ccNumber];
107 | }
108 |
109 |
110 | public virtual byte[] SaveState()
111 | {
112 | SaveStateData.SaveParameterValues(Parameters);
113 |
114 | SaveStateData.EditorWidth = EditorWidth;
115 | SaveStateData.EditorHeight = EditorHeight;
116 |
117 | XmlSerializer serializer = new XmlSerializer(SaveStateData.GetType());
118 |
119 | try
120 | {
121 | using (MemoryStream memoryStream = new MemoryStream())
122 | {
123 | serializer.Serialize(memoryStream, SaveStateData);
124 |
125 | return memoryStream.ToArray();
126 | }
127 | }
128 | catch (Exception ex)
129 | {
130 | Logger.Log("Save state serialization failed with: " + ex.ToString());
131 | }
132 |
133 | return null;
134 | }
135 |
136 | public virtual void RestoreState(byte[] stateData)
137 | {
138 | if (stateData != null)
139 | {
140 | XmlSerializer serializer = new XmlSerializer(SaveStateData.GetType());
141 |
142 | try
143 | {
144 | using (MemoryStream memoryStream = new MemoryStream(stateData))
145 | {
146 | SaveStateData = serializer.Deserialize(memoryStream) as AudioPluginSaveState;
147 | }
148 | }
149 | catch (Exception ex)
150 | {
151 | Logger.Log("Save state deserialization failed with: " + ex.ToString());
152 | }
153 |
154 | SaveStateData.RestoreParameterValues(Parameters);
155 |
156 | EditorWidth = SaveStateData.EditorWidth;
157 | EditorHeight = SaveStateData.EditorHeight;
158 | }
159 | }
160 |
161 | public virtual void InitializeProcessing()
162 | {
163 | Logger.Log("Initialize Processing");
164 | }
165 |
166 | public virtual void Start()
167 | {
168 | Logger.Log("Start Processor");
169 | }
170 |
171 | public virtual void Stop()
172 | {
173 | Logger.Log("Stop Processor");
174 | }
175 |
176 | public virtual void HandleParameterChange(AudioPluginParameter parameter, double newNormalizedValue, int sampleOffset)
177 | {
178 | parameter.AddParameterChangePoint(newNormalizedValue, sampleOffset);
179 | }
180 |
181 | public virtual void HandleNoteOn(int channel, int noteNumber, float velocity, int sampleOffset)
182 | {
183 | }
184 |
185 | public virtual void HandleNoteOff(int channel, int noteNumber, float velocity, int sampleOffset)
186 | {
187 | }
188 |
189 | public virtual void HandlePolyPressure(int channel, int noteNumber, float pressure, int sampleOffset)
190 | {
191 | }
192 |
193 | public virtual void SetMaxAudioBufferSize(uint maxSamples, EAudioBitsPerSample bitsPerSample)
194 | {
195 | foreach (AudioIOPort port in InputPorts)
196 | {
197 | port.SetMaxSize(maxSamples, bitsPerSample);
198 | }
199 |
200 | foreach (AudioIOPort port in OutputPorts)
201 | {
202 | port.SetMaxSize(maxSamples, bitsPerSample);
203 | }
204 | }
205 |
206 | public virtual void PreProcess()
207 | {
208 | foreach (AudioIOPort input in InputPorts)
209 | {
210 | input.ReadData();
211 | }
212 | }
213 |
214 | public virtual void Process()
215 | {
216 | }
217 |
218 | public virtual void PostProcess()
219 | {
220 | foreach (AudioIOPort output in OutputPorts)
221 | {
222 | output.WriteData();
223 | }
224 | }
225 |
226 |
227 | //
228 | // IAudioPluginEditor Methods
229 | //
230 |
231 | public virtual double GetDpiScale()
232 | {
233 | return 1.0;
234 | }
235 |
236 | public virtual void InitializeEditor()
237 | {
238 | Logger.Log("Initialize Editor");
239 | }
240 |
241 | public virtual void ResizeEditor(uint newWidth, uint newHeight)
242 | {
243 | EditorWidth = newWidth;
244 | EditorHeight = newHeight;
245 | }
246 |
247 | public virtual void ShowEditor(IntPtr parentWindow)
248 | {
249 | }
250 |
251 | public virtual void HideEditor()
252 | {
253 | }
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/AudioPlugSharp/AudioPluginParameter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Text;
5 |
6 | namespace AudioPlugSharp
7 | {
8 | public enum EAudioPluginParameterType
9 | {
10 | Float,
11 | Bool,
12 | Int,
13 | Enum
14 | }
15 |
16 | public class AudioPluginParameter : INotifyPropertyChanged
17 | {
18 | public IAudioPluginEditor Editor { get; internal set; }
19 |
20 | public event PropertyChangedEventHandler PropertyChanged;
21 |
22 | public int ParameterIndex { get; internal set; }
23 | public string ID { get; set; }
24 | public string Name { get; set; }
25 | public EAudioPluginParameterType Type { get; set; }
26 | public double MinValue { get; set; }
27 | public double MaxValue { get; set; }
28 | public double DefaultValue { get; set; }
29 | public string ValueFormat { get; set; }
30 |
31 | double processValue;
32 | double editValue;
33 |
34 | public double ProcessValue
35 | {
36 | get { return processValue; }
37 | set
38 | {
39 | if (this.processValue != value)
40 | {
41 | this.processValue = value;
42 | }
43 | }
44 | }
45 |
46 | // To be used by UI controls to access the value
47 | public double EditValue
48 | {
49 | get
50 | {
51 | return editValue;
52 | }
53 | set
54 | {
55 | if (editValue != value)
56 | {
57 | editValue = value;
58 |
59 | if (Editor != null) // Just in case EditValue is set before parameter has been added
60 | {
61 | // This should really be exposed to UI controls so multiple edits can be inside a begin/end edit
62 | Editor.Host.BeginEdit(ParameterIndex);
63 | Editor.Host.PerformEdit(ParameterIndex, GetValueNormalized(value));
64 | Editor.Host.EndEdit(ParameterIndex);
65 | }
66 |
67 | OnPropertyChanged("EditValue");
68 | OnPropertyChanged("DisplayValue");
69 | }
70 | }
71 | }
72 |
73 | public string DisplayValue { get { return String.Format(ValueFormat, editValue); } }
74 |
75 | public double NormalizedProcessValue
76 | {
77 | get { return GetValueNormalized(processValue); }
78 | set { processValue = GetValueDenormalized(value); }
79 | }
80 |
81 | public double NormalizedEditValue
82 | {
83 | get { return GetValueNormalized(editValue); }
84 | set { editValue = GetValueDenormalized(value); }
85 | }
86 |
87 | public AudioPluginParameter()
88 | {
89 | MinValue = 0;
90 | MaxValue = 1;
91 | DefaultValue = 0.5;
92 | ValueFormat = "{0:0.0}";
93 | }
94 |
95 | public double GetValueNormalized(double value)
96 | {
97 | return (value - MinValue) / (MaxValue - MinValue);
98 | }
99 |
100 | public double GetValueDenormalized(double value)
101 | {
102 | return MinValue + ((MaxValue - MinValue) * value);
103 | }
104 |
105 | double prevParamValue;
106 | double slope;
107 | int prevParamChangeSample;
108 | int nextParamChangeSample;
109 | bool needInterpolationUpdate = false;
110 |
111 | public void AddParameterChangePoint(double newNormalizedValue, int sampleOffset)
112 | {
113 | prevParamChangeSample = nextParamChangeSample;
114 | prevParamValue = processValue;
115 |
116 | nextParamChangeSample = sampleOffset;
117 | NormalizedProcessValue = newNormalizedValue;
118 |
119 | needInterpolationUpdate = true;
120 |
121 | if (needInterpolationUpdate)
122 | {
123 | slope = (double)(processValue - prevParamValue) / (double)(nextParamChangeSample - prevParamChangeSample);
124 |
125 | // No need to update if the parameter isn't changing
126 | if (slope == 0)
127 | needInterpolationUpdate = false;
128 | }
129 | }
130 |
131 | public void ResetParameterChange()
132 | {
133 | prevParamValue = processValue;
134 | prevParamChangeSample = -1;
135 | nextParamChangeSample = -1;
136 | needInterpolationUpdate = false;
137 | }
138 |
139 | public bool NeedInterpolationUpdate
140 | {
141 | get { return needInterpolationUpdate; }
142 | }
143 |
144 | public double GetInterpolatedProcessValue(int sampleOffset)
145 | {
146 | if (!needInterpolationUpdate)
147 | return prevParamValue;
148 |
149 | // If we go past the last control point, the value stays the same
150 | if (sampleOffset > nextParamChangeSample)
151 | {
152 | needInterpolationUpdate = false;
153 |
154 | return prevParamValue;
155 | }
156 |
157 | return prevParamValue + ((double)(sampleOffset - prevParamChangeSample) * slope);
158 | }
159 |
160 | public void OnPropertyChanged(string name)
161 | {
162 | if (PropertyChanged != null)
163 | {
164 | PropertyChanged(this, new PropertyChangedEventArgs(name));
165 | }
166 | }
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/AudioPlugSharp/AudioPluginSaveState.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace AudioPlugSharp
6 | {
7 | public class AudioPluginSaveParameter
8 | {
9 | public string ID { get; set; }
10 | public double Value { get; set; }
11 | }
12 |
13 | public class AudioPluginSaveState
14 | {
15 | public List ParameterValues { get; set; }
16 | public uint EditorWidth { get; set; } = 800;
17 | public uint EditorHeight { get; set; } = 600;
18 |
19 | public void SaveParameterValues(IReadOnlyList parameters)
20 | {
21 | ParameterValues = new List();
22 |
23 | foreach (AudioPluginParameter parameter in parameters)
24 | {
25 | ParameterValues.Add(new AudioPluginSaveParameter { ID = parameter.ID, Value = parameter.ProcessValue });
26 | }
27 | }
28 |
29 | public void RestoreParameterValues(IReadOnlyList parameters)
30 | {
31 | foreach (AudioPluginParameter parameter in parameters)
32 | {
33 | foreach (AudioPluginSaveParameter saveParam in ParameterValues)
34 | {
35 | if (saveParam.ID == parameter.ID)
36 | {
37 | parameter.EditValue = saveParam.Value;
38 | parameter.ProcessValue = saveParam.Value;
39 |
40 | break;
41 | }
42 | }
43 | }
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/AudioPlugSharp/IAudioHost.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace AudioPlugSharp
8 | {
9 | public interface IAudioHost
10 | {
11 | double SampleRate { get; }
12 | UInt32 MaxAudioBufferSize { get; }
13 | UInt32 CurrentAudioBufferSize { get; }
14 | EAudioBitsPerSample BitsPerSample { get; }
15 | double BPM { get; }
16 | Int64 CurrentProjectSample { get; }
17 | bool IsPlaying { get; }
18 |
19 | void SendNoteOn(int channel, int noteNumber, float velocity, int sampleOffset);
20 | void SendNoteOff(int channel, int noteNumber, float velocity, int sampleOffset);
21 | void SendCC(int channel, int ccNumber, int ccValue, int sampleOffset);
22 | void SendPolyPressure(int channel, int noteNumber, float pressure, int sampleOffset);
23 |
24 | void ProcessAllEvents();
25 | int ProcessEvents();
26 |
27 | void BeginEdit(int parameter);
28 | void PerformEdit(int parameter, double normalizedValue);
29 | void EndEdit(int parameter);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AudioPlugSharp/IAudioPlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace AudioPlugSharp
6 | {
7 | public interface IAudioPlugin
8 | {
9 | string Company { get; }
10 | string Website { get; }
11 | string Contact { get; }
12 | string PluginName { get; }
13 | string PluginCategory { get; }
14 | string PluginVersion { get; }
15 | ulong PluginID { get; }
16 |
17 | bool CacheLoadContext { get; }
18 |
19 | IAudioHost Host { get; set; }
20 |
21 | IAudioPluginProcessor Processor { get; }
22 | IAudioPluginEditor Editor { get; }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/AudioPlugSharp/IAudioPluginEditor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.Text;
5 |
6 | namespace AudioPlugSharp
7 | {
8 | public interface IAudioPluginEditor
9 | {
10 | IAudioHost Host { get; }
11 | uint EditorWidth { get; }
12 | uint EditorHeight { get; }
13 | IReadOnlyList Parameters { get; }
14 | IAudioPluginProcessor Processor { get; }
15 |
16 | bool HasUserInterface { get; }
17 |
18 | double GetDpiScale();
19 | void InitializeEditor();
20 | void ResizeEditor(uint newWidth, uint newHeight);
21 | void ShowEditor(IntPtr parentWindow);
22 | void HideEditor();
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/AudioPlugSharp/IAudioPluginProcessor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace AudioPlugSharp
6 | {
7 | public interface IAudioPluginProcessor
8 | {
9 | AudioIOPort[] InputPorts { get; }
10 | AudioIOPort[] OutputPorts { get; }
11 | EAudioBitsPerSample SampleFormatsSupported { get; }
12 | IReadOnlyList Parameters { get; }
13 |
14 | void Initialize();
15 | void AddParameter(AudioPluginParameter parameter);
16 | AudioPluginParameter GetParameter(string paramID);
17 | AudioPluginParameter GetParameterByMidiController(uint ccNumber);
18 | byte[] SaveState();
19 | void RestoreState(byte[] stateData);
20 |
21 | void InitializeProcessing();
22 |
23 | void Start();
24 | void Stop();
25 |
26 | void HandleParameterChange(AudioPluginParameter parameter, double newNormalizedValue, int sampleOffset);
27 | void HandleNoteOn(int channel, int noteNumber, float velocity, int sampleOffset);
28 | void HandleNoteOff(int channel, int noteNumber, float velocity, int sampleOffset);
29 | void HandlePolyPressure(int channel, int noteNumber, float pressure, int sampleOffset);
30 |
31 | void SetMaxAudioBufferSize(uint maxSamples, EAudioBitsPerSample bitsPerSample);
32 | void PreProcess();
33 | void Process();
34 | void PostProcess();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/AudioPlugSharp/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Concurrent;
3 | using System.Collections.Generic;
4 | using System.Globalization;
5 | using System.IO;
6 | using System.Threading;
7 |
8 | namespace AudioPlugSharp
9 | {
10 | public class Logger
11 | {
12 | const int NumLogsToRetain = 10;
13 |
14 | ///
15 | /// Write log entries immediately - should not be used in normal operation
16 | ///
17 | public static bool ImmediateMode { get; set; }
18 |
19 | public static bool WriteToStdErr { get; set; }
20 |
21 | static ConcurrentQueue logQueue;
22 | static StreamWriter logWriter = null;
23 | static string logPath;
24 | static string logFileDateFormat = "yyyy-MM-dd-HH-mm-ss-ffff";
25 |
26 | static Logger()
27 | {
28 | //ImmediateMode = true;
29 |
30 | logQueue = new ConcurrentQueue();
31 |
32 | logPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "AudioPlugSharp");
33 |
34 | try
35 | {
36 | if (!Directory.Exists(logPath))
37 | Directory.CreateDirectory(logPath);
38 |
39 | DeleteOldLogs();
40 | }
41 | catch (Exception ex)
42 | {
43 | Log("Failed to delete old logfiles: " + ex.ToString());
44 | }
45 |
46 | try
47 | {
48 | string filename = string.Format("{0:" + logFileDateFormat + "}.log", DateTime.Now);
49 |
50 | string logFile = Path.Combine(logPath, filename);
51 |
52 | logWriter = new StreamWriter(logFile);
53 |
54 | Thread logThread = new Thread(() => DoLogging());
55 |
56 | logThread.Priority = ThreadPriority.Lowest;
57 | logThread.IsBackground = true;
58 | logThread.Start();
59 | }
60 | catch
61 | {
62 | }
63 | }
64 |
65 | public static void Log(string logEntry)
66 | {
67 | logEntry = string.Format("[{0:yyyy/MM/dd HH:mm:ss:ffff}] {1}", DateTime.Now, logEntry);
68 |
69 | logQueue.Enqueue(logEntry);
70 |
71 | if (ImmediateMode)
72 | WriteLogs();
73 |
74 | if (WriteToStdErr)
75 | Console.Error.WriteLine(logEntry);
76 | }
77 |
78 | static void DeleteOldLogs()
79 | {
80 | string[] logFiles = Directory.GetFiles(logPath, "*.log");
81 |
82 | if (logFiles.Length >= NumLogsToRetain)
83 | {
84 | Dictionary fileTimes = new Dictionary();
85 |
86 | foreach (string logFile in logFiles)
87 | {
88 | DateTime fileTime = DateTime.MinValue;
89 |
90 | DateTime.TryParseExact(Path.GetFileNameWithoutExtension(logFile), logFileDateFormat, CultureInfo.InvariantCulture, DateTimeStyles.None, out fileTime);
91 |
92 | fileTimes[logFile] = fileTime.Ticks;
93 | }
94 |
95 | Array.Sort(logFiles, delegate (string file1, string file2)
96 | {
97 | return fileTimes[file2].CompareTo(fileTimes[file1]);
98 | });
99 | }
100 |
101 | for (int i = NumLogsToRetain - 1; i < logFiles.Length; i++)
102 | {
103 | try
104 | {
105 | File.Delete(logFiles[i]);
106 | }
107 | catch { }
108 | }
109 | }
110 |
111 | static void DoLogging()
112 | {
113 | do
114 | {
115 | Thread.Sleep(250);
116 |
117 | if (logQueue.Count > 0)
118 | {
119 | WriteLogs();
120 | }
121 | }
122 | while (true);
123 | }
124 |
125 | static void WriteLogs()
126 | {
127 | string logEntry;
128 | bool wroteEntries = false;
129 |
130 | while (logQueue.TryDequeue(out logEntry))
131 | {
132 | try
133 | {
134 | logWriter.WriteLine(logEntry);
135 | }
136 | catch { }
137 |
138 | wroteEntries = true;
139 | }
140 |
141 | if (wroteEntries)
142 | {
143 | try
144 | {
145 | logWriter.Flush();
146 | }
147 | catch { }
148 | }
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/AudioPlugSharp/PluginLoader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Reflection;
5 | using System.Runtime.InteropServices;
6 | using System.Runtime.Loader;
7 |
8 | namespace AudioPlugSharp
9 | {
10 | public static class PluginLoader
11 | {
12 | private static Dictionary Contexts = new Dictionary();
13 |
14 | public static IAudioPlugin LoadPluginFromAssembly(string assemblyName)
15 | {
16 | string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
17 |
18 | if (!Contexts.TryGetValue(assemblyPath, out var loadContext))
19 | {
20 | loadContext = new PluginLoadContext(Path.Combine(assemblyPath, assemblyName) + ".dll");
21 | }
22 |
23 | Assembly pluginAssembly = LoadAssembly(assemblyName, loadContext);
24 |
25 | IAudioPlugin plugin = GetObjectByInterface(pluginAssembly, typeof(IAudioPlugin)) as IAudioPlugin;
26 |
27 | if ((plugin != null) && plugin.CacheLoadContext)
28 | {
29 | Contexts[assemblyPath] = loadContext;
30 | }
31 |
32 | return plugin;
33 | }
34 |
35 | static Assembly LoadAssembly(String assemblyName, AssemblyLoadContext loadContext)
36 | {
37 | try
38 | {
39 | Logger.Log("Load assembly: " + assemblyName);
40 |
41 | String executingAseemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
42 |
43 | //Assembly^ assembly = AssemblyLoadContext::GetLoadContext(Reflection::Assembly::GetExecutingAssembly())->LoadFromAssemblyPath(
44 | // Path::Combine(executingAseemblyPath, (gcnew Reflection::AssemblyName(assemblyName))->Name + ".dll"));
45 |
46 | AssemblyName actualAssemblyName = new AssemblyName(assemblyName);
47 |
48 | Logger.Log("Actual assembly name is " + actualAssemblyName);
49 |
50 | Assembly assembly = loadContext.LoadFromAssemblyName(actualAssemblyName);
51 |
52 | //Assembly^ assembly = loadContext->LoadFromAssemblyPath(
53 | // Path::Combine(executingAseemblyPath, (gcnew Reflection::AssemblyName(assemblyName))->Name + ".dll"));
54 |
55 | return assembly;
56 | }
57 | catch (System.Exception ex)
58 | {
59 | Logger.Log("Unable to load assembly: " + assemblyName + " with: " + ex.ToString());
60 | }
61 |
62 | return null;
63 | }
64 |
65 | static Object GetObjectByInterface(Assembly assembly, Type interfaceType)
66 | {
67 | Logger.Log("Looking for type: " + interfaceType.AssemblyQualifiedName + " in assembly " + assembly.FullName);
68 |
69 | Logger.Log("FullyQualifiedName: " + typeof(IAudioPlugin).Module.FullyQualifiedName);
70 |
71 |
72 | Type matchedType = null;
73 |
74 | try
75 | {
76 | Logger.Log("Assembly types: " + assembly.GetTypes().Length);
77 |
78 |
79 | foreach (Type type in assembly.GetTypes())
80 | {
81 | if (type.IsPublic)
82 | {
83 | Logger.Log("Checking type: " + type.Name + " -- " + type.Module.FullyQualifiedName);
84 |
85 | if (interfaceType.IsAssignableFrom(type))
86 | {
87 | Logger.Log("Matched");
88 |
89 | matchedType = type;
90 |
91 | break;
92 | }
93 |
94 | //for each (System::Type^ iType in type->GetInterfaces())
95 | //{
96 | // Logger::Log("Checking interface type: " + iType->Name + " -- " + iType->Module->FullyQualifiedName);
97 |
98 | // if (iType == interfaceType)
99 | // {
100 | // Logger::Log("Matched");
101 |
102 | // matchedType = type;
103 |
104 | // break;
105 | // }
106 | //}
107 | }
108 |
109 | if (matchedType != null)
110 | break;
111 | }
112 | }
113 | catch (Exception ex)
114 | {
115 | Logger.Log("Type scanning failed with: " + ex.ToString());
116 | }
117 |
118 | if (matchedType == null)
119 | {
120 | Logger.Log("Unable to find type");
121 |
122 | return null;
123 | }
124 |
125 | Object obj = null;
126 |
127 | try
128 | {
129 | obj = System.Activator.CreateInstance(matchedType);
130 | }
131 | catch (Exception ex)
132 | {
133 | Logger.Log("Failed to create object: " + ex.ToString());
134 | }
135 |
136 | return obj;
137 | }
138 | }
139 |
140 |
141 | public class PluginLoadContext : AssemblyLoadContext
142 | {
143 | AssemblyDependencyResolver resolver;
144 |
145 | public PluginLoadContext(String pluginPath)
146 | {
147 | Logger.Log("Load context from: " + pluginPath);
148 |
149 | resolver = new AssemblyDependencyResolver(pluginPath);
150 | }
151 |
152 | protected override Assembly Load(AssemblyName assemblyName)
153 | {
154 | if (assemblyName.Name == "AudioPlugSharp")
155 | {
156 | Logger.Log("Skipping AudioPlugSharp assembly load");
157 |
158 | return AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).LoadFromAssemblyName(assemblyName);
159 | }
160 |
161 | String assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
162 |
163 | Logger.Log("PluginLoadContext load [" + assemblyName + "] from: " + assemblyPath);
164 |
165 | if (assemblyPath != null)
166 | {
167 | Assembly assembly = null;
168 |
169 | try
170 | {
171 | assembly = LoadFromAssemblyPath(assemblyPath);
172 | }
173 | catch (Exception ex)
174 | {
175 | Logger.Log("Assembly load failed with: " + ex.ToString());
176 | }
177 |
178 | return assembly;
179 | }
180 |
181 | return null;
182 | }
183 |
184 | protected override IntPtr LoadUnmanagedDll(String unmanagedDllName)
185 | {
186 | String libraryPath = resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
187 |
188 | if (libraryPath != null)
189 | {
190 | return LoadUnmanagedDllFromPath(libraryPath);
191 | }
192 |
193 | return IntPtr.Zero;
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/AudioPlugSharp/build/AudioPlugSharp.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PreserveNewest
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/Asio/ASIOStructures.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.InteropServices;
3 |
4 | namespace AudioPlugSharp.Asio
5 | {
6 | // -------------------------------------------------------------------------------
7 | // Structures used by the ASIODriver and ASIODriverExt
8 | // -------------------------------------------------------------------------------
9 |
10 | // -------------------------------------------------------------------------------
11 | // Error and Exceptions
12 | // -------------------------------------------------------------------------------
13 | internal enum ASIOError
14 | {
15 | ASE_OK = 0, // This value will be returned whenever the call succeeded
16 | ASE_SUCCESS = 0x3f4847a0, // unique success return value for ASIOFuture calls
17 | ASE_NotPresent = -1000, // hardware input or output is not present or available
18 | ASE_HWMalfunction, // hardware is malfunctioning (can be returned by any ASIO function)
19 | ASE_InvalidParameter, // input parameter invalid
20 | ASE_InvalidMode, // hardware is in a bad mode or used in a bad mode
21 | ASE_SPNotAdvancing, // hardware is not running when sample position is inquired
22 | ASE_NoClock, // sample clock or rate cannot be determined or is not present
23 | ASE_NoMemory // not enough memory for completing the request
24 | }
25 |
26 | ///
27 | /// ASIO common Exception.
28 | ///
29 | internal class ASIOException : Exception
30 | {
31 | private ASIOError error;
32 |
33 | public ASIOException()
34 | {
35 | }
36 |
37 | public ASIOException(string message)
38 | : base(message)
39 | {
40 | }
41 |
42 | public ASIOException(string message, Exception innerException)
43 | : base(message, innerException)
44 | {
45 | }
46 |
47 | public ASIOError Error
48 | {
49 | get { return error; }
50 | set
51 | {
52 | error = value;
53 | Data["ASIOError"] = error;
54 | }
55 | }
56 |
57 | ///
58 | /// Gets the name of the error.
59 | ///
60 | /// The error.
61 | /// the name of the error
62 | static public String getErrorName(ASIOError error)
63 | {
64 | return Enum.GetName(typeof(ASIOError), error);
65 | }
66 | }
67 |
68 | // -------------------------------------------------------------------------------
69 | // Channel Info, Buffer Info
70 | // -------------------------------------------------------------------------------
71 |
72 |
73 |
74 | [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
75 | internal struct ASIOChannelInfo
76 | {
77 | public int channel; // on input, channel index
78 | public bool isInput; // on input
79 | public bool isActive; // on exit
80 | public int channelGroup; // dto
81 | [MarshalAs(UnmanagedType.U4)]
82 | public AsioSampleType type; // dto
83 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
84 | public String name; // dto
85 | };
86 |
87 | [StructLayout(LayoutKind.Sequential, Pack = 4)]
88 | internal struct ASIOBufferInfo
89 | {
90 | public bool isInput; // on input: ASIOTrue: input, else output
91 | public int channelNum; // on input: channel index
92 | public IntPtr pBuffer0; // on output: double buffer addresses
93 | public IntPtr pBuffer1; // on output: double buffer addresses
94 |
95 | public IntPtr Buffer(int bufferIndex)
96 | {
97 | return (bufferIndex == 0) ? pBuffer0 : pBuffer1;
98 | }
99 |
100 | }
101 |
102 | internal enum ASIOMessageSelector
103 | {
104 | kAsioSelectorSupported = 1, // selector in , returns 1L if supported,
105 | kAsioEngineVersion, // returns engine (host) asio implementation version,
106 | kAsioResetRequest, // request driver reset. if accepted, this
107 | kAsioBufferSizeChange, // not yet supported, will currently always return 0L.
108 | kAsioResyncRequest, // the driver went out of sync, such that
109 | kAsioLatenciesChanged, // the drivers latencies have changed. The engine
110 | kAsioSupportsTimeInfo, // if host returns true here, it will expect the
111 | kAsioSupportsTimeCode, //
112 | kAsioMMCCommand, // unused - value: number of commands, message points to mmc commands
113 | kAsioSupportsInputMonitor, // kAsioSupportsXXX return 1 if host supports this
114 | kAsioSupportsInputGain, // unused and undefined
115 | kAsioSupportsInputMeter, // unused and undefined
116 | kAsioSupportsOutputGain, // unused and undefined
117 | kAsioSupportsOutputMeter, // unused and undefined
118 | kAsioOverload, // driver detected an overload
119 | }
120 |
121 | // -------------------------------------------------------------------------------
122 | // Time structures
123 | // -------------------------------------------------------------------------------
124 |
125 | [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
126 | internal struct ASIOTimeCode
127 | {
128 | public double speed; // speed relation (fraction of nominal speed)
129 | // ASIOSamples timeCodeSamples; // time in samples
130 | public ASIO64Bit timeCodeSamples; // time in samples
131 | public ASIOTimeCodeFlags flags; // some information flags (see below)
132 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
133 | public string future;
134 | }
135 |
136 | [StructLayout(LayoutKind.Sequential, Pack = 4)]
137 | internal struct ASIO64Bit
138 | {
139 | public uint hi;
140 | public uint lo;
141 | // TODO: IMPLEMENT AN EASY WAY TO CONVERT THIS TO double AND long
142 | };
143 |
144 | [Flags]
145 | internal enum ASIOTimeCodeFlags
146 | {
147 | kTcValid = 1,
148 | kTcRunning = 1 << 1,
149 | kTcReverse = 1 << 2,
150 | kTcOnspeed = 1 << 3,
151 | kTcStill = 1 << 4,
152 | kTcSpeedValid = 1 << 8
153 | };
154 |
155 | [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
156 | internal struct AsioTimeInfo
157 | {
158 | public double speed; // absolute speed (1. = nominal)
159 | public ASIO64Bit systemTime; // system time related to samplePosition, in nanoseconds
160 | public ASIO64Bit samplePosition;
161 | public double sampleRate; // current rate
162 | public AsioTimeInfoFlags flags; // (see below)
163 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)]
164 | public string reserved;
165 | }
166 |
167 | [Flags]
168 | internal enum AsioTimeInfoFlags
169 | {
170 | kSystemTimeValid = 1, // must always be valid
171 | kSamplePositionValid = 1 << 1, // must always be valid
172 | kSampleRateValid = 1 << 2,
173 | kSpeedValid = 1 << 3,
174 | kSampleRateChanged = 1 << 4,
175 | kClockSourceChanged = 1 << 5
176 | }
177 |
178 | [StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
179 | internal struct ASIOTime
180 | { // both input/output
181 | public int reserved1;
182 | public int reserved2;
183 | public int reserved3;
184 | public int reserved4;
185 | public AsioTimeInfo timeInfo; // required
186 | public ASIOTimeCode timeCode; // optional, evaluated if (timeCode.flags & kTcValid)
187 | }
188 |
189 | // -------------------------------------------------------------------------------
190 | // Callbacks
191 | // -------------------------------------------------------------------------------
192 |
193 | [StructLayout(LayoutKind.Sequential, Pack = 4)]
194 | internal struct ASIOCallbacks
195 | {
196 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
197 | internal delegate void ASIOBufferSwitchCallBack(int doubleBufferIndex, bool directProcess);
198 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
199 | internal delegate void ASIOSampleRateDidChangeCallBack(double sRate);
200 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
201 | internal delegate int ASIOAsioMessageCallBack(ASIOMessageSelector selector, int value, IntPtr message, IntPtr opt);
202 | // return ASIOTime*
203 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
204 | internal delegate IntPtr ASIOBufferSwitchTimeInfoCallBack(IntPtr asioTimeParam, int doubleBufferIndex, bool directProcess);
205 | // internal delegate IntPtr ASIOBufferSwitchTimeInfoCallBack(ref ASIOTime asioTimeParam, int doubleBufferIndex, bool directProcess);
206 |
207 | // void (*bufferSwitch) (long doubleBufferIndex, ASIOBool directProcess);
208 | public ASIOBufferSwitchCallBack pbufferSwitch;
209 | // void (*sampleRateDidChange) (ASIOSampleRate sRate);
210 | public ASIOSampleRateDidChangeCallBack psampleRateDidChange;
211 | // long (*asioMessage) (long selector, long value, void* message, double* opt);
212 | public ASIOAsioMessageCallBack pasioMessage;
213 | // ASIOTime* (*bufferSwitchTimeInfo) (ASIOTime* params, long doubleBufferIndex, ASIOBool directProcess);
214 | public ASIOBufferSwitchTimeInfoCallBack pbufferSwitchTimeInfo;
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/Asio/AsioDriver.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading;
4 | using Microsoft.Win32;
5 |
6 | namespace AudioPlugSharp.Asio
7 | {
8 | public class AsioDriverEntry
9 | {
10 | private string name;
11 | private string description;
12 | private Guid clsID;
13 |
14 | public string Name
15 | {
16 | get { return name; }
17 | set { name = value; }
18 | }
19 |
20 | public string Description
21 | {
22 | get { return description; }
23 | set { description = value; }
24 | }
25 |
26 | public Guid ClsID
27 | {
28 | get { return clsID; }
29 | set { clsID = value; }
30 | }
31 |
32 | public AsioDriverEntry(string name, string description, Guid clsID)
33 | {
34 | this.name = name;
35 | this.description = description;
36 | this.clsID = clsID;
37 | }
38 | }
39 |
40 | public unsafe class AsioDriver : IDisposable
41 | {
42 | bool isDisposed = false;
43 |
44 | AsioInterop driver = null;
45 | ASIOCallbacks callbacks;
46 | private AsioDriverCapability capability;
47 | private ASIOBufferInfo[] bufferInfos;
48 | private bool isOutputReadySupported;
49 | int bufferBits;
50 | int bufferSize;
51 | IntPtr[] inputBufferPtrs;
52 | IntPtr[] outputBufferPtrs;
53 |
54 | public double SampleRate
55 | {
56 | get
57 | {
58 | return capability.SampleRate;
59 | }
60 | }
61 |
62 | public string Name { get; set; }
63 | public bool Muted { get; set; }
64 |
65 | public int NumChannels { get { return capability.NbInputChannels; } }
66 | public int NumInputChannels { get { return capability.NbInputChannels; } }
67 | public int NumOutputChannels { get { return capability.NbOutputChannels; } }
68 |
69 | public Action ProcessAction { get; set; } = null;
70 |
71 | public AsioDriver(string name)
72 | : this(AsioInterop.GetASIODriverByName(name))
73 | {
74 | }
75 |
76 | public AsioDriver(Guid id)
77 | : this(AsioInterop.GetASIODriverByGuid(id))
78 | {
79 | }
80 |
81 | ~AsioDriver()
82 | {
83 | Dispose(false);
84 | }
85 |
86 | private AsioDriver(AsioInterop driver)
87 | {
88 | this.driver = driver;
89 |
90 | Name = driver.getDriverName();
91 |
92 | if (!driver.init(IntPtr.Zero))
93 | {
94 | throw new InvalidOperationException(driver.getErrorMessage());
95 | }
96 |
97 | callbacks = new ASIOCallbacks();
98 | callbacks.pasioMessage = AsioMessageCallBack;
99 | callbacks.pbufferSwitch = BufferSwitchCallBack;
100 | callbacks.pbufferSwitchTimeInfo = BufferSwitchTimeInfoCallBack;
101 | callbacks.psampleRateDidChange = SampleRateDidChangeCallBack;
102 |
103 | int minSize;
104 | int maxSize;
105 | int preferredSize;
106 | int granularity;
107 |
108 | driver.getBufferSize(out minSize, out maxSize, out preferredSize, out granularity);
109 |
110 | bufferSize = preferredSize;
111 |
112 | BuildCapabilities();
113 |
114 | CreateBuffers(bufferSize);
115 | }
116 |
117 | protected void Dispose(bool disposing)
118 | {
119 | if (disposing)
120 | {
121 | }
122 |
123 | if (driver != null)
124 | driver.disposeBuffers();
125 |
126 | isDisposed = true;
127 | }
128 |
129 | public void Dispose()
130 | {
131 | Dispose(true);
132 | GC.SuppressFinalize(this);
133 | }
134 |
135 | public int PreferredBufferSize()
136 | {
137 | return bufferSize;
138 | }
139 |
140 | public static List GetAsioDriverEntries()
141 | {
142 | List entries = new List();
143 |
144 | RegistryKey asioKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\ASIO");
145 |
146 | if (asioKey != null)
147 | {
148 | string[] names = asioKey.GetSubKeyNames();
149 |
150 | foreach (string name in names)
151 | {
152 | RegistryKey driverKey = asioKey.OpenSubKey(name);
153 |
154 | if (driverKey != null)
155 | {
156 | string clsidStr = (string)driverKey.GetValue("CLSID");
157 |
158 | if (clsidStr != null)
159 | {
160 | Guid clsid;
161 |
162 | if (Guid.TryParse(clsidStr, out clsid))
163 | {
164 | entries.Add(new AsioDriverEntry(name, (string)driverKey.GetValue("Description"), clsid));
165 | }
166 | }
167 | }
168 | }
169 | }
170 |
171 | return entries;
172 | }
173 |
174 | public void SetSampleRate(int sampleRate)
175 | {
176 | driver.setSampleRate(sampleRate);
177 | }
178 |
179 | public void ShowControlPanel()
180 | {
181 | driver.controlPanel();
182 | }
183 |
184 | public void Start()
185 | {
186 | Thread.CurrentThread.Priority = ThreadPriority.Highest;
187 |
188 | bufferBits = (capability.OutputChannelInfos[0].type == AsioSampleType.Int16LSB) ? 16 : 32;
189 |
190 | driver.start();
191 | }
192 |
193 | public void Stop()
194 | {
195 | driver.stop();
196 | }
197 |
198 | public void Release()
199 | {
200 | driver.ReleaseComASIODriver();
201 | }
202 |
203 | void BuildCapabilities()
204 | {
205 | capability = new AsioDriverCapability();
206 |
207 | capability.DriverName = driver.getDriverName();
208 |
209 | // Get nb Input/Output channels
210 | driver.getChannels(out capability.NbInputChannels, out capability.NbOutputChannels);
211 |
212 | capability.InputChannelInfos = new ASIOChannelInfo[capability.NbInputChannels];
213 | capability.OutputChannelInfos = new ASIOChannelInfo[capability.NbOutputChannels];
214 |
215 | // Get ChannelInfo for Inputs
216 | for (int i = 0; i < capability.NbInputChannels; i++)
217 | {
218 | capability.InputChannelInfos[i] = driver.getChannelInfo(i, true);
219 | }
220 |
221 | // Get ChannelInfo for Output
222 | for (int i = 0; i < capability.NbOutputChannels; i++)
223 | {
224 | capability.OutputChannelInfos[i] = driver.getChannelInfo(i, false);
225 | }
226 |
227 | // Get the current SampleRate
228 | capability.SampleRate = driver.getSampleRate();
229 |
230 | var error = driver.GetLatencies(out capability.InputLatency, out capability.OutputLatency);
231 | // focusrite scarlett 2i4 returns ASE_NotPresent here
232 |
233 | if (error != ASIOError.ASE_OK && error != ASIOError.ASE_NotPresent)
234 | {
235 | var ex = new ASIOException("ASIOgetLatencies");
236 | ex.Error = error;
237 | throw ex;
238 | }
239 |
240 | // Get BufferSize
241 | driver.getBufferSize(out capability.BufferMinSize, out capability.BufferMaxSize, out capability.BufferPreferredSize, out capability.BufferGranularity);
242 | }
243 |
244 | int CreateBuffers(int bufferSize)
245 | {
246 | bufferInfos = new ASIOBufferInfo[capability.NbInputChannels + capability.NbOutputChannels];
247 |
248 | for (int index = 0; index < capability.NbInputChannels; index++)
249 | {
250 | bufferInfos[index].isInput = true;
251 | bufferInfos[index].channelNum = index;
252 | bufferInfos[index].pBuffer0 = IntPtr.Zero;
253 | bufferInfos[index].pBuffer1 = IntPtr.Zero;
254 | }
255 |
256 | for (int index = 0; index < capability.NbOutputChannels; index++)
257 | {
258 | bufferInfos[index + capability.NbInputChannels].isInput = false;
259 | bufferInfos[index + capability.NbInputChannels].channelNum = index;
260 | bufferInfos[index + capability.NbInputChannels].pBuffer0 = IntPtr.Zero;
261 | bufferInfos[index + capability.NbInputChannels].pBuffer1 = IntPtr.Zero;
262 | }
263 |
264 | fixed (ASIOBufferInfo* infos = &bufferInfos[0])
265 | {
266 | IntPtr pOutputBufferInfos = new IntPtr(infos);
267 |
268 | // Create the ASIO Buffers with the callbacks
269 | driver.createBuffers(pOutputBufferInfos, capability.NbInputChannels + capability.NbOutputChannels, bufferSize, ref callbacks);
270 | }
271 |
272 | inputBufferPtrs = new IntPtr[capability.NbInputChannels];
273 | outputBufferPtrs = new IntPtr[capability.NbOutputChannels];
274 |
275 | // Check if outputReady is supported
276 | isOutputReadySupported = (driver.outputReady() == ASIOError.ASE_OK);
277 |
278 | return bufferSize;
279 | }
280 |
281 | private void BufferSwitchCallBack(int doubleBufferIndex, bool directProcess)
282 | {
283 | for (int index = 0; index < capability.NbInputChannels; index++)
284 | {
285 | inputBufferPtrs[index] = bufferInfos[index].Buffer(doubleBufferIndex);
286 | }
287 |
288 | for (int index = 0; index < capability.NbOutputChannels; index++)
289 | {
290 | outputBufferPtrs[index] = bufferInfos[index + capability.NbInputChannels].Buffer(doubleBufferIndex);
291 | }
292 |
293 | if (ProcessAction != null)
294 | {
295 | ProcessAction(inputBufferPtrs, outputBufferPtrs);
296 | }
297 |
298 | if (isOutputReadySupported)
299 | {
300 | driver.outputReady();
301 | }
302 | }
303 |
304 | private void SampleRateDidChangeCallBack(double sRate)
305 | {
306 | capability.SampleRate = sRate;
307 | }
308 |
309 | private int AsioMessageCallBack(ASIOMessageSelector selector, int value, IntPtr message, IntPtr opt)
310 | {
311 | switch (selector)
312 | {
313 | case ASIOMessageSelector.kAsioSelectorSupported:
314 | ASIOMessageSelector subValue = (ASIOMessageSelector)Enum.ToObject(typeof(ASIOMessageSelector), value);
315 | switch (subValue)
316 | {
317 | case ASIOMessageSelector.kAsioEngineVersion:
318 | return 1;
319 | case ASIOMessageSelector.kAsioResetRequest:
320 | return 0;
321 | case ASIOMessageSelector.kAsioBufferSizeChange:
322 | return 1;
323 | case ASIOMessageSelector.kAsioResyncRequest:
324 | return 0;
325 | case ASIOMessageSelector.kAsioLatenciesChanged:
326 | return 0;
327 | case ASIOMessageSelector.kAsioSupportsTimeInfo:
328 | // return 1; DON'T SUPPORT FOR NOW. NEED MORE TESTING.
329 | return 0;
330 | case ASIOMessageSelector.kAsioSupportsTimeCode:
331 | // return 1; DON'T SUPPORT FOR NOW. NEED MORE TESTING.
332 | return 0;
333 | }
334 | break;
335 | case ASIOMessageSelector.kAsioEngineVersion:
336 | return 2;
337 | case ASIOMessageSelector.kAsioResetRequest:
338 | //NeedReset = true;
339 | return 1;
340 | case ASIOMessageSelector.kAsioBufferSizeChange:
341 | return 0;
342 | case ASIOMessageSelector.kAsioResyncRequest:
343 | return 0;
344 | case ASIOMessageSelector.kAsioLatenciesChanged:
345 | return 0;
346 | case ASIOMessageSelector.kAsioSupportsTimeInfo:
347 | return 0;
348 | case ASIOMessageSelector.kAsioSupportsTimeCode:
349 | return 0;
350 | }
351 |
352 | return 0;
353 | }
354 |
355 | private IntPtr BufferSwitchTimeInfoCallBack(IntPtr asioTimeParam, int doubleBufferIndex, bool directProcess)
356 | {
357 | return IntPtr.Zero;
358 | }
359 | }
360 | }
361 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/Asio/AsioDriverCapability.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace AudioPlugSharp.Asio
6 | {
7 | ///
8 | /// ASIODriverCapability holds all the information from the ASIODriver.
9 | /// Use ASIODriverExt to get the Capabilities
10 | ///
11 | internal class AsioDriverCapability
12 | {
13 | public String DriverName;
14 |
15 | public int NbInputChannels;
16 | public int NbOutputChannels;
17 |
18 | public int InputLatency;
19 | public int OutputLatency;
20 |
21 | public int BufferMinSize;
22 | public int BufferMaxSize;
23 | public int BufferPreferredSize;
24 | public int BufferGranularity;
25 |
26 | public double SampleRate;
27 |
28 | public ASIOChannelInfo[] InputChannelInfos;
29 | public ASIOChannelInfo[] OutputChannelInfos;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/Asio/AsioSampleType.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace AudioPlugSharp.Asio
6 | {
7 | ///
8 | /// ASIO Sample Type
9 | ///
10 | public enum AsioSampleType
11 | {
12 | ///
13 | /// Int 16 MSB
14 | ///
15 | Int16MSB = 0,
16 | ///
17 | /// Int 24 MSB (used for 20 bits as well)
18 | ///
19 | Int24MSB = 1,
20 | ///
21 | /// Int 32 MSB
22 | ///
23 | Int32MSB = 2,
24 | ///
25 | /// IEEE 754 32 bit float
26 | ///
27 | Float32MSB = 3,
28 | ///
29 | /// IEEE 754 64 bit double float
30 | ///
31 | Float64MSB = 4,
32 | ///
33 | /// 32 bit data with 16 bit alignment
34 | ///
35 | Int32MSB16 = 8,
36 | ///
37 | /// 32 bit data with 18 bit alignment
38 | ///
39 | Int32MSB18 = 9, //
40 | ///
41 | /// 32 bit data with 20 bit alignment
42 | ///
43 | Int32MSB20 = 10,
44 | ///
45 | /// 32 bit data with 24 bit alignment
46 | ///
47 | Int32MSB24 = 11,
48 | ///
49 | /// Int 16 LSB
50 | ///
51 | Int16LSB = 16,
52 | ///
53 | /// Int 24 LSB
54 | /// used for 20 bits as well
55 | ///
56 | Int24LSB = 17,
57 | ///
58 | /// Int 32 LSB
59 | ///
60 | Int32LSB = 18,
61 | ///
62 | /// IEEE 754 32 bit float, as found on Intel x86 architecture
63 | ///
64 | Float32LSB = 19,
65 | ///
66 | /// IEEE 754 64 bit double float, as found on Intel x86 architecture
67 | ///
68 | Float64LSB = 20,
69 | ///
70 | /// 32 bit data with 16 bit alignment
71 | ///
72 | Int32LSB16 = 24,
73 | ///
74 | /// 32 bit data with 18 bit alignment
75 | ///
76 | Int32LSB18 = 25,
77 | ///
78 | /// 32 bit data with 20 bit alignment
79 | ///
80 | Int32LSB20 = 26,
81 | ///
82 | /// 32 bit data with 24 bit alignment
83 | ///
84 | Int32LSB24 = 27,
85 | ///
86 | /// DSD 1 bit data, 8 samples per byte. First sample in Least significant bit.
87 | ///
88 | DSDInt8LSB1 = 32,
89 | ///
90 | /// DSD 1 bit data, 8 samples per byte. First sample in Most significant bit.
91 | ///
92 | DSDInt8MSB1 = 33,
93 | ///
94 | /// DSD 8 bit data, 1 sample per byte. No Endianness required.
95 | ///
96 | DSDInt8NER8 = 40,
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/Asio/README.md:
--------------------------------------------------------------------------------
1 | Asio driver code is based on the implementation in NAudio:
2 |
3 | https://github.com/naudio/NAudio
4 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/AudioPlugSharpHost.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing.Design;
3 | using System.Xml.Serialization;
4 | using AudioPlugSharp;
5 | using AudioPlugSharp.Asio;
6 |
7 | namespace AudioPlugSharpHost
8 | {
9 | public class HostSettings
10 | {
11 | public string AsioDeviceName { get; set; }
12 | }
13 |
14 | public class AudioPlugSharpHost : IAudioHost where T : IAudioPlugin, IAudioPluginProcessor, IAudioPluginEditor
15 | {
16 | public T Plugin { get; private set; }
17 |
18 | public HostSettings HostSettings { get; private set; } = new HostSettings();
19 | public double SampleRate { get; private set; }
20 | public uint MaxAudioBufferSize { get; private set; }
21 | public uint CurrentAudioBufferSize { get; private set; }
22 | public EAudioBitsPerSample BitsPerSample { get; private set; }
23 | public double BPM { get; private set; }
24 | public long CurrentProjectSample { get; private set; }
25 | public bool IsPlaying { get; private set; }
26 |
27 | public AsioDriver AsioDriver { get; private set; } = null;
28 |
29 | string saveFolder;
30 | string hostSettingsFile;
31 |
32 | public AudioPlugSharpHost(T plugin)
33 | {
34 | this.Plugin = plugin;
35 |
36 | (plugin as IAudioPlugin).Host = this;
37 |
38 | plugin.Initialize();
39 |
40 | foreach (AudioIOPort input in plugin.InputPorts)
41 | {
42 | input.ForceBackingBuffer = true;
43 | }
44 |
45 | foreach (AudioIOPort output in plugin.OutputPorts)
46 | {
47 | output.ForceBackingBuffer = true;
48 | }
49 |
50 | saveFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Plugin.PluginName);
51 |
52 | hostSettingsFile = Path.Combine(saveFolder, "HostSettings.xml");
53 |
54 | if (File.Exists(hostSettingsFile))
55 | {
56 | try
57 | {
58 | XmlSerializer serializer = new XmlSerializer(typeof(HostSettings));
59 |
60 | using (Stream inputStream = File.OpenRead(hostSettingsFile))
61 | {
62 | HostSettings = serializer.Deserialize(inputStream) as HostSettings;
63 | }
64 |
65 | using (Stream saveStream = File.OpenRead(Path.Combine(saveFolder, "SaveData")))
66 | {
67 | byte[] data = new byte[saveStream.Length];
68 |
69 | saveStream.Read(data);
70 |
71 | plugin.RestoreState(data);
72 | }
73 | }
74 | catch (Exception ex)
75 | {
76 | Logger.Log("AudioPlugSharpHost - error loading HostSettings.xml: " + ex.ToString());
77 | }
78 | }
79 |
80 | if (!String.IsNullOrEmpty(HostSettings.AsioDeviceName))
81 | {
82 | SetAsioDriver(HostSettings.AsioDeviceName);
83 | }
84 | }
85 |
86 | public void Exit()
87 | {
88 | byte[] data = Plugin.SaveState();
89 |
90 | try
91 | {
92 | if (!Directory.Exists(saveFolder))
93 | {
94 | Directory.CreateDirectory(saveFolder);
95 | }
96 |
97 | XmlSerializer serializer = new XmlSerializer(typeof(HostSettings));
98 |
99 | using (Stream hostStream = File.Create(hostSettingsFile))
100 | {
101 | serializer.Serialize(hostStream, HostSettings);
102 | }
103 |
104 | using (Stream saveStream = File.Create(Path.Combine(saveFolder, "SaveData")))
105 | {
106 | saveStream.Write(data);
107 | }
108 | }
109 | catch (Exception ex)
110 | {
111 |
112 | }
113 |
114 | Plugin.HideEditor();
115 |
116 | if (AsioDriver != null)
117 | {
118 | AsioDriver.Stop();
119 | AsioDriver.Release();
120 | }
121 | }
122 |
123 | public void SetAsioDriver(string asioDeviceName)
124 | {
125 | HostSettings.AsioDeviceName = asioDeviceName;
126 |
127 | try
128 | {
129 | if (AsioDriver != null)
130 | {
131 | AsioDriver.Stop();
132 | }
133 |
134 | this.AsioDriver = new AsioDriver(asioDeviceName);
135 |
136 | AsioDriver.ProcessAction = AsioProcess;
137 |
138 | SampleRate = AsioDriver.SampleRate;
139 | MaxAudioBufferSize = CurrentAudioBufferSize = (uint)AsioDriver.PreferredBufferSize();
140 | BitsPerSample = EAudioBitsPerSample.Bits32;
141 |
142 | Plugin.InitializeProcessing();
143 |
144 | Plugin.SetMaxAudioBufferSize(MaxAudioBufferSize, BitsPerSample);
145 |
146 | AsioDriver.Start();
147 | }
148 | catch (Exception ex)
149 | {
150 | Logger.Log("AudioPlugSharpHost - error initializing Asio device [" + asioDeviceName + "]: " + ex.ToString());
151 |
152 | HostSettings.AsioDeviceName = null;
153 | }
154 | }
155 |
156 | unsafe void AsioProcess(IntPtr[] inputBuffers, IntPtr[] outputBuffers)
157 | {
158 | int inputCount = 0;
159 |
160 | for (int input = 0; input < Plugin.InputPorts.Length; input++)
161 | {
162 | AudioIOPort port = Plugin.InputPorts[input];
163 |
164 | for (int channel = 0; channel < port.NumChannels; channel++)
165 | {
166 | port.SetCurrentBufferSize(CurrentAudioBufferSize);
167 |
168 | int* asioPtr = (int*)inputBuffers[inputCount % AsioDriver.NumInputChannels]; // recyle inputs if we don't have enough
169 |
170 | port.CopyFrom(new ReadOnlySpan((void*)asioPtr, (int)CurrentAudioBufferSize), channel);
171 |
172 | inputCount++;
173 | }
174 | }
175 |
176 | foreach (AudioIOPort port in Plugin.OutputPorts)
177 | {
178 | for (int output = 0; output < Plugin.OutputPorts.Length; output++)
179 | port.SetCurrentBufferSize(CurrentAudioBufferSize);
180 | }
181 |
182 | Plugin.PreProcess();
183 | Plugin.Process();
184 | Plugin.PostProcess();
185 |
186 | int outputCount = 0;
187 |
188 | while (outputCount < AsioDriver.NumOutputChannels)
189 | {
190 | for (int output = 0; output < Plugin.OutputPorts.Length; output++)
191 | {
192 | if (outputCount >= AsioDriver.NumOutputChannels)
193 | break;
194 |
195 | AudioIOPort port = Plugin.OutputPorts[output];
196 |
197 | for (int channel = 0; channel < port.NumChannels; channel++)
198 | {
199 | if (outputCount >= AsioDriver.NumOutputChannels)
200 | break;
201 |
202 | int* asioPtr = (int*)outputBuffers[outputCount];
203 |
204 | port.CopyTo(new Span((void*)asioPtr, (int)CurrentAudioBufferSize), channel);
205 |
206 | outputCount++;
207 | }
208 | }
209 | }
210 | }
211 |
212 | public void BeginEdit(int parameter)
213 | {
214 | }
215 |
216 | public void EndEdit(int parameter)
217 | {
218 | }
219 |
220 | public void PerformEdit(int parameter, double normalizedValue)
221 | {
222 | }
223 |
224 | public void ProcessAllEvents()
225 | {
226 | }
227 |
228 | public int ProcessEvents()
229 | {
230 | return (int)CurrentAudioBufferSize;
231 | }
232 |
233 | public void SendCC(int channel, int ccNumber, int ccValue, int sampleOffset)
234 | {
235 | }
236 |
237 | public void SendNoteOff(int channel, int noteNumber, float velocity, int sampleOffset)
238 | {
239 | }
240 |
241 | public void SendNoteOn(int channel, int noteNumber, float velocity, int sampleOffset)
242 | {
243 | }
244 |
245 | public void SendPolyPressure(int channel, int noteNumber, float pressure, int sampleOffset)
246 | {
247 | }
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/AudioPlugSharpHost.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0-windows
5 | enable
6 | enable
7 | True
8 | true
9 | 0.6.11
10 | Mike Oliphant
11 | Stand-alone ASIO host for AudioPlugSharp plugins.
12 | MIT
13 | true
14 | true
15 | snupkg
16 | true
17 | true
18 | README.md
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/AudioSettingsForm.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace AudioPlugSharpHost
2 | {
3 | partial class AudioSettingsForm
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | deviceCombo = new ComboBox();
32 | label1 = new Label();
33 | applyButton = new Button();
34 | cancelButton = new Button();
35 | SuspendLayout();
36 | //
37 | // deviceCombo
38 | //
39 | deviceCombo.FormattingEnabled = true;
40 | deviceCombo.Location = new Point(86, 12);
41 | deviceCombo.Name = "deviceCombo";
42 | deviceCombo.Size = new Size(163, 23);
43 | deviceCombo.TabIndex = 0;
44 | //
45 | // label1
46 | //
47 | label1.AutoSize = true;
48 | label1.Location = new Point(12, 15);
49 | label1.Name = "label1";
50 | label1.Size = new Size(68, 15);
51 | label1.TabIndex = 1;
52 | label1.Text = "Asio Device";
53 | //
54 | // applyButton
55 | //
56 | applyButton.Location = new Point(86, 85);
57 | applyButton.Name = "applyButton";
58 | applyButton.Size = new Size(75, 23);
59 | applyButton.TabIndex = 2;
60 | applyButton.Text = "Apply";
61 | applyButton.UseVisualStyleBackColor = true;
62 | applyButton.Click += applyButton_Click;
63 | //
64 | // cancelButton
65 | //
66 | cancelButton.Location = new Point(174, 85);
67 | cancelButton.Name = "cancelButton";
68 | cancelButton.Size = new Size(75, 23);
69 | cancelButton.TabIndex = 3;
70 | cancelButton.Text = "Cancel";
71 | cancelButton.UseVisualStyleBackColor = true;
72 | cancelButton.Click += cancelButton_Click;
73 | //
74 | // AudioSettingsForm
75 | //
76 | AutoScaleDimensions = new SizeF(7F, 15F);
77 | AutoScaleMode = AutoScaleMode.Font;
78 | ClientSize = new Size(261, 120);
79 | Controls.Add(cancelButton);
80 | Controls.Add(applyButton);
81 | Controls.Add(label1);
82 | Controls.Add(deviceCombo);
83 | Name = "AudioSettingsForm";
84 | Text = "Audio Settings";
85 | ResumeLayout(false);
86 | PerformLayout();
87 | }
88 |
89 | #endregion
90 |
91 | private ComboBox deviceCombo;
92 | private Label label1;
93 | private Button applyButton;
94 | private Button cancelButton;
95 | }
96 | }
--------------------------------------------------------------------------------
/AudioPlugSharpHost/AudioSettingsForm.cs:
--------------------------------------------------------------------------------
1 | using AudioPlugSharp.Asio;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Data;
6 | using System.Drawing;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using System.Windows.Forms;
11 |
12 | namespace AudioPlugSharpHost
13 | {
14 | public partial class AudioSettingsForm : Form
15 | {
16 | public string AsioDeviceName { get; set; }
17 |
18 | public AudioSettingsForm()
19 | {
20 | InitializeComponent();
21 |
22 | FormBorderStyle = FormBorderStyle.FixedDialog;
23 |
24 | foreach (var entry in AsioDriver.GetAsioDriverEntries())
25 | {
26 | deviceCombo.Items.Add(entry.Name);
27 | }
28 |
29 | deviceCombo.SelectedItem = AsioDeviceName;
30 |
31 | DialogResult = DialogResult.Cancel;
32 | }
33 |
34 | private void applyButton_Click(object sender, EventArgs e)
35 | {
36 | this.AsioDeviceName = deviceCombo.SelectedItem.ToString();
37 |
38 | DialogResult = DialogResult.OK;
39 |
40 | Close();
41 | }
42 |
43 | private void cancelButton_Click(object sender, EventArgs e)
44 | {
45 | DialogResult = DialogResult.Cancel;
46 |
47 | Close();
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/AudioSettingsForm.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/README.md:
--------------------------------------------------------------------------------
1 | AudioPlugSharpHost is a small library to make it easy to host an AudioPlugSharp plugin in a standalone Windows application.
2 |
3 | Have a look here for an example of it being used:
4 |
5 | https://github.com/mikeoliphant/ChartPlayer
6 |
--------------------------------------------------------------------------------
/AudioPlugSharpHost/WindowsFormsHost.cs:
--------------------------------------------------------------------------------
1 | using AudioPlugSharp;
2 | using System.Reflection;
3 |
4 | namespace AudioPlugSharpHost
5 | {
6 | public class WindowsFormsHost where T : IAudioPlugin, IAudioPluginProcessor, IAudioPluginEditor
7 | {
8 | public T Plugin { get; private set; }
9 |
10 | AudioSettingsForm settingsForm;
11 | AudioPlugSharpHost audioHost;
12 | NotifyIcon notifyIcon;
13 |
14 | public WindowsFormsHost(T plugin)
15 | {
16 | this.Plugin = plugin;
17 |
18 | notifyIcon = new NotifyIcon();
19 | notifyIcon.Icon = Icon.ExtractAssociatedIcon(Assembly.GetEntryAssembly().Location);
20 | notifyIcon.Text = plugin.PluginName;
21 | notifyIcon.Click += NotifyIcon_Click;
22 |
23 | settingsForm = new AudioSettingsForm();
24 |
25 | audioHost = new AudioPlugSharpHost(Plugin);
26 | }
27 |
28 | public void Run()
29 | {
30 | notifyIcon.Visible = true;
31 |
32 | if (audioHost.AsioDriver == null)
33 | ShowAudioSettings();
34 |
35 | audioHost.Plugin.ShowEditor(IntPtr.Zero);
36 | audioHost.Exit();
37 |
38 | notifyIcon.Visible = false;
39 | }
40 |
41 | void ShowAudioSettings()
42 | {
43 | if (!settingsForm.Visible)
44 | {
45 | if (settingsForm.ShowDialog() == DialogResult.OK)
46 | {
47 | audioHost.SetAsioDriver(settingsForm.AsioDeviceName);
48 | }
49 | }
50 | }
51 |
52 | void NotifyIcon_Click(object sender, EventArgs e)
53 | {
54 | ShowAudioSettings();
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/AudioPlugSharpJack/AudioPlugSharpJack.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 | 0.6.15
8 | Mike Oliphant
9 | Jack Audio host for AudioPlugSharp plugins.
10 | MIT
11 | true
12 | true
13 | snupkg
14 | true
15 | true
16 | README.md
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/AudioPlugSharpJack/JackHost.cs:
--------------------------------------------------------------------------------
1 | using AudioPlugSharp;
2 | using JackSharp;
3 | using JackSharp.Processing;
4 |
5 | namespace AudioPlugSharpJack
6 | {
7 | public class JackHost : IAudioHost where T : IAudioPlugin, IAudioPluginProcessor, IAudioPluginEditor
8 | {
9 | public T Plugin { get; private set; }
10 |
11 | public double SampleRate { get; private set; }
12 | public uint MaxAudioBufferSize { get; private set; }
13 | public uint CurrentAudioBufferSize { get; private set; }
14 | public EAudioBitsPerSample BitsPerSample { get; private set; }
15 | public double BPM { get; private set; }
16 | public long CurrentProjectSample { get; private set; }
17 | public bool IsPlaying { get; private set; }
18 |
19 | string saveFolder;
20 | Processor jackProcessor;
21 |
22 | public JackHost(T plugin)
23 | {
24 | this.Plugin = plugin;
25 |
26 | (plugin as IAudioPlugin).Host = this;
27 |
28 | plugin.Initialize();
29 |
30 | foreach (AudioIOPort input in plugin.InputPorts)
31 | {
32 | input.ForceBackingBuffer = true;
33 | }
34 |
35 | foreach (AudioIOPort output in plugin.OutputPorts)
36 | {
37 | output.ForceBackingBuffer = true;
38 | }
39 |
40 | saveFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), Plugin.PluginName);
41 |
42 | try
43 | {
44 | using (Stream saveStream = File.OpenRead(Path.Combine(saveFolder, "SaveData")))
45 | {
46 | byte[] data = new byte[saveStream.Length];
47 |
48 | saveStream.Read(data);
49 |
50 | plugin.RestoreState(data);
51 | }
52 | }
53 | catch (Exception ex)
54 | {
55 | Logger.Log("Error loading save data: " + ex.ToString());
56 | }
57 | }
58 |
59 | public void Exit()
60 | {
61 | byte[] data = Plugin.SaveState();
62 |
63 | try
64 | {
65 | if (!Directory.Exists(saveFolder))
66 | {
67 | Directory.CreateDirectory(saveFolder);
68 | }
69 |
70 | using (Stream saveStream = File.Create(Path.Combine(saveFolder, "SaveData")))
71 | {
72 | saveStream.Write(data);
73 | }
74 | }
75 | catch (Exception ex)
76 | {
77 |
78 | }
79 |
80 | Plugin.HideEditor();
81 |
82 | jackProcessor.Stop();
83 | }
84 |
85 | public void Run()
86 | {
87 | MaxAudioBufferSize = 512;
88 | BitsPerSample = EAudioBitsPerSample.Bits32;
89 |
90 | jackProcessor = new(Plugin.PluginName, 1, 2, 0, 0, autoconnect: true);
91 |
92 | jackProcessor.Start();
93 |
94 | SampleRate = jackProcessor.SampleRate;
95 |
96 | Plugin.InitializeProcessing();
97 |
98 | Plugin.SetMaxAudioBufferSize(MaxAudioBufferSize, BitsPerSample);
99 |
100 | jackProcessor.ProcessFunc = Process;
101 |
102 | if (Plugin.HasUserInterface)
103 | {
104 | Plugin.ShowEditor(IntPtr.Zero);
105 |
106 | Exit();
107 | }
108 | else
109 | {
110 | Thread.Sleep(Timeout.Infinite);
111 | }
112 | }
113 |
114 | void Process(ProcessBuffer buffer)
115 | {
116 | CurrentAudioBufferSize = (uint)jackProcessor.BufferSize;
117 |
118 | AudioIOPort input = Plugin.InputPorts[0];
119 | input.SetCurrentBufferSize((uint)buffer.Frames);
120 |
121 | AudioIOPort output = Plugin.OutputPorts[0];
122 | output.SetCurrentBufferSize((uint)buffer.Frames);
123 |
124 | float[] jackIn = buffer.AudioIn[0].Audio;
125 |
126 | input.CopyFrom(jackIn, 0);
127 |
128 | Plugin.PreProcess();
129 | Plugin.Process();
130 | Plugin.PostProcess();
131 |
132 | for (int channel = 0; channel < output.NumChannels; channel++)
133 | {
134 | float[] jackOut = buffer.AudioOut[channel].Audio;
135 |
136 | output.CopyTo(jackOut, channel);
137 | }
138 | }
139 |
140 | public void BeginEdit(int parameter)
141 | {
142 | }
143 |
144 | public void EndEdit(int parameter)
145 | {
146 | }
147 |
148 | public void PerformEdit(int parameter, double normalizedValue)
149 | {
150 | }
151 |
152 | public void ProcessAllEvents()
153 | {
154 | }
155 |
156 | public int ProcessEvents()
157 | {
158 | return (int)CurrentAudioBufferSize;
159 | }
160 |
161 | public void SendCC(int channel, int ccNumber, int ccValue, int sampleOffset)
162 | {
163 | }
164 |
165 | public void SendNoteOff(int channel, int noteNumber, float velocity, int sampleOffset)
166 | {
167 | }
168 |
169 | public void SendNoteOn(int channel, int noteNumber, float velocity, int sampleOffset)
170 | {
171 | }
172 |
173 | public void SendPolyPressure(int channel, int noteNumber, float pressure, int sampleOffset)
174 | {
175 | }
176 | }
177 | }
178 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpController.cpp:
--------------------------------------------------------------------------------
1 | #include "pluginterfaces/base/ibstream.h"
2 | #include "base/source/fstreamer.h"
3 |
4 | #include "AudioPlugSharpProcessor.h"
5 | #include "AudioPlugSharpController.h"
6 | #include "AudioPlugSharpFactory.h"
7 |
8 | using namespace System;
9 | using namespace System::Runtime::InteropServices;
10 |
11 | AudioPlugSharpController::AudioPlugSharpController(void)
12 | {
13 | }
14 |
15 | AudioPlugSharpController::~AudioPlugSharpController(void)
16 | {
17 | }
18 |
19 | FUnknown* AudioPlugSharpController::createInstance(void* factory)
20 | {
21 | Logger::Log("Create controller instance");
22 |
23 | AudioPlugSharpController* controller = new AudioPlugSharpController();
24 | controller->plugin = ((AudioPlugSharpFactory*)factory)->GetPlugin();
25 |
26 | return (IAudioProcessor*)controller;
27 | }
28 |
29 | tresult PLUGIN_API AudioPlugSharpController::initialize(FUnknown* context)
30 | {
31 | tresult result = EditController::initialize(context);
32 |
33 | if (result != kResultOk)
34 | {
35 | return result;
36 | }
37 |
38 | try
39 | {
40 | plugin->Editor->InitializeEditor();
41 | }
42 | catch (Exception^ ex)
43 | {
44 | Logger::Log("Unable to initialize managed editor: " + ex->ToString());
45 | }
46 |
47 | uint16 paramID = PLUGIN_PARAMETER_USER_START;
48 |
49 | for each (auto parameter in plugin->Processor->Parameters)
50 | {
51 | Logger::Log("Registering parameter: " + parameter->Name);
52 |
53 | TChar* paramName = (TChar*)(void*)Marshal::StringToHGlobalUni(parameter->Name);
54 |
55 | parameters.addParameter(paramName, nullptr, 0, parameter->GetValueNormalized(parameter->DefaultValue), ParameterInfo::kCanAutomate, paramID);
56 |
57 | Marshal::FreeHGlobal((IntPtr)paramName);
58 |
59 | paramID++;
60 | }
61 |
62 | return result;
63 | }
64 |
65 | tresult PLUGIN_API AudioPlugSharpController::terminate()
66 | {
67 | return EditController::terminate();
68 | }
69 |
70 | tresult PLUGIN_API AudioPlugSharpController::setParamNormalized(ParamID tag, ParamValue value)
71 | {
72 | if (tag < PLUGIN_PARAMETER_USER_START)
73 | {
74 | return kResultFalse;
75 | }
76 |
77 | //Logger::Log("Set param normalized " + tag + " to " + value);
78 |
79 | plugin->Processor->Parameters[tag - PLUGIN_PARAMETER_USER_START]->NormalizedEditValue = value;
80 |
81 | return kResultOk;
82 | }
83 |
84 | ParamValue PLUGIN_API AudioPlugSharpController::getParamNormalized(ParamID tag)
85 | {
86 | if (tag < PLUGIN_PARAMETER_USER_START)
87 | {
88 | return 0;
89 | }
90 |
91 | return plugin->Processor->Parameters[tag - PLUGIN_PARAMETER_USER_START]->NormalizedEditValue;
92 | }
93 |
94 | tresult PLUGIN_API AudioPlugSharpController::getParamStringByValue(ParamID tag, ParamValue valueNormalized, String128 string)
95 | {
96 | if (tag < PLUGIN_PARAMETER_USER_START)
97 | {
98 | return kResultFalse;
99 | }
100 |
101 | TChar* paramStr = (TChar*)(void*)Marshal::StringToHGlobalUni(plugin->Processor->Parameters[tag - PLUGIN_PARAMETER_USER_START]->DisplayValue);
102 |
103 | strcpy16(string, paramStr);
104 |
105 | Marshal::FreeHGlobal((IntPtr)paramStr);
106 |
107 | return kResultOk;
108 | }
109 |
110 | tresult PLUGIN_API AudioPlugSharpController::setComponentState(IBStream* state)
111 | {
112 | Logger::Log("Controller setComponentState");
113 |
114 | if (!state)
115 | return kResultFalse;
116 |
117 | //IBStreamer streamer(state, kLittleEndian);
118 |
119 | //float savedGain = 0.f;
120 | //if (streamer.readFloat(savedGain) == false)
121 | // return kResultFalse;
122 |
123 | //setParamNormalized(kGainId, savedGain);
124 |
125 | return kResultOk;
126 | }
127 |
128 | void AudioPlugSharpController::sendIntMessage(const char* idTag, const Steinberg::int64 value)
129 | {
130 | if (auto* message = allocateMessage())
131 | {
132 | const FReleaser releaser(message);
133 |
134 | message->setMessageID(idTag);
135 | message->getAttributes()->setInt(idTag, value);
136 |
137 | sendMessage(message);
138 | }
139 | }
140 |
141 | tresult PLUGIN_API AudioPlugSharpController::connect(IConnectionPoint* other)
142 | {
143 | tresult result = EditController::connect(other);
144 |
145 | Logger::Log("Connect controller to processor");
146 |
147 | sendIntMessage("AudioPlugSharpControllerPtr", (Steinberg::int64)this);
148 |
149 | return result;
150 | }
151 |
152 | void AudioPlugSharpController::setProcessor(AudioPlugSharpProcessor* processor, IAudioPlugin^ plugin)
153 | {
154 | this->processor = processor;
155 | this->plugin = plugin;
156 | }
157 |
158 |
159 |
160 | IPlugView* PLUGIN_API AudioPlugSharpController::createView(const char* name)
161 | {
162 | Logger::Log("Create Editor View");
163 |
164 | if (!plugin->Editor->HasUserInterface)
165 | {
166 | return nullptr;
167 | }
168 |
169 | editorView = new AudioPlugSharpEditor(this, plugin);
170 |
171 | return editorView;
172 | }
173 |
174 | tresult PLUGIN_API AudioPlugSharpController::getMidiControllerAssignment(int32 busIndex, int16 channel,
175 | CtrlNumber midiControllerNumber,
176 | ParamID& tag /*out*/)
177 | {
178 | if (busIndex == 0)
179 | {
180 | AudioPluginParameter^ parameter = plugin->Processor->GetParameterByMidiController(midiControllerNumber);
181 |
182 | if (parameter != nullptr)
183 | {
184 | for (int i = 0; i < plugin->Processor->Parameters->Count; i++)
185 | {
186 | if (plugin->Processor->Parameters[i] == parameter)
187 | {
188 | tag = PLUGIN_PARAMETER_USER_START + i;
189 |
190 | return kResultTrue;
191 | }
192 | }
193 | }
194 | }
195 |
196 | return kResultFalse;
197 | }
198 |
199 |
200 | tresult PLUGIN_API AudioPlugSharpController::queryInterface(const char* iid, void** obj)
201 | {
202 | QUERY_INTERFACE(iid, obj, IMidiMapping::iid, IMidiMapping)
203 | return EditController::queryInterface(iid, obj);
204 | }
205 |
206 |
207 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpController.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "public.sdk/source/vst/vsteditcontroller.h"
4 |
5 | #include "AudioPlugSharpEditor.h"
6 |
7 | using namespace Steinberg;
8 | using namespace Steinberg::Vst;
9 |
10 | #include
11 | using namespace msclr;
12 |
13 | #define PLUGIN_PARAMETER_USER_START 128
14 |
15 | class AudioPlugSharpProcessor;
16 |
17 | class AudioPlugSharpController : public EditController, public IMidiMapping
18 | {
19 | public:
20 | AudioPlugSharpController(void);
21 |
22 | static FUnknown* createInstance(void* factory);
23 |
24 | tresult PLUGIN_API initialize(FUnknown* context) SMTG_OVERRIDE;
25 | tresult PLUGIN_API terminate() SMTG_OVERRIDE;
26 |
27 | tresult PLUGIN_API setComponentState(IBStream* state) SMTG_OVERRIDE;
28 |
29 | tresult PLUGIN_API getParamStringByValue(ParamID tag, ParamValue valueNormalized, String128 string) SMTG_OVERRIDE;
30 | tresult PLUGIN_API setParamNormalized(ParamID tag, ParamValue value) SMTG_OVERRIDE;
31 | ParamValue PLUGIN_API getParamNormalized(ParamID tag) SMTG_OVERRIDE;
32 |
33 | // Uncomment to add a GUI
34 | IPlugView * PLUGIN_API createView (const char * name) SMTG_OVERRIDE;
35 |
36 | tresult PLUGIN_API getMidiControllerAssignment(int32 busIndex, int16 channel, CtrlNumber midiControllerNumber, ParamID& tag/*out*/) SMTG_OVERRIDE;
37 |
38 | // Uncomment to override default EditController behavior
39 | // tresult PLUGIN_API setState(IBStream* state) SMTG_OVERRIDE;
40 | // tresult PLUGIN_API getState(IBStream* state) SMTG_OVERRIDE;
41 | // tresult PLUGIN_API getParamValueByString(ParamID tag, TChar* string, ParamValue& valueNormalized) SMTG_OVERRIDE;
42 |
43 | tresult PLUGIN_API connect(IConnectionPoint* other) SMTG_OVERRIDE;
44 |
45 | void sendIntMessage(const char* idTag, const Steinberg::int64 value);
46 | void setProcessor(AudioPlugSharpProcessor* processor, IAudioPlugin^ plugin);
47 |
48 | ~AudioPlugSharpController(void);
49 |
50 | DELEGATE_REFCOUNT(EditController)
51 | tresult PLUGIN_API queryInterface(const char* iid, void** obj) SMTG_OVERRIDE;
52 |
53 | private:
54 | AudioPlugSharpProcessor* processor = nullptr;
55 | AudioPlugSharpEditor* editorView = nullptr;
56 | gcroot plugin;
57 | };
58 |
59 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpEditor.cpp:
--------------------------------------------------------------------------------
1 | #include "AudioPlugSharpEditor.h"
2 | #include "AudioPlugSharpProcessor.h"
3 | #include "AudioPlugSharpController.h"
4 | #include "AudioPlugSharpFactory.h"
5 |
6 | using namespace AudioPlugSharp;
7 |
8 | AudioPlugSharpEditor::AudioPlugSharpEditor(AudioPlugSharpController* controller, IAudioPlugin^ plugin)
9 | : EditorView(controller, nullptr)
10 | {
11 | this->controller = controller;
12 | this->plugin = plugin;
13 |
14 | double scale = plugin->Editor->GetDpiScale();
15 |
16 | rect.right = plugin->Editor->EditorWidth * scale;
17 | rect.bottom = plugin->Editor->EditorHeight * scale;
18 | }
19 |
20 | AudioPlugSharpEditor::~AudioPlugSharpEditor(void)
21 | {
22 | }
23 |
24 | tresult PLUGIN_API AudioPlugSharpEditor::isPlatformTypeSupported(FIDString type)
25 | {
26 | Logger::Log("IsPlatformSupported");
27 |
28 | if (strcmp(type, kPlatformTypeHWND) == 0)
29 | {
30 | Logger::Log("HWND supported");
31 |
32 | return kResultTrue;
33 | }
34 |
35 | Logger::Log("Not supported");
36 |
37 | return kResultFalse;
38 | }
39 |
40 | tresult PLUGIN_API AudioPlugSharpEditor::onSize(ViewRect* newSize)
41 | {
42 | if (newSize)
43 | rect = *newSize;
44 |
45 | double scale = plugin->Editor->GetDpiScale();
46 |
47 | plugin->Editor->ResizeEditor(newSize->getWidth() / scale, newSize->getHeight() / scale);
48 |
49 | return kResultTrue;
50 | }
51 |
52 | void AudioPlugSharpEditor::attachedToParent()
53 | {
54 | Logger::Log("Show Editor");
55 |
56 | plugin->Editor->ShowEditor((IntPtr)systemWindow);
57 | }
58 |
59 | void AudioPlugSharpEditor::removedFromParent()
60 | {
61 | Logger::Log("Hide Editor");
62 |
63 | plugin->Editor->HideEditor();
64 | }
65 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpEditor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "public.sdk/source/vst/vsteditcontroller.h"
4 |
5 | class AudioPlugSharpController;
6 |
7 | using namespace System;
8 |
9 | using namespace Steinberg;
10 | using namespace Steinberg::Vst;
11 |
12 | #include
13 | using namespace msclr;
14 |
15 | using namespace AudioPlugSharp;
16 |
17 | public class AudioPlugSharpEditor : public EditorView
18 | {
19 | public:
20 | AudioPlugSharpEditor(AudioPlugSharpController* controller, IAudioPlugin^ plugin);
21 | ~AudioPlugSharpEditor(void);
22 |
23 | tresult PLUGIN_API isPlatformTypeSupported(FIDString type) SMTG_OVERRIDE;
24 | tresult PLUGIN_API canResize() SMTG_OVERRIDE { return kResultTrue; }
25 | tresult PLUGIN_API onSize(ViewRect* newSize) SMTG_OVERRIDE;
26 | void attachedToParent() SMTG_OVERRIDE;
27 | void removedFromParent() SMTG_OVERRIDE;
28 | private:
29 | AudioPlugSharpController* controller = nullptr;
30 | gcroot plugin;
31 | };
32 |
33 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpEntryPoints.cpp:
--------------------------------------------------------------------------------
1 | #include "AudioPlugSharpFactory.h"
2 |
3 | #include "public.sdk/source/main/pluginfactory.h"
4 |
5 | using namespace Steinberg;
6 |
7 | IPluginFactory* PLUGIN_API GetPluginFactory()
8 | {
9 | AudioPlugSharp::Logger::Log("GetPluginFactory");
10 |
11 | //if (!gPluginFactory)
12 | //{
13 | try
14 | {
15 | gPluginFactory = new AudioPlugSharpFactory();
16 | }
17 | catch (Exception^ ex)
18 | {
19 | Logger::Log("Error creating plugin factory: " + ex->ToString());
20 | }
21 | //}
22 |
23 | return gPluginFactory;
24 | }
25 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpFactory.cpp:
--------------------------------------------------------------------------------
1 | #include "AudioPlugSharpFactory.h"
2 |
3 | using namespace System::IO;
4 | using namespace System::Reflection;
5 | using namespace System::Runtime::InteropServices;
6 | using namespace System::Runtime::Loader;
7 | using namespace AudioPlugSharp;
8 |
9 | #include
10 |
11 | using namespace msclr;
12 |
13 | AudioPlugSharpFactory::AudioPlugSharpFactory()
14 | : CPluginFactory(PFactoryInfo())
15 | {
16 | Logger::Log("AudioPlugSharpFactory running under .NET version: " + System::Environment::Version);
17 |
18 | Logger::Log("Load Context: " + AssemblyLoadContext::GetLoadContext(Assembly::GetExecutingAssembly())->ToString());
19 |
20 | System::String^ assemblyName = Path::GetFileNameWithoutExtension(Assembly::GetExecutingAssembly()->Location);
21 |
22 | // Our plugin should be our name but without the 'Bridge' at the end
23 | assemblyName = assemblyName->Substring(0, assemblyName->Length - 6);
24 |
25 | Logger::Log("Plugin assembly name: " + assemblyName);
26 |
27 | plugin = PluginLoader::LoadPluginFromAssembly(assemblyName);
28 |
29 | char* companyChars = (char*)(void*)Marshal::StringToHGlobalAnsi(plugin->Company);
30 | char* websiteChars = (char*)(void*)Marshal::StringToHGlobalAnsi(plugin->Website);
31 | char* contactChars = (char*)(void*)Marshal::StringToHGlobalAnsi(plugin->Contact);
32 | char* pluginNameChars = (char*)(void*)Marshal::StringToHGlobalAnsi(plugin->PluginName);
33 | char* pluginCategoryChars = (char*)(void*)Marshal::StringToHGlobalAnsi(plugin->PluginCategory);
34 | char* pluginVersionChars = (char*)(void*)Marshal::StringToHGlobalAnsi(plugin->PluginVersion);
35 |
36 | AudioPlugSharpProcessorUID = FUID(AUDIO_PLUG_SHARP_ID, AUDIO_PLUG_SHARP_PROCESSOR_ID, plugin->PluginID >> 32, plugin->PluginID & 0x00000000ffffffff);
37 | AudioPlugSharpControllerUID = FUID(AUDIO_PLUG_SHARP_ID, AUDIO_PLUG_SHARP_CONTROLLER_ID, plugin->PluginID >> 32, plugin->PluginID & 0x00000000ffffffff);
38 |
39 | factoryInfo = PFactoryInfo(companyChars, websiteChars, contactChars, Vst::kDefaultFactoryFlags);
40 |
41 | PClassInfo2 componentClass
42 | (
43 | AudioPlugSharpProcessorUID,
44 | PClassInfo::kManyInstances,
45 | kVstAudioEffectClass,
46 | pluginNameChars,
47 | 0,
48 | pluginCategoryChars,
49 | companyChars,
50 | pluginVersionChars,
51 | kVstVersionString
52 | );
53 |
54 | registerClass(&componentClass, AudioPlugSharpProcessor::createInstance, this);
55 |
56 | PClassInfo2 controllerClass
57 | (
58 | AudioPlugSharpControllerUID,
59 | PClassInfo::kManyInstances,
60 | kVstComponentControllerClass,
61 | pluginNameChars,
62 | 0,
63 | "",
64 | companyChars,
65 | pluginVersionChars,
66 | kVstVersionString
67 | );
68 |
69 | Marshal::FreeHGlobal((IntPtr)companyChars);
70 | Marshal::FreeHGlobal((IntPtr)websiteChars);
71 | Marshal::FreeHGlobal((IntPtr)contactChars);
72 | Marshal::FreeHGlobal((IntPtr)pluginNameChars);
73 | Marshal::FreeHGlobal((IntPtr)pluginCategoryChars);
74 | Marshal::FreeHGlobal((IntPtr)pluginVersionChars);
75 |
76 | registerClass(&controllerClass, AudioPlugSharpController::createInstance, this);
77 | }
78 |
79 | gcroot AudioPlugSharpFactory::GetPlugin()
80 | {
81 | if (getCount == 2) // Both a processor and a controller have grabbed the plugin, so this must be a new instance
82 | {
83 | Logger::Log("Creating new plugin instance");
84 |
85 | System::String^ assemblyName = Path::GetFileNameWithoutExtension(Assembly::GetExecutingAssembly()->Location);
86 |
87 | // Our plugin should be our name but without the 'Bridge' at the end
88 | assemblyName = assemblyName->Substring(0, assemblyName->Length - 6);
89 |
90 | plugin = PluginLoader::LoadPluginFromAssembly(assemblyName);
91 |
92 | getCount = 0;
93 | }
94 | else
95 | {
96 | getCount++;
97 | }
98 |
99 | return plugin;
100 | };
101 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpFactory.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AudioPlugSharpProcessor.h"
4 | #include "AudioPlugSharpController.h"
5 |
6 | #include "public.sdk/source/main/pluginfactory.h"
7 |
8 | #include
9 |
10 | #define AUDIO_PLUG_SHARP_ID 0xB0FA8B5C
11 | #define AUDIO_PLUG_SHARP_PROCESSOR_ID 0xC4B5BA02
12 | #define AUDIO_PLUG_SHARP_CONTROLLER_ID 0xD0411FA8
13 |
14 | using namespace msclr;
15 |
16 | class AudioPlugSharpFactory : public CPluginFactory
17 | {
18 | public:
19 | AudioPlugSharpFactory();
20 |
21 | gcroot GetPlugin();
22 |
23 | FUID AudioPlugSharpProcessorUID;
24 | FUID AudioPlugSharpControllerUID;
25 |
26 | protected:
27 | gcroot plugin = nullptr;
28 | int getCount = 0;
29 | };
30 |
31 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpHost.cpp:
--------------------------------------------------------------------------------
1 | #include "AudioPlugSharpHost.h"
2 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpHost.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "AudioPlugSharpController.h"
4 | #include "public.sdk/source/vst/vstaudioprocessoralgo.h"
5 | #include "public.sdk/source/vst/vsteditcontroller.h"
6 | #include "public.sdk/source/vst/vstaudioeffect.h"
7 |
8 | using namespace Steinberg;
9 | using namespace Steinberg::Vst;
10 |
11 | using namespace AudioPlugSharp;
12 |
13 | public ref class AudioPlugSharpHost : public IAudioHost
14 | {
15 | public:
16 | // Inherited via IAudioHost
17 | virtual property double SampleRate;
18 | virtual property unsigned int MaxAudioBufferSize;
19 | virtual property unsigned int CurrentAudioBufferSize;
20 | virtual property AudioPlugSharp::EAudioBitsPerSample BitsPerSample;
21 | virtual property double BPM;
22 | virtual property bool IsPlaying;
23 | virtual property Int64 CurrentProjectSample;
24 |
25 | virtual void AudioPlugSharpHost::ProcessAllEvents()
26 | {
27 | int nextSample = 0;
28 |
29 | do
30 | {
31 | nextSample = ProcessEvents();
32 | }
33 | while (nextSample < processData->numSamples);
34 | }
35 |
36 | virtual int AudioPlugSharpHost::ProcessEvents()
37 | {
38 | int minSample = processData->numSamples;
39 |
40 | // Handle parameter changes
41 | if (processData->inputParameterChanges != nullptr)
42 | {
43 | int32 numParamsChanged = processData->inputParameterChanges->getParameterCount();
44 |
45 | for (int32 i = 0; i < numParamsChanged; i++)
46 | {
47 | IParamValueQueue* paramQueue = processData->inputParameterChanges->getParameterData(i);
48 |
49 | if (paramQueue != nullptr)
50 | {
51 | ParamValue value;
52 | int32 sampleOffset;
53 | int32 numPoints = paramQueue->getPointCount();
54 |
55 | ParamID paramID = paramQueue->getParameterId();
56 |
57 | AudioPlugSharp::AudioPluginParameter^ param = plugin->Processor->Parameters[paramID - PLUGIN_PARAMETER_USER_START];
58 |
59 | for (int32 i = startEvent; i < numPoints; i++)
60 | {
61 | if (paramQueue->getPoint(i, sampleOffset, value) == kResultTrue)
62 | {
63 | if (sampleOffset == 0)
64 | {
65 | plugin->Processor->HandleParameterChange(param, value, sampleOffset);
66 | }
67 | else if (sampleOffset > eventSampleOffset)
68 | {
69 | plugin->Processor->HandleParameterChange(param, value, sampleOffset);
70 | minSample = sampleOffset + 1;
71 |
72 | startEvent = i + 1;
73 |
74 | break;
75 | }
76 | }
77 | }
78 | }
79 | }
80 | }
81 |
82 | // Handle MIDI events
83 | if (processData->inputEvents != nullptr)
84 | {
85 | int32 numEvent = processData->inputEvents->getEventCount();
86 |
87 | for (int32 i = 0; i < numEvent; i++)
88 | {
89 | Event event;
90 |
91 | if (processData->inputEvents->getEvent(i, event) == kResultOk)
92 | {
93 | if (event.sampleOffset == eventSampleOffset)
94 | {
95 | switch (event.type)
96 | {
97 | case Event::kNoteOnEvent:
98 | {
99 | plugin->Processor->HandleNoteOn(event.noteOn.channel, event.noteOn.pitch, event.noteOn.velocity, event.sampleOffset);
100 |
101 | break;
102 | }
103 | case Event::kNoteOffEvent:
104 | {
105 | plugin->Processor->HandleNoteOff(event.noteOff.channel, event.noteOff.pitch, event.noteOff.velocity, event.sampleOffset);
106 |
107 | break;
108 | }
109 | case Event::kPolyPressureEvent:
110 | plugin->Processor->HandlePolyPressure(event.polyPressure.channel, event.polyPressure.pitch, event.polyPressure.pressure, event.sampleOffset);
111 |
112 | break;
113 | }
114 | }
115 | else if (event.sampleOffset > eventSampleOffset)
116 | {
117 | if (event.sampleOffset < minSample)
118 | minSample = event.sampleOffset;
119 |
120 | break;
121 | }
122 | }
123 | }
124 | }
125 |
126 | eventSampleOffset = minSample;
127 |
128 | return eventSampleOffset;
129 | }
130 |
131 | virtual void AudioPlugSharpHost::SendNoteOn(int channel, int noteNumber, float velocity, int sampleOffset)
132 | {
133 | Event event;
134 |
135 | event.type = Event::kNoteOnEvent;
136 | event.sampleOffset = sampleOffset;
137 | event.noteOn.channel = channel;
138 | event.noteOn.pitch = noteNumber;
139 | event.noteOn.velocity = velocity;
140 |
141 | processData->outputEvents->addEvent(event);
142 | }
143 |
144 | virtual void AudioPlugSharpHost::SendNoteOff(int channel, int noteNumber, float velocity, int sampleOffset)
145 | {
146 | Event event;
147 |
148 | event.type = Event::kNoteOffEvent;
149 | event.sampleOffset = sampleOffset;
150 | event.noteOff.channel = channel;
151 | event.noteOff.pitch = noteNumber;
152 | event.noteOff.velocity = velocity;
153 |
154 | processData->outputEvents->addEvent(event);
155 | }
156 |
157 | virtual void AudioPlugSharpHost::SendCC(int channel, int ccNumber, int ccValue, int sampleOffset)
158 | {
159 | Event event;
160 |
161 | event.type = Event::kLegacyMIDICCOutEvent;
162 | event.sampleOffset = sampleOffset;
163 | event.midiCCOut.channel = channel;
164 | event.midiCCOut.controlNumber = ccNumber;
165 | event.midiCCOut.value = ccValue;
166 |
167 | processData->outputEvents->addEvent(event);
168 | }
169 |
170 | virtual void AudioPlugSharpHost::SendPolyPressure(int channel, int noteNumber, float pressure, int sampleOffset)
171 | {
172 | Event event;
173 |
174 | event.type = Event::kNoteOnEvent;
175 | event.sampleOffset = sampleOffset;
176 | event.polyPressure.channel = channel;
177 | event.polyPressure.pitch = noteNumber;
178 | event.polyPressure.pressure = pressure;
179 |
180 | processData->outputEvents->addEvent(event);
181 | }
182 |
183 | virtual void AudioPlugSharpHost::BeginEdit(int parameter)
184 | {
185 | if (controller != nullptr)
186 | controller->beginEdit(parameter + PLUGIN_PARAMETER_USER_START);
187 | }
188 |
189 | virtual void AudioPlugSharpHost::PerformEdit(int parameter, double normalizedValue)
190 | {
191 | if (controller != nullptr)
192 | controller->performEdit(parameter + PLUGIN_PARAMETER_USER_START, (float)normalizedValue);
193 | }
194 |
195 | virtual void AudioPlugSharpHost::EndEdit(int parameter)
196 | {
197 | if (controller != nullptr)
198 | controller->endEdit(parameter + PLUGIN_PARAMETER_USER_START);
199 | }
200 |
201 | internal:
202 | ProcessData* processData = nullptr;
203 | property AudioPlugSharp::IAudioPlugin^ plugin;
204 | AudioPlugSharpController* controller = nullptr;
205 |
206 | void SetProcessData(ProcessData* data)
207 | {
208 | eventSampleOffset = 0;
209 | startEvent = 0;
210 | processData = data;
211 | CurrentAudioBufferSize = data->numSamples;
212 | BPM = data->processContext->tempo;
213 | IsPlaying = (data->processContext->state & ProcessContext::kPlaying) != 0;
214 | CurrentProjectSample = data->processContext->projectTimeSamples;
215 | }
216 |
217 | private:
218 | int eventSampleOffset = 0;
219 | int startEvent = 0;
220 |
221 | };
222 |
223 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpParameter.cpp:
--------------------------------------------------------------------------------
1 | #include "AudioPlugSharpParameter.h"
2 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpParameter.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "public.sdk/source/vst/vsteditcontroller.h"
4 |
5 | using namespace Steinberg;
6 |
7 | public class AudioPlugSharpParameter : public Vst::Parameter
8 | {
9 | };
10 |
11 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpProcessor.cpp:
--------------------------------------------------------------------------------
1 | #include "public.sdk/source/vst/vstaudioprocessoralgo.h"
2 |
3 | #include "pluginterfaces/vst/ivstevents.h"
4 | #include "pluginterfaces/vst/ivstparameterchanges.h"
5 | #include "pluginterfaces/base/ibstream.h"
6 | #include "base/source/fstreamer.h"
7 |
8 | #include
9 |
10 | #include "AudioPlugSharpProcessor.h"
11 | #include "AudioPlugSharpController.h"
12 | #include "AudioPlugSharpFactory.h"
13 |
14 | using namespace System;
15 | using namespace System::Collections::Generic;
16 | using namespace System::Runtime::InteropServices;
17 |
18 | using namespace AudioPlugSharp;
19 |
20 | AudioPlugSharpProcessor::AudioPlugSharpProcessor(void)
21 | {
22 | }
23 |
24 | AudioPlugSharpProcessor::~AudioPlugSharpProcessor(void)
25 | {
26 | }
27 |
28 | FUnknown* AudioPlugSharpProcessor::createInstance(void* factory)
29 | {
30 | Logger::Log("Create processor instance");
31 |
32 | AudioPlugSharpProcessor* processor = new AudioPlugSharpProcessor();
33 | processor->plugin = ((AudioPlugSharpFactory *)factory)->GetPlugin();
34 |
35 | processor->setControllerClass(((AudioPlugSharpFactory*)factory)->AudioPlugSharpControllerUID);
36 |
37 | return (IAudioProcessor*)processor;
38 | }
39 |
40 | tresult PLUGIN_API AudioPlugSharpProcessor::initialize(FUnknown* context)
41 | {
42 | tresult result = AudioEffect::initialize(context);
43 |
44 | if (result != kResultOk)
45 | {
46 | return result;
47 | }
48 |
49 | try
50 | {
51 | audioPlugHost = gcnew AudioPlugSharpHost();
52 |
53 | plugin->Host = audioPlugHost;
54 |
55 | plugin->Processor->Initialize();
56 | }
57 | catch (Exception^ ex)
58 | {
59 | Logger::Log("Unable to initialize managed processor: " + ex->ToString());
60 | }
61 |
62 | // Add audio inputs
63 | for each (auto port in plugin->Processor->InputPorts)
64 | {
65 | TChar* portName = (TChar*)(void*)Marshal::StringToHGlobalUni(plugin->Company);
66 |
67 | addAudioInput(portName, port->ChannelConfiguration == EAudioChannelConfiguration::Mono ? SpeakerArr::kMono : SpeakerArr::kStereo);
68 |
69 | Marshal::FreeHGlobal((IntPtr)portName);
70 | }
71 |
72 | // Add audio outputs
73 | for each (auto port in plugin->Processor->OutputPorts)
74 | {
75 | TChar* portName = (TChar*)(void*)Marshal::StringToHGlobalUni(plugin->Company);
76 |
77 | addAudioOutput(portName, port->ChannelConfiguration == EAudioChannelConfiguration::Mono ? SpeakerArr::kMono : SpeakerArr::kStereo);
78 |
79 | Marshal::FreeHGlobal((IntPtr)portName);
80 | }
81 |
82 | // Set up an event intput
83 | addEventInput(STR16("Event In"), 1);
84 |
85 | // Set up an event output
86 | addEventOutput(STR16("Event Out"), 1);
87 |
88 | return kResultOk;
89 | }
90 |
91 | tresult PLUGIN_API AudioPlugSharpProcessor::terminate()
92 | {
93 | return AudioEffect::terminate();
94 | }
95 |
96 | tresult PLUGIN_API AudioPlugSharpProcessor::setActive(TBool state)
97 | {
98 | if (state)
99 | {
100 | plugin->Processor->Start();
101 | }
102 | else
103 | {
104 | plugin->Processor->Stop();
105 | }
106 |
107 | return AudioEffect::setActive(state);
108 | }
109 |
110 | tresult PLUGIN_API AudioPlugSharpProcessor::setState(IBStream* state)
111 | {
112 | Logger::Log("Restore State");
113 |
114 | if (state != nullptr)
115 | {
116 | std::stringstream stringStream;
117 |
118 | char readBuf[1024];
119 |
120 | int32 numRead;
121 |
122 | do
123 | {
124 | state->read(readBuf, 1024, &numRead);
125 |
126 | stringStream.write(readBuf, numRead);
127 | } while (numRead == 1024);
128 |
129 | std::string dataString = stringStream.str();
130 |
131 | array^ byteArray = gcnew array((int)dataString.size());
132 |
133 | Marshal::Copy((IntPtr)&dataString[0], byteArray, 0, byteArray->Length);
134 |
135 | plugin->Processor->RestoreState(byteArray);
136 | }
137 |
138 | return kResultOk;
139 | }
140 |
141 | tresult PLUGIN_API AudioPlugSharpProcessor::getState(IBStream* state)
142 | {
143 | Logger::Log("Save State");
144 |
145 | auto data = plugin->Processor->SaveState();
146 |
147 | if (data != nullptr)
148 | {
149 | unsigned char* dataChars = new unsigned char[data->Length];
150 |
151 | Marshal::Copy(data, 0, (IntPtr)dataChars, data->Length);
152 |
153 | state->write(dataChars, data->Length, 0);
154 |
155 | delete[] dataChars;
156 | }
157 |
158 | return kResultOk;
159 | }
160 |
161 | tresult PLUGIN_API AudioPlugSharpProcessor::canProcessSampleSize(int32 symbolicSampleSize)
162 | {
163 | if (symbolicSampleSize == kSample32)
164 | {
165 | if ((plugin->Processor->SampleFormatsSupported & EAudioBitsPerSample::Bits32) == EAudioBitsPerSample::Bits32)
166 | {
167 | return kResultTrue;
168 | }
169 | }
170 |
171 | if (symbolicSampleSize == kSample64)
172 | {
173 | if ((plugin->Processor->SampleFormatsSupported & EAudioBitsPerSample::Bits64) == EAudioBitsPerSample::Bits64)
174 | {
175 | return kResultTrue;
176 | }
177 | }
178 |
179 | return kResultFalse;
180 | }
181 |
182 | tresult PLUGIN_API AudioPlugSharpProcessor::setBusArrangements(SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts)
183 | {
184 | // We should be ok with any arrangement
185 |
186 | return kResultOk;
187 | }
188 |
189 | tresult PLUGIN_API AudioPlugSharpProcessor::notify(Vst::IMessage* message)
190 | {
191 | Logger::Log("Got message from controller");
192 |
193 | if (message != nullptr)
194 | {
195 | Steinberg::int64 value = 0;
196 |
197 | if (message->getAttributes()->getInt("AudioPlugSharpControllerPtr", value) == kResultTrue)
198 | {
199 | Logger::Log("Got controller pointer");
200 |
201 | controller = (AudioPlugSharpController*)value;
202 |
203 | controller->setProcessor(this, plugin);
204 |
205 | audioPlugHost->plugin = plugin;
206 | audioPlugHost->controller = controller;
207 | }
208 | }
209 |
210 | return kResultTrue;
211 | }
212 |
213 |
214 | tresult PLUGIN_API AudioPlugSharpProcessor::setupProcessing(ProcessSetup& newSetup)
215 | {
216 | Logger::Log("Setup Processing. " + ((newSetup.symbolicSampleSize == kSample32) ? "32bit" : "64bit"));
217 |
218 | tresult result = AudioEffect::setupProcessing(newSetup);
219 |
220 | if (result != kResultOk)
221 | {
222 | Logger::Log("Setup processing failed");
223 |
224 | return result;
225 | }
226 |
227 | audioPlugHost->SampleRate = newSetup.sampleRate;
228 | audioPlugHost->BitsPerSample = (newSetup.symbolicSampleSize == kSample32) ? EAudioBitsPerSample::Bits32 : EAudioBitsPerSample::Bits64;
229 | audioPlugHost->MaxAudioBufferSize = newSetup.maxSamplesPerBlock;
230 |
231 | plugin->Processor->InitializeProcessing();
232 |
233 | plugin->Processor->SetMaxAudioBufferSize(newSetup.maxSamplesPerBlock, (newSetup.symbolicSampleSize == kSample32) ? EAudioBitsPerSample::Bits32 : EAudioBitsPerSample::Bits64);
234 |
235 |
236 | return kResultOk;
237 | }
238 |
239 | tresult PLUGIN_API AudioPlugSharpProcessor::process(ProcessData& data)
240 | {
241 | try
242 | {
243 | audioPlugHost->SetProcessData(&data);
244 |
245 | if ((data.numInputs == 0) && (data.numOutputs == 0))
246 | {
247 | // The host is just flushing events without sending audio data
248 | }
249 | else
250 | {
251 | for (int input = 0; input < plugin->Processor->InputPorts->Length; input++)
252 | {
253 | plugin->Processor->InputPorts[input]->SetCurrentBufferSize(data.numSamples);
254 | plugin->Processor->InputPorts[input]->SetAudioBufferPtrs((IntPtr)getChannelBuffersPointer(processSetup, data.inputs[input]));
255 | }
256 |
257 | for (int output = 0; output < plugin->Processor->OutputPorts->Length; output++)
258 | {
259 | plugin->Processor->OutputPorts[output]->SetCurrentBufferSize(data.numSamples);
260 | plugin->Processor->OutputPorts[output]->SetAudioBufferPtrs((IntPtr)getChannelBuffersPointer(processSetup, data.outputs[output]));
261 | }
262 |
263 | plugin->Processor->PreProcess();
264 | plugin->Processor->Process();
265 | plugin->Processor->PostProcess();
266 | }
267 |
268 | // Reset any parameters that had changes
269 | if (data.inputParameterChanges != nullptr)
270 | {
271 | int32 numParamsChanged = data.inputParameterChanges->getParameterCount();
272 |
273 | for (int32 i = 0; i < numParamsChanged; i++)
274 | {
275 | IParamValueQueue* paramQueue = data.inputParameterChanges->getParameterData(i);
276 |
277 | if (paramQueue != nullptr)
278 | {
279 | ParamID paramID = paramQueue->getParameterId();
280 |
281 | plugin->Processor->Parameters[paramID - PLUGIN_PARAMETER_USER_START]->ResetParameterChange();
282 | }
283 | }
284 | }
285 | }
286 | catch (Exception^ ex)
287 | {
288 | Logger::Log("Error in process loop: " + ex->ToString());
289 | }
290 |
291 | // Handle any output parameter changes (such as volume meter output)
292 | // We don't have any
293 | //IParameterChanges* outParamChanges = data.outputParameterChanges;
294 |
295 | // Null our temporary pointer to process data
296 | audioPlugHost->processData = nullptr;
297 |
298 | return kResultOk;
299 | }
300 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpProcessor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "public.sdk/source/vst/vsteditcontroller.h"
4 | #include "public.sdk/source/vst/vstaudioeffect.h"
5 | #include "base/source/fstring.h"
6 | #include "pluginterfaces/base/funknown.h"
7 | #include "AudioPlugSharpHost.h"
8 |
9 | #include
10 |
11 | using namespace msclr;
12 |
13 | using namespace Steinberg;
14 | using namespace Steinberg::Vst;
15 |
16 | using namespace AudioPlugSharp;
17 |
18 | class AudioPlugSharpFactory;
19 | class AudioPlugSharpController;
20 |
21 | class AudioPlugSharpProcessor : public AudioEffect
22 | {
23 | public:
24 | AudioPlugSharpProcessor(void);
25 |
26 | static FUnknown* createInstance(void* factory);
27 |
28 | tresult PLUGIN_API initialize(FUnknown* context) SMTG_OVERRIDE;
29 | tresult PLUGIN_API terminate() SMTG_OVERRIDE;
30 | tresult PLUGIN_API setActive(TBool state) SMTG_OVERRIDE;
31 | tresult PLUGIN_API setupProcessing(ProcessSetup& newSetup) SMTG_OVERRIDE;
32 | tresult PLUGIN_API process(ProcessData& data) SMTG_OVERRIDE;
33 | tresult PLUGIN_API setState(IBStream* state) SMTG_OVERRIDE;
34 | tresult PLUGIN_API getState(IBStream* state) SMTG_OVERRIDE;
35 | tresult PLUGIN_API canProcessSampleSize(int32 symbolicSampleSize) SMTG_OVERRIDE;
36 | tresult PLUGIN_API setBusArrangements(SpeakerArrangement* inputs, int32 numIns, SpeakerArrangement* outputs, int32 numOuts);
37 | tresult PLUGIN_API notify(Vst::IMessage* message) SMTG_OVERRIDE;
38 | ~AudioPlugSharpProcessor(void);
39 |
40 | gcroot plugin;
41 |
42 | private:
43 | AudioPlugSharpController* controller = nullptr;
44 | gcroot audioPlugHost = nullptr;
45 |
46 | };
47 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpVst.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | 16.0
23 | {99BCC0C3-3295-4AF3-A7CF-5ED582D2FD30}
24 | NetCoreCProj
25 | AudioPlugSharpVst
26 | 10.0
27 | true
28 | true
29 | net8.0
30 | 7.0
31 |
32 |
33 |
34 | DynamicLibrary
35 | true
36 | v143
37 | NetCore
38 | Unicode
39 |
40 |
41 | DynamicLibrary
42 | false
43 | v143
44 | NetCore
45 | Unicode
46 |
47 |
48 | DynamicLibrary
49 | true
50 | v143
51 | NetCore
52 | Unicode
53 |
54 |
55 | DynamicLibrary
56 | false
57 | v143
58 | NetCore
59 | Unicode
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | .vst3
81 |
82 |
83 | .vst3
84 |
85 |
86 |
87 | Level3
88 | WIN32;_DEBUG;%(PreprocessorDefinitions)
89 | ..\vst3sdk;..\vst3sdk\base\..
90 |
91 |
92 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
93 | %(AdditionalOptions) /machine:x64 /EXPORT:GetPluginFactory
94 |
95 |
96 | Ijwhost.xml
97 |
98 |
99 |
100 |
101 | Level3
102 | _DEBUG;%(PreprocessorDefinitions)
103 | ..\vst3sdk;..\vst3sdk\base\..
104 | MultiThreadedDebugDLL
105 | ProgramDatabase
106 | true
107 |
108 |
109 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
110 | %(AdditionalOptions) /machine:x64 /EXPORT:GetPluginFactory
111 |
112 |
113 |
114 |
115 | Ijwhost.xml
116 |
117 |
118 |
119 |
120 | Level3
121 | WIN32;NDEBUG;%(PreprocessorDefinitions)
122 | ..\vst3sdk;..\vst3sdk\base\..
123 |
124 |
125 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
126 | %(AdditionalOptions) /machine:x64 /EXPORT:GetPluginFactory
127 |
128 |
129 | Ijwhost.xml
130 |
131 |
132 |
133 |
134 | Level3
135 | NDEBUG;%(PreprocessorDefinitions)
136 | ..\vst3sdk;..\vst3sdk\base\..
137 | MaxSpeed
138 | MultiThreadedDLL
139 |
140 |
141 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
142 | %(AdditionalOptions) /machine:x64 /EXPORT:GetPluginFactory
143 |
144 |
145 | Ijwhost.xml
146 |
147 |
148 |
149 |
150 | {9418f8d2-a4bb-4a69-a2dc-7fd312e49d77}
151 |
152 |
153 | {d47ca71a-67fb-39fc-a8d8-c42e728e322d}
154 |
155 |
156 | {61b1304b-aa25-31a3-83d3-9de105658b7b}
157 |
158 |
159 | {0e457a07-0e59-35dd-ba1c-904d39085c52}
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 | true
185 |
186 |
187 |
188 |
189 |
190 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/AudioPlugSharpVst.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Source Files
20 |
21 |
22 | Source Files
23 |
24 |
25 | Source Files
26 |
27 |
28 | Source Files
29 |
30 |
31 | Source Files
32 |
33 |
34 | Source Files
35 |
36 |
37 | Source Files
38 |
39 |
40 |
41 |
42 | Header Files
43 |
44 |
45 | Header Files
46 |
47 |
48 | Header Files
49 |
50 |
51 | Header Files
52 |
53 |
54 | Header Files
55 |
56 |
57 | Header Files
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/Ijwhost.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/AudioPlugSharpVst/desktop.runtimeconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "runtimeOptions": {
3 | "tfm": "net8.0",
4 | "rollForward": "LatestMajor",
5 | "framework": {
6 | "name": "Microsoft.WindowsDesktop.App",
7 | "version": "8.0.0"
8 | },
9 | "configProperties": {
10 | "System.Runtime.InteropServices.CppCLI.LoadComponentInIsolatedContext": true
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/AudioPlugSharpWPF.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0-windows
5 | true
6 | true
7 | 0.6.10
8 | Mike Oliphant
9 | Add WPF GUI to your AudioPlugSharp plugin.
10 | MIT
11 | true
12 | true
13 | snupkg
14 | true
15 | true
16 | True
17 | README.md
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 | MSBuild:Compile
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/AudioPluginWPF.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 | using System.Windows.Controls;
8 | using AudioPlugSharp;
9 |
10 | [assembly: System.Windows.ThemeInfo(
11 | System.Windows.ResourceDictionaryLocation.None,
12 | System.Windows.ResourceDictionaryLocation.SourceAssembly
13 | )]
14 |
15 | namespace AudioPlugSharpWPF
16 | {
17 | public class AudioPluginWPF : AudioPluginBase
18 | {
19 | [DllImport("User32.dll")]
20 | private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
21 |
22 | [DllImport("Shcore.dll")]
23 | private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
24 |
25 | public enum DpiType
26 | {
27 | Effective = 0,
28 | Angular = 1,
29 | Raw = 2,
30 | }
31 |
32 | public EditorWindow EditorWindow { get; set; }
33 | public UserControl EditorView { get; set; }
34 |
35 | public AudioPluginWPF()
36 | {
37 | // WPF requires a shared assembly load context: https://github.com/dotnet/wpf/issues/1700
38 | CacheLoadContext = true;
39 | }
40 |
41 | public override double GetDpiScale()
42 | {
43 | uint dpiX;
44 | uint dpiY;
45 |
46 | var pnt = new System.Drawing.Point(0, 0);
47 | var mon = MonitorFromPoint(pnt, 2);
48 | GetDpiForMonitor(mon, DpiType.Effective, out dpiX, out dpiY);
49 |
50 | return (double)dpiX / 96.0;
51 | }
52 |
53 | public virtual UserControl GetEditorView()
54 | {
55 | return new EditorView();
56 | }
57 |
58 | public override void ResizeEditor(uint newWidth, uint newHeight)
59 | {
60 | base.ResizeEditor(newWidth, newHeight);
61 |
62 | if (EditorWindow != null)
63 | {
64 | EditorWindow.SetSize(EditorWidth, EditorHeight);
65 | }
66 | }
67 |
68 | public override void ShowEditor(IntPtr parentWindow)
69 | {
70 | Logger.Log("Open editor. Thread ID is: " + System.Threading.Thread.CurrentThread.ManagedThreadId);
71 |
72 | if (EditorView == null)
73 | EditorView = GetEditorView();
74 |
75 | EditorWindow = new EditorWindow(this, EditorView);
76 |
77 | EditorWindow.SetSize(EditorWidth, EditorHeight);
78 |
79 | EditorWindow.Show(parentWindow);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/Dial.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Windows;
4 | using System.Windows.Controls.Primitives;
5 | using System.Windows.Data;
6 | using System.Windows.Input;
7 |
8 | namespace AudioPlugSharpWPF
9 | {
10 | public class Dial : RangeBase
11 | {
12 | public static readonly DependencyProperty DefaultValueProperty =
13 | DependencyProperty.Register("DefaultValue", typeof(double),
14 | typeof(Dial), new PropertyMetadata(default(double)));
15 |
16 | public double DefaultValue
17 | {
18 | get { return (double)this.GetValue(DefaultValueProperty); }
19 | set { this.SetValue(DefaultValueProperty, value); }
20 | }
21 | private bool dragging = false;
22 | private Point dragStart;
23 | private System.Drawing.Point absDragStart;
24 | private double valAtStart;
25 |
26 | static Dial()
27 | {
28 | DefaultStyleKeyProperty.OverrideMetadata(typeof(Dial), new FrameworkPropertyMetadata(typeof(Dial)));
29 | }
30 |
31 | public Dial()
32 | {
33 | this.MouseLeftButtonDown += new MouseButtonEventHandler(Dial_MouseLeftButtonDown);
34 | this.MouseLeftButtonUp += new MouseButtonEventHandler(Dial_MouseLeftButtonUp);
35 | this.MouseMove += new MouseEventHandler(Dial_MouseMove);
36 | this.MouseDoubleClick += new MouseButtonEventHandler(Dial_MouseDoubleClick);
37 | }
38 |
39 | void Dial_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
40 | {
41 | dragging = true;
42 | dragStart = e.GetPosition(this);
43 | absDragStart = System.Windows.Forms.Cursor.Position;
44 | valAtStart = this.Value;
45 |
46 | this.CaptureMouse();
47 | this.Cursor = Cursors.None;
48 |
49 | e.Handled = true;
50 | }
51 |
52 | void Dial_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
53 | {
54 | this.ReleaseMouseCapture();
55 |
56 | e.Handled = true;
57 | }
58 |
59 | void Dial_MouseMove(object sender, MouseEventArgs e)
60 | {
61 | if (dragging)
62 | {
63 | Point currentPos = e.GetPosition(this);
64 |
65 | double delta = dragStart.Y - currentPos.Y;
66 | double pixelScale = (this.Maximum - this.Minimum) / 100;
67 | double newValue = valAtStart + delta * pixelScale;
68 |
69 | if (newValue > this.Maximum)
70 | {
71 | this.Value = this.Maximum;
72 | }
73 | else if (newValue < this.Minimum)
74 | {
75 | this.Value = this.Minimum;
76 | }
77 | else
78 | {
79 | this.Value = newValue;
80 | }
81 |
82 | e.Handled = true;
83 | }
84 | }
85 |
86 | protected override void OnLostMouseCapture(MouseEventArgs e)
87 | {
88 | dragging = false;
89 |
90 | System.Windows.Forms.Cursor.Position = absDragStart;
91 | this.ClearValue(FrameworkElement.CursorProperty);
92 |
93 | base.OnLostMouseCapture(e);
94 | }
95 |
96 | void Dial_MouseDoubleClick(object sender, MouseButtonEventArgs e)
97 | {
98 | this.Value = DefaultValue;
99 | }
100 | }
101 |
102 | public sealed class DialToAngleConverter : IMultiValueConverter
103 | {
104 | public object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)
105 | {
106 | Dial dial = (Dial)(value[0]);
107 |
108 | double val = (dial.Value - dial.Minimum) / (dial.Maximum - dial.Minimum);
109 |
110 | double angle = 143;
111 |
112 | return -angle + (val * angle * 2);
113 | }
114 |
115 | public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
116 | {
117 | throw new NotSupportedException("DialToAngleConverter.ConvertBack is not supported.");
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/EditorView.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/EditorView.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Data;
7 | using System.Windows.Documents;
8 | using System.Windows.Input;
9 | using System.Windows.Media;
10 | using System.Windows.Media.Imaging;
11 | using System.Windows.Navigation;
12 | using System.Windows.Shapes;
13 |
14 | namespace AudioPlugSharpWPF
15 | {
16 | ///
17 | /// Interaction logic for EditorView.xaml
18 | ///
19 | public partial class EditorView : UserControl
20 | {
21 | public EditorView()
22 | {
23 | InitializeComponent();
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/EditorWindow.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.Text;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 | using System.Runtime.InteropServices;
8 | using AudioPlugSharp;
9 |
10 | namespace AudioPlugSharpWPF
11 | {
12 | public class EditorWindow : Window
13 | {
14 | [DllImport("user32.dll", SetLastError = true)]
15 | static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
16 |
17 | [DllImport("user32.dll")]
18 | public static extern bool GetWindowRect(IntPtr hwnd, ref Rectangle rectangle);
19 |
20 | public IAudioPluginEditor Editor { get; private set; }
21 | public UserControl EditorView { get; private set; }
22 |
23 | public Rectangle DisplayRectangle
24 | {
25 | get
26 | {
27 | Rectangle displayRectangle = new Rectangle();
28 |
29 | GetWindowRect(parentWindow, ref displayRectangle);
30 |
31 | return displayRectangle;
32 | }
33 | }
34 |
35 | IntPtr parentWindow;
36 |
37 | public EditorWindow(IAudioPluginEditor editor, UserControl editorView)
38 | {
39 | this.Editor = editor;
40 | this.EditorView = editorView;
41 |
42 | DataContext = Editor.Processor;
43 | }
44 |
45 | public void SetSize(uint width, uint height)
46 | {
47 | Width = width;
48 | Height = height;
49 | }
50 |
51 | public void Show(IntPtr parentWindow)
52 | {
53 | this.parentWindow = parentWindow;
54 |
55 | Content = EditorView;
56 |
57 | Top = 0;
58 | Left = 0;
59 | ShowInTaskbar = false;
60 | WindowStyle = System.Windows.WindowStyle.None;
61 | ResizeMode = System.Windows.ResizeMode.NoResize;
62 |
63 | Show();
64 |
65 | var windowHwnd = new System.Windows.Interop.WindowInteropHelper(this);
66 | IntPtr hWnd = windowHwnd.Handle;
67 | SetParent(hWnd, parentWindow);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/Images/DialBackground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikeoliphant/AudioPlugSharp/68cffdb9a35e2abd92003b958dcb063556102792/AudioPlugSharpWPF/Images/DialBackground.png
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/Images/DialBackgroundWhite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikeoliphant/AudioPlugSharp/68cffdb9a35e2abd92003b958dcb063556102792/AudioPlugSharpWPF/Images/DialBackgroundWhite.png
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/Images/DialPointer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikeoliphant/AudioPlugSharp/68cffdb9a35e2abd92003b958dcb063556102792/AudioPlugSharpWPF/Images/DialPointer.png
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/Images/DialPointerBlack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikeoliphant/AudioPlugSharp/68cffdb9a35e2abd92003b958dcb063556102792/AudioPlugSharpWPF/Images/DialPointerBlack.png
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/Images/PowerOff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikeoliphant/AudioPlugSharp/68cffdb9a35e2abd92003b958dcb063556102792/AudioPlugSharpWPF/Images/PowerOff.png
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/Images/PowerOn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mikeoliphant/AudioPlugSharp/68cffdb9a35e2abd92003b958dcb063556102792/AudioPlugSharpWPF/Images/PowerOn.png
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/ParameterDisplay.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
22 |
23 |
24 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/ParameterDisplay.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Data;
9 | using System.Windows.Documents;
10 | using System.Windows.Input;
11 | using System.Windows.Media;
12 | using System.Windows.Media.Imaging;
13 | using System.Windows.Navigation;
14 | using System.Windows.Shapes;
15 |
16 | namespace AudioPlugSharpWPF
17 | {
18 | ///
19 | /// Interaction logic for ParameterDisplay.xaml
20 | ///
21 | public partial class ParameterDisplay : UserControl
22 | {
23 | public ParameterDisplay()
24 | {
25 | InitializeComponent();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/Themes/Generic.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
40 |
41 |
72 |
73 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/TypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Controls;
3 | using System.Windows.Data;
4 |
5 | namespace AudioPlugSharpWPF
6 | {
7 | public class ObjectTypeConverter : IValueConverter
8 | {
9 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
10 | {
11 | return (value == null) ? null : value.GetType();
12 | }
13 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
14 | {
15 | throw new NotImplementedException();
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/AudioPlugSharpWPF/build/AudioPlugSharpWPF.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreserveNewest
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Dist/.gitignore:
--------------------------------------------------------------------------------
1 | *
2 | !.gitignore
3 |
--------------------------------------------------------------------------------
/JackHostTest/JackHostTest.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net8.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/JackHostTest/Program.cs:
--------------------------------------------------------------------------------
1 | using AudioPlugSharpJack;
2 | using SimpleExample;
3 |
4 | namespace JackHostTest
5 | {
6 | internal class Program
7 | {
8 | static void Main(string[] args)
9 | {
10 | JackHost host = new JackHost(new SimpleExamplePlugin());
11 |
12 | host.Run();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Mike Oliphant
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/MakeDist.bat:
--------------------------------------------------------------------------------
1 | del /S /Q Dist
2 | mkdir Dist
3 | mkdir Dist\x64
4 | echo * > Dist\.gitignore
5 | echo !.gitignore >> Dist\.gitignore
6 | copy x64\Release\*.dll Dist\x64
7 | copy x64\Release\*.json Dist\x64
8 | copy x64\Release\*.vst3 Dist\x64
9 | copy AudioPlugSharpWPF\bin\Release\net6.0-windows\AudioPlugSharpWPF.dll Dist\x64
10 |
--------------------------------------------------------------------------------
/MidiExample/MidiExample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 |
6 |
7 |
8 | false
9 |
10 |
11 |
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/MidiExample/MidiExamplePlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AudioPlugSharp;
3 |
4 | namespace MidiExample
5 | {
6 | public class MidiExamplePlugin : AudioPluginBase
7 | {
8 | public MidiExamplePlugin()
9 | {
10 | Company = "My Company";
11 | Website = "www.mywebsite.com";
12 | Contact = "contact@my.email";
13 | PluginName = "Midi Example Plugin";
14 | PluginCategory = "Instrument|Synth";
15 | PluginVersion = "1.0.0";
16 |
17 | // Unique 64bit ID for the plugin
18 | PluginID = 0x74466AE90F4F40CD;
19 | }
20 |
21 | DoubleAudioIOPort monoOutput;
22 | AudioPluginParameter gainParameter;
23 |
24 | public override void Initialize()
25 | {
26 | base.Initialize();
27 |
28 | OutputPorts = new AudioIOPort[] { monoOutput = new DoubleAudioIOPort("Mono Output", EAudioChannelConfiguration.Mono) };
29 |
30 | AddParameter(gainParameter = new AudioPluginParameter
31 | {
32 | ID = "gain",
33 | Name = "Gain",
34 | Type = EAudioPluginParameterType.Float,
35 | MinValue = -20,
36 | MaxValue = 6,
37 | DefaultValue = 0,
38 | ValueFormat = "{0:0.0}dB"
39 | });
40 | }
41 |
42 | public override void HandleNoteOn(int channel, int noteNumber, float velocity, int sampleOffset)
43 | {
44 | Logger.Log("Note on: " + noteNumber + " velocity: " + velocity.ToString() + " offset: " + sampleOffset);
45 |
46 | freq = Math.Pow(2, (noteNumber - 49) / 12.0) * 440;
47 | desiredNoteVolume = velocity * 0.5f;
48 | }
49 |
50 | public override void HandleNoteOff(int channel, int noteNumber, float velocity, int sampleOffset)
51 | {
52 | Logger.Log("Note off: " + noteNumber + " offset: " + sampleOffset);
53 |
54 | desiredNoteVolume = 0;
55 | }
56 |
57 | public override void HandleParameterChange(AudioPluginParameter parameter, double newNormalizedValue, int sampleOffset)
58 | {
59 | Logger.Log("Param change: " + parameter.Name + " val: " + parameter.ProcessValue + " offset: " + sampleOffset);
60 |
61 | base.HandleParameterChange(parameter, newNormalizedValue, sampleOffset);
62 | }
63 |
64 | double Lerp(double value1, double value2, double amount)
65 | {
66 | return value1 + (value2 - value1) * amount;
67 | }
68 |
69 | double noteVolume = 0;
70 | double desiredNoteVolume = 0;
71 | long samplesSoFar = 0;
72 | double freq = 0;
73 | double linearGain = 1.0f;
74 |
75 | public override void Process()
76 | {
77 | base.Process();
78 |
79 | linearGain = Math.Pow(10.0, 0.05 * gainParameter.ProcessValue);
80 |
81 | Span outSamples = monoOutput.GetAudioBuffer(0);
82 |
83 | int currentSample = 0;
84 | int nextSample = 0;
85 |
86 | do
87 | {
88 | // Trigger any events at our current sample, and find out how many samples we have until there are more events
89 | nextSample = Host.ProcessEvents();
90 |
91 | Logger.Log("Process from " + currentSample + " to " + (nextSample - 1));
92 |
93 | Logger.Log("Current freq: " + freq + " volume: " + desiredNoteVolume);
94 |
95 | bool needGainUpdate = gainParameter.NeedInterpolationUpdate;
96 |
97 | // Loop over the samples we have remaining until we need to trigger more events
98 | for (int i = currentSample; i < nextSample; i++)
99 | {
100 | noteVolume = Lerp(noteVolume, desiredNoteVolume, 0.001);
101 |
102 | // If we need to update our gain paramter, get the sample-accurate interpolated value
103 | if (needGainUpdate)
104 | {
105 | linearGain = Math.Pow(10.0, 0.05 * gainParameter.GetInterpolatedProcessValue(i));
106 | }
107 |
108 | outSamples[i] = Math.Sin(((double)samplesSoFar * 2 * Math.PI * freq) / Host.SampleRate) * noteVolume * linearGain;
109 |
110 | samplesSoFar++;
111 | }
112 |
113 | currentSample = nextSample;
114 | }
115 | while (nextSample < outSamples.Length); // Continue looping until we hit the end of the buffer
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AudioPlugSharp
2 | Easily create VST3 audio plugins in C#
3 |
4 | AudioPlugSharp provides a C++/CLI bridge to load managed audio plugins into VST hosts. User interfaces can be created with built-in support for WPF. Windows Forms interfaces are also possible.
5 |
6 | # Current Release
7 |
8 | NuGet packages are available:
9 |
10 | [AudioPlugSharp](https://www.nuget.org/packages/AudioPlugSharp) (Needed for all plugins)
11 |
12 | [AudioPlugSharpWPF](https://www.nuget.org/packages/AudioPlugSharpWPF) (Also needed for WPF user interfaces)
13 |
14 | [AudioPlugSharpHost](https://www.nuget.org/packages/AudioPlugSharpHost) (For createing stand-alone Windows applictions)
15 |
16 | [AudioPlugSharpJack](https://www.nuget.org/packages/AudioPlugSharpJack) (For createing cross-platform Jack Audio clients)
17 |
18 | # Plugin Project Setup and Deployment
19 |
20 | ## NuGet Packages
21 | Using the NuGet packages is the recommended way to build a project using AudioPlugSharp.
22 |
23 | If you use the AudioPlugSharp/AudioPlugSharpWPF NuGet packages, copying the appropriate files to your output folder will be handled for you. You simply need to copy the files from the output folder to a folder on your VST plugin path (usually "**C:\Program Files\Common Files\VST3**").
24 |
25 | ## Direct Referecing
26 | If you are referencing AudioPlugSharp manually, your plugin project will need an assembly dependency on **AudioPlugSharp.dll** (and **AudioPlugSharpWPF.dll** if you are using it).
27 |
28 | For deployment, you need to copy **"AudioPlugSharpVst.vst3"** to your output folder, and rename it to be **"YourPluginDllNameBridge.vst3"**. So if your plugin dll is called **"MyPlugin.dll"**, then you would rename **"AudioPlugSharpVst.vst3"** to **"MyPluginBridge.vst3"**. You also need to copy **"AudioPlugSharpVst.runtimeconfig.json"** (or **"wpf.runtimeconfig.json"** if you are using WPF in your plugin) to your output folder as **"YourPluginDllNameBridge.runtimeconfig.json"**. You also need to copy **"Ijwhost.dll"** to your output folder.
29 |
30 | These steps can be done using a Post-build event. Have a look at the included sample plugins for examples - keep in mind you may need to change the source folder of the "copy" commands depending on where your copy of AudioPlugSharp is.
31 |
32 | # Examples
33 |
34 | See the [SimpleExample](https://github.com/mikeoliphant/AudioPlugSharp/blob/master/SimpleExample/SimpleExamplePlugin.cs), [MidiExample](https://github.com/mikeoliphant/AudioPlugSharp/tree/master/MidiExample) and [WPFExample](https://github.com/mikeoliphant/AudioPlugSharp/blob/master/WPFExample/WPFExamplePlugin.cs) projects for example usage.
35 |
36 | Here are some examples of some larger projects using AudioPlugSharp:
37 |
38 | [ChartPlayer](https://github.com/mikeoliphant/ChartPlayer), an app for playing along to song charts synchronized to music recordings.
39 |
40 | [Stompbox](https://github.com/mikeoliphant/StompboxUI), guitar amplifier and effects simulation.
41 |
42 | [LiveSPICE VST](https://github.com/dsharlet/LiveSPICE/tree/master/LiveSPICEVst), real time SPICE simulation for audio signals.
43 |
44 | # Creating a Stand-Alone Application
45 |
46 | The [AudioPlugSharpHost library](https://github.com/mikeoliphant/AudioPlugSharp/tree/master/AudioPlugSharpHost) can be used to easily create a stand-alone Windows application for your plugin.
47 |
48 | # AudioPlugSharp Building Instructions
49 |
50 | You will need to have CMake (https://cmake.org) installed.
51 |
52 | From a shell, run the following:
53 |
54 | ```bash
55 | git clone --recursive https://github.com/mikeoliphant/AudioPlugSharp
56 | cd AudioPlugSharp
57 | cd vstbuild
58 | cmake.exe -G "Visual Studio 17 2022" -A x64 ../vst3sdk
59 | ```
60 |
61 | Then you can load the solution in Visual Studio and build. **Note that you will need to change the last line above if you have a different version of Visual Studio.**
62 |
--------------------------------------------------------------------------------
/SimpleExample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "SimpleExample": {
4 | "commandName": "Executable",
5 | "executablePath": "C:\\Program Files\\REAPER (x64)\\reaper.exe",
6 | "nativeDebugging": true
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/SimpleExample/SimpleExample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 |
6 |
7 |
8 | false
9 |
10 |
11 |
12 | false
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SimpleExample/SimpleExamplePlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using AudioPlugSharp;
3 |
4 | namespace SimpleExample
5 | {
6 | public class SimpleExamplePlugin : AudioPluginBase
7 | {
8 | public SimpleExamplePlugin()
9 | {
10 | Company = "My Company";
11 | Website = "www.mywebsite.com";
12 | Contact = "contact@my.email";
13 | PluginName = "Simple Gain Plugin";
14 | PluginCategory = "Fx";
15 | PluginVersion = "1.0.0";
16 |
17 | // Unique 64bit ID for the plugin
18 | PluginID = 0xF57703946AFC4EF8;
19 |
20 | SampleFormatsSupported = EAudioBitsPerSample.Bits32;
21 | }
22 |
23 | FloatAudioIOPort monoInput;
24 | FloatAudioIOPort monoOutput;
25 |
26 | public override void Initialize()
27 | {
28 | base.Initialize();
29 |
30 | InputPorts = new AudioIOPort[] { monoInput = new FloatAudioIOPort("Mono Input", EAudioChannelConfiguration.Mono) };
31 | OutputPorts = new AudioIOPort[] { monoOutput = new FloatAudioIOPort("Mono Output", EAudioChannelConfiguration.Mono) };
32 |
33 | AddParameter(new AudioPluginParameter
34 | {
35 | ID = "gain",
36 | Name = "Gain",
37 | Type = EAudioPluginParameterType.Float,
38 | MinValue = -20,
39 | MaxValue = 20,
40 | DefaultValue = 0,
41 | ValueFormat = "{0:0.0}dB"
42 | });
43 | }
44 |
45 | public override void Process()
46 | {
47 | base.Process();
48 |
49 | // This will trigger all Midi note events and parameter changes that happend during this process window
50 | // For sample-accurate tracking, see the WPFExample or MidiExample plugins
51 | Host.ProcessAllEvents();
52 |
53 | double gain = GetParameter("gain").ProcessValue;
54 | float linearGain = (float)Math.Pow(10.0, 0.05 * gain);
55 |
56 | ReadOnlySpan inSamples = monoInput.GetAudioBuffer(0);
57 | Span outSamples = monoOutput.GetAudioBuffer(0);
58 |
59 | for (int i = 0; i < inSamples.Length; i++)
60 | {
61 | outSamples[i] = inSamples[i] * linearGain;
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/WPFExample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "WPFExample": {
4 | "commandName": "Executable",
5 | "executablePath": "C:\\Program Files\\REAPER (x64)\\reaper.exe",
6 | "nativeDebugging": true
7 | }
8 | }
9 | }
--------------------------------------------------------------------------------
/WPFExample/WPFExample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0-windows
5 | true
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/WPFExample/WPFExamplePlugin.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows.Controls;
3 | using AudioPlugSharp;
4 | using AudioPlugSharpWPF;
5 |
6 | namespace WPFExample
7 | {
8 | public class WPFExamplePlugin : AudioPluginWPF
9 | {
10 | DoubleAudioIOPort monoInput;
11 | DoubleAudioIOPort stereoOutput;
12 |
13 | AudioPluginParameter gainParameter = null;
14 | AudioPluginParameter panParameter = null;
15 |
16 | public WPFExamplePlugin()
17 | {
18 | Company = "My Company";
19 | Website = "www.mywebsite.com";
20 | Contact = "contact@my.email";
21 | PluginName = "WPF Example Plugin";
22 | PluginCategory = "Fx";
23 | PluginVersion = "1.0.0";
24 |
25 | // Unique 64bit ID for the plugin
26 | PluginID = 0x1E92758E710B4947;
27 |
28 | HasUserInterface = true;
29 | EditorWidth = 200;
30 | EditorHeight = 100;
31 | }
32 |
33 | public override void Initialize()
34 | {
35 | base.Initialize();
36 |
37 | InputPorts = new AudioIOPort[] { monoInput = new DoubleAudioIOPort("Mono Input", EAudioChannelConfiguration.Mono) };
38 | OutputPorts = new AudioIOPort[] { stereoOutput = new DoubleAudioIOPort("Stereo Output", EAudioChannelConfiguration.Stereo) };
39 |
40 | AddParameter(gainParameter = new AudioPluginParameter
41 | {
42 | ID = "gain",
43 | Name = "Gain",
44 | Type = EAudioPluginParameterType.Float,
45 | MinValue = -20,
46 | MaxValue = 20,
47 | DefaultValue = 0,
48 | ValueFormat = "{0:0.0}dB"
49 | });
50 |
51 | AddParameter(panParameter = new AudioPluginParameter
52 | {
53 | ID = "pan",
54 | Name = "Pan",
55 | Type = EAudioPluginParameterType.Float,
56 | MinValue = -1,
57 | MaxValue = 1,
58 | DefaultValue = 0,
59 | ValueFormat = "{0:0.0}"
60 | });
61 | }
62 |
63 | public override void Process()
64 | {
65 | base.Process();
66 |
67 | ReadOnlySpan inSamples = monoInput.GetAudioBuffer(0);
68 |
69 | Span outLeftSamples = stereoOutput.GetAudioBuffer(0);
70 | Span outRightSamples = stereoOutput.GetAudioBuffer(1);
71 |
72 | int currentSample = 0;
73 | int nextSample = 0;
74 |
75 | double linearGain = Math.Pow(10.0, 0.05 * gainParameter.ProcessValue);
76 | double pan = panParameter.ProcessValue;
77 |
78 | do
79 | {
80 | nextSample = Host.ProcessEvents(); // Handle sample-accurate parameters - see the SimpleExample plugin for a simpler, per-buffer parameter approach
81 |
82 | bool needGainUpdate = gainParameter.NeedInterpolationUpdate;
83 | bool needPanUpdate = panParameter.NeedInterpolationUpdate;
84 |
85 | for (int i = currentSample; i < nextSample; i++)
86 | {
87 | if (needGainUpdate)
88 | {
89 | linearGain = Math.Pow(10.0, 0.05 * gainParameter.GetInterpolatedProcessValue(i));
90 | }
91 |
92 | if (needPanUpdate)
93 | {
94 | pan = panParameter.GetInterpolatedProcessValue(i);
95 | }
96 |
97 | outLeftSamples[i] = inSamples[i] * linearGain * (1 - pan);
98 | outRightSamples[i] = inSamples[i] * linearGain * (1 + pan);
99 | }
100 |
101 | currentSample = nextSample;
102 | }
103 | while (nextSample < inSamples.Length); // Continue looping until we hit the end of the buffer
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/vstbuild/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore everything in this directory
2 | *
3 | # Except this file
4 | !.gitignore
--------------------------------------------------------------------------------