diff --git a/source/backend/CarlaEngine.hpp b/source/backend/CarlaEngine.hpp index be239ece9..31b14fc2c 100644 --- a/source/backend/CarlaEngine.hpp +++ b/source/backend/CarlaEngine.hpp @@ -492,6 +492,11 @@ public: */ virtual void initBuffer(CarlaEngine* const engine) override; + /*! + * Clear the port's internal buffer. + */ + virtual void clearBuffer(); + /*! * Get the number of events present in the buffer. * \note You must only call this for input ports. diff --git a/source/backend/CarlaPlugin.hpp b/source/backend/CarlaPlugin.hpp index 35cf3b002..5b27ae658 100644 --- a/source/backend/CarlaPlugin.hpp +++ b/source/backend/CarlaPlugin.hpp @@ -30,6 +30,10 @@ typedef struct _PluginDescriptor PluginDescriptor; CARLA_BACKEND_START_NAMESPACE +#ifndef DOXYGEN +class CarlaEngineEventPort; +#endif + /*! * @defgroup CarlaPluginAPI Carla Plugin API * @@ -838,12 +842,16 @@ protected: CarlaString fName; //!< Plugin name CarlaString fFilename; //!< Plugin filename, if applicable + friend class CarlaEngineBridge; friend struct CarlaPluginProtectedData; CarlaPluginProtectedData* const kData; //!< Internal data, for CarlaPlugin subclasses only. // ------------------------------------------------------------------- // Helper classes + // Get default event input port + virtual CarlaEngineEventPort* getDefaultEventInPort() const; + // Fully disable plugin in scope and also its engine client // May wait-block on constructor for plugin process to end class ScopedDisabler diff --git a/source/backend/engine/CarlaEngine.cpp b/source/backend/engine/CarlaEngine.cpp index 98ec0f52a..269303dab 100644 --- a/source/backend/engine/CarlaEngine.cpp +++ b/source/backend/engine/CarlaEngine.cpp @@ -137,6 +137,17 @@ void CarlaEngineEventPort::initBuffer(CarlaEngine* const engine) carla_zeroStruct(fBuffer, kMaxEventCount); } +void CarlaEngineEventPort::clearBuffer() +{ + CARLA_ASSERT(fBuffer != nullptr); + + if (fBuffer == nullptr) + return; + + if (kProcessMode == PROCESS_MODE_PATCHBAY || kProcessMode == PROCESS_MODE_BRIDGE) + carla_zeroStruct(fBuffer, kMaxEventCount); +} + uint32_t CarlaEngineEventPort::getEventCount() { CARLA_ASSERT(kIsInput); @@ -226,15 +237,19 @@ void CarlaEngineEventPort::writeControlEvent(const uint32_t time, const uint8_t void CarlaEngineEventPort::writeMidiEvent(const uint32_t time, const uint8_t channel, const uint8_t port, const uint8_t* const data, const uint8_t size) { +#ifndef BUILD_BRIDGE CARLA_ASSERT(! kIsInput); +#endif CARLA_ASSERT(fBuffer != nullptr); CARLA_ASSERT(kProcessMode == PROCESS_MODE_CONTINUOUS_RACK || kProcessMode == PROCESS_MODE_PATCHBAY || kProcessMode == PROCESS_MODE_BRIDGE); CARLA_ASSERT(channel < MAX_MIDI_CHANNELS); CARLA_ASSERT(data != nullptr); CARLA_ASSERT(size > 0); +#ifndef BUILD_BRIDGE if (kIsInput) return; +#endif if (fBuffer == nullptr) return; if (kProcessMode != PROCESS_MODE_CONTINUOUS_RACK && kProcessMode != PROCESS_MODE_PATCHBAY && kProcessMode != PROCESS_MODE_BRIDGE) @@ -2052,7 +2067,7 @@ void CarlaEngine::osc_send_control_set_parameter_value(const int32_t pluginId, c void CarlaEngine::osc_send_control_set_default_value(const int32_t pluginId, const int32_t index, const float value) { CARLA_ASSERT(kData->oscData != nullptr); - CARLA_ASSERT(pluginId >= 0 && pluginId < static_cast(kData->curPluginCount)); + CARLA_ASSERT(pluginId >= 0 && pluginId < static_cast(kData->maxPluginNumber)); CARLA_ASSERT(index >= 0); carla_debug("CarlaEngine::osc_send_control_set_default_value(%i, %i, %f)", pluginId, index, value); diff --git a/source/backend/engine/CarlaEngineBridge.cpp b/source/backend/engine/CarlaEngineBridge.cpp index f91fc9614..a6c6ed321 100644 --- a/source/backend/engine/CarlaEngineBridge.cpp +++ b/source/backend/engine/CarlaEngineBridge.cpp @@ -34,7 +34,7 @@ CARLA_BACKEND_START_NAMESPACE } // Fix editor indentation #endif -// ----------------------------------------- +// ------------------------------------------------------------------- class CarlaEngineBridge : public CarlaEngine, public QThread @@ -43,7 +43,8 @@ public: CarlaEngineBridge(const char* const audioBaseName, const char* const controlBaseName) : CarlaEngine(), fIsRunning(false), - fQuitNow(false) + fQuitNow(false), + fEventsInPort(nullptr) { carla_debug("CarlaEngineBridge::CarlaEngineBridge()"); @@ -179,10 +180,18 @@ public: case kPluginBridgeOpcodeNull: break; - case kPluginBridgeOpcodeReadyWait: + case kPluginBridgeOpcodeSetAudioPool: { - const int size(rdwr_readInt(&fShmControl.data->ringBuffer)); - fShmAudioPool.data = (float*)carla_shm_map(fShmAudioPool.shm, size); + const int poolSize(rdwr_readInt(&fShmControl.data->ringBuffer)); + fShmAudioPool.data = (float*)carla_shm_map(fShmAudioPool.shm, poolSize); + + fEventsInPort = nullptr; + + CarlaPlugin* const plugin(getPluginUnchecked(0)); + + if (plugin != nullptr && plugin->enabled()) + fEventsInPort = plugin->getDefaultEventInPort(); + break; } @@ -246,6 +255,25 @@ public: break; } + case kPluginBridgeOpcodeMidiEvent: + { + uint8_t data[4] = { 0 }; + const long time(rdwr_readLong(&fShmControl.data->ringBuffer)); + const int dataSize(rdwr_readInt(&fShmControl.data->ringBuffer)); + + CARLA_ASSERT_INT(dataSize >= 1 && dataSize <= 4, dataSize); + + for (int i=0; i < dataSize && i < 4; ++i) + data[i] = rdwr_readChar(&fShmControl.data->ringBuffer); + + CARLA_ASSERT(fEventsInPort != nullptr); + + if (fEventsInPort != nullptr) + fEventsInPort->writeMidiEvent(time, data, dataSize); + + break; + } + case kPluginBridgeOpcodeProcess: { CARLA_ASSERT(fShmAudioPool.data != nullptr); @@ -267,6 +295,9 @@ public: plugin->initBuffers(); plugin->process(inBuffer, outBuffer, fBufferSize); plugin->unlock(); + + if (fEventsInPort != nullptr) + fEventsInPort->clearBuffer(); } break; } @@ -313,6 +344,8 @@ private: bool fIsRunning; bool fQuitNow; + CarlaEngineEventPort* fEventsInPort; + void _cleanup() { if (fShmAudioPool.filename.isNotEmpty()) diff --git a/source/backend/plugin/BridgePlugin.cpp b/source/backend/plugin/BridgePlugin.cpp index 47c510201..78f536100 100644 --- a/source/backend/plugin/BridgePlugin.cpp +++ b/source/backend/plugin/BridgePlugin.cpp @@ -88,7 +88,7 @@ shm_t shm_mkstemp(char* const fileBase) while (true) { - for (int c = size - 6; c < size; c++) + for (int c = size - 6; c < size; ++c) { // Note the -1 to avoid the trailing '\0' in charSet. fileBase[c] = charSet[std::rand() % (sizeof(charSet) - 1)]; @@ -110,6 +110,8 @@ struct BridgeParamInfo { BridgeParamInfo() : value(0.0f) {} + + CARLA_DECLARE_NON_COPY_STRUCT_WITH_LEAK_DETECTOR(BridgeParamInfo) }; class BridgePlugin : public CarlaPlugin @@ -332,10 +334,12 @@ public: rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeSetParameter); rdwr_writeInt(&fShmControl.data->ringBuffer, parameterId); rdwr_writeFloat(&fShmControl.data->ringBuffer, value); - rdwr_commitWrite(&fShmControl.data->ringBuffer); if (doLock) + { + rdwr_commitWrite(&fShmControl.data->ringBuffer); kData->singleMutex.unlock(); + } CarlaPlugin::setParameterValue(parameterId, fixedValue, sendGui, sendOsc, sendCallback); } @@ -356,10 +360,12 @@ public: rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeSetProgram); rdwr_writeInt(&fShmControl.data->ringBuffer, index); - rdwr_commitWrite(&fShmControl.data->ringBuffer); if (doLock) + { + rdwr_commitWrite(&fShmControl.data->ringBuffer); kData->singleMutex.unlock(); + } CarlaPlugin::setProgram(index, sendGui, sendOsc, sendCallback); } @@ -380,10 +386,12 @@ public: rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeSetMidiProgram); rdwr_writeInt(&fShmControl.data->ringBuffer, index); - rdwr_commitWrite(&fShmControl.data->ringBuffer); if (doLock) + { + rdwr_commitWrite(&fShmControl.data->ringBuffer); kData->singleMutex.unlock(); + } CarlaPlugin::setMidiProgram(index, sendGui, sendOsc, sendCallback); } @@ -583,6 +591,7 @@ public: void activate() override { + // already locked before rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeSetParameter); rdwr_writeInt(&fShmControl.data->ringBuffer, PARAMETER_ACTIVE); rdwr_writeFloat(&fShmControl.data->ringBuffer, 1.0f); @@ -592,6 +601,7 @@ public: void deactivate() override { + // already locked before rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeSetParameter); rdwr_writeInt(&fShmControl.data->ringBuffer, PARAMETER_ACTIVE); rdwr_writeFloat(&fShmControl.data->ringBuffer, 0.0f); @@ -601,7 +611,7 @@ public: void process(float** const inBuffer, float** const outBuffer, const uint32_t frames) override { - uint32_t i/*, k*/; + uint32_t i, k; // -------------------------------------------------------------------------------------------------------- // Check if active @@ -626,13 +636,204 @@ public: } // -------------------------------------------------------------------------------------------------------- - // Plugin processing (no events) + // Event Input - //else + if (kData->event.portIn != nullptr) { - processSingle(inBuffer, outBuffer, frames); + // ---------------------------------------------------------------------------------------------------- + // MIDI Input (External) + + if (kData->extNotes.mutex.tryLock()) + { + while (! kData->extNotes.data.isEmpty()) + { + const ExternalMidiNote& note(kData->extNotes.data.getFirst(true)); + + CARLA_ASSERT(note.channel >= 0 && note.channel < MAX_MIDI_CHANNELS); + + char data1, data2, data3; + data1 = (note.velo > 0) ? MIDI_STATUS_NOTE_ON : MIDI_STATUS_NOTE_OFF; + data1 += note.channel; + data2 = note.note; + data3 = note.velo; + + rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeMidiEvent); + rdwr_writeLong(&fShmControl.data->ringBuffer, 0); + rdwr_writeInt(&fShmControl.data->ringBuffer, 3); + rdwr_writeChar(&fShmControl.data->ringBuffer, data1); + rdwr_writeChar(&fShmControl.data->ringBuffer, data2); + rdwr_writeChar(&fShmControl.data->ringBuffer, data3); + } + + kData->extNotes.mutex.unlock(); + + } // End of MIDI Input (External) + + // ---------------------------------------------------------------------------------------------------- + // Event Input (System) + + bool allNotesOffSent = false; + + uint32_t nEvents = kData->event.portIn->getEventCount(); + uint32_t nextBankId = 0; + + if (kData->midiprog.current >= 0 && kData->midiprog.count > 0) + nextBankId = kData->midiprog.data[kData->midiprog.current].bank; + + for (i=0; i < nEvents; ++i) + { + const EngineEvent& event(kData->event.portIn->getEvent(i)); + + // Control change + switch (event.type) + { + case kEngineEventTypeNull: + break; + + case kEngineEventTypeControl: + { + const EngineControlEvent& ctrlEvent = event.ctrl; + + switch (ctrlEvent.type) + { + case kEngineControlEventTypeNull: + break; + + case kEngineControlEventTypeParameter: + { + // Control plugin parameters + for (k=0; k < kData->param.count; ++k) + { + if (kData->param.data[k].midiChannel != event.channel) + continue; + if (kData->param.data[k].midiCC != ctrlEvent.param) + continue; + if (kData->param.data[k].type != PARAMETER_INPUT) + continue; + if ((kData->param.data[k].hints & PARAMETER_IS_AUTOMABLE) == 0) + continue; + + float value; + + if (kData->param.data[k].hints & PARAMETER_IS_BOOLEAN) + { + value = (ctrlEvent.value < 0.5f) ? kData->param.ranges[k].min : kData->param.ranges[k].max; + } + else + { + value = kData->param.ranges[i].unnormalizeValue(ctrlEvent.value); + + if (kData->param.data[k].hints & PARAMETER_IS_INTEGER) + value = std::rint(value); + } + + setParameterValue(k, value, false, false, false); + postponeRtEvent(kPluginPostRtEventParameterChange, static_cast(k), 0, value); + } + + break; + } - } // End of Plugin processing (no events) + case kEngineControlEventTypeMidiBank: + if (event.channel == kData->ctrlChannel && (fOptions & PLUGIN_OPTION_MAP_PROGRAM_CHANGES) != 0) + nextBankId = ctrlEvent.param; + break; + + case kEngineControlEventTypeMidiProgram: + if (event.channel == kData->ctrlChannel && (fOptions & PLUGIN_OPTION_MAP_PROGRAM_CHANGES) != 0) + { + const uint32_t nextProgramId(ctrlEvent.param); + + if (kData->midiprog.count > 0) + { + for (k=0; k < kData->midiprog.count; ++k) + { + if (kData->midiprog.data[k].bank == nextBankId && kData->midiprog.data[k].program == nextProgramId) + { + setMidiProgram(k, false, false, false); + postponeRtEvent(kPluginPostRtEventMidiProgramChange, k, 0, 0.0f); + break; + } + } + } + else + { + } + } + break; + + case kEngineControlEventTypeAllSoundOff: + if (fOptions & PLUGIN_OPTION_SEND_ALL_SOUND_OFF) + { + // TODO + } + break; + + case kEngineControlEventTypeAllNotesOff: + if (fOptions & PLUGIN_OPTION_SEND_ALL_SOUND_OFF) + { + if (event.channel == kData->ctrlChannel && ! allNotesOffSent) + { + allNotesOffSent = true; + sendMidiAllNotesOffToCallback(); + } + + // TODO + } + break; + } + + break; + } + + case kEngineEventTypeMidi: + { + const EngineMidiEvent& midiEvent(event.midi); + + uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent.data); + uint8_t channel = event.channel; + + if (MIDI_IS_STATUS_AFTERTOUCH(status) && (fOptions & PLUGIN_OPTION_SEND_CHANNEL_PRESSURE) == 0) + continue; + if (MIDI_IS_STATUS_CONTROL_CHANGE(status) && (fOptions & PLUGIN_OPTION_SEND_CONTROL_CHANGES) == 0) + continue; + if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status) && (fOptions & PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH) == 0) + continue; + if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status) && (fOptions & PLUGIN_OPTION_SEND_PITCHBEND) == 0) + continue; + + // Fix bad note-off + if (status == MIDI_STATUS_NOTE_ON && midiEvent.data[2] == 0) + status -= 0x10; + + char data[4]; + data[0] = status + channel; + data[1] = midiEvent.data[1]; + data[2] = midiEvent.data[2]; + data[3] = midiEvent.data[3]; + + rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeMidiEvent); + rdwr_writeLong(&fShmControl.data->ringBuffer, event.time); + rdwr_writeInt(&fShmControl.data->ringBuffer, midiEvent.size); + + for (uint8_t j=0; j < midiEvent.size && j < 4; ++j) + rdwr_writeChar(&fShmControl.data->ringBuffer, data[j]); + + if (status == MIDI_STATUS_NOTE_ON) + postponeRtEvent(kPluginPostRtEventNoteOn, channel, midiEvent.data[1], midiEvent.data[2]); + else if (status == MIDI_STATUS_NOTE_OFF) + postponeRtEvent(kPluginPostRtEventNoteOff, channel, midiEvent.data[1], 0.0f); + + break; + } + } + } + + kData->postRtEvents.trySplice(); + + } // End of Event Input + + processSingle(inBuffer, outBuffer, frames); } bool processSingle(float** const inBuffer, float** const outBuffer, const uint32_t frames) @@ -684,7 +885,11 @@ public: rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeProcess); rdwr_commitWrite(&fShmControl.data->ringBuffer); - waitForServer(); + if (! waitForServer()) + { + kData->singleMutex.unlock(); + return true; + } for (i=0; i < fInfo.aOuts; ++i) carla_copyFloat(outBuffer[i], fShmAudioPool.data + ((i + fInfo.aIns) * frames), frames); @@ -1385,7 +1590,7 @@ public: carla_msleep(50); } - if (! fInitiated) + if (fInitError || ! fInitiated) { // unregister so it gets handled properly registerEnginePlugin(kData->engine, fId, nullptr); @@ -1395,20 +1600,9 @@ public: if (kData->osc.thread.isRunning()) kData->osc.thread.terminate(); - kData->engine->setLastError("Timeout while waiting for a response from plugin-bridge\n(or the plugin crashed on initialization?)"); - return false; - } - else if (fInitError) - { - // unregister so it gets handled properly - registerEnginePlugin(kData->engine, fId, nullptr); - - kData->osc.thread.quit(); + if (! fInitError) + kData->engine->setLastError("Timeout while waiting for a response from plugin-bridge\n(or the plugin crashed on initialization?)"); - if (kData->osc.thread.isRunning()) - kData->osc.thread.terminate(); - - // last error was set before return false; } @@ -1526,14 +1720,14 @@ private: fShmAudioPool.data = (float*)carla_shm_map(fShmAudioPool.shm, fShmAudioPool.size); - rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeReadyWait); + rdwr_writeOpcode(&fShmControl.data->ringBuffer, kPluginBridgeOpcodeSetAudioPool); rdwr_writeInt(&fShmControl.data->ringBuffer, fShmAudioPool.size); rdwr_commitWrite(&fShmControl.data->ringBuffer); waitForServer(); } - void waitForServer() + bool waitForServer() { sem_post(&fShmControl.data->runServer); @@ -1541,7 +1735,10 @@ private: { carla_stderr("waitForServer() timeout"); kData->active = false; // TODO + return false; } + + return true; } CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(BridgePlugin) diff --git a/source/backend/plugin/CarlaPlugin.cpp b/source/backend/plugin/CarlaPlugin.cpp index d202502e1..5dc167944 100644 --- a/source/backend/plugin/CarlaPlugin.cpp +++ b/source/backend/plugin/CarlaPlugin.cpp @@ -2102,6 +2102,14 @@ void CarlaPlugin::uiNoteOff(const uint8_t channel, const uint8_t note) (void)note; } +// ------------------------------------------------------------------- +// Helpers + +CarlaEngineEventPort* CarlaPlugin::getDefaultEventInPort() const +{ + return kData->event.portIn; +} + // ------------------------------------------------------------------- // Scoped Disabler diff --git a/source/backend/plugin/Lv2Plugin.cpp b/source/backend/plugin/Lv2Plugin.cpp index 3ff062ee0..309cd0a19 100644 --- a/source/backend/plugin/Lv2Plugin.cpp +++ b/source/backend/plugin/Lv2Plugin.cpp @@ -3443,6 +3443,14 @@ public: } // ------------------------------------------------------------------- + // Helpers + + CarlaEngineEventPort* getDefaultEventInPort() const override + { + return (fEventsIn.ctrl != nullptr) ? fEventsIn.ctrl->port : nullptr; + } + + // ------------------------------------------------------------------- protected: void guiClosedCallback() override diff --git a/source/utils/CarlaBridgeUtils.hpp b/source/utils/CarlaBridgeUtils.hpp index 0b0333ea0..3a5c8c0c6 100644 --- a/source/utils/CarlaBridgeUtils.hpp +++ b/source/utils/CarlaBridgeUtils.hpp @@ -53,13 +53,13 @@ enum PluginBridgeInfoType { enum PluginBridgeOpcode { kPluginBridgeOpcodeNull = 0, - kPluginBridgeOpcodeReadyWait = 1, + kPluginBridgeOpcodeSetAudioPool = 1, // int kPluginBridgeOpcodeSetBufferSize = 2, // int kPluginBridgeOpcodeSetSampleRate = 3, // float kPluginBridgeOpcodeSetParameter = 4, // int, float kPluginBridgeOpcodeSetProgram = 5, // int kPluginBridgeOpcodeSetMidiProgram = 6, // int - kPluginBridgeOpcodeMidiEvent = 7, // int, char, ... (int = size, max 4) + kPluginBridgeOpcodeMidiEvent = 7, // long, int, char, ... (long = timeFrame, int = size max 4) kPluginBridgeOpcodeProcess = 8, kPluginBridgeOpcodeQuit = 9 }; @@ -102,11 +102,11 @@ struct BridgeShmControl { // Let's make sure there's plenty of room for either one. union { sem_t runServer; - char _alignServer[32]; + char _alignServer[128]; }; union { sem_t runClient; - char _alignClient[32]; + char _alignClient[128]; }; BridgeRingBuffer ringBuffer; }; @@ -167,6 +167,11 @@ const char* PluginBridgeInfoType2str(const PluginBridgeInfoType type) static inline void rdwr_tryRead(BridgeRingBuffer* const ringbuf, void* const buf, const size_t size) { + CARLA_ASSERT(buf != nullptr); + + if (buf == nullptr) + return; + char* const charbuf(static_cast(buf)); size_t tail = ringbuf->tail; @@ -200,6 +205,11 @@ void rdwr_tryRead(BridgeRingBuffer* const ringbuf, void* const buf, const size_t static inline void rdwr_tryWrite(BridgeRingBuffer* const ringbuf, const void* const buf, const size_t size) { + CARLA_ASSERT(buf != nullptr); + + if (buf == nullptr) + return; + const char* const charbuf(static_cast(buf)); size_t written = ringbuf->written; @@ -259,11 +269,19 @@ bool rdwr_dataAvailable(BridgeRingBuffer* const ringbuf) static inline PluginBridgeOpcode rdwr_readOpcode(BridgeRingBuffer* const ringbuf) { - int i = kPluginBridgeOpcodeNull; + int i = static_cast(kPluginBridgeOpcodeNull); rdwr_tryRead(ringbuf, &i, sizeof(int)); return static_cast(i); } +static inline +char rdwr_readChar(BridgeRingBuffer* const ringbuf) +{ + char c = 0; + rdwr_tryRead(ringbuf, &c, sizeof(char)); + return c; +} + static inline int rdwr_readInt(BridgeRingBuffer* const ringbuf) { @@ -272,6 +290,14 @@ int rdwr_readInt(BridgeRingBuffer* const ringbuf) return i; } +static inline +long rdwr_readLong(BridgeRingBuffer* const ringbuf) +{ + long l = 0; + rdwr_tryRead(ringbuf, &l, sizeof(long)); + return l; +} + static inline float rdwr_readFloat(BridgeRingBuffer* const ringbuf) { @@ -285,8 +311,14 @@ float rdwr_readFloat(BridgeRingBuffer* const ringbuf) static inline void rdwr_writeOpcode(BridgeRingBuffer* const ringbuf, const PluginBridgeOpcode opcode) { - const int i = opcode; - rdwr_tryWrite(ringbuf, &i, sizeof(int)); + const int intcode(static_cast(opcode)); + rdwr_tryWrite(ringbuf, &intcode, sizeof(int)); +} + +static inline +void rdwr_writeChar(BridgeRingBuffer* const ringbuf, const char value) +{ + rdwr_tryWrite(ringbuf, &value, sizeof(char)); } static inline @@ -295,6 +327,12 @@ void rdwr_writeInt(BridgeRingBuffer* const ringbuf, const int value) rdwr_tryWrite(ringbuf, &value, sizeof(int)); } +static inline +void rdwr_writeLong(BridgeRingBuffer* const ringbuf, const long value) +{ + rdwr_tryWrite(ringbuf, &value, sizeof(long)); +} + static inline void rdwr_writeFloat(BridgeRingBuffer* const ringbuf, const float value) {