@@ -6,7 +6,7 @@ | |||||
<rect> | <rect> | ||||
<x>0</x> | <x>0</x> | ||||
<y>0</y> | <y>0</y> | ||||
<width>414</width> | |||||
<width>418</width> | |||||
<height>306</height> | <height>306</height> | ||||
</rect> | </rect> | ||||
</property> | </property> | ||||
@@ -314,9 +314,6 @@ | |||||
</item> | </item> | ||||
<item row="0" column="6"> | <item row="0" column="6"> | ||||
<widget class="QLabel" name="label_7"> | <widget class="QLabel" name="label_7"> | ||||
<property name="enabled"> | |||||
<bool>false</bool> | |||||
</property> | |||||
<property name="text"> | <property name="text"> | ||||
<string>MIDI inputs:</string> | <string>MIDI inputs:</string> | ||||
</property> | </property> | ||||
@@ -327,9 +324,6 @@ | |||||
</item> | </item> | ||||
<item row="0" column="7"> | <item row="0" column="7"> | ||||
<widget class="QSpinBox" name="sb_midi_ins"> | <widget class="QSpinBox" name="sb_midi_ins"> | ||||
<property name="enabled"> | |||||
<bool>false</bool> | |||||
</property> | |||||
<property name="maximum"> | <property name="maximum"> | ||||
<number>1</number> | <number>1</number> | ||||
</property> | </property> | ||||
@@ -1094,6 +1094,7 @@ protected: | |||||
const uint8_t size(fShmRtClientControl.readByte()); | const uint8_t size(fShmRtClientControl.readByte()); | ||||
CARLA_SAFE_ASSERT_BREAK(size > 0); | CARLA_SAFE_ASSERT_BREAK(size > 0); | ||||
// FIXME variable-size stack | |||||
uint8_t data[size]; | uint8_t data[size]; | ||||
for (uint8_t i=0; i<size; ++i) | for (uint8_t i=0; i<size; ++i) | ||||
@@ -89,6 +89,8 @@ public: | |||||
: fServer(this), | : fServer(this), | ||||
fAudioPoolCopy(nullptr), | fAudioPoolCopy(nullptr), | ||||
fAudioTmpBuf(nullptr), | fAudioTmpBuf(nullptr), | ||||
fMidiBufferIn(true), | |||||
fMidiBufferOut(false), | |||||
fIsOffline(false), | fIsOffline(false), | ||||
fLastPingTime(-1), | fLastPingTime(-1), | ||||
fRealtimeThread(this), | fRealtimeThread(this), | ||||
@@ -198,6 +200,9 @@ private: | |||||
float* fAudioPoolCopy; | float* fAudioPoolCopy; | ||||
float* fAudioTmpBuf; | float* fAudioTmpBuf; | ||||
JackMidiPortBuffer fMidiBufferIn; | |||||
JackMidiPortBuffer fMidiBufferOut; | |||||
char fBaseNameAudioPool[6+1]; | char fBaseNameAudioPool[6+1]; | ||||
char fBaseNameRtClientControl[6+1]; | char fBaseNameRtClientControl[6+1]; | ||||
char fBaseNameNonRtClientControl[6+1]; | char fBaseNameNonRtClientControl[6+1]; | ||||
@@ -448,9 +453,42 @@ bool CarlaJackAppClient::handleRtData() | |||||
case kPluginBridgeRtClientControlEventMidiProgram: | case kPluginBridgeRtClientControlEventMidiProgram: | ||||
case kPluginBridgeRtClientControlEventAllSoundOff: | case kPluginBridgeRtClientControlEventAllSoundOff: | ||||
case kPluginBridgeRtClientControlEventAllNotesOff: | case kPluginBridgeRtClientControlEventAllNotesOff: | ||||
case kPluginBridgeRtClientMidiEvent: | |||||
break; | break; | ||||
case kPluginBridgeRtClientMidiEvent: { | |||||
const uint32_t time(fShmRtClientControl.readUInt()); | |||||
const uint8_t port(fShmRtClientControl.readByte()); | |||||
const uint8_t size(fShmRtClientControl.readByte()); | |||||
CARLA_SAFE_ASSERT_BREAK(size > 0); | |||||
if (size > JackMidiPortBuffer::kMaxEventSize || ! fRealtimeThreadMutex.tryLock()) | |||||
{ | |||||
for (uint8_t i=0; i<size; ++i) | |||||
fShmRtClientControl.readByte(); | |||||
break; | |||||
} | |||||
if (fMidiBufferIn.count < JackMidiPortBuffer::kMaxEventCount && | |||||
fMidiBufferIn.bufferPoolPos + size < JackMidiPortBuffer::kBufferPoolSize) | |||||
{ | |||||
jack_midi_event_t& ev(fMidiBufferIn.events[fMidiBufferIn.count++]); | |||||
ev.time = time; | |||||
ev.size = size; | |||||
ev.buffer = fMidiBufferIn.bufferPool + fMidiBufferIn.bufferPoolPos; | |||||
fMidiBufferIn.bufferPoolPos += size; | |||||
for (uint8_t i=0; i<size; ++i) | |||||
ev.buffer[i] = fShmRtClientControl.readByte(); | |||||
} | |||||
fRealtimeThreadMutex.unlock(true); | |||||
// TODO multiple midi ports | |||||
(void)port; | |||||
break; | |||||
} | |||||
case kPluginBridgeRtClientProcess: { | case kPluginBridgeRtClientProcess: { | ||||
// FIXME - lock if offline | // FIXME - lock if offline | ||||
const CarlaMutexTryLocker cmtl(fRealtimeThreadMutex); | const CarlaMutexTryLocker cmtl(fRealtimeThreadMutex); | ||||
@@ -578,6 +616,19 @@ bool CarlaJackAppClient::handleRtData() | |||||
if (needsTmpBufClear) | if (needsTmpBufClear) | ||||
FloatVectorOperations::clear(fAudioTmpBuf, fServer.bufferSize); | FloatVectorOperations::clear(fAudioTmpBuf, fServer.bufferSize); | ||||
// set midi buffers | |||||
if (jclient->midiIns.count() > 0) | |||||
{ | |||||
if (JackPortState* const jport = jclient->midiIns.getFirst(nullptr)) | |||||
jport->buffer = &fMidiBufferIn; | |||||
} | |||||
if (jclient->midiOuts.count() > 0) | |||||
{ | |||||
if (JackPortState* const jport = jclient->midiOuts.getFirst(nullptr)) | |||||
jport->buffer = &fMidiBufferOut; | |||||
} | |||||
jclient->processCb(fServer.bufferSize, jclient->processCbPtr); | jclient->processCb(fServer.bufferSize, jclient->processCbPtr); | ||||
if (fNumPorts.audioOuts > 0) | if (fNumPorts.audioOuts > 0) | ||||
@@ -612,6 +663,9 @@ bool CarlaJackAppClient::handleRtData() | |||||
FloatVectorOperations::clear(fdataRealOuts, fServer.bufferSize*fNumPorts.audioOuts); | FloatVectorOperations::clear(fdataRealOuts, fServer.bufferSize*fNumPorts.audioOuts); | ||||
} | } | ||||
fMidiBufferIn.count = 0; | |||||
fMidiBufferIn.bufferPoolPos = 0; | |||||
if (fNumPorts.midiOuts > 0) | if (fNumPorts.midiOuts > 0) | ||||
carla_zeroBytes(fShmRtClientControl.data->midiOut, kBridgeRtClientDataMidiOutSize); | carla_zeroBytes(fShmRtClientControl.data->midiOut, kBridgeRtClientDataMidiOutSize); | ||||
} | } | ||||
@@ -59,6 +59,32 @@ class CarlaJackAppClient; | |||||
struct JackClientState; | struct JackClientState; | ||||
struct JackServerState; | struct JackServerState; | ||||
struct JackMidiPortBuffer { | |||||
static const size_t kMaxEventSize = 128; | |||||
static const size_t kMaxEventCount = 512; | |||||
static const size_t kBufferPoolSize = kMaxEventCount*8; | |||||
uint16_t count; | |||||
bool isInput; | |||||
jack_midi_event_t* events; | |||||
size_t bufferPoolPos; | |||||
jack_midi_data_t* bufferPool; | |||||
JackMidiPortBuffer(const bool input) | |||||
: count(0), | |||||
isInput(input), | |||||
events(new jack_midi_event_t[kMaxEventCount]), | |||||
bufferPoolPos(0), | |||||
bufferPool(new jack_midi_data_t[kBufferPoolSize]) {} | |||||
~JackMidiPortBuffer() | |||||
{ | |||||
delete[] events; | |||||
delete[] bufferPool; | |||||
} | |||||
}; | |||||
struct JackPortState { | struct JackPortState { | ||||
char* name; | char* name; | ||||
char* fullname; | char* fullname; | ||||
@@ -15,7 +15,6 @@ | |||||
* For a full copy of the GNU General Public License see the doc/GPL.txt file. | * For a full copy of the GNU General Public License see the doc/GPL.txt file. | ||||
*/ | */ | ||||
// need to include this first | |||||
#include "libjack.hpp" | #include "libjack.hpp" | ||||
CARLA_BACKEND_USE_NAMESPACE | CARLA_BACKEND_USE_NAMESPACE | ||||
@@ -23,43 +22,90 @@ CARLA_BACKEND_USE_NAMESPACE | |||||
// -------------------------------------------------------------------------------------------------------------------- | // -------------------------------------------------------------------------------------------------------------------- | ||||
CARLA_EXPORT | CARLA_EXPORT | ||||
jack_nframes_t jack_midi_get_event_count(void*) | |||||
jack_nframes_t jack_midi_get_event_count(void* buf) | |||||
{ | { | ||||
return 0; | |||||
JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); | |||||
CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr, 0); | |||||
CARLA_SAFE_ASSERT_RETURN(jmidibuf->isInput, 0); | |||||
return jmidibuf->count; | |||||
} | } | ||||
CARLA_EXPORT | CARLA_EXPORT | ||||
int jack_midi_event_get(jack_midi_event_t*, void*, uint32_t) | |||||
int jack_midi_event_get(jack_midi_event_t* ev, void* buf, uint32_t index) | |||||
{ | { | ||||
return ENODATA; | |||||
JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); | |||||
CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr, EFAULT); | |||||
CARLA_SAFE_ASSERT_RETURN(jmidibuf->isInput, EFAULT); | |||||
CARLA_SAFE_ASSERT_RETURN(index < jmidibuf->count, ENODATA); | |||||
std::memcpy(ev, &jmidibuf->events[index], sizeof(jack_midi_event_t)); | |||||
return 0; | |||||
} | } | ||||
CARLA_EXPORT | CARLA_EXPORT | ||||
void jack_midi_clear_buffer(void*) | |||||
void jack_midi_clear_buffer(void* buf) | |||||
{ | { | ||||
JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); | |||||
CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr,); | |||||
CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput,); | |||||
jmidibuf->count = 0; | |||||
jmidibuf->bufferPoolPos = 0; | |||||
std::memset(jmidibuf->bufferPool, 0, JackMidiPortBuffer::kBufferPoolSize); | |||||
} | } | ||||
CARLA_EXPORT | CARLA_EXPORT | ||||
void jack_midi_reset_buffer(void*) | |||||
void jack_midi_reset_buffer(void* buf) | |||||
{ | { | ||||
jack_midi_clear_buffer(buf); | |||||
} | } | ||||
CARLA_EXPORT | CARLA_EXPORT | ||||
size_t jack_midi_max_event_size(void*) | size_t jack_midi_max_event_size(void*) | ||||
{ | { | ||||
return 0; | |||||
return JackMidiPortBuffer::kMaxEventSize; | |||||
} | } | ||||
CARLA_EXPORT | CARLA_EXPORT | ||||
jack_midi_data_t* jack_midi_event_reserve(void*, jack_nframes_t, size_t) | |||||
jack_midi_data_t* jack_midi_event_reserve(void* buf, jack_nframes_t frame, size_t size) | |||||
{ | { | ||||
return nullptr; | |||||
JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); | |||||
CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr, nullptr); | |||||
CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput, nullptr); | |||||
CARLA_SAFE_ASSERT_RETURN(size > 0, nullptr); | |||||
if (jmidibuf->count >= JackMidiPortBuffer::kMaxEventCount) | |||||
return nullptr; | |||||
if (jmidibuf->bufferPoolPos + size >= JackMidiPortBuffer::kBufferPoolSize) | |||||
return nullptr; | |||||
jack_midi_data_t* const jmdata = jmidibuf->bufferPool + jmidibuf->bufferPoolPos; | |||||
jmidibuf->bufferPoolPos += size; | |||||
jmidibuf->events[jmidibuf->count++] = { frame, size, jmdata }; | |||||
std::memset(jmdata, 0, size); | |||||
return jmdata; | |||||
} | } | ||||
CARLA_EXPORT | CARLA_EXPORT | ||||
int jack_midi_event_write(void*, jack_nframes_t, const jack_midi_data_t*, size_t) | |||||
int jack_midi_event_write(void* buf, jack_nframes_t frame, const jack_midi_data_t* data, size_t size) | |||||
{ | { | ||||
return ENOBUFS; | |||||
JackMidiPortBuffer* const jmidibuf((JackMidiPortBuffer*)buf); | |||||
CARLA_SAFE_ASSERT_RETURN(jmidibuf != nullptr, EFAULT); | |||||
CARLA_SAFE_ASSERT_RETURN(! jmidibuf->isInput, EFAULT); | |||||
if (jmidibuf->count >= JackMidiPortBuffer::kMaxEventCount) | |||||
return ENOBUFS; | |||||
if (jmidibuf->bufferPoolPos + size >= JackMidiPortBuffer::kBufferPoolSize) | |||||
return ENOBUFS; | |||||
jack_midi_data_t* const jmdata = jmidibuf->bufferPool + jmidibuf->bufferPoolPos; | |||||
jmidibuf->bufferPoolPos += size; | |||||
jmidibuf->events[jmidibuf->count++] = { frame, size, jmdata }; | |||||
std::memcpy(jmdata, data, size); | |||||
return 0; | |||||
} | } | ||||
CARLA_EXPORT | CARLA_EXPORT | ||||