pdvst-0.52/vst-template/pdvst.cpp

1213 lines
38 KiB
C++

/* PdVst v0.0.2 - VST - Pd bridging plugin
** Copyright (C) 2004 Joseph A. Sarlo
**
** This program is free software; you can redistribute it and/orsig
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
** jsarlo@ucsd.edu
*/
#define _WIN32_WINDOWS 0x410
#include "pdvst.hpp"
#include <malloc.h>
#include <process.h>
#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fstream>
#include <unistd.h>
#ifdef _MSC_VER
#define stat _stat
#endif
extern HINSTANCE hInstance;
extern bool globalDebug;
extern int globalNChannels;
extern int globalNPrograms;
extern int globalNParams;
extern long globalPluginId;
extern int globalNExternalLibs;
extern char globalExternalLib[MAXEXTERNS][MAXSTRLEN];
extern char globalVstParamName[MAXPARAMETERS][MAXSTRLEN];
extern char globalPluginPath[MAXFILENAMELEN];
extern char globalVstPluginPath[MAXFILENAMELEN]; // chemin d'accès dans le dossier vst contenant la dll)
extern char globalPluginName[MAXSTRLEN];
extern char globalPdFile[MAXFILENAMELEN];
extern char globalPureDataPath[MAXFILENAMELEN];
extern char globalHostPdvstPath[MAXFILENAMELEN];
extern bool globalCustomGui;
extern int globalCustomGuiWidth;
extern int globalCustomGuiHeight;
extern bool globalProgramsAreChunks;
extern bool globalIsASynth;
extern pdvstProgram globalProgram[MAXPROGRAMS];
int pdvst::referenceCount = 0;
/* change '/' characters to the system's native file separator */
void sys_bashfilename(const char *from, char *to)
{
char c;
while ((c = *from++))
{
#ifdef _WIN32
if (c == '/') c = '\\';
#endif
*to++ = c;
}
*to = 0;
}
/* change the system's native file separator to '/' characters */
void sys_unbashfilename(const char *from, char *to)
{
char c;
while ((c = *from++))
{
#ifdef _WIN32
if (c == '\\') c = '/';
#endif
*to++ = c;
}
*to = 0;
}
/*
bool fexists(const char *filename)
{
std::ifstream ifile(filename);
return ifile;
}
*/
//-------------------------------------------------------------------------------------------------------
AudioEffect* createEffectInstance (audioMasterCallback audioMaster)
{
return new pdvst (audioMaster);
}
pdvst::pdvst(audioMasterCallback audioMaster)
:AudioEffectX(audioMaster, globalNPrograms, globalNParams)
{
// set debug output
debugFile = fopen("pdvstdebug.txt", "wt");
// debugFile = NULL;
// copy global data
isASynth = globalIsASynth;
customGui = globalCustomGui;
customGuiHeight = globalCustomGuiHeight;
customGuiWidth = globalCustomGuiWidth;
nChannels = globalNChannels;
nPrograms = globalNPrograms;
nParameters = globalNParams;
pluginId = globalPluginId;
nExternalLibs = globalNExternalLibs;
debugLog("name: %s", globalPluginName);
debugLog("synth: %d", globalIsASynth);
// VST setup
setNumInputs(nChannels);
setNumOutputs(nChannels);
setUniqueID(pluginId);
setInitialDelay(PDBLKSIZE * 2);
canProcessReplacing();
if (isASynth)
{
isSynth();
}
int i, j;
// initialize memory
vstParamName = new char*[MAXPARAMETERS];
for (i = 0; i < MAXPARAMETERS; i++)
vstParamName[i] = new char[MAXSTRLEN];
memset(vstParam, 0, MAXPARAMETERS * sizeof(float));
program = new pdvstProgram[MAXPROGRAMS];
for (i = 0; i < nExternalLibs; i++)
{
strcpy(externalLib[i], globalExternalLib[i]);
}
debugLog("channels: %d", nChannels);
audioBuffer = new pdVstBuffer(nChannels);
for (i = 0; i < MAXPARAMETERS; i++)
{
strcpy(vstParamName[i], globalVstParamName[i]);
}
strcpy(pluginPath, globalPluginPath);
strcpy(vstPluginPath, globalVstPluginPath);
strcpy(pluginName, globalPluginName);
strcpy(pdFile, globalPdFile);
debugLog("path: %s", pluginPath);
debugLog("nParameters = %d", nParameters);
debugLog("nPrograms = %d", nPrograms);
for (i = 0; i < nPrograms; i++)
{
strcpy(program[i].name, globalProgram[i].name);
for (j = 0; j < nParameters; j++)
{
program[i].paramValue[j] = globalProgram[i].paramValue[j];
}
debugLog(" %s", program[i].name);
}
// create editor
if (customGui)
{
editor = new pdvstEditor(this);
debugLog(" -has custom GUI-");
}
guiNameUpdated=false;
#ifdef VSTMIDIOUTENABLE
// initializing structures for midiout support
nbFramesWithoutMidiOutEvent=0;
evnts = (struct VstEvents*)malloc(sizeof(struct VstEvents) +
MAXMIDIOUTQUEUESIZE*sizeof(VstEvent*));
//initialisation du tableau midiEvnts[MAXMIDIOUTQUEUESIZE];
for (int k=0; k<MAXMIDIOUTQUEUESIZE;k++)
{
// Constants to all midi messages we send (NoteOn and NoteOff)
midiEvnts[k].type = kVstMidiType;
midiEvnts[k].byteSize = sizeof(VstMidiEvent);
midiEvnts[k].deltaFrames = 0;
midiEvnts[k].flags |= kVstMidiEventIsRealtime;
midiEvnts[k].detune = 0;
midiEvnts[k].noteOffVelocity = (char)0;
midiEvnts[k].reserved1 = 0;
midiEvnts[k].reserved2 = 0;
midiEvnts[k].midiData[3] = 0;
midiEvnts[k].noteOffset = 0;
//cast
evnts->events[k] = (VstEvent*)&midiEvnts[k];
}
#endif //VSTMIDIOUTENABLE
//timeBeginPeriod(1);
debugLog("startingPd...");
// start pd.exe
startPd();
debugLog("done");
//setProgram(curProgram);
referenceCount++;
// {JYG see pdvst::setProgram below for explanation
timeFromStartup=GetTickCount();
// JYG }
programsAreChunks(globalProgramsAreChunks);
sendPlugName(globalPluginName);
}
pdvst::~pdvst()
{
int i;
referenceCount--;
pdvstData->active = 0;
CloseHandle(pdvstTransferMutex);
UnmapViewOfFile(pdvstTransferFileMap);
CloseHandle(pdvstTransferFileMap);
for (i = 0; i < MAXPARAMETERS; i++)
delete vstParamName[i];
delete vstParamName;
delete program;
delete audioBuffer;
if (debugFile)
{
fclose(debugFile);
}
}
void pdvst::debugLog(char *fmt, ...)
{
va_list ap;
if (debugFile)
{
va_start(ap, fmt);
vfprintf(debugFile, fmt, ap);
va_end(ap);
putc('\n', debugFile);
fflush(debugFile);
}
}
void pdvst::startPd()
{
int i;
STARTUPINFO si;
PROCESS_INFORMATION pi;
char commandLineArgs[MAXSTRLEN],
debugString[MAXSTRLEN],pathToCheck[MAXSTRLEN];
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
sprintf(pdvstTransferMutexName, "mutex%d%x", GetCurrentProcessId(), this);
sprintf(pdvstTransferFileMapName, "filemap%d%x", GetCurrentProcessId(), this);
sprintf(vstProcEventName, "vstprocevent%d%x", GetCurrentProcessId(), this);
sprintf(pdProcEventName, "pdprocevent%d%x", GetCurrentProcessId(), this);
pdvstTransferMutex = CreateMutex(NULL, 0, pdvstTransferMutexName);
vstProcEvent = CreateEvent(NULL, TRUE, TRUE, vstProcEventName);
pdProcEvent = CreateEvent(NULL, TRUE, FALSE, pdProcEventName);
pdvstTransferFileMap = CreateFileMapping(INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
sizeof(pdvstTransferData),
pdvstTransferFileMapName);
pdvstData = (pdvstTransferData *)MapViewOfFile(pdvstTransferFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
sizeof(pdvstTransferData));
pdvstData->active = 1;
pdvstData->blockSize = PDBLKSIZE;
pdvstData->nChannels = nChannels;
pdvstData->sampleRate = (int)getSampleRate();
pdvstData->nParameters = nParameters;
pdvstData->guiState.updated = 0;
pdvstData->guiState.type = FLOAT_TYPE;
pdvstData->guiState.direction = PD_RECEIVE;
for (i = 0; i < MAXPARAMETERS; i++)
{
pdvstData->vstParameters[i].direction = PD_RECEIVE;
pdvstData->vstParameters[i].updated = 0;
}
if (globalDebug)
{
strcpy(debugString, "");
}
else
{
strcpy(debugString, " -nogui");
}
if (strcmp(globalPureDataPath, "") == 0) // unspecified puredata path. Defaults to globalHostPdvstPath
{
sprintf(commandLineArgs, "%s\\pd\\bin\\pd.exe", globalHostPdvstPath);
if( access( commandLineArgs, F_OK ) != -1 ) // check if file exists
sprintf(commandLineArgs, "\"%s\\pd\\bin\\pd.exe\"", globalHostPdvstPath);
else
{
sprintf(commandLineArgs, "%s..\\.pd\\bin\\pd.exe", globalPluginPath);
//MessageBox(NULL,commandLineArgs,"commandLineArgs...",MB_OK);
if( access( commandLineArgs, F_OK ) != -1 ) // check if file exists
sprintf(commandLineArgs, "\"%s..\\.pd\\bin\\pd.exe\"", globalPluginPath);
else
{
sprintf(errorMessage,"pd.exe program not found in \n1: %spd\\bin\\\n2: %s..\\.pd\\bin\\\nCheck your settings in \n%s%s.pdv",
globalHostPdvstPath,globalPluginPath,globalPluginPath,globalPluginName);
MessageBox(NULL,errorMessage,"ERROR",MB_OK);
return;
}
}
}
else
{
sprintf(commandLineArgs, "%s\\bin\\pd.exe", globalPureDataPath);
if ( access( commandLineArgs, F_OK ) != -1 ) // file exists
sprintf(commandLineArgs,"\"%s\\bin\\pd.exe\"", globalPureDataPath);
else
{
sprintf(errorMessage,"pd.exe program not found. Check your settings in \n%s%s.pdv \n setup file. (search for the line PDPATH)",
globalPluginPath,globalPluginName);
MessageBox(NULL,errorMessage,"ERROR",MB_OK);
return;
}
}
// if (!fexists(commandLineArgs))
// sprintf(commandLineArgs, "\"%s\\pd\\bin\\pd.exe\"", pluginPath);
// check validity of pd.exe path
/*
struct stat statbuf;
sys_bashfilename(commandLineArgs, pathToCheck);
if (stat(pathToCheck, &statbuf) < 0)
// if failed, use defaults
sprintf(commandLineArgs, "\"%s\\pd\\bin\\pd.exe\"", pluginPath);*/
sprintf(commandLineArgs,
"%s%s",
commandLineArgs,
debugString);
sprintf(commandLineArgs,
"%s -schedlib vstschedlib",
commandLineArgs);
sprintf(commandLineArgs,
"%s -extraflags \"-vstproceventname %s -pdproceventname %s -vsthostid %d -mutexname %s -filemapname %s\"",
commandLineArgs,
vstProcEventName,
pdProcEventName,
GetCurrentProcessId(),
pdvstTransferMutexName,
pdvstTransferFileMapName);
sprintf(commandLineArgs,
"%s -outchannels %d -inchannels %d",
commandLineArgs,
nChannels,
nChannels);
sprintf(commandLineArgs,
"%s -r %d",
commandLineArgs,
(int)getSampleRate());
sprintf(commandLineArgs,
"%s -open \"%s%s\"",
commandLineArgs,
pluginPath,
pdFile);
sprintf(commandLineArgs,
"%s -path \"%s\"",
commandLineArgs,
pluginPath);
for (i = 0; i < nExternalLibs; i++)
{
sprintf(commandLineArgs,
"%s -lib %s",
commandLineArgs,
externalLib[i]);
}
debugLog("command line: %s", commandLineArgs);
suspend();
CreateProcess(NULL,
commandLineArgs,
NULL,
NULL,
0,
0,
NULL,
NULL,
&si,
&pi);
}
void pdvst::sendGuiAction(int action)
{
WaitForSingleObject(pdvstTransferMutex, 10);// if (WaitForSingleObject(pdvstTransferMutex, 100)!= WAIT_TIMEOUT)
{
pdvstData->guiState.direction = PD_RECEIVE;
pdvstData->guiState.type = FLOAT_TYPE;
pdvstData->guiState.value.floatData = (float)action;
pdvstData->guiState.updated = 1;
ReleaseMutex(pdvstTransferMutex);
}
}
void pdvst::sendPlugName(char * pName ) // pour envoyer le nom du plugin à puredata
{
WaitForSingleObject(pdvstTransferMutex, 10);//if (WaitForSingleObject(pdvstTransferMutex, 100)!= WAIT_TIMEOUT)
{
pdvstData->plugName.direction = PD_RECEIVE;
pdvstData->plugName.type = STRING_TYPE;
strcpy(pdvstData->plugName.value.stringData,pName);
pdvstData->plugName.updated = 1;
ReleaseMutex(pdvstTransferMutex);
}
}
void pdvst::setSyncToVst(int value)
{
if (WaitForSingleObject(pdvstTransferMutex, 10)==WAIT_OBJECT_0)
{
if (pdvstData->syncToVst != value)
{
pdvstData->syncToVst = value;
}
ReleaseMutex(pdvstTransferMutex);
}
}
void pdvst::suspend()
{
int i;
setSyncToVst(0);
SetEvent(vstProcEvent);
for (i = 0; i < audioBuffer->nChannels; i++)
{
memset(audioBuffer->in[i], 0, audioBuffer->size * sizeof(float));
memset(audioBuffer->out[i], 0, audioBuffer->size * sizeof(float));
}
audioBuffer->inFrameCount = audioBuffer->outFrameCount = 0;
dspActive = false;
setInitialDelay(PDBLKSIZE * 2);
}
void pdvst::resume()
{
int i;
setSyncToVst(1);
SetEvent(vstProcEvent);
for (i = 0; i < audioBuffer->nChannels; i++)
{
memset(audioBuffer->in[i], 0, audioBuffer->size * sizeof(float));
memset(audioBuffer->out[i], 0, audioBuffer->size * sizeof(float));
}
audioBuffer->inFrameCount = audioBuffer->outFrameCount = 0;
dspActive = true;
setInitialDelay(PDBLKSIZE * 2);
if (isASynth)
{
//wantEvents(); deprecated since VST 2.4
}
}
void pdvst::sendProgChange(VstInt32 prgm)
{
WaitForSingleObject(pdvstTransferMutex, 10);
{
pdvstData->prognumber2pd.direction = PD_RECEIVE;
pdvstData->prognumber2pd.type = FLOAT_TYPE;
pdvstData->prognumber2pd.value.floatData = (float)prgm;
pdvstData->prognumber2pd.updated = 1;
pdvstData->progname2pd.direction = PD_RECEIVE;
pdvstData->progname2pd.type = STRING_TYPE;
strcpy(pdvstData->progname2pd.value.stringData, program[prgm].name);
pdvstData->progname2pd.updated = 1;
ReleaseMutex(pdvstTransferMutex);
}
}
void pdvst::setProgram(VstInt32 prgmNum)
{
debugLog("appel de setProgram %d", prgmNum);
int i;
if (prgmNum >= 0 && prgmNum < nPrograms)
{
curProgram = prgmNum;
sendProgChange(prgmNum);
// {JYG to prevent host call of setProgram to override current param settings
// see https://www.kvraudio.com/forum/viewtopic.php?p=6391144
// sprintf(errorMessage,"GetTickCount=%d\ntimeFromStartup=%d",
// GetTickCount(),timeFromStartup);
//MessageBox(NULL, errorMessage,"setProgram",MB_OK);
if ((GetTickCount()-timeFromStartup) < 1000)
return;
else
{
for (i = 0; i < nParameters; i++)
{
setParameter(i, program[curProgram].paramValue[i]);
// MessageBox(NULL,"setvalue","setProgram",MB_OK);
}
}
}
}
VstInt32 pdvst::getProgram()
{
return curProgram;
}
void pdvst::setProgramName(char *name)
{
strcpy(program[curProgram].name, name);
}
void pdvst::getProgramName(char *name)
{
strcpy(name, program[curProgram].name);
// {JYG see pdvst::setProgram below for explanation
timeFromStartup=GetTickCount();
// JYG }
}
void pdvst::setParameter(VstInt32 index, float value)
{
if (vstParam[index] != value)
{
vstParam[index] = value;
WaitForSingleObject(pdvstTransferMutex, 10);// if (WaitForSingleObject(pdvstTransferMutex, 100)!= WAIT_TIMEOUT)
{
pdvstData->vstParameters[index].type = FLOAT_TYPE;
pdvstData->vstParameters[index].value.floatData = value;
pdvstData->vstParameters[index].direction = PD_RECEIVE;
pdvstData->vstParameters[index].updated = 1;
ReleaseMutex(pdvstTransferMutex);
}
}
}
float pdvst::getParameter(VstInt32 index)
{
if (index >= 0 && index < MAXPARAMETERS)
return vstParam[index];
else
return 0;
}
void pdvst::getParameterName(VstInt32 index, char *label)
{
if (index >= 0 && index < MAXPARAMETERS)
strcpy(label, vstParamName[index]);
}
void pdvst::getParameterDisplay(VstInt32 index, char *text)
{
if (index >= 0 && index < MAXPARAMETERS)
float2string(vstParam[index], text, 10);
}
void pdvst::getParameterLabel(VstInt32 index, char *label)
{
strcpy(label, "");
/* if (index >= 0 && index < MAXPARAMETERS)
strcpy(label, vstParamName[index]);*/
}
// FIXME
bool pdvst::getEffectName(char* name)
{
strcpy (name, globalPluginName);
return true;
}
// FIXME
bool pdvst::getOutputProperties(VstInt32 index, VstPinProperties* properties)
{
if (1)//index < 2)
{
sprintf (properties->label, "pd-vst %1d", index + 1);
properties->flags = kVstPinIsActive;
if ((index %2)==0)
properties->flags |= kVstPinIsStereo; // make channel 1+2 stereo
return true;
}
return false;
}
VstInt32 pdvst::getChunk (void** data, bool isPreset)
{
VstInt32 len;
//MessageBoxA(NULL, "getchunk call", "debug", MB_OK); // all host gets here
if(*data)
{
//MessageBoxA(NULL, "getchunk if data", "debug", MB_OK); // not all hosts gets here
WaitForSingleObject(pdvstTransferMutex, 10);
{
//strcpy ((char *)*data, pdvstData->datachunk.value.stringData);
*data = pdvstData->datachunk.value.stringData;
len = (VstInt32)strlen(pdvstData->datachunk.value.stringData);
//memcpy(*data, pdvstData->datachunk.value.stringData, (size_t)len+1);
ReleaseMutex(pdvstTransferMutex);
}
return len+1;
}
else
return 0;
}
VstInt32 pdvst::setChunk (void* data, VstInt32 byteSize, bool isPreset)
{
//MessageBoxA(NULL, "setchunk call", "debug", MB_OK);
if(byteSize)
{
//MessageBoxA(NULL, "setchunk call if bytesize", "debug", MB_OK);
WaitForSingleObject(pdvstTransferMutex, 10);
{
pdvstData->datachunk.direction = PD_RECEIVE;
pdvstData->datachunk.type = STRING_TYPE;
memset(&pdvstData->datachunk.value.stringData, '\0', MAXSTRINGSIZE);
strncpy(pdvstData->datachunk.value.stringData,(char *)data, (size_t)byteSize);
pdvstData->datachunk.updated = 1;
ReleaseMutex(pdvstTransferMutex);
}
}
return 0;
}
VstInt32 pdvst::canDo(char* text)
{
//if (isASynth)
if (1)
{
if (!strcmp (text, "sendVstEvents"))
return 1;
if (!strcmp (text, "sendVstMidiEvent"))
return 1;
if (!strcmp (text, "receiveVstEvents"))
return 1;
if (!strcmp (text, "receiveVstMidiEvent"))
return 1;
}
return -1;
}
void pdvst::process(float **input, float **output, VstInt32 sampleFrames)
{
#ifdef VSTMIDIOUTENABLE
WaitForSingleObject(pdvstTransferMutex, 10);//if (WaitForSingleObject(pdvstTransferMutex, 100)!= WAIT_TIMEOUT)
{
if (pdvstData->midiOutQueueUpdated)
{
// Faire une loop pour parcourir la midiqueue et recopier
//dans la structure midiOutEvents
int midiOutQueueSize=pdvstData->midiOutQueueSize;
for (int ka=0;ka<midiOutQueueSize;ka++)
{
midiEvnts[ka].midiData[0] = pdvstData->midiOutQueue[ka].statusByte;
midiEvnts[ka].midiData[1] = pdvstData->midiOutQueue[ka].dataByte1;
midiEvnts[ka].midiData[2] = pdvstData->midiOutQueue[ka].dataByte2;
}
evnts->numEvents=midiOutQueueSize;
sendVstEventsToHost(evnts); /// TODO : rajouter un test de réussite
pdvstData->midiOutQueueUpdated=0;
pdvstData->midiOutQueueSize=0;
evnts->numEvents=0;
}
ReleaseMutex(pdvstTransferMutex);
}
#endif // VSTMIDIOUTENABLE
/* // Get play head information
VstTimeInfo * info = getTimeInfo(
kVstTempoValid |
kVstPpqPosValid |
kVstTransportPlaying |
kVstTransportChanged |
kVstTransportCycleActive |
kVstTransportRecording |
kVstAutomationWriting |
kVstAutomationReading |
0);
/* if(infos)
{
m_playing_list.setFloat(0, m_playinfos.isPlaying);
m_playing_list.setFloat(1, m_playinfos.timeInSeconds);
sendMessageAnything(m_patch_tie, s_playing, m_playing_list);
m_measure_list.setFloat(0, m_playinfos.bpm);
m_measure_list.setFloat(1, m_playinfos.timeSigNumerator);
m_measure_list.setFloat(2, m_playinfos.timeSigDenominator);
m_measure_list.setFloat(3, m_playinfos.ppqPosition);
m_measure_list.setFloat(4, m_playinfos.ppqPositionOfLastBarStart);
sendMessageAnything(m_patch_tie, s_measure, m_measure_list);
*/
int i, j, k, l;
int framesOut = 0;
if (!dspActive)
{
resume();
}
else
{
setSyncToVst(1);
}
for (i = 0; i < sampleFrames; i++)
{
for (j = 0; j < audioBuffer->nChannels; j++)
{
audioBuffer->in[j][audioBuffer->inFrameCount] = input[j][i];
}
(audioBuffer->inFrameCount)++;
// if enough samples to process then do it
if (audioBuffer->inFrameCount >= PDBLKSIZE)
{
audioBuffer->inFrameCount = 0;
updatePdvstParameters();
// wait for pd process event
// { JYG
//WaitForSingleObject(pdProcEvent, 10000);///int couldSync = WaitForSingleObject(pdProcEvent, 100)!= WAIT_TIMEOUT;
WaitForSingleObject(pdProcEvent,10);
//WaitForSingleObject(pdProcEvent, 10000);
/// JYG: 10000 était une valeur trop élevée qui faisait planter ableton live 8.04, va savoir pourquoi
// JYG }
///if (couldSync)
ResetEvent(pdProcEvent);
for (k = 0; k < PDBLKSIZE; k++)
{
for (l = 0; l < audioBuffer->nChannels; l++)
{
while (audioBuffer->outFrameCount >= audioBuffer->size)
{
audioBuffer->resize(audioBuffer->size * 2);
}
// get pd processed samples
///if (couldSync)
{
// get pd processed samples
audioBuffer->out[l][audioBuffer->outFrameCount] = pdvstData->samples[l][k];
// put new samples in for processing
pdvstData->samples[l][k] = audioBuffer->in[l][k];
}
}
(audioBuffer->outFrameCount)++;
}
///if (couldSync)
pdvstData->sampleRate = (int)getSampleRate();
// signal vst process event
SetEvent(vstProcEvent);
}
}
// output pd processed samples
for (i = 0; i < sampleFrames; i++)
{
for (j = 0; j < audioBuffer->nChannels; j++)
{
if (audioBuffer->outFrameCount > 0)
{
output[j][i] = audioBuffer->out[j][i];
}
else
{
output[j][i] = 0;
}
}
if (audioBuffer->outFrameCount > 0)
{
audioBuffer->outFrameCount--;
framesOut++;
}
}
// shift any remaining buffered out samples
if (audioBuffer->outFrameCount > 0)
{
for (i = 0; i < audioBuffer->nChannels; i++)
{
memmove(&(audioBuffer->out[i][0]),
&(audioBuffer->out[i][framesOut]),
(audioBuffer->outFrameCount) * sizeof(float));
}
}
}
DWORD WINAPI NotifySyncError(LPVOID lpParam) {
MessageBoxA(NULL, "Le processus PureData met du temps à répondre", "PdVST", MB_OK);
return 0;
}
void pdvst::processReplacing(float **input, float **output, VstInt32 sampleFrames)
{
#ifdef VSTMIDIOUTENABLE
if (WaitForSingleObject(pdvstTransferMutex, 10)==WAIT_OBJECT_0)
{
if (pdvstData->midiOutQueueUpdated)
{
// Faire une loop pour parcourir la midiqueue et recopier
//dans la structure midiOutEvents
int midiOutQueueSize=pdvstData->midiOutQueueSize;
for (int ka=0;ka<midiOutQueueSize;ka++)
{
midiEvnts[ka].midiData[0] = pdvstData->midiOutQueue[ka].statusByte;
midiEvnts[ka].midiData[1] = pdvstData->midiOutQueue[ka].dataByte1;
midiEvnts[ka].midiData[2] = pdvstData->midiOutQueue[ka].dataByte2;
}
evnts->numEvents=midiOutQueueSize;
sendVstEventsToHost(evnts); // rajouter un test de réussite
pdvstData->midiOutQueueUpdated=0;
pdvstData->midiOutQueueSize=0;
evnts->numEvents=0;
}
else
{
// Envoyer périodiquement un sendVstEventsToHost(no_event) pour signifier
// à Ableton Live qu'on est bien vivant et qu'on envoie du midi
// (tous les 120 tours de buffer)
nbFramesWithoutMidiOutEvent++;
if (nbFramesWithoutMidiOutEvent>120)
{
nbFramesWithoutMidiOutEvent=0;
midiEvnts[0].midiData[0]=0;
midiEvnts[0].midiData[1]=0;
midiEvnts[0].midiData[2]=0;
evnts->numEvents=1;
sendVstEventsToHost(evnts);
}
}
ReleaseMutex(pdvstTransferMutex);
}
#endif // VSTMIDIOUTENABLE
// Get play head information
VstTimeInfo * infos = getTimeInfo(
kVstTempoValid |
kVstPpqPosValid |
kVstTransportPlaying |
kVstTransportChanged |
kVstTransportCycleActive |
kVstTransportRecording |
kVstAutomationWriting |
kVstAutomationReading |
0);
if(infos)
{
if (WaitForSingleObject(pdvstTransferMutex, 10)==WAIT_OBJECT_0)
{
pdvstData->hostTimeInfo.updated=1;
//copying vstTimeInfo into pdvst transfer data structure
pdvstData->hostTimeInfo.samplePos=infos->samplePos; ///< current Position in audio samples (always valid)
pdvstData->hostTimeInfo.sampleRate=infos->sampleRate; ///< current Sample Rate in Herz (always valid)
pdvstData->hostTimeInfo.nanoSeconds=infos->nanoSeconds; ///< System Time in nanoseconds (10^-9 second)
pdvstData->hostTimeInfo.ppqPos=infos->ppqPos; ///< Musical Position, in Quarter Note (1.0 equals 1 Quarter Note)
pdvstData->hostTimeInfo.tempo=infos->tempo; ///< current Tempo in BPM (Beats Per Minute)
pdvstData->hostTimeInfo.barStartPos=infos->barStartPos; ///< last Bar Start Position, in Quarter Note
pdvstData->hostTimeInfo.cycleStartPos=infos->cycleStartPos; ///< Cycle Start (left locator), in Quarter Note
pdvstData->hostTimeInfo.cycleEndPos=infos->cycleEndPos; ///< Cycle End (right locator), in Quarter Note
pdvstData->hostTimeInfo.timeSigNumerator=(int)infos->timeSigNumerator; ///< Time Signature Numerator (e.g. 3 for 3/4)
pdvstData->hostTimeInfo.timeSigDenominator=(int)infos->timeSigDenominator; ///< Time Signature Denominator (e.g. 4 for 3/4)
pdvstData->hostTimeInfo.smpteOffset=(int)infos->smpteOffset; ///< SMPTE offset (in SMPTE subframes (bits; 1/80 of a frame)). The current SMPTE position can be calculated using #samplePos, #sampleRate, and #smpteFrameRate.
pdvstData->hostTimeInfo.smpteFrameRate=(int)infos->smpteFrameRate; ///< @see VstSmpteFrameRate
pdvstData->hostTimeInfo.samplesToNextClock=(int)infos->samplesToNextClock; ///< MIDI Clock Resolution (24 Per Quarter Note), can be negative (nearest clock)
pdvstData->hostTimeInfo.flags=(int)infos->flags;
ReleaseMutex(pdvstTransferMutex);
}
}
int i, j, k, l;
int framesOut = 0;
if (!dspActive)
{
resume();
}
else
{
setSyncToVst(1);
}
for (i = 0; i < sampleFrames; i++)
{
for (j = 0; j < audioBuffer->nChannels; j++)
{
audioBuffer->in[j][audioBuffer->inFrameCount] = input[j][i];
}
(audioBuffer->inFrameCount)++;
// if enough samples to process then do it
if (audioBuffer->inFrameCount >= PDBLKSIZE)
{
audioBuffer->inFrameCount = 0;
updatePdvstParameters();
// wait for pd process event
// { JYG
int gotPdProcEvent = (WaitForSingleObject(pdProcEvent, 10)==WAIT_OBJECT_0);
// WaitForSingleObject(pdProcEvent,100);
//WaitForSingleObject(pdProcEvent, 10000);
// JYG: 10000 était une valeur trop élevée qui faisait planter ableton live
// JYG }
if (gotPdProcEvent)
{
ResetEvent(pdProcEvent);
syncDefeatNumber=0;
}
else if (syncDefeatNumber==50)
{
//afficher messageErreur
//CreateThread(NULL, 0, &NotifySyncError, NULL, 0, NULL);
//startPd();
syncDefeatNumber++;
}
else
syncDefeatNumber++;
for (k = 0; k < PDBLKSIZE; k++)
{
for (l = 0; l < audioBuffer->nChannels; l++)
{
while (audioBuffer->outFrameCount >= audioBuffer->size)
{
audioBuffer->resize(audioBuffer->size * 2);
}
// get pd processed samples
if (gotPdProcEvent)
{
// get pd processed samples
audioBuffer->out[l][audioBuffer->outFrameCount] = pdvstData->samples[l][k];
// put new samples in for processing
pdvstData->samples[l][k] = audioBuffer->in[l][k];
}
}
(audioBuffer->outFrameCount)++;
}
pdvstData->sampleRate = (int)getSampleRate();
// signal vst process event
SetEvent(vstProcEvent);
}
}
// output pd processed samples
for (i = 0; i < sampleFrames; i++)
{
for (j = 0; j < audioBuffer->nChannels; j++)
{
if (audioBuffer->outFrameCount > 0)
{
output[j][i] = audioBuffer->out[j][i];
}
else
{
output[j][i] = 0;
}
}
if (audioBuffer->outFrameCount > 0)
{
audioBuffer->outFrameCount--;
framesOut++;
}
}
// shift any remaining buffered out samples
if (audioBuffer->outFrameCount > 0)
{
for (i = 0; i < audioBuffer->nChannels; i++)
{
memmove(&(audioBuffer->out[i][0]),
&(audioBuffer->out[i][framesOut]),
(audioBuffer->outFrameCount) * sizeof(float));
}
}
}
VstInt32 pdvst::processEvents(VstEvents* ev)
{
VstMidiEvent* event;
char* midiData;
long statusType;
long statusChannel;
WaitForSingleObject(pdvstTransferMutex, 10);/// if (WaitForSingleObject(pdvstTransferMutex, 100)!= WAIT_TIMEOUT)
// valeur d'attente courte (1000 au lieu de 10000 dans pdvst 0.2)
{
for (long i = 0; i < ev->numEvents; i++)
{
if ((ev->events[i])->type != kVstMidiType)
{
continue;
}
event = (VstMidiEvent*)ev->events[i];
midiData = event->midiData;
statusType = midiData[0] & 0xF0;
statusChannel = midiData[0] & 0x0F;
pdvstData->midiQueue[pdvstData->midiQueueSize].statusByte = midiData[0];
pdvstData->midiQueue[pdvstData->midiQueueSize].dataByte1 = midiData[1];
pdvstData->midiQueue[pdvstData->midiQueueSize].dataByte2 = midiData[2];
if (statusType == 0x80)
{
// note off
pdvstData->midiQueue[pdvstData->midiQueueSize].channelNumber = statusChannel;
pdvstData->midiQueue[pdvstData->midiQueueSize].messageType = NOTE_OFF;
}
else if (statusType == 0x90)
{
// note on
pdvstData->midiQueue[pdvstData->midiQueueSize].channelNumber = statusChannel;
pdvstData->midiQueue[pdvstData->midiQueueSize].messageType = NOTE_ON;
}
else if (statusType == 0xA0)
{
// key pressure
pdvstData->midiQueue[pdvstData->midiQueueSize].channelNumber = statusChannel;
pdvstData->midiQueue[pdvstData->midiQueueSize].messageType = KEY_PRESSURE;
}
else if (statusType == 0xB0)
{
// controller change
pdvstData->midiQueue[pdvstData->midiQueueSize].channelNumber = statusChannel;
pdvstData->midiQueue[pdvstData->midiQueueSize].messageType = CONTROLLER_CHANGE;
}
else if (statusType == 0xC0)
{
// program change
pdvstData->midiQueue[pdvstData->midiQueueSize].channelNumber = statusChannel;
pdvstData->midiQueue[pdvstData->midiQueueSize].messageType = PROGRAM_CHANGE;
}
else if (statusType == 0xD0)
{
// channel pressure
pdvstData->midiQueue[pdvstData->midiQueueSize].channelNumber = statusChannel;
pdvstData->midiQueue[pdvstData->midiQueueSize].messageType = CHANNEL_PRESSURE;
}
else if (statusType == 0xE0)
{
// pitch bend
pdvstData->midiQueue[pdvstData->midiQueueSize].channelNumber = statusChannel;
pdvstData->midiQueue[pdvstData->midiQueueSize].messageType = PITCH_BEND;
}
else
{
// something else
pdvstData->midiQueue[pdvstData->midiQueueSize].messageType = OTHER;
}
pdvstData->midiQueueSize++;
event++;
}
pdvstData->midiQueueUpdated = 1;
ReleaseMutex(pdvstTransferMutex);
return 1;
}
///else
return 0;
}
void pdvst::updatePdvstParameters()
{
int i;
WaitForSingleObject(pdvstTransferMutex, 10);
// WaitForSingleObject(pdvstTransferMutex, 10000);///if (WaitForSingleObject(pdvstTransferMutex, 100)!= WAIT_TIMEOUT)
{
for (i = 0; i < pdvstData->nParameters; i++)
{
if (pdvstData->vstParameters[i].direction == PD_SEND && \
pdvstData->vstParameters[i].updated)
{
if (pdvstData->vstParameters[i].type == FLOAT_TYPE)
{
//beginEdit (i);
setParameterAutomated(i,
pdvstData->vstParameters[i].value.floatData);
//endEdit(i);
//l updateDisplay (); // I have commented this because it breaks sliders, numbox, etc. If this is on it jumps to max when first clicking on sliders, etc.
}
pdvstData->vstParameters[i].updated = 0;
}
}
if (pdvstData->guiName.direction == PD_SEND && \
pdvstData->guiName.updated)
{
if (pdvstData->guiName.type == STRING_TYPE)
{
strcpy(guiName, pdvstData->guiName.value.stringData);
pdvstData->guiName.updated=0;
// signal to vesteditor that guiname is updated
guiNameUpdated=true;
}
}
// to data chunk
if (pdvstData->datachunk.direction == PD_SEND && \
pdvstData->datachunk.updated)
{
if (pdvstData->datachunk.type == STRING_TYPE)
{
pdvstData->datachunk.updated=0;
}
}
ReleaseMutex(pdvstTransferMutex);
}
}
pdVstBuffer::pdVstBuffer(int nChans)
{
int i;
nChannels = nChans;
in = (float **)malloc(nChannels * sizeof(float *));
out = (float **)malloc(nChannels * sizeof(float *));
for (i = 0; i < nChannels; i++)
{
in[i] = (float *)calloc(DEFPDVSTBUFFERSIZE,
sizeof(float));
out[i] = (float *)calloc(DEFPDVSTBUFFERSIZE,
sizeof(float));
}
size = DEFPDVSTBUFFERSIZE;
}
pdVstBuffer::~pdVstBuffer()
{
int i;
for (i = 0; i < nChannels; i++)
{
free(in[i]);
free(out[i]);
}
}
void pdVstBuffer::resize(int newSize)
{
int i;
for (i = 0; i < nChannels; i++)
{
in[i] = (float *)realloc(in[i], newSize * sizeof(float));
out[i] = (float *)realloc(out[i], newSize * sizeof(float));
}
size = newSize;
}