Skip to content

Commit

Permalink
Switched to using shared memory for process communication.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
falkTX committed Aug 27, 2012
1 parent 2dda7dc commit 2cefab1
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 149 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ LINK_FLAGS = -lz $(LDFLAGS)
LINK_PLUGIN = -shared $(shell pkg-config --libs alsa jack) $(LINK_FLAGS)
LINK_HOST = $(shell pkg-config --libs alsa jack) $(LINK_FLAGS)
LINK_GUI = $(shell pkg-config --libs liblo) $(LINK_FLAGS)
LINK_WINE = -m32 -L/usr/lib32/wine -L/usr/lib/i386-linux-gnu/wine -lpthread $(LINK_FLAGS)
LINK_WINE = -m32 -L/usr/lib32/wine -L/usr/lib/i386-linux-gnu/wine -lpthread -lrt $(LINK_FLAGS)

TARGETS = dssi-vst.so dssi-vst_gui vsthost dssi-vst-scanner.exe dssi-vst-server.exe

Expand Down
6 changes: 0 additions & 6 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------
Expand Down
3 changes: 1 addition & 2 deletions dssi-vst.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,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;
Expand Down Expand Up @@ -505,7 +504,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);
Expand Down
160 changes: 137 additions & 23 deletions rdwrops.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@

#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <zlib.h>
#include <cstdio>
#include <iostream>

//#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;
Expand Down Expand Up @@ -52,7 +54,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);
Expand All @@ -76,22 +78,92 @@ 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<char *>(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<const char *>(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 <typename T> 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 <typename T> 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 <typename T> std::string
rdwr_readString(T fd, const char *file, int line)
{
int len;
static char *buf = 0;
Expand All @@ -107,36 +179,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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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 <typename T> 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];
Expand All @@ -151,8 +223,8 @@ rdwr_readMIDIData(int fd, int **frameoffsets, int &events, const char *file, int
}

//Deryabin Andrew: vst chunks support
extern void
rdwr_writeRaw(int fd, std::vector<char> rawdata, const char *file, int line)
template <typename T> void
rdwr_writeRaw(T fd, std::vector<char> rawdata, const char *file, int line)
{
unsigned long complen = compressBound(rawdata.size());
char *compressed = new char [complen];
Expand Down Expand Up @@ -182,8 +254,8 @@ rdwr_writeRaw(int fd, std::vector<char> rawdata, const char *file, int line)
delete [] compressed;
}

extern std::vector<char>
rdwr_readRaw(int fd, const char *file, int line)
template <typename T> std::vector<char>
rdwr_readRaw(T fd, const char *file, int line)
{
int complen, len;
static char *rawbuf = 0;
Expand Down Expand Up @@ -227,3 +299,45 @@ rdwr_readRaw(int fd, const char *file, int line)
return rawout;
}
//Deryabin Andrew: vst chunks support: end code

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_writeRaw(int fd, std::vector<char> rawdata, const char *file, int line);
template
std::vector<char> rdwr_readRaw(int fd, 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);
template
void rdwr_writeRaw(RingBuffer *ringbuf, std::vector<char> rawdata, const char *file, int line);
template
std::vector<char> rdwr_readRaw(RingBuffer *ringbuf, const char *file, int line);
72 changes: 57 additions & 15 deletions rdwrops.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,64 @@
#include <vector>
#include "remoteplugin.h"

#include <semaphore.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);
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);

//Deryabin Andrew: vst chunks support
extern void rdwr_writeRaw(int fd, std::vector<char>, const char *file, int line);
extern std::vector<char> rdwr_readRaw(int fd, const char *file, int line);
//Deryabin Andrew: vst chunks support: end code
#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 <typename T>
void rdwr_writeOpcode(T fd, RemotePluginOpcode opcode, const char *file, int line);
template <typename T>
void rdwr_writeString(T fd, const std::string &str, const char *file, int line);
template <typename T>
std::string rdwr_readString(T fd, const char *file, int line);
template <typename T>
void rdwr_writeInt(T fd, int i, const char *file, int line);
template <typename T>
int rdwr_readInt(T fd, const char *file, int line);
template <typename T>
void rdwr_writeFloat(T fd, float f, const char *file, int line);
template <typename T>
float rdwr_readFloat(T fd, const char *file, int line);
template <typename T>
unsigned char *rdwr_readMIDIData(T fd, int **frameoffsets, int &events, const char *file, int line);
template <typename T>
void rdwr_writeRaw(T fd, std::vector<char> rawdata, const char *file, int line);
template <typename T>
std::vector<char> rdwr_readRaw(T fd, 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__)
Expand All @@ -41,6 +81,8 @@ extern std::vector<char> rdwr_readRaw(int fd, const char *file, int line);
#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__)

//Deryabin Andrew: chunks support
#define writeRaw(a, b) rdwr_writeRaw(a, b, __FILE__, __LINE__)
Expand Down
Loading

0 comments on commit 2cefab1

Please sign in to comment.