From 220936d9b1072314b410db8da6db124e508c10d0 Mon Sep 17 00:00:00 2001 From: Kristian Amlie Date: Tue, 20 Sep 2011 10:03:04 +0200 Subject: [PATCH 1/9] Removed memory allocation when reading MIDI data. --- dssi-vst.cpp | 4 +--- rdwrops.cpp | 14 ++------------ rdwrops.h | 3 +++ 3 files changed, 6 insertions(+), 15 deletions(-) --- dssi-vst.cpp | 4 +--- rdwrops.cpp | 14 ++------------ rdwrops.h | 3 +++ 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/dssi-vst.cpp b/dssi-vst.cpp index d165533..d7ab17b 100644 --- a/dssi-vst.cpp +++ b/dssi-vst.cpp @@ -7,6 +7,7 @@ */ #include "remotevstclient.h" +#include "rdwrops.h" #include #include @@ -21,9 +22,6 @@ #include #include -// Should be divisible by three -#define MIDI_BUFFER_SIZE 1023 - class DSSIVSTPluginInstance { public: diff --git a/rdwrops.cpp b/rdwrops.cpp index aef9ce2..276ba62 100644 --- a/rdwrops.cpp +++ b/rdwrops.cpp @@ -137,20 +137,10 @@ rdwr_readFloat(int fd, const char *file, int line) extern unsigned char * rdwr_readMIDIData(int fd, int **frameoffsets, int &events, const char *file, int line) { - static unsigned char *buf = 0; - static int *frameoffbuf = 0; - static int bufEvts = 0; + static unsigned char buf[MIDI_BUFFER_SIZE * 3]; + static int frameoffbuf[MIDI_BUFFER_SIZE]; rdwr_tryRead(fd, &events, sizeof(int), file, line); - - if (events > bufEvts) { - delete buf; - delete frameoffbuf; - buf = new unsigned char[events * 3]; - frameoffbuf = new int[events]; - bufEvts = events; - } - rdwr_tryRead(fd, buf, events * 3, file, line); rdwr_tryRead(fd, frameoffbuf, events * sizeof(int), file, line); diff --git a/rdwrops.h b/rdwrops.h index 038be17..4a10dfc 100644 --- a/rdwrops.h +++ b/rdwrops.h @@ -10,6 +10,9 @@ #include #include "remoteplugin.h" +// Should be divisible by three +#define MIDI_BUFFER_SIZE 1023 + extern void rdwr_tryRead(int fd, void *buf, size_t count, const char *file, int line); extern void rdwr_tryWrite(int fd, const void *buf, size_t count, const char *file, int line); extern void rdwr_writeOpcode(int fd, RemotePluginOpcode opcode, const char *file, int line); From 7029311ece058d13366c94cb0d5ce83b78b12839 Mon Sep 17 00:00:00 2001 From: Kristian Amlie Date: Tue, 20 Sep 2011 10:03:04 +0200 Subject: [PATCH 2/9] Move reset() out of the audio thread. It doesn need to be there, and could possibly starve other threads since the audio thread is realtime. --- remotepluginclient.cpp | 2 +- remotepluginserver.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) --- remotepluginclient.cpp | 2 +- remotepluginserver.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/remotepluginclient.cpp b/remotepluginclient.cpp index 1d08f51..4a4767a 100644 --- a/remotepluginclient.cpp +++ b/remotepluginclient.cpp @@ -286,7 +286,7 @@ RemotePluginClient::setSampleRate(int s) void RemotePluginClient::reset() { - writeOpcode(m_processFd, RemotePluginReset); + writeOpcode(m_controlRequestFd, RemotePluginReset); if (m_shmSize > 0) { memset(m_shm, 0, m_shmSize); } diff --git a/remotepluginserver.cpp b/remotepluginserver.cpp index 7228d52..363f6d5 100644 --- a/remotepluginserver.cpp +++ b/remotepluginserver.cpp @@ -320,10 +320,6 @@ RemotePluginServer::dispatchProcessEvents() setSampleRate(readInt(m_processFd)); break; - case RemotePluginReset: - reset(); - break; - default: std::cerr << "WARNING: RemotePluginServer::dispatchProcessEvents: unexpected opcode " << opcode << std::endl; @@ -448,6 +444,10 @@ RemotePluginServer::dispatchControlEvents() case RemotePluginNoOpcode: break; + case RemotePluginReset: + reset(); + break; + default: std::cerr << "WARNING: RemotePluginServer::dispatchControlEvents: unexpected opcode " << opcode << std::endl; From 9851511eff5989ccb3ff42bcf942e39273575072 Mon Sep 17 00:00:00 2001 From: Kristian Amlie Date: Tue, 20 Sep 2011 10:03:04 +0200 Subject: [PATCH 3/9] Added missing header define. --- rdwrops.h | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) --- rdwrops.h | 1 + 1 file changed, 1 insertion(+) diff --git a/rdwrops.h b/rdwrops.h index 4a10dfc..ffdf536 100644 --- a/rdwrops.h +++ b/rdwrops.h @@ -6,6 +6,7 @@ */ #ifndef _RD_WR_OPS_H_ +#define _RD_WR_OPS_H_ #include #include "remoteplugin.h" From ed8d44f497bad12cfe097f2bc17404dfb34030e9 Mon Sep 17 00:00:00 2001 From: Kristian Amlie Date: Tue, 20 Sep 2011 10:03:04 +0200 Subject: [PATCH 4/9] Removed unused function. It will conflict with later patches. --- remotepluginserver.cpp | 26 -------------------------- remotepluginserver.h | 1 - 2 files changed, 0 insertions(+), 27 deletions(-) --- remotepluginserver.cpp | 26 -------------------------- remotepluginserver.h | 1 - 2 files changed, 27 deletions(-) diff --git a/remotepluginserver.cpp b/remotepluginserver.cpp index 363f6d5..0c6c2c0 100644 --- a/remotepluginserver.cpp +++ b/remotepluginserver.cpp @@ -170,32 +170,6 @@ RemotePluginServer::sizeShm() } } -void -RemotePluginServer::dispatch(int timeout) -{ - struct pollfd pfd[2]; - - pfd[0].fd = m_controlRequestFd; - pfd[1].fd = m_processFd; - pfd[0].events = pfd[1].events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; - - if (poll(pfd, 2, timeout) < 0) { - throw RemotePluginClosedException(); - } - - if ((pfd[0].revents & POLLIN) || (pfd[0].revents & POLLPRI)) { - dispatchControl(); - } else if (pfd[1].revents) { - throw RemotePluginClosedException(); - } - - if ((pfd[1].revents & POLLIN) || (pfd[1].revents & POLLPRI)) { - dispatchProcess(); - } else if (pfd[1].revents) { - throw RemotePluginClosedException(); - } -} - void RemotePluginServer::dispatchControl(int timeout) { diff --git a/remotepluginserver.h b/remotepluginserver.h index 91a671c..4c60809 100644 --- a/remotepluginserver.h +++ b/remotepluginserver.h @@ -57,7 +57,6 @@ class RemotePluginServer virtual void showGUI(std::string guiData) { } virtual void hideGUI() { } - void dispatch(int timeout = -1); // may throw RemotePluginClosedException void dispatchControl(int timeout = -1); // may throw RemotePluginClosedException void dispatchProcess(int timeout = -1); // may throw RemotePluginClosedException From 138ee184d3bd386d4de744079ba16312d566554d Mon Sep 17 00:00:00 2001 From: Kristian Amlie Date: Tue, 20 Sep 2011 10:03:04 +0200 Subject: [PATCH 5/9] Removed pointless calls to gettimeofday. They are not used anywhere in the function. --- remotepluginclient.cpp | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) --- remotepluginclient.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/remotepluginclient.cpp b/remotepluginclient.cpp index 4a4767a..3a6ee4b 100644 --- a/remotepluginclient.cpp +++ b/remotepluginclient.cpp @@ -417,8 +417,8 @@ RemotePluginClient::sendMIDIData(unsigned char *data, int *frameoffsets, int eve void RemotePluginClient::process(float **inputs, float **outputs) { - struct timeval start, finish; - gettimeofday(&start, 0); + //struct timeval start, finish; + //gettimeofday(&start, 0); if (m_bufferSize < 0) { std::cerr << "ERROR: RemotePluginClient::setBufferSize must be called before RemotePluginClient::process" << std::endl; @@ -455,7 +455,7 @@ RemotePluginClient::process(float **inputs, float **outputs) // std::cout << "process: wrote opcode " << RemotePluginProcess << std::endl; - gettimeofday(&finish, 0); + //gettimeofday(&finish, 0); // std::cout << "process: time " << finish.tv_sec - start.tv_sec // << " sec, " << finish.tv_usec - start.tv_usec << " usec" // << std::endl; From 454cff194434cee49f5798d25831c094d1a6d610 Mon Sep 17 00:00:00 2001 From: Kristian Amlie Date: Tue, 20 Sep 2011 10:03:04 +0200 Subject: [PATCH 6/9] Switched to using shared memory for process communication. The reason this was done is to improve the real time performance of dssi-vst. The old communication method used in the plugin was pipes, which are not guaranteed to have a bounded execution time, safe for a real time environment. Therefore, in this patch, we switch to using shared memory with a ring buffer as the data stream and semaphores for synchronization. Pros: - The plugin has no extra latency anymore, since the semaphores will properly wait for data to be processed in each call to run(). - If there are xruns, they will be filled with silence and properly reported to jack. Previously, jack would not get notified of the xrun, and there would be a terrible noise (which was because the old buffer data was not cleared and would be repeated several times over). - We can enable the LADSPA_PROPERTY_HARD_RT_CAPABLE flag, since the dssi-vst layer no longer introduces any function calls with unbounded execution time. It is still possible for a plugin to be badly coded and cause xruns, but then the problem is in the VST plugin and not in dssi-vst. Cons: - It is now harder to detect whether the remote side has shut down, since the semaphores will not detect this like a pipe does. - The ring buffer has a certain size, which could theoretically overflow. However, it is very unlikely, unless you send a huge amount of MIDI data at once. Some comments about things that were done in the patch: - Linking to a new library, -lrt, was necessary because we need to call clock_gettime when using semaphores. - "extern" was removed from functions because... well, I didn't see why they needed to be there, and they caused some problems with the templates. - The template functions were introduced mainly to save time, not having to reimplement the read/write functions for all the datatypes. --- Makefile | 2 +- README | 6 -- dssi-vst.cpp | 3 +- rdwrops.cpp | 143 ++++++++++++++++++++++++++++++++++++++++++------ rdwrops.h | 64 ++++++++++++++++++--- remotepluginclient.cpp | 139 ++++++++++++++++++++++++++-------------------- remotepluginclient.h | 8 ++- remotepluginserver.cpp | 94 ++++++++++++++++++++------------ remotepluginserver.h | 6 +- vsthost.cpp | 1 - 10 files changed, 329 insertions(+), 137 deletions(-) --- Makefile | 2 +- README | 6 -- dssi-vst.cpp | 3 +- rdwrops.cpp | 143 +++++++++++++++++++++++++++++++++++------ rdwrops.h | 64 +++++++++++++++--- remotepluginclient.cpp | 139 ++++++++++++++++++++++----------------- remotepluginclient.h | 8 ++- remotepluginserver.cpp | 96 ++++++++++++++++----------- remotepluginserver.h | 6 +- vsthost.cpp | 1 - 10 files changed, 330 insertions(+), 138 deletions(-) diff --git a/Makefile b/Makefile index b1d55ce..dcd9247 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ distclean: clean rm -f $(TARGETS) dssi-vst-scanner dssi-vst-server *~ *.bak %.exe.so: %.cpp libremoteplugin.w32.a $(HEADERS) - wineg++ -m32 $(CXXFLAGS) $< -o $* $(LDFLAGS) -L. -lremoteplugin.w32 -lpthread + wineg++ -m32 $(CXXFLAGS) $< -o $* $(LDFLAGS) -L. -lremoteplugin.w32 -lpthread -lrt libremoteplugin.a: remotepluginclient.o remotepluginserver.o rdwrops.o paths.o ar r $@ $^ diff --git a/README b/README index 2a10a2a..fcd4536 100644 --- a/README +++ b/README @@ -171,12 +171,6 @@ Bad points: plugins would cope better with this by having the server take ownership of such resources instead of the DSSI plugin. -* The comms model dssi-vst uses introduces a fixed latency equal to - the JACK period size, as well as any existing latency in the VST - plugin. (The fixed latency is exposed through the _latency output - control port. Does anyone know how to find out the latency of a - VST plugin?) - Licence ------- diff --git a/dssi-vst.cpp b/dssi-vst.cpp index d7ab17b..2f3c001 100644 --- a/dssi-vst.cpp +++ b/dssi-vst.cpp @@ -339,7 +339,6 @@ DSSIVSTPluginInstance::run(unsigned long sampleCount) if (sampleCount != m_lastSampleCount) { m_plugin->setBufferSize(sampleCount); m_lastSampleCount = sampleCount; - if (m_latencyOut) *m_latencyOut = sampleCount; } int modifiedCount = 0; @@ -494,7 +493,7 @@ DSSIVSTPlugin::DSSIVSTPlugin() ldesc->UniqueID = 6666 + p; ldesc->Label = label; - ldesc->Properties = 0; + ldesc->Properties = LADSPA_PROPERTY_HARD_RT_CAPABLE; ldesc->Name = strdup(std::string(rec.pluginName + " VST").c_str()); ldesc->Maker = strdup(rec.vendorName.c_str()); ldesc->Copyright = strdup(ldesc->Maker); diff --git a/rdwrops.cpp b/rdwrops.cpp index 276ba62..490d2e5 100644 --- a/rdwrops.cpp +++ b/rdwrops.cpp @@ -11,11 +11,13 @@ #include #include +#include #include +#include //#define DEBUG_RDWR 1 -extern void +void rdwr_tryRead(int fd, void *buf, size_t count, const char *file, int line) { ssize_t r = 0; @@ -51,7 +53,7 @@ rdwr_tryRead(int fd, void *buf, size_t count, const char *file, int line) #endif } -extern void +void rdwr_tryWrite(int fd, const void *buf, size_t count, const char *file, int line) { ssize_t w = write(fd, buf, count); @@ -75,22 +77,94 @@ rdwr_tryWrite(int fd, const void *buf, size_t count, const char *file, int line) #endif } -extern void -rdwr_writeOpcode(int fd, RemotePluginOpcode opcode, const char *file, int line) +void +rdwr_tryRead(RingBuffer *ringbuf, void *buf, size_t count, const char *file, int line) +{ + char *charbuf = static_cast(buf); + size_t tail = ringbuf->tail; + size_t head = ringbuf->head; + size_t wrap = 0; + + if (head <= tail) { + wrap = SHM_RING_BUFFER_SIZE; + } + if (head - tail + wrap < count) { + throw RemotePluginClosedException(); + } + + size_t readto = tail + count; + if (readto >= SHM_RING_BUFFER_SIZE) { + readto -= SHM_RING_BUFFER_SIZE; + size_t firstpart = SHM_RING_BUFFER_SIZE - tail; + memcpy(charbuf, ringbuf->buf + tail, firstpart); + memcpy(charbuf + firstpart, ringbuf->buf, readto); + } else { + memcpy(charbuf, ringbuf->buf + tail, count); + } + ringbuf->tail = readto; +} + +void +rdwr_tryWrite(RingBuffer *ringbuf, const void *buf, size_t count, const char *file, int line) +{ + const char *charbuf = static_cast(buf); + size_t written = ringbuf->written; + size_t tail = ringbuf->tail; + size_t wrap = 0; + + if (tail <= written) { + wrap = SHM_RING_BUFFER_SIZE; + } + if (tail - written + wrap < count) { + std::cerr << "Operation ring buffer full! Dropping events." << std::endl; + ringbuf->invalidateCommit = true; + return; + } + + size_t writeto = written + count; + if (writeto >= SHM_RING_BUFFER_SIZE) { + writeto -= SHM_RING_BUFFER_SIZE; + size_t firstpart = SHM_RING_BUFFER_SIZE - written; + memcpy(ringbuf->buf + written, charbuf, firstpart); + memcpy(ringbuf->buf, charbuf + firstpart, writeto); + } else { + memcpy(ringbuf->buf + written, charbuf, count); + } + ringbuf->written = writeto; +} + +void +rdwr_commitWrite(RingBuffer *ringbuf, const char *file, int line) +{ + if (ringbuf->invalidateCommit) { + ringbuf->written = ringbuf->head; + ringbuf->invalidateCommit = false; + } else { + ringbuf->head = ringbuf->written; + } +} + +bool dataAvailable(RingBuffer *ringbuf) +{ + return ringbuf->tail != ringbuf->head; +} + +template void +rdwr_writeOpcode(T fd, RemotePluginOpcode opcode, const char *file, int line) { rdwr_tryWrite(fd, &opcode, sizeof(RemotePluginOpcode), file, line); } -extern void -rdwr_writeString(int fd, const std::string &str, const char *file, int line) +template void +rdwr_writeString(T fd, const std::string &str, const char *file, int line) { int len = str.length(); rdwr_tryWrite(fd, &len, sizeof(int), file, line); rdwr_tryWrite(fd, str.c_str(), len, file, line); } -extern std::string -rdwr_readString(int fd, const char *file, int line) +template std::string +rdwr_readString(T fd, const char *file, int line) { int len; static char *buf = 0; @@ -106,36 +180,36 @@ rdwr_readString(int fd, const char *file, int line) return std::string(buf); } -extern void -rdwr_writeInt(int fd, int i, const char *file, int line) +template void +rdwr_writeInt(T fd, int i, const char *file, int line) { rdwr_tryWrite(fd, &i, sizeof(int), file, line); } -extern int -rdwr_readInt(int fd, const char *file, int line) +template int +rdwr_readInt(T fd, const char *file, int line) { int i = 0; rdwr_tryRead(fd, &i, sizeof(int), file, line); return i; } -extern void -rdwr_writeFloat(int fd, float f, const char *file, int line) +template void +rdwr_writeFloat(T fd, float f, const char *file, int line) { rdwr_tryWrite(fd, &f, sizeof(float), file, line); } -extern float -rdwr_readFloat(int fd, const char *file, int line) +template float +rdwr_readFloat(T fd, const char *file, int line) { float f = 0; rdwr_tryRead(fd, &f, sizeof(float), file, line); return f; } -extern unsigned char * -rdwr_readMIDIData(int fd, int **frameoffsets, int &events, const char *file, int line) +template unsigned char * +rdwr_readMIDIData(T fd, int **frameoffsets, int &events, const char *file, int line) { static unsigned char buf[MIDI_BUFFER_SIZE * 3]; static int frameoffbuf[MIDI_BUFFER_SIZE]; @@ -148,4 +222,37 @@ rdwr_readMIDIData(int fd, int **frameoffsets, int &events, const char *file, int return buf; } +template +void rdwr_writeOpcode(int fd, RemotePluginOpcode opcode, const char *file, int line); +template +void rdwr_writeString(int fd, const std::string &str, const char *file, int line); +template +std::string rdwr_readString(int fd, const char *file, int line); +template +void rdwr_writeInt(int fd, int i, const char *file, int line); +template +int rdwr_readInt(int fd, const char *file, int line); +template +void rdwr_writeFloat(int fd, float f, const char *file, int line); +template +float rdwr_readFloat(int fd, const char *file, int line); +template +unsigned char *rdwr_readMIDIData(int fd, int **frameoffsets, int &events, const char *file, int line); + +template +void rdwr_writeOpcode(RingBuffer *ringbuf, RemotePluginOpcode opcode, const char *file, int line); +template +void rdwr_writeString(RingBuffer *ringbuf, const std::string &str, const char *file, int line); +template +std::string rdwr_readString(RingBuffer *ringbuf, const char *file, int line); +template +void rdwr_writeInt(RingBuffer *ringbuf, int i, const char *file, int line); +template +int rdwr_readInt(RingBuffer *ringbuf, const char *file, int line); +template +void rdwr_writeFloat(RingBuffer *ringbuf, float f, const char *file, int line); +template +float rdwr_readFloat(RingBuffer *ringbuf, const char *file, int line); +template +unsigned char *rdwr_readMIDIData(RingBuffer *ringbuf, int **frameoffsets, int &events, const char *file, int line); diff --git a/rdwrops.h b/rdwrops.h index ffdf536..1979be0 100644 --- a/rdwrops.h +++ b/rdwrops.h @@ -11,19 +11,61 @@ #include #include "remoteplugin.h" +#include + // Should be divisible by three #define MIDI_BUFFER_SIZE 1023 -extern void rdwr_tryRead(int fd, void *buf, size_t count, const char *file, int line); -extern void rdwr_tryWrite(int fd, const void *buf, size_t count, const char *file, int line); -extern void rdwr_writeOpcode(int fd, RemotePluginOpcode opcode, const char *file, int line); -extern void rdwr_writeString(int fd, const std::string &str, const char *file, int line); -extern std::string rdwr_readString(int fd, const char *file, int line); -extern void rdwr_writeInt(int fd, int i, const char *file, int line); -extern int rdwr_readInt(int fd, const char *file, int line); -extern void rdwr_writeFloat(int fd, float f, const char *file, int line); -extern float rdwr_readFloat(int fd, const char *file, int line); -extern unsigned char *rdwr_readMIDIData(int fd, int **frameoffsets, int &events, const char *file, int line); +#define SHM_RING_BUFFER_SIZE 2048 + +struct RingBuffer +{ + int head; + int tail; + int written; + bool invalidateCommit; + char buf[SHM_RING_BUFFER_SIZE]; +}; + +struct ShmControl +{ + // 32 and 64-bit binaries align semaphores differently. + // Let's make sure there's plenty of room for either one. + union { + sem_t runServer; + char alignServerSem[32]; + }; + union { + sem_t runClient; + char alignClientSem[32]; + }; + RingBuffer ringBuffer; +}; + +void rdwr_tryRead(int fd, void *buf, size_t count, const char *file, int line); +void rdwr_tryWrite(int fd, const void *buf, size_t count, const char *file, int line); + +void rdwr_tryRead(RingBuffer *ringbuf, void *buf, size_t count, const char *file, int line); +void rdwr_tryWrite(RingBuffer *ringbuf, const void *buf, size_t count, const char *file, int line); +void rdwr_commitWrite(RingBuffer *ringbuf, const char *file, int line); +bool dataAvailable(RingBuffer *ringbuf); + +template +void rdwr_writeOpcode(T fd, RemotePluginOpcode opcode, const char *file, int line); +template +void rdwr_writeString(T fd, const std::string &str, const char *file, int line); +template +std::string rdwr_readString(T fd, const char *file, int line); +template +void rdwr_writeInt(T fd, int i, const char *file, int line); +template +int rdwr_readInt(T fd, const char *file, int line); +template +void rdwr_writeFloat(T fd, float f, const char *file, int line); +template +float rdwr_readFloat(T fd, const char *file, int line); +template +unsigned char *rdwr_readMIDIData(T fd, int **frameoffsets, int &events, const char *file, int line); #define tryRead(a, b, c) rdwr_tryRead(a, b, c, __FILE__, __LINE__) #define tryWrite(a, b, c) rdwr_tryWrite(a, b, c, __FILE__, __LINE__) @@ -35,5 +77,7 @@ extern unsigned char *rdwr_readMIDIData(int fd, int **frameoffsets, int &events, #define writeFloat(a, b) rdwr_writeFloat(a, b, __FILE__, __LINE__) #define readFloat(a) rdwr_readFloat(a, __FILE__, __LINE__) #define readMIDIData(a, b, c) rdwr_readMIDIData(a, b, c, __FILE__, __LINE__) +#define commitWrite(a) rdwr_commitWrite(a, __FILE__, __LINE__) +#define purgeRead(a) rdwr_purgeRead(a, __FILE__, __LINE__) #endif diff --git a/remotepluginclient.cpp b/remotepluginclient.cpp index 3a6ee4b..0a64f64 100644 --- a/remotepluginclient.cpp +++ b/remotepluginclient.cpp @@ -20,19 +20,18 @@ #include #include -#include "rdwrops.h" - RemotePluginClient::RemotePluginClient() : m_controlRequestFd(-1), m_controlResponseFd(-1), - m_processFd(-1), m_shmFd(-1), + m_shmControlFd(-1), m_controlRequestFileName(0), m_controlResponseFileName(0), - m_processFileName(0), m_shmFileName(0), + m_shmControlFileName(0), m_shm(0), m_shmSize(0), + m_shmControl(0), m_bufferSize(-1), m_numInputs(-1), m_numOutputs(-1) @@ -67,18 +66,31 @@ RemotePluginClient::RemotePluginClient() : throw((std::string)"Failed to create FIFO"); } - sprintf(tmpFileBase, "/tmp/rplugin_prc_XXXXXX"); + sprintf(tmpFileBase, "/tmp/rplugin_shc_XXXXXX"); if (mkstemp(tmpFileBase) < 0) { cleanup(); throw((std::string)"Failed to obtain temporary filename"); } - m_processFileName = strdup(tmpFileBase); + m_shmControlFileName = strdup(tmpFileBase); - unlink(m_processFileName); - if (mkfifo(m_processFileName, 0666)) { - perror(m_processFileName); + m_shmControlFd = open(m_shmControlFileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (m_shmControlFd < 0) { cleanup(); - throw((std::string)"Failed to create FIFO"); + throw((std::string)"Failed to open or create shared memory file"); + } + ftruncate(m_shmControlFd, sizeof(ShmControl)); + m_shmControl = static_cast(mmap(0, sizeof(ShmControl), PROT_READ | PROT_WRITE, MAP_SHARED, m_shmControlFd, 0)); + if (!m_shmControl) { + cleanup(); + throw((std::string)"Failed to mmap shared memory file"); + } + memset(m_shmControl, 0, sizeof(ShmControl)); + + if (sem_init(&m_shmControl->runServer, 1, 0)) { + throw((std::string)"Failed to initialize shared memory semaphore"); + } + if (sem_init(&m_shmControl->runClient, 1, 0)) { + throw((std::string)"Failed to initialize shared memory semaphore"); } sprintf(tmpFileBase, "/tmp/rplugin_shm_XXXXXX"); @@ -106,7 +118,7 @@ RemotePluginClient::syncStartup() // The first (write) fd we open in a nonblocking call, with a // short retry loop so we can easily give up if the other end // doesn't appear to be responding. We want a nonblocking FIFO - // for this and the process fd anyway. + // for this anyway. bool connected = false; int timeout = 40; @@ -135,26 +147,6 @@ RemotePluginClient::syncStartup() throw((std::string)"Failed to open control FIFO"); } - connected = false; - - for (int attempt = 0; attempt < 6; ++attempt) { - - if ((m_processFd = open(m_processFileName, O_WRONLY | O_NONBLOCK)) >= 0) { - connected = true; - break; - } else if (errno != ENXIO) { - // an actual error occurred - break; - } - - sleep(1); - } - - if (!connected) { - cleanup(); - throw((std::string)"Failed to open process FIFO"); - } - bool b = false; tryRead(m_controlResponseFd, &b, sizeof(bool)); if (!b) { @@ -170,6 +162,10 @@ RemotePluginClient::cleanup() munmap(m_shm, m_shmSize); m_shm = 0; } + if (m_shmControl) { + munmap(m_shmControl, sizeof(ShmControl)); + m_shmControl = 0; + } if (m_controlRequestFd >= 0) { close(m_controlRequestFd); m_controlRequestFd = -1; @@ -178,14 +174,14 @@ RemotePluginClient::cleanup() close(m_controlResponseFd); m_controlResponseFd = -1; } - if (m_processFd >= 0) { - close(m_processFd); - m_processFd = -1; - } if (m_shmFd >= 0) { close(m_shmFd); m_shmFd = -1; } + if (m_shmControlFd >= 0) { + close(m_shmControlFd); + m_shmControlFd = -1; + } if (m_controlRequestFileName) { unlink(m_controlRequestFileName); free(m_controlRequestFileName); @@ -196,16 +192,16 @@ RemotePluginClient::cleanup() free(m_controlResponseFileName); m_controlResponseFileName = 0; } - if (m_processFileName) { - unlink(m_processFileName); - free(m_processFileName); - m_processFileName = 0; - } if (m_shmFileName) { unlink(m_shmFileName); free(m_shmFileName); m_shmFileName = 0; } + if (m_shmControlFileName) { + unlink(m_shmControlFileName); + free(m_shmControlFileName); + m_shmControlFileName = 0; + } } std::string @@ -214,7 +210,7 @@ RemotePluginClient::getFileIdentifiers() std::string id; id += m_controlRequestFileName + strlen(m_controlRequestFileName) - 6; id += m_controlResponseFileName + strlen(m_controlResponseFileName) - 6; - id += m_processFileName + strlen(m_processFileName) - 6; + id += m_shmControlFileName + strlen(m_shmControlFileName) - 6; id += m_shmFileName + strlen(m_shmFileName) - 6; std::cerr << "Returning file identifiers: " << id << std::endl; return id; @@ -272,15 +268,19 @@ RemotePluginClient::setBufferSize(int s) if (s == m_bufferSize) return; m_bufferSize = s; sizeShm(); - writeOpcode(m_processFd, RemotePluginSetBufferSize); - writeInt(m_processFd, s); + writeOpcode(&m_shmControl->ringBuffer, RemotePluginSetBufferSize); + writeInt(&m_shmControl->ringBuffer, s); + commitWrite(&m_shmControl->ringBuffer); + waitForServer(); } void RemotePluginClient::setSampleRate(int s) { - writeOpcode(m_processFd, RemotePluginSetSampleRate); - writeInt(m_processFd, s); + writeOpcode(&m_shmControl->ringBuffer, RemotePluginSetSampleRate); + writeInt(&m_shmControl->ringBuffer, s); + commitWrite(&m_shmControl->ringBuffer); + waitForServer(); } void @@ -334,9 +334,10 @@ RemotePluginClient::getParameterName(int p) void RemotePluginClient::setParameter(int p, float v) { - writeOpcode(m_processFd, RemotePluginSetParameter); - writeInt(m_processFd, p); - writeFloat(m_processFd, v); + writeOpcode(&m_shmControl->ringBuffer, RemotePluginSetParameter); + writeInt(&m_shmControl->ringBuffer, p); + writeFloat(&m_shmControl->ringBuffer, v); + commitWrite(&m_shmControl->ringBuffer); } float @@ -391,16 +392,18 @@ RemotePluginClient::getProgramName(int n) void RemotePluginClient::setCurrentProgram(int n) { - writeOpcode(m_processFd, RemotePluginSetCurrentProgram); - writeInt(m_processFd, n); + writeOpcode(&m_shmControl->ringBuffer, RemotePluginSetCurrentProgram); + writeInt(&m_shmControl->ringBuffer, n); + commitWrite(&m_shmControl->ringBuffer); + waitForServer(); } void RemotePluginClient::sendMIDIData(unsigned char *data, int *frameoffsets, int events) { - writeOpcode(m_processFd, RemotePluginSendMIDIData); - writeInt(m_processFd, events); - tryWrite(m_processFd, data, events * 3); + writeOpcode(&m_shmControl->ringBuffer, RemotePluginSendMIDIData); + writeInt(&m_shmControl->ringBuffer, events); + tryWrite(&m_shmControl->ringBuffer, data, events * 3); if (!frameoffsets) { // This should not happen with a good client, but we'd better @@ -411,7 +414,8 @@ RemotePluginClient::sendMIDIData(unsigned char *data, int *frameoffsets, int eve // std::cerr << "RemotePluginClient::sendMIDIData(" << events << ")" << std::endl; - tryWrite(m_processFd, frameoffsets, events * sizeof(int)); + tryWrite(&m_shmControl->ringBuffer, frameoffsets, events * sizeof(int)); + commitWrite(&m_shmControl->ringBuffer); } void @@ -442,16 +446,18 @@ RemotePluginClient::process(float **inputs, float **outputs) //!!! put counter in shm to indicate number of blocks processed? // (so we know if we've screwed up) - // retrieve results of previous process() instead of waiting for this - for (int i = 0; i < m_numOutputs; ++i) { - memcpy(outputs[i], m_shm + (i + m_numInputs) * blocksz, blocksz); - } - for (int i = 0; i < m_numInputs; ++i) { memcpy(m_shm + i * blocksz, inputs[i], blocksz); } - writeOpcode(m_processFd, RemotePluginProcess); + writeOpcode(&m_shmControl->ringBuffer, RemotePluginProcess); + commitWrite(&m_shmControl->ringBuffer); + + waitForServer(); + + for (int i = 0; i < m_numOutputs; ++i) { + memcpy(outputs[i], m_shm + (i + m_numInputs) * blocksz, blocksz); + } // std::cout << "process: wrote opcode " << RemotePluginProcess << std::endl; @@ -462,6 +468,19 @@ RemotePluginClient::process(float **inputs, float **outputs) return; } +void +RemotePluginClient::waitForServer() +{ + sem_post(&m_shmControl->runServer); + + timespec ts_timeout; + clock_gettime(CLOCK_REALTIME, &ts_timeout); + ts_timeout.tv_sec += 5; + if (sem_timedwait(&m_shmControl->runClient, &ts_timeout) != 0) { + throw RemotePluginClosedException(); + } +} + void RemotePluginClient::setDebugLevel(RemotePluginDebugLevel level) { diff --git a/remotepluginclient.h b/remotepluginclient.h index 26b1309..5c7341b 100644 --- a/remotepluginclient.h +++ b/remotepluginclient.h @@ -9,6 +9,7 @@ #define REMOTE_PLUGIN_CLIENT_H #include "remoteplugin.h" +#include "rdwrops.h" #include #include #include @@ -56,6 +57,8 @@ class RemotePluginClient // Either inputs or outputs may be NULL if (and only if) there are none void process(float **inputs, float **outputs); + void waitForServer(); + void setDebugLevel(RemotePluginDebugLevel); bool warn(std::string); @@ -74,16 +77,17 @@ class RemotePluginClient int m_controlRequestFd; int m_controlResponseFd; - int m_processFd; int m_shmFd; + int m_shmControlFd; char *m_controlRequestFileName; char *m_controlResponseFileName; - char *m_processFileName; char *m_shmFileName; + char *m_shmControlFileName; char *m_shm; size_t m_shmSize; + ShmControl *m_shmControl; int m_bufferSize; int m_numInputs; diff --git a/remotepluginserver.cpp b/remotepluginserver.cpp index 0c6c2c0..ad5b84f 100644 --- a/remotepluginserver.cpp +++ b/remotepluginserver.cpp @@ -20,8 +20,7 @@ #include #include #include - -#include "rdwrops.h" +#include RemotePluginServer::RemotePluginServer(std::string fileIdentifiers) : m_bufferSize(-1), @@ -29,14 +28,15 @@ RemotePluginServer::RemotePluginServer(std::string fileIdentifiers) : m_numOutputs(-1), m_controlRequestFd(-1), m_controlResponseFd(-1), - m_processFd(-1), m_shmFd(-1), + m_shmControlFd(-1), m_controlRequestFileName(0), m_controlResponseFileName(0), - m_processFileName(0), m_shmFileName(0), + m_shmControlFileName(0), m_shm(0), m_shmSize(0), + m_shmControl(0), m_inputs(0), m_outputs(0) { @@ -60,20 +60,29 @@ RemotePluginServer::RemotePluginServer(std::string fileIdentifiers) : throw((std::string)"Failed to open FIFO"); } - sprintf(tmpFileBase, "/tmp/rplugin_prc_%s", + bool b = false; + + sprintf(tmpFileBase, "/tmp/rplugin_shc_%s", fileIdentifiers.substr(12, 6).c_str()); - m_processFileName = strdup(tmpFileBase); + m_shmControlFileName = strdup(tmpFileBase); - if ((m_processFd = open(m_processFileName, O_RDONLY)) < 0) { + m_shmControlFd = open(m_shmControlFileName, O_RDWR); + if (m_shmControlFd < 0) { + tryWrite(m_controlResponseFd, &b, sizeof(bool)); cleanup(); - throw((std::string)"Failed to open FIFO"); + throw((std::string)"Failed to open or create shared memory file"); } - + m_shmControl = static_cast(mmap(0, sizeof(ShmControl), PROT_READ | PROT_WRITE, MAP_SHARED, m_shmControlFd, 0)); + if (!m_shmControl) { + tryWrite(m_controlResponseFd, &b, sizeof(bool)); + cleanup(); + throw((std::string)"Failed to mmap shared memory file"); + } + sprintf(tmpFileBase, "/tmp/rplugin_shm_%s", fileIdentifiers.substr(18, 6).c_str()); m_shmFileName = strdup(tmpFileBase); - bool b = false; if ((m_shmFd = open(m_shmFileName, O_RDWR)) < 0) { tryWrite(m_controlResponseFd, &b, sizeof(bool)); @@ -97,6 +106,10 @@ RemotePluginServer::cleanup() munmap(m_shm, m_shmSize); m_shm = 0; } + if (m_shmControl) { + munmap(m_shmControl, sizeof(ShmControl)); + m_shmControl = 0; + } if (m_controlRequestFd >= 0) { close(m_controlRequestFd); m_controlRequestFd = -1; @@ -105,14 +118,14 @@ RemotePluginServer::cleanup() close(m_controlResponseFd); m_controlResponseFd = -1; } - if (m_processFd >= 0) { - close(m_processFd); - m_processFd = -1; - } if (m_shmFd >= 0) { close(m_shmFd); m_shmFd = -1; } + if (m_shmControlFd >= 0) { + close(m_shmControlFd); + m_shmControlFd = -1; + } if (m_controlRequestFileName) { free(m_controlRequestFileName); m_controlRequestFileName = 0; @@ -121,14 +134,14 @@ RemotePluginServer::cleanup() free(m_controlResponseFileName); m_controlResponseFileName = 0; } - if (m_processFileName) { - free(m_processFileName); - m_processFileName = 0; - } if (m_shmFileName) { free(m_shmFileName); m_shmFileName = 0; } + if (m_shmControlFileName) { + free(m_shmControlFileName); + m_shmControlFileName = 0; + } delete m_inputs; m_inputs = 0; @@ -192,19 +205,30 @@ RemotePluginServer::dispatchControl(int timeout) void RemotePluginServer::dispatchProcess(int timeout) { - struct pollfd pfd; - - pfd.fd = m_processFd; - pfd.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; - - if (poll(&pfd, 1, timeout) < 0) { - throw RemotePluginClosedException(); + timespec ts_timeout; + clock_gettime(CLOCK_REALTIME, &ts_timeout); + time_t seconds = timeout / 1000; + ts_timeout.tv_sec += seconds; + ts_timeout.tv_nsec += (timeout - seconds * 1000) * 1000000; + if (ts_timeout.tv_nsec >= 1000000000) { + ts_timeout.tv_nsec -= 1000000000; + ts_timeout.tv_sec++; + } + + if (sem_timedwait(&m_shmControl->runServer, &ts_timeout)) { + if (errno == ETIMEDOUT) { + return; + } else { + throw RemotePluginClosedException(); + } } - - if ((pfd.revents & POLLIN) || (pfd.revents & POLLPRI)) { + + while (dataAvailable(&m_shmControl->ringBuffer)) { dispatchProcessEvents(); - } else if (pfd.revents) { - throw RemotePluginClosedException(); + } + + if (sem_post(&m_shmControl->runClient)) { + std::cerr << "Could not post to semaphore\n"; } } @@ -213,7 +237,7 @@ RemotePluginServer::dispatchProcessEvents() { RemotePluginOpcode opcode = RemotePluginNoOpcode; - tryRead(m_processFd, &opcode, sizeof(RemotePluginOpcode)); + tryRead(&m_shmControl->ringBuffer, &opcode, sizeof(RemotePluginOpcode)); // std::cerr << "read opcode: " << opcode << std::endl; @@ -260,20 +284,20 @@ RemotePluginServer::dispatchProcessEvents() case RemotePluginSetParameter: { - int pn(readInt(m_processFd)); - setParameter(pn, readFloat(m_processFd)); + int pn(readInt(&m_shmControl->ringBuffer)); + setParameter(pn, readFloat(&m_shmControl->ringBuffer)); break; } case RemotePluginSetCurrentProgram: - setCurrentProgram(readInt(m_processFd)); + setCurrentProgram(readInt(&m_shmControl->ringBuffer)); break; case RemotePluginSendMIDIData: { int events = 0; int *frameoffsets = 0; - unsigned char *data = readMIDIData(m_processFd, &frameoffsets, events); + unsigned char *data = readMIDIData(&m_shmControl->ringBuffer, &frameoffsets, events); if (events && data && frameoffsets) { // std::cerr << "RemotePluginServer::sendMIDIData(" << events << ")" << std::endl; @@ -284,14 +308,14 @@ RemotePluginServer::dispatchProcessEvents() case RemotePluginSetBufferSize: { - int newSize = readInt(m_processFd); + int newSize = readInt(&m_shmControl->ringBuffer); setBufferSize(newSize); m_bufferSize = newSize; break; } case RemotePluginSetSampleRate: - setSampleRate(readInt(m_processFd)); + setSampleRate(readInt(&m_shmControl->ringBuffer)); break; default: diff --git a/remotepluginserver.h b/remotepluginserver.h index 4c60809..1375b99 100644 --- a/remotepluginserver.h +++ b/remotepluginserver.h @@ -9,6 +9,7 @@ #define REMOTE_PLUGIN_SERVER_H #include "remoteplugin.h" +#include "rdwrops.h" #include class RemotePluginServer @@ -78,16 +79,17 @@ class RemotePluginServer int m_controlRequestFd; int m_controlResponseFd; - int m_processFd; int m_shmFd; + int m_shmControlFd; char *m_controlRequestFileName; char *m_controlResponseFileName; - char *m_processFileName; char *m_shmFileName; + char *m_shmControlFileName; char *m_shm; size_t m_shmSize; + ShmControl *m_shmControl; float **m_inputs; float **m_outputs; diff --git a/vsthost.cpp b/vsthost.cpp index 6bca0f8..6cd09dd 100644 --- a/vsthost.cpp +++ b/vsthost.cpp @@ -23,7 +23,6 @@ static RemotePluginClient *plugin = 0; #define MIDI_DECODED_SIZE 3 -#define MIDI_BUFFER_SIZE 3072 static unsigned char midiStreamBuffer[(MIDI_BUFFER_SIZE + 1) * MIDI_DECODED_SIZE]; static struct timeval midiTimeBuffer[MIDI_BUFFER_SIZE]; From c9a9e2c9dd4eb04347e231f0677fad88be4f240b Mon Sep 17 00:00:00 2001 From: Kristian Amlie Date: Tue, 20 Sep 2011 10:03:04 +0200 Subject: [PATCH 7/9] Fixed sporadic xrun bug in shared memory code. This one was hard to track down. The symptom was that a few times, sometimes several hours apart, the native plugin code would take much longer than normal to complete, causing xruns for the plugin host. It turns out that since the shared memory being used is backed by a file, the Linux kernel likes to flush the contents in memory back to the file occasionally. While it does this *it locks the memory pages*, meaning that the plugin, which tries to write to the memory, is suspended until the file write is complete. Of course, waiting for file I/O inside the realtime code is unacceptable, so we need shared memory that does not flush to disk. The fix was to switch to POSIX shared memory objects, using shm_open(), instead of normal file open(). Also fixed a trivial bug where the shared memory was opened twice (mkstemp already opens the file, but we still opened it again afterwards). --- paths.cpp | 36 ++++++++++++++++++++++++++++++++++++ paths.h | 2 ++ remotepluginclient.cpp | 29 +++++++++++------------------ remotepluginserver.cpp | 8 ++++---- 4 files changed, 53 insertions(+), 22 deletions(-) --- paths.cpp | 36 ++++++++++++++++++++++++++++++++++++ paths.h | 2 ++ remotepluginclient.cpp | 29 +++++++++++------------------ remotepluginserver.cpp | 8 ++++---- 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/paths.cpp b/paths.cpp index c27ecbb..d167468 100644 --- a/paths.cpp +++ b/paths.cpp @@ -9,6 +9,11 @@ #include #include +#include +#include +#include +#include +#include std::vector Paths::getPath(std::string envVar, std::string deflt, std::string defltHomeRelPath) @@ -39,3 +44,34 @@ Paths::getPath(std::string envVar, std::string deflt, std::string defltHomeRelPa return pathList; } + +// Behaves like mkstemp, but for shared memory. +int shm_mkstemp(char *fileBase) +{ + const char charSet[] = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789"; + int size = strlen(fileBase); + if (size < 6) { + errno = EINVAL; + return -1; + } + + if (strcmp(fileBase + size - 6, "XXXXXX") != 0) { + errno = EINVAL; + return -1; + } + + while (1) { + for (int c = size - 6; c < size; c++) { + // Note the -1 to avoid the trailing '\0' in charSet. + fileBase[c] = charSet[rand() % (sizeof(charSet) - 1)]; + } + int fd = shm_open(fileBase, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + return fd; + } else if (errno != EEXIST) { + return -1; + } + } +} diff --git a/paths.h b/paths.h index 514fb41..0b6a6aa 100644 --- a/paths.h +++ b/paths.h @@ -20,4 +20,6 @@ class Paths std::string defltHomeRelPath); }; +int shm_mkstemp(char *fileBase); + #endif diff --git a/remotepluginclient.cpp b/remotepluginclient.cpp index 0a64f64..954782a 100644 --- a/remotepluginclient.cpp +++ b/remotepluginclient.cpp @@ -6,6 +6,7 @@ */ #include "remotepluginclient.h" +#include "paths.h" #include #include @@ -38,6 +39,8 @@ RemotePluginClient::RemotePluginClient() : { char tmpFileBase[60]; + srand(time(NULL)); + sprintf(tmpFileBase, "/tmp/rplugin_crq_XXXXXX"); if (mkstemp(tmpFileBase) < 0) { cleanup(); @@ -66,18 +69,13 @@ RemotePluginClient::RemotePluginClient() : throw((std::string)"Failed to create FIFO"); } - sprintf(tmpFileBase, "/tmp/rplugin_shc_XXXXXX"); - if (mkstemp(tmpFileBase) < 0) { - cleanup(); - throw((std::string)"Failed to obtain temporary filename"); - } - m_shmControlFileName = strdup(tmpFileBase); - - m_shmControlFd = open(m_shmControlFileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + sprintf(tmpFileBase, "/dssi-vst-rplugin_shc_XXXXXX"); + m_shmControlFd = shm_mkstemp(tmpFileBase); if (m_shmControlFd < 0) { cleanup(); throw((std::string)"Failed to open or create shared memory file"); } + m_shmControlFileName = strdup(tmpFileBase); ftruncate(m_shmControlFd, sizeof(ShmControl)); m_shmControl = static_cast(mmap(0, sizeof(ShmControl), PROT_READ | PROT_WRITE, MAP_SHARED, m_shmControlFd, 0)); if (!m_shmControl) { @@ -93,18 +91,13 @@ RemotePluginClient::RemotePluginClient() : throw((std::string)"Failed to initialize shared memory semaphore"); } - sprintf(tmpFileBase, "/tmp/rplugin_shm_XXXXXX"); - if (mkstemp(tmpFileBase) < 0) { - cleanup(); - throw((std::string)"Failed to obtain temporary filename"); - } - m_shmFileName = strdup(tmpFileBase); - - m_shmFd = open(m_shmFileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + sprintf(tmpFileBase, "/dssi-vst-rplugin_shm_XXXXXX"); + m_shmFd = shm_mkstemp(tmpFileBase); if (m_shmFd < 0) { cleanup(); throw((std::string)"Failed to open or create shared memory file"); } + m_shmFileName = strdup(tmpFileBase); } RemotePluginClient::~RemotePluginClient() @@ -193,12 +186,12 @@ RemotePluginClient::cleanup() m_controlResponseFileName = 0; } if (m_shmFileName) { - unlink(m_shmFileName); + shm_unlink(m_shmFileName); free(m_shmFileName); m_shmFileName = 0; } if (m_shmControlFileName) { - unlink(m_shmControlFileName); + shm_unlink(m_shmControlFileName); free(m_shmControlFileName); m_shmControlFileName = 0; } diff --git a/remotepluginserver.cpp b/remotepluginserver.cpp index ad5b84f..9752dfb 100644 --- a/remotepluginserver.cpp +++ b/remotepluginserver.cpp @@ -62,11 +62,11 @@ RemotePluginServer::RemotePluginServer(std::string fileIdentifiers) : bool b = false; - sprintf(tmpFileBase, "/tmp/rplugin_shc_%s", + sprintf(tmpFileBase, "/dssi-vst-rplugin_shc_%s", fileIdentifiers.substr(12, 6).c_str()); m_shmControlFileName = strdup(tmpFileBase); - m_shmControlFd = open(m_shmControlFileName, O_RDWR); + m_shmControlFd = shm_open(m_shmControlFileName, O_RDWR, 0); if (m_shmControlFd < 0) { tryWrite(m_controlResponseFd, &b, sizeof(bool)); cleanup(); @@ -79,12 +79,12 @@ RemotePluginServer::RemotePluginServer(std::string fileIdentifiers) : throw((std::string)"Failed to mmap shared memory file"); } - sprintf(tmpFileBase, "/tmp/rplugin_shm_%s", + sprintf(tmpFileBase, "/dssi-vst-rplugin_shm_%s", fileIdentifiers.substr(18, 6).c_str()); m_shmFileName = strdup(tmpFileBase); - if ((m_shmFd = open(m_shmFileName, O_RDWR)) < 0) { + if ((m_shmFd = shm_open(m_shmFileName, O_RDWR, 0)) < 0) { tryWrite(m_controlResponseFd, &b, sizeof(bool)); cleanup(); throw((std::string)"Failed to open shared memory file"); From bb5b14e8e6c99434e79f38c7e846321d3502a633 Mon Sep 17 00:00:00 2001 From: Kristian Amlie Date: Tue, 20 Sep 2011 10:03:05 +0200 Subject: [PATCH 8/9] Made the standalone vsthost respond properly to MIDI program changes. The MIDI data for program change should not be passed to the plugin (at least not as per the DSSI spec). Instead, translate them into a function call to set the current program in the synth. --- vsthost.cpp | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) --- vsthost.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/vsthost.cpp b/vsthost.cpp index 6cd09dd..56f4863 100644 --- a/vsthost.cpp +++ b/vsthost.cpp @@ -142,6 +142,13 @@ alsaSeqCallback(snd_seq_t *alsaSeqHandle) continue; } + if (ev->type == SND_SEQ_EVENT_PGMCHANGE) { + pthread_mutex_lock(&pluginMutex); + plugin->setCurrentProgram(ev->data.control.value); + pthread_mutex_unlock(&pluginMutex); + continue; + } + if (midiReadIndex == midiWriteIndex + 1) { fprintf(stderr, "WARNING: MIDI stream buffer overflow\n"); continue; From 947e90751d09422e19b001ab59f26684a8e0445d Mon Sep 17 00:00:00 2001 From: Kristian Amlie Date: Fri, 14 Oct 2011 09:17:15 +0200 Subject: [PATCH 9/9] Switched to using vfork(). It causes less xruns when launching a new plugin instance, even if the launch happens from a non-real time thread. Presumably, fork() blocks all threads in the parent task while the process is cloned, while vfork() doesn't make a copy and therefore doesn't block. --- remotevstclient.cpp | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) --- remotevstclient.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/remotevstclient.cpp b/remotevstclient.cpp index 7fbcbae..9884b2f 100644 --- a/remotevstclient.cpp +++ b/remotevstclient.cpp @@ -74,13 +74,16 @@ RemoteVSTClient::RemoteVSTClient(std::string dllName, bool showGUI) : std::cerr << "RemoteVSTClient: executing " << fileName << " " << argStr << std::endl; - if ((child = fork()) < 0) { + const char* fileNameStr = fileName.c_str(); + if ((child = vfork()) < 0) { cleanup(); throw((std::string)"Fork failed"); } else if (child == 0) { // child process - if (execlp(fileName.c_str(), fileName.c_str(), argStr, NULL)) { + if (execlp(fileNameStr, fileNameStr, argStr, NULL)) { + // vfork() docs say you shouldn't call a function here, + // but it seems to work for me. perror("Exec failed"); - exit(1); + _exit(1); } }