2024-09-24 12:54:57 +00:00
/*
* BridgeWrapper . cpp
* - - - - - - - - - - - - - - - - -
* Purpose : VST plugin bridge wrapper ( host side )
* Notes : ( currently none )
* Authors : OpenMPT Devs
* The OpenMPT source code is released under the BSD license . Read LICENSE for more details .
*/
# include "stdafx.h"
# ifdef MPT_WITH_VST
# include "BridgeWrapper.h"
# include "../soundlib/plugins/PluginManager.h"
# include "../mptrack/Mainfrm.h"
# include "../mptrack/Mptrack.h"
# include "../mptrack/Vstplug.h"
# include "../mptrack/ExceptionHandler.h"
# include "../common/mptFileIO.h"
# include "../common/mptStringBuffer.h"
# include "../common/misc_util.h"
using namespace Vst ;
OPENMPT_NAMESPACE_BEGIN
std : : vector < BridgeCommon * > BridgeCommon : : m_plugins ;
HWND BridgeCommon : : m_communicationWindow = nullptr ;
int BridgeCommon : : m_instanceCount = 0 ;
thread_local bool BridgeCommon : : m_isAudioThread = false ;
std : : size_t GetPluginArchPointerSize ( PluginArch arch )
{
std : : size_t result = 0 ;
switch ( arch )
{
case PluginArch_x86 :
result = 4 ;
break ;
case PluginArch_amd64 :
result = 8 ;
break ;
case PluginArch_arm :
result = 4 ;
break ;
case PluginArch_arm64 :
result = 8 ;
break ;
default :
result = 0 ;
break ;
}
return result ;
}
ComponentPluginBridge : : ComponentPluginBridge ( PluginArch arch , Generation generation )
: ComponentBase ( ComponentTypeBundled )
, arch ( arch )
, generation ( generation )
{
}
bool ComponentPluginBridge : : DoInitialize ( )
{
mpt : : PathString archName ;
switch ( arch )
{
case PluginArch_x86 :
if ( mpt : : OS : : Windows : : HostCanRun ( mpt : : OS : : Windows : : GetHostArchitecture ( ) , mpt : : OS : : Windows : : Architecture : : x86 ) = = mpt : : OS : : Windows : : EmulationLevel : : NA )
{
return false ;
}
archName = P_ ( " x86 " ) ;
break ;
case PluginArch_amd64 :
if ( mpt : : OS : : Windows : : HostCanRun ( mpt : : OS : : Windows : : GetHostArchitecture ( ) , mpt : : OS : : Windows : : Architecture : : amd64 ) = = mpt : : OS : : Windows : : EmulationLevel : : NA )
{
return false ;
}
archName = P_ ( " amd64 " ) ;
break ;
case PluginArch_arm :
if ( mpt : : OS : : Windows : : HostCanRun ( mpt : : OS : : Windows : : GetHostArchitecture ( ) , mpt : : OS : : Windows : : Architecture : : arm ) = = mpt : : OS : : Windows : : EmulationLevel : : NA )
{
return false ;
}
archName = P_ ( " arm " ) ;
break ;
case PluginArch_arm64 :
if ( mpt : : OS : : Windows : : HostCanRun ( mpt : : OS : : Windows : : GetHostArchitecture ( ) , mpt : : OS : : Windows : : Architecture : : arm64 ) = = mpt : : OS : : Windows : : EmulationLevel : : NA )
{
return false ;
}
archName = P_ ( " arm64 " ) ;
break ;
default :
break ;
}
if ( archName . empty ( ) )
{
return false ;
}
exeName = mpt : : PathString ( ) ;
const mpt : : PathString generationSuffix = ( generation = = Generation : : Legacy ) ? P_ ( " Legacy " ) : P_ ( " " ) ;
const mpt : : PathString exeNames [ ] =
{
theApp . GetInstallPath ( ) + P_ ( " PluginBridge " ) + generationSuffix + P_ ( " - " ) + archName + P_ ( " .exe " ) , // Local
theApp . GetInstallBinPath ( ) + archName + P_ ( " \\ " ) + P_ ( " PluginBridge " ) + generationSuffix + P_ ( " .exe " ) , // Multi-arch
theApp . GetInstallBinPath ( ) + archName + P_ ( " \\ " ) + P_ ( " PluginBridge " ) + generationSuffix + P_ ( " - " ) + archName + P_ ( " .exe " ) // Multi-arch transitional
} ;
for ( const auto & candidate : exeNames )
{
if ( candidate . IsFile ( ) )
{
exeName = candidate ;
break ;
}
}
if ( exeName . empty ( ) )
{
availability = AvailabilityMissing ;
return false ;
}
std : : vector < WCHAR > exePath ( MAX_PATH ) ;
while ( GetModuleFileNameW ( 0 , exePath . data ( ) , mpt : : saturate_cast < DWORD > ( exePath . size ( ) ) ) > = exePath . size ( ) )
{
exePath . resize ( exePath . size ( ) * 2 ) ;
}
uint64 mptVersion = BridgeWrapper : : GetFileVersion ( exePath . data ( ) ) ;
uint64 bridgeVersion = BridgeWrapper : : GetFileVersion ( exeName . ToWide ( ) . c_str ( ) ) ;
if ( bridgeVersion ! = mptVersion )
{
availability = AvailabilityWrongVersion ;
return false ;
}
availability = AvailabilityOK ;
return true ;
}
PluginArch BridgeWrapper : : GetNativePluginBinaryType ( )
{
PluginArch result = PluginArch_unknown ;
switch ( mpt : : OS : : Windows : : GetProcessArchitecture ( ) )
{
case mpt : : OS : : Windows : : Architecture : : x86 :
result = PluginArch_x86 ;
break ;
case mpt : : OS : : Windows : : Architecture : : amd64 :
result = PluginArch_amd64 ;
break ;
case mpt : : OS : : Windows : : Architecture : : arm :
result = PluginArch_arm ;
break ;
case mpt : : OS : : Windows : : Architecture : : arm64 :
result = PluginArch_arm64 ;
break ;
default :
result = PluginArch_unknown ;
break ;
}
return result ;
}
// Check whether we need to load a 32-bit or 64-bit wrapper.
PluginArch BridgeWrapper : : GetPluginBinaryType ( const mpt : : PathString & pluginPath )
{
PluginArch type = PluginArch_unknown ;
mpt : : ifstream file ( pluginPath , std : : ios : : in | std : : ios : : binary ) ;
if ( file . is_open ( ) )
{
IMAGE_DOS_HEADER dosHeader ;
IMAGE_NT_HEADERS ntHeader ;
file . read ( reinterpret_cast < char * > ( & dosHeader ) , sizeof ( dosHeader ) ) ;
if ( dosHeader . e_magic = = IMAGE_DOS_SIGNATURE )
{
file . seekg ( dosHeader . e_lfanew ) ;
file . read ( reinterpret_cast < char * > ( & ntHeader ) , sizeof ( ntHeader ) ) ;
MPT_ASSERT ( ( ntHeader . FileHeader . Characteristics & IMAGE_FILE_DLL ) ! = 0 ) ;
switch ( ntHeader . FileHeader . Machine )
{
case IMAGE_FILE_MACHINE_I386 :
type = PluginArch_x86 ;
break ;
case IMAGE_FILE_MACHINE_AMD64 :
type = PluginArch_amd64 ;
break ;
# if defined(MPT_WITH_WINDOWS10)
case IMAGE_FILE_MACHINE_ARM :
type = PluginArch_arm ;
break ;
case IMAGE_FILE_MACHINE_ARM64 :
type = PluginArch_arm64 ;
break ;
# endif // MPT_WITH_WINDOWS10
default :
type = PluginArch_unknown ;
break ;
}
}
}
return type ;
}
uint64 BridgeWrapper : : GetFileVersion ( const WCHAR * exePath )
{
DWORD verHandle = 0 ;
DWORD verSize = GetFileVersionInfoSizeW ( exePath , & verHandle ) ;
uint64 result = 0 ;
if ( verSize = = 0 )
{
return result ;
}
char * verData = new ( std : : nothrow ) char [ verSize ] ;
if ( verData & & GetFileVersionInfoW ( exePath , verHandle , verSize , verData ) )
{
UINT size = 0 ;
void * lpBuffer = nullptr ;
if ( VerQueryValue ( verData , _T ( " \\ " ) , & lpBuffer , & size ) & & size ! = 0 )
{
auto * verInfo = static_cast < const VS_FIXEDFILEINFO * > ( lpBuffer ) ;
if ( verInfo - > dwSignature = = 0xfeef04bd )
{
result = ( uint64 ( HIWORD ( verInfo - > dwFileVersionMS ) ) < < 48 )
| ( uint64 ( LOWORD ( verInfo - > dwFileVersionMS ) ) < < 32 )
| ( uint64 ( HIWORD ( verInfo - > dwFileVersionLS ) ) < < 16 )
| uint64 ( LOWORD ( verInfo - > dwFileVersionLS ) ) ;
}
}
}
delete [ ] verData ;
return result ;
}
// Create a plugin bridge object
AEffect * BridgeWrapper : : Create ( const VSTPluginLib & plugin , bool forceLegacy )
{
BridgeWrapper * wrapper = new ( std : : nothrow ) BridgeWrapper ( ) ;
BridgeWrapper * sharedInstance = nullptr ;
const Generation wantedGeneration = ( plugin . modernBridge & & ! forceLegacy ) ? Generation : : Modern : Generation : : Legacy ;
// Should we share instances?
if ( plugin . shareBridgeInstance )
{
// Well, then find some instance to share with!
CVstPlugin * vstPlug = dynamic_cast < CVstPlugin * > ( plugin . pPluginsList ) ;
while ( vstPlug ! = nullptr )
{
if ( vstPlug - > isBridged )
{
BridgeWrapper * instance = FromIntPtr < BridgeWrapper > ( vstPlug - > Dispatch ( effVendorSpecific , kVendorOpenMPT , kGetWrapperPointer , nullptr , 0.0f ) ) ;
if ( wantedGeneration = = instance - > m_Generation )
{
sharedInstance = instance ;
break ;
}
}
vstPlug = dynamic_cast < CVstPlugin * > ( vstPlug - > GetNextInstance ( ) ) ;
}
}
try
{
if ( wrapper ! = nullptr & & wrapper - > Init ( plugin . dllPath , wantedGeneration , sharedInstance ) & & wrapper - > m_queueMem . Good ( ) )
{
return & wrapper - > m_sharedMem - > effect ;
}
delete wrapper ;
return nullptr ;
} catch ( BridgeException & )
{
delete wrapper ;
throw ;
}
}
BridgeWrapper : : BridgeWrapper ( )
{
m_thisPluginID = static_cast < int32 > ( m_plugins . size ( ) ) ;
m_plugins . push_back ( this ) ;
if ( m_instanceCount = = 1 )
CreateCommunicationWindow ( WindowProc ) ;
}
BridgeWrapper : : ~ BridgeWrapper ( )
{
if ( m_instanceCount = = 1 )
DestroyWindow ( m_communicationWindow ) ;
}
// Initialize and launch bridge
bool BridgeWrapper : : Init ( const mpt : : PathString & pluginPath , Generation bridgeGeneration , BridgeWrapper * sharedInstace )
{
static uint32 plugId = 0 ;
plugId + + ;
const DWORD procId = GetCurrentProcessId ( ) ;
const std : : wstring mapName = MPT_WFORMAT ( " Local \\ openmpt-{}-{} " ) ( procId , plugId ) ;
// Create our shared memory object.
if ( ! m_queueMem . Create ( mapName . c_str ( ) , sizeof ( SharedMemLayout ) )
| | ! CreateSignals ( mapName . c_str ( ) ) )
{
throw BridgeException ( " Could not initialize plugin bridge memory. " ) ;
}
m_sharedMem = m_queueMem . Data < SharedMemLayout > ( ) ;
if ( sharedInstace = = nullptr )
{
// Create a new bridge instance
const PluginArch arch = GetPluginBinaryType ( pluginPath ) ;
bool available = false ;
ComponentPluginBridge : : Availability availability = ComponentPluginBridge : : AvailabilityUnknown ;
switch ( arch )
{
case PluginArch_x86 :
if ( available = ( bridgeGeneration = = Generation : : Modern ) ? IsComponentAvailable ( pluginBridge_x86 ) : IsComponentAvailable ( pluginBridgeLegacy_x86 ) ; ! available )
availability = ( bridgeGeneration = = Generation : : Modern ) ? pluginBridge_x86 - > GetAvailability ( ) : pluginBridgeLegacy_x86 - > GetAvailability ( ) ;
break ;
case PluginArch_amd64 :
if ( available = ( bridgeGeneration = = Generation : : Modern ) ? IsComponentAvailable ( pluginBridge_amd64 ) : IsComponentAvailable ( pluginBridgeLegacy_amd64 ) ; ! available )
availability = ( bridgeGeneration = = Generation : : Modern ) ? pluginBridge_amd64 - > GetAvailability ( ) : pluginBridgeLegacy_amd64 - > GetAvailability ( ) ;
break ;
# if defined(MPT_WITH_WINDOWS10)
case PluginArch_arm :
if ( available = ( bridgeGeneration = = Generation : : Modern ) ? IsComponentAvailable ( pluginBridge_arm ) : IsComponentAvailable ( pluginBridgeLegacy_arm ) ; ! available )
availability = ( bridgeGeneration = = Generation : : Modern ) ? pluginBridge_arm - > GetAvailability ( ) : pluginBridgeLegacy_arm - > GetAvailability ( ) ;
break ;
case PluginArch_arm64 :
if ( available = ( bridgeGeneration = = Generation : : Modern ) ? IsComponentAvailable ( pluginBridge_arm64 ) : IsComponentAvailable ( pluginBridgeLegacy_arm64 ) ; ! available )
availability = ( bridgeGeneration = = Generation : : Modern ) ? pluginBridge_arm64 - > GetAvailability ( ) : pluginBridgeLegacy_arm64 - > GetAvailability ( ) ;
break ;
# endif // MPT_WITH_WINDOWS10
default :
break ;
}
if ( arch = = PluginArch_unknown )
{
return false ;
}
if ( ! available )
{
switch ( availability )
{
case ComponentPluginBridge : : AvailabilityMissing :
// Silently fail if bridge is missing.
throw BridgeNotFoundException ( ) ;
break ;
case ComponentPluginBridge : : AvailabilityWrongVersion :
throw BridgeException ( " The plugin bridge version does not match your OpenMPT version. " ) ;
break ;
default :
throw BridgeNotFoundException ( ) ;
break ;
}
}
const ComponentPluginBridge * const pluginBridge =
( arch = = PluginArch_x86 & & bridgeGeneration = = Generation : : Modern ) ? static_cast < const ComponentPluginBridge * > ( pluginBridge_x86 . get ( ) ) :
( arch = = PluginArch_x86 & & bridgeGeneration = = Generation : : Legacy ) ? static_cast < const ComponentPluginBridge * > ( pluginBridgeLegacy_x86 . get ( ) ) :
( arch = = PluginArch_amd64 & & bridgeGeneration = = Generation : : Modern ) ? static_cast < const ComponentPluginBridge * > ( pluginBridge_amd64 . get ( ) ) :
( arch = = PluginArch_amd64 & & bridgeGeneration = = Generation : : Legacy ) ? static_cast < const ComponentPluginBridge * > ( pluginBridgeLegacy_amd64 . get ( ) ) :
# if defined(MPT_WITH_WINDOWS10)
( arch = = PluginArch_arm & & bridgeGeneration = = Generation : : Modern ) ? static_cast < const ComponentPluginBridge * > ( pluginBridge_arm . get ( ) ) :
( arch = = PluginArch_arm & & bridgeGeneration = = Generation : : Legacy ) ? static_cast < const ComponentPluginBridge * > ( pluginBridgeLegacy_arm . get ( ) ) :
( arch = = PluginArch_arm64 & & bridgeGeneration = = Generation : : Modern ) ? static_cast < const ComponentPluginBridge * > ( pluginBridge_arm64 . get ( ) ) :
( arch = = PluginArch_arm64 & & bridgeGeneration = = Generation : : Legacy ) ? static_cast < const ComponentPluginBridge * > ( pluginBridgeLegacy_arm64 . get ( ) ) :
# endif // MPT_WITH_WINDOWS10
nullptr ;
if ( ! pluginBridge )
{
return false ;
}
m_Generation = bridgeGeneration ;
const mpt : : PathString exeName = pluginBridge - > GetFileName ( ) ;
m_otherPtrSize = static_cast < int32 > ( GetPluginArchPointerSize ( arch ) ) ;
std : : wstring cmdLine = MPT_WFORMAT ( " {} {} " ) ( mapName , procId ) ;
STARTUPINFOW info ;
MemsetZero ( info ) ;
info . cb = sizeof ( info ) ;
PROCESS_INFORMATION processInfo ;
MemsetZero ( processInfo ) ;
if ( ! CreateProcessW ( exeName . ToWide ( ) . c_str ( ) , cmdLine . data ( ) , NULL , NULL , FALSE , 0 , NULL , NULL , & info , & processInfo ) )
{
throw BridgeException ( " Failed to launch plugin bridge. " ) ;
}
CloseHandle ( processInfo . hThread ) ;
m_otherProcess = processInfo . hProcess ;
} else
{
// Re-use existing bridge instance
m_otherPtrSize = sharedInstace - > m_otherPtrSize ;
m_otherProcess . DuplicateFrom ( sharedInstace - > m_otherProcess ) ;
BridgeMessage msg ;
msg . NewInstance ( mapName . c_str ( ) ) ;
if ( ! sharedInstace - > SendToBridge ( msg ) )
{
// Something went wrong, try a new instance
return Init ( pluginPath , bridgeGeneration , nullptr ) ;
}
}
// Initialize bridge
m_sharedMem - > effect . object = this ;
m_sharedMem - > effect . dispatcher = DispatchToPlugin ;
m_sharedMem - > effect . setParameter = SetParameter ;
m_sharedMem - > effect . getParameter = GetParameter ;
m_sharedMem - > effect . process = Process ;
std : : memcpy ( & ( m_sharedMem - > effect . reservedForHost2 ) , " OMPT " , 4 ) ;
m_sigAutomation . Create ( true ) ;
m_sharedMem - > hostCommWindow = m_communicationWindow ;
const HANDLE objects [ ] = { m_sigBridgeReady , m_otherProcess } ;
if ( WaitForMultipleObjects ( mpt : : saturate_cast < DWORD > ( std : : size ( objects ) ) , objects , FALSE , 10000 ) ! = WAIT_OBJECT_0 )
{
throw BridgeException ( " Could not connect to plugin bridge, it probably crashed. " ) ;
}
m_otherPluginID = m_sharedMem - > bridgePluginID ;
BridgeMessage initMsg ;
initMsg . Init ( pluginPath . ToWide ( ) . c_str ( ) , MIXBUFFERSIZE , m_thisPluginID , ExceptionHandler : : fullMemDump ) ;
if ( ! SendToBridge ( initMsg ) )
{
throw BridgeException ( " Could not initialize plugin bridge, it probably crashed. " ) ;
} else if ( initMsg . init . result ! = 1 )
{
throw BridgeException ( mpt : : ToCharset ( mpt : : Charset : : UTF8 , initMsg . init . str ) . c_str ( ) ) ;
}
if ( m_sharedMem - > effect . flags & effFlagsCanReplacing )
m_sharedMem - > effect . processReplacing = ProcessReplacing ;
if ( m_sharedMem - > effect . flags & effFlagsCanDoubleReplacing )
m_sharedMem - > effect . processDoubleReplacing = ProcessDoubleReplacing ;
return true ;
}
// Send an arbitrary message to the bridge.
// Returns true if the message was processed by the bridge.
bool BridgeWrapper : : SendToBridge ( BridgeMessage & sendMsg )
{
const bool inAudioThread = m_isAudioThread | | CMainFrame : : GetMainFrame ( ) - > InAudioThread ( ) ;
auto & messages = m_sharedMem - > ipcMessages ;
const auto msgID = CopyToSharedMemory ( sendMsg , messages ) ;
if ( msgID < 0 )
return false ;
BridgeMessage & sharedMsg = messages [ msgID ] ;
if ( ! inAudioThread )
{
if ( SendMessage ( m_sharedMem - > bridgeCommWindow , WM_BRIDGE_MESSAGE_TO_BRIDGE , m_otherPluginID , msgID ) = = WM_BRIDGE_SUCCESS )
{
sharedMsg . CopyTo ( sendMsg ) ;
return true ;
}
return false ;
}
// Audio thread: Use signals instead of window messages
m_sharedMem - > audioThreadToBridgeMsgID = msgID ;
m_sigToBridgeAudio . Send ( ) ;
// Wait until we get the result from the bridge
DWORD result ;
const HANDLE objects [ ] = { m_sigToBridgeAudio . confirm , m_sigToHostAudio . send , m_otherProcess } ;
do
{
result = WaitForMultipleObjects ( mpt : : saturate_cast < DWORD > ( std : : size ( objects ) ) , objects , FALSE , INFINITE ) ;
if ( result = = WAIT_OBJECT_0 )
{
// Message got answered
sharedMsg . CopyTo ( sendMsg ) ;
break ;
} else if ( result = = WAIT_OBJECT_0 + 1 )
{
ParseNextMessage ( m_sharedMem - > audioThreadToHostMsgID ) ;
m_sigToHostAudio . Confirm ( ) ;
}
} while ( result ! = WAIT_OBJECT_0 + 2 & & result ! = WAIT_FAILED ) ;
return ( result = = WAIT_OBJECT_0 ) ;
}
// Receive a message from the host and translate it.
void BridgeWrapper : : ParseNextMessage ( int msgID )
{
auto & msg = m_sharedMem - > ipcMessages [ msgID ] ;
switch ( msg . header . type )
{
case MsgHeader : : dispatch :
DispatchToHost ( msg . dispatch ) ;
break ;
case MsgHeader : : errorMsg :
// TODO Showing a message box here will deadlock as the main thread can be in a waiting state
//throw BridgeErrorException(msg.error.str);
break ;
}
}
void BridgeWrapper : : DispatchToHost ( DispatchMsg & msg )
{
// Various dispatch data - depending on the opcode, one of those might be used.
std : : vector < char > extraData ;
MappedMemory auxMem ;
// Content of ptr is usually stored right after the message header, ptr field indicates size.
void * ptr = ( msg . ptr ! = 0 ) ? ( & msg + 1 ) : nullptr ;
if ( msg . size > sizeof ( BridgeMessage ) )
{
if ( ! auxMem . Open ( static_cast < const wchar_t * > ( ptr ) ) )
{
return ;
}
ptr = auxMem . Data ( ) ;
}
void * origPtr = ptr ;
switch ( msg . opcode )
{
case audioMasterProcessEvents :
// VstEvents* in [ptr]
TranslateBridgeToVstEvents ( extraData , ptr ) ;
ptr = extraData . data ( ) ;
break ;
case audioMasterVendorSpecific :
if ( msg . index ! = kVendorOpenMPT | | msg . value ! = kUpdateProcessingBuffer )
{
break ;
}
[[fallthrough]] ;
case audioMasterIOChanged :
{
// If the song is playing, the rendering thread might be active at the moment,
// so we should keep the current processing memory alive until it is done for sure.
const CVstPlugin * plug = static_cast < CVstPlugin * > ( m_sharedMem - > effect . reservedForHost1 ) ;
const bool isPlaying = plug ! = nullptr & & plug - > IsResumed ( ) ;
if ( isPlaying )
{
m_oldProcessMem . CopyFrom ( m_processMem ) ;
}
// Set up new processing file
m_processMem . Open ( static_cast < wchar_t * > ( ptr ) ) ;
if ( isPlaying )
{
msg . result = 1 ;
return ;
}
}
break ;
case audioMasterUpdateDisplay :
m_cachedProgNames . clear ( ) ;
m_cachedParamInfo . clear ( ) ;
break ;
case audioMasterOpenFileSelector :
TranslateBridgeToVstFileSelect ( extraData , ptr , static_cast < size_t > ( msg . ptr ) ) ;
ptr = extraData . data ( ) ;
break ;
}
intptr_t result = CVstPlugin : : MasterCallBack ( & m_sharedMem - > effect , static_cast < VstOpcodeToHost > ( msg . opcode ) , msg . index , static_cast < intptr_t > ( msg . value ) , ptr , msg . opt ) ;
msg . result = static_cast < int32 > ( result ) ;
// Post-fix some opcodes
switch ( msg . opcode )
{
case audioMasterGetTime :
// VstTimeInfo* in [return value]
if ( msg . result ! = 0 )
{
m_sharedMem - > timeInfo = * FromIntPtr < VstTimeInfo > ( result ) ;
}
break ;
case audioMasterGetDirectory :
// char* in [return value]
if ( msg . result ! = 0 )
{
char * target = static_cast < char * > ( ptr ) ;
strncpy ( target , FromIntPtr < const char > ( result ) , static_cast < size_t > ( msg . ptr - 1 ) ) ;
target [ msg . ptr - 1 ] = 0 ;
}
break ;
case audioMasterOpenFileSelector :
if ( msg . result ! = 0 )
{
std : : vector < char > fileSelect ;
TranslateVstFileSelectToBridge ( fileSelect , * static_cast < const VstFileSelect * > ( ptr ) , m_otherPtrSize ) ;
std : : memcpy ( origPtr , fileSelect . data ( ) , std : : min ( fileSelect . size ( ) , static_cast < size_t > ( msg . ptr ) ) ) ;
// Directly free memory on host side, we don't need it anymore
CVstPlugin : : MasterCallBack ( & m_sharedMem - > effect , audioMasterCloseFileSelector , msg . index , static_cast < intptr_t > ( msg . value ) , ptr , msg . opt ) ;
}
break ;
}
}
intptr_t VSTCALLBACK BridgeWrapper : : DispatchToPlugin ( AEffect * effect , VstOpcodeToPlugin opcode , int32 index , intptr_t value , void * ptr , float opt )
{
BridgeWrapper * that = static_cast < BridgeWrapper * > ( effect - > object ) ;
if ( that ! = nullptr )
{
return that - > DispatchToPlugin ( opcode , index , value , ptr , opt ) ;
}
return 0 ;
}
intptr_t BridgeWrapper : : DispatchToPlugin ( VstOpcodeToPlugin opcode , int32 index , intptr_t value , void * ptr , float opt )
{
std : : vector < char > dispatchData ( sizeof ( DispatchMsg ) , 0 ) ;
int64 ptrOut = 0 ;
bool copyPtrBack = false , ptrIsSize = true ;
char * ptrC = static_cast < char * > ( ptr ) ;
switch ( opcode )
{
case effGetParamLabel :
case effGetParamDisplay :
case effGetParamName :
if ( index > = m_cachedParamInfoStart & & index < m_cachedParamInfoStart + mpt : : saturate_cast < int32 > ( m_cachedParamInfo . size ( ) ) )
{
if ( opcode = = effGetParamLabel )
strcpy ( ptrC , m_cachedParamInfo [ index - m_cachedParamInfoStart ] . label ) ;
else if ( opcode = = effGetParamDisplay )
strcpy ( ptrC , m_cachedParamInfo [ index - m_cachedParamInfoStart ] . display ) ;
else if ( opcode = = effGetParamName )
strcpy ( ptrC , m_cachedParamInfo [ index - m_cachedParamInfoStart ] . name ) ;
return 1 ;
}
[[fallthrough]] ;
case effGetProgramName :
case effString2Parameter :
case effGetProgramNameIndexed :
case effGetEffectName :
case effGetErrorText :
case effGetVendorString :
case effGetProductString :
case effShellGetNextPlugin :
// Name in [ptr]
if ( opcode = = effGetProgramNameIndexed & & ! m_cachedProgNames . empty ( ) )
{
// First check if we have cached this program name
if ( index > = m_cachedProgNameStart & & index < m_cachedProgNameStart + mpt : : saturate_cast < int32 > ( m_cachedProgNames . size ( ) / kCachedProgramNameLength ) )
{
strcpy ( ptrC , & m_cachedProgNames [ ( index - m_cachedProgNameStart ) * kCachedProgramNameLength ] ) ;
return 1 ;
}
}
ptrOut = 256 ;
copyPtrBack = true ;
break ;
case effSetProgramName :
m_cachedProgNames . clear ( ) ;
[[fallthrough]] ;
case effCanDo :
// char* in [ptr]
ptrOut = strlen ( ptrC ) + 1 ;
dispatchData . insert ( dispatchData . end ( ) , ptrC , ptrC + ptrOut ) ;
break ;
case effIdle :
// The plugin bridge will generate these messages by itself
return 0 ;
case effEditGetRect :
// ERect** in [ptr]
ptrOut = sizeof ( ERect ) ;
copyPtrBack = true ;
break ;
case effEditOpen :
2024-09-29 02:04:03 +00:00
// HWND in [ptr] - Note: Window handles are interoperable between 32-bit and 64-bit applications in Windows (http://msdn.microsoft.com/en-us/library/arch/desktop/aa384203%28v=vs.85%29.aspx)
2024-09-24 12:54:57 +00:00
ptrOut = reinterpret_cast < int64 > ( ptr ) ;
ptrIsSize = false ;
m_cachedProgNames . clear ( ) ;
m_cachedParamInfo . clear ( ) ;
break ;
case effEditIdle :
// The plugin bridge will generate these messages by itself
return 0 ;
case effGetChunk :
// void** in [ptr] for chunk data address
{
static uint32 chunkId = 0 ;
const std : : wstring mapName = L " Local \\ openmpt- " + mpt : : wfmt : : val ( GetCurrentProcessId ( ) ) + L " -chunkdata- " + mpt : : wfmt : : val ( chunkId + + ) ;
ptrOut = ( mapName . length ( ) + 1 ) * sizeof ( wchar_t ) ;
PushToVector ( dispatchData , * mapName . c_str ( ) , static_cast < size_t > ( ptrOut ) ) ;
}
break ;
case effSetChunk :
// void* in [ptr] for chunk data
ptrOut = value ;
dispatchData . insert ( dispatchData . end ( ) , ptrC , ptrC + value ) ;
m_cachedProgNames . clear ( ) ;
m_cachedParamInfo . clear ( ) ;
break ;
case effProcessEvents :
// VstEvents* in [ptr]
// We process in a separate memory segment to save a bridge communication message.
{
std : : vector < char > events ;
TranslateVstEventsToBridge ( events , * static_cast < VstEvents * > ( ptr ) , m_otherPtrSize ) ;
if ( m_eventMem . Size ( ) < events . size ( ) )
{
// Resize memory
static uint32 chunkId = 0 ;
const std : : wstring mapName = L " Local \\ openmpt- " + mpt : : wfmt : : val ( GetCurrentProcessId ( ) ) + L " -events- " + mpt : : wfmt : : val ( chunkId + + ) ;
ptrOut = ( mapName . length ( ) + 1 ) * sizeof ( wchar_t ) ;
PushToVector ( dispatchData , * mapName . c_str ( ) , static_cast < size_t > ( ptrOut ) ) ;
m_eventMem . Create ( mapName . c_str ( ) , static_cast < uint32 > ( events . size ( ) + 1024 ) ) ;
opcode = effVendorSpecific ;
index = kVendorOpenMPT ;
value = kUpdateEventMemName ;
}
std : : memcpy ( m_eventMem . Data ( ) , events . data ( ) , events . size ( ) ) ;
}
if ( opcode ! = effVendorSpecific )
{
return 1 ;
}
break ;
case effGetInputProperties :
case effGetOutputProperties :
// VstPinProperties* in [ptr]
ptrOut = sizeof ( VstPinProperties ) ;
copyPtrBack = true ;
break ;
case effOfflineNotify :
// VstAudioFile* in [ptr]
ptrOut = sizeof ( VstAudioFile ) * value ;
// TODO
return 0 ;
break ;
case effOfflinePrepare :
case effOfflineRun :
// VstOfflineTask* in [ptr]
ptrOut = sizeof ( VstOfflineTask ) * value ;
// TODO
return 0 ;
break ;
case effProcessVarIo :
// VstVariableIo* in [ptr]
ptrOut = sizeof ( VstVariableIo ) ;
// TODO
return 0 ;
break ;
case effSetSpeakerArrangement :
// VstSpeakerArrangement* in [value] and [ptr]
ptrOut = sizeof ( VstSpeakerArrangement ) * 2 ;
PushToVector ( dispatchData , * static_cast < VstSpeakerArrangement * > ( ptr ) ) ;
PushToVector ( dispatchData , * FromIntPtr < VstSpeakerArrangement > ( value ) ) ;
break ;
case effVendorSpecific :
if ( index = = kVendorOpenMPT )
{
switch ( value )
{
case kGetWrapperPointer :
return ToIntPtr < BridgeWrapper > ( this ) ;
case kCloseOldProcessingMemory :
{
intptr_t result = m_oldProcessMem . Good ( ) ;
m_oldProcessMem . Close ( ) ;
return result ;
}
case kCacheProgramNames :
{
int32 * prog = static_cast < int32 * > ( ptr ) ;
m_cachedProgNameStart = prog [ 0 ] ;
ptrOut = std : : max ( static_cast < int64 > ( sizeof ( int32 ) * 2 ) , static_cast < int64 > ( ( prog [ 1 ] - prog [ 0 ] ) * kCachedProgramNameLength ) ) ;
dispatchData . insert ( dispatchData . end ( ) , ptrC , ptrC + 2 * sizeof ( int32 ) ) ;
}
break ;
case kCacheParameterInfo :
{
int32 * param = static_cast < int32 * > ( ptr ) ;
m_cachedParamInfoStart = param [ 0 ] ;
ptrOut = std : : max ( static_cast < int64 > ( sizeof ( int32 ) * 2 ) , static_cast < int64 > ( ( param [ 1 ] - param [ 0 ] ) * sizeof ( ParameterInfo ) ) ) ;
dispatchData . insert ( dispatchData . end ( ) , ptrC , ptrC + 2 * sizeof ( int32 ) ) ;
}
break ;
case kBeginGetProgram :
ptrOut = m_sharedMem - > effect . numParams * sizeof ( float ) ;
break ;
case kEndGetProgram :
m_cachedParamValues . clear ( ) ;
return 1 ;
}
}
break ;
case effGetTailSize :
return m_sharedMem - > tailSize ;
case effGetParameterProperties :
// VstParameterProperties* in [ptr]
if ( index > = m_cachedParamInfoStart & & index < m_cachedParamInfoStart + mpt : : saturate_cast < int32 > ( m_cachedParamInfo . size ( ) ) )
{
* static_cast < VstParameterProperties * > ( ptr ) = m_cachedParamInfo [ index - m_cachedParamInfoStart ] . props ;
return 1 ;
}
ptrOut = sizeof ( VstParameterProperties ) ;
copyPtrBack = true ;
break ;
case effGetMidiProgramName :
case effGetCurrentMidiProgram :
// MidiProgramName* in [ptr]
ptrOut = sizeof ( MidiProgramName ) ;
copyPtrBack = true ;
break ;
case effGetMidiProgramCategory :
// MidiProgramCategory* in [ptr]
ptrOut = sizeof ( MidiProgramCategory ) ;
copyPtrBack = true ;
break ;
case effGetMidiKeyName :
// MidiKeyName* in [ptr]
ptrOut = sizeof ( MidiKeyName ) ;
copyPtrBack = true ;
break ;
case effBeginSetProgram :
m_isSettingProgram = true ;
break ;
case effEndSetProgram :
m_isSettingProgram = false ;
if ( m_sharedMem - > automationQueue . pendingEvents )
{
SendAutomationQueue ( ) ;
}
m_cachedProgNames . clear ( ) ;
m_cachedParamInfo . clear ( ) ;
break ;
case effGetSpeakerArrangement :
// VstSpeakerArrangement* in [value] and [ptr]
ptrOut = sizeof ( VstSpeakerArrangement ) * 2 ;
copyPtrBack = true ;
break ;
case effBeginLoadBank :
case effBeginLoadProgram :
// VstPatchChunkInfo* in [ptr]
ptrOut = sizeof ( VstPatchChunkInfo ) ;
m_cachedProgNames . clear ( ) ;
m_cachedParamInfo . clear ( ) ;
break ;
default :
MPT_ASSERT ( ptr = = nullptr ) ;
}
if ( ptrOut ! = 0 & & ptrIsSize )
{
// In case we only reserve space and don't copy stuff over...
dispatchData . resize ( sizeof ( DispatchMsg ) + static_cast < size_t > ( ptrOut ) , 0 ) ;
}
uint32 extraSize = static_cast < uint32 > ( dispatchData . size ( ) - sizeof ( DispatchMsg ) ) ;
// Create message header
BridgeMessage & msg = * reinterpret_cast < BridgeMessage * > ( dispatchData . data ( ) ) ;
msg . Dispatch ( opcode , index , value , ptrOut , opt , extraSize ) ;
const bool useAuxMem = dispatchData . size ( ) > sizeof ( BridgeMessage ) ;
AuxMem * auxMem = nullptr ;
if ( useAuxMem )
{
// Extra data doesn't fit in message - use secondary memory
if ( dispatchData . size ( ) > std : : numeric_limits < uint32 > : : max ( ) )
return 0 ;
auxMem = GetAuxMemory ( mpt : : saturate_cast < uint32 > ( dispatchData . size ( ) ) ) ;
if ( auxMem = = nullptr )
return 0 ;
// First, move message data to shared memory...
std : : memcpy ( auxMem - > memory . Data ( ) , & dispatchData [ sizeof ( DispatchMsg ) ] , extraSize ) ;
// ...Now put the shared memory name in the message instead.
std : : memcpy ( & dispatchData [ sizeof ( DispatchMsg ) ] , auxMem - > name , sizeof ( auxMem - > name ) ) ;
}
try
{
if ( ! SendToBridge ( msg ) & & opcode ! = effClose )
{
return 0 ;
}
} catch ( . . . )
{
// Don't do anything for now.
#if 0
if ( opcode ! = effClose )
{
throw ;
}
# endif
}
const DispatchMsg & resultMsg = msg . dispatch ;
// cppcheck false-positive
// cppcheck-suppress nullPointerRedundantCheck
const void * extraData = useAuxMem ? auxMem - > memory . Data < const char > ( ) : reinterpret_cast < const char * > ( & resultMsg + 1 ) ;
// Post-fix some opcodes
switch ( opcode )
{
case effClose :
m_sharedMem - > effect . object = nullptr ;
delete this ;
return 0 ;
case effGetProgramName :
case effGetParamLabel :
case effGetParamDisplay :
case effGetParamName :
case effString2Parameter :
case effGetProgramNameIndexed :
case effGetEffectName :
case effGetErrorText :
case effGetVendorString :
case effGetProductString :
case effShellGetNextPlugin :
// Name in [ptr]
strcpy ( ptrC , static_cast < const char * > ( extraData ) ) ;
break ;
case effEditGetRect :
// ERect** in [ptr]
m_editRect = * static_cast < const ERect * > ( extraData ) ;
* static_cast < const ERect * * > ( ptr ) = & m_editRect ;
break ;
case effGetChunk :
// void** in [ptr] for chunk data address
if ( const wchar_t * str = static_cast < const wchar_t * > ( extraData ) ; m_getChunkMem . Open ( str ) )
* static_cast < void * * > ( ptr ) = m_getChunkMem . Data ( ) ;
else
return 0 ;
break ;
case effVendorSpecific :
if ( index = = kVendorOpenMPT & & resultMsg . result = = 1 )
{
switch ( value )
{
case kCacheProgramNames :
m_cachedProgNames . assign ( static_cast < const char * > ( extraData ) , static_cast < const char * > ( extraData ) + ptrOut ) ;
break ;
case kCacheParameterInfo :
{
const ParameterInfo * params = static_cast < const ParameterInfo * > ( extraData ) ;
m_cachedParamInfo . assign ( params , params + ptrOut / sizeof ( ParameterInfo ) ) ;
break ;
}
case kBeginGetProgram :
m_cachedParamValues . assign ( static_cast < const float * > ( extraData ) , static_cast < const float * > ( extraData ) + ptrOut / sizeof ( float ) ) ;
break ;
}
}
break ;
case effGetSpeakerArrangement :
// VstSpeakerArrangement* in [value] and [ptr]
m_speakers [ 0 ] = * static_cast < const VstSpeakerArrangement * > ( extraData ) ;
m_speakers [ 1 ] = * ( static_cast < const VstSpeakerArrangement * > ( extraData ) + 1 ) ;
* static_cast < VstSpeakerArrangement * > ( ptr ) = m_speakers [ 0 ] ;
* FromIntPtr < VstSpeakerArrangement > ( value ) = m_speakers [ 1 ] ;
break ;
default :
// TODO: Translate VstVariableIo, offline tasks
if ( copyPtrBack )
{
std : : memcpy ( ptr , extraData , static_cast < size_t > ( ptrOut ) ) ;
}
}
if ( auxMem ! = nullptr )
{
auxMem - > used = false ;
}
return static_cast < intptr_t > ( resultMsg . result ) ;
}
// Allocate auxiliary shared memory for too long bridge messages
BridgeWrapper : : AuxMem * BridgeWrapper : : GetAuxMemory ( uint32 size )
{
std : : size_t index = std : : size ( m_auxMems ) ;
for ( int pass = 0 ; pass < 2 ; pass + + )
{
for ( std : : size_t i = 0 ; i < std : : size ( m_auxMems ) ; i + + )
{
if ( m_auxMems [ i ] . size > = size | | pass = = 1 )
{
// Good candidate - is it taken yet?
bool expected = false ;
if ( m_auxMems [ i ] . used . compare_exchange_strong ( expected , true ) )
{
index = i ;
break ;
}
}
}
if ( index ! = std : : size ( m_auxMems ) )
break ;
}
if ( index = = std : : size ( m_auxMems ) )
return nullptr ;
AuxMem & auxMem = m_auxMems [ index ] ;
if ( auxMem . size > = size & & auxMem . memory . Good ( ) )
{
// Re-use as-is
return & auxMem ;
}
// Create new memory with appropriate size
static_assert ( sizeof ( DispatchMsg ) + sizeof ( auxMem . name ) < = sizeof ( BridgeMessage ) , " Check message sizes, this will crash! " ) ;
static unsigned int auxMemCount = 0 ;
mpt : : String : : WriteAutoBuf ( auxMem . name ) = MPT_WFORMAT ( " Local \\ openmpt-{}-auxmem-{} " ) ( GetCurrentProcessId ( ) , auxMemCount + + ) ;
if ( auxMem . memory . Create ( auxMem . name , size ) )
{
auxMem . size = size ;
return & auxMem ;
} else
{
auxMem . used = false ;
return nullptr ;
}
}
// Send any pending automation events
void BridgeWrapper : : SendAutomationQueue ( )
{
m_sigAutomation . Reset ( ) ;
BridgeMessage msg ;
msg . Automate ( ) ;
if ( ! SendToBridge ( msg ) )
{
// Failed (plugin probably crashed) - auto-fix event count
m_sharedMem - > automationQueue . pendingEvents = 0 ;
}
m_sigAutomation . Trigger ( ) ;
}
void VSTCALLBACK BridgeWrapper : : SetParameter ( AEffect * effect , int32 index , float parameter )
{
BridgeWrapper * that = static_cast < BridgeWrapper * > ( effect - > object ) ;
if ( that )
{
try
{
that - > SetParameter ( index , parameter ) ;
} catch ( . . . )
{
// Be quiet about exceptions here
}
}
}
void BridgeWrapper : : SetParameter ( int32 index , float parameter )
{
const CVstPlugin * plug = static_cast < CVstPlugin * > ( m_sharedMem - > effect . reservedForHost1 ) ;
AutomationQueue & autoQueue = m_sharedMem - > automationQueue ;
if ( m_isSettingProgram | | ( plug & & plug - > IsResumed ( ) ) )
{
// Queue up messages while rendering to reduce latency introduced by every single bridge call
uint32 i ;
while ( ( i = autoQueue . pendingEvents . fetch_add ( 1 ) ) > = std : : size ( autoQueue . params ) )
{
// Queue full!
if ( i = = std : : size ( autoQueue . params ) )
{
// We're the first to notice that it's full
SendAutomationQueue ( ) ;
} else
{
// Wait until queue is emptied by someone else (this branch is very unlikely to happen)
WaitForSingleObject ( m_sigAutomation , INFINITE ) ;
}
}
autoQueue . params [ i ] . index = index ;
autoQueue . params [ i ] . value = parameter ;
return ;
} else if ( autoQueue . pendingEvents )
{
// Actually, this should never happen as pending events are cleared before processing and at the end of a set program event.
SendAutomationQueue ( ) ;
}
BridgeMessage msg ;
msg . SetParameter ( index , parameter ) ;
SendToBridge ( msg ) ;
}
float VSTCALLBACK BridgeWrapper : : GetParameter ( AEffect * effect , int32 index )
{
BridgeWrapper * that = static_cast < BridgeWrapper * > ( effect - > object ) ;
if ( that )
{
if ( static_cast < size_t > ( index ) < that - > m_cachedParamValues . size ( ) )
return that - > m_cachedParamValues [ index ] ;
try
{
return that - > GetParameter ( index ) ;
} catch ( . . . )
{
// Be quiet about exceptions here
}
}
return 0.0f ;
}
float BridgeWrapper : : GetParameter ( int32 index )
{
BridgeMessage msg ;
msg . GetParameter ( index ) ;
if ( SendToBridge ( msg ) )
{
return msg . parameter . value ;
}
return 0.0f ;
}
void VSTCALLBACK BridgeWrapper : : Process ( AEffect * effect , float * * inputs , float * * outputs , int32 sampleFrames )
{
BridgeWrapper * that = static_cast < BridgeWrapper * > ( effect - > object ) ;
if ( sampleFrames ! = 0 & & that ! = nullptr )
{
that - > BuildProcessBuffer ( ProcessMsg : : process , effect - > numInputs , effect - > numOutputs , inputs , outputs , sampleFrames ) ;
}
}
void VSTCALLBACK BridgeWrapper : : ProcessReplacing ( AEffect * effect , float * * inputs , float * * outputs , int32 sampleFrames )
{
BridgeWrapper * that = static_cast < BridgeWrapper * > ( effect - > object ) ;
if ( sampleFrames ! = 0 & & that ! = nullptr )
{
that - > BuildProcessBuffer ( ProcessMsg : : processReplacing , effect - > numInputs , effect - > numOutputs , inputs , outputs , sampleFrames ) ;
}
}
void VSTCALLBACK BridgeWrapper : : ProcessDoubleReplacing ( AEffect * effect , double * * inputs , double * * outputs , int32 sampleFrames )
{
BridgeWrapper * that = static_cast < BridgeWrapper * > ( effect - > object ) ;
if ( sampleFrames ! = 0 & & that ! = nullptr )
{
that - > BuildProcessBuffer ( ProcessMsg : : processDoubleReplacing , effect - > numInputs , effect - > numOutputs , inputs , outputs , sampleFrames ) ;
}
}
template < typename buf_t >
void BridgeWrapper : : BuildProcessBuffer ( ProcessMsg : : ProcessType type , int32 numInputs , int32 numOutputs , buf_t * * inputs , buf_t * * outputs , int32 sampleFrames )
{
if ( ! m_processMem . Good ( ) )
{
MPT_ASSERT_NOTREACHED ( ) ;
return ;
}
ProcessMsg * processMsg = m_processMem . Data < ProcessMsg > ( ) ;
new ( processMsg ) ProcessMsg { type , numInputs , numOutputs , sampleFrames } ;
// Anticipate that many plugins will query the play position in a process call and send it along the process call
// to save some valuable inter-process calls.
m_sharedMem - > timeInfo = * FromIntPtr < VstTimeInfo > ( CVstPlugin : : MasterCallBack ( & m_sharedMem - > effect , audioMasterGetTime , 0 , kVstNanosValid | kVstPpqPosValid | kVstTempoValid | kVstBarsValid | kVstCyclePosValid | kVstTimeSigValid | kVstSmpteValid | kVstClockValid , nullptr , 0.0f ) ) ;
buf_t * ptr = reinterpret_cast < buf_t * > ( processMsg + 1 ) ;
for ( int32 i = 0 ; i < numInputs ; i + + )
{
std : : memcpy ( ptr , inputs [ i ] , sampleFrames * sizeof ( buf_t ) ) ;
ptr + = sampleFrames ;
}
// Theoretically, we should memcpy() instead of memset() here in process(), but OpenMPT always clears the output buffer before processing so it doesn't matter.
memset ( ptr , 0 , numOutputs * sampleFrames * sizeof ( buf_t ) ) ;
// In case we called Process() from the GUI thread (panic button or song stop => CSoundFile::SuspendPlugins)
m_isAudioThread = true ;
m_sigProcessAudio . Send ( ) ;
const HANDLE objects [ ] = { m_sigProcessAudio . confirm , m_sigToHostAudio . send , m_otherProcess } ;
DWORD result ;
do
{
result = WaitForMultipleObjects ( mpt : : saturate_cast < DWORD > ( std : : size ( objects ) ) , objects , FALSE , INFINITE ) ;
if ( result = = WAIT_OBJECT_0 + 1 )
{
ParseNextMessage ( m_sharedMem - > audioThreadToHostMsgID ) ;
m_sigToHostAudio . Confirm ( ) ;
}
} while ( result ! = WAIT_OBJECT_0 & & result ! = WAIT_OBJECT_0 + 2 & & result ! = WAIT_FAILED ) ;
m_isAudioThread = false ;
for ( int32 i = 0 ; i < numOutputs ; i + + )
{
//std::memcpy(outputs[i], ptr, sampleFrames * sizeof(buf_t));
outputs [ i ] = ptr ; // Exactly what you don't want plugins to do usually (bend your output pointers)... muahahaha!
ptr + = sampleFrames ;
}
// Did we receive any audioMasterProcessEvents data?
if ( auto * events = m_eventMem . Data < int32 > ( ) ; events ! = nullptr & & * events ! = 0 )
{
std : : vector < char > eventCache ;
TranslateBridgeToVstEvents ( eventCache , events ) ;
* events = 0 ;
CVstPlugin : : MasterCallBack ( & m_sharedMem - > effect , audioMasterProcessEvents , 0 , 0 , eventCache . data ( ) , 0.0f ) ;
}
}
LRESULT CALLBACK BridgeWrapper : : WindowProc ( HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
if ( hwnd = = m_communicationWindow & & wParam < m_plugins . size ( ) )
{
auto * that = static_cast < BridgeWrapper * > ( m_plugins [ wParam ] ) ;
if ( that ! = nullptr & & uMsg = = WM_BRIDGE_MESSAGE_TO_HOST )
{
that - > ParseNextMessage ( static_cast < int > ( lParam ) ) ;
return WM_BRIDGE_SUCCESS ;
}
}
return DefWindowProc ( hwnd , uMsg , wParam , lParam ) ;
}
# endif // MPT_WITH_VST
OPENMPT_NAMESPACE_END