commit ba89a58ecef0ac976160e2287ee0ae692ed197ef Author: onyx-and-iris Date: Sat May 27 19:16:48 2023 +0100 Add project files. diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 0000000..76869e7 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,23 @@ +# Step by step + +These instructions should work for both Debug/Release builds. In this case we target Release x64. + +![Release][release] + +Open up project properties.
+![Properties][properties] + +In advanced properties ensure `Character Set` is set to `Not Set` +![Character Set][char_set] + +Check that `_CRT_SECURE_NO_DEPRECATE` and if targeting an x64 build `VB_X64` are in the preprocessor definitions. +![Preprocessor Definitions][pp_def] + +Ensure `Ws2_32.lib` is included as an additional dependency. +![Linker dependencies][linker] + +[release]: vmr_streamer/img/vm1-inst.png +[properties]: vmr_streamer/img/vm2-inst.png +[char_set]: vmr_streamer/img/vm3-inst.png +[pp_def]: vmr_streamer/img/vm4-inst.png +[linker]: vmr_streamer/img/vm5-inst.png diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c75366d --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License (with one restriction) + +Copyright (c) 2021 Vincent Burel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software and the created Software Application +must interact with the VoicemeeterRemote API. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..03e5463 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# Voicemeeter Streamer View App + +Source files for [Voicemeeter Streamer View App][streamer_view_app] which can be found in the [applications SDK](https://github.com/vburel2018/Voicemeeter-SDK). + +### Build + +For a step-by-step follow [these instructions][install]. + +### Special Thanks + +Vincent Burel for creating Voicemeeter, its SDK and the various apps released as part of it. + +[install]: INSTALL.md +[streamer_view_app]: https://voicemeeter.com/the-voicemeeter-streamer-view-app-overview/ diff --git a/vmr_streamer.sln b/vmr_streamer.sln new file mode 100644 index 0000000..723514c --- /dev/null +++ b/vmr_streamer.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33723.286 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vmr_streamer", "vmr_streamer\vmr_streamer.vcxproj", "{CB959FDD-2AA4-4ECD-8A50-41777BF38EEA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CB959FDD-2AA4-4ECD-8A50-41777BF38EEA}.Debug|x64.ActiveCfg = Debug|x64 + {CB959FDD-2AA4-4ECD-8A50-41777BF38EEA}.Debug|x64.Build.0 = Debug|x64 + {CB959FDD-2AA4-4ECD-8A50-41777BF38EEA}.Debug|x86.ActiveCfg = Debug|Win32 + {CB959FDD-2AA4-4ECD-8A50-41777BF38EEA}.Debug|x86.Build.0 = Debug|Win32 + {CB959FDD-2AA4-4ECD-8A50-41777BF38EEA}.Release|x64.ActiveCfg = Release|x64 + {CB959FDD-2AA4-4ECD-8A50-41777BF38EEA}.Release|x64.Build.0 = Release|x64 + {CB959FDD-2AA4-4ECD-8A50-41777BF38EEA}.Release|x86.ActiveCfg = Release|Win32 + {CB959FDD-2AA4-4ECD-8A50-41777BF38EEA}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {00B208A7-9AA5-482F-9929-0DE2CB5EA336} + EndGlobalSection +EndGlobal diff --git a/vmr_streamer/Resource.h b/vmr_streamer/Resource.h new file mode 100644 index 0000000..af1e53f --- /dev/null +++ b/vmr_streamer/Resource.h @@ -0,0 +1,30 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by vmr_streamer.rc + +#define IDS_APP_TITLE 103 + +#define IDR_MAINFRAME 128 +#define IDD_VMRSTREAMER_DIALOG 102 +#define IDD_ABOUTBOX 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define IDI_VMRSTREAMER 107 +#define IDI_SMALL 108 +#define IDC_VMRSTREAMER 109 +#define IDC_MYICON 2 +#ifndef IDC_STATIC +#define IDC_STATIC -1 +#endif +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS + +#define _APS_NO_MFC 130 +#define _APS_NEXT_RESOURCE_VALUE 129 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/vmr_streamer/framework.h b/vmr_streamer/framework.h new file mode 100644 index 0000000..33a6be6 --- /dev/null +++ b/vmr_streamer/framework.h @@ -0,0 +1,15 @@ +// header.h : include file for standard system include files, +// or project specific include files +// + +#pragma once + +#include "targetver.h" +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files +#include +// C RunTime Header Files +#include +#include +#include +#include diff --git a/vmr_streamer/img/vm1-inst.png b/vmr_streamer/img/vm1-inst.png new file mode 100644 index 0000000..60a66f3 Binary files /dev/null and b/vmr_streamer/img/vm1-inst.png differ diff --git a/vmr_streamer/img/vm2-inst.png b/vmr_streamer/img/vm2-inst.png new file mode 100644 index 0000000..73dcd96 Binary files /dev/null and b/vmr_streamer/img/vm2-inst.png differ diff --git a/vmr_streamer/img/vm3-inst.png b/vmr_streamer/img/vm3-inst.png new file mode 100644 index 0000000..311ffe4 Binary files /dev/null and b/vmr_streamer/img/vm3-inst.png differ diff --git a/vmr_streamer/img/vm4-inst.png b/vmr_streamer/img/vm4-inst.png new file mode 100644 index 0000000..177a1ef Binary files /dev/null and b/vmr_streamer/img/vm4-inst.png differ diff --git a/vmr_streamer/img/vm5-inst.png b/vmr_streamer/img/vm5-inst.png new file mode 100644 index 0000000..a6d70e7 Binary files /dev/null and b/vmr_streamer/img/vm5-inst.png differ diff --git a/vmr_streamer/small.ico b/vmr_streamer/small.ico new file mode 100644 index 0000000..b3ec03b Binary files /dev/null and b/vmr_streamer/small.ico differ diff --git a/vmr_streamer/source/SourceCodeOrganization.jpg b/vmr_streamer/source/SourceCodeOrganization.jpg new file mode 100644 index 0000000..9a63c2c Binary files /dev/null and b/vmr_streamer/source/SourceCodeOrganization.jpg differ diff --git a/vmr_streamer/source/StripCTL_Design.jpg b/vmr_streamer/source/StripCTL_Design.jpg new file mode 100644 index 0000000..793caea Binary files /dev/null and b/vmr_streamer/source/StripCTL_Design.jpg differ diff --git a/vmr_streamer/source/Thumbs.db b/vmr_streamer/source/Thumbs.db new file mode 100644 index 0000000..1592273 Binary files /dev/null and b/vmr_streamer/source/Thumbs.db differ diff --git a/vmr_streamer/source/VoicemeeterRemote.h b/vmr_streamer/source/VoicemeeterRemote.h new file mode 100644 index 0000000..82224fb --- /dev/null +++ b/vmr_streamer/source/VoicemeeterRemote.h @@ -0,0 +1,974 @@ +/******************************************************************************/ +/* Voicemeeter Remote API. V.Burel©2015-2021 */ +/******************************************************************************/ +/* This Library allows communication with Voicemeeter applications */ +/* 4 Client Applications can be connected to remote Voicemeeter. */ +/* it also allows communication with MacroButtons application. */ +/******************************************************************************/ +/* */ +/* OFFICIAL LINK : WWW.VOICEMEETER.COM */ +/* */ +/******************************************************************************/ +/* COPYRIGHT: Vincent Burel(c)2015-2021 All Rights Reserved */ +/******************************************************************************/ +/* */ +/* LICENSING: VoicemeeterRemote.dll usage is driven by a license agreement */ +/* given in VoicemeeterRemoteAPI.pdf or readme.txt */ +/* */ +/******************************************************************************/ +/* long = 32 bit integer */ +/******************************************************************************/ + + +#ifndef __VOICEMEETER_REMOTE_H__ +#define __VOICEMEETER_REMOTE_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +#define VBVMR_RESULT_OK 0 + +/******************************************************************************/ +/* */ +/* Login */ +/* */ +/******************************************************************************/ + +/** @name Communication Login / logout +* @{ */ + + /** + @brief Open Communication Pipe With Voicemeeter (typically called on software startup). + @return : 0: OK (no error). + 1: OK but Voicemeeter Application not launched. + -1: cannot get client (unexpected) + -2: unexpected login (logout was expected before). + */ + +long __stdcall VBVMR_Login(void); + + /** + @brief Close Communication Pipe With Voicemeeter (typically called on software end). + @return : 0 if ok. + */ + +long __stdcall VBVMR_Logout(void); + + + /** + @brief Run Voicemeeter Application (get installation directory and run Voicemeeter Application). + @param vType : Voicemeeter type (1 = Voicemeeter, 2= Voicemeeter Banana, 3= Voicemeeter Potato, 6 = Potato x64 bits). + @return : 0: Ok. + -1: not installed (UninstallString not found in registry). + -2: unknown vType number + */ + +long __stdcall VBVMR_RunVoicemeeter(long vType); + + +/** @} */ + + + + + + + + + + + +/******************************************************************************/ +/* */ +/* General Information */ +/* */ +/******************************************************************************/ + +/** @name General Information +* @{ */ + + /** + @brief Get Voicemeeter Type + @param pType : Pointer on 32bit long receiving the type (1 = Voicemeeter, 2= Voicemeeter Banana, 3 Potato). + + VOICEMEETER STRIP/BUS INDEX ASSIGNMENT + + | Strip 1 | Strip 2 |Virtual Input| BUS A | BUS B | + +---------+---------+-------------+---------+---------+ + | 0 | 1 | 2 | 0 | 1 | + + VOICEMEETER BANANA STRIP/BUS INDEX ASSIGNMENT + + | Strip 1 | Strip 2 | Strip 2 |Virtual Input|Virtual AUX|BUS A1|BUS A2|BUS A3|BUS B1|BUS B2| + +---------+---------+---------+-------------+-----------+------+------+------+------+------+ + | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 2 | 3 | 4 | + + VOICEMEETER POTATO STRIP/BUS INDEX ASSIGNMENT + + | Strip 1 | Strip 2 | Strip 2 | Strip 2 | Strip 2 |Virtual Input|Virtual AUX| VAIO3 |BUS A1|BUS A2|BUS A3|BUS A4|BUS A5|BUS B1|BUS B2|BUS B3| + +---------+---------+---------+---------+---------+-------------+-----------+-----------+------+------+------+------+------+------+------+------+ + | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | + + + @return : 0: OK (no error). + -1: cannot get client (unexpected) + -2: no server. + */ + +long __stdcall VBVMR_GetVoicemeeterType(long * pType); + + /** + @brief Get Voicemeeter Version + @param pType : Pointer on 32bit integer receiving the version (v1.v2.v3.v4) + v1 = (version & 0xFF000000)>>24; + v2 = (version & 0x00FF0000)>>16; + v3 = (version & 0x0000FF00)>>8; + v4 = version & 0x000000FF; + + @return : 0: OK (no error). + -1: cannot get client (unexpected) + -2: no server. + */ + +long __stdcall VBVMR_GetVoicemeeterVersion(long * pVersion); + + +/** @} */ + + + + + + + + + + + + + + + + + + +/******************************************************************************/ +/* */ +/* Get parameters */ +/* */ +/******************************************************************************/ + +/** @name Getting Parameters +* @{ */ + + /** + @brief Check if parameters have changed. + Call this function periodically (typically every 10 or 20ms). + (this function must be called from one thread only) + + @return: 0: no new paramters. + 1: New parameters -> update your display. + -1: error (unexpected) + -2: no server. + */ + +long __stdcall VBVMR_IsParametersDirty(void); + + /** + @brief get parameter value. + @param szParamName : Null Terminal ASCII String giving the name of the parameter (see parameters name table) + @param pValue : Pointer on float (32bit float by reference) receiving the wanted value. + @return : 0: OK (no error). + -1: error + -2: no server. + -3: unknown parameter + -5: structure mismatch + */ + +long __stdcall VBVMR_GetParameterFloat(char * szParamName, float * pValue); + + /** + @brief get parameter value. + @param szParamName : Null Terminal ASCII String giving the name of the parameter (see parameters name table) + @param pValue : Pointer on String (512 char or wchar) receiving the wanted value. + @return : 0: OK (no error). + -1: error + -2: no server. + -3: unknown parameter + -5: structure mismatch + */ + +long __stdcall VBVMR_GetParameterStringA(char * szParamName, char * szString); +long __stdcall VBVMR_GetParameterStringW(char * szParamName, unsigned short * wszString); + +/** @} */ + + + + + + + + + +/******************************************************************************/ +/* */ +/* Get levels */ +/* */ +/******************************************************************************/ + +/** @name Getting RT Data +* @{ */ + + /** + @brief Get Current levels. + (this function must be called from one thread only) + + @param nType: 0= pre fader input levels. + 1= post fader input levels. + 2= post Mute input levels. + 3= output levels. + + @param nuChannel: audio channel zero based index + for input 0 = in#1 left, 1= in#1 Right, etc... + for output 0 = busA ch1, 1 = busA ch2... + + VOICEMEETER CHANNEL ASSIGNMENT + + | Strip 1 | Strip 2 | Virtual Input | + +----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | + + | Output A1 / A2 | Virtual Output | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | + + VOICEMEETER BANANA CHANNEL ASSIGNMENT + + | Strip 1 | Strip 2 | Strip 3 | Virtual Input | Virtual Input AUX | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | + + | Output A1 | Output A2 | Output A3 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | + + | Virtual Output B1 | Virtual Output B2 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | + + VOICEMEETER POTATO CHANNEL ASSIGNMENT + + | Strip 1 | Strip 2 | Strip 3 | Strip 4 | Strip 5 | Virtual Input | Virtual Input AUX | VAIO3 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 25 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | + + | Output A1 | Output A2 | Output A3 | Output A4 | Output A5 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | + + | Virtual Output B1 | Virtual Output B2 | Virtual Output B3 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | + + + @return : 0: OK (no error). + -1: error + -2: no server. + -3: no level available + -4: out of range + */ + +long __stdcall VBVMR_GetLevel(long nType, long nuChannel, float * pValue); + + + /** + @brief Get MIDI message from M.I.D.I. input device used by Voicemeeter M.I.D.I. mapping. + (this function must be called from one thread only) + + @param pMIDIBuffer: pointer on MIDI Buffer. Expected message size is below 4 bytes, + but it's recommended to use 1024 Bytes local buffer to receive + possible multiple M.I.D.I. event message in optimal way: + unsigned char pBuffer[1024]; + + + @return : >0: number of bytes placed in buffer (2 or 3 byte for usual M.I.D.I. message) + -1: error + -2: no server. + -5: no MIDI data + -6: no MIDI data + */ + + +long __stdcall VBVMR_GetMidiMessage(unsigned char *pMIDIBuffer, long nbByteMax); + + +/** @} */ + + + + + + + + + + +/******************************************************************************/ +/* */ +/* Set Parameters */ +/* */ +/******************************************************************************/ + +/** @name Setting Parameters +* @{ */ + + /** + @brief Set a single float 32 bits parameters . + @param szParamName : Null Terminal ASCII String giving the name of the parameter (see parameters name table) + example: + Strip[1].gain + Strip[0].mute + Bus[0].gain + Bus[0].eq.channel[0].cell[0].gain + + @param pValue : float 32bit containing the new value. + @return : 0: OK (no error). + -1: error + -2: no server. + -3: unknown parameter + */ + +long __stdcall VBVMR_SetParameterFloat(char * szParamName, float Value); + + + + /** + @brief Set a single string parameters . + @param szParamName : Null Terminal ASCII String giving the name of the parameter (see parameters name table) + example: + Strip[1].name + Strip[0].device.mme + Bus[0].device.asio + + @param szString : zero terminal string. + @return : 0: OK (no error). + -1: error + -2: no server. + -3: unknown parameter + + */ + + +long __stdcall VBVMR_SetParameterStringA(char * szParamName, char * szString); +long __stdcall VBVMR_SetParameterStringW(char * szParamName, unsigned short * wszString); + + + + + /** + @brief Set one or several parameters by a script ( < 48 kB ). + @param szParamName : Null Terminal ASCII String giving the script + (script allows to change several parameters in the same time - SYNCHRO). + Possible Instuction separators: ',' ';' or '\n'(CR) + EXAMPLE: + "Strip[0].gain = -6.0 + Strip[0].A1 = 0 + Strip[0].B1 = 1 + Strip[1].gain = -6.0 + Strip[2].gain = 0.0 + Strip[3].name = "Skype Caller" " + + @return : 0: OK (no error). + >0: number of line causing script error. + -1: error + -2: no server. + -3: unexpected error + -4: unexpected error + */ + +long __stdcall VBVMR_SetParameters(char * szParamScript); +long __stdcall VBVMR_SetParametersW(unsigned short * szParamScript); + + +/** @} */ + + + + + + + + + + + +/******************************************************************************/ +/* DEVICES ENUMERATOR */ +/******************************************************************************/ + + +/** @name Device Enumeration Functions +* @{ */ + +#define VBVMR_DEVTYPE_MME 1 +#define VBVMR_DEVTYPE_WDM 3 +#define VBVMR_DEVTYPE_KS 4 +#define VBVMR_DEVTYPE_ASIO 5 + + /** + @brief Get number of Audio Output Device available on the system + @return : return number of device found. + */ + +long __stdcall VBVMR_Output_GetDeviceNumber(void); + + /** + @brief Return pointer on Output Device Descriptor according index + @param zindex : zero based index + @param nType : Pointer on 32bit long receiving the type (pointer can be NULL). + @param szName : Pointer on string (256 char min) receiving the device name (pointer can be NULL). + @param szHardwareId : Pointer on string (256 char min) receiving the hardware ID (pointer can be NULL). + @return : 0: OK (no error). + */ + +long __stdcall VBVMR_Output_GetDeviceDescA(long zindex, long * nType, char * szDeviceName, char * szHardwareId); +long __stdcall VBVMR_Output_GetDeviceDescW(long zindex, long * nType, unsigned short * wszDeviceName, unsigned short * wszHardwareId); + + /** + @brief Get number of Audio Input Device available on the system + @return : return number of device found. + */ + +long __stdcall VBVMR_Input_GetDeviceNumber(void); + + /** + @brief Return pointer on Input Device Descriptor according index + @param zindex : zero based index + @param nType : Pointer on 32bit long receiving the type (pointer can be NULL). + @param szName : Pointer on string (256 char min) receiving the device name (pointer can be NULL). + @param szHardwareId : Pointer on string (256 char min) receiving the hardware ID (pointer can be NULL). + @return : 0: OK (no error). + */ + +long __stdcall VBVMR_Input_GetDeviceDescA(long zindex, long * nType, char * szDeviceName, char * szHardwareId); +long __stdcall VBVMR_Input_GetDeviceDescW(long zindex, long * nType, unsigned short * wszDeviceName, unsigned short * wszHardwareId); + + + +/** @} */ + + + + + + +/******************************************************************************/ +/* VB-AUDIO CALLBACK */ +/******************************************************************************/ +/* 4x Functions to process all voicemeeter audio input and output channels */ +/* */ +/* VBVMR_AudioCallbackRegister :to register your audio callback(s) */ +/* VBVMR_AudioCallbackStart :to start the audio stream */ +/* VBVMR_AudioCallbackStop :to stop the audio stream */ +/* VBVMR_AudioCallbackUnregister :to unregister / Release callback(s) */ +/******************************************************************************/ + +/** @name VB-Audio Callback Functions +* @{ */ + + +typedef struct tagVBVMR_AUDIOINFO +{ + long samplerate; + long nbSamplePerFrame; +} VBVMR_T_AUDIOINFO, *VBVMR_PT_AUDIOINFO, *VBVMR_LPT_AUDIOINFO; + + +typedef struct tagVBVMR_AUDIOBUFFER +{ + long audiobuffer_sr; //Sampling Rate + long audiobuffer_nbs; //number of sample per frame + long audiobuffer_nbi; //number of inputs + long audiobuffer_nbo; //number of outputs + float * audiobuffer_r[128]; //nbi input pointers containing frame of nbs sample (of 32bits float) + float * audiobuffer_w[128]; //nbo output pointers containing frame of nbs sample (of 32bits float) +} VBVMR_T_AUDIOBUFFER, *VBVMR_PT_AUDIOBUFFER, *VBVMR_LPT_AUDIOBUFFER; + + + /** + @brief VB-AUDIO Callback is called for different task to Initialize, perform and end your process. + VB-AUDIO Callback is part of single TIME CRITICAL Thread. + VB-AUDIO Callback is non re-entrant (cannot be called while in process) + VB-AUDIO Callback is supposed to be REAL TIME when called to process buffer. + (it means that the process has to be performed as fast as possible, waiting cycles are forbidden. + do not use O/S synchronization object, even Critical_Section can generate waiting cycle. Do not use + system functions that can generate waiting cycle like display, disk or communication functions for example). + + @param lpUser: User pointer given on callback registration. + @param ncommand: reason why the callback is called. + @param lpData: pointer on structure, pending on nCommand. + @param nnn: additional data, unused + + @return : 0: always 0 (unused). + */ + + +typedef long (__stdcall *T_VBVMR_VBAUDIOCALLBACK)(void * lpUser, long nCommand, void * lpData, long nnn); + + +#define VBVMR_CBCOMMAND_STARTING 1 //command to initialize data according SR and buffer size + //info = (VBVMR_LPT_AUDIOINFO)lpData + +#define VBVMR_CBCOMMAND_ENDING 2 //command to release data +#define VBVMR_CBCOMMAND_CHANGE 3 //If change in audio stream, you will have to restart audio + +#define VBVMR_CBCOMMAND_BUFFER_IN 10 //input insert +#define VBVMR_CBCOMMAND_BUFFER_OUT 11 //bus output insert +#define VBVMR_CBCOMMAND_BUFFER_MAIN 20 //all i/o + //audiobuffer = (VBVMR_LPT_AUDIOBUFFER)lpData + //nnn = synchro = 1 if synchro with Voicemeeter + +/* + ----------------------------------------------------- + AUDIO BUFFER for VBVMR_CBCOMMAND_BUFFER_IN + ----------------------------------------------------- + VOICEMEETER + + | Strip 1 | Strip 2 | Virtual Input | + +----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | + + VOICEMEETER BANANA + + | Strip 1 | Strip 2 | Strip 3 | Virtual Input | Virtual Input AUX | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | + + VOICEMEETER 8 + + | Strip 1 | Strip 2 | Strip 3 | Strip 4 | Strip 5 | Virtual Input | Virtual Input AUX | Virtual Input 8 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | + + +----------------------------------------------------- + AUDIO BUFFER for VBVMR_CBCOMMAND_BUFFER_OUT +----------------------------------------------------- + VOICEMEETER + + | Output A1 / A2 | Virtual Output | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | + + VOICEMEETER BANANA + + | Output A1 | Output A2 | Output A3 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | + + | Virtual Output B1 | Virtual Output B2 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | + + + VOICEMEETER 8 + + | Output A1 | Output A2 | Output A3 | Output A4 | Output A5 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | + + | Virtual Output B1 | Virtual Output B2 | Virtual Output B3 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | + + +----------------------------------------------------- + AUDIO BUFFER for VBVMR_CBCOMMAND_BUFFER_MAIN +----------------------------------------------------- + VOICEMEETER + + | Strip 1 | Strip 2 | Virtual Input | + +----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | + + | Output A1 / A2 | Virtual Output | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | + Output buffer provides outputs only: + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | + + VOICEMEETER BANANA + + | Strip 1 | Strip 2 | Strip 3 | Virtual Input | Virtual Input AUX | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | + + | Output A1 | Output A2 | Output A3 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | + Output buffer provides outputs only: + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | + + | Virtual Output B1 | Virtual Output B2 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | + Output buffer provides outputs only: + | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | + + + VOICEMEETER 8 + + | Strip 1 | Strip 2 | Strip 3 | Strip 4 | Strip 5 | Virtual Input | Virtual Input AUX | Virtual Input 8 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | + + | Output A1 | Output A2 | Output A3 | Output A4 | Output A5 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | + Output buffer provides outputs only: + | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | + + | Virtual Output B1 | Virtual Output B2 | Virtual Output B3 | + +----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+ + | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | + Output buffer provides outputs only: + | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | + + +*/ + + /** + @brief register your audio callback function to receive real time audio buffer + it's possible to register up to 3x different Audio Callback in the same application or in + 3x different applications. In the same application, this is possible because Voicemeeter + provides 3 kind of audio Streams: + - AUDIO INPUT INSERT (to process all Voicemeeter inputs as insert) + - AUDIO OUTPUT INSERT (to process all Voicemeeter BUS outputs as insert) + - ALL AUDIO I/O (to process all Voicemeeter i/o). + Note: a single callback can be used to receive the 3 possible audio streams. + + @param mode : callback type (main, input or bus output) see define below + @param pCallback : Pointer on your callback function. + @param lpUser : user pointer (pointer that will be passed in callback first argument). + @param szClientName[64]: IN: Name of the application registering the Callback. + OUT: Name of the application already registered. + @return : 0: OK (no error). + -1: error + 1: callback already registered (by another application). + */ + +long __stdcall VBVMR_AudioCallbackRegister(long mode, T_VBVMR_VBAUDIOCALLBACK pCallback, void * lpUser, char szClientName[64]); + +#define VBVMR_AUDIOCALLBACK_IN 0x00000001 //to process input insert +#define VBVMR_AUDIOCALLBACK_OUT 0x00000002 //to process output bus insert +#define VBVMR_AUDIOCALLBACK_MAIN 0x00000004 //to receive all i/o + + /** + @brief Start / Stop Audio processing + + he Callback will be called with + @return : 0: OK (no error). + -1: error + -2: no callback registred. + */ + +long __stdcall VBVMR_AudioCallbackStart(void); +long __stdcall VBVMR_AudioCallbackStop(void); + + + /** + @brief unregister your callback to release voicemeeter virtual driver + (this function will automatically call VBVMR_AudioCallbackStop() function) + @param pCallback : Pointer on your callback function. + @return : 0: OK (no error). + -1: error + 1: callback already unregistered. + */ + +long __stdcall VBVMR_AudioCallbackUnregister(void); + + + +/** @} */ + + + + + + +/******************************************************************************/ +/* */ +/* Macro Buttons */ +/* */ +/******************************************************************************/ + +/** @name Macro Buttons functions +* @{ */ + + /** + @brief Check if Macro Buttons states changed. + Call this function periodically (typically every 50 or 500ms) to know if something happen on MacroButton states . + (this function must be called from one thread only) + + @return: 0: no new status. + >0: last nu logical button status changed. + -1: error (unexpected) + -2: no server. + */ + +long __stdcall VBVMR_MacroButton_IsDirty(void); + + /** + @brief get current status of a given button. + @param nuLogicalButton : button index: 0 to 79) + @param pValue : Pointer on float (32bit float by reference) receiving the wanted value (0.0 = OFF / 1.0 = ON). + @param bitmode: define what kind of value you want to read (see MACROBUTTON_MODE below) + @return : 0: OK (no error). + -1: error + -2: no server. + -3: unknown parameter + -5: structure mismatch + */ + +long __stdcall VBVMR_MacroButton_GetStatus(long nuLogicalButton, float * pValue, long bitmode); + + /** + @brief set current button value. + @param nuLogicalButton : button index: 0 to 79) + @param fValue : float 32 bit value giving the status (0.0 = OFF / 1.0 = ON). + @param bitmode: define what kind of value you want to write/modify (see MACROBUTTON_MODE below) + @return : 0: OK (no error). + -1: error + -2: no server. + -3: unknown parameter + -5: structure mismatch + */ + +long __stdcall VBVMR_MacroButton_SetStatus(long nuLogicalButton, float fValue, long bitmode); + +#define VBVMR_MACROBUTTON_MODE_DEFAULT 0x00000000 //PUSH or RELEASE button +#define VBVMR_MACROBUTTON_MODE_STATEONLY 0x00000002 //change Displayed State only +#define VBVMR_MACROBUTTON_MODE_TRIGGER 0x00000003 //change Trigger State + + + +/** @} */ + + + + + + + + + +/******************************************************************************/ +/* 'C' STRUCTURED INTERFACE */ +/******************************************************************************/ + +typedef long (__stdcall *T_VBVMR_Login)(void); +typedef long (__stdcall *T_VBVMR_Logout)(void); +typedef long (__stdcall *T_VBVMR_RunVoicemeeter)(long vType); + +typedef long (__stdcall *T_VBVMR_GetVoicemeeterType)(long * pType); +typedef long (__stdcall *T_VBVMR_GetVoicemeeterVersion)(long * pVersion); + +typedef long (__stdcall *T_VBVMR_IsParametersDirty)(void); +typedef long (__stdcall *T_VBVMR_GetParameterFloat)(char * szParamName, float * pValue); +typedef long (__stdcall *T_VBVMR_GetParameterStringA)(char * szParamName, char * szString); +typedef long (__stdcall *T_VBVMR_GetParameterStringW)(char * szParamName, unsigned short * wszString); + + +typedef long (__stdcall *T_VBVMR_GetLevel)(long nType, long nuChannel, float * pValue); +typedef long (__stdcall *T_VBVMR_GetMidiMessage)(unsigned char *pMIDIBuffer, long nbByteMax); + +typedef long (__stdcall *T_VBVMR_SetParameterFloat)(char * szParamName, float Value); +typedef long (__stdcall *T_VBVMR_SetParameters)(char * szParamScript); +typedef long (__stdcall *T_VBVMR_SetParametersW)(unsigned short * szParamScript); +typedef long (__stdcall *T_VBVMR_SetParameterStringA)(char * szParamName, char * szString); +typedef long (__stdcall *T_VBVMR_SetParameterStringW)(char * szParamName, unsigned short * wszString); + +typedef long (__stdcall *T_VBVMR_Output_GetDeviceNumber)(void); +typedef long (__stdcall *T_VBVMR_Output_GetDeviceDescA)(long zindex, long * nType, char * szDeviceName, char * szHardwareId); +typedef long (__stdcall *T_VBVMR_Output_GetDeviceDescW)(long zindex, long * nType, unsigned short * wszDeviceName, unsigned short * wszHardwareId); +typedef long (__stdcall *T_VBVMR_Input_GetDeviceNumber)(void); +typedef long (__stdcall *T_VBVMR_Input_GetDeviceDescA)(long zindex, long * nType, char * szDeviceName, char * szHardwareId); +typedef long (__stdcall *T_VBVMR_Input_GetDeviceDescW)(long zindex, long * nType, unsigned short * wszDeviceName, unsigned short * wszHardwareId); + +typedef long (__stdcall *T_VBVMR_AudioCallbackRegister)(long mode, T_VBVMR_VBAUDIOCALLBACK pCallback, void * lpUser, char szClientName[64]); +typedef long (__stdcall *T_VBVMR_AudioCallbackStart)(void); +typedef long (__stdcall *T_VBVMR_AudioCallbackStop)(void); +typedef long (__stdcall *T_VBVMR_AudioCallbackUnregister)(void); + +typedef long (__stdcall *T_VBVMR_MacroButton_IsDirty)(void); +typedef long (__stdcall *T_VBVMR_MacroButton_GetStatus)(long nuLogicalButton, float * pValue, long bitmode); +typedef long (__stdcall *T_VBVMR_MacroButton_SetStatus)(long nuLogicalButton, float fValue, long bitmode); + + + +typedef struct tagVBVMR_INTERFACE +{ + T_VBVMR_Login VBVMR_Login; + T_VBVMR_Logout VBVMR_Logout; + T_VBVMR_RunVoicemeeter VBVMR_RunVoicemeeter; + T_VBVMR_GetVoicemeeterType VBVMR_GetVoicemeeterType; + T_VBVMR_GetVoicemeeterVersion VBVMR_GetVoicemeeterVersion; + T_VBVMR_IsParametersDirty VBVMR_IsParametersDirty; + T_VBVMR_GetParameterFloat VBVMR_GetParameterFloat; + T_VBVMR_GetParameterStringA VBVMR_GetParameterStringA; + T_VBVMR_GetParameterStringW VBVMR_GetParameterStringW; + + T_VBVMR_GetLevel VBVMR_GetLevel; + T_VBVMR_GetMidiMessage VBVMR_GetMidiMessage; + + T_VBVMR_SetParameterFloat VBVMR_SetParameterFloat; + T_VBVMR_SetParameters VBVMR_SetParameters; + T_VBVMR_SetParametersW VBVMR_SetParametersW; + T_VBVMR_SetParameterStringA VBVMR_SetParameterStringA; + T_VBVMR_SetParameterStringW VBVMR_SetParameterStringW; + + T_VBVMR_Output_GetDeviceNumber VBVMR_Output_GetDeviceNumber; + T_VBVMR_Output_GetDeviceDescA VBVMR_Output_GetDeviceDescA; + T_VBVMR_Output_GetDeviceDescW VBVMR_Output_GetDeviceDescW; + T_VBVMR_Input_GetDeviceNumber VBVMR_Input_GetDeviceNumber; + T_VBVMR_Input_GetDeviceDescA VBVMR_Input_GetDeviceDescA; + T_VBVMR_Input_GetDeviceDescW VBVMR_Input_GetDeviceDescW; + + T_VBVMR_AudioCallbackRegister VBVMR_AudioCallbackRegister; + T_VBVMR_AudioCallbackStart VBVMR_AudioCallbackStart; + T_VBVMR_AudioCallbackStop VBVMR_AudioCallbackStop; + T_VBVMR_AudioCallbackUnregister VBVMR_AudioCallbackUnregister; + + T_VBVMR_MacroButton_IsDirty VBVMR_MacroButton_IsDirty; + T_VBVMR_MacroButton_GetStatus VBVMR_MacroButton_GetStatus; + T_VBVMR_MacroButton_SetStatus VBVMR_MacroButton_SetStatus; + +} T_VBVMR_INTERFACE, *PT_VBVMR_INTERFACE, *LPT_VBVMR_INTERFACE; + +#ifdef VBUSE_LOCALLIB + // internal used (not public) + void __stdcall VBVMR_SetHinstance(HINSTANCE hinst); +#endif + + + + +/******************************************************************************/ +/* VBAN RT PACKET */ +/******************************************************************************/ + +#pragma pack(1) + +// COMPATIBILITY: defined structure cannot be changed. +// some field could be added at the end of the structure to keep the compatibility in the time. + +typedef struct tagVBAN_VMRT_PACKET +{ + unsigned char voicemeeterType; // 1 = Voicemeeter, 2= Voicemeeter Banana, 3 Potato + unsigned char reserved; // unused + unsigned short buffersize; // main stream buffer size + unsigned long voicemeeterVersion; // version like for VBVMR_GetVoicemeeterVersion() functino + unsigned long optionBits; // unused + unsigned long samplerate; // main stream samplerate + short inputLeveldB100[34]; // pre fader input peak level in dB * 100 + short outputLeveldB100[64]; // bus output peak level in dB * 100 + unsigned long TransportBit; // Transport Status + unsigned long stripState[8]; // Strip Buttons Status (see MODE bits below) + unsigned long busState[8]; // Bus Buttons Status (see MODE bits below) + short stripGaindB100Layer1[8]; // Strip Gain in dB * 100 + short stripGaindB100Layer2[8]; + short stripGaindB100Layer3[8]; + short stripGaindB100Layer4[8]; + short stripGaindB100Layer5[8]; + short stripGaindB100Layer6[8]; + short stripGaindB100Layer7[8]; + short stripGaindB100Layer8[8]; + short busGaindB100[8]; // Bus Gain in dB * 100 + char stripLabelUTF8c60[8][60]; // Strip Label + char busLabelUTF8c60[8][60]; // Bus Label +} T_VBAN_VMRT_PACKET, *PT_VBAN_VMRT_PACKET, *LPT_VBAN_VMRT_PACKET; + +#define expected_size_T_VBAN_VMRT_PACKET 1384 //1436 max + +#pragma pack() + +#define VMRTSTATE_MODE_MUTE 0x00000001 +#define VMRTSTATE_MODE_SOLO 0x00000002 +#define VMRTSTATE_MODE_MONO 0x00000004 +#define VMRTSTATE_MODE_MUTEC 0x00000008 + +#define VMRTSTATE_MODE_MIXDOWN 0x00000010 +#define VMRTSTATE_MODE_REPEAT 0x00000020 +#define VMRTSTATE_MODE_MIXDOWNB 0x00000030 +#define VMRTSTATE_MODE_COMPOSITE 0x00000040 +#define VMRTSTATE_MODE_UPMIXTV 0x00000050 +#define VMRTSTATE_MODE_UPMIX2 0x00000060 +#define VMRTSTATE_MODE_UPMIX4 0x00000070 +#define VMRTSTATE_MODE_UPMIX6 0x00000080 +#define VMRTSTATE_MODE_CENTER 0x00000090 +#define VMRTSTATE_MODE_LFE 0x000000A0 +#define VMRTSTATE_MODE_REAR 0x000000B0 + +#define VMRTSTATE_MODE_MASK 0x000000F0 + +#define VMRTSTATE_MODE_EQ 0x00000100 +#define VMRTSTATE_MODE_CROSS 0x00000200 +#define VMRTSTATE_MODE_EQB 0x00000800 + +#define VMRTSTATE_MODE_BUSA 0x00001000 +#define VMRTSTATE_MODE_BUSA1 0x00001000 +#define VMRTSTATE_MODE_BUSA2 0x00002000 +#define VMRTSTATE_MODE_BUSA3 0x00004000 +#define VMRTSTATE_MODE_BUSA4 0x00008000 +#define VMRTSTATE_MODE_BUSA5 0x00080000 + +#define VMRTSTATE_MODE_BUSB 0x00010000 +#define VMRTSTATE_MODE_BUSB1 0x00010000 +#define VMRTSTATE_MODE_BUSB2 0x00020000 +#define VMRTSTATE_MODE_BUSB3 0x00040000 + +#define VMRTSTATE_MODE_PAN0 0x00000000 +#define VMRTSTATE_MODE_PANCOLOR 0x00100000 +#define VMRTSTATE_MODE_PANMOD 0x00200000 +#define VMRTSTATE_MODE_PANMASK 0x00F00000 + +#define VMRTSTATE_MODE_POSTFX_R 0x01000000 +#define VMRTSTATE_MODE_POSTFX_D 0x02000000 +#define VMRTSTATE_MODE_POSTFX1 0x04000000 +#define VMRTSTATE_MODE_POSTFX2 0x08000000 + +#define VMRTSTATE_MODE_SEL 0x10000000 +#define VMRTSTATE_MODE_MONITOR 0x20000000 + + + + + +/******************************************************************************/ +/* LOCAL FUNCTIONS */ +/******************************************************************************/ + +long VBVMR_LocalInit(void); +long VBVMR_LocalEnd(void); +void * VBVMR_GetRequestVB0STREAMPTR(void); + +long VBVMR_SetParametersWEx(unsigned short * szParamScript, long fCopyToClient); + +long VBVMR_LoginEx(long properties); + +long VBVMR_MB_PushSettings(void * lpParam); + + + + + +#ifdef __cplusplus +} +#endif + +#endif /*__VOICEMEETER_REMOTE_H__*/ + + diff --git a/vmr_streamer/source/streamer_ctrl.c b/vmr_streamer/source/streamer_ctrl.c new file mode 100644 index 0000000..3b5a48e --- /dev/null +++ b/vmr_streamer/source/streamer_ctrl.c @@ -0,0 +1,1924 @@ +/**********************************************************************************/ +/* VMSCTL V.Burel */ +/**********************************************************************************/ +/* Contains our custom windows control for vmr_streamer project */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* COMPILATION DIRECTIVES: */ +/* */ +/* To compile With Microsoft VC2005 or higher, you need to create an */ +/* empty Win32 project with the following options: */ +/* - Configuration Properties / General : Char Set = NOT SET */ +/* - Configuration Properties / C/C++ / Preprocessor : _CRT_SECURE_NO_DEPRECATE */ +/* */ +/* This source code can be compiled for 32bit or 64 bits targets as well */ +/* WARNING: FOR 64x COMPILATION, ADD PREPROCESSOR DEFINE: VB_X64 */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* LICENSING: VoicemeeterRemote.dll usage is driven by a license agreement */ +/* given in VoicemeeterRemoteAPI.pdf or readme.txt */ +/* This Source Code can be used only in a program using Voicemeeter */ +/* Remote API. */ +/* */ +/*--------------------------------------------------------------------------------*/ + + +#ifndef __cplusplus + #ifndef STRICT + #define STRICT + #endif +#endif + +#include +#include +#include "streamer_ctrl.h" + +static HINSTANCE G_hinstance=NULL; + + +#define PEAKMETER_RELEASE_DECREMENT 1.0f + +/*---------------------------------------------------------------*/ +/* TOOL */ +/*---------------------------------------------------------------*/ + +void TOOL_StorePointerInWindow(HWND hw,LPVOID pp) +{ +#ifdef VB_X64 + SetWindowLongPtr(hw,0,(LONG_PTR)pp); +#else + SetWindowLong(hw,0,(ULONG)pp); +#endif +} + +LPVOID TOOL_RecallPointerFromWindow(HWND hw) +{ +#ifdef VB_X64 + return (LPVOID)GetWindowLongPtr(hw,0); +#else + return (LPVOID)GetWindowLong(hw,0); +#endif +} + + + + + + + + + + + + + + + + + +/*---------------------------------------------------------------*/ +/* STRIP CONTROL */ +/*---------------------------------------------------------------*/ + +#define VMSCTL_STRIPBUS_MARGIN 5 +#define VMSCTL_STRIP_DY_BUTTON 20 +#define VMSCTL_STRIP_DY_ASSIGN 30 + +#define VMSCTL_STRIP_DX_NICKNAME 40 +#define VMSCTL_STRIP_DX_ASSIGN 30 + +#define VMSCTL_STRIP_DXMIN_PEAKMETER 100 +#define VMSCTL_STRIP_DYMIN_SLIDER 100 + + +typedef struct tagVMSCTL_STRIPCTX +{ + HWND hw; + long Ident; + long x0,y0,dx,dy; + long isShown; + + RECT rect_nickname; + RECT rect_name; + RECT rect_meters; + RECT rect_slider[VMSCTL_MAX_NBBUS]; + RECT rect_assign[VMSCTL_MAX_NBBUS]; + + long dx_meter; + long dy_meter; + long dx_slider; + long dx_name; + long margin_slider; + long dy_slider; + HBITMAP tempbmp; +// long SliderLinkMode; + + float start_value; + HWND LastCapture; + long mouse_y0; + long last_y0; + BOOL mouseCapture; + long ctlRunning; + long iStripRunning; + + T_VMSCTL_STRIPPARAM param; + T_VMSCTL_STRIP_DATA data; + T_VMSCTL_RT_DATA rtdata; +} T_VMSCTL_STRIPCTX, *PT_VMSCTL_STRIPCTX, *LPT_VMSCTL_STRIPCTX; + + + + +static void DrawSTRIP_NickNameMute(LPT_VMSCTL_STRIPCTX lpobject, HDC dc) +{ + char sss[64]; + RECT rect; + HFONT oldfont; + HPEN oldpen; + HBRUSH oldbrush; + if (dc == NULL) return; + + rect.left=VMSCTL_STRIPBUS_MARGIN; + rect.top=VMSCTL_STRIPBUS_MARGIN; + rect.right=rect.left+VMSCTL_STRIP_DX_NICKNAME; + rect.bottom=rect.top+VMSCTL_STRIP_DY_BUTTON; + lpobject->rect_nickname=rect; + + oldpen = (HPEN)SelectObject(dc,lpobject->param.assignbus_pen); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg1_brush); + oldfont = (HFONT)SelectObject(dc,lpobject->param.font0); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,lpobject->param.assignbus_color); + + if (lpobject->data.strip_mute != 0) + { + SetTextColor(dc,lpobject->param.mutered_color); + SelectObject(dc,lpobject->param.mutered_pen); + } + + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom, 5,5); + strcpy(sss,lpobject->data.strip_nickname); + DrawText(dc,sss,(long)strlen(sss),&rect,DT_SINGLELINE | DT_VCENTER | DT_CENTER); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); + +} + +static void DrawSTRIP_Name(LPT_VMSCTL_STRIPCTX lpobject, HDC dc) +{ + RECT rect; + HFONT oldfont; + HPEN oldpen; + HBRUSH oldbrush; + if (dc == NULL) return; + + rect.left=VMSCTL_STRIPBUS_MARGIN+VMSCTL_STRIP_DX_NICKNAME+10; + rect.top=VMSCTL_STRIPBUS_MARGIN; + rect.right=rect.left+lpobject->dx_name; + rect.bottom=rect.top+VMSCTL_STRIP_DY_BUTTON; + lpobject->rect_name=rect; + + oldpen = (HPEN)SelectObject(dc,GetStockObject(NULL_PEN)); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg1_brush); + oldfont = (HFONT)SelectObject(dc,lpobject->param.font2); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,lpobject->param.name_color); + + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom, 5,5); + DrawTextW(dc,lpobject->data.strip_namew,(long)wcslen(lpobject->data.strip_namew),&rect,DT_SINGLELINE | DT_VCENTER | DT_LEFT); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); +} + + +static void DrawSTRIP_PeakMetersBkg(LPT_VMSCTL_STRIPCTX lpobject, HDC dc) +{ + long dx; + RECT rect; + HPEN oldpen; + HBRUSH oldbrush; + if (dc == NULL) return; + + dx = lpobject->dx_meter; + rect.left=VMSCTL_STRIPBUS_MARGIN; + rect.top=VMSCTL_STRIPBUS_MARGIN+VMSCTL_STRIP_DY_BUTTON+10; + rect.right=rect.left+dx; + rect.bottom=rect.top+lpobject->dy_meter; + lpobject->rect_meters=rect; + + oldpen = (HPEN)SelectObject(dc,GetStockObject(BLACK_PEN)); + oldbrush = (HBRUSH)SelectObject(dc,GetStockObject(BLACK_BRUSH)); + + Rectangle(dc,rect.left,rect.top,rect.right,rect.bottom); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); +} + +static void DrawSTRIP_PeakMeters(LPT_VMSCTL_STRIPCTX lpobject, HDC dc) +{ + LPT_VMSCTL_STRIPPARAM lpp; + float dBValue, dBmin=-80.0f, dBmax=+12.0f; + long nu,nbMeter=2; + long dx,dy,xx,x1,xgreen,xred; + RECT rect; + HPEN oldpen; + HBRUSH oldbrush; + if (dc == NULL) return; + + dx = lpobject->dx_meter-6; + rect.left=VMSCTL_STRIPBUS_MARGIN; + rect.top=VMSCTL_STRIPBUS_MARGIN+VMSCTL_STRIP_DY_BUTTON+10; + rect.bottom=rect.top+lpobject->dy_meter; + dy = (rect.bottom-rect.top-4) / nbMeter; + + rect.left+=3; + rect.top+=3; + rect.right=rect.left+dx; + rect.bottom = rect.top+dy; + + oldpen = (HPEN)SelectObject(dc,GetStockObject(NULL_PEN)); + oldbrush = (HBRUSH)SelectObject(dc,GetStockObject(BLACK_BRUSH)); + xgreen = (long)(((-24.0f - dBmin) * dx)/ (dBmax - dBmin)); + xred = (long)(((0.0f - dBmin) * dx)/ (dBmax - dBmin)); + lpp = &(lpobject->param); + + for (nu=0;nurtdata.dbLevel[nu]; + xx = (long)(((dBValue - dBmin) * dx)/ (dBmax - dBmin)); + if (xx < 0) xx=0; + if (xx > dx) xx=dx; + + // blue + if (xx > 0) + { + SelectObject(dc,lpp->meter_brush1); + x1=xx; + if (x1 > xgreen) x1=xgreen; + Rectangle(dc,rect.left,rect.top,rect.left+x1+1,rect.bottom); + } + //green + if (xx >= xgreen) + { + SelectObject(dc,lpp->meter_brush2); + x1=xx; + if (x1 > xred) x1=xred; + Rectangle(dc,rect.left+xgreen,rect.top,rect.left+x1+1,rect.bottom); + } + //red + if (xx >= xred) + { + SelectObject(dc,lpp->meter_brush3); + x1=xx; + Rectangle(dc,rect.left+xred,rect.top,rect.left+x1+1,rect.bottom); + } + //black + if (xx <= dx) + { + SelectObject(dc,GetStockObject(BLACK_BRUSH)); + Rectangle(dc,rect.left+xx,rect.top,rect.right+1,rect.bottom); + } + + rect.top +=dy; + rect.bottom +=dy; + } + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); +} + +static void DrawSTRIP_BUSAssignmentButton(LPT_VMSCTL_STRIPCTX lpobject, HDC dc, long nuVisibleSlider, long fAssigned, char * szBUSName) +{ + long x0,y0, dx,dy; + long cursor_height=20; + RECT rect; + HPEN oldpen; + HBRUSH oldbrush; + HFONT oldfont; + if (dc == NULL) return; + //compute local coordintate + dx = lpobject->dx_slider; + dy = VMSCTL_STRIP_DY_ASSIGN; + x0=VMSCTL_STRIPBUS_MARGIN + lpobject->margin_slider+ (lpobject->dx_slider*nuVisibleSlider); + //y0=lpobject->dy - VMSCTL_STRIPBUS_MARGIN- VMSCTL_STRIP_DY_ASSIGN; + + y0=VMSCTL_STRIPBUS_MARGIN+ VMSCTL_STRIP_DY_BUTTON +10 +lpobject->dy_meter+10+lpobject->dy_slider+10; + + dx = dx -4; + dy = dy -6; + + rect.left=x0+2; + rect.top=y0+3; + rect.right=rect.left+dx; + rect.bottom=rect.top+dy; + lpobject->rect_assign[nuVisibleSlider]=rect; + + + oldpen = (HPEN)SelectObject(dc,lpobject->param.assignbus_pen); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg1_brush); + oldfont = (HFONT)SelectObject(dc,lpobject->param.font1); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,lpobject->param.assignbus_color); + + if (fAssigned != 0) + { + SetTextColor(dc,lpobject->param.assigned_color); + SelectObject(dc,lpobject->param.assigned_pen); + if (dx >= 40) SelectObject(dc,lpobject->param.font2); + } + + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom, 7,7); + DrawText(dc,szBUSName,(long)strlen(szBUSName),&rect,DT_SINGLELINE | DT_VCENTER | DT_CENTER); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); + + +} + +static void DrawSTRIP_BUSAssignment(LPT_VMSCTL_STRIPCTX lpobject, HDC dc) +{ + LPT_VMSCTL_STRIP_DATA lpdata; + long vi, nuVisibleSlider; + nuVisibleSlider=0; + lpdata = &(lpobject->data); + if (lpdata->strip_pBUSNameList == NULL) return; + for (vi=0; vi < VMSCTL_MAX_NBBUS; vi++) + { + if ((lpdata->strip_assignmentbit[vi] & 0x10) != 0) + { + DrawSTRIP_BUSAssignmentButton(lpobject, dc, nuVisibleSlider, (long)(lpdata->strip_assignmentbit[vi] & 0x0F), lpdata->strip_pBUSNameList[vi]); + nuVisibleSlider++; + } + } +} + +static void DrawSTRIP_Slider(LPT_VMSCTL_STRIPCTX lpobject, HDC dc, long nuVisibleSlider, float dBValue) +{ + LPT_VMSCTL_STRIPPARAM lpp; + HDC dcmem; + char sss[64]; + long x0,y0, yy, dx,dy,x_middle; + long cursor_height=20; + RECT rect,rect2; + HPEN oldpen, sliderpen; + HBRUSH oldbrush, sliderbrush; + HFONT oldfont; + HBITMAP oldbmp; + if (dc == NULL) return; + + //compute local coordintate + dx = lpobject->dx_slider; + dy = lpobject->dy_slider; + x0=VMSCTL_STRIPBUS_MARGIN + lpobject->margin_slider+ (lpobject->dx_slider*nuVisibleSlider); + y0=VMSCTL_STRIPBUS_MARGIN + VMSCTL_STRIP_DY_BUTTON +10 +lpobject->dy_meter+10; + + dx = dx -4; + + rect.left=x0; + rect.top=y0; + rect.right=rect.left+dx; + rect.bottom=rect.top+dy; + lpobject->rect_slider[nuVisibleSlider]=rect; + + rect.left=0; + rect.top=0; + rect.right=rect.left+dx; + rect.bottom=rect.top+dy; + + x_middle = (rect.left+rect.right)>>1; + lpp = &(lpobject->param); + //define slider color according level + if (dBValue > 0.0f) + { + sliderpen = lpp->slider_pen_red; + sliderbrush = lpp->slider_brush_red; + } + else + { + sliderpen = lpp->slider_pen_green; + sliderbrush = lpp->slider_brush_green; + } + //compute slider horizontal position + if (dBValue > lpp->vmax) dBValue=lpp->vmax; + if (dBValue < lpp->vmin) dBValue=lpp->vmin; + yy = (long)(((dBValue - lpp->vmin) * (dy - cursor_height))/ (lpp->vmax - lpp->vmin)); + if (yy < 0) yy=0; + if (yy > dy) yy=dy; + + //we don't draw directly in screen. + //to avoid flickering we first build a bitmap in memory + dcmem=CreateCompatibleDC(dc); + if (lpobject->tempbmp ==NULL) lpobject->tempbmp=CreateCompatibleBitmap(dc,dx,dy); + oldbmp=(HBITMAP)SelectObject(dcmem,lpobject->tempbmp); + //Draw background + oldpen = (HPEN)SelectObject(dcmem,GetStockObject(NULL_PEN)); + oldbrush = (HBRUSH)SelectObject(dcmem,lpp->bkg1_brush); + oldfont = (HFONT)SelectObject(dcmem,lpobject->param.font0); + SetBkMode(dcmem,TRANSPARENT); + SetTextColor(dcmem,RGB(0,0,0)); + + //draw background + Rectangle(dcmem,rect.left,rect.top,rect.right+1,rect.bottom+1); + SelectObject(dcmem,sliderpen); + MoveToEx(dcmem,x_middle, rect.top, NULL); + LineTo(dcmem,x_middle, rect.bottom); + + //draw cursor + SelectObject(dcmem,sliderpen); + SelectObject(dcmem,sliderbrush); + RoundRect(dcmem,rect.left, rect.bottom-yy-cursor_height,rect.right, rect.bottom-yy-1,7,7); + rect2=rect; + rect2.top =rect2.bottom-yy-cursor_height; + rect2.bottom = rect2.top+cursor_height; + sprintf(sss,"%0.1f", dBValue); + DrawText(dcmem,sss,(long)strlen(sss),&rect2,DT_SINGLELINE | DT_VCENTER | DT_CENTER); + + SelectObject(dcmem,oldpen); + SelectObject(dcmem,oldbrush); + SelectObject(dcmem,oldfont); + //display bitmap on screen at the end. + BitBlt(dc,x0,y0,dx,dy,dcmem,0,0,SRCCOPY); + SelectObject(dcmem,oldbmp); + DeleteDC(dcmem); + +} + +static void DrawSTRIP_AllSliders(LPT_VMSCTL_STRIPCTX lpobject, HDC dc) +{ + LPT_VMSCTL_STRIP_DATA lpdata; + long vi, nuVisibleSlider; + nuVisibleSlider=0; + lpdata = &(lpobject->data); + for (vi=0; vi < VMSCTL_MAX_NBBUS; vi++) + { + if ((lpdata->strip_assignmentbit[vi] & 0x10) != 0) + { + DrawSTRIP_Slider(lpobject, dc, nuVisibleSlider, lpdata->strip_gain[vi]); + nuVisibleSlider++; + } + } +} + +static void ComputeStripDisplayData(LPT_VMSCTL_STRIPCTX lpobject) +{ + long ddx, dx, dy, nbBus; + dx= lpobject->dx - VMSCTL_STRIPBUS_MARGIN -VMSCTL_STRIPBUS_MARGIN; + dy= lpobject->dy - VMSCTL_STRIPBUS_MARGIN -VMSCTL_STRIPBUS_MARGIN; + if (dx < 80) dx =80; + if (dy < 150) dy =150; + lpobject->dx_meter = dx; + lpobject->dy_meter = 20; + lpobject->dy_slider = dy - VMSCTL_STRIP_DY_BUTTON -10 -lpobject->dy_meter - 10 - VMSCTL_STRIP_DY_ASSIGN-10; + + lpobject->dx_name = dx-VMSCTL_STRIP_DX_NICKNAME-10; + + // Bus number gives the slider number + nbBus=1; + if (lpobject->data.strip_nbBusDisplayed > nbBus) nbBus=lpobject->data.strip_nbBusDisplayed; + + // compute slider size according slider number. + ddx = dx / (nbBus+1); // we add a margin + if (ddx < 35) + { + ddx = (dx - (35 * nbBus)) >> 1; + if (ddx < 0) ddx =0; + } + ddx=ddx>>1; + dx=dx-ddx-ddx; + lpobject->margin_slider = ddx; + lpobject->dx_slider = dx / nbBus; +} + +static void DrawSTRIP(LPT_VMSCTL_STRIPCTX lpobject, HDC dc) +{ + ComputeStripDisplayData(lpobject); + DrawSTRIP_NickNameMute(lpobject, dc); + DrawSTRIP_Name(lpobject, dc); + DrawSTRIP_PeakMetersBkg(lpobject, dc); + DrawSTRIP_AllSliders(lpobject, dc); + DrawSTRIP_BUSAssignment(lpobject, dc); +} + + +/*---------------------------------------------------------------*/ +/* BUS CALLBACK */ +/*---------------------------------------------------------------*/ + +static long VMSCTL_Strip_GetStripIndex(LPT_VMSCTL_STRIPCTX lpobject,long nuVisibleSlider) +{ + LPT_VMSCTL_STRIP_DATA lpdata; + long vi, nu=0; + lpdata = &(lpobject->data); + for (vi=0; vi < VMSCTL_MAX_NBBUS; vi++) + { + if ((lpdata->strip_assignmentbit[vi] & 0x10) != 0) + { + if (nuVisibleSlider == nu) return vi; + nu++; + } + } + return 0; +} + +static long VMSCTL_Strip_WhereAmI(LPT_VMSCTL_STRIPCTX lpobject,long x0, long y0) +{ + long nuVisibleSlider; + RECT rect; + rect=lpobject->rect_nickname; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_STRIP_ID_MUTE; + rect=lpobject->rect_name; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_STRIP_ID_NAME; + rect=lpobject->rect_meters; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_STRIP_ID_METER; + + for (nuVisibleSlider=0; nuVisibleSlider < lpobject->data.strip_nbBusDisplayed; nuVisibleSlider++) + { + rect=lpobject->rect_slider[nuVisibleSlider]; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_STRIP_ID_GAIN+nuVisibleSlider; + rect=lpobject->rect_assign[nuVisibleSlider]; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_STRIP_ID_ASSIGN+nuVisibleSlider; + } + return 0; +} + +static long VMSCTL_Strip_ManageLButtonDown(LPT_VMSCTL_STRIPCTX lpobject, HWND hw, long x0, long y0) +{ + LPT_VMSCTL_STRIP_DATA lpdata; + HDC dc; + float fValue; + long nuCtl, iStrip, nuVisibleSlider; + nuCtl=VMSCTL_Strip_WhereAmI(lpobject,x0, y0); + if (nuCtl == 0) return 0; + + lpdata = &(lpobject->data); + + // if the control is in the 8 possible displayed Sliders + if ((nuCtl >= VMSCTL_STRIP_ID_GAIN) && (nuCtl < (VMSCTL_STRIP_ID_GAIN + VMSCTL_MAX_NBBUS))) + { + nuVisibleSlider = nuCtl - VMSCTL_STRIP_ID_GAIN; + iStrip = VMSCTL_Strip_GetStripIndex(lpobject,nuVisibleSlider); + lpobject->start_value = lpdata->strip_gain[iStrip]; + if (lpobject->start_value > lpobject->param.vmax) lpobject->start_value = lpobject->param.vmax; + if (lpobject->start_value < lpobject->param.vmin) lpobject->start_value = lpobject->param.vmin; + lpobject->LastCapture = SetCapture(hw); + lpobject->mouse_y0=y0; + lpobject->last_y0=y0; + lpobject->mouseCapture=TRUE; + lpobject->ctlRunning=nuCtl; + lpobject->iStripRunning=iStrip; + return 0; + } + // if the control is in the 8 possible assignation buttton. + if ((nuCtl >= VMSCTL_STRIP_ID_ASSIGN) && (nuCtl < (VMSCTL_STRIP_ID_ASSIGN + VMSCTL_MAX_NBBUS))) + { + nuVisibleSlider = nuCtl - VMSCTL_STRIP_ID_ASSIGN; + iStrip = VMSCTL_Strip_GetStripIndex(lpobject,nuVisibleSlider); + + if ((lpdata->strip_assignmentbit[iStrip] & 0x0F) == 0) + { + lpdata->strip_assignmentbit[iStrip] |= 0x01; + fValue=1.0f; + } + else + { + lpdata->strip_assignmentbit[iStrip] &= 0xF0; + fValue=0.0f; + } + lpobject->param.lpCallback(lpobject->param.lpuser, lpobject->Ident, nuCtl, fValue); + dc=GetDC(hw); + DrawSTRIP_BUSAssignmentButton(lpobject, dc, nuVisibleSlider, (long)(fValue), lpdata->strip_pBUSNameList[iStrip]); + ReleaseDC(hw,dc); + return 0; + } + + // test other control if required + switch(nuCtl) + { + case VMSCTL_STRIP_ID_MUTE: + if (lpdata->strip_mute == 0) + { + lpdata->strip_mute =1; + fValue=1.0f; + } + else + { + lpdata->strip_mute =0; + fValue=0.0f; + } + lpobject->param.lpCallback(lpobject->param.lpuser, lpobject->Ident, nuCtl, fValue); + dc=GetDC(hw); + DrawSTRIP_NickNameMute(lpobject, dc); + ReleaseDC(hw,dc); + break; + case VMSCTL_BUS_ID_NAME: + break; + case VMSCTL_BUS_ID_METER: + break; + } + + return 0; +} + +static long VMSCTL_Strip_ManageLButtonDbclick(LPT_VMSCTL_STRIPCTX lpobject, HWND hw, long x0, long y0) +{ + HDC dc; + float fValue; + long nuCtl, iStrip, nuVisibleSlider; + nuCtl=VMSCTL_Strip_WhereAmI(lpobject,x0, y0); + if (nuCtl == 0) return 0; + + // if the control is in the 8 possible displayed Sliders + if ((nuCtl >= VMSCTL_STRIP_ID_GAIN) && (nuCtl < (VMSCTL_STRIP_ID_GAIN + VMSCTL_MAX_NBBUS))) + { + nuVisibleSlider = nuCtl - VMSCTL_STRIP_ID_GAIN; + iStrip = VMSCTL_Strip_GetStripIndex(lpobject,nuVisibleSlider); + + fValue=0.0f; + lpobject->data.strip_gain[iStrip]=fValue; + lpobject->param.lpCallback(lpobject->param.lpuser, lpobject->Ident, nuCtl, fValue); + + dc=GetDC(hw); + DrawSTRIP_Slider(lpobject, dc, nuVisibleSlider, fValue); + ReleaseDC(hw,dc); + return 0; + } + VMSCTL_Strip_ManageLButtonDown(lpobject, hw, x0, y0); + return 0; +} + + +static long VMSCTL_Strip_ManageMouseMove(LPT_VMSCTL_STRIPCTX lpobject, HWND hw, long x0, long y0) +{ + HDC dc; + LPT_VMSCTL_STRIPPARAM lpp; + long dy,ddy, nuCtl, iStrip, nuVisibleSlider; + float fValue; + if (lpobject->mouseCapture == FALSE) return -1; + lpp = &(lpobject->param); + nuCtl=lpobject->ctlRunning; + if ((nuCtl >= VMSCTL_STRIP_ID_GAIN) && (nuCtl < (VMSCTL_STRIP_ID_GAIN + VMSCTL_MAX_NBBUS))) + { + iStrip = lpobject->iStripRunning; + if (y0 != lpobject->last_y0) + { + dy=y0-lpobject->mouse_y0; + ddy=lpobject->dy_slider; + + fValue=lpobject->start_value - (((lpp->vmax - lpp->vmin) * dy) / ddy); + if (fValue < lpp->vmin) fValue=lpp->vmin; + if (fValue > lpp->vmax) fValue=lpp->vmax; + if (fValue != lpobject->data.strip_gain[iStrip]) + { + lpobject->data.strip_gain[iStrip]=fValue; + lpobject->param.lpCallback(lpobject->param.lpuser, lpobject->Ident, nuCtl, fValue); + + nuVisibleSlider = nuCtl - VMSCTL_STRIP_ID_GAIN; + dc=GetDC(hw); + DrawSTRIP_Slider(lpobject, dc, nuVisibleSlider, fValue); + ReleaseDC(hw,dc); + } + lpobject->last_y0 = y0; + } + + } + return 0; +} + +static long VMSCTL_Strip_ManageLButtonUp(LPT_VMSCTL_STRIPCTX lpobject, HWND hw, long x0, long y0) +{ + if (lpobject->mouseCapture == FALSE) return -1; + ReleaseCapture(); + if (lpobject->LastCapture != NULL) SetCapture(lpobject->LastCapture); + lpobject->mouseCapture=FALSE; + return 0; +} + + +static LRESULT CALLBACK VMSCTL_StripCallback(HWND hw, //handle of the window. + UINT msg, //Message Ident. + WPARAM p1, //parameter 1. + LPARAM p2) //parameter 2 +{ + HPEN oldpen; + HBRUSH oldbrush; + RECT rect; + LPCREATESTRUCT lpcs; + LPT_VMSCTL_STRIPCTX lpobject; + HDC dc; + PAINTSTRUCT ps; + switch (msg) + { + //here we create our structure + case WM_CREATE: + lpcs=(LPCREATESTRUCT)p2; + lpobject=(LPT_VMSCTL_STRIPCTX)lpcs->lpCreateParams; + lpobject->hw=hw; + TOOL_StorePointerInWindow(hw,lpobject); + break; + //on mouse click we get the focus. + case WM_MOUSEACTIVATE: + if (GetFocus()!=hw) SetFocus(hw); + return MA_ACTIVATE; + //on Focus change we redraw the control + case WM_SETFOCUS: + case WM_KILLFOCUS: + InvalidateRect(hw,NULL,FALSE); + return 0; + //here we draw all the control + case WM_PAINT: + dc=BeginPaint(hw,&ps); + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject != NULL) DrawSTRIP(lpobject, dc); + EndPaint(hw,&ps); + return 0; + case WM_ERASEBKGND: + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return 0; + + dc=(HDC)p1; + GetClientRect(hw,&rect); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg0_brush); + oldpen = (HPEN)SelectObject(dc,GetStockObject(NULL_PEN)); + Rectangle(dc,rect.left-1,rect.top-1,rect.right+1,rect.bottom+1); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg1_brush); + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom, 7,7); + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + return 0; + //mouse behavior + case WM_LBUTTONDOWN: + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) break; + VMSCTL_Strip_ManageLButtonDown(lpobject, hw, (short int)LOWORD(p2), (short int)HIWORD(p2)); + break; + case WM_LBUTTONDBLCLK: + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) break; + VMSCTL_Strip_ManageLButtonDbclick(lpobject, hw, (short int)LOWORD(p2), (short int)HIWORD(p2)); + break; + case WM_MOUSEMOVE: + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) break; + VMSCTL_Strip_ManageMouseMove(lpobject, hw, (short int)LOWORD(p2), (short int)HIWORD(p2)); + break; + case WM_LBUTTONUP: + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) break; + VMSCTL_Strip_ManageLButtonUp(lpobject, hw, (short int)LOWORD(p2), (short int)HIWORD(p2)); + break; + // control ending + case WM_CLOSE: + DestroyWindow(hw); + return 0; + case WM_DESTROY: + lpobject=TOOL_RecallPointerFromWindow(hw); + if (lpobject != NULL) + { + //if (lpobject->tempbmp !=NULL) DeleteObject(lpobject->tempbmp); + free(lpobject); + TOOL_StorePointerInWindow(hw,NULL); + } + break; + + } + return DefWindowProc(hw,msg,p1,p2); +} + + +HWND VMSCTL_CreateSTRIP(HWND hParent, long Ident, long x0,long y0, LPT_VMSCTL_STRIPPARAM lpparam) +{ + HWND hw; + char szName[64]; + long wstyle, vi; + LPT_VMSCTL_STRIPCTX lpobject; + //we allocate memory for our structure + lpobject=(LPT_VMSCTL_STRIPCTX)malloc(sizeof(T_VMSCTL_STRIPCTX)); + if (lpobject == NULL) return NULL; + memset(lpobject,0, sizeof(T_VMSCTL_STRIPCTX)); + if (lpparam == NULL) return NULL; + lpobject->param = *lpparam; + //define control size according bitmap. + lpobject->dx=100; + lpobject->dy=100; + //init rtdata; + for (vi=0;vi<8;vi++) + { + lpobject->rtdata.dbLevel[vi]=-100.0f; + } + //set control ident + lpobject->Ident=Ident; + //we create window with window-creation data + wstyle=WS_CHILD | WS_TABSTOP; // | WS_VISIBLE + sprintf(szName,"Strip #%i", Ident); + hw=CreateWindow(VMSCTL_CLASSNAME_STRIP, szName, + wstyle,x0,y0, + lpobject->dx, // window width + lpobject->dy, // window height + hParent, // parent Windows Handle. + (HMENU)Ident, // child-window identifier. + G_hinstance, // handle of application instance + lpobject); // we communicate our pointer (see WM_CREATE) + + return hw; +} + +long VMSCTL_PositionSTRIP(HWND hw, long x0,long y0, long dx, long dy, long fShow) +{ + LPT_VMSCTL_STRIPCTX lpobject; + BOOL fUpdate=FALSE; + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return -1; + if (fShow == 0) + { + lpobject->x0 = x0; + lpobject->y0 = y0; + lpobject->dx = dx; + lpobject->dy = dy; + if (lpobject->isShown != 0) + { + ShowWindow(hw, SW_HIDE); + } + lpobject->isShown = fShow; + } + else + { + if (lpobject->x0 != x0) fUpdate=TRUE; + if (lpobject->y0 != y0) fUpdate=TRUE; + if (lpobject->dx != dx) fUpdate=TRUE; + if (lpobject->dy != dy) fUpdate=TRUE; + if (lpobject->isShown == 0) fUpdate=TRUE; + + if (fUpdate == TRUE) + { + //internal bitmap size can be changed + if (lpobject->tempbmp !=NULL) DeleteObject(lpobject->tempbmp); + lpobject->tempbmp=NULL; + + lpobject->x0 = x0; + lpobject->y0 = y0; + lpobject->dx = dx; + lpobject->dy = dy; + + ComputeStripDisplayData(lpobject); + SetWindowPos(hw,HWND_TOP,lpobject->x0,lpobject->y0,lpobject->dx,lpobject->dy,SWP_SHOWWINDOW); + lpobject->isShown = fShow; + } + } + //if (fUpdate == TRUE) InvalidateRect(hw, NULL, TRUE); + return 0; +} + +long VMSCTL_SetDataSTRIP(HWND hw, LPT_VMSCTL_STRIP_DATA pData, long fUpdateAll) +{ + BOOL fUpdateMuteDisplay; + long vi,nuVisibleSlider; + BOOL fBusChanged, fAssignChanged, fGainChanged; + HDC dc=NULL; + LPT_VMSCTL_STRIP_DATA pCurrent; + LPT_VMSCTL_STRIPCTX lpobject; + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return -1; + + pCurrent = &(lpobject->data); + if (lpobject->isShown != 0) + { + dc=GetDC(hw); + } + + // NickName Button + fUpdateMuteDisplay=FALSE; + if ((fUpdateAll != 0) || (pCurrent->strip_nickname[0] == 0)) + { + strcpy(pCurrent->strip_nickname, pData->strip_nickname); + fUpdateMuteDisplay=TRUE; + } + // Strip Name + if ((wcscmp(pCurrent->strip_namew, pData->strip_namew) != 0) || (fUpdateAll != 0)) + { + wcscpy(pCurrent->strip_namew, pData->strip_namew); + DrawSTRIP_Name(lpobject, dc); + } + + // check bus number change + fBusChanged = fUpdateAll; + fAssignChanged = fUpdateAll; + fGainChanged =fUpdateAll; + if (pCurrent->strip_nbBusDisplayed != pData->strip_nbBusDisplayed) + { + fBusChanged = TRUE; + fAssignChanged = TRUE; + pCurrent->strip_nbBusDisplayed = pData->strip_nbBusDisplayed; + //internal bitmap size can be changed + if (lpobject->tempbmp !=NULL) DeleteObject(lpobject->tempbmp); + lpobject->tempbmp=NULL; + } + // check slide bus assignement change + pCurrent->strip_pBUSNameList = pData->strip_pBUSNameList; + for (vi=0; vi < VMSCTL_MAX_NBBUS; vi++) + { + if ((pCurrent->strip_assignmentbit[vi] & 0x10) != (pData->strip_assignmentbit[vi] & 0x10)) fBusChanged=TRUE; + if ((pCurrent->strip_assignmentbit[vi] & 0x01) != (pData->strip_assignmentbit[vi] & 0x01)) fAssignChanged=TRUE; + pCurrent->strip_assignmentbit[vi] = pData->strip_assignmentbit[vi]; + } + if (fBusChanged == TRUE) ComputeStripDisplayData(lpobject); + if ((fBusChanged == TRUE) || (fAssignChanged == TRUE)) DrawSTRIP_BUSAssignment(lpobject, dc); + + // check slider gain + nuVisibleSlider=0; + for (vi=0; vi < VMSCTL_MAX_NBBUS; vi++) + { + fGainChanged=fUpdateAll; + if ((pCurrent->strip_assignmentbit[vi] & 0x10) != 0) + { + if (pCurrent->strip_gain[vi] != pData->strip_gain[vi]) fGainChanged=TRUE; + // if the slider is used by the mouse we do not update display. + if ((lpobject->mouseCapture == TRUE) && (lpobject->iStripRunning == vi)) fGainChanged=FALSE; + if ((fBusChanged == TRUE) || (fGainChanged == TRUE)) + { + pCurrent->strip_gain[vi] = pData->strip_gain[vi]; + DrawSTRIP_Slider(lpobject, dc, nuVisibleSlider, pCurrent->strip_gain[vi]); + } + nuVisibleSlider++; + } + } + // Mute Button + if ((pCurrent->strip_mute != pData->strip_mute) || (fUpdateMuteDisplay == TRUE) || (fUpdateAll == TRUE)) + { + pCurrent->strip_mute = pData->strip_mute; + DrawSTRIP_NickNameMute(lpobject, dc); + } + + if (dc != NULL) ReleaseDC(hw,dc); + + return 0; +} + +long VMSCTL_ResetDataSTRIP(HWND hw) +{ + long vi; + LPT_VMSCTL_STRIPCTX lpobject; + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return -1; + memset(&(lpobject->data), 0, sizeof(T_VMSCTL_STRIPPARAM)); + for (vi=0;vi<8;vi++) lpobject->rtdata.dbLevel[vi]=-100.0f; + return 0; +} + + +long VMSCTL_SetRTDataSTRIP(HWND hw, LPT_VMSCTL_RT_DATA pRTData) +{ + HDC dc; + long nu; + LPT_VMSCTL_STRIPCTX lpobject; + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return -1; + + if (pRTData == NULL) return -2; + + //compute value to display, with a release factor + for (nu=0;nu<2;nu++) //display 2 channels only + { + if (pRTData->dbLevel[nu] > lpobject->rtdata.dbLevel[nu]) lpobject->rtdata.dbLevel[nu]=pRTData->dbLevel[nu]; + else + { + lpobject->rtdata.dbLevel[nu] = lpobject->rtdata.dbLevel[nu] - PEAKMETER_RELEASE_DECREMENT; + if (lpobject->rtdata.dbLevel[nu] < -100.0f) lpobject->rtdata.dbLevel[nu]=-100.0f; + } + } + //display peak meter. + if (lpobject->isShown != 0) + { + dc=GetDC(hw); + DrawSTRIP_PeakMeters(lpobject, dc); + ReleaseDC(hw,dc); + } + + return 0; +} + +long VMSCTL_GetIndexSTRIP(HWND hw,long nuVisibleSlider) +{ + LPT_VMSCTL_STRIPCTX lpobject; + lpobject=(LPT_VMSCTL_STRIPCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return 0; + return VMSCTL_Strip_GetStripIndex(lpobject,nuVisibleSlider); +} + + + + + + + + + + + + + + + + + + + + + + + +/*---------------------------------------------------------------*/ +/* BUS CONTROL */ +/*---------------------------------------------------------------*/ + +#define VMSCTL_BUS_DX_NICKNAME 30 +#define VMSCTL_BUS_DX_MON 40 + +#define VMSCTL_BUS_DXMIN_NAME 100 +#define VMSCTL_BUS_DXMIN_PEAKMETER 100 +#define VMSCTL_BUS_DXMIN_SLIDER 100 + +typedef struct tagVMSCTL_BUSCTX +{ + HWND hw; + long Ident; + long x0,y0,dx,dy; + long isShown; + + RECT rect_nickname; + RECT rect_name; + RECT rect_meters; + RECT rect_slider; + RECT rect_monitor; + + long dx_meter; + long dx_slider; + HBITMAP tempbmp; + + float start_value; + HWND LastCapture; + long mouse_x0; + long last_x0; + BOOL mouseCapture; + long ctlrunning; + + float fMaxLevelToDisplay; + float fMaxLevelDisplayed; + long nMaxLevelToDisplay; + T_VMSCTL_BUSPARAM param; + T_VMSCTL_BUS_DATA data; + T_VMSCTL_RT_DATA rtdata; +} T_VMSCTL_BUSCTX, *PT_VMSCTL_vCTX, *LPT_VMSCTL_BUSCTX; + + +static void DrawBUS_NickNameMute(LPT_VMSCTL_BUSCTX lpobject, HDC dc) +{ + char sss[64]; + RECT rect; + HFONT oldfont; + HPEN oldpen; + HBRUSH oldbrush; + if (dc == NULL) return; + + rect.left=VMSCTL_STRIPBUS_MARGIN; + rect.top=VMSCTL_STRIPBUS_MARGIN; + rect.right=rect.left+VMSCTL_BUS_DX_NICKNAME; + rect.bottom=lpobject->dy-5; + lpobject->rect_nickname=rect; + + oldpen = (HPEN)SelectObject(dc,lpobject->param.assignbus_pen); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg1_brush); + oldfont = (HFONT)SelectObject(dc,lpobject->param.font1); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,lpobject->param.assignbus_color); + + if (lpobject->data.bus_mute != 0) + { + SetTextColor(dc,lpobject->param.mutered_color); + SelectObject(dc,lpobject->param.mutered_pen); + } + + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom, 5,5); + strcpy(sss,lpobject->data.bus_nickname); + DrawText(dc,sss,(long)strlen(sss),&rect,DT_SINGLELINE | DT_VCENTER | DT_CENTER); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); + +} + +static void DrawBUS_Name(LPT_VMSCTL_BUSCTX lpobject, HDC dc) +{ + RECT rect; + HFONT oldfont; + HPEN oldpen; + HBRUSH oldbrush; + if (dc == NULL) return; + + rect.left=VMSCTL_STRIPBUS_MARGIN+VMSCTL_BUS_DX_NICKNAME+10; + rect.top=2; + rect.right=rect.left+VMSCTL_BUS_DXMIN_NAME; + rect.bottom=lpobject->dy-2; + lpobject->rect_name=rect; + + oldpen = (HPEN)SelectObject(dc,GetStockObject(NULL_PEN)); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg1_brush); + oldfont = (HFONT)SelectObject(dc,lpobject->param.font2); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,lpobject->param.name_color); + + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom, 5,5); + DrawTextW(dc,lpobject->data.bus_namew,(long)wcslen(lpobject->data.bus_namew),&rect,DT_SINGLELINE | DT_VCENTER | DT_LEFT); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); +} + + +static void DrawBUS_PeakMetersBkg(LPT_VMSCTL_BUSCTX lpobject, HDC dc) +{ + long dx; + RECT rect; + HPEN oldpen; + HBRUSH oldbrush; + if (dc == NULL) return; + + dx = lpobject->dx_meter; + rect.left=VMSCTL_STRIPBUS_MARGIN+VMSCTL_BUS_DX_NICKNAME+10+VMSCTL_BUS_DXMIN_NAME+10; + rect.top=VMSCTL_STRIPBUS_MARGIN; + rect.right=rect.left+dx; + rect.bottom=lpobject->dy-VMSCTL_STRIPBUS_MARGIN; + lpobject->rect_meters=rect; + + oldpen = (HPEN)SelectObject(dc,GetStockObject(BLACK_PEN)); + oldbrush = (HBRUSH)SelectObject(dc,GetStockObject(BLACK_BRUSH)); + Rectangle(dc,rect.left,rect.top,rect.right,rect.bottom); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); +} + +static void DrawBUS_PeakMeters(LPT_VMSCTL_BUSCTX lpobject, HDC dc) +{ + char sss[64]; + LPT_VMSCTL_BUSPARAM lpp; + float dBValue, dBmin=-80.0f, dBmax=+12.0f; + long nu,nbMeter=2; + long dx,dy,xx,x1,xgreen,xred; + RECT rect, rectmax; + HPEN oldpen; + HBRUSH oldbrush; + HFONT oldfont; + if (dc == NULL) return; + + dx = lpobject->dx_meter-6; + rect.left=VMSCTL_STRIPBUS_MARGIN+VMSCTL_BUS_DX_NICKNAME+10+VMSCTL_BUS_DXMIN_NAME+10; + rect.top=VMSCTL_STRIPBUS_MARGIN; + rect.bottom=lpobject->dy-VMSCTL_STRIPBUS_MARGIN; + dy = (rect.bottom-rect.top-4) / nbMeter; + + // compute peak meter zone + rect.left+=3; + rect.top+=3; + rect.right=rect.left+dx; + rect.bottom = rect.top+dy; + + // compute dB max zone and reduce peak meter zone + rectmax = rect; + rect.right -=25; + dx -=25; + rectmax.left=rect.right; + rectmax.bottom +=dy; + + // prepare display and compute different level + oldfont = (HFONT)SelectObject(dc,lpobject->param.font0); + oldpen = (HPEN)SelectObject(dc,GetStockObject(NULL_PEN)); + oldbrush = (HBRUSH)SelectObject(dc,GetStockObject(BLACK_BRUSH)); + xgreen = (long)(((-24.0f - dBmin) * dx)/ (dBmax - dBmin)); + xred = (long)(((0.0f - dBmin) * dx)/ (dBmax - dBmin)); + lpp = &(lpobject->param); + + for (nu=0;nurtdata.dbLevel[nu]; + xx = (long)(((dBValue - dBmin) * dx)/ (dBmax - dBmin)); + if (xx < 0) xx=0; + if (xx > dx) xx=dx; + + // blue + if (xx > 0) + { + SelectObject(dc,lpp->meter_brush1); + x1=xx; + if (x1 > xgreen) x1=xgreen; + Rectangle(dc,rect.left,rect.top,rect.left+x1+1,rect.bottom); + } + //green + if (xx >= xgreen) + { + SelectObject(dc,lpp->meter_brush2); + x1=xx; + if (x1 > xred) x1=xred; + Rectangle(dc,rect.left+xgreen,rect.top,rect.left+x1+1,rect.bottom); + } + //red + if (xx >= xred) + { + SelectObject(dc,lpp->meter_brush3); + x1=xx; + Rectangle(dc,rect.left+xred,rect.top,rect.left+x1+1,rect.bottom); + } + //black + if (xx <= dx) + { + SelectObject(dc,GetStockObject(BLACK_BRUSH)); + Rectangle(dc,rect.left+xx,rect.top,rect.right+1,rect.bottom); + } + + rect.top +=dy; + rect.bottom +=dy; + } + // display max level + if (lpobject->fMaxLevelDisplayed != lpobject->fMaxLevelToDisplay) + { + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,lpobject->param.name_color); + if (lpobject->fMaxLevelToDisplay < -99.0f) strcpy(sss,"-"); + else sprintf(sss,"%0.1f", lpobject->fMaxLevelToDisplay); + SelectObject(dc,GetStockObject(BLACK_BRUSH)); + Rectangle(dc,rectmax.left,rectmax.top,rectmax.right+1,rectmax.bottom+1); + DrawText(dc,sss,(long)strlen(sss),&rectmax,DT_SINGLELINE | DT_VCENTER | DT_CENTER); + lpobject->fMaxLevelDisplayed=lpobject->fMaxLevelToDisplay; + } + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); +} + + +static void DrawBUS_Slider(LPT_VMSCTL_BUSCTX lpobject, HDC dc) +{ + HDC dcmem; + char sss[64]; + float dBValue; + LPT_VMSCTL_BUSPARAM lpp; + long x0,y0, xx,dx,dy,y_middle; + long cursor_width=35; + RECT rect,rect2; + HPEN oldpen, sliderpen; + HBRUSH oldbrush, sliderbrush; + HFONT oldfont; + HBITMAP oldbmp; + if (dc == NULL) return; + + //compute local coordintate + dx = lpobject->dx_slider; + dy = lpobject->dy-VMSCTL_STRIPBUS_MARGIN-VMSCTL_STRIPBUS_MARGIN; + x0=VMSCTL_STRIPBUS_MARGIN+VMSCTL_BUS_DX_NICKNAME+10+VMSCTL_BUS_DXMIN_NAME+10+lpobject->dx_meter+10; + y0=VMSCTL_STRIPBUS_MARGIN; + + rect.left=x0; + rect.top=y0; + rect.right=rect.left+dx; + rect.bottom=rect.top+dy; + lpobject->rect_slider=rect; + + rect.left=0; + rect.top=0; + rect.right=rect.left+dx; + rect.bottom=rect.top+dy; + + y_middle = (rect.bottom+rect.top)>>1; + lpp = &(lpobject->param); + dBValue = lpobject->data.bus_gain; + //define slider color according level + if (dBValue > 0.0f) + { + sliderpen = lpp->slider_pen_red; + sliderbrush = lpp->slider_brush_red; + } + else + { + sliderpen = lpp->slider_pen_green; + sliderbrush = lpp->slider_brush_green; + } + //compute slider horizontal position + xx = (long)(((dBValue - lpp->vmin) * (dx - cursor_width))/ (lpp->vmax - lpp->vmin)); + if (xx < 0) xx=0; + if (xx > dx) xx=dx; + + //we don't draw directly in screen. + //to avoid flickering we first build a bitmap in memory + dcmem=CreateCompatibleDC(dc); + if (lpobject->tempbmp ==NULL) lpobject->tempbmp=CreateCompatibleBitmap(dc,dx,dy); + oldbmp=(HBITMAP)SelectObject(dcmem,lpobject->tempbmp); + //Draw background + oldpen = (HPEN)SelectObject(dcmem,GetStockObject(NULL_PEN)); + oldbrush = (HBRUSH)SelectObject(dcmem,lpp->bkg1_brush); + oldfont = (HFONT)SelectObject(dcmem,lpobject->param.font0); + SetBkMode(dcmem,TRANSPARENT); + SetTextColor(dcmem,RGB(0,0,0)); + + //draw background + Rectangle(dcmem,rect.left,rect.top,rect.right+1,rect.bottom+1); + SelectObject(dcmem,sliderpen); + MoveToEx(dcmem,rect.left,y_middle, NULL); + LineTo(dcmem,rect.right, y_middle); + + //draw cursor + SelectObject(dcmem,sliderpen); + SelectObject(dcmem,sliderbrush); + RoundRect(dcmem,rect.left+xx, rect.top,rect.left+xx+cursor_width-1,rect.bottom,7,7); + rect2=rect; + rect2.left +=xx; + rect2.right = rect2.left+cursor_width; + sprintf(sss,"%0.1f", dBValue); + DrawText(dcmem,sss,(long)strlen(sss),&rect2,DT_SINGLELINE | DT_VCENTER | DT_CENTER); + + SelectObject(dcmem,oldpen); + SelectObject(dcmem,oldbrush); + SelectObject(dcmem,oldfont); + //display bitmap on screen at the end. + BitBlt(dc,x0,y0,dx,dy,dcmem,0,0,SRCCOPY); + SelectObject(dcmem,oldbmp); + DeleteDC(dcmem); + +} + +static void DrawBUS_Monitor(LPT_VMSCTL_BUSCTX lpobject, HDC dc) +{ + char sss[64]; + RECT rect; + HFONT oldfont; + HPEN oldpen; + HBRUSH oldbrush; + if (dc == NULL) return; + + if (lpobject->data.bus_fMonitorSupport == 0) return; + + rect.right = lpobject->dx-VMSCTL_STRIPBUS_MARGIN; + rect.left=rect.right-VMSCTL_BUS_DX_MON; + //check if there is enough place to display it. + if (rect.left < (lpobject->rect_slider.right+10)) rect.left = lpobject->rect_slider.right+10; + rect.right = rect.left + VMSCTL_BUS_DX_MON; + + rect.top=VMSCTL_STRIPBUS_MARGIN; + rect.bottom=lpobject->dy-VMSCTL_STRIPBUS_MARGIN; + lpobject->rect_monitor=rect; + + oldpen = (HPEN)SelectObject(dc,lpobject->param.assignbus_pen); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg1_brush); + oldfont = (HFONT)SelectObject(dc,lpobject->param.font1); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,lpobject->param.assignbus_color); + + if (lpobject->data.bus_monitor != 0) + { + SetTextColor(dc,RGB(0,0,0)); + SelectObject(dc,lpobject->param.monitor_brush); + SelectObject(dc,lpobject->param.monitor_pen); + } + + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom, 5,5); + strcpy(sss,"Mon"); + DrawText(dc,sss,(long)strlen(sss),&rect,DT_SINGLELINE | DT_VCENTER | DT_CENTER); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); + +} + +static void ComputeBusDisplayData(LPT_VMSCTL_BUSCTX lpobject) +{ + long dx; + dx= lpobject->dx - VMSCTL_STRIPBUS_MARGIN- VMSCTL_BUS_DX_NICKNAME-10-VMSCTL_BUS_DXMIN_NAME-VMSCTL_STRIPBUS_MARGIN; + if (lpobject->data.bus_fMonitorSupport != 0) dx = dx -10 -VMSCTL_BUS_DX_MON; + dx= (dx-20) >> 1; + if (dx < 100) dx =100; + lpobject->dx_meter = dx; + lpobject->dx_slider = dx; + lpobject->fMaxLevelDisplayed=-500.0f; //to force display refresh +} + + +static void DrawBUS(LPT_VMSCTL_BUSCTX lpobject, HDC dc) +{ + ComputeBusDisplayData(lpobject); + DrawBUS_NickNameMute(lpobject, dc); + DrawBUS_Name(lpobject, dc); + DrawBUS_PeakMetersBkg(lpobject, dc); + DrawBUS_Slider(lpobject, dc); + DrawBUS_Monitor(lpobject, dc); +} + + +/*---------------------------------------------------------------*/ +/* BUS CALLBACK */ +/*---------------------------------------------------------------*/ + + +static long VMSCTL_Bus_WhereAmI(LPT_VMSCTL_BUSCTX lpobject,long x0, long y0) +{ + RECT rect; + rect=lpobject->rect_nickname; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_BUS_ID_MUTE; + rect=lpobject->rect_name; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_BUS_ID_NAME; + rect=lpobject->rect_meters; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_BUS_ID_METER; + rect=lpobject->rect_slider; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_BUS_ID_GAIN; + rect=lpobject->rect_monitor; + if ((x0 >= rect.left) && (x0 <= rect.right) && (y0>= rect.top) && (y0<=rect.bottom)) return VMSCTL_BUS_ID_MONITOR; + return 0; +} + +static long VMSCTL_Bus_ManageLButtonDown(LPT_VMSCTL_BUSCTX lpobject, HWND hw, long x0, long y0) +{ + HDC dc; + float fValue; + long nuCtl; + nuCtl=VMSCTL_Bus_WhereAmI(lpobject,x0, y0); + if (nuCtl == 0) return 0; + + switch(nuCtl) + { + case VMSCTL_BUS_ID_MUTE: + if (lpobject->data.bus_mute == 0) + { + lpobject->data.bus_mute =1; + fValue=1.0f; + } + else + { + lpobject->data.bus_mute =0; + fValue=0.0f; + } + lpobject->param.lpCallback(lpobject->param.lpuser, lpobject->Ident, nuCtl, fValue); + dc=GetDC(hw); + DrawBUS_NickNameMute(lpobject, dc); + ReleaseDC(hw,dc); + break; + case VMSCTL_BUS_ID_NAME: + break; + case VMSCTL_BUS_ID_METER: + break; + case VMSCTL_BUS_ID_GAIN: + lpobject->start_value = lpobject->data.bus_gain; + if (lpobject->start_value > lpobject->param.vmax) lpobject->start_value = lpobject->param.vmax; + if (lpobject->start_value < lpobject->param.vmin) lpobject->start_value = lpobject->param.vmin; + lpobject->LastCapture = SetCapture(hw); + lpobject->mouse_x0=x0; + lpobject->last_x0=x0; + lpobject->mouseCapture=TRUE; + lpobject->ctlrunning=nuCtl; + break; + case VMSCTL_BUS_ID_MONITOR: + if (lpobject->data.bus_monitor == 0) + { + lpobject->data.bus_monitor =1; + fValue=1.0f; + } + else + { + lpobject->data.bus_monitor =0; + fValue=0.0f; + } + lpobject->param.lpCallback(lpobject->param.lpuser, lpobject->Ident, nuCtl, fValue); + dc=GetDC(hw); + DrawBUS_Monitor(lpobject, dc); + ReleaseDC(hw,dc); + break; + } + return 0; +} + +static long VMSCTL_Bus_ManageLButtonDbclick(LPT_VMSCTL_BUSCTX lpobject, HWND hw, long x0, long y0) +{ + HDC dc; + float fValue; + long nuCtl; + nuCtl=VMSCTL_Bus_WhereAmI(lpobject,x0, y0); + if (nuCtl == 0) return 0; + + switch(nuCtl) + { + //double click to reset slider to 0.0 dB + case VMSCTL_BUS_ID_GAIN: + fValue=0.0f; + lpobject->data.bus_gain=fValue; + lpobject->param.lpCallback(lpobject->param.lpuser, lpobject->Ident, nuCtl, fValue); + + dc=GetDC(hw); + DrawBUS_Slider(lpobject, dc); + ReleaseDC(hw,dc); + break; + //otherwise we consider it as a simple click + default: + VMSCTL_Bus_ManageLButtonDown(lpobject, hw, x0, y0); + break; + } + return 0; +} + + +static long VMSCTL_Bus_ManageMouseMove(LPT_VMSCTL_BUSCTX lpobject, HWND hw, long x0, long y0) +{ + HDC dc; + LPT_VMSCTL_BUSPARAM lpp; + long dx,ddx, nuCtl; + float fValue; + if (lpobject->mouseCapture == FALSE) return -1; + lpp = &(lpobject->param); + nuCtl=lpobject->ctlrunning; + switch(nuCtl) + { + case VMSCTL_BUS_ID_GAIN: + if (x0 != lpobject->last_x0) + { + dx=x0-lpobject->mouse_x0; + ddx=lpobject->dx_slider; + + fValue=lpobject->start_value + (((lpp->vmax - lpp->vmin) * dx) / ddx); + if (fValue < lpp->vmin) fValue=lpp->vmin; + if (fValue > lpp->vmax) fValue=lpp->vmax; + if (fValue != lpobject->data.bus_gain) + { + lpobject->data.bus_gain=fValue; + lpobject->param.lpCallback(lpobject->param.lpuser, lpobject->Ident, nuCtl, fValue); + + dc=GetDC(hw); + DrawBUS_Slider(lpobject, dc); + ReleaseDC(hw,dc); + } + lpobject->last_x0 = x0; + } + break; + } + return 0; +} + +static long VMSCTL_Bus_ManageLButtonUp(LPT_VMSCTL_BUSCTX lpobject, HWND hw, long x0, long y0) +{ + if (lpobject->mouseCapture == FALSE) return -1; + ReleaseCapture(); + if (lpobject->LastCapture != NULL) SetCapture(lpobject->LastCapture); + lpobject->mouseCapture=FALSE; + return 0; +} + + +static LRESULT CALLBACK VMSCTL_BusCallback(HWND hw, //handle of the window. + UINT msg, //Message Ident. + WPARAM p1, //parameter 1. + LPARAM p2) //parameter 2 +{ + HPEN oldpen; + HBRUSH oldbrush; + RECT rect; + LPCREATESTRUCT lpcs; + LPT_VMSCTL_BUSCTX lpobject; + HDC dc; + PAINTSTRUCT ps; + switch (msg) + { + //here we create our structure + case WM_CREATE: + lpcs=(LPCREATESTRUCT)p2; + lpobject=(LPT_VMSCTL_BUSCTX)lpcs->lpCreateParams; + lpobject->hw=hw; + TOOL_StorePointerInWindow(hw,lpobject); + break; + //on mouse click we get the focus. + case WM_MOUSEACTIVATE: + if (GetFocus()!=hw) SetFocus(hw); + return MA_ACTIVATE; + //on Focus change we redraw the control + case WM_SETFOCUS: + case WM_KILLFOCUS: + InvalidateRect(hw,NULL,FALSE); + return 0; + //here we draw all the control + case WM_PAINT: + dc=BeginPaint(hw,&ps); + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject != NULL) DrawBUS(lpobject, dc); + EndPaint(hw,&ps); + return 0; + case WM_ERASEBKGND: + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return 0; + dc=(HDC)p1; + GetClientRect(hw,&rect); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg0_brush); + oldpen = (HPEN)SelectObject(dc,GetStockObject(NULL_PEN)); + Rectangle(dc,rect.left-1,rect.top-1,rect.right+1,rect.bottom+1); + oldbrush = (HBRUSH)SelectObject(dc,lpobject->param.bkg1_brush); + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom, 7,7); + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + return 0; + //mouse behavior + case WM_LBUTTONDOWN: + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) break; + VMSCTL_Bus_ManageLButtonDown(lpobject, hw, (short int)LOWORD(p2), (short int)HIWORD(p2)); + break; + case WM_LBUTTONDBLCLK: + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) break; + VMSCTL_Bus_ManageLButtonDbclick(lpobject, hw, (short int)LOWORD(p2), (short int)HIWORD(p2)); + break; + case WM_MOUSEMOVE: + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) break; + VMSCTL_Bus_ManageMouseMove(lpobject, hw, (short int)LOWORD(p2), (short int)HIWORD(p2)); + break; + case WM_LBUTTONUP: + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) break; + VMSCTL_Bus_ManageLButtonUp(lpobject, hw, (short int)LOWORD(p2), (short int)HIWORD(p2)); + break; + + //ending + case WM_CLOSE: + DestroyWindow(hw); + return 0; + case WM_DESTROY: + lpobject=TOOL_RecallPointerFromWindow(hw); + if (lpobject != NULL) + { + if (lpobject->tempbmp !=NULL) DeleteObject(lpobject->tempbmp); + free(lpobject); + TOOL_StorePointerInWindow(hw,NULL); + } + break; + + } + return DefWindowProc(hw,msg,p1,p2); +} + + +HWND VMSCTL_CreateBUS(HWND hParent, long Ident, long x0,long y0, LPT_VMSCTL_BUSPARAM lpparam) +{ + HWND hw; + char szName[64]; + long wstyle,vi; + LPT_VMSCTL_BUSCTX lpobject; + //we allocate memory for our structure + lpobject=(LPT_VMSCTL_BUSCTX)malloc(sizeof(T_VMSCTL_BUSCTX)); + if (lpobject == NULL) return NULL; + memset(lpobject,0, sizeof(T_VMSCTL_BUSCTX)); + if (lpparam == NULL) return NULL; + lpobject->param = *lpparam; + //define control size according bitmap. + lpobject->dx=100; + lpobject->dy=100; + //init rtdata; + for (vi=0;vi<8;vi++) + { + lpobject->rtdata.dbLevel[vi]=-100.0f; + } + //set control ident + lpobject->Ident=Ident; + + //we create window with window-creation data + wstyle=WS_CHILD | WS_TABSTOP; // | WS_VISIBLE + sprintf(szName,"Bus #%i", Ident); + hw=CreateWindow(VMSCTL_CLASSNAME_BUS, szName, + wstyle,x0,y0, + lpobject->dx, // window width + lpobject->dy, // window height + hParent, // parent Windows Handle. + (HMENU)Ident, // child-window identifier. + G_hinstance, // handle of application instance + lpobject); // we communicate our pointer (see WM_CREATE) + + return hw; +} + + +long VMSCTL_PositionBUS(HWND hw, long x0,long y0, long dx, long dy, long fShow) +{ + LPT_VMSCTL_BUSCTX lpobject; + BOOL fUpdate=FALSE; + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return -1; + if (fShow == 0) + { + lpobject->x0 = x0; + lpobject->y0 = y0; + lpobject->dx = dx; + lpobject->dy = dy; + if (lpobject->isShown != 0) + { + ShowWindow(hw, SW_HIDE); + } + lpobject->isShown = fShow; + } + else + { + if (lpobject->x0 != x0) fUpdate=TRUE; + if (lpobject->y0 != y0) fUpdate=TRUE; + if (lpobject->dx != dx) fUpdate=TRUE; + if (lpobject->dy != dy) fUpdate=TRUE; + if (lpobject->isShown == 0) fUpdate=TRUE; + + if (fUpdate == TRUE) + { + if (lpobject->tempbmp !=NULL) DeleteObject(lpobject->tempbmp); + lpobject->tempbmp=NULL; + + lpobject->x0 = x0; + lpobject->y0 = y0; + lpobject->dx = dx; + lpobject->dy = dy; + + ComputeBusDisplayData(lpobject); + SetWindowPos(hw,HWND_TOP,lpobject->x0,lpobject->y0,lpobject->dx,lpobject->dy,SWP_SHOWWINDOW); + lpobject->isShown = fShow; + } + } + //if (fUpdate == TRUE) InvalidateRect(hw, NULL, TRUE); + return 0; +} + +long VMSCTL_SetDataBUS(HWND hw, LPT_VMSCTL_BUS_DATA pData, long fUpdateAll) +{ + BOOL fUpdateMuteDisplay; + HDC dc=NULL; + LPT_VMSCTL_BUS_DATA pCurrent; + LPT_VMSCTL_BUSCTX lpobject; + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return -1; + if (pData == NULL) return -2; + pCurrent = &(lpobject->data); + + if (pCurrent->bus_fMonitorSupport != pData->bus_fMonitorSupport) + { + *pCurrent = *pData; + InvalidateRect(hw,NULL,TRUE); + return 0; + } + if (lpobject->isShown != 0) + { + dc=GetDC(hw); + } + + fUpdateMuteDisplay=FALSE; + if ((fUpdateAll != 0) || (pCurrent->bus_nickname[0] == 0)) + { + strcpy(pCurrent->bus_nickname, pData->bus_nickname); + fUpdateMuteDisplay = TRUE; + } + if ((wcscmp(pCurrent->bus_namew, pData->bus_namew) != 0) || (fUpdateAll != 0)) + { + wcscpy(pCurrent->bus_namew, pData->bus_namew); + DrawBUS_Name(lpobject, dc); + } + if ((pCurrent->bus_gain != pData->bus_gain) || (fUpdateAll != 0)) + { + if ((lpobject->mouseCapture == FALSE) || (fUpdateAll != 0)) + { + pCurrent->bus_gain = pData->bus_gain; + DrawBUS_Slider(lpobject, dc); + } + } + if ((pCurrent->bus_mute != pData->bus_mute) || (fUpdateMuteDisplay == TRUE) || (fUpdateAll != 0)) + { + pCurrent->bus_mute = pData->bus_mute; + DrawBUS_NickNameMute(lpobject, dc); + } + if ((pCurrent->bus_monitor != pData->bus_monitor) || (fUpdateAll != 0)) + { + pCurrent->bus_monitor = pData->bus_monitor; + DrawBUS_Monitor(lpobject, dc); + } + + if (dc != NULL) ReleaseDC(hw,dc); + + return 0; +} + +long VMSCTL_ResetDataBUS(HWND hw) +{ + long vi; + LPT_VMSCTL_BUSCTX lpobject; + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return -1; + memset(&(lpobject->data), 0, sizeof(T_VMSCTL_BUSPARAM)); + for (vi=0;vi<8;vi++) lpobject->rtdata.dbLevel[vi]=-100.0f; + return 0; +} + +long VMSCTL_SetRTDataBUS(HWND hw, LPT_VMSCTL_RT_DATA pRTData) +{ + float fMax; + HDC dc; + long nu; + LPT_VMSCTL_BUSCTX lpobject; + lpobject=(LPT_VMSCTL_BUSCTX)TOOL_RecallPointerFromWindow(hw); + if (lpobject == NULL) return -1; + if (pRTData == NULL) return -2; + + fMax = -100.0f; + lpobject->nMaxLevelToDisplay--; + if (lpobject->nMaxLevelToDisplay < 1) lpobject->fMaxLevelToDisplay=fMax; + //compute value to display, with a release factor + for (nu=0;nu<2;nu++) //display 2 channels only + { + if (pRTData->dbLevel[nu] > fMax) fMax=pRTData->dbLevel[nu]; + if (pRTData->dbLevel[nu] > lpobject->rtdata.dbLevel[nu]) lpobject->rtdata.dbLevel[nu]=pRTData->dbLevel[nu]; + else + { + lpobject->rtdata.dbLevel[nu] = lpobject->rtdata.dbLevel[nu] - PEAKMETER_RELEASE_DECREMENT; + if (lpobject->rtdata.dbLevel[nu] < -100.0f) lpobject->rtdata.dbLevel[nu]=-100.0f; + } + } + // Compute max level to display + if (fMax > lpobject->fMaxLevelToDisplay) + { + lpobject->fMaxLevelToDisplay = fMax; + lpobject->nMaxLevelToDisplay = 200; + } + //display peak meter. + if (lpobject->isShown != 0) + { + dc=GetDC(hw); + DrawBUS_PeakMeters(lpobject, dc); + ReleaseDC(hw,dc); + } + + return 0; +} + + + + + + + + + + + + + + + +/*---------------------------------------------------------------*/ +/* INIT / END */ +/*---------------------------------------------------------------*/ +long VMSCTL_InitLib(HINSTANCE hinst) +{ + long rep; + WNDCLASS wc; + + G_hinstance=hinst; + + //register class for our custom Strip + wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; //allows double click + wc.lpfnWndProc =(WNDPROC)VMSCTL_StripCallback; //Adresse of the related callback + wc.cbClsExtra =0; //0 per default. + wc.cbWndExtra =sizeof(void*); //alloc some byte to store pointer inside window. + wc.hInstance =hinst; //application hinstance. + wc.hIcon =NULL; //no Icon + wc.hCursor =LoadCursor(NULL,IDC_ARROW); //handle on cursor mouse. + wc.hbrBackground=NULL; //no background + wc.lpszMenuName =NULL; //no menu. + wc.lpszClassName=VMSCTL_CLASSNAME_STRIP; + rep=RegisterClass(&wc); + if (rep==0) return -1; + + //register class for our custom BUS + wc.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW; //allows double click + wc.lpfnWndProc =(WNDPROC)VMSCTL_BusCallback; //Adresse of the related callback + wc.cbClsExtra =0; //0 per default. + wc.cbWndExtra =sizeof(void*); //alloc some byte to store pointer inside window. + wc.hInstance =hinst; //application hinstance. + wc.hIcon =NULL; //no Icon + wc.hCursor =LoadCursor(NULL,IDC_ARROW); //handle on cursor mouse. + wc.hbrBackground=NULL; //no background + wc.lpszMenuName =NULL; //no menu. + wc.lpszClassName=VMSCTL_CLASSNAME_BUS; + rep=RegisterClass(&wc); + if (rep==0) return -1; + + return 0; +} + +void VMSCTL_EndLib(void) +{ + UnregisterClass(VMSCTL_CLASSNAME_STRIP,G_hinstance); + UnregisterClass(VMSCTL_CLASSNAME_BUS,G_hinstance); +} + diff --git a/vmr_streamer/source/streamer_ctrl.h b/vmr_streamer/source/streamer_ctrl.h new file mode 100644 index 0000000..6fcf2f7 --- /dev/null +++ b/vmr_streamer/source/streamer_ctrl.h @@ -0,0 +1,199 @@ +/**********************************************************************************/ +/* VMSCTL V.Burel */ +/**********************************************************************************/ +/* Contains our custom windows control for vmr_streamer project */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* COMPILATION DIRECTIVES: */ +/* */ +/* To compile With Microsoft VC2005 or higher, you need to create an */ +/* empty Win32 project with the following options: */ +/* - Configuration Properties / General : Char Set = NOT SET */ +/* - Configuration Properties / C/C++ / Preprocessor : _CRT_SECURE_NO_DEPRECATE */ +/* */ +/* This source code can be compiled for 32bit or 64 bits targets as well */ +/* WARNING: FOR 64x COMPILATION, ADD PREPROCESSOR DEFINE: VB_X64 */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* LICENSING: VoicemeeterRemote.dll usage is driven by a license agreement */ +/* given in VoicemeeterRemoteAPI.pdf or readme.txt */ +/* This Source Code can be used only in a program using Voicemeeter */ +/* Remote API anyway */ +/* */ +/*--------------------------------------------------------------------------------*/ + + + +#ifndef __VMSCTL_H__ +#define __VMSCTL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +// if compiling for x64 target define this +//#define VB_X64 + + +#define VMSCTL_MAX_NBSTRIP 8 +#define VMSCTL_MAX_NBBUS 8 + + +#define VMSCTL_CLASSNAME_STRIP "VMStreamerCtrl_Strip" +#define VMSCTL_CLASSNAME_BUS "VMStreamerCtrl_BUS" + + +//Initialize library +long VMSCTL_InitLib(HINSTANCE hinst); +void VMSCTL_EndLib(void); + + +typedef struct tagVMSCTL_RT_DATA +{ + float dbLevel[8]; +} T_VMSCTL_RT_DATA, *PT_VMSCTL_RT_DATA, *LPT_VMSCTL_RT_DATA; + +/*---------------------------------------------------------------*/ +/* STRIP CTL */ +/*---------------------------------------------------------------*/ +typedef void (__stdcall *VMSCTL_LPT_SBCALLBACK)(void * lpuser,long Ident, long ICtl, float value); + + +#define VMSCTL_STRIP_ID_MUTE 100 +#define VMSCTL_STRIP_ID_NAME 101 +#define VMSCTL_STRIP_ID_METER 102 +#define VMSCTL_STRIP_ID_GAIN 132 +#define VMSCTL_STRIP_ID_ASSIGN 148 + +typedef struct tagVMSCTL_STRIPPARAM +{ + HFONT font0; + HFONT font1; + HFONT font2; + float vmin,vmax; + HBRUSH bkg0_brush; + HBRUSH bkg1_brush; + + HPEN assignbus_pen; + COLORREF assignbus_color; + COLORREF name_color; + + HPEN slider_pen_green; + HPEN slider_pen_red; + HBRUSH slider_brush_green; + HBRUSH slider_brush_red; + + HBRUSH meter_brush1; + HBRUSH meter_brush2; + HBRUSH meter_brush3; + + COLORREF mutered_color; + HPEN mutered_pen; + COLORREF assigned_color; + HPEN assigned_pen; + + VMSCTL_LPT_SBCALLBACK lpCallback; + void * lpuser; +} T_VMSCTL_STRIPPARAM, *PT_VMSCTL_STRIPPARAM, *LPT_VMSCTL_STRIPPARAM; + +HWND VMSCTL_CreateSTRIP(HWND hParent, long Ident, long x0,long y0, LPT_VMSCTL_STRIPPARAM lpparam); + +long VMSCTL_PositionSTRIP(HWND hw, long x0,long y0, long dx, long dy, long fShow); + + +typedef struct tagVMSCTL_STRIP_DATA +{ + WCHAR strip_namew[64]; + char strip_nickname[8]; + float strip_gain[VMSCTL_MAX_NBBUS]; + long strip_mute; + char strip_assignmentbit[VMSCTL_MAX_NBBUS]; //0x10 bus activated / 0x01 bus assigned + long strip_nbBusDisplayed; + long strip_fMultiLayer; + char ** strip_pBUSNameList; +} T_VMSCTL_STRIP_DATA, *PT_VMSCTL_STRIP_DATA, *LPT_VMSCTL_STRIP_DATA; + +long VMSCTL_SetDataSTRIP(HWND hw, LPT_VMSCTL_STRIP_DATA pData, long fUpdateAll); +long VMSCTL_ResetDataSTRIP(HWND hw); + +long VMSCTL_SetRTDataSTRIP(HWND hw, LPT_VMSCTL_RT_DATA pRTData); + +long VMSCTL_GetIndexSTRIP(HWND hw,long nuVisibleSlider); + + + + +/*---------------------------------------------------------------*/ +/* BUS CTL */ +/*---------------------------------------------------------------*/ + +#define VMSCTL_BUS_ID_MUTE 200 +#define VMSCTL_BUS_ID_NAME 201 +#define VMSCTL_BUS_ID_METER 202 +#define VMSCTL_BUS_ID_GAIN 203 +#define VMSCTL_BUS_ID_MONITOR 204 + +typedef struct tagVMSCTL_BUSPARAM +{ + HFONT font0; + HFONT font1; + HFONT font2; + float vmin,vmax; + + HBRUSH bkg0_brush; + HBRUSH bkg1_brush; + HPEN assignbus_pen; + COLORREF assignbus_color; + COLORREF name_color; + + HPEN slider_pen_green; + HPEN slider_pen_red; + HBRUSH slider_brush_green; + HBRUSH slider_brush_red; + + HBRUSH meter_brush1; + HBRUSH meter_brush2; + HBRUSH meter_brush3; + + COLORREF mutered_color; + HPEN mutered_pen; + + HBRUSH monitor_brush; + HPEN monitor_pen; + + VMSCTL_LPT_SBCALLBACK lpCallback; + void * lpuser; +} T_VMSCTL_BUSPARAM, *PT_VMSCTL_BUSPARAM, *LPT_VMSCTL_BUSPARAM; + +HWND VMSCTL_CreateBUS(HWND hParent, long Ident, long x0,long y0, LPT_VMSCTL_BUSPARAM lpparam); + +long VMSCTL_PositionBUS(HWND hw, long x0,long y0, long dx, long dy, long fShow); + + +typedef struct tagVMSCTL_BUS_DATA +{ + WCHAR bus_namew[64]; + char bus_nickname[8]; + float bus_gain; + long bus_mute; + long bus_monitor; + long bus_fMonitorSupport; +} T_VMSCTL_BUS_DATA, *PT_VMSCTL_BUS_DATA, *LPT_VMSCTL_BUS_DATA; + +long VMSCTL_SetDataBUS(HWND hw, LPT_VMSCTL_BUS_DATA pData, long fUpdateAll); +long VMSCTL_ResetDataBUS(HWND hw); + +long VMSCTL_SetRTDataBUS(HWND hw, LPT_VMSCTL_RT_DATA pRTData); + + +//End Of Header. + +#ifdef __cplusplus +} +#endif + + +#endif /*__VMSCTL_H__*/ + + diff --git a/vmr_streamer/source/vban_cmd.c b/vmr_streamer/source/vban_cmd.c new file mode 100644 index 0000000..96adc6e --- /dev/null +++ b/vmr_streamer/source/vban_cmd.c @@ -0,0 +1,600 @@ +/**********************************************************************************/ +/* VBAN COMMAND V.Burel(c)2015-2021 */ +/**********************************************************************************/ +/* */ +/* THIS PROGRAM PROVIDES A MINIMAL VBAN-SERVICE */ +/* PROVIDES A FUNCTION TO SEND VBAN-TEXT REQUEST */ +/* PROVIDES A METHOD TO MANAGE VBAN RT-PACKET */ +/* */ +/* This program example shows */ +/* - How to use Windows Socket */ +/* - How to send and recieve VBAN request */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* COMPILATION DIRECTIVES: */ +/* */ +/* To compile With Microsoft VC2005 or higher, you need to create an */ +/* empty Win32 project with the following options: */ +/* - Configuration Properties / General : Char Set = NOT SET */ +/* - Configuration Properties / C/C++ / Preprocessor : _CRT_SECURE_NO_DEPRECATE */ +/* */ +/* LINKER: Ws2_32.lib */ +/* */ +/* This source code can be compiled for 32bit or 64 bits targets as well */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* LICENSING: VoicemeeterRemote.dll usage is driven by a license agreement */ +/* given in VoicemeeterRemoteAPI.pdf or readme.txt */ +/* THIS SOURCE CODE CAN BE USED ONLY IN A PROGRAM USING VOICEMEETER */ +/* REMOTE API */ +/* */ +/*--------------------------------------------------------------------------------*/ + + +#ifndef __cplusplus +#ifndef STRICT +#define STRICT +#endif +#endif + +#include +//#include +#include +//#include +#include + +#include "vban_cmd.h" +#include "VoicemeeterRemote.h" + + + +typedef struct tagVBANCMD_CONTEXT +{ + long mode; + BOOL ThreadStarted, finitlib; + HANDLE Thread_handle; + DWORD Thread_ident; + BOOL Thread_flagOK; + long WSAStartupReply; + WSADATA WSAData; + SOCKET vban_socket; + unsigned long IPv4Address; + unsigned short UDPport; + long fPortUsedByOtherApplication; + CRITICAL_SECTION ctc_section; + T_VBAN_VMRT_PACKET CurrentRTPPacket; + long CurrentRTPPacket_dirtyflag; + DWORD CurrentRTPPacket_counter; + T_VBAN_VMRT_PACKET PublicRTPPacket; + unsigned char last_voicemeeterType; + + T_VBAN_HEADER VBANHeader; + +} T_VBANCMD_CONTEXT, * PT_VBANCMD_CONTEXT, * LPT_VBANCMD_CONTEXT; + + +static T_VBANCMD_CONTEXT G_vban_ctx = { 0, 0, 0, NULL }; + + +long VBANCMD_GetIncomingRequestCounter(void) +{ + return G_vban_ctx.CurrentRTPPacket_counter; +} + +/******************************************************************************/ +/* SOCKET TOOL */ +/******************************************************************************/ + + +static long VBAN_CloseSocket(SOCKET* psocket) +{ + long rep; + if (*psocket != INVALID_SOCKET) + { + shutdown(*psocket, 2); + rep = closesocket(*psocket); + *psocket = INVALID_SOCKET; + } + return 0; +} + + +static long VBAN_SetSocketNonblocking(SOCKET socket) +{ + u_long flags; + flags = 1; + return ioctlsocket(socket, FIONBIO, &flags); +} + +static long VBAN_InitSocket(LPT_VBANCMD_CONTEXT lpctx, SOCKET* psocket, unsigned short nuPort, long SocketBufferSize) +{ + SOCKADDR_IN local_sin; + long rep, nnn; + long nsize, nbByte; + long nError; + + *psocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (*psocket == INVALID_SOCKET) return -1; + //set non blocking socket + rep = VBAN_SetSocketNonblocking(*psocket); + if (rep != 0) + { + closesocket(*psocket); + *psocket = INVALID_SOCKET; + return -2; + } + //disable debug mode + nsize = sizeof(rep); + nnn = getsockopt(*psocket, SOL_SOCKET, SO_DEBUG, (char*)&(rep), &nsize); + if (nnn == 0) + { + if (rep != 0) + { + rep = 0; + nsize = sizeof(rep); + setsockopt(*psocket, SOL_SOCKET, SO_DEBUG, (char*)&rep, nsize); + } + } + //set Time Out = ZERO. + rep = 0; + nsize = sizeof(rep); + setsockopt(*psocket, SOL_SOCKET, SO_SNDTIMEO, (char*)&rep, nsize); + setsockopt(*psocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&rep, nsize); + + //set Socket buffer size + nsize = sizeof(SocketBufferSize); + setsockopt(*psocket, SOL_SOCKET, SO_RCVBUF, (char*)&SocketBufferSize, nsize); + + //128 kB buffer is senough to send VBAN-TEXT request + nbByte = 1024 * 128; + nsize = sizeof(nbByte); + setsockopt(*psocket, SOL_SOCKET, SO_SNDBUF, (char*)&nbByte, nsize); + + //define socket port and ipaddress to + memset(&local_sin, 0, sizeof(SOCKADDR_IN)); + local_sin.sin_family = AF_INET; + local_sin.sin_port = htons((unsigned short)nuPort); + local_sin.sin_addr.s_addr = htonl(INADDR_ANY); + + lpctx->fPortUsedByOtherApplication = 0; + if (bind(*psocket, (struct sockaddr*)&(local_sin), sizeof(local_sin)) == SOCKET_ERROR) + { + nError = WSAGetLastError(); + if (nError == WSAEADDRINUSE) + { + lpctx->fPortUsedByOtherApplication = 1; + } + closesocket(*psocket); + *psocket = INVALID_SOCKET; + return -5; + } + return 0; +} + + +/******************************************************************************/ +/* FUNCTIONS REQUEST */ +/******************************************************************************/ + +long VBANCMD_SendRequest_String(char* pString) +{ + SOCKADDR_IN dest; + LPT_VBAN_HEADER lpHeader; + char Buffer[2048]; + long rep, nbByte; + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + if (lpctx->ThreadStarted == 0) return -9; + if (pString == NULL) return -10; + + lpHeader = (LPT_VBAN_HEADER)Buffer; + // build VBAN Header from our current VBAN Header + *lpHeader = lpctx->VBANHeader; + lpctx->VBANHeader.nuFrame++; + strcpy((char*)(lpHeader + 1), pString); + //send request + memset(&dest, 0, sizeof(SOCKADDR_IN)); + dest.sin_family = AF_INET; + dest.sin_port = htons(lpctx->UDPport); + dest.sin_addr.s_addr = lpctx->IPv4Address; + + nbByte = sizeof(T_VBAN_HEADER) + (long)strlen(pString); + rep = sendto(lpctx->vban_socket, Buffer, nbByte, 0, (struct sockaddr*)&dest, sizeof(dest)); + if (rep == SOCKET_ERROR) return -20; + return 0; +} + +long VBANCMD_SendRequest_Float(char* szParam, float value) +{ + char String[512]; + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + + if (szParam == NULL) return -1; + if (szParam[0] == 0) return -2; + + sprintf(String, "%s = %f;", szParam, value); + return VBANCMD_SendRequest_String(String); +} + + + +long VBANCMD_SendRequest_RegisterRTPacket(unsigned char sTimeOut, char* szVBANStreamName) +{ + SOCKADDR_IN dest; + T_VBAN_HEADER Header; + long rep, nbByte; + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + if (lpctx->ThreadStarted == 0) return -9; + + Header.vban = 'NABV'; + Header.format_SR = 0x60; + Header.format_nbs = 0; + Header.format_nbc = VBAN_SERVICE_RTPACKETREGISTER; + Header.format_bit = sTimeOut & 0x000000FF; + if (szVBANStreamName != NULL) strcpy(Header.streamname, "Register RTP"); + else strncpy(Header.streamname, szVBANStreamName, 16); + Header.nuFrame = 0; + + + //send request + memset(&dest, 0, sizeof(SOCKADDR_IN)); + dest.sin_family = AF_INET; + dest.sin_port = htons(lpctx->UDPport); + dest.sin_addr.s_addr = lpctx->IPv4Address; + + nbByte = sizeof(T_VBAN_HEADER); + rep = sendto(lpctx->vban_socket, (char*)&Header, nbByte, 0, (struct sockaddr*)&dest, sizeof(dest)); + if (rep == SOCKET_ERROR) return -20; + return 0; +} + + + + + + +/******************************************************************************/ +/* CURRENT PARAMETERS FUNCTIONS */ +/******************************************************************************/ + +long VBANCMD_IsParameterDirty(void) +{ + unsigned char vmType; + long reply; + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + reply = lpctx->CurrentRTPPacket_dirtyflag; + if (reply != 0) + { + // if dirty we copy our buffer in public structure + EnterCriticalSection(&(lpctx->ctc_section)); + lpctx->PublicRTPPacket = lpctx->CurrentRTPPacket; + LeaveCriticalSection(&(lpctx->ctc_section)); + } + // to fit the same behavior than the remote API function + vmType = lpctx->PublicRTPPacket.voicemeeterType; + if (vmType == 0) reply = -1; + if (lpctx->last_voicemeeterType != vmType) reply = -2; + lpctx->last_voicemeeterType = vmType; + return reply; +} + +// then all information will come directly from our public RTPacket structure + +long VBANCMD_GetVoicemeeterType(unsigned long* pType) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + *pType = (unsigned long)(lpctx->PublicRTPPacket.voicemeeterType); + return 0; +} + +long VBANCMD_GetVoicemeeterVersion(unsigned long* pVersion) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + *pVersion = lpctx->PublicRTPPacket.voicemeeterVersion; + return 0; +} + + +void TOOL_ConvertUTF8ToWCHAR(char* UTF8_String, WCHAR* wString, long nbCharMax) +{ + if (UTF8_String[0] == 0) + { + wString[0] = 0; + return; + } + memset(wString, 0, nbCharMax * sizeof(WCHAR)); + MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)UTF8_String, (int)strlen(UTF8_String), wString, nbCharMax); + wString[nbCharMax - 1] = 0; +} + + +long VBANCMD_GetBusLabel(long index, WCHAR* pwsz) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + TOOL_ConvertUTF8ToWCHAR(lpctx->PublicRTPPacket.busLabelUTF8c60[index], pwsz, 64); + return 0; +} + +long VBANCMD_GetBusGain(long index, float* pValue) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + *pValue = ((float)lpctx->PublicRTPPacket.busGaindB100[index]) * 0.01f; + return 0; +} + +long VBANCMD_GetBusSel(long index, float* pValue) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + if ((lpctx->PublicRTPPacket.busState[index] & VMRTSTATE_MODE_SEL) != 0) *pValue = 1.0f; + else *pValue = 0.0f; + return 0; +} + +long VBANCMD_GetBusMute(long index, float* pValue) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + if ((lpctx->PublicRTPPacket.busState[index] & VMRTSTATE_MODE_MUTE) != 0) *pValue = 1.0f; + else *pValue = 0.0f; + return 0; +} + +long VBANCMD_GetBusMonitor(long index, float* pValue) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + if ((lpctx->PublicRTPPacket.busState[index] & VMRTSTATE_MODE_MONITOR) != 0) *pValue = 1.0f; + else *pValue = 0.0f; + return 0; +} + + + +long VBANCMD_GetStripLabel(long index, WCHAR* pwsz) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + TOOL_ConvertUTF8ToWCHAR(lpctx->PublicRTPPacket.stripLabelUTF8c60[index], pwsz, 64); + return 0; +} + +long VBANCMD_GetStripGain(long index, float* pValue) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + *pValue = ((float)lpctx->PublicRTPPacket.stripGaindB100Layer1[index]) * 0.01f; + return 0; +} + +long VBANCMD_GetStripGainLayer(long index, long layer, float* pValue) +{ + short* pGain; + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + + pGain = lpctx->PublicRTPPacket.stripGaindB100Layer1; + pGain = pGain + (layer * 8); + *pValue = ((float)pGain[index]) * 0.01f; + return 0; +} + +long VBANCMD_GetStripMute(long index, float* pValue) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + if ((lpctx->PublicRTPPacket.stripState[index] & VMRTSTATE_MODE_MUTE) != 0) *pValue = 1.0f; + else *pValue = 0.0f; + return 0; +} + + +static long G_MaskBusAssign_v1[8] = { VMRTSTATE_MODE_BUSA1, VMRTSTATE_MODE_BUSB1, 0, 0, 0, 0, 0, 0 }; +static long G_MaskBusAssign_v2[8] = { VMRTSTATE_MODE_BUSA1, VMRTSTATE_MODE_BUSA2, VMRTSTATE_MODE_BUSA3, VMRTSTATE_MODE_BUSB1, VMRTSTATE_MODE_BUSB2, 0, 0, 0 }; +static long G_MaskBusAssign_v3[8] = { VMRTSTATE_MODE_BUSA1, VMRTSTATE_MODE_BUSA2, VMRTSTATE_MODE_BUSA3, VMRTSTATE_MODE_BUSA4, VMRTSTATE_MODE_BUSA5, VMRTSTATE_MODE_BUSB1, VMRTSTATE_MODE_BUSB2, VMRTSTATE_MODE_BUSB3 }; + +long VBANCMD_GetStripAssignation(long index, long nBus, float* pValue) +{ + long* pMask; + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + pMask = NULL; + if (lpctx->PublicRTPPacket.voicemeeterType == 1) pMask = G_MaskBusAssign_v1; + if (lpctx->PublicRTPPacket.voicemeeterType == 2) pMask = G_MaskBusAssign_v2; + if (lpctx->PublicRTPPacket.voicemeeterType == 3) pMask = G_MaskBusAssign_v3; + + if ((lpctx->PublicRTPPacket.stripState[index] & pMask[nBus]) != 0) *pValue = 1.0f; + else *pValue = 0.0f; + return 0; +} + + +long VBANCMD_GetLevel(long index, long nuChannel, float* pLevelDB) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + switch (index) + { + case 0: + *pLevelDB = ((float)lpctx->PublicRTPPacket.inputLeveldB100[nuChannel]) * 0.01f; + return 0; + case 3: + *pLevelDB = ((float)lpctx->PublicRTPPacket.outputLeveldB100[nuChannel]) * 0.01f; + return 0; + default: + *pLevelDB = -100.0f; + break; + } + return -1; +} + + +/******************************************************************************/ +/* START / STOP VBAN CLIENT */ +/******************************************************************************/ + + +static DWORD WINAPI VBANCMD_Thread(LPT_VBANCMD_CONTEXT lpctx) +{ + SOCKADDR_IN from; + LPT_VBAN_HEADER lpHeader; + long rep, datasize, nbByte, mstemp, fromlen; + char Buffer[2048]; + unsigned char protocol; + DWORD current_ipv4; + HANDLE h_t; + h_t = GetCurrentThread(); + SetThreadPriority(h_t, THREAD_PRIORITY_HIGHEST); + CloseHandle(h_t); + lpHeader = (LPT_VBAN_HEADER)Buffer; + current_ipv4 = lpctx->IPv4Address; + while (lpctx->Thread_flagOK == TRUE) + { + mstemp = 1; + fromlen = sizeof(SOCKADDR_IN); + rep = recvfrom(lpctx->vban_socket, Buffer, 2048, 0, (struct sockaddr*)&from, &fromlen); + if (rep != SOCKET_ERROR) + { + mstemp = 0; + // if we recieve something + datasize = rep; + if ((datasize > sizeof(T_VBAN_HEADER)) && (from.sin_addr.S_un.S_addr == current_ipv4)) + { + // if it's a VBAN packet + if (lpHeader->vban == 'NABV') + { + protocol = lpHeader->format_SR & VBAN_PROTOCOL_MASK; + // if it's a VBAN-SERVICE packet + if (protocol == VBAN_PROTOCOL_SERVICE) + { + if (lpHeader->format_nbc == VBAN_SERVICE_RTPACKET) + { + // if it's a RTPacket + nbByte = datasize - sizeof(T_VBAN_HEADER); + if (nbByte >= sizeof(T_VBAN_VMRT_PACKET)) + { + // just copy it into our context structure and set dirty flag + EnterCriticalSection(&(lpctx->ctc_section)); + memcpy(&(lpctx->CurrentRTPPacket), lpHeader + 1, sizeof(T_VBAN_VMRT_PACKET)); + lpctx->CurrentRTPPacket_dirtyflag = 1; + lpctx->CurrentRTPPacket_counter++; + LeaveCriticalSection(&(lpctx->ctc_section)); + } + } + } + } + } + } + // if we did nothing we wait for 1 ms, otherwise we wait for next O/S scheduler cycle + Sleep(mstemp); + } + return 0; +} + + +long VBANCMD_StopThread(void) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + if (lpctx->finitlib == 0) return -1; + + lpctx->ThreadStarted = 0; + // Stop Thread + lpctx->Thread_flagOK = FALSE; + if (lpctx->Thread_handle != NULL) + { + WaitForSingleObject(lpctx->Thread_handle, INFINITE); + CloseHandle(lpctx->Thread_handle); + lpctx->Thread_handle = NULL; + } + // Close Soccket + VBAN_CloseSocket(&(lpctx->vban_socket)); + + memset(&(lpctx->CurrentRTPPacket), 0, sizeof(T_VBAN_VMRT_PACKET)); + memset(&(lpctx->PublicRTPPacket), 0, sizeof(T_VBAN_VMRT_PACKET)); + lpctx->CurrentRTPPacket_dirtyflag = 1; + return 0; +} + + +long VBANCMD_StartThread(char* szVBANStreamName, char* szIPAddressTo, unsigned short UDPport) +{ + long rep, reply = 0; + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + if (lpctx->finitlib == 0) return -1; + VBANCMD_StopThread(); + + // build our header to send VBAN-TEXT request + lpctx->VBANHeader.vban = 'NABV'; + lpctx->VBANHeader.format_SR = 0x52; + lpctx->VBANHeader.format_nbs = 0; + lpctx->VBANHeader.format_nbc = 0; + lpctx->VBANHeader.format_bit = 0x10; + if (szVBANStreamName == NULL) strcpy(lpctx->VBANHeader.streamname, "Command1"); + else strncpy(lpctx->VBANHeader.streamname, szVBANStreamName, 16); + lpctx->VBANHeader.nuFrame = 0; + + // Init Socket with 2 MB on RCV buffer + lpctx->IPv4Address = inet_addr(szIPAddressTo); + lpctx->UDPport = UDPport; + rep = VBAN_InitSocket(lpctx, &(lpctx->vban_socket), UDPport, 1024 * 1024 * 2); + if (rep != 0) return -2; + + // Start Thread + lpctx->Thread_flagOK = TRUE; + lpctx->Thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)VBANCMD_Thread, (void*)lpctx, 0, &(lpctx->Thread_ident)); + if (lpctx->Thread_handle == NULL) + { + reply = -100; + } + // we set STARTED flag only if all is good. + lpctx->ThreadStarted = 1; + return reply; +} + + + + + + + +/******************************************************************************/ +/* INIT / END LIB */ +/******************************************************************************/ + +long VBANCMD_EndLib(void) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + + if (lpctx->finitlib == 0) return 0; + VBANCMD_StopThread(); + + DeleteCriticalSection(&(lpctx->ctc_section)); + memset(lpctx, 0, sizeof(T_VBANCMD_CONTEXT)); + return 0; +} + +long VBANCMD_InitLib(long mode) +{ + LPT_VBANCMD_CONTEXT lpctx; + lpctx = &G_vban_ctx; + memset(lpctx, 0, sizeof(T_VBANCMD_CONTEXT)); + lpctx->mode = mode; + lpctx->finitlib = 1; + lpctx->WSAStartupReply = WSAStartup(MAKEWORD(1, 1), &(lpctx->WSAData)); + InitializeCriticalSection(&(lpctx->ctc_section)); + lpctx->vban_socket = INVALID_SOCKET; + return 0; +} + diff --git a/vmr_streamer/source/vban_cmd.h b/vmr_streamer/source/vban_cmd.h new file mode 100644 index 0000000..e97818a --- /dev/null +++ b/vmr_streamer/source/vban_cmd.h @@ -0,0 +1,121 @@ +/**********************************************************************************/ +/* VBAN COMMAND V.Burel(c)2015-2021 */ +/**********************************************************************************/ +/* */ +/* THIS PROGRAM PROVIDES A MINIMAL VBAN-SERVICE */ +/* PROVIDES A FUNCTION TO SEND VBAN-TEXT REQUEST */ +/* PROVIDES A METHOD TO MANAGE VBAN RT-PACKET */ +/* */ +/* This program example shows */ +/* - How to use Windows Socket */ +/* - How to send and recieve VBAN request */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* COMPILATION DIRECTIVES: */ +/* */ +/* To compile With Microsoft VC2005 or higher, you need to create an */ +/* empty Win32 project with the following options: */ +/* - Configuration Properties / General : Char Set = NOT SET */ +/* - Configuration Properties / C/C++ / Preprocessor : _CRT_SECURE_NO_DEPRECATE */ +/* */ +/* LINKER: Ws2_32.lib */ +/* */ +/* This source code can be compiled for 32bit or 64 bits targets as well */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* LICENSING: VoicemeeterRemote.dll usage is driven by a license agreement */ +/* given in VoicemeeterRemoteAPI.pdf or readme.txt */ +/* THIS SOURCE CODE CAN BE USED ONLY IN A PROGRAM USING VOICEMEETER */ +/* REMOTE API */ +/* */ +/*--------------------------------------------------------------------------------*/ + +#ifndef __VBAN_CMD_H__ +#define __VBAN_CMD_H__ + + +#pragma pack(1) + +struct tagVBAN_HEADER +{ + unsigned long vban; // contains 'V' 'B', 'A', 'N' + unsigned char format_SR; // SR index (see SRList above) + unsigned char format_nbs; // nb sample per frame (1 to 256) + unsigned char format_nbc; // nb channel (1 to 256) + unsigned char format_bit; // mask = 0x07 (see DATATYPE table below) + char streamname[16]; // stream name + unsigned long nuFrame; // growing frame number +}; + +#pragma pack() + +typedef struct tagVBAN_HEADER T_VBAN_HEADER; +typedef struct tagVBAN_HEADER* PT_VBAN_HEADER; +typedef struct tagVBAN_HEADER* LPT_VBAN_HEADER; + + +#define VBAN_PROTOCOL_MASK 0xE0 +#define VBAN_PROTOCOL_SERVICE 0x60 + +#define VBAN_SERVICE_RTPACKETREGISTER 32 +#define VBAN_SERVICE_RTPACKET 33 + + +/******************************************************************************/ +/* FUNCTIONS */ +/******************************************************************************/ + +long VBANCMD_SendRequest_String(char* pString); +long VBANCMD_SendRequest_Float(char* szParam, float value); + +long VBANCMD_SendRequest_RegisterRTPacket(unsigned char sTimeOut, char* szVBANStreamName); + +// to check the VBAN connection works +long VBANCMD_GetIncomingRequestCounter(void); + + +/******************************************************************************/ +/* CURRENT PARAMETERS FUNCTIONS */ +/******************************************************************************/ +// we provide same function than Voicemeeter Remote API to simplify the process + +long VBANCMD_IsParameterDirty(void); + +long VBANCMD_GetVoicemeeterType(unsigned long* pType); +long VBANCMD_GetVoicemeeterVersion(unsigned long* pVersion); + +long VBANCMD_GetBusLabel(long index, WCHAR* pwsz); +long VBANCMD_GetBusGain(long index, float* pValue); +long VBANCMD_GetBusSel(long index, float* pValue); +long VBANCMD_GetBusMute(long index, float* pValue); +long VBANCMD_GetBusMonitor(long index, float* pValue); + + +long VBANCMD_GetStripLabel(long index, WCHAR* pwsz); +long VBANCMD_GetStripGain(long index, float* pValue); +long VBANCMD_GetStripGainLayer(long index, long layer, float* pValue); +long VBANCMD_GetStripMute(long index, float* pValue); +long VBANCMD_GetStripAssignation(long index, long nBus, float* pValue); + +long VBANCMD_GetLevel(long index, long nuChannel, float* pLevelDB); + + + +/******************************************************************************/ +/* START / STOP THREAD */ +/******************************************************************************/ + +long VBANCMD_StartThread(char* szVBANStreamName, char* szIPAddressTo, unsigned short UDPport); +long VBANCMD_StopThread(void); + +/******************************************************************************/ +/* INIT / END LIB */ +/******************************************************************************/ + +long VBANCMD_EndLib(void); +long VBANCMD_InitLib(long mode); + + +#endif /* __VBAN_CMD_H__*/ \ No newline at end of file diff --git a/vmr_streamer/source/vmr_streamer.c b/vmr_streamer/source/vmr_streamer.c new file mode 100644 index 0000000..1fd7dab --- /dev/null +++ b/vmr_streamer/source/vmr_streamer.c @@ -0,0 +1,2768 @@ +/*--------------------------------------------------------------------------------*/ +/* VMR example: Voicemeeter Custom GUI for Streamers */ +/*--------------------------------------------------------------------------------*/ +/* 'C' Sample Code to make a Voicemeeter Custom GUI V.Burel (c)2016-2021 */ +/* */ +/* THIS PROGRAM PROVIDES A GUI TO CONTROL VOICEMEETER */ +/* PROVIDES A SIMPLIFIED GUI WITH ONLY USED STRIP/BUS */ +/* PROVIDES A RESIZABLE GRAPHIC USER INTERFACE */ +/* */ +/* This program example shows */ +/* - How to link VoicemeeterRemote.dll */ +/* - How to Login / logout */ +/* - How to Manage regular parameters to make a custom controller. */ +/* - How to make a VBAN layer to also manage distant Voicemeeter. */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* COMPILATION DIRECTIVES: */ +/* */ +/* To compile With Microsoft VC2005 or higher, you need to create an */ +/* empty Win32 project with the following options: */ +/* - Configuration Properties / General : Char Set = NOT SET */ +/* - Configuration Properties / C/C++ / Preprocessor : _CRT_SECURE_NO_DEPRECATE */ +/* */ +/* This source code can be compiled for 32bit or 64 bits targets as well */ +/* WARNING: FOR 64x COMPILATION, ADD PREPROCESSOR DEFINE: VB_X64 */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* LICENSING: VoicemeeterRemote.dll usage is driven by a license agreement */ +/* given in VoicemeeterRemoteAPI.pdf or readme.txt */ +/* This Source Code can be used only in a program using Voicemeeter */ +/* Remote API. */ +/* */ +/*--------------------------------------------------------------------------------*/ + + +#ifndef __cplusplus + #ifndef STRICT + #define STRICT + #endif +#endif + +#include +#include +#include + +#include "VoicemeeterRemote.h" +#include "vmr_streamer.h" +#include "streamer_ctrl.h" +#include "vban_cmd.h" + +/*******************************************************************************/ +/** GET VOICEMEETER DIRECTORY **/ +/*******************************************************************************/ + +static char uninstDirKey[]="SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"; + +#define INSTALLER_UNINST_KEY "VB:Voicemeeter {17359A74-1236-5467}" + + +void RemoveNameInPath(char * szPath) +{ + long ll; + ll=(long)strlen(szPath); + while ((ll>0) && (szPath[ll]!='\\')) ll--; + if (szPath[ll] == '\\') szPath[ll]=0; +} + +#ifndef KEY_WOW64_32KEY + #define KEY_WOW64_32KEY 0x0200 +#endif + +BOOL __cdecl RegistryGetVoicemeeterFolder(char * szDir) +{ + char szKey[256]; + char sss[1024]; + DWORD nnsize=1024; + HKEY hkResult; + LONG rep; + DWORD pptype=REG_SZ; + sss[0]=0; + + // build Voicemeeter uninstallation key + strcpy(szKey,uninstDirKey); + strcat(szKey,"\\"); + strcat(szKey,INSTALLER_UNINST_KEY); + + // open key + rep=RegOpenKeyEx(HKEY_LOCAL_MACHINE,szKey,0, KEY_READ, &hkResult); + if (rep != ERROR_SUCCESS) + { + // if not present we consider running in 64bit mode and force to read 32bit registry + rep=RegOpenKeyEx(HKEY_LOCAL_MACHINE,szKey,0, KEY_READ | KEY_WOW64_32KEY, &hkResult); + } + if (rep != ERROR_SUCCESS) return FALSE; + // read uninstall profram path + rep=RegQueryValueEx(hkResult,"UninstallString",0,&pptype,(unsigned char *)sss,&nnsize); + RegCloseKey(hkResult); + + if (pptype != REG_SZ) return FALSE; + if (rep != ERROR_SUCCESS) return FALSE; + // remove name to get the path only + RemoveNameInPath(sss); + if (nnsize>512) nnsize=512; + strncpy(szDir,sss,nnsize); + + return TRUE; +} + + + + + + +/*******************************************************************************/ +/** GET DLL INTERFACE **/ +/*******************************************************************************/ + +static HMODULE G_H_Module=NULL; +static T_VBVMR_INTERFACE iVMR; + +//if we directly link source code (for vb-audio development only) +#ifdef VBUSE_LOCALLIB + + long InitializeDLLInterfaces(void) + { + iVMR.VBVMR_Login =VBVMR_Login; + iVMR.VBVMR_Logout =VBVMR_Logout; + iVMR.VBVMR_RunVoicemeeter =VBVMR_RunVoicemeeter; + iVMR.VBVMR_GetVoicemeeterType =VBVMR_GetVoicemeeterType; + iVMR.VBVMR_GetVoicemeeterVersion =VBVMR_GetVoicemeeterVersion; + iVMR.VBVMR_IsParametersDirty =VBVMR_IsParametersDirty; + iVMR.VBVMR_GetParameterFloat =VBVMR_GetParameterFloat; + iVMR.VBVMR_GetParameterStringA =VBVMR_GetParameterStringA; + iVMR.VBVMR_GetParameterStringW =VBVMR_GetParameterStringW; + + iVMR.VBVMR_GetLevel =VBVMR_GetLevel; + iVMR.VBVMR_GetMidiMessage =VBVMR_GetMidiMessage; + iVMR.VBVMR_SetParameterFloat =VBVMR_SetParameterFloat; + iVMR.VBVMR_SetParameters =VBVMR_SetParameters; + iVMR.VBVMR_SetParametersW =VBVMR_SetParametersW; + iVMR.VBVMR_SetParameterStringA =VBVMR_SetParameterStringA; + iVMR.VBVMR_SetParameterStringW =VBVMR_SetParameterStringW; + + iVMR.VBVMR_Output_GetDeviceNumber =VBVMR_Output_GetDeviceNumber; + iVMR.VBVMR_Output_GetDeviceDescA =VBVMR_Output_GetDeviceDescA; + iVMR.VBVMR_Output_GetDeviceDescW =VBVMR_Output_GetDeviceDescW; + iVMR.VBVMR_Input_GetDeviceNumber =VBVMR_Input_GetDeviceNumber; + iVMR.VBVMR_Input_GetDeviceDescA =VBVMR_Input_GetDeviceDescA; + iVMR.VBVMR_Input_GetDeviceDescW =VBVMR_Input_GetDeviceDescW; + + iVMR.VBVMR_AudioCallbackRegister =VBVMR_AudioCallbackRegister; + iVMR.VBVMR_AudioCallbackStart =VBVMR_AudioCallbackStart; + iVMR.VBVMR_AudioCallbackStop =VBVMR_AudioCallbackStop; + iVMR.VBVMR_AudioCallbackUnregister =VBVMR_AudioCallbackUnregister; + return 0; + } + +//Dynamic link to DLL in 'C' (regular use) +#else + + long InitializeDLLInterfaces(void) + { + char szDllName[1024]; + memset(&iVMR,0,sizeof(T_VBVMR_INTERFACE)); + + //get folder where is installed Voicemeeter + if (RegistryGetVoicemeeterFolder(szDllName) == FALSE) + { + // voicemeeter not installed + return -100; + } + //use right dll according O/S type + if (sizeof(void*) == 8) strcat(szDllName,"\\VoicemeeterRemote64.dll"); + else strcat(szDllName,"\\VoicemeeterRemote.dll"); + + // Load Dll + G_H_Module=LoadLibrary(szDllName); + if (G_H_Module == NULL) return -101; + + // Get function pointers + iVMR.VBVMR_Login =(T_VBVMR_Login)GetProcAddress(G_H_Module,"VBVMR_Login"); + iVMR.VBVMR_Logout =(T_VBVMR_Logout)GetProcAddress(G_H_Module,"VBVMR_Logout"); + iVMR.VBVMR_RunVoicemeeter =(T_VBVMR_RunVoicemeeter)GetProcAddress(G_H_Module,"VBVMR_RunVoicemeeter"); + iVMR.VBVMR_GetVoicemeeterType =(T_VBVMR_GetVoicemeeterType)GetProcAddress(G_H_Module,"VBVMR_GetVoicemeeterType"); + iVMR.VBVMR_GetVoicemeeterVersion =(T_VBVMR_GetVoicemeeterVersion)GetProcAddress(G_H_Module,"VBVMR_GetVoicemeeterVersion"); + + iVMR.VBVMR_IsParametersDirty =(T_VBVMR_IsParametersDirty)GetProcAddress(G_H_Module,"VBVMR_IsParametersDirty"); + iVMR.VBVMR_GetParameterFloat =(T_VBVMR_GetParameterFloat)GetProcAddress(G_H_Module,"VBVMR_GetParameterFloat"); + iVMR.VBVMR_GetParameterStringA =(T_VBVMR_GetParameterStringA)GetProcAddress(G_H_Module,"VBVMR_GetParameterStringA"); + iVMR.VBVMR_GetParameterStringW =(T_VBVMR_GetParameterStringW)GetProcAddress(G_H_Module,"VBVMR_GetParameterStringW"); + iVMR.VBVMR_GetLevel =(T_VBVMR_GetLevel)GetProcAddress(G_H_Module,"VBVMR_GetLevel"); + iVMR.VBVMR_GetMidiMessage =(T_VBVMR_GetMidiMessage)GetProcAddress(G_H_Module,"VBVMR_GetMidiMessage"); + + iVMR.VBVMR_SetParameterFloat =(T_VBVMR_SetParameterFloat)GetProcAddress(G_H_Module,"VBVMR_SetParameterFloat"); + iVMR.VBVMR_SetParameters =(T_VBVMR_SetParameters)GetProcAddress(G_H_Module,"VBVMR_SetParameters"); + iVMR.VBVMR_SetParametersW =(T_VBVMR_SetParametersW)GetProcAddress(G_H_Module,"VBVMR_SetParametersW"); + iVMR.VBVMR_SetParameterStringA =(T_VBVMR_SetParameterStringA)GetProcAddress(G_H_Module,"VBVMR_SetParameterStringA"); + iVMR.VBVMR_SetParameterStringW =(T_VBVMR_SetParameterStringW)GetProcAddress(G_H_Module,"VBVMR_SetParameterStringW"); + + iVMR.VBVMR_Output_GetDeviceNumber =(T_VBVMR_Output_GetDeviceNumber)GetProcAddress(G_H_Module,"VBVMR_Output_GetDeviceNumber"); + iVMR.VBVMR_Output_GetDeviceDescA =(T_VBVMR_Output_GetDeviceDescA)GetProcAddress(G_H_Module,"VBVMR_Output_GetDeviceDescA"); + iVMR.VBVMR_Output_GetDeviceDescW =(T_VBVMR_Output_GetDeviceDescW)GetProcAddress(G_H_Module,"VBVMR_Output_GetDeviceDescW"); + iVMR.VBVMR_Input_GetDeviceNumber =(T_VBVMR_Input_GetDeviceNumber)GetProcAddress(G_H_Module,"VBVMR_Input_GetDeviceNumber"); + iVMR.VBVMR_Input_GetDeviceDescA =(T_VBVMR_Input_GetDeviceDescA)GetProcAddress(G_H_Module,"VBVMR_Input_GetDeviceDescA"); + iVMR.VBVMR_Input_GetDeviceDescW =(T_VBVMR_Input_GetDeviceDescW)GetProcAddress(G_H_Module,"VBVMR_Input_GetDeviceDescW"); + + // check pointers are valid + if (iVMR.VBVMR_Login == NULL) return -1; + if (iVMR.VBVMR_Logout == NULL) return -2; + if (iVMR.VBVMR_RunVoicemeeter == NULL) return -2; + if (iVMR.VBVMR_GetVoicemeeterType == NULL) return -3; + if (iVMR.VBVMR_GetVoicemeeterVersion == NULL) return -4; + if (iVMR.VBVMR_IsParametersDirty == NULL) return -5; + if (iVMR.VBVMR_GetParameterFloat == NULL) return -6; + if (iVMR.VBVMR_GetParameterStringA == NULL) return -7; + if (iVMR.VBVMR_GetParameterStringW == NULL) return -8; + if (iVMR.VBVMR_GetLevel == NULL) return -9; + if (iVMR.VBVMR_SetParameterFloat == NULL) return -10; + if (iVMR.VBVMR_SetParameters == NULL) return -11; + if (iVMR.VBVMR_SetParametersW == NULL) return -12; + if (iVMR.VBVMR_SetParameterStringA == NULL) return -13; + if (iVMR.VBVMR_SetParameterStringW == NULL) return -14; + if (iVMR.VBVMR_GetMidiMessage == NULL) return -15; + + if (iVMR.VBVMR_Output_GetDeviceNumber == NULL) return -30; + if (iVMR.VBVMR_Output_GetDeviceDescA == NULL) return -31; + if (iVMR.VBVMR_Output_GetDeviceDescW == NULL) return -32; + if (iVMR.VBVMR_Input_GetDeviceNumber == NULL) return -33; + if (iVMR.VBVMR_Input_GetDeviceDescA == NULL) return -34; + if (iVMR.VBVMR_Input_GetDeviceDescW == NULL) return -35; + + return 0; + } + + +#endif + + +/*******************************************************************************/ +/* CONSTANT FUNCTION OF VOICEMEETER TYPE */ +/*******************************************************************************/ + + +static char * G_szBUSNameList_v1[2]={"A", "B"}; +static char * G_szBUSNameList_v2[5]={"A1", "A2", "A3", "B1", "B2"}; +static char * G_szBUSNameList_v3[8]={"A1", "A2", "A3", "A4", "A5", "B1", "B2", "B3"}; + +static char * G_szStripNameList_v1[3]={"IN #1", "IN #2", "VIN #1"}; +static char * G_szStripNameList_v2[5]={"IN #1", "IN #2", "IN #3", "VIN #1", "VIN #2"}; +static char * G_szStripNameList_v3[8]={"IN #1", "IN #2", "IN #3", "IN #4", "IN #5", "VIN #1", "VIN #2", "VIN #3"}; + +static long G_nbChannelPerStrip_v1[3]={2, 2, 8}; +static long G_nbChannelPerStrip_v2[5]={2, 2, 2, 8, 8}; +static long G_nbChannelPerStrip_v3[8]={2, 2, 2, 2, 2, 8, 8, 8}; + + +/*******************************************************************************/ +/* COLOR / PEN TOOLS */ +/*******************************************************************************/ + +typedef struct tagCOLORPENBRUSH +{ + COLORREF color; + HPEN pen; + HBRUSH brush; +} T_COLORPENBRUSH, *PT_COLORPENBRUSH, *LPT_COLORPENBRUSH; + +long CreateColorPenBrush(LPT_COLORPENBRUSH lpgdi, COLORREF color) +{ + LOGBRUSH lb; + if (lpgdi == NULL) return -1; + lpgdi->color=color; + lpgdi->pen=CreatePen(PS_SOLID,0,color); + + lb.lbStyle=BS_SOLID; + lb.lbColor=color; + lpgdi->brush=CreateBrushIndirect(&lb); + return 0; +} + +long DestroyColorPenBrush(LPT_COLORPENBRUSH lpgdi) +{ + if (lpgdi == NULL) return -1; + + lpgdi->color=0; + if (lpgdi->pen != NULL) DeleteObject(lpgdi->pen); + lpgdi->pen=NULL; + if (lpgdi->brush != NULL) DeleteObject(lpgdi->brush); + lpgdi->brush=NULL; + return 0; +} + + + + +/*******************************************************************************/ +/* OUR PARAM STRUCTURE */ +/* to store all voicemeeter parameter we manage */ +/*******************************************************************************/ + + + +typedef struct tagMYVM_PARAMETERS +{ + long nbStrip; + T_VMSCTL_STRIP_DATA strip[VMSCTL_MAX_NBSTRIP]; + + long nbBus; + T_VMSCTL_BUS_DATA bus[VMSCTL_MAX_NBBUS]; + + long monitorbus; +} T_MYVM_PARAMETERS, *PT_MYVM_PARAMETERS, *LPT_MYVM_PARAMETERS; + + +/*******************************************************************************/ +/* APPLICATION CONTEXT */ +/*******************************************************************************/ + +#define SLIDERLINKMODE_INDEPENDENT 0 +#define SLIDERLINKMODE_ABSLINK 1 +#define SLIDERLINKMODE_RELLINK 2 + +#define SLIDERLINKMODE_DX 80 + +typedef struct tagVBAN_CMD_PARAM +{ + char VBAN_szStreamName[32]; + char VBAN_szIPAddress[32]; + long VBAN_UDPport; +} T_VBAN_CMD_PARAM, *PT_VBAN_CMD_PARAM, *LPT_VBAN_CMD_PARAM; + + +typedef struct tagAPP_CONTEXT +{ + HWND hwnd_MainWindow; + HINSTANCE hinstance; + size_t wTimer; + long hw_dx; + long hw_dy; + long vbvmr_NotInstalled; + long vbvmr_error; + long vbvmr_connect; + long vbvmr_nbbus; + long vbvmr_nbstrip; + long vbvmr_multilayer; + long vbvmr_version; + long vbvmr_monitor_support; + + char ** vbvmr_pBUSName; + char ** vbvmr_pStripName; + long * vbvmr_pStripChannel; + DWORD msLastTimeForVBANRegister; // time reference in ms + DWORD msLastTimeForVBANCheck; // time reference in ms + DWORD LastCounter; // time reference in ms + BOOL VBANisConnected; // time reference in ms + T_MYVM_PARAMETERS current_param; + long StripSliderMode; + + long VMConnectionType; // 0 = direct, 1 = VBAN1, 2 = VBAN2, 3 = VBAN3, 4 = VBAN4. + T_VBAN_CMD_PARAM VMLinkVBAN[4]; + + HWND hw_StripList[VMSCTL_MAX_NBSTRIP]; + HWND hw_BusList[VMSCTL_MAX_NBBUS]; + + HFONT font0; + HFONT font1; + HFONT font2; + + T_COLORPENBRUSH gdiobjects_black; + T_COLORPENBRUSH gdiobjects_bkg; + T_COLORPENBRUSH gdiobjects_bkg_ctl; + T_COLORPENBRUSH gdiobjects_blue0; + T_COLORPENBRUSH gdiobjects_blue1; + T_COLORPENBRUSH gdiobjects_blue2; + T_COLORPENBRUSH gdiobjects_blue3; + T_COLORPENBRUSH gdiobjects_buttongreen; + T_COLORPENBRUSH gdiobjects_slidergreen; + T_COLORPENBRUSH gdiobjects_sliderred; + T_COLORPENBRUSH gdiobjects_white; + T_COLORPENBRUSH gdiobjects_meter_blue; + T_COLORPENBRUSH gdiobjects_meter_green; + T_COLORPENBRUSH gdiobjects_meter_red; + T_COLORPENBRUSH gdiobjects_mute; + T_COLORPENBRUSH gdiobjects_monitor; + T_COLORPENBRUSH gdiobjects_grey; + + RECT CurrentWindowRect; + RECT rect_inputs; + RECT rect_outputs; + RECT rect_SliderMode; + RECT rect_VMConnectionType; +} T_APP_CONTEXT, *PT_APP_CONTEXT, *LPT_APP_CONTEXT; + +static T_APP_CONTEXT G_MainAppCtx = {NULL, NULL}; + + + +long InitResources(LPT_APP_CONTEXT lpapp) +{ + LOGFONT lf; + + if (lpapp == NULL) return -1; + + //make Font + memset(&lf,0, sizeof(LOGFONT)); + lf.lfHeight = 14; + lf.lfWeight = 400; + strcpy(lf.lfFaceName,"Arial"); + lpapp->font0 =CreateFontIndirect(&lf); + + lf.lfHeight = 16; + lf.lfWeight = 400; + strcpy(lf.lfFaceName,"Arial"); + lpapp->font1 =CreateFontIndirect(&lf); + + lf.lfHeight = 18; + lf.lfWeight = 600; + strcpy(lf.lfFaceName,"Arial"); + lpapp->font2 =CreateFontIndirect(&lf); + + //make pen brush + CreateColorPenBrush(&(lpapp->gdiobjects_black), RGB(0,0,0)); + CreateColorPenBrush(&(lpapp->gdiobjects_bkg), RGB(18,32,41)); + CreateColorPenBrush(&(lpapp->gdiobjects_bkg_ctl), RGB(44,61,77)); + CreateColorPenBrush(&(lpapp->gdiobjects_blue0), RGB(71,90,103)); + CreateColorPenBrush(&(lpapp->gdiobjects_blue1), RGB(95,120,137)); + CreateColorPenBrush(&(lpapp->gdiobjects_blue2), RGB(111,140,160)); + CreateColorPenBrush(&(lpapp->gdiobjects_blue3), RGB(104,230,248)); + CreateColorPenBrush(&(lpapp->gdiobjects_buttongreen), RGB(36,167,49)); + CreateColorPenBrush(&(lpapp->gdiobjects_slidergreen), RGB(112,195,153)); + CreateColorPenBrush(&(lpapp->gdiobjects_sliderred), RGB(248,99,77)); + CreateColorPenBrush(&(lpapp->gdiobjects_white), RGB(255,255,255)); + + CreateColorPenBrush(&(lpapp->gdiobjects_meter_blue), RGB(130,170,180)); + CreateColorPenBrush(&(lpapp->gdiobjects_meter_green), RGB(30,255,90)); + CreateColorPenBrush(&(lpapp->gdiobjects_meter_red), RGB(250,0,0)); + + CreateColorPenBrush(&(lpapp->gdiobjects_mute), RGB(246,91,81)); + CreateColorPenBrush(&(lpapp->gdiobjects_monitor), RGB(203,174,130)); + CreateColorPenBrush(&(lpapp->gdiobjects_grey), RGB(100,100,100)); + + return 0; +} + +long EndResources(LPT_APP_CONTEXT lpapp) +{ + if (lpapp == NULL) return -1; + //Delete font Object + if (lpapp->font0 != NULL) DeleteObject(lpapp->font0); + lpapp->font0=NULL; + if (lpapp->font1 != NULL) DeleteObject(lpapp->font1); + lpapp->font1=NULL; + if (lpapp->font2 != NULL) DeleteObject(lpapp->font2); + lpapp->font2=NULL; + //Delete Pen Brush + DestroyColorPenBrush(&(lpapp->gdiobjects_black)); + DestroyColorPenBrush(&(lpapp->gdiobjects_bkg)); + DestroyColorPenBrush(&(lpapp->gdiobjects_bkg_ctl)); + DestroyColorPenBrush(&(lpapp->gdiobjects_blue0)); + DestroyColorPenBrush(&(lpapp->gdiobjects_blue1)); + DestroyColorPenBrush(&(lpapp->gdiobjects_blue2)); + DestroyColorPenBrush(&(lpapp->gdiobjects_blue3)); + DestroyColorPenBrush(&(lpapp->gdiobjects_buttongreen)); + DestroyColorPenBrush(&(lpapp->gdiobjects_slidergreen)); + DestroyColorPenBrush(&(lpapp->gdiobjects_sliderred)); + DestroyColorPenBrush(&(lpapp->gdiobjects_white)); + + DestroyColorPenBrush(&(lpapp->gdiobjects_meter_blue)); + DestroyColorPenBrush(&(lpapp->gdiobjects_meter_green)); + DestroyColorPenBrush(&(lpapp->gdiobjects_meter_red)); + DestroyColorPenBrush(&(lpapp->gdiobjects_mute)); + DestroyColorPenBrush(&(lpapp->gdiobjects_monitor)); + return 0; +} + + +/*******************************************************************************/ +/* LOCAL TOOL Functions */ +/*******************************************************************************/ + + +#ifndef SM_CXPADDEDBORDER + #define SM_CXPADDEDBORDER 92 +#endif + +typedef struct tagVBNONCLIENTMETRICS +{ + UINT cbSize; + int iBorderWidth; + int iScrollWidth; + int iScrollHeight; + int iCaptionWidth; + int iCaptionHeight; + LOGFONT lfCaptionFont; + int iSmCaptionWidth; + int iSmCaptionHeight; + LOGFONT lfSmCaptionFont; + int iMenuWidth; + int iMenuHeight; + LOGFONT lfMenuFont; + LOGFONT lfStatusFont; + LOGFONT lfMessageFont; + int iPaddedBorderWidth; +} VBNONCLIENTMETRICS, *LPVBNONCLIENTMETRICS; + + +void TOOL_GetWindowsMetricInfo(long * win_border, long * win_title,long * win_menu) +{ + long xyadjust=0; + BOOL ok; + VBNONCLIENTMETRICS ncm; + ncm.cbSize=sizeof(VBNONCLIENTMETRICS); + ok=SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(VBNONCLIENTMETRICS),&ncm,0L); + if (ok != 0) + { + xyadjust=ncm.iPaddedBorderWidth;//+2; + if (win_menu != NULL) *win_menu =ncm.iMenuHeight; + if (win_border != NULL) *win_border =xyadjust+ncm.iBorderWidth; + if (win_title != NULL) *win_title =ncm.iCaptionHeight; + return; + } + xyadjust=GetSystemMetrics(SM_CXPADDEDBORDER); + if (win_menu != NULL) *win_menu=GetSystemMetrics(SM_CYMENU); + if (win_border != NULL) *win_border= xyadjust+GetSystemMetrics(SM_CXDLGFRAME); //SM_CXDLGFRAME SM_CXBORDER ncm.iBorderWidth; + if (win_title != NULL) *win_title=GetSystemMetrics(SM_CYCAPTION);//ncm.iCaptionHeight; +} + + + +void TOOL_RemoveUnauthorizedChar(char * szString) +{ + char cc,* lpTarget,*lpSource; + if (szString == NULL) return; + lpSource=szString; + lpTarget=szString; + + while(*lpSource != 0) + { + cc=*lpSource; + if (((cc > 47) && (cc<58)) || + ((cc>64) && (cc<91)) || + ((cc>96) && (cc<123)) || + (cc == '-') || (cc == '_') || (cc == '{') || (cc == '}') || (cc =='(') || + (cc == ')') || (cc == '[') || (cc == ']') || (cc == 32)) + { + *lpTarget=cc; + lpTarget++; + } + lpSource++; + } + *lpTarget=0; +} + + +long TOOL_IsItIPAddress(char* sss) +{ + long nbn=0,num=0,fdigi=0; + char cc; + cc=*sss++; + while (cc > 31) + { + if ((cc > 47) && (cc < 58)) + { + num = (num*10) + (cc - 48); + fdigi=1; + } + else + { + if (cc == '.') + { + fdigi=0; + if ((num >= 0) && (num < 256)) nbn++; + else return -2; + num=0; + } + else + { + if (cc != 32) return -1; + } + } + cc=*sss++; + } + if (fdigi != 0) + { + if ((num >= 0) && (num < 256)) nbn++; + } + if (nbn == 4) return 1; + return 0; +} + +DWORD TOOL_GetDeltaMsDword(DWORD previous_ms, DWORD current_ms) +{ + DWORD delta; + if (current_ms >= previous_ms) delta = current_ms-previous_ms; + else + { + delta = (0xFFFFFFFF-previous_ms); + delta +=current_ms; + } + return delta; +} + + + +/*******************************************************************************/ +/* REGISTRY TOOL */ +/*******************************************************************************/ + + +BOOL __cdecl TOOL_RegistrySaveUserString(char * key,char * ValueName,char * strvalue) +{ + DWORD nbc; + HKEY hkResult; + LONG rep; + rep=RegOpenKey(HKEY_CURRENT_USER,key,&hkResult); + if (rep != ERROR_SUCCESS) + { + rep=RegCreateKey(HKEY_CURRENT_USER,key,&hkResult); + if (rep != ERROR_SUCCESS) return FALSE; + } + nbc= (DWORD)((strlen(strvalue)+1)*sizeof(char)); + rep=RegSetValueEx(hkResult, ValueName,0,REG_SZ,(unsigned char *)strvalue,nbc); + RegCloseKey(hkResult); + if (rep != ERROR_SUCCESS) return FALSE; + return TRUE; +} + +BOOL __cdecl TOOL_RegistryLoadUserString(char * key,char * ValueName,char * strvalue) +{ + char sss[4100]; + DWORD nnsize=4096; + HKEY hkResult; + LONG rep; + DWORD pptype=REG_SZ; + sss[0]=0; + rep=RegOpenKey(HKEY_CURRENT_USER,key,&hkResult); + if (rep != ERROR_SUCCESS) return FALSE; + strvalue[0]=0; + rep=RegQueryValueEx(hkResult,ValueName,0,&pptype,(unsigned char *)sss,&nnsize); + RegCloseKey(hkResult); + if (pptype != REG_SZ) return FALSE; + if (rep != ERROR_SUCCESS) return FALSE; + if (nnsize>4050) nnsize=4050; + strncpy(strvalue,sss,nnsize); + return TRUE; +} + +BOOL __cdecl TOOL_RegistrySaveUserDWORD(char * key,char * ValueName,DWORD value) +{ + HKEY hkResult; + LONG rep; + rep=RegOpenKey(HKEY_CURRENT_USER,key,&hkResult); + if (rep != ERROR_SUCCESS) + { + rep=RegCreateKey(HKEY_CURRENT_USER,key,&hkResult); + if (rep != ERROR_SUCCESS) return FALSE; + } + rep=RegSetValueEx(hkResult, ValueName,0,REG_DWORD,(unsigned char *)&value,sizeof(DWORD)); + RegCloseKey(hkResult); + if (rep != ERROR_SUCCESS) return FALSE; + return TRUE; +} + +BOOL __cdecl TOOL_RegistryLoadUserDWORD(char * key,char * ValueName,DWORD * pvalue) +{ + HKEY hkResult; + LONG rep; + DWORD bSize = sizeof(DWORD); + DWORD bType = REG_DWORD; + + rep=RegOpenKey(HKEY_CURRENT_USER,key,&hkResult); + if (rep != ERROR_SUCCESS) return FALSE; + rep=RegQueryValueEx(hkResult, ValueName,0,&bType,(unsigned char *)pvalue,&bSize); + RegCloseKey(hkResult); + if (bType != REG_DWORD) return FALSE; + if (rep != ERROR_SUCCESS) return FALSE; + return TRUE; +} + + + + + + + + +/*******************************************************************************/ +/* VBAN CONFIGURATION DIALOG BOX */ +/*******************************************************************************/ + + +//VBAN Edit Dialog Box Context +typedef struct tagVBANOPTION_DIALOGBOX +{ + HGLOBAL hdlgtemplate; + HWND hparent; + HWND hw; + int fInit; + long x0,y0,dx,dy; + long style; + + HWND hw_StreamName[4]; + HWND hw_StreamIPTo[4]; + HWND hw_StreamPort[4]; + HWND hw_StreamCheck[4]; + +} T_VBANOPTION_DIALOGBOX, *PT_VBANOPTION_DIALOGBOX, *LPT_VBANOPTION_DIALOGBOX; + +static T_VBANOPTION_DIALOGBOX G_VBANDialog_CTX={NULL,NULL, NULL}; + + +#define IDC_VBANEDIT_NAME 100 +#define IDC_VBANEDIT_IP 200 +#define IDC_VBANEDIT_CHECK 300 +#define IDC_VBANEDIT_PORT 400 + +void VBANDIALOG_CreateControls(LPT_VBANOPTION_DIALOGBOX lpctx, HWND hwParent) +{ + LPT_VBAN_CMD_PARAM lpVBAN_param; + char sz[128]; + long x0,y0,vi; + HWND hh; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + + y0=10+30+20; + for (vi=0;vi<4;vi++) + { + lpVBAN_param = &(lpapp->VMLinkVBAN[vi]); + x0=10; + + //name + x0=x0+80; + hh=CreateWindowEx(WS_EX_CLIENTEDGE,"edit",lpVBAN_param->VBAN_szStreamName, + WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | ES_AUTOHSCROLL, + x0,y0,200,26, + hwParent,(HMENU)(IDC_VBANEDIT_NAME+vi),lpapp->hinstance,NULL); + SendMessage(hh,WM_SETFONT,(WPARAM)lpapp->font1,MAKELPARAM(1,0)); + SendMessage(hh,EM_SETMARGINS,EC_LEFTMARGIN | EC_RIGHTMARGIN,(LPARAM) MAKELONG(2,2)); + lpctx->hw_StreamName[vi]=hh; + + //IP to + x0=x0+210; + hh=CreateWindowEx(WS_EX_CLIENTEDGE,"edit",lpVBAN_param->VBAN_szIPAddress, + WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | ES_AUTOHSCROLL, + x0,y0,180,26, + hwParent,(HMENU)(IDC_VBANEDIT_IP+vi),lpapp->hinstance,NULL); + SendMessage(hh,WM_SETFONT,(WPARAM)lpapp->font1,MAKELPARAM(1,0)); + SendMessage(hh,EM_SETMARGINS,EC_LEFTMARGIN | EC_RIGHTMARGIN,(LPARAM) MAKELONG(2,2)); + lpctx->hw_StreamIPTo[vi]=hh; + + // + x0=x0+190; + sprintf(sz,"%i", lpVBAN_param->VBAN_UDPport); + hh=CreateWindowEx(WS_EX_CLIENTEDGE,"edit",sz, + WS_CHILD | WS_VISIBLE | ES_LEFT | WS_TABSTOP | ES_AUTOHSCROLL, + x0,y0,80,26, + hwParent,(HMENU)(IDC_VBANEDIT_PORT+vi),lpapp->hinstance,NULL); + SendMessage(hh,WM_SETFONT,(WPARAM)lpapp->font1,MAKELPARAM(1,0)); + SendMessage(hh,EM_SETMARGINS,EC_LEFTMARGIN | EC_RIGHTMARGIN,(LPARAM) MAKELONG(2,2)); + lpctx->hw_StreamPort[vi]=hh; + + y0=y0+40; + } + + + // + // button Ok/Cancel + // + hh=CreateWindow("button","Ok",WS_CHILD | WS_VISIBLE | WS_TABSTOP, + lpctx->dx-200,lpctx->dy-40,90,30, + hwParent,(HMENU)1,lpapp->hinstance,NULL); + + hh=CreateWindow("button","Cancel",WS_CHILD | WS_VISIBLE | WS_TABSTOP, + lpctx->dx-100,lpctx->dy-40,90,30, + hwParent,(HMENU)2,lpapp->hinstance,NULL); + + // + +} + +static void VBANDIALOG_ValidateOption(LPT_VBANOPTION_DIALOGBOX lpctx) +{ + LPT_VBAN_CMD_PARAM lpVBAN_param; + char sz[128]; + long nnn,vi; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + + for (vi=0;vi<4;vi++) + { + lpVBAN_param = &(lpapp->VMLinkVBAN[vi]); + + GetWindowText(lpctx->hw_StreamName[vi],sz,64); + TOOL_RemoveUnauthorizedChar(sz); + sz[17]=0; + strcpy(lpVBAN_param->VBAN_szStreamName, sz); + + GetWindowText(lpctx->hw_StreamIPTo[vi],sz,64); + if (TOOL_IsItIPAddress(sz) == 1) + { + strcpy(lpVBAN_param->VBAN_szIPAddress, sz); + } + + GetWindowText(lpctx->hw_StreamPort[vi],sz,64); + nnn=atoi(sz); + nnn=nnn & 0x0000FFFF; + if (nnn > 0) + { + lpVBAN_param->VBAN_UDPport= nnn; + } + } +} + + + +void VBANDIALOG_DrawStuff(LPT_VBANOPTION_DIALOGBOX lpctx, HDC dc) +{ + long vi, x0,y0; + char sss[512]; + HBRUSH oldbrush; + HPEN oldpen; + HFONT oldfont; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + + oldbrush=(HBRUSH)SelectObject(dc,lpapp->gdiobjects_grey.brush); + oldpen=(HPEN)SelectObject(dc,GetStockObject(NULL_PEN)); + oldfont=(HFONT)SelectObject(dc,lpapp->font1); + SetBkMode(dc,TRANSPARENT); + + x0=10; + y0=10; + + SetTextColor(dc,RGB(0,0,0)); + strcpy(sss,"VBAN Connections Configuration:"); + TextOut(dc,x0+5,y0+2,sss,(int)strlen(sss)); + + SetTextColor(dc,RGB(255,255,255)); + y0=y0+30; + x0=x0+80; + strcpy(sss,"Stream Name:"); + Rectangle(dc,x0,y0,x0+200,y0+20); + TextOut(dc,x0+5,y0+2,sss,(int)strlen(sss)); + + x0=x0+210; + strcpy(sss,"IP Addres To:"); + Rectangle(dc,x0,y0,x0+200,y0+20); + TextOut(dc,x0+5,y0+2,sss,(int)strlen(sss)); + + x0=x0+190; + strcpy(sss,"UDP port:"); + Rectangle(dc,x0,y0,x0+80,y0+20); + TextOut(dc,x0+5,y0+2,sss,(int)strlen(sss)); + + + y0=10+30+20; + x0=10; + SetTextColor(dc,RGB(0,0,0)); + for (vi=0;vi<4;vi++) + { + sprintf(sss,"VBAN #%i", vi+1); + TextOut(dc,x0+5,y0+2,sss,(int)strlen(sss)); + y0=y0+40; + } + + strcpy(sss,"WARNING: VBAN Incoming Command Stream must be activated on Voicemeeter."); + TextOut(dc,x0+45,y0+2,sss,(int)strlen(sss)); + + SelectObject(dc,oldfont); + SelectObject(dc,oldbrush); + SelectObject(dc,oldpen); + +} + +static BOOL CALLBACK VBANDIALOG_DialogProc(HWND hw, //handle de la fenetre concernés. + UINT te, //Type d'événement. + WPARAM p1, //paramètre 1. + LPARAM p2) //Ligne de paramètre 2 +{ + char sss[128]; + LPT_VBANOPTION_DIALOGBOX lpctx; + HPEN oldpen; + HBRUSH oldbrush; + long win_border, win_title, win_menu; + RECT rect; + HDC dc; + PAINTSTRUCT ps; + long dx,dy,nnn,nNotify; + lpctx=&G_VBANDialog_CTX; + switch (te) + { + case WM_INITDIALOG: + lpctx->hw=hw; + //-------------------------------------------- + strcpy(sss,"VBAN Configuration..."); + SetWindowText(hw,sss); + VBANDIALOG_CreateControls(lpctx, hw); + TOOL_GetWindowsMetricInfo(&win_border, &win_title,&win_menu); + + dx=lpctx->dx+(win_border*2); + dy=lpctx->dy+(win_border*2)+win_title; + SetWindowPos(hw,HWND_TOP,lpctx->x0,lpctx->y0,dx,dy,SWP_SHOWWINDOW); + SetFocus(hw); + lpctx->fInit=1; + break; + + case WM_COMMAND: + if (lpctx->fInit == 0) break; + nnn=LOWORD(p1); + nNotify=HIWORD(p1); + switch(nnn) + { + case 1: + //CancelEditFlag(lpctx, lpctx->dialopt_track,FALSE); + VBANDIALOG_ValidateOption(lpctx); + EndDialog(hw,1); + break; + case 2: + EndDialog(hw,0); + break; + + } + break; + case WM_PAINT: + dc=BeginPaint(hw,&ps); + VBANDIALOG_DrawStuff(lpctx, dc); + EndPaint(hw,&ps); + break; + + case WM_ERASEBKGND: + dc=(HDC)p1; + GetClientRect(hw,&rect); + oldpen=(HPEN)SelectObject(dc,GetStockObject(WHITE_PEN)); + oldbrush=(HBRUSH)SelectObject(dc,GetStockObject(LTGRAY_BRUSH)); + Rectangle(dc, 0,0,rect.right,rect.bottom); + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + return TRUE; + + case WM_KEYDOWN: + switch(p1) + { + case VK_RETURN: + PostMessage(hw,WM_COMMAND,1,0); + return 0; + case VK_ESCAPE: + PostMessage(hw,WM_COMMAND,2,0); + return 0; + } + return -1; + case WM_CLOSE: + PostMessage(hw,WM_COMMAND,2,0); + return TRUE; + case WM_DESTROY: + GlobalFree(lpctx->hdlgtemplate); + lpctx->hw=NULL; + break; + } + return 0; +} + +BOOL VBANDIALOG_DialogGo(HWND h_parent,DWORD style) +{ + LPDLGTEMPLATE lpdlgtemplate; + RECT rect; + LPT_VBANOPTION_DIALOGBOX lpctx; + LPT_APP_CONTEXT lpapp; + lpctx=&G_VBANDialog_CTX; + lpapp=&G_MainAppCtx; + + if (lpctx->hw != NULL) EndDialog(lpctx->hw, 0); + GetWindowRect(h_parent, &rect); + + lpctx->hparent=h_parent; + lpctx->style=style; + lpctx->dx=600; + lpctx->dy=300; + lpctx->fInit = 0; + + lpctx->x0 = ((rect.right-rect.left)-lpctx->dx)>>1; + lpctx->y0 = ((rect.bottom-rect.top)-lpctx->dy)>>1; + if (lpctx->x0 < 0) lpctx->x0=0; + if (lpctx->y0 < 0) lpctx->y0=0; + lpctx->x0 +=rect.left; + lpctx->y0 +=rect.top; + + //------------------------------------------------------------- + lpctx->hdlgtemplate=GlobalAlloc(GMEM_ZEROINIT,1024); + lpdlgtemplate=(LPDLGTEMPLATE)GlobalLock(lpctx->hdlgtemplate); + lpdlgtemplate->style=WS_POPUP | DS_NOIDLEMSG | WS_VISIBLE | WS_CLIPCHILDREN | WS_BORDER | WS_CAPTION | WS_SYSMENU; + lpdlgtemplate->dwExtendedStyle=0L; + lpdlgtemplate->cdit=0L; + GlobalUnlock(lpctx->hdlgtemplate); + + return (BOOL)DialogBoxIndirectParam(lpapp->hinstance,(const DLGTEMPLATE *)lpctx->hdlgtemplate,h_parent,(DLGPROC)VBANDIALOG_DialogProc,(LPARAM)lpctx); +} + + + + + + + + + + + + + + + + + +/*******************************************************************************/ +/* LOAD / SAVE CONFIGURATION */ +/*******************************************************************************/ + +static char * G_OptionDirKey ="VB-Audio\\VMStreamerView"; + +static char * G_OptionKey_VBANx_name="VBAN%i_name"; +static char * G_OptionKey_VBANx_ip="VBAN%i_ip"; +static char * G_OptionKey_VBANx_port="VBAN%i_port"; + +static char * G_OptionKey_ConnectionType="VMConnectType"; +static char * G_OptionKey_StripSliderMode="StripSliderMode"; + +static char * G_OptionKey_WindowRectLeft="WinPos_X0"; +static char * G_OptionKey_WindowRectTop="WinPos_Y0"; +static char * G_OptionKey_WindowRectRight="WinPos_X1"; +static char * G_OptionKey_WindowRectBottom="WinPos_Y1"; + + +void LoadAppConfiguration(LPT_APP_CONTEXT lpapp) +{ + LPT_VBAN_CMD_PARAM lpVBANParam; + BOOL ok; + long nnn; + int vi; + char sz[2048]; + char szKey[128]; + + // set default parameters + for (vi=0;vi<4;vi++) + { + lpVBANParam = &(lpapp->VMLinkVBAN[vi]); + strcpy(lpVBANParam->VBAN_szStreamName, "Command1"); + sprintf(lpVBANParam->VBAN_szIPAddress, "192.168.10.%i", 10+vi); + lpVBANParam->VBAN_UDPport = 6980; + } + + // windows rect + ok=TOOL_RegistryLoadUserDWORD(G_OptionDirKey, G_OptionKey_WindowRectLeft, &nnn); + if (ok == TRUE) lpapp->CurrentWindowRect.left = nnn; + ok=TOOL_RegistryLoadUserDWORD(G_OptionDirKey, G_OptionKey_WindowRectTop, &nnn); + if (ok == TRUE) lpapp->CurrentWindowRect.top = nnn; + ok=TOOL_RegistryLoadUserDWORD(G_OptionDirKey, G_OptionKey_WindowRectRight, &nnn); + if (ok == TRUE) lpapp->CurrentWindowRect.right = nnn; + ok=TOOL_RegistryLoadUserDWORD(G_OptionDirKey, G_OptionKey_WindowRectBottom, &nnn); + if (ok == TRUE) lpapp->CurrentWindowRect.bottom = nnn; + + // SliderMode + ok=TOOL_RegistryLoadUserDWORD(G_OptionDirKey, G_OptionKey_StripSliderMode, &nnn); + if (ok == TRUE) + { + nnn=nnn & 0x0000000F; + if (nnn > 2) nnn=0; + lpapp->StripSliderMode = nnn; + } + + // Link Type + ok=TOOL_RegistryLoadUserDWORD(G_OptionDirKey, G_OptionKey_ConnectionType, &nnn); + if (ok == TRUE) + { + nnn=nnn & 0x0000000F; + if (nnn > 4) nnn=0; + lpapp->VMConnectionType = nnn; + } + + // VBAN Configuration + for (vi=0;vi<4;vi++) + { + lpVBANParam = &(lpapp->VMLinkVBAN[vi]); + + sprintf(szKey,G_OptionKey_VBANx_name, vi+1); + ok=TOOL_RegistryLoadUserString(G_OptionDirKey, szKey, sz); + if (ok == TRUE) + { + strncpy(lpVBANParam->VBAN_szStreamName,sz,32); + lpVBANParam->VBAN_szStreamName[32-1]=0; + } + sprintf(szKey,G_OptionKey_VBANx_ip, vi+1); + ok=TOOL_RegistryLoadUserString(G_OptionDirKey, szKey, sz); + if (ok == TRUE) + { + strncpy(lpVBANParam->VBAN_szIPAddress,sz,32); + lpVBANParam->VBAN_szIPAddress[32-1]=0; + } + sprintf(szKey,G_OptionKey_VBANx_port, vi+1); + ok=TOOL_RegistryLoadUserDWORD(G_OptionDirKey, szKey, &nnn); + if (ok == TRUE) + { + lpVBANParam->VBAN_UDPport = nnn & 0x0000FFFF; + } + } +} + +void SaveAppConfiguration(LPT_APP_CONTEXT lpapp) +{ + LPT_VBAN_CMD_PARAM lpVBANParam; + DWORD nnn; + int vi; + char szKey[128]; + + // store current windows rect + TOOL_RegistrySaveUserDWORD(G_OptionDirKey, G_OptionKey_WindowRectLeft, lpapp->CurrentWindowRect.left); + TOOL_RegistrySaveUserDWORD(G_OptionDirKey, G_OptionKey_WindowRectTop, lpapp->CurrentWindowRect.top); + TOOL_RegistrySaveUserDWORD(G_OptionDirKey, G_OptionKey_WindowRectRight, lpapp->CurrentWindowRect.right); + TOOL_RegistrySaveUserDWORD(G_OptionDirKey, G_OptionKey_WindowRectBottom, lpapp->CurrentWindowRect.bottom); + + // SliderMode + nnn=lpapp->StripSliderMode; + TOOL_RegistrySaveUserDWORD(G_OptionDirKey, G_OptionKey_StripSliderMode, nnn); + + // Link Type + nnn=lpapp->VMConnectionType; + TOOL_RegistrySaveUserDWORD(G_OptionDirKey, G_OptionKey_ConnectionType, nnn); + + // VBAN Configuration + for (vi=0;vi<4;vi++) + { + lpVBANParam = &(lpapp->VMLinkVBAN[vi]); + + sprintf(szKey,G_OptionKey_VBANx_name, vi+1); + TOOL_RegistrySaveUserString(G_OptionDirKey, szKey, lpVBANParam->VBAN_szStreamName); + + sprintf(szKey,G_OptionKey_VBANx_ip, vi+1); + TOOL_RegistrySaveUserString(G_OptionDirKey, szKey, lpVBANParam->VBAN_szIPAddress); + + sprintf(szKey,G_OptionKey_VBANx_port, vi+1); + nnn=lpVBANParam->VBAN_UDPport; + TOOL_RegistrySaveUserDWORD(G_OptionDirKey, szKey, nnn); + } +} + + + + + + +/*******************************************************************************/ +/* VBAN OR DIRECT */ +/*******************************************************************************/ + + +long SendParameterToVoicemeeter(char * szParam, float value) +{ + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + // If Direct Link (we use Voicemeeter Remote API + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + return iVMR.VBVMR_SetParameterFloat(szParam, value); // set value in Local Voicemeeter + } + // Otherwise we send the VBAN request. + else + { + return VBANCMD_SendRequest_Float(szParam, value); // set value by a VBAN request + } +} + + +long GetCurrentVoicemeeterType(unsigned long * pType) +{ + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + // If Direct Link (we use Voicemeeter Remote API + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + return iVMR.VBVMR_GetVoicemeeterType(pType); + } + // Otherwise we ask to our VBANCMD Lib. + else + { + return VBANCMD_GetVoicemeeterType(pType); + } +} + +long GetCurrentVoicemeeterVersion(unsigned long * pVersion) +{ + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + // If Direct Link (we use Voicemeeter Remote API + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + return iVMR.VBVMR_GetVoicemeeterVersion(pVersion); + } + // Otherwise we ask to our VBANCMD Lib. + else + { + return VBANCMD_GetVoicemeeterVersion(pVersion); + } +} + +long GetCurrentBusLabel(long index, WCHAR * pwsz) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Bus[%i].Label", index); + return iVMR.VBVMR_GetParameterStringW(szParam, pwsz); + } + else return VBANCMD_GetBusLabel(index, pwsz); +} + +long GetCurrentBusGain(long index, float * pValue) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Bus[%i].Gain", index); + return iVMR.VBVMR_GetParameterFloat(szParam, pValue); + } + else return VBANCMD_GetBusGain(index, pValue); +} + +long GetCurrentBusSel(long index, float * pValue) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Bus[%i].Sel", index); + return iVMR.VBVMR_GetParameterFloat(szParam, pValue); + } + else return VBANCMD_GetBusSel(index, pValue); +} + +long GetCurrentBusMute(long index, float * pValue) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Bus[%i].Mute", index); + return iVMR.VBVMR_GetParameterFloat(szParam, pValue); + } + else return VBANCMD_GetBusMute(index, pValue); +} + +long GetCurrentBusMonitor(long index, float * pValue) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Bus[%i].Monitor", index); + return iVMR.VBVMR_GetParameterFloat(szParam, pValue); + } + else return VBANCMD_GetBusMonitor(index, pValue); +} + + +long GetCurrentStripLabel(long index, WCHAR * pwsz) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Strip[%i].Label", index); + return iVMR.VBVMR_GetParameterStringW(szParam, pwsz); + } + else return VBANCMD_GetStripLabel(index, pwsz); +} + +long GetCurrentStripGain(long index, float * pValue) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Strip[%i].Gain", index); + return iVMR.VBVMR_GetParameterFloat(szParam, pValue); + } + else return VBANCMD_GetStripGain(index, pValue); +} + +long GetCurrentStripGainLayer(long index, long layer, float * pValue) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Strip[%i].GainLayer[%i]", index, layer); + return iVMR.VBVMR_GetParameterFloat(szParam, pValue); + } + else return VBANCMD_GetStripGainLayer(index, layer, pValue); +} + +long GetCurrentStripMute(long index, float * pValue) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Strip[%i].Mute", index); + return iVMR.VBVMR_GetParameterFloat(szParam, pValue); + } + else return VBANCMD_GetStripMute(index, pValue); +} + +long GetCurrentStripAssignation(long index, long nuBus, float * pValue) +{ + char szParam[128]; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + sprintf(szParam,"Strip[%i].%s", index, lpapp->vbvmr_pBUSName[nuBus]); + return iVMR.VBVMR_GetParameterFloat(szParam, pValue); + } + else return VBANCMD_GetStripAssignation(index, nuBus, pValue); +} + +long GetCurrentLevelDB(long index, long nuChannel, float * pLevelDB) +{ + long rep; + float NormalLevel; + LPT_APP_CONTEXT lpapp; + lpapp=&G_MainAppCtx; + if (lpapp->VMConnectionType == 0) + { + if (lpapp->vbvmr_NotInstalled != 0) return -1; + NormalLevel =0.0f; + rep= iVMR.VBVMR_GetLevel(index, nuChannel, &NormalLevel); + if (NormalLevel > 1.0e-5f) *pLevelDB=(float)(20.0*log10(NormalLevel)); + else *pLevelDB=-100.0f; + return rep; + } + else return VBANCMD_GetLevel(index, nuChannel, pLevelDB); +} + + +/*******************************************************************************/ +/* UPDATE CONTROLS */ +/*******************************************************************************/ + +#define VMRS_BITSTRIP 0x00000001 +#define VMRS_BITBUS 0x00000100 +#define VMRS_BITSTRIPNB 0x00010000 +#define VMRS_BITBUSNB 0x00020000 +//#define VMRS_BITMONBUT 0x00040000 +#define VMRS_BITMON 0x01000000 + +#define VMRS_BITALL 0xFFFFFFFF + + +#define VMRS_BITSTRIP_MASK 0x000000FF +#define VMRS_BITBUS_MASK 0x0000FF00 + + +static long UpdateAllControl(LPT_APP_CONTEXT lpapp, long nChangeBit) +{ + BOOL fStripMustBeUpdated; + LPT_MYVM_PARAMETERS lpp; + long nu,mask,fUpdateAll; + lpp = &(lpapp->current_param); + if (nChangeBit == VMRS_BITALL) fUpdateAll=1; + else fUpdateAll=0; + //if BUS parameters are changed + if ((nChangeBit & VMRS_BITBUS_MASK) != 0) + { + mask=VMRS_BITBUS; + for (nu=0;nuvbvmr_nbbus;nu++) + { + if ((nChangeBit & mask) != 0) + { + VMSCTL_SetDataBUS(lpapp->hw_BusList[nu], &(lpp->bus[nu]), fUpdateAll); + } + mask = mask << 1; + } + } + //if STRIP parameters are changed + fStripMustBeUpdated = ((nChangeBit & VMRS_BITBUSNB) != 0); + if (((nChangeBit & VMRS_BITSTRIP_MASK) != 0) || (fStripMustBeUpdated == TRUE)) + { + mask=VMRS_BITSTRIP; + for (nu=0;nuvbvmr_nbstrip;nu++) + { + if (((nChangeBit & mask) != 0) || (fStripMustBeUpdated == TRUE)) + { + VMSCTL_SetDataSTRIP(lpapp->hw_StripList[nu], &(lpp->strip[nu]), fUpdateAll); + } + mask = mask << 1; + } + } + + return 0; +} + + + + +static long AnalyseParamChange(LPT_APP_CONTEXT lpapp) +{ + char c1,c2, bus_assignbit[VMSCTL_MAX_NBBUS]; //0x10 bus activated / 0x01 bus assigned + WCHAR wszString[256]; + LPT_MYVM_PARAMETERS lpp; + float fff; + long vi, nu, nbstrip, nbbus, rep, updatebit=0; + long mask, monitorbus=-1; + +#ifdef _DEBUG + if (lpapp->vbvmr_pStripName == NULL) + { + return 0; + } + if (lpapp->vbvmr_pBUSName == NULL) + { + return 0; + } +#endif + + lpp = &(lpapp->current_param); + nbstrip=0; + nbbus = 0; + + for (vi=0;vivbvmr_nbbus; vi++) + { + lpp->bus[vi].bus_fMonitorSupport = lpapp->vbvmr_monitor_support; + strcpy(lpp->bus[vi].bus_nickname,lpapp->vbvmr_pBUSName[vi]); + // bus label + rep = GetCurrentBusLabel(vi, wszString); + if (rep >= 0) + { + if (wcscmp(wszString, lpp->bus[vi].bus_namew) != 0) + { + updatebit |= mask; + } + wcscpy(lpp->bus[vi].bus_namew, wszString); + } + // if the BUS is displayed + if ((lpp->bus[vi].bus_namew[0] != 0) || (vi==0)) + { + bus_assignbit[vi] = 0x10; + nbbus++; + } + // bus gain + rep = GetCurrentBusGain(vi, &fff); + if (rep >= 0) + { + if (fff != lpp->bus[vi].bus_gain) + { + updatebit |= mask; + } + lpp->bus[vi].bus_gain = fff; + } + // BUS SEL + if (lpapp->vbvmr_multilayer != 0) + { + rep=GetCurrentBusSel(vi, &fff); + if (rep >= 0) + { + if ((long)fff != lpp->bus[vi].bus_monitor) + { + updatebit |= mask; + } + lpp->bus[vi].bus_monitor = (long)fff; + } + } + else lpp->bus[vi].bus_monitor=0; + // Bus Mute + rep=GetCurrentBusMute(vi, &fff); + if (rep >= 0) + { + if ((long)fff != lpp->bus[vi].bus_mute) + { + updatebit |= mask; + } + lpp->bus[vi].bus_mute = (long)fff; + } + // Bus Monitor + rep=GetCurrentBusMonitor(vi, &fff); + if (rep >= 0) + { + if ((fff != 0.0f) && (monitorbus < 0)) + { + monitorbus = vi; + } + } + + mask=mask<<1; + } + // check monitoring options + if (lpapp->vbvmr_multilayer != 0) + { + // define what is the BUS monitoring + if (monitorbus != lpp->monitorbus) + { + lpp->monitorbus = monitorbus; + updatebit |= VMRS_BITMON; + } + //check bus monitoring option consistency (only one BUS can be monitored) + nu=0; + for (vi=0;vivbvmr_nbbus; vi++) + { + if (lpp->bus[vi].bus_monitor != 0) nu++; + } + //otherwise we consider there is no monitoring + if (nu > 1) + { + mask = VMRS_BITBUS; + for (vi=0;vivbvmr_nbbus; vi++) + { + if (lpp->bus[vi].bus_monitor != 0) updatebit |= mask; + lpp->bus[vi].bus_monitor =0; + } + mask=mask<<1; + } + } + // + //get parameters for strips + // + mask = VMRS_BITSTRIP; + for (vi=0;vivbvmr_nbstrip; vi++) + { + strcpy(lpp->strip[vi].strip_nickname,lpapp->vbvmr_pStripName[vi]); + // Strip label + rep = GetCurrentStripLabel(vi, wszString); + if (rep >= 0) + { + if (wcscmp(wszString, lpp->strip[vi].strip_namew) != 0) + { + updatebit |= mask; + } + wcscpy(lpp->strip[vi].strip_namew, wszString); + } + if (lpp->strip[vi].strip_namew[0] != 0) nbstrip++; + + // Strip Gain + if (lpapp->vbvmr_multilayer == 0) + { + rep = GetCurrentStripGain(vi, &fff); + if (rep >= 0) + { + if (fff != lpp->strip[vi].strip_gain[0]) + { + updatebit |= mask; + } + for (nu=0;nuvbvmr_nbbus;nu++) + lpp->strip[vi].strip_gain[nu] = fff; + } + } + else + { + for (nu=0;nuvbvmr_nbbus;nu++) + { + rep= GetCurrentStripGainLayer(vi, nu, &fff); + if (rep >= 0) + { + if (fff != lpp->strip[vi].strip_gain[nu]) + { + updatebit |= mask; + } + lpp->strip[vi].strip_gain[nu] = fff; + } + } + + } + + // bus assignation on the strip + lpp->strip[vi].strip_pBUSNameList = lpapp->vbvmr_pBUSName; + lpp->strip[vi].strip_nbBusDisplayed = nbbus; + lpp->strip[vi].strip_fMultiLayer = lpapp->vbvmr_multilayer; + + for (nu=0;nuvbvmr_nbbus;nu++) + { + c1=0; + rep= GetCurrentStripAssignation(vi, nu, &fff); + if (rep >= 0) + { + if (fff != 0) c1 = 0x01; + c2= lpp->strip[vi].strip_assignmentbit[nu] & 0x0F; + if (c1 != c2) + { + updatebit |= mask; + } + } + lpp->strip[vi].strip_assignmentbit[nu] = bus_assignbit[nu] | c1; + } + // Strip Mute + rep =GetCurrentStripMute(vi,&fff); + if (rep >= 0) + { + if (fff != lpp->strip[vi].strip_mute) + { + updatebit |= mask; + } + lpp->strip[vi].strip_mute = (long)fff; + } + mask=mask<<1; + } + + + //check if the number of strip to display is changed... + if (nbstrip != lpp->nbStrip) + { + updatebit |= VMRS_BITSTRIPNB; + lpp->nbStrip=nbstrip; + } + //check if the number of bus to display is changed... + if (nbbus != lpp->nbBus) + { + updatebit |= VMRS_BITBUSNB; + lpp->nbBus=nbbus; + } + return updatebit; +} + + +void UpdateAllPeakMeters(LPT_APP_CONTEXT lpapp) +{ + float dBlevel; + long nu, *pChannel, nuChannel, nc, nbMax; + T_VMSCTL_RT_DATA rtData; + + //update peak meters for all STRIP + pChannel= lpapp->vbvmr_pStripChannel; + nuChannel=0; + for (nu=0;nuvbvmr_nbstrip;nu++) + { + nbMax=pChannel[nu]; // get number of channel per Strip + for (nc = 0; nc hw_StripList[nu], &rtData); + } + + //update peak meters for all BUS + nuChannel =0; + for (nu=0;nuvbvmr_nbbus;nu++) + { + for (nc = 0; nc <8; nc++) // always 8 channel per Bus + { + GetCurrentLevelDB(3, nuChannel, &dBlevel); + rtData.dbLevel[nc] = dBlevel; + nuChannel++; + } + VMSCTL_SetRTDataBUS(lpapp->hw_BusList[nu], &rtData); + } +} + + + + +/*******************************************************************************/ +/* DISPLAY FUNCTIONS */ +/*******************************************************************************/ + +void DrawVMConnectionMode(LPT_APP_CONTEXT lpapp, HDC dc) +{ + HBRUSH oldbrush; + HPEN oldpen; + HFONT oldfont; + char sz1[128]; + char sss[256]; + RECT rect; + rect=lpapp->rect_VMConnectionType; + + oldpen = (HPEN)SelectObject(dc,lpapp->gdiobjects_blue0.pen); + oldbrush = (HBRUSH)SelectObject(dc,lpapp->gdiobjects_bkg.brush); + oldfont = (HFONT)SelectObject(dc,lpapp->font1); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,RGB(255,255,255)); + if (lpapp->vbvmr_connect == 0) SetTextColor(dc,RGB(200,0,0)); + + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom,7,7); + + if (lpapp->VMConnectionType == 0) strcpy(sz1,"Direct"); + else + { + if (lpapp->VBANisConnected == FALSE) SetTextColor(dc,RGB(200,0,0)); + sprintf(sz1,"VBAN #%i", lpapp->VMConnectionType); + } + strcpy(sss,"Connection: "); + strcat(sss,sz1); + + DrawText(dc,sss,(int)strlen(sss),&rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); +} + +void DrawSliderMode(LPT_APP_CONTEXT lpapp, HDC dc) +{ + HBRUSH oldbrush; + HPEN oldpen; + HFONT oldfont; + char sss[128]; + RECT rect; + rect=lpapp->rect_SliderMode; + + oldpen = (HPEN)SelectObject(dc,lpapp->gdiobjects_blue0.pen); + oldbrush = (HBRUSH)SelectObject(dc,lpapp->gdiobjects_blue0.brush); + oldfont = (HFONT)SelectObject(dc,lpapp->font1); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,RGB(255,255,255)); + + RoundRect(dc,rect.left,rect.top,rect.right,rect.bottom,7,7); + + strcpy(sss,"NO Link"); + if (lpapp->StripSliderMode == SLIDERLINKMODE_ABSLINK) strcpy(sss,"ABS Link"); + if (lpapp->StripSliderMode == SLIDERLINKMODE_RELLINK) strcpy(sss,"REL Link"); + + DrawText(dc,sss,(int)strlen(sss),&rect,DT_SINGLELINE | DT_CENTER | DT_VCENTER); + + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); +} + +void DrawAllStuff(LPT_APP_CONTEXT lpapp, HWND hw, HDC dc) +{ + char sss[512]; + long nBus, xCenter; + long x0,y0,dx,dy; + RECT rect; + HBRUSH oldbrush; + HPEN oldpen; + HFONT oldfont; + + oldpen = (HPEN)SelectObject(dc,lpapp->gdiobjects_blue0.pen); + oldbrush = (HBRUSH)SelectObject(dc,lpapp->gdiobjects_blue2.brush); + oldfont = (HFONT)SelectObject(dc,lpapp->font2); + SetBkMode(dc,TRANSPARENT); + SetTextColor(dc,RGB(0,0,0)); + + rect=lpapp->rect_inputs; + dx=(rect.right-rect.left) / 8; + dy = rect.bottom-rect.top; + x0=rect.left; + y0=rect.top; + + nBus=lpapp->vbvmr_nbbus; + if (nBus < 2) + { + if (lpapp->VMConnectionType == 0) strcpy(sss,"Voicemeeter is not running..."); + else sprintf(sss,"Voicemeeter is not responding on VBAN #%i", lpapp->VMConnectionType); + Rectangle(dc,rect.left,rect.top,rect.right,rect.bottom); + DrawText(dc,sss,(long)strlen(sss),&rect,DT_SINGLELINE | DT_VCENTER | DT_CENTER); + } + else + { + SelectObject(dc,lpapp->gdiobjects_bkg.pen); + SelectObject(dc,lpapp->gdiobjects_bkg.brush); + SetTextColor(dc,lpapp->gdiobjects_blue2.color); + SelectObject(dc,lpapp->font1); + // Display STRIP LABEL + if (lpapp->current_param.nbStrip < 1) + { + strcpy(sss,"Label Voicemeeter Strip you want to see in the INPUTS section..."); + Rectangle(dc,rect.left,rect.top,rect.right,rect.bottom); + DrawText(dc,sss,(long)strlen(sss),&rect,DT_SINGLELINE | DT_VCENTER | DT_LEFT); + + } + else + { + strcpy(sss,"INPUT STRIP"); + Rectangle(dc,rect.left,rect.top,rect.right,rect.bottom); + DrawText(dc,sss,(long)strlen(sss),&rect,DT_SINGLELINE | DT_VCENTER | DT_LEFT); + lpapp->rect_VMConnectionType = rect; + + // display Strip slider mode + lpapp->rect_SliderMode.left=rect.right; + lpapp->rect_SliderMode.top=0; + if (lpapp->vbvmr_multilayer != 0) + { + + strcpy(sss,"Slider Mode:"); + rect.left = rect.right - 72- SLIDERLINKMODE_DX-10; + if (rect.left < 120) rect.left=120; + rect.right = rect.left+300; + + DrawText(dc,sss,(long)strlen(sss),&rect,DT_SINGLELINE | DT_VCENTER | DT_LEFT); + + rect.top+=2; + rect.bottom-=2; + lpapp->rect_SliderMode = rect; + lpapp->rect_SliderMode.left +=80; + lpapp->rect_SliderMode.right= lpapp->rect_SliderMode.left+SLIDERLINKMODE_DX; + + DrawSliderMode(lpapp, dc); + + } + // display connection type + lpapp->rect_VMConnectionType.left =100; + lpapp->rect_VMConnectionType.right=lpapp->rect_SliderMode.left-100; + if ((lpapp->rect_VMConnectionType.right - lpapp->rect_VMConnectionType.left) > 140) + { + xCenter = ((lpapp->rect_VMConnectionType.left +lpapp->rect_VMConnectionType.right)>>1); + lpapp->rect_VMConnectionType.left =xCenter-70; + lpapp->rect_VMConnectionType.right=xCenter+70; + DrawVMConnectionMode(lpapp, dc); + } + + } + // Display BUS LABEL + SetTextColor(dc,lpapp->gdiobjects_blue2.color); + rect=lpapp->rect_outputs; + strcpy(sss,"OUTPUT BUS"); + Rectangle(dc,rect.left,rect.top,rect.right,rect.bottom); + DrawText(dc,sss,(long)strlen(sss),&rect,DT_SINGLELINE | DT_VCENTER | DT_LEFT); + if (lpapp->vbvmr_multilayer != 0) + { + rect.left = rect.right - 110; + if (rect.left < 120) rect.left=120; + strcpy(sss,"Monitor BUS: "); + if (lpapp->vbvmr_monitor_support == 0) strcat(sss, "OFF"); + else + { + if (lpapp->current_param.monitorbus >=0) + { + nBus = (lpapp->current_param.monitorbus) & 0x00000007; + if (lpapp->vbvmr_pBUSName != NULL) strcat(sss,lpapp->vbvmr_pBUSName[nBus]); + else strcat(sss, "OFF"); + } + else strcat(sss, "OFF"); + } + DrawText(dc,sss,(long)strlen(sss),&rect,DT_SINGLELINE | DT_VCENTER | DT_LEFT); + } + } + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + SelectObject(dc,oldfont); +} + + + + +/*******************************************************************************/ +/* USER ACTIONS MANAGEMENT */ +/*******************************************************************************/ + +#define APP_ZONE_SLIDERMODE 100 + + +long APP_WhereAmI(LPT_APP_CONTEXT lpapp, long x0, long y0, long * pnuOut, long * pnuIn) +{ + RECT rect; + + if (lpapp->vbvmr_multilayer != 0) + { + rect= lpapp->rect_SliderMode; + if ((x0>=rect.left) && (x0<=rect.right) && (y0>=rect.top) && (y0<=rect.bottom)) return APP_ZONE_SLIDERMODE; + } + return -1; +} + +void APP_ManageLRButtonDown(LPT_APP_CONTEXT lpapp, HWND hw, long x0, long y0, long fRightClick) +{ + HDC dc; + long rep, nuIn,nuOut; + rep= APP_WhereAmI(lpapp, x0, y0, &nuOut, &nuIn); + if (rep <0) return; + + switch(rep) + { + case APP_ZONE_SLIDERMODE: + lpapp->StripSliderMode++; + if (lpapp->StripSliderMode > 2) lpapp->StripSliderMode=0; + dc=GetDC(hw); + DrawSliderMode(lpapp, dc); + ReleaseDC(hw,dc); + break; + } +} + + +/*******************************************************************************/ +/* Init / End Software */ +/*******************************************************************************/ + + +long DetectVoicemeeterType(LPT_APP_CONTEXT lpapp, HWND hw) +{ + char szParam[128]; + long rep,vmType; + + lpapp->vbvmr_nbbus =0; + lpapp->vbvmr_nbstrip =0; + lpapp->vbvmr_multilayer =0; + lpapp->vbvmr_monitor_support=0; + + rep = GetCurrentVoicemeeterType(&vmType); + if (rep == 0) + { + rep=GetCurrentVoicemeeterVersion(&(lpapp->vbvmr_version)); + if (lpapp->vbvmr_connect != vmType) + { + lpapp->vbvmr_connect =vmType; + switch(vmType) + { + case 1://Voicemeeter + lpapp->vbvmr_nbbus =2; + lpapp->vbvmr_nbstrip =3; + lpapp->vbvmr_pBUSName =G_szBUSNameList_v1; + lpapp->vbvmr_pStripName =G_szStripNameList_v1; + lpapp->vbvmr_pStripChannel =G_nbChannelPerStrip_v1; + break; + case 2://Voicemeeter Banana + lpapp->vbvmr_nbbus =5; + lpapp->vbvmr_nbstrip =5; + lpapp->vbvmr_pBUSName =G_szBUSNameList_v2; + lpapp->vbvmr_pStripName =G_szStripNameList_v2; + lpapp->vbvmr_pStripChannel =G_nbChannelPerStrip_v2; + break; + case 3://Voicemeeter 8 + lpapp->vbvmr_nbbus =8; + lpapp->vbvmr_nbstrip =8; + lpapp->vbvmr_multilayer =1; + lpapp->vbvmr_pBUSName =G_szBUSNameList_v3; + lpapp->vbvmr_pStripName =G_szStripNameList_v3; + lpapp->vbvmr_pStripChannel =G_nbChannelPerStrip_v3; + if (lpapp->vbvmr_version >= 0x02000109) + { + // we activate the Monitor on SEL option + strcpy(szParam,"Option.MonitorOnSEL"); + rep=SendParameterToVoicemeeter(szParam, 1.0f); + if (rep >= 0) + { + lpapp->vbvmr_monitor_support=1; + } + } + break; + } + return 1; + } + } + return 0; +} + + + +BOOL InitSoftware(LPT_APP_CONTEXT lpapp, HWND hw) +{ + char szTitle[]="Init Error"; + long rep; + + //init context + lpapp->hwnd_MainWindow = hw; + lpapp->vbvmr_connect=0; + + //init resources + InitResources(lpapp); + + //get DLL interface for Voicemeeter remote API (local use) + //the VM Streamer View can work with Voicemeeter Dll + lpapp->vbvmr_NotInstalled=0; + rep=InitializeDLLInterfaces(); + if (rep >= 0) + { + //Log in + rep=iVMR.VBVMR_Login(); + if (rep < 0) + { + MessageBox(hw,"Failed To Login",szTitle,MB_APPLMODAL | MB_OK | MB_ICONERROR); + lpapp->vbvmr_NotInstalled=1; + } + //call this to get first parameters state (if server already launched) + lpapp->vbvmr_error = iVMR.VBVMR_IsParametersDirty(); + if (lpapp->vbvmr_error == 0) + { + DetectVoicemeeterType(lpapp, hw); + AnalyseParamChange(lpapp); + } + } + else lpapp->vbvmr_NotInstalled=1; + // we return always true, because the application can work with VBAN connection only + return TRUE; +} + +BOOL EndSoftware(LPT_APP_CONTEXT lpapp, HWND hw) +{ + VBANCMD_StopThread(); + if (iVMR.VBVMR_Logout != NULL) iVMR.VBVMR_Logout(); + EndResources(lpapp); + return TRUE; +} + + + +void ManageMenu(LPT_APP_CONTEXT lpapp, HWND hw, WPARAM wparam, LPARAM lparam) +{ + switch(LOWORD(wparam)) + { + case IDM_UPDATE_ALL_CONTROL: + UpdateAllControl(lpapp, VMRS_BITALL); + break; + + } +} + + +/*******************************************************************************/ +/* CONTROLS CALLBACK */ +/*******************************************************************************/ + +void __stdcall APP_InputCtlCallback(void * lpuser,long Ident, long ICtl, float value) +{ + float delta, vvv; + char szParam[128], c1, bus_assignbit; + long rep,nuStrip, nuVisibleSlider, index, nu; + LPT_MYVM_PARAMETERS lpp; + LPT_APP_CONTEXT lpapp; + lpapp=(LPT_APP_CONTEXT)lpuser; + lpp = &(lpapp->current_param); + nuStrip = Ident; + + // from gain slider (8 possible slider + if ((ICtl >= VMSCTL_STRIP_ID_GAIN) && (ICtl < (VMSCTL_STRIP_ID_GAIN + VMSCTL_MAX_NBBUS))) + { + nuVisibleSlider = ICtl - VMSCTL_STRIP_ID_GAIN; + index = VMSCTL_GetIndexSTRIP(lpapp->hw_StripList[nuStrip],nuVisibleSlider); + + // no multi layer + if (lpapp->vbvmr_multilayer == 0) + { + sprintf(szParam,"Strip[%i].Gain", nuStrip); + rep=SendParameterToVoicemeeter(szParam, value); + if (rep >= 0) + { + for (nu=0;nuvbvmr_nbbus;nu++) + lpp->strip[nuStrip].strip_gain[nu] = value; + } + VMSCTL_SetDataSTRIP(lpapp->hw_StripList[nuStrip], &(lpp->strip[nuStrip]), FALSE); + } + // multi layer + else + { + delta=0.0f; + sprintf(szParam,"Strip[%i].GainLayer[%i]", nuStrip, index); + rep=SendParameterToVoicemeeter(szParam, value); + if (rep >= 0) + { + delta= value-lpp->strip[nuStrip].strip_gain[index]; + lpp->strip[nuStrip].strip_gain[index] = value; + } + switch(lpapp->StripSliderMode) + { + case SLIDERLINKMODE_ABSLINK: // absolute link = all slider get the same value + for (nu=0;nuvbvmr_nbbus;nu++) + { + // we update other slider / visible on the strip + if ((nu != index) && ((lpp->strip[nuStrip].strip_assignmentbit[nu] & 0xF0) != 0)) + { + sprintf(szParam,"Strip[%i].GainLayer[%i]", nuStrip, nu); + rep=SendParameterToVoicemeeter(szParam, value); + if (rep >= 0) + { + lpp->strip[nuStrip].strip_gain[nu] = value; + } + } + } + VMSCTL_SetDataSTRIP(lpapp->hw_StripList[nuStrip], &(lpp->strip[nuStrip]), FALSE); + break; + case SLIDERLINKMODE_RELLINK:// relative link = all slider are moved by the same amount (delta) + for (nu=0;nuvbvmr_nbbus;nu++) + { + // we update other slider / visible on the strip + if ((nu != index) && ((lpp->strip[nuStrip].strip_assignmentbit[nu] & 0xF0) != 0)) + { + vvv = lpp->strip[nuStrip].strip_gain[nu] + delta; + sprintf(szParam,"Strip[%i].GainLayer[%i]", nuStrip, nu); + rep=SendParameterToVoicemeeter(szParam, vvv); + if (rep >= 0) + { + lpp->strip[nuStrip].strip_gain[nu] = vvv; + } + } + } + VMSCTL_SetDataSTRIP(lpapp->hw_StripList[nuStrip], &(lpp->strip[nuStrip]), FALSE); + break; + } + } + return; + } + // from BUS Assignation button + if ((ICtl >= VMSCTL_STRIP_ID_ASSIGN) && (ICtl < (VMSCTL_STRIP_ID_ASSIGN + VMSCTL_MAX_NBBUS))) + { + nuVisibleSlider = ICtl - VMSCTL_STRIP_ID_ASSIGN; + index = VMSCTL_GetIndexSTRIP(lpapp->hw_StripList[nuStrip],nuVisibleSlider); + + if ((index >=0) && (lpapp->vbvmr_pBUSName!=NULL)) + { + sprintf(szParam,"Strip[%i].%s", nuStrip, lpapp->vbvmr_pBUSName[index]); + rep=SendParameterToVoicemeeter(szParam, value); + if (rep >= 0) + { + c1=0x00; + bus_assignbit = lpp->strip[nuStrip].strip_assignmentbit[index] & 0xF0; + if (value != 0.0f) c1=0x01; + lpp->strip[nuStrip].strip_assignmentbit[index] = bus_assignbit | c1; + } + } + return; + } + + // other sub control + switch(ICtl) + { + case VMSCTL_STRIP_ID_MUTE: + sprintf(szParam,"Strip[%i].Mute", nuStrip); + rep=SendParameterToVoicemeeter(szParam, value); // set value in Voicemeeter + if (rep >= 0) lpp->strip[nuStrip].strip_mute = (long)value; // update internal parameters. + break; + } +} + +void __stdcall APP_OutputCtlCallback(void * lpuser,long Ident, long ICtl, float value) +{ + char szParam[128]; + LPT_MYVM_PARAMETERS lpp; + long rep,nuBus, vi; + LPT_APP_CONTEXT lpapp; + lpapp=(LPT_APP_CONTEXT)lpuser; + lpp = &(lpapp->current_param); + nuBus = Ident; + switch(ICtl) + { + // From Mute button + case VMSCTL_BUS_ID_MUTE: + sprintf(szParam,"Bus[%i].Mute", nuBus); + rep=SendParameterToVoicemeeter(szParam, value); // set value in Voicemeeter + if (rep >= 0) lpp->bus[nuBus].bus_mute = (long)value; // update internal parameters. + break; + // From Gain Slider + case VMSCTL_BUS_ID_GAIN: + sprintf(szParam,"Bus[%i].Gain", nuBus); + rep=SendParameterToVoicemeeter(szParam, value); // set value in Voicemeeter + if (rep >= 0) lpp->bus[nuBus].bus_gain = value; // update internal parameters. + break; + // from monitor + case VMSCTL_BUS_ID_MONITOR: + sprintf(szParam,"Bus[%i].Sel", nuBus); + rep=SendParameterToVoicemeeter(szParam, value); // set value in Voicemeeter + if (rep >= 0) + { + lpp->bus[nuBus].bus_monitor = (long)value; // update internal parameters. + if (value != 0.0) + { + // Remove all other SEL (Monitor = exclusive SEL) + for (vi=0;vivbvmr_nbbus;vi++) + { + if (vi != nuBus) + { + sprintf(szParam,"Bus[%i].Sel", vi); + rep=SendParameterToVoicemeeter(szParam, 0.0f); + if (rep >= 0) + { + lpp->bus[nuBus].bus_monitor=0; + VMSCTL_SetDataBUS(lpapp->hw_BusList[vi], &(lpp->bus[vi]), FALSE); + } + } + } + } + } + break; + } +} + + + +/*******************************************************************************/ +/* CONTROLS */ +/*******************************************************************************/ + +static long CreateAllControls(LPT_APP_CONTEXT lpapp, HWND hParent) +{ + T_VMSCTL_STRIPPARAM param_strip; + T_VMSCTL_BUSPARAM param_bus; + long vi; + + // Create all STRIP Control + memset(¶m_strip, 0, sizeof(T_VMSCTL_STRIPPARAM)); + + param_strip.vmin =-60.0f; + param_strip.vmax =+12.0f; + + param_strip.font0 =lpapp->font0; + param_strip.font1 =lpapp->font1; + param_strip.font2 =lpapp->font2; + param_strip.bkg0_brush =lpapp->gdiobjects_bkg.brush; + param_strip.bkg1_brush =lpapp->gdiobjects_bkg_ctl.brush; + + param_strip.lpCallback =APP_InputCtlCallback; + param_strip.lpuser =lpapp; + + param_strip.assignbus_pen =lpapp->gdiobjects_blue1.pen; + param_strip.assignbus_color =lpapp->gdiobjects_blue1.color; + param_strip.name_color =lpapp->gdiobjects_white.color; + param_strip.meter_brush1 =lpapp->gdiobjects_meter_blue.brush; + param_strip.meter_brush2 =lpapp->gdiobjects_meter_green.brush; + param_strip.meter_brush3 =lpapp->gdiobjects_meter_red.brush; + + param_strip.slider_pen_green =lpapp->gdiobjects_slidergreen.pen; + param_strip.slider_brush_green =lpapp->gdiobjects_slidergreen.brush; + param_strip.slider_pen_red =lpapp->gdiobjects_sliderred.pen; + param_strip.slider_brush_red =lpapp->gdiobjects_sliderred.brush; + + param_strip.mutered_color =lpapp->gdiobjects_mute.color; + param_strip.mutered_pen =lpapp->gdiobjects_mute.pen; + param_strip.assigned_color =lpapp->gdiobjects_slidergreen.color; + param_strip.assigned_pen =lpapp->gdiobjects_slidergreen.pen; + + + for (vi=0;vihw_StripList[vi] = VMSCTL_CreateSTRIP(hParent, vi, 10,10, ¶m_strip); + } + + + // Create all BUS Control + memset(¶m_bus, 0, sizeof(T_VMSCTL_STRIPPARAM)); + + param_bus.vmin =-60.0f; + param_bus.vmax =+12.0f; + + param_bus.font0 =lpapp->font0; + param_bus.font1 =lpapp->font1; + param_bus.font2 =lpapp->font2; + param_bus.bkg0_brush =lpapp->gdiobjects_bkg.brush; + param_bus.bkg1_brush =lpapp->gdiobjects_bkg_ctl.brush; + param_bus.assignbus_pen =lpapp->gdiobjects_blue1.pen; + param_bus.assignbus_color=lpapp->gdiobjects_blue1.color; + param_bus.name_color =lpapp->gdiobjects_white.color; + param_bus.meter_brush1 =lpapp->gdiobjects_meter_blue.brush; + param_bus.meter_brush2 =lpapp->gdiobjects_meter_green.brush; + param_bus.meter_brush3 =lpapp->gdiobjects_meter_red.brush; + + param_bus.slider_pen_green =lpapp->gdiobjects_slidergreen.pen; + param_bus.slider_brush_green=lpapp->gdiobjects_slidergreen.brush; + param_bus.slider_pen_red =lpapp->gdiobjects_sliderred.pen; + param_bus.slider_brush_red =lpapp->gdiobjects_sliderred.brush; + + param_bus.mutered_color =lpapp->gdiobjects_mute.color; + param_bus.mutered_pen =lpapp->gdiobjects_mute.pen; + + param_bus.monitor_brush =lpapp->gdiobjects_monitor.brush; + param_bus.monitor_pen =lpapp->gdiobjects_monitor.pen; + + param_bus.lpCallback =APP_OutputCtlCallback; + param_bus.lpuser =lpapp; + + for (vi=0;vihw_BusList[vi]; + lpapp->hw_BusList[vi] = VMSCTL_CreateBUS(hParent, vi, 10,10, ¶m_bus); + + } + return 0; +} + + +static void DestroyAllControls(LPT_APP_CONTEXT lpapp) +{ + long vi; + for (vi=0;vihw_StripList[vi] != NULL) DestroyWindow(lpapp->hw_StripList[vi]); + lpapp->hw_StripList[vi]=NULL; + + } + for (vi=0;vihw_BusList[vi] != NULL) DestroyWindow(lpapp->hw_BusList[vi]); + lpapp->hw_BusList[vi]=NULL; + } +} + +#define APP_MARGIN 10 +#define APP_SECTIONLABEL 25 + +static void APP_RecomputeLayout(LPT_APP_CONTEXT lpapp, long dx, long dy) +{ + RECT rect; + LPT_MYVM_PARAMETERS lpp; + long fShow; + long vi, x0, y0; + long nbStrip, nbBus; + long dx_strip, dy_strip; + long dx_bus, dy_bus; + long fRecompute = 0; + if ((dx == 0) || (dy == 0)) fRecompute=1; + if (dx > 0) + { + if (lpapp->hw_dx !=dx) fRecompute=1; + lpapp->hw_dx =dx; + } + else dx = lpapp->hw_dx; + if (dy > 0) + { + if (lpapp->hw_dy !=dy) fRecompute=1; + lpapp->hw_dy =dy; + } + else dy = lpapp->hw_dy; + if (fRecompute == 0) return; + + rect.left=APP_MARGIN; + rect.right=dx-APP_MARGIN; + rect.top=APP_MARGIN; + rect.bottom=dy-APP_MARGIN; + + dx=dx-APP_MARGIN; + dy=dy-APP_MARGIN; + lpp = &(lpapp->current_param); + nbStrip =lpp->nbStrip; + nbBus =lpp->nbBus; + + dx_bus = dx-APP_MARGIN; + dy_bus = 40; + if (nbStrip > 0) dx_strip = dx / nbStrip; + else dx_strip=dx; + dy_strip = dy - (dy_bus*nbBus) - APP_SECTIONLABEL - APP_SECTIONLABEL-APP_MARGIN-APP_MARGIN; + + lpapp->rect_inputs=rect; + lpapp->rect_inputs.bottom=lpapp->rect_inputs.top+APP_SECTIONLABEL; + + lpapp->rect_outputs=rect; + lpapp->rect_outputs.top += APP_SECTIONLABEL + dy_strip+APP_MARGIN; + lpapp->rect_outputs.bottom=lpapp->rect_outputs.top+APP_SECTIONLABEL; + + //position strip + x0 = APP_MARGIN; + y0 = APP_MARGIN+APP_SECTIONLABEL+10; + dy_strip -=10; + for (vi = 0; vi < VMSCTL_MAX_NBSTRIP; vi++) + { + if (lpp->strip[vi].strip_namew[0] != 0) + { + fShow=1; + } + else fShow=0; + VMSCTL_PositionSTRIP(lpapp->hw_StripList[vi], x0,y0, dx_strip-APP_MARGIN, dy_strip, fShow); + if (fShow != 0) x0=x0+dx_strip; + } + + //position bus + x0 = APP_MARGIN; + y0 = lpapp->rect_outputs.bottom+10; + for (vi = 0; vi < VMSCTL_MAX_NBBUS; vi++) + { + if ((lpp->bus[vi].bus_namew[0] != 0) || (vi==0)) + { + fShow=1; + } + else fShow=0; + VMSCTL_PositionBUS(lpapp->hw_BusList[vi], x0,y0, dx_bus, dy_bus-APP_MARGIN, fShow); + if (fShow != 0) y0=y0+dy_bus; + } +} + +static void APP_ResetData(LPT_APP_CONTEXT lpapp) +{ + long vi; + memset(&(lpapp->current_param), 0 , sizeof(T_MYVM_PARAMETERS )); //reset parameters + for (vi = 0; vi < VMSCTL_MAX_NBSTRIP; vi++) + { + VMSCTL_ResetDataSTRIP(lpapp->hw_StripList[vi]); + } + for (vi = 0; vi < VMSCTL_MAX_NBBUS; vi++) + { + VMSCTL_ResetDataBUS(lpapp->hw_BusList[vi]); + } +} + + + +/*******************************************************************************/ +/* WIN CALLBACK */ +/*******************************************************************************/ + +#define MYTIMERID 16489 + +static void StartVBANConnection(LPT_APP_CONTEXT lpapp, HWND hw, long nuVBAN) +{ + LPT_VBAN_CMD_PARAM lpVBANParam; + lpVBANParam = &(lpapp->VMLinkVBAN[nuVBAN]); + VBANCMD_StartThread(lpVBANParam->VBAN_szStreamName, lpVBANParam->VBAN_szIPAddress, (unsigned short)lpVBANParam->VBAN_UDPport); + lpapp->VMConnectionType =nuVBAN+1; + // send request to register for RT-Packet. + VBANCMD_SendRequest_RegisterRTPacket(15, "VMStreamerView"); + //disconnect to force reconnection + lpapp->vbvmr_connect =0; + if (hw != NULL) InvalidateRect(hw,NULL,TRUE); +} + + +LRESULT CALLBACK MainWindowManageEvent(HWND hw, //handle of the window. + UINT msg, //Message Ident. + WPARAM wparam, //parameter 1. + LPARAM lparam) //parameter 2 +{ + HMENU sysmenu; + HPEN oldpen; + HBRUSH oldbrush; + RECT rect; + long dx,dy, nChange, nNewLogin, fUpdatePaint; + long nCommand, nuVBAN; + BOOL LastConnectedState; + DWORD ms, delta, nnn; + LPT_APP_CONTEXT lpapp; + HDC dc; + PAINTSTRUCT ps; + + lpapp = &G_MainAppCtx; + switch (msg) + { + + + case WM_CREATE: + if (InitSoftware(lpapp,hw) == FALSE) return -1;//return -1 here cancel the window creation + CreateAllControls(lpapp, hw); + + // add item in system menu + sysmenu=GetSystemMenu(hw,FALSE); + if (sysmenu != NULL) + { + AppendMenu(sysmenu,MF_SEPARATOR,0,NULL); + AppendMenu(sysmenu,MF_STRING,IDMSYS_LINK_DIRECT,"Direct Connection"); + AppendMenuA(sysmenu,MF_STRING,IDMSYS_LINK_VBAN1,"VBAN #1 Connection"); + AppendMenuA(sysmenu,MF_STRING,IDMSYS_LINK_VBAN2,"VBAN #2 Connection"); + AppendMenuA(sysmenu,MF_STRING,IDMSYS_LINK_VBAN3,"VBAN #3 Connection"); + AppendMenuA(sysmenu,MF_STRING,IDMSYS_LINK_VBAN4,"VBAN #4 Connection"); + AppendMenu(sysmenu,MF_SEPARATOR,0,NULL); + AppendMenuA(sysmenu,MF_STRING,IDMSYS_VBAN_CONFIG,"VBAN Configuration..."); + } + PostMessage(hw,WM_COMMAND, IDM_UPDATE_ALL_CONTROL,0); + lpapp->wTimer = SetTimer(hw,MYTIMERID, 30,NULL);//30 Hz timer + + if (lpapp->VMConnectionType > 0) + { + StartVBANConnection(lpapp, NULL, lpapp->VMConnectionType-1); + } + break; + case WM_INITMENU: + sysmenu = GetSystemMenu(hw,FALSE); + if (sysmenu != NULL) + { + if (lpapp->VMConnectionType == 0) CheckMenuItem(sysmenu, IDMSYS_LINK_DIRECT, MF_BYCOMMAND | MF_CHECKED); + else CheckMenuItem(sysmenu, IDMSYS_LINK_DIRECT, MF_BYCOMMAND | MF_UNCHECKED); + if (lpapp->VMConnectionType == 1) CheckMenuItem(sysmenu, IDMSYS_LINK_VBAN1, MF_BYCOMMAND | MF_CHECKED); + else CheckMenuItem(sysmenu, IDMSYS_LINK_VBAN1, MF_BYCOMMAND | MF_UNCHECKED); + if (lpapp->VMConnectionType == 2) CheckMenuItem(sysmenu, IDMSYS_LINK_VBAN2, MF_BYCOMMAND | MF_CHECKED); + else CheckMenuItem(sysmenu, IDMSYS_LINK_VBAN2, MF_BYCOMMAND | MF_UNCHECKED); + if (lpapp->VMConnectionType == 3) CheckMenuItem(sysmenu, IDMSYS_LINK_VBAN3, MF_BYCOMMAND | MF_CHECKED); + else CheckMenuItem(sysmenu, IDMSYS_LINK_VBAN3, MF_BYCOMMAND | MF_UNCHECKED); + if (lpapp->VMConnectionType == 4) CheckMenuItem(sysmenu, IDMSYS_LINK_VBAN4, MF_BYCOMMAND | MF_CHECKED); + else CheckMenuItem(sysmenu, IDMSYS_LINK_VBAN4, MF_BYCOMMAND | MF_UNCHECKED); + } + break; + case WM_LBUTTONDOWN: + APP_ManageLRButtonDown(lpapp, hw, (short int)LOWORD(lparam),(short int)HIWORD(lparam),0); + break; + case WM_SIZE: + dx=(short)LOWORD(lparam); + dy=(short)HIWORD(lparam); + APP_RecomputeLayout(lpapp, dx,dy); + break; + + case WM_COMMAND: + ManageMenu(lpapp,hw,wparam,lparam); + break; + + case WM_SYSCOMMAND: + nCommand = LOWORD(wparam); + switch(nCommand) + { + case IDMSYS_VBAN_CONFIG: + VBANDIALOG_DialogGo(hw,0); + break; + case IDMSYS_LINK_DIRECT: + if (lpapp->VMConnectionType == 0) break; + VBANCMD_StopThread(); + lpapp->VMConnectionType =0; + //disconnect to force reconnection + lpapp->vbvmr_connect =0; + InvalidateRect(hw,NULL,TRUE); + break; + case IDMSYS_LINK_VBAN1: + case IDMSYS_LINK_VBAN2: + case IDMSYS_LINK_VBAN3: + case IDMSYS_LINK_VBAN4: + nuVBAN = nCommand-IDMSYS_LINK_VBAN1; + if (lpapp->VMConnectionType == (nuVBAN+1)) break; + StartVBANConnection(lpapp, hw, nuVBAN); + break; + + default: + return DefWindowProc(hw,msg,wparam,lparam); + } + break; + case WM_TIMER: + if (wparam == MYTIMERID) + { + ms = GetTickCount(); + + // if direct connection + if ((lpapp->VMConnectionType == 0) && (lpapp->vbvmr_NotInstalled == 0)) + { + //check what is changed + lpapp->vbvmr_error=iVMR.VBVMR_IsParametersDirty(); + } + // if VBAN connection + else + { + // every 1 seconds + delta = TOOL_GetDeltaMsDword(lpapp->msLastTimeForVBANCheck, ms); + if (delta > 1000) + { + LastConnectedState = lpapp->VBANisConnected; + nnn = VBANCMD_GetIncomingRequestCounter(); + if (nnn != lpapp->LastCounter) lpapp->VBANisConnected=TRUE; + else lpapp->VBANisConnected=FALSE; + lpapp->LastCounter = nnn; + lpapp->msLastTimeForVBANCheck = ms; + // if the VBAN connection is changed, we update the display + if (LastConnectedState != lpapp->VBANisConnected) + { + dc=GetDC(hw); + DrawVMConnectionMode(lpapp, dc); + ReleaseDC(hw,dc); + } + } + // every 10 seconds + delta = TOOL_GetDeltaMsDword(lpapp->msLastTimeForVBANRegister, ms); + if (delta > 10000) + { + // we ask for 15 second timeout = we will get RT-Packet for 15 seconds + VBANCMD_SendRequest_RegisterRTPacket(15, "VMStreamerView"); + lpapp->msLastTimeForVBANRegister = ms; + } + lpapp->vbvmr_error=VBANCMD_IsParameterDirty(); + } + // manage behavior + if (lpapp->vbvmr_error >= 0) + { + //if not already connected we detect Voicemeeter type + nNewLogin=0; + if (lpapp->vbvmr_connect == 0) + { + nNewLogin = DetectVoicemeeterType(lpapp, hw); + if (nNewLogin != 0) APP_ResetData(lpapp); + } + //if param change + if ((lpapp->vbvmr_error == 1) || (nNewLogin != 0)) + { + fUpdatePaint=0; + nChange=AnalyseParamChange(lpapp); + if (nNewLogin != 0) nChange=VMRS_BITALL; + //if layout change + if ((nChange & (VMRS_BITSTRIPNB | VMRS_BITBUSNB)) != 0) + { + APP_RecomputeLayout(lpapp, 0,0); + fUpdatePaint=1; + } + if ((nChange & VMRS_BITMON) != 0) fUpdatePaint=1; + if (nChange != 0) + { + UpdateAllControl(lpapp, nChange); + if (fUpdatePaint != 0) InvalidateRect(hw,NULL,FALSE); + } + } + } + // otherwise we consider it's not connected + else + { + // manage disconnection + if (lpapp->vbvmr_connect != 0) + { + //Voicemeeter has been shut down / disconnected + lpapp->vbvmr_connect =0; + InvalidateRect(hw,NULL,TRUE); + } + } + //Peak Meter display + if (lpapp->vbvmr_connect != 0) UpdateAllPeakMeters(lpapp); + } + break; + case WM_PAINT: + dc=BeginPaint(hw,&ps); + DrawAllStuff(lpapp,hw, dc); + EndPaint(hw,&ps); + break; + case WM_ERASEBKGND: + dc=(HDC)wparam; + GetClientRect(hw,&rect); + oldbrush = (HBRUSH)SelectObject(dc,lpapp->gdiobjects_bkg.brush); + oldpen = (HPEN)SelectObject(dc,lpapp->gdiobjects_bkg.pen); + Rectangle(dc,rect.left,rect.top,rect.right,rect.bottom); + SelectObject(dc,oldpen); + SelectObject(dc,oldbrush); + return 0; + case WM_DESTROY: + GetWindowRect(hw, &(lpapp->CurrentWindowRect)); + SaveAppConfiguration(lpapp); + if (lpapp->wTimer != 0) KillTimer(hw,lpapp->wTimer); + lpapp->wTimer=0; + DestroyAllControls(lpapp); + EndSoftware(lpapp,hw); + PostQuitMessage(0); + break; + default: + return (DefWindowProc(hw,msg,wparam,lparam)); + + } + return (0L); +} + + +/*******************************************************************************/ +/** MAIN PROCDURE **/ +/*******************************************************************************/ + +int APIENTRY WinMain(HINSTANCE handle_app, //Application hinstance. + HINSTANCE handle_prev, //NULL. + LPTSTR param, //Command Line Parameter. + int com_show) //How to display window (optionnal). +{ + HANDLE hMutex; + HWND hh; + long wstyle, wdx, wdy; + MSG msg; + char szWindowClassName[]="MainWindowClass"; + char * title="Sorry"; + + WNDCLASS wc; + LPT_APP_CONTEXT lpapp; + lpapp = &G_MainAppCtx; + + //we first store the APP Hinstance + memset(lpapp, 0, sizeof(T_APP_CONTEXT)); + lpapp->hinstance=handle_app; + + // + // Use Mutex to prevent to launch several instance of the application + // + hMutex = CreateMutex(NULL,TRUE,APP_UNIQUEMUTEX); + if (hMutex == NULL) return -1; + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + ReleaseMutex(hMutex); + return -1; + } + else WaitForSingleObject(hMutex,INFINITE); + +#ifdef VBUSE_LOCALLIB + //if we directly link source code (VB local development only) + VBVMR_SetHinstance(handle_app); +#endif + //here you can make some early initialization and analyze command line if any. + VMSCTL_InitLib(handle_app); + VBANCMD_InitLib(0); + + //load last configuration and get Windows position/size + lpapp->CurrentWindowRect.left = CW_USEDEFAULT; + lpapp->CurrentWindowRect.top = CW_USEDEFAULT; + lpapp->CurrentWindowRect.right = CW_USEDEFAULT+UI_WIN_DX; + lpapp->CurrentWindowRect.bottom = CW_USEDEFAULT+UI_WIN_DY; + LoadAppConfiguration(lpapp); + wdx = lpapp->CurrentWindowRect.right-lpapp->CurrentWindowRect.left; + wdy = lpapp->CurrentWindowRect.bottom - lpapp->CurrentWindowRect.top; + + //we define a window class to create a window from this class + wc.style =CS_HREDRAW | CS_VREDRAW; //property. + wc.lpfnWndProc=(WNDPROC)MainWindowManageEvent; //Adresse of our Callback. + wc.cbClsExtra =0; //Possibility to store some byte inside a class object. + wc.cbWndExtra =0; //Possibility to store some byte inside a window object. + wc.hInstance =handle_app; //handle of the application hinstance. + wc.hIcon =LoadIcon(handle_app, MAKEINTRESOURCE(100)); //handle of icon displayed in the caption. + wc.hCursor =LoadCursor(NULL,IDC_ARROW); //handle of cursor mouse . + wc.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);//Background color. + wc.lpszMenuName=NULL; //pointer on menu defined in resource. + wc.lpszClassName=szWindowClassName; //pointer on class name. + + //register class. + if (RegisterClass(&wc)==0) + { + MessageBox(NULL,"Failed to register main class...",title,MB_APPLMODAL | MB_OK | MB_ICONERROR); + return 0; + } + + + //classical Main Window + wstyle = WS_OVERLAPPEDWINDOW | WS_VISIBLE; + + hh=CreateWindow(szWindowClassName, // address of registered class name. + SZPUBLICNAME, // address of window name string + wstyle, // window style + lpapp->CurrentWindowRect.left, // horizontal position of window + lpapp->CurrentWindowRect.top, // vertical position of window + wdx, // window width + wdy, // window height + NULL, // parent handle is NULL since it's a main window. + NULL, // menu name defined in resource (NULL if no menu or already defined in the Class). + handle_app, // handle of application instance + NULL); // address of window-creation data + + if (hh==NULL) + { + MessageBox(NULL,"Failed to create window...",title,MB_APPLMODAL | MB_OK | MB_ICONERROR); + return 0; + } + ShowWindow(hh,SW_SHOW); //Display the window. + UpdateWindow(hh); //Send WM_PAINT. + /*---------------------------------------------------------------------------*/ + /* Messages Loop. */ + /*---------------------------------------------------------------------------*/ + while (GetMessage(&msg,NULL,0,0)) //Get Message if any. + { + TranslateMessage(&msg); //Translate the virtuel keys event. + DispatchMessage(&msg); //DispatchMessage to the related window. + } + + //here you can make last uninitialization and release + VBANCMD_EndLib(); + VMSCTL_EndLib(); + return (int)(msg.wParam); +} diff --git a/vmr_streamer/source/vmr_streamer.h b/vmr_streamer/source/vmr_streamer.h new file mode 100644 index 0000000..a4874e5 --- /dev/null +++ b/vmr_streamer/source/vmr_streamer.h @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------*/ +/* VMR example: Voicemeeter Custom GUI for Streamers */ +/*--------------------------------------------------------------------------------*/ +/* 'C' Sample Code to make a Voicemeeter Custom GUI V.Burel (c)2016-2021 */ +/* */ +/* THIS PROGRAM PROVIDES A GUI TO CONTROL VOICEMEETER */ +/* PROVIDES A SIMPLIFIED GUI WITH ONLY USED STRIP/BUS */ +/* PROVIDES A RESIZABLE GRAPHIC USER INTERFACE */ +/* */ +/* This program example shows */ +/* - How to link VoicemeeterRemote.dll */ +/* - How to Login / logout */ +/* - How to Manage regular parameters to make a custom controller. */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* COMPILATION DIRECTIVES: */ +/* */ +/* To compile With Microsoft VC2005 or higher, you need to create an */ +/* empty Win32 project with the following options: */ +/* - Configuration Properties / General : Char Set = NOT SET */ +/* - Configuration Properties / C/C++ / Preprocessor : _CRT_SECURE_NO_DEPRECATE */ +/* */ +/* This source code can be compiled for 32bit or 64 bits targets as well */ +/* WARNING: FOR 64x COMPILATION, ADD PREPROCESSOR DEFINE: VB_X64 */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* LICENSING: VoicemeeterRemote.dll usage is driven by a license agreement */ +/* given in VoicemeeterRemoteAPI.pdf or readme.txt */ +/* THIS SOURCE CODE CAN BE USED ONLY IN A PROGRAM USING VOICEMEETER */ +/* REMOTE API */ +/* */ +/*--------------------------------------------------------------------------------*/ + +#ifndef __VMR_STREAMER_H__ +#define __VMR_STREAMER_H__ + +//to prevent to launch the same application twice (single instance app) +#define APP_UNIQUEMUTEX "VB-AudioVMRStreamerViewExample_VoicemeeterCustomGUI" + +//version information (for program) +#define SZPUBLICVERSION "1.0.0.2" //displayed version in about box +#define SZPUBLICNAME "VM-Streamer View (Voicemeeter Custom GUI Example)" //displayed title in main window + +//Information for Main window +#define UI_WIN_DX 800 +#define UI_WIN_DY 600 + +//version information (used in resource file) +#define __FILEVERSION__ 1,0,0,2 +#define __PRODUCTVERSION__ 1,0,0,2 +#define __SZFILEVERSION__ "1, 0, 0, 2\0" +#define __SZPRODUCTVERSION__ "1, 0, 0, 2\0" + + #define __COMMENTS__ "Example of custom GUI mae for Voicemeeter with the Voicemeeter Remote API" + #define __COMPANYNAME__ "Audio Mechanic & Sound Breeder\0" + #define __FILEDESCRIPTION__ "Custom GUI for Streamers, Remoting Voicemeeter potato\0" + #define __INTERNALNAME__ "vmr_streamer" + #define __LEGALCOPYRIGHT__ "Copyright V.Burel©2015-2021\0" + #define __ORIGINALFILENAME__ "vmr_streamer.EXE\0" + #define __PRODUCTNAME__ "vmr_streamer\0" + + + +//definitions for MENU + +#define IDM_QUIT 100 +#define IDM_ABOUT 101 + +#define IDM_UPDATE_ALL_CONTROL 200 + +#define IDMSYS_LINK_DIRECT 300 +#define IDMSYS_LINK_VBAN1 301 +#define IDMSYS_LINK_VBAN2 302 +#define IDMSYS_LINK_VBAN3 303 +#define IDMSYS_LINK_VBAN4 304 + +#define IDMSYS_VBAN_CONFIG 350 + +#endif /*__VMR_STREAMER_H__*/ + + diff --git a/vmr_streamer/source/vmr_streamer.ico b/vmr_streamer/source/vmr_streamer.ico new file mode 100644 index 0000000..75e8d7e Binary files /dev/null and b/vmr_streamer/source/vmr_streamer.ico differ diff --git a/vmr_streamer/source/vmr_streamer.rc b/vmr_streamer/source/vmr_streamer.rc new file mode 100644 index 0000000..3aac284 --- /dev/null +++ b/vmr_streamer/source/vmr_streamer.rc @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------*/ +/* VMR example: Voicemeeter Custom GUI for Streamers */ +/*--------------------------------------------------------------------------------*/ +/* 'C' Sample Code to make a Voicemeeter Custom GUI V.Burel (c)2016-2021 */ +/* */ +/* THIS PROGRAM PROVIDES A GUI TO CONTROL VOICEMEETER */ +/* PROVIDES A SIMPLIFIED GUI WITH ONLY USED STRIP/BUS */ +/* PROVIDES A RESIZABLE GRAPHIC USER INTERFACE */ +/* */ +/* This program example shows */ +/* - How to link VoicemeeterRemote.dll */ +/* - How to Login / logout */ +/* - How to Manage regular parameters to make a custom controller. */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* COMPILATION DIRECTIVES: */ +/* */ +/* To compile With Microsoft VC2005 or higher, you need to create an */ +/* empty Win32 project with the following options: */ +/* - Configuration Properties / General : Char Set = NOT SET */ +/* - Configuration Properties / C/C++ / Preprocessor : _CRT_SECURE_NO_DEPRECATE */ +/* */ +/* This source code can be compiled for 32bit or 64 bits targets as well */ +/* WARNING: FOR 64x COMPILATION, ADD PREPROCESSOR DEFINE: VB_X64 */ +/* */ +/*--------------------------------------------------------------------------------*/ +/* */ +/* LICENSING: VoicemeeterRemote.dll usage is driven by a license agreement */ +/* given in VoicemeeterRemoteAPI.pdf or readme.txt */ +/* */ +/*--------------------------------------------------------------------------------*/ + + +#include "vmr_streamer.h" + +100 ICON "vmr_streamer.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +1 VERSIONINFO + FILEVERSION __FILEVERSION__ + PRODUCTVERSION __PRODUCTVERSION__ + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x0L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Comments", __COMMENTS__ + VALUE "CompanyName", __COMPANYNAME__ + VALUE "FileDescription", __FILEDESCRIPTION__ + VALUE "FileVersion", __SZFILEVERSION__ + VALUE "InternalName", __INTERNALNAME__ + VALUE "LegalCopyright", __LEGALCOPYRIGHT__ + VALUE "OriginalFilename", __ORIGINALFILENAME__ + VALUE "ProductName", __PRODUCTNAME__ + VALUE "ProductVersion", __SZPRODUCTVERSION__ + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END + + diff --git a/vmr_streamer/targetver.h b/vmr_streamer/targetver.h new file mode 100644 index 0000000..bf75e08 --- /dev/null +++ b/vmr_streamer/targetver.h @@ -0,0 +1,6 @@ +#pragma once + +// // Including SDKDDKVer.h defines the highest available Windows platform. +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. +#include diff --git a/vmr_streamer/vmr_streamer.cpp b/vmr_streamer/vmr_streamer.cpp new file mode 100644 index 0000000..1630a06 --- /dev/null +++ b/vmr_streamer/vmr_streamer.cpp @@ -0,0 +1,180 @@ +// vmr_streamer.cpp : Defines the entry point for the application. +// + +#include "framework.h" +#include "vmr_streamer.h" + +#define MAX_LOADSTRING 100 + +// Global Variables: +HINSTANCE hInst; // current instance +WCHAR szTitle[MAX_LOADSTRING]; // The title bar text +WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name + +// Forward declarations of functions included in this code module: +ATOM MyRegisterClass(HINSTANCE hInstance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + // TODO: Place code here. + + // Initialize global strings + LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadStringW(hInstance, IDC_VMRSTREAMER, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + // Perform application initialization: + if (!InitInstance (hInstance, nCmdShow)) + { + return FALSE; + } + + HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_VMRSTREAMER)); + + MSG msg; + + // Main message loop: + while (GetMessage(&msg, nullptr, 0, 0)) + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return (int) msg.wParam; +} + + + +// +// FUNCTION: MyRegisterClass() +// +// PURPOSE: Registers the window class. +// +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEXW wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_VMRSTREAMER)); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_VMRSTREAMER); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassExW(&wcex); +} + +// +// FUNCTION: InitInstance(HINSTANCE, int) +// +// PURPOSE: Saves instance handle and creates main window +// +// COMMENTS: +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +// +BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) +{ + hInst = hInstance; // Store instance handle in our global variable + + HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); + + if (!hWnd) + { + return FALSE; + } + + ShowWindow(hWnd, nCmdShow); + UpdateWindow(hWnd); + + return TRUE; +} + +// +// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) +// +// PURPOSE: Processes messages for the main window. +// +// WM_COMMAND - process the application menu +// WM_PAINT - Paint the main window +// WM_DESTROY - post a quit message and return +// +// +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_COMMAND: + { + int wmId = LOWORD(wParam); + // Parse the menu selections: + switch (wmId) + { + case IDM_ABOUT: + DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About); + break; + case IDM_EXIT: + DestroyWindow(hWnd); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + break; + case WM_PAINT: + { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hWnd, &ps); + // TODO: Add any drawing code that uses hdc here... + EndPaint(hWnd, &ps); + } + break; + case WM_DESTROY: + PostQuitMessage(0); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (message) + { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hDlg, LOWORD(wParam)); + return (INT_PTR)TRUE; + } + break; + } + return (INT_PTR)FALSE; +} diff --git a/vmr_streamer/vmr_streamer.vcxproj b/vmr_streamer/vmr_streamer.vcxproj new file mode 100644 index 0000000..ba96530 --- /dev/null +++ b/vmr_streamer/vmr_streamer.vcxproj @@ -0,0 +1,165 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {cb959fdd-2aa4-4ecd-8a50-41777bf38eea} + vmrstreamer + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + NotSet + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + NotSet + + + + + + + + + + + + + + + + + + + + + true + + + false + + + true + + + false + + + + Level3 + true + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Ws2_32.lib ;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + + + Windows + true + + + + + Level3 + true + true + true + NDEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;VB_X64;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vmr_streamer/vmr_streamer.vcxproj.filters b/vmr_streamer/vmr_streamer.vcxproj.filters new file mode 100644 index 0000000..3b6604b --- /dev/null +++ b/vmr_streamer/vmr_streamer.vcxproj.filters @@ -0,0 +1,58 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file