@@ -798,8 +798,8 @@ public: | |||
static CarlaPlugin* newLV2(const Initializer& init); | |||
static CarlaPlugin* newVST(const Initializer& init); | |||
static CarlaPlugin* newGIG(const Initializer& init); | |||
static CarlaPlugin* newSF2(const Initializer& init, const bool use16Outs); | |||
static CarlaPlugin* newSFZ(const Initializer& init); | |||
static CarlaPlugin* newSF2(const Initializer& init); | |||
// ------------------------------------------------------------------- | |||
@@ -713,7 +713,7 @@ bool CarlaEngine::addPlugin(const BinaryType btype, const PluginType ptype, cons | |||
break; | |||
case PLUGIN_SF2: | |||
plugin = CarlaPlugin::newSF2(init); | |||
plugin = CarlaPlugin::newSF2(init, (extra != nullptr)); | |||
break; | |||
case PLUGIN_SFZ: | |||
@@ -49,7 +49,7 @@ public: | |||
kClient(client), | |||
kPort(port) | |||
{ | |||
qDebug("CarlaEngineJackAudioPort::CarlaEngineJackAudioPort(%s, %s, %p, %p)", bool2str(isInput), ProcessMode2Str(processMode), client, port); | |||
carla_debug("CarlaEngineJackAudioPort::CarlaEngineJackAudioPort(%s, %s, %p, %p)", bool2str(isInput), ProcessMode2Str(processMode), client, port); | |||
if (processMode == PROCESS_MODE_SINGLE_CLIENT || processMode == PROCESS_MODE_MULTIPLE_CLIENTS) | |||
{ | |||
@@ -63,7 +63,7 @@ public: | |||
~CarlaEngineJackAudioPort() | |||
{ | |||
qDebug("CarlaEngineJackAudioPort::~CarlaEngineJackAudioPort()"); | |||
carla_debug("CarlaEngineJackAudioPort::~CarlaEngineJackAudioPort()"); | |||
if (kClient != nullptr && kPort != nullptr) | |||
jackbridge_port_unregister(kClient, kPort); | |||
@@ -111,7 +111,7 @@ public: | |||
kPort(port), | |||
fJackBuffer(nullptr) | |||
{ | |||
qDebug("CarlaEngineJackEventPort::CarlaEngineJackEventPort(%s, %s, %p, %p)", bool2str(isInput), ProcessMode2Str(processMode), client, port); | |||
carla_debug("CarlaEngineJackEventPort::CarlaEngineJackEventPort(%s, %s, %p, %p)", bool2str(isInput), ProcessMode2Str(processMode), client, port); | |||
if (processMode == PROCESS_MODE_SINGLE_CLIENT || processMode == PROCESS_MODE_MULTIPLE_CLIENTS) | |||
{ | |||
@@ -125,7 +125,7 @@ public: | |||
~CarlaEngineJackEventPort() | |||
{ | |||
qDebug("CarlaEngineJackEventPort::~CarlaEngineJackEventPort()"); | |||
carla_debug("CarlaEngineJackEventPort::~CarlaEngineJackEventPort()"); | |||
if (kClient != nullptr && kPort != nullptr) | |||
jackbridge_port_unregister(kClient, kPort); | |||
@@ -368,7 +368,7 @@ public: | |||
kClient(client), | |||
kUseClient(processMode == PROCESS_MODE_SINGLE_CLIENT || processMode == PROCESS_MODE_MULTIPLE_CLIENTS) | |||
{ | |||
qDebug("CarlaEngineJackClient::CarlaEngineJackClient(%s, %s, %p)", EngineType2Str(engineType), ProcessMode2Str(processMode), client); | |||
carla_debug("CarlaEngineJackClient::CarlaEngineJackClient(%s, %s, %p)", EngineType2Str(engineType), ProcessMode2Str(processMode), client); | |||
if (kUseClient) | |||
{ | |||
@@ -382,7 +382,7 @@ public: | |||
~CarlaEngineJackClient() | |||
{ | |||
qDebug("CarlaEngineClient::~CarlaEngineClient()"); | |||
carla_debug("CarlaEngineClient::~CarlaEngineClient()"); | |||
if (kProcessMode == PROCESS_MODE_MULTIPLE_CLIENTS) | |||
{ | |||
@@ -393,7 +393,7 @@ public: | |||
void activate() | |||
{ | |||
qDebug("CarlaEngineJackClient::activate()"); | |||
carla_debug("CarlaEngineJackClient::activate()"); | |||
if (kProcessMode == PROCESS_MODE_MULTIPLE_CLIENTS) | |||
{ | |||
@@ -408,7 +408,7 @@ public: | |||
void deactivate() | |||
{ | |||
qDebug("CarlaEngineJackClient::deactivate()"); | |||
carla_debug("CarlaEngineJackClient::deactivate()"); | |||
if (kProcessMode == PROCESS_MODE_MULTIPLE_CLIENTS) | |||
{ | |||
@@ -423,7 +423,7 @@ public: | |||
bool isOk() const | |||
{ | |||
qDebug("CarlaEngineJackClient::isOk()"); | |||
carla_debug("CarlaEngineJackClient::isOk()"); | |||
if (kUseClient) | |||
return bool(kClient); | |||
@@ -441,7 +441,7 @@ public: | |||
const CarlaEnginePort* addPort(const EnginePortType portType, const char* const name, const bool isInput) | |||
{ | |||
qDebug("CarlaEngineJackClient::addPort(%s, \"%s\", %s)", EnginePortType2Str(portType), name, bool2str(isInput)); | |||
carla_debug("CarlaEngineJackClient::addPort(%s, \"%s\", %s)", EnginePortType2Str(portType), name, bool2str(isInput)); | |||
jack_port_t* port = nullptr; | |||
@@ -500,7 +500,7 @@ public: | |||
#endif | |||
fFreewheel(false) | |||
{ | |||
qDebug("CarlaEngineJack::CarlaEngineJack()"); | |||
carla_debug("CarlaEngineJack::CarlaEngineJack()"); | |||
#ifdef BUILD_BRIDGE | |||
fOptions.processMode = PROCESS_MODE_MULTIPLE_CLIENTS; | |||
@@ -511,7 +511,7 @@ public: | |||
~CarlaEngineJack() | |||
{ | |||
qDebug("CarlaEngineJack::~CarlaEngineJack()"); | |||
carla_debug("CarlaEngineJack::~CarlaEngineJack()"); | |||
CARLA_ASSERT(fClient == nullptr); | |||
} | |||
@@ -539,7 +539,7 @@ public: | |||
bool init(const char* const clientName) | |||
{ | |||
qDebug("CarlaEngineJack::init(\"%s\")", clientName); | |||
carla_debug("CarlaEngineJack::init(\"%s\")", clientName); | |||
fFreewheel = false; | |||
fTransportState = JackTransportStopped; | |||
@@ -607,7 +607,7 @@ public: | |||
bool close() | |||
{ | |||
qDebug("CarlaEngineJack::close()"); | |||
carla_debug("CarlaEngineJack::close()"); | |||
CarlaEngine::close(); | |||
#ifdef BUILD_BRIDGE | |||
@@ -53,10 +53,10 @@ public: | |||
{ | |||
showGui(false); | |||
// Wait a bit first, try safe quit, then force kill | |||
// Wait a bit first, then force kill | |||
if (kData->osc.thread.isRunning() && ! kData->osc.thread.stop(kData->engine->getOptions().oscUiTimeout)) | |||
{ | |||
carla_stderr("Failed to properly stop DSSI GUI thread"); | |||
carla_stderr("DSSI GUI thread still running, forcing termination now"); | |||
kData->osc.thread.terminate(); | |||
} | |||
} | |||
@@ -1170,7 +1170,7 @@ public: | |||
const EngineMidiEvent& midiEvent = event.midi; | |||
uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent.data); | |||
uint8_t channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.data); | |||
uint8_t channel = event.channel; | |||
// Fix bad note-off (per DSSI spec) | |||
if (MIDI_IS_STATUS_NOTE_ON(status) && midiEvent.data[2] == 0) | |||
@@ -28,15 +28,22 @@ CARLA_BACKEND_START_NAMESPACE | |||
class FluidSynthPlugin : public CarlaPlugin | |||
{ | |||
public: | |||
FluidSynthPlugin(CarlaEngine* const engine, const unsigned int id) | |||
: CarlaPlugin(engine, id) | |||
FluidSynthPlugin(CarlaEngine* const engine, const unsigned int id, const bool use16Outs) | |||
: CarlaPlugin(engine, id), | |||
kUses16Outs(use16Outs), | |||
fSettings(nullptr), | |||
fSynth(nullptr), | |||
fSynthId(-1), | |||
fAudio16Buffers(nullptr) | |||
{ | |||
carla_debug("FluidSynthPlugin::FluidSynthPlugin()"); | |||
carla_debug("FluidSynthPlugin::FluidSynthPlugin(%p, %i, %s)", engine, id, bool2str(use16Outs)); | |||
// create settings | |||
fSettings = new_fluid_settings(); | |||
// define settings | |||
fluid_settings_setint(fSettings, "synth.audio-channels", use16Outs ? 16 : 1); | |||
fluid_settings_setint(fSettings, "synth.audio-groups", use16Outs ? 16 : 1); | |||
fluid_settings_setnum(fSettings, "synth.sample-rate", kData->engine->getSampleRate()); | |||
fluid_settings_setint(fSettings, "synth.threadsafe-api ", 0); | |||
@@ -66,6 +73,8 @@ public: | |||
delete_fluid_synth(fSynth); | |||
delete_fluid_settings(fSettings); | |||
deleteBuffers(); | |||
} | |||
// ------------------------------------------------------------------- | |||
@@ -400,7 +409,7 @@ public: | |||
deleteBuffers(); | |||
uint32_t aOuts, params, j; | |||
aOuts = 2; | |||
aOuts = kUses16Outs ? 32 : 2; | |||
params = FluidSynthParametersMax; | |||
kData->audioOut.createNew(aOuts); | |||
@@ -412,7 +421,44 @@ public: | |||
// --------------------------------------- | |||
// Audio Outputs | |||
if (kUses16Outs) | |||
{ | |||
for (j=0; j < 32; j++) | |||
{ | |||
portName.clear(); | |||
if (processMode == PROCESS_MODE_SINGLE_CLIENT) | |||
{ | |||
portName = fName; | |||
portName += ":"; | |||
} | |||
portName += "out-"; | |||
if ((j+2)/2 < 9) | |||
portName += "0"; | |||
portName += CarlaString((j+2)/2); | |||
if (j % 2 == 0) | |||
portName += "L"; | |||
else | |||
portName += "R"; | |||
portName.truncate(portNameSize); | |||
kData->audioOut.ports[j].port = (CarlaEngineAudioPort*)kData->client->addPort(kEnginePortTypeAudio, portName, false); | |||
kData->audioOut.ports[j].rindex = j; | |||
} | |||
fAudio16Buffers = new float*[aOuts]; | |||
for (j=0; j < aOuts; j++) | |||
fAudio16Buffers[j] = nullptr; | |||
} | |||
else | |||
{ | |||
// out-left | |||
portName.clear(); | |||
if (processMode == PROCESS_MODE_SINGLE_CLIENT) | |||
@@ -426,9 +472,8 @@ public: | |||
kData->audioOut.ports[0].port = (CarlaEngineAudioPort*)kData->client->addPort(kEnginePortTypeAudio, portName, false); | |||
kData->audioOut.ports[0].rindex = 0; | |||
} | |||
{ | |||
// out-right | |||
portName.clear(); | |||
if (processMode == PROCESS_MODE_SINGLE_CLIENT) | |||
@@ -714,6 +759,7 @@ public: | |||
fHints |= PLUGIN_CAN_BALANCE; | |||
fHints |= PLUGIN_CAN_FORCE_STEREO; | |||
bufferSizeChanged(kData->engine->getBufferSize()); | |||
reloadPrograms(true); | |||
kData->client->activate(); | |||
@@ -819,7 +865,6 @@ public: | |||
void process(float** const, float** const outBuffer, const uint32_t frames, const uint32_t framesOffset) | |||
{ | |||
uint32_t i, k; | |||
uint32_t midiEventCount = 0; | |||
// -------------------------------------------------------------------------------------------------------- | |||
// Check if active | |||
@@ -871,8 +916,6 @@ public: | |||
fluid_synth_noteon(fSynth, note.channel, note.note, note.velo); | |||
else | |||
fluid_synth_noteoff(fSynth,note.channel, note.note); | |||
midiEventCount += 1; | |||
} | |||
kData->extNotes.mutex.unlock(); | |||
@@ -905,7 +948,11 @@ public: | |||
if (time > timeOffset) | |||
{ | |||
fluid_synth_write_float(fSynth, time - timeOffset, outBuffer[0] + timeOffset, 0, 1, outBuffer[1] + timeOffset, 0, 1); | |||
if (kUses16Outs) | |||
processSingle(outBuffer, time - timeOffset, timeOffset); | |||
else | |||
fluid_synth_write_float(fSynth, time - timeOffset, outBuffer[0] + timeOffset, 0, 1, outBuffer[1] + timeOffset, 0, 1); | |||
timeOffset = time; | |||
} | |||
@@ -1011,7 +1058,7 @@ public: | |||
} | |||
case kEngineControlEventTypeMidiBank: | |||
if (event.channel < 16 && event.channel != 9) // FIXME | |||
if (event.channel < 16) | |||
nextBankIds[event.channel] = ctrlEvent.param; | |||
break; | |||
@@ -1068,13 +1115,10 @@ public: | |||
case kEngineEventTypeMidi: | |||
{ | |||
if (midiEventCount >= MAX_MIDI_EVENTS) | |||
continue; | |||
const EngineMidiEvent& midiEvent = event.midi; | |||
uint8_t status = MIDI_GET_STATUS_FROM_DATA(midiEvent.data); | |||
uint8_t channel = MIDI_GET_CHANNEL_FROM_DATA(midiEvent.data); | |||
uint8_t channel = event.channel; | |||
// Fix bad note-off | |||
if (MIDI_IS_STATUS_NOTE_ON(status) && midiEvent.data[2] == 0) | |||
@@ -1103,8 +1147,18 @@ public: | |||
const uint8_t pressure = midiEvent.data[2]; | |||
// TODO, not in fluidsynth API? | |||
Q_UNUSED(note); | |||
Q_UNUSED(pressure); | |||
continue; | |||
// unused | |||
(void)note; | |||
(void)pressure; | |||
} | |||
else if (MIDI_IS_STATUS_CONTROL_CHANGE(status) && (fHints & PLUGIN_OPTION_SELF_AUTOMATION) != 0) | |||
{ | |||
const uint8_t control = midiEvent.data[1]; | |||
const uint8_t value = midiEvent.data[2]; | |||
fluid_synth_cc(fSynth, channel, control, value); | |||
} | |||
else if (MIDI_IS_STATUS_AFTERTOUCH(status)) | |||
{ | |||
@@ -1122,8 +1176,6 @@ public: | |||
else | |||
continue; | |||
midiEventCount += 1; | |||
break; | |||
} | |||
} | |||
@@ -1132,7 +1184,12 @@ public: | |||
kData->postRtEvents.trySplice(); | |||
if (frames > timeOffset) | |||
fluid_synth_write_float(fSynth, frames - timeOffset, outBuffer[0] + timeOffset, 0, 1, outBuffer[1] + timeOffset, 0, 1); | |||
{ | |||
if (kUses16Outs) | |||
processSingle(outBuffer, frames - timeOffset, timeOffset); | |||
else | |||
fluid_synth_write_float(fSynth, frames - timeOffset, outBuffer[0] + timeOffset, 0, 1, outBuffer[1] + timeOffset, 0, 1); | |||
} | |||
} // End of Event Input and Processing | |||
@@ -1208,6 +1265,60 @@ public: | |||
kData->activeBefore = kData->active; | |||
} | |||
void processSingle(float** const outBuffer, const uint32_t frames, const uint32_t timeOffset) | |||
{ | |||
for (uint32_t i=0; i < kData->audioOut.count; i++) | |||
carla_zeroFloat(fAudio16Buffers[i], frames); | |||
fluid_synth_process(fSynth, frames, 0, nullptr, kData->audioOut.count, fAudio16Buffers); | |||
for (uint32_t i=0, k; i < kData->audioOut.count; i++) | |||
{ | |||
for (k=0; k < frames; k++) | |||
outBuffer[i][k+timeOffset] = fAudio16Buffers[i][k]; | |||
} | |||
} | |||
void bufferSizeChanged(const uint32_t newBufferSize) | |||
{ | |||
if (! kUses16Outs) | |||
return; | |||
for (uint32_t i=0; i < kData->audioOut.count; i++) | |||
{ | |||
if (fAudio16Buffers[i] != nullptr) | |||
delete[] fAudio16Buffers[i]; | |||
fAudio16Buffers[i] = new float[newBufferSize]; | |||
} | |||
} | |||
// ------------------------------------------------------------------- | |||
// Cleanup | |||
void deleteBuffers() | |||
{ | |||
carla_debug("FluidSynthPlugin::deleteBuffers() - start"); | |||
if (fAudio16Buffers != nullptr) | |||
{ | |||
for (uint32_t i=0; i < kData->audioOut.count; i++) | |||
{ | |||
if (fAudio16Buffers[i] != nullptr) | |||
{ | |||
delete[] fAudio16Buffers[i]; | |||
fAudio16Buffers[i] = nullptr; | |||
} | |||
} | |||
delete[] fAudio16Buffers; | |||
fAudio16Buffers = nullptr; | |||
} | |||
CarlaPlugin::deleteBuffers(); | |||
carla_debug("FluidSynthPlugin::deleteBuffers() - end"); | |||
} | |||
// ------------------------------------------------------------------- | |||
bool init(const char* const filename, const char* const name, const char* const label) | |||
@@ -1271,13 +1382,16 @@ private: | |||
FluidSynthParametersMax = 14 | |||
}; | |||
const bool kUses16Outs; | |||
CarlaString fLabel; | |||
fluid_settings_t* fSettings; | |||
fluid_synth_t* fSynth; | |||
int fSynthId; | |||
double fParamBuffers[FluidSynthParametersMax]; | |||
float** fAudio16Buffers; | |||
double fParamBuffers[FluidSynthParametersMax]; | |||
}; | |||
/**@}*/ | |||
@@ -1290,19 +1404,24 @@ CARLA_BACKEND_END_NAMESPACE | |||
CARLA_BACKEND_START_NAMESPACE | |||
CarlaPlugin* CarlaPlugin::newSF2(const Initializer& init) | |||
CarlaPlugin* CarlaPlugin::newSF2(const Initializer& init, const bool use16Outs) | |||
{ | |||
carla_debug("CarlaPlugin::newSF2({%p, \"%s\", \"%s\", \"%s\"})", init.engine, init.filename, init.name, init.label); | |||
#ifdef WANT_FLUIDSYNTH | |||
if (! fluid_is_soundfont(init.filename)) | |||
{ | |||
init.engine->setLastError("Requested file is not a valid SoundFont"); | |||
return nullptr; | |||
} | |||
FluidSynthPlugin* const plugin = new FluidSynthPlugin(init.engine, init.id); | |||
if (init.engine->getProccessMode() == PROCESS_MODE_CONTINUOUS_RACK && use16Outs) | |||
{ | |||
init.engine->setLastError("Carla's rack mode can only work with Stereo modules, please choose the 2-channel only SoundFont version"); | |||
return nullptr; | |||
} | |||
FluidSynthPlugin* const plugin = new FluidSynthPlugin(init.engine, init.id, use16Outs); | |||
if (! plugin->init(init.filename, init.name, init.label)) | |||
{ | |||
@@ -509,7 +509,7 @@ public: | |||
if (max - min == 0.0f) | |||
{ | |||
carla_stderr2("Broken plugin parameter: max - min == 0"); | |||
carla_stderr2("WARNING - Broken plugin parameter '%s': max - min == 0.0f", fDescriptor->PortNames[i]); | |||
max = min + 0.1f; | |||
} | |||
@@ -1069,7 +1069,6 @@ public: | |||
#endif | |||
} // End of Post-processing | |||
CARLA_PROCESS_CONTINUE_CHECK; | |||
// -------------------------------------------------------------------------------------------------------- | |||
@@ -1075,6 +1075,14 @@ class CarlaMainW(QMainWindow): | |||
if gui: | |||
return gui.encode("utf-8") | |||
elif ptype == PLUGIN_SF2: | |||
if plugin['name'].endswith(" (16 outputs)"): | |||
# return a dummy non-null pointer | |||
INTPOINTER = POINTER(c_int) | |||
ptr = c_int(0x1) | |||
addr = addressof(ptr) | |||
return cast(addr, INTPOINTER) | |||
return c_nullptr | |||
def loadRDFs(self): | |||
@@ -86,3 +86,8 @@ carla-discovery-win64.exe: $(OBJS) ../libs/lilv_win64.a | |||
clean: | |||
rm -f carla-discovery-* | |||
# -------------------------------------------------------------- | |||
debug: | |||
$(MAKE) DEBUG=true |
@@ -1291,9 +1291,21 @@ void do_fluidsynth_check(const char* const filename, const bool init) | |||
delete_fluid_settings(f_settings); | |||
} | |||
#if CARLA_OS_WIN | |||
int sep = '\\'; | |||
#else | |||
int sep = '/'; | |||
#endif | |||
CarlaString name(std::strrchr(filename, sep)+1); | |||
name.truncate(name.rfind('.')); | |||
CarlaString label(name); | |||
// 2 channels | |||
DISCOVERY_OUT("init", "-----------"); | |||
DISCOVERY_OUT("name", ""); | |||
DISCOVERY_OUT("label", ""); | |||
DISCOVERY_OUT("name", (const char*)name); | |||
DISCOVERY_OUT("label", (const char*)label); | |||
DISCOVERY_OUT("maker", ""); | |||
DISCOVERY_OUT("copyright", ""); | |||
DISCOVERY_OUT("hints", PLUGIN_IS_SYNTH); | |||
@@ -1307,6 +1319,26 @@ void do_fluidsynth_check(const char* const filename, const bool init) | |||
DISCOVERY_OUT("parameters.total", 14); | |||
DISCOVERY_OUT("build", BINARY_NATIVE); | |||
DISCOVERY_OUT("end", "------------"); | |||
// 16 channels | |||
if (name.isNotEmpty()) | |||
name += " (16 outputs)"; | |||
DISCOVERY_OUT("init", "-----------"); | |||
DISCOVERY_OUT("name", ""); | |||
DISCOVERY_OUT("name", (const char*)name); | |||
DISCOVERY_OUT("label", (const char*)label); | |||
DISCOVERY_OUT("copyright", ""); | |||
DISCOVERY_OUT("hints", PLUGIN_IS_SYNTH); | |||
DISCOVERY_OUT("audio.outs", 32); | |||
DISCOVERY_OUT("audio.total", 32); | |||
DISCOVERY_OUT("midi.ins", 1); | |||
DISCOVERY_OUT("midi.total", 1); | |||
DISCOVERY_OUT("programs.total", programs); | |||
DISCOVERY_OUT("parameters.ins", 13); // defined in Carla | |||
DISCOVERY_OUT("parameters.outs", 1); | |||
DISCOVERY_OUT("parameters.total", 14); | |||
DISCOVERY_OUT("build", BINARY_NATIVE); | |||
DISCOVERY_OUT("end", "------------"); | |||
#else | |||
DISCOVERY_OUT("error", "SF2 support not available"); | |||
Q_UNUSED(filename); | |||
@@ -187,6 +187,30 @@ public: | |||
truncate(0); | |||
} | |||
size_t find(const char c) | |||
{ | |||
for (size_t i=0; i < bufferLen; i++) | |||
{ | |||
if (buffer[i] == c) | |||
return i; | |||
} | |||
return 0; | |||
} | |||
size_t rfind(const char c) | |||
{ | |||
size_t pos = 0; | |||
for (size_t i=0; i < bufferLen; i++) | |||
{ | |||
if (buffer[i] == c) | |||
pos = i; | |||
} | |||
return pos; | |||
} | |||
void replace(const char before, const char after) | |||
{ | |||
if (after == '\0') | |||