pdvst-0.52/vst-scheduler/vstschedlib.c

796 lines
24 KiB
C
Raw Permalink Blame History

/* 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 <windows.h>
#include <io.h>
#include <stdio.h>
#include "m_pd.h"
#include "s_stuff.h"
#include "pdvstTransfer.h"
#define MAXARGS 1024
#define MAXSTRLEN 1024
#define TIMEUNITPERSEC (32.*441000.)
#define kVstTransportChanged 1
#define kVstTransportPlaying 2
#define kVstTransportRecording 8
#ifdef VSTMIDIOUTENABLE
EXTERN int midi_outhead;
int _midi_outtail=0; // _midi_outtail defined locally
typedef struct _midiqelem
{
double q_time;
int q_portno;
unsigned char q_onebyte;
unsigned char q_byte1;
unsigned char q_byte2;
unsigned char q_byte3;
} t_midiqelem;
#ifndef MIDIQSIZE
#define MIDIQSIZE 1024
#endif /*MIDIQSIZE */
EXTERN t_midiqelem midi_outqueue[MIDIQSIZE];
//EXTERN int msw_nmidiout;
int msw_nmidiout=0;
#endif // VSTMIDIOUTENABLE
typedef struct _vstParameterReceiver
{
t_object x_obj;
t_symbol *x_sym;
}t_vstParameterReceiver;
typedef struct _vstGuiNameReceiver
{
t_object x_obj;
}t_vstGuiNameReceiver;
t_vstGuiNameReceiver *vstGuiNameReceiver;
typedef struct _vstChunkReceiver
{
t_object x_obj;
}t_vstChunkReceiver;
t_vstChunkReceiver *vstChunkReceiver;
t_vstParameterReceiver *vstParameterReceivers[MAXPARAMETERS];
t_class *vstParameterReceiver_class;
t_class *vstGuiNameReceiver_class;
t_class *vstChunkReceiver_class;
char *pdvstTransferMutexName,
*pdvstTransferFileMapName,
*vstProcEventName,
*pdProcEventName;
pdvstTransferData *pdvstData;
HANDLE vstHostProcess,
pdvstTransferMutex,
pdvstTransferFileMap,
vstProcEvent,
pdProcEvent;
pdvstTimeInfo timeInfo;
int tokenizeCommandLineString(char *clString, char **tokens)
{
int i, charCount = 0;
int tokCount= 0;
int quoteOpen = 0;
for (i = 0; i < (signed int)strlen(clString); i++)
{
if (clString[i] == '"')
{
quoteOpen = !quoteOpen;
}
else if (clString[i] == ' ' && !quoteOpen)
{
tokens[tokCount][charCount] = 0;
tokCount++;
charCount = 0;
}
else
{
tokens[tokCount][charCount] = clString[i];
charCount++;
}
}
tokens[tokCount][charCount] = 0;
tokCount++;
return tokCount;
}
void parseArgs(int argc, char **argv)
{
int vstHostProcessId;
while ((argc > 0) && (**argv == '-'))
{
if (strcmp(*argv, "-vsthostid") == 0)
{
vstHostProcessId = atoi(argv[1]);
vstHostProcess = OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
vstHostProcessId);
if (vstHostProcess == NULL)
{
exit(1);
}
argc -= 2;
argv += 2;
}
if (strcmp(*argv, "-mutexname") == 0)
{
pdvstTransferMutexName = argv[1];
argc -= 2;
argv += 2;
}
if (strcmp(*argv, "-filemapname") == 0)
{
pdvstTransferFileMapName = argv[1];
argc -= 2;
argv += 2;
}
if (strcmp(*argv, "-vstproceventname") == 0)
{
vstProcEventName = argv[1];
argc -= 2;
argv += 2;
}
if (strcmp(*argv, "-pdproceventname") == 0)
{
pdProcEventName = argv[1];
argc -= 2;
argv += 2;
}
else
{
argc--;
argv++;
}
}
}
int setPdvstGuiState(int state)
{
t_symbol *tempSym;
tempSym = gensym("rvstopengui");
if (tempSym->s_thing)
{
pd_float(tempSym->s_thing, (float)state);
return 1;
}
else
return 0;
}
int setPdvstPlugName(char* instanceName)
{
t_symbol *tempSym;
tempSym = gensym("rvstplugname");
if (tempSym->s_thing)
{
pd_symbol(tempSym->s_thing, gensym(instanceName));
return 1;
}
else
return 0;
}
int setPdvstChunk(char* instanceName)
{
t_symbol *tempSym;
tempSym = gensym("rvstdata");
if (tempSym->s_thing)
{
pd_symbol(tempSym->s_thing, gensym(instanceName));
return 1;
}
else
return 0;
}
int setPdvstFloatParameter(int index, float value)
{
t_symbol *tempSym;
char string[1024];
sprintf(string, "rvstparameter%d", index);
tempSym = gensym(string);
if (tempSym->s_thing)
{
pd_float(tempSym->s_thing, value);
return 1;
}
else
{
return 0;
}
}
void sendPdVstFloatParameter(t_vstParameterReceiver *x, t_float floatValue)
{
int index;
index = atoi(x->x_sym->s_name + strlen("svstParameter"));
WaitForSingleObject(pdvstTransferMutex, INFINITE);
pdvstData->vstParameters[index].type = FLOAT_TYPE;
pdvstData->vstParameters[index].direction = PD_SEND;
pdvstData->vstParameters[index].updated = 1;
pdvstData->vstParameters[index].value.floatData = floatValue;
ReleaseMutex(pdvstTransferMutex);
}
void sendPdVstChunk(t_vstChunkReceiver *x, t_symbol *sym)
{
WaitForSingleObject(pdvstTransferMutex, INFINITE);
pdvstData->datachunk.type = STRING_TYPE;
pdvstData->datachunk.direction = PD_SEND;
pdvstData->datachunk.updated = 1;
strcpy(pdvstData->datachunk.value.stringData,sym->s_name);
ReleaseMutex(pdvstTransferMutex);
}
void sendPdVstGuiName(t_vstGuiNameReceiver *x, t_symbol *symbolValue)
{
WaitForSingleObject(pdvstTransferMutex, INFINITE);
pdvstData->guiName.type = STRING_TYPE;
pdvstData->guiName.direction = PD_SEND;
pdvstData->guiName.updated = 1;
strcpy(pdvstData->guiName.value.stringData,symbolValue->s_name);
ReleaseMutex(pdvstTransferMutex);
}
void makePdvstParameterReceivers()
{
int i;
char string[1024];
for (i = 0; i < MAXPARAMETERS; i++)
{
vstParameterReceivers[i] = (t_vstParameterReceiver *)pd_new(vstParameterReceiver_class);
sprintf(string, "svstparameter%d", i);
vstParameterReceivers[i]->x_sym = gensym(string);
pd_bind(&vstParameterReceivers[i]->x_obj.ob_pd, gensym(string));
}
}
void makePdvstGuiNameReceiver()
{
vstGuiNameReceiver = (t_vstGuiNameReceiver *)pd_new(vstGuiNameReceiver_class);
pd_bind(&vstGuiNameReceiver->x_obj.ob_pd, gensym("guiName"));
}
void makevstChunkReceiver()
{
vstChunkReceiver = (t_vstChunkReceiver *)pd_new(vstChunkReceiver_class);
pd_bind(&vstChunkReceiver->x_obj.ob_pd, gensym("svstdata"));
}
void send_dacs(void)
{
int i, j, sampleCount, nChannels, blockSize;
t_sample *soundin, *soundout;
soundin = get_sys_soundin();
soundout = get_sys_soundout();
nChannels = pdvstData->nChannels;
blockSize = pdvstData->blockSize;
if (blockSize == *(get_sys_schedblocksize()))
{
sampleCount = 0;
for (i = 0; i < nChannels; i++)
{
for (j = 0; j < blockSize; j++)
{
soundin[sampleCount] = pdvstData->samples[i][j];
pdvstData->samples[i][j] = soundout[sampleCount];
soundout[sampleCount] = 0;
sampleCount++;
}
}
}
}
void scheduler_tick( void)
{
//l sys_addhist(0);
send_dacs();
//l sys_setmiditimediff(0, 1e-6 * *(get_sys_schedadvance()));
//l sys_addhist(1);
/// { JYG
//sched_tick(*(get_sys_time()) + *(get_sys_time_per_dsp_tick()));
sched_tick();
/// JYG }
//l sys_addhist(2);
sys_pollmidiqueue();
sys_pollgui();
//l sys_addhist(3);
}
/*
int HFClock()
{
LARGE_INTEGER freq, now;
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&now);
return (int)((now.QuadPart * 1000) / freq.QuadPart);
}
*/
int scheduler()
{
int i,
blockTime,
active = 1;
DWORD vstHostProcessStatus = 0;
vstParameterReceiver_class = class_new(gensym("vstParameterReceiver"),
0,
0,
sizeof(t_vstParameterReceiver),
0,
0);
class_addfloat(vstParameterReceiver_class, (t_method)sendPdVstFloatParameter);
makePdvstParameterReceivers();
vstChunkReceiver_class = class_new(gensym("vstChunkReceiver"),
0,
0,
sizeof(t_vstChunkReceiver),
0,
0);
class_addsymbol(vstChunkReceiver_class,(t_method)sendPdVstChunk);
makevstChunkReceiver();
vstGuiNameReceiver_class = class_new(gensym("vstGuiNameReceiver"),
0,
0,
sizeof(t_vstGuiNameReceiver),
0,
0);
class_addsymbol(vstGuiNameReceiver_class,(t_method)sendPdVstGuiName);
makePdvstGuiNameReceiver();
*(get_sys_time_per_dsp_tick()) = (TIMEUNITPERSEC) * \
((double)*(get_sys_schedblocksize())) / \
*(get_sys_dacsr());
//l sys_clearhist();
//if (*(get_sys_sleepgrain()) < 1000) // JYG
if (*(get_sys_sleepgrain()) < 100)
{
*(get_sys_sleepgrain()) = *(get_sys_schedadvance()) / 4;
}
if (*(get_sys_sleepgrain()) < 100)
{
*(get_sys_sleepgrain()) = 100;
}
else if (*(get_sys_sleepgrain()) > 5000)
{
*(get_sys_sleepgrain()) = 5000;
}
sys_initmidiqueue();
#ifdef VSTMIDIOUTENABLE
pdvstData->midiOutQueueSize=0;
#endif // VSTMIDIOUTENABLE
while (active)
{
WaitForSingleObject(pdvstTransferMutex, INFINITE);
active = pdvstData->active;
// check sample rate
if (pdvstData->sampleRate != (int)sys_getsr())
{
post("check samplerate");
sys_setchsr(pdvstData->nChannels,
pdvstData->nChannels,
pdvstData->sampleRate);
}
// check for vstParameter changed
if (pdvstData->guiState.direction == PD_RECEIVE && \
pdvstData->guiState.updated)
{
if(setPdvstGuiState((int)pdvstData->guiState.value.floatData))
pdvstData->guiState.updated=0;
}
// JYG { check for vstplug instance name
if (pdvstData->plugName.direction == PD_RECEIVE && \
pdvstData->plugName.updated)
{
if (setPdvstPlugName((char*)pdvstData->plugName.value.stringData))
pdvstData->plugName.updated=0;
}
// lucarda get data chunk from file
if (pdvstData->datachunk.direction == PD_RECEIVE && \
pdvstData->datachunk.updated)
{
if (setPdvstChunk((char*)pdvstData->datachunk.value.stringData))
pdvstData->datachunk.updated=0;
}
if (pdvstData->hostTimeInfo.updated)
{
pdvstData->hostTimeInfo.updated=0;
t_symbol *tempSym;
if (timeInfo.flags!=pdvstData->hostTimeInfo.flags)
{
timeInfo.flags=pdvstData->hostTimeInfo.flags;
tempSym = gensym("vstTimeInfo.flags");
if (tempSym->s_thing)
{
pd_float(tempSym->s_thing, (float)timeInfo.flags);
}
else
{
timeInfo.flags=0;
pdvstData->hostTimeInfo.updated=1; // keep flag as updated
}
}
if ((timeInfo.flags&(kVstTransportChanged|kVstTransportPlaying|kVstTransportRecording))||(timeInfo.ppqPos!=(float)pdvstData->hostTimeInfo.ppqPos))
{
timeInfo.ppqPos=(float)pdvstData->hostTimeInfo.ppqPos;
tempSym = gensym("vstTimeInfo.ppqPos");
if (tempSym->s_thing)
{
pd_float(tempSym->s_thing, timeInfo.ppqPos);
}
else
{
timeInfo.ppqPos=0.;
pdvstData->hostTimeInfo.updated=1; // keep flag as updated
}
}
if ((timeInfo.flags&kVstTransportChanged)|(timeInfo.tempo!=(float)pdvstData->hostTimeInfo.tempo))
{
timeInfo.tempo=(float)pdvstData->hostTimeInfo.tempo;
tempSym = gensym("vstTimeInfo.tempo");
if (tempSym->s_thing)
{
pd_float(tempSym->s_thing, timeInfo.tempo);
}
else
{
timeInfo.tempo=0.;
pdvstData->hostTimeInfo.updated=1; // keep flag as updated
}
}
if ((timeInfo.flags&kVstTransportChanged)|timeInfo.timeSigNumerator!=pdvstData->hostTimeInfo.timeSigNumerator)
{
timeInfo.timeSigNumerator=pdvstData->hostTimeInfo.timeSigNumerator;
tempSym = gensym("vstTimeInfo.timeSigNumerator");
if (tempSym->s_thing)
{
pd_float(tempSym->s_thing, (float)timeInfo.timeSigNumerator);
}
else
{
timeInfo.timeSigNumerator=0;
pdvstData->hostTimeInfo.updated=1; // keep flag as updated
}
}
if ((timeInfo.flags&kVstTransportChanged)| timeInfo.timeSigDenominator!=pdvstData->hostTimeInfo.timeSigDenominator)
{
timeInfo.timeSigDenominator=pdvstData->hostTimeInfo.timeSigDenominator;
tempSym = gensym("vstTimeInfo.timeSigDenominator");
if (tempSym->s_thing)
{
pd_float(tempSym->s_thing, (float)timeInfo.timeSigDenominator);
}
else
{
timeInfo.timeSigDenominator=0;
pdvstData->hostTimeInfo.updated=1; // keep flag as updated
}
}
}
// JYG }
for (i = 0; i < pdvstData->nParameters; i++)
{
if (pdvstData->vstParameters[i].direction == PD_RECEIVE && \
pdvstData->vstParameters[i].updated)
{
if (pdvstData->vstParameters[i].type == FLOAT_TYPE)
{
if (setPdvstFloatParameter(i,
pdvstData->vstParameters[i].value.floatData))
{
pdvstData->vstParameters[i].updated = 0;
}
}
}
}
// check for new midi-in message (VSTi)
if (pdvstData->midiQueueUpdated)
{
for (i = 0; i < pdvstData->midiQueueSize; i++)
{
if (pdvstData->midiQueue[i].messageType == NOTE_ON)
{
inmidi_noteon(0,
pdvstData->midiQueue[i].channelNumber,
pdvstData->midiQueue[i].dataByte1,
pdvstData->midiQueue[i].dataByte2);
}
else if (pdvstData->midiQueue[i].messageType == NOTE_OFF)
{
inmidi_noteon(0,
pdvstData->midiQueue[i].channelNumber,
pdvstData->midiQueue[i].dataByte1,
0);
}
else if (pdvstData->midiQueue[i].messageType == CONTROLLER_CHANGE)
{
inmidi_controlchange(0,
pdvstData->midiQueue[i].channelNumber,
pdvstData->midiQueue[i].dataByte1,
pdvstData->midiQueue[i].dataByte2);
}
else if (pdvstData->midiQueue[i].messageType == PROGRAM_CHANGE)
{
inmidi_programchange(0,
pdvstData->midiQueue[i].channelNumber,
pdvstData->midiQueue[i].dataByte1);
}
else if (pdvstData->midiQueue[i].messageType == PITCH_BEND)
{
int value = (((int)pdvstData->midiQueue[i].dataByte2) * 16) + \
(int)pdvstData->midiQueue[i].dataByte1;
inmidi_pitchbend(0,
pdvstData->midiQueue[i].channelNumber,
value);
}
else if (pdvstData->midiQueue[i].messageType == CHANNEL_PRESSURE)
{
inmidi_aftertouch(0,
pdvstData->midiQueue[i].channelNumber,
pdvstData->midiQueue[i].dataByte1);
}
else if (pdvstData->midiQueue[i].messageType == KEY_PRESSURE)
{
inmidi_polyaftertouch(0,
pdvstData->midiQueue[i].channelNumber,
pdvstData->midiQueue[i].dataByte1,
pdvstData->midiQueue[i].dataByte2);
}
else if (pdvstData->midiQueue[i].messageType == OTHER)
{
// FIXME: what to do?
}
else
{
post("pdvstData->midiQueue error"); // FIXME: error?
}
}
pdvstData->midiQueueSize = 0;
pdvstData->midiQueueUpdated = 0;
}
/// flush vstmidi out messages here
#ifdef VSTMIDIOUTENABLE
if (msw_nmidiout==0)
{
int i=pdvstData->midiOutQueueSize; // si la midiOutQueue n'a pas <20>t<EFBFBD> vid<69>e, rajouter des <20>l<EFBFBD>ments <20> la fin de celle-ci
while (midi_outhead != _midi_outtail)
{
int statusType = midi_outqueue[_midi_outtail].q_byte1 & 0xF0;
int statusChannel = midi_outqueue[_midi_outtail].q_byte1 & 0x0F;
// faut il g<>rer midi_outqueue[_midi_outtail].q_onebyte ?
///copie de la pile midi_outqueue dans la pile vst midiOutQueue
pdvstData->midiOutQueue[i].channelNumber= statusChannel;
pdvstData->midiOutQueue[i].statusByte = midi_outqueue[_midi_outtail].q_byte1;
pdvstData->midiOutQueue[i].dataByte1= midi_outqueue[_midi_outtail].q_byte2;
pdvstData->midiOutQueue[i].dataByte2= midi_outqueue[_midi_outtail].q_byte3;
if (statusType == 0x90)
if (midi_outqueue[_midi_outtail].q_byte3==0)
// note off
{
pdvstData->midiOutQueue[i].messageType = NOTE_OFF;
pdvstData->midiOutQueue[i].statusByte = statusChannel|0x80;
// post("note_off[%d] : %d",i,midi_outqueue[_midi_outtail].q_byte2);
}
else
{
// note on
pdvstData->midiOutQueue[i].messageType = NOTE_ON;
//post("note_on[%d] : %d",i,midi_outqueue[_midi_outtail].q_byte2);
}
else if (statusType == 0xA0)
// key pressure
pdvstData->midiOutQueue[i].messageType = KEY_PRESSURE;
else if (statusType == 0xB0)
// controller change
pdvstData->midiOutQueue[i].messageType = CONTROLLER_CHANGE;
else if (statusType == 0xC0)
// program change
pdvstData->midiOutQueue[i].messageType = PROGRAM_CHANGE;
else if (statusType == 0xD0)
// channel pressure
pdvstData->midiOutQueue[i].messageType = CHANNEL_PRESSURE;
else if (statusType == 0xE0)
// pitch bend
pdvstData->midiOutQueue[i].messageType = PITCH_BEND;
else
// something else
pdvstData->midiOutQueue[i].messageType = OTHER;
_midi_outtail = (_midi_outtail + 1 == MIDIQSIZE ? 0 : _midi_outtail + 1);
i = i + 1;
if (i>= MAXMIDIOUTQUEUESIZE)
break;
}
if (i>0)
{
pdvstData->midiOutQueueSize=i;
pdvstData->midiOutQueueUpdated=1;
}
else
{
pdvstData->midiOutQueueSize=0;
pdvstData->midiOutQueueUpdated=0;
}
}
#endif // VSTMIDIOUTENABLE
// run at approx. real-time
blockTime = (int)((float)(pdvstData->blockSize) / \
(float)pdvstData->sampleRate * 1000.0);
if (blockTime < 1)
{
blockTime = 1;
}
if (pdvstData->syncToVst)
{
ReleaseMutex(pdvstTransferMutex);
if (WaitForSingleObject(vstProcEvent, 1000) == WAIT_TIMEOUT)
{
// we have probably lost sync by now (1 sec)
WaitForSingleObject(pdvstTransferMutex, 100);
pdvstData->syncToVst = 0;
ReleaseMutex(pdvstTransferMutex);
}
ResetEvent(vstProcEvent);
scheduler_tick();
SetEvent(pdProcEvent);
}
else
{
ReleaseMutex(pdvstTransferMutex);
scheduler_tick();
Sleep(blockTime);
}
GetExitCodeProcess(vstHostProcess, &vstHostProcessStatus);
if (vstHostProcessStatus != STILL_ACTIVE)
{
active = 0;
}
}
return 1;
}
int pd_extern_sched(char *flags)
{
int i, argc;
char *argv[MAXARGS];
//l sys_audioapi = API_NONE;
t_audiosettings as;
sys_get_audio_settings(&as);
as.a_api = API_NONE;
sys_set_audio_settings(&as);
//
for (i = 0; i < MAXARGS; i++)
{
argv[i] = (char *)malloc(MAXSTRLEN * sizeof(char));
}
argc = tokenizeCommandLineString(flags, argv);
parseArgs(argc, argv);
pdvstTransferMutex = OpenMutex(MUTEX_ALL_ACCESS, 0, pdvstTransferMutexName);
vstProcEvent = OpenEvent(EVENT_ALL_ACCESS, 0, vstProcEventName);
pdProcEvent = OpenEvent(EVENT_ALL_ACCESS, 0, pdProcEventName);
pdvstTransferFileMap = OpenFileMapping(FILE_MAP_ALL_ACCESS,
0,
pdvstTransferFileMapName);
pdvstData = (pdvstTransferData *)MapViewOfFile(pdvstTransferFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
sizeof(pdvstTransferData));
WaitForSingleObject(pdvstTransferMutex, INFINITE);
//l *(get_sys_schedadvance()) = *(get_sys_main_advance()) * 1000;
//l post("Hello, appel sys_setchsr dans _main. Version 64");
sys_setchsr(pdvstData->nChannels,
pdvstData->nChannels,
pdvstData->sampleRate);
ReleaseMutex(pdvstTransferMutex);
//timeBeginPeriod(1);
scheduler();
UnmapViewOfFile(pdvstData);
CloseHandle(pdvstTransferFileMap);
CloseHandle(pdvstTransferMutex);
for (i = 0; i < MAXARGS; i++)
{
free(argv[i]);
}
return (0);
}