/* * Carla Plugin Host * Copyright (C) 2011-2021 Filipe Coelho * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For a full copy of the GNU General Public License see the doc/GPL.txt file. */ #include "CarlaEngine.hpp" #include "CarlaMathUtils.hpp" #include "CarlaMIDI.h" CARLA_BACKEND_START_NAMESPACE // ----------------------------------------------------------------------- // EngineControlEvent uint8_t EngineControlEvent::convertToMidiData(const uint8_t channel, uint8_t data[3]) const noexcept { switch (type) { case kEngineControlEventTypeNull: break; case kEngineControlEventTypeParameter: CARLA_SAFE_ASSERT_RETURN(param < MAX_MIDI_VALUE, 0); data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); if (MIDI_IS_CONTROL_BANK_SELECT(param)) { data[1] = MIDI_CONTROL_BANK_SELECT; if (midiValue >= 0) data[2] = uint8_t(midiValue); else data[2] = uint8_t(carla_fixedValue(0.0f, static_cast(MAX_MIDI_VALUE-1), normalizedValue)); } else { data[1] = static_cast(param); if (midiValue >= 0) data[2] = uint8_t(midiValue); else data[2] = uint8_t(carla_fixedValue(0.0f, 1.0f, normalizedValue) * static_cast(MAX_MIDI_VALUE-1) + 0.5f); } return 3; case kEngineControlEventTypeMidiBank: data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); data[1] = MIDI_CONTROL_BANK_SELECT; data[2] = uint8_t(carla_fixedValue(0, MAX_MIDI_VALUE-1, param)); return 3; case kEngineControlEventTypeMidiProgram: data[0] = static_cast(MIDI_STATUS_PROGRAM_CHANGE | (channel & MIDI_CHANNEL_BIT)); data[1] = uint8_t(carla_fixedValue(0, MAX_MIDI_VALUE-1, param)); return 2; case kEngineControlEventTypeAllSoundOff: data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); data[1] = MIDI_CONTROL_ALL_SOUND_OFF; return 2; case kEngineControlEventTypeAllNotesOff: data[0] = static_cast(MIDI_STATUS_CONTROL_CHANGE | (channel & MIDI_CHANNEL_BIT)); data[1] = MIDI_CONTROL_ALL_NOTES_OFF; return 2; } return 0; } // ----------------------------------------------------------------------- // EngineEvent void EngineEvent::fillFromMidiData(const uint8_t size, const uint8_t* const data, const uint8_t midiPortOffset) noexcept { if (size == 0 || data == nullptr || data[0] < MIDI_STATUS_NOTE_OFF) { type = kEngineEventTypeNull; channel = 0; return; } // get channel channel = uint8_t(MIDI_GET_CHANNEL_FROM_DATA(data)); // get status const uint8_t midiStatus(uint8_t(MIDI_GET_STATUS_FROM_DATA(data))); if (midiStatus == MIDI_STATUS_CONTROL_CHANGE) { CARLA_SAFE_ASSERT_RETURN(size >= 2,); type = kEngineEventTypeControl; const uint8_t midiControl(data[1]); if (MIDI_IS_CONTROL_BANK_SELECT(midiControl)) { CARLA_SAFE_ASSERT_RETURN(size >= 3,); const uint8_t midiBank(data[2]); ctrl.type = kEngineControlEventTypeMidiBank; ctrl.param = midiBank; ctrl.midiValue = -1; ctrl.normalizedValue = 0.0f; ctrl.handled = true; } else if (midiControl == MIDI_CONTROL_ALL_SOUND_OFF) { ctrl.type = kEngineControlEventTypeAllSoundOff; ctrl.param = 0; ctrl.midiValue = -1; ctrl.normalizedValue = 0.0f; ctrl.handled = true; } else if (midiControl == MIDI_CONTROL_ALL_NOTES_OFF) { ctrl.type = kEngineControlEventTypeAllNotesOff; ctrl.param = 0; ctrl.midiValue = -1; ctrl.normalizedValue = 0.0f; ctrl.handled = true; } else { CARLA_SAFE_ASSERT_RETURN(size >= 3,); // ensures 0.0<->1.0 value range const int8_t midiValue = static_cast(carla_fixedValue(0, 127, data[2])); ctrl.type = kEngineControlEventTypeParameter; ctrl.param = midiControl; ctrl.midiValue = midiValue; ctrl.normalizedValue = float(midiValue)/127.0f; ctrl.handled = false; } } else if (midiStatus == MIDI_STATUS_PROGRAM_CHANGE) { CARLA_SAFE_ASSERT_RETURN(size >= 2,); type = kEngineEventTypeControl; const uint8_t midiProgram(data[1]); ctrl.type = kEngineControlEventTypeMidiProgram; ctrl.param = midiProgram; ctrl.midiValue = -1; ctrl.normalizedValue = 0.0f; ctrl.handled = true; } else { type = kEngineEventTypeMidi; midi.port = midiPortOffset; midi.size = size; if (size > EngineMidiEvent::kDataSize) { midi.dataExt = data; std::memset(midi.data, 0, sizeof(uint8_t)*EngineMidiEvent::kDataSize); } else { midi.data[0] = midiStatus; uint8_t i=1; for (; i < size; ++i) midi.data[i] = data[i]; for (; i < EngineMidiEvent::kDataSize; ++i) midi.data[i] = 0; midi.dataExt = nullptr; } } } // ----------------------------------------------------------------------- // EngineOptions EngineOptions::EngineOptions() noexcept #ifdef CARLA_OS_LINUX : processMode(ENGINE_PROCESS_MODE_MULTIPLE_CLIENTS), transportMode(ENGINE_TRANSPORT_MODE_JACK), #else : processMode(ENGINE_PROCESS_MODE_PATCHBAY), transportMode(ENGINE_TRANSPORT_MODE_INTERNAL), #endif transportExtra(nullptr), forceStereo(false), resetXruns(false), preferPluginBridges(false), #if defined(CARLA_OS_MAC) || defined(CARLA_OS_WIN) preferUiBridges(false), #else preferUiBridges(true), #endif uisAlwaysOnTop(true), pluginsAreStandalone(false), bgColor(0x000000ff), fgColor(0xffffffff), uiScale(1.0f), maxParameters(MAX_DEFAULT_PARAMETERS), uiBridgesTimeout(4000), audioBufferSize(512), audioSampleRate(44100), audioTripleBuffer(false), audioDriver(nullptr), audioDevice(nullptr), #ifndef BUILD_BRIDGE # ifdef CARLA_OS_WIN oscEnabled(false), # else oscEnabled(true), # endif oscPortTCP(22752), oscPortUDP(22752), #endif pathAudio(nullptr), pathMIDI(nullptr), pathLADSPA(nullptr), pathDSSI(nullptr), pathLV2(nullptr), pathVST2(nullptr), pathVST3(nullptr), pathSF2(nullptr), pathSFZ(nullptr), pathJSFX(nullptr), binaryDir(nullptr), resourceDir(nullptr), clientNamePrefix(nullptr), preventBadBehaviour(false), frontendWinId(0) #ifndef CARLA_OS_WIN , wine() #endif { } EngineOptions::~EngineOptions() noexcept { if (audioDriver != nullptr) { delete[] audioDriver; audioDriver = nullptr; } if (audioDevice != nullptr) { delete[] audioDevice; audioDevice = nullptr; } if (pathAudio != nullptr) { delete[] pathAudio; pathAudio = nullptr; } if (pathMIDI != nullptr) { delete[] pathMIDI; pathMIDI = nullptr; } if (pathLADSPA != nullptr) { delete[] pathLADSPA; pathLADSPA = nullptr; } if (pathDSSI != nullptr) { delete[] pathDSSI; pathDSSI = nullptr; } if (pathLV2 != nullptr) { delete[] pathLV2; pathLV2 = nullptr; } if (pathVST2 != nullptr) { delete[] pathVST2; pathVST2 = nullptr; } if (pathVST3 != nullptr) { delete[] pathVST3; pathVST3 = nullptr; } if (pathSF2 != nullptr) { delete[] pathSF2; pathSF2 = nullptr; } if (pathSFZ != nullptr) { delete[] pathSFZ; pathSFZ = nullptr; } if (pathJSFX != nullptr) { delete[] pathJSFX; pathJSFX = nullptr; } if (binaryDir != nullptr) { delete[] binaryDir; binaryDir = nullptr; } if (resourceDir != nullptr) { delete[] resourceDir; resourceDir = nullptr; } if (clientNamePrefix != nullptr) { delete[] clientNamePrefix; clientNamePrefix = nullptr; } } #ifndef CARLA_OS_WIN EngineOptions::Wine::Wine() noexcept : executable(nullptr), autoPrefix(true), fallbackPrefix(nullptr), rtPrio(true), baseRtPrio(15), serverRtPrio(10) {} EngineOptions::Wine::~Wine() noexcept { if (executable != nullptr) { delete[] executable; executable = nullptr; } if (fallbackPrefix != nullptr) { delete[] fallbackPrefix; fallbackPrefix = nullptr; } } #endif // ----------------------------------------------------------------------- // EngineTimeInfoBBT EngineTimeInfoBBT::EngineTimeInfoBBT() noexcept : valid(false), bar(0), beat(0), tick(0.0), barStartTick(0.0), beatsPerBar(0.0f), beatType(0.0f), ticksPerBeat(0.0), beatsPerMinute(0.0) {} EngineTimeInfoBBT::EngineTimeInfoBBT(const EngineTimeInfoBBT& bbt) noexcept : valid(bbt.valid), bar(bbt.bar), beat(bbt.beat), tick(bbt.tick), barStartTick(bbt.barStartTick), beatsPerBar(bbt.beatsPerBar), beatType(bbt.beatType), ticksPerBeat(bbt.ticksPerBeat), beatsPerMinute(bbt.beatsPerMinute) {} void EngineTimeInfoBBT::clear() noexcept { valid = false; bar = 0; beat = 0; tick = 0.0; barStartTick = 0.0; beatsPerBar = 0.0f; beatType = 0.0f; ticksPerBeat = 0.0; beatsPerMinute = 0.0; } // ----------------------------------------------------------------------- // EngineTimeInfo EngineTimeInfo::EngineTimeInfo() noexcept : playing(false), frame(0), usecs(0), bbt() {} void EngineTimeInfo::clear() noexcept { playing = false; frame = 0; usecs = 0; bbt.clear(); } EngineTimeInfo::EngineTimeInfo(const EngineTimeInfo& info) noexcept : playing(info.playing), frame(info.frame), usecs(info.usecs), bbt(info.bbt) {} EngineTimeInfo& EngineTimeInfo::operator=(const EngineTimeInfo& info) noexcept { playing = info.playing; frame = info.frame; usecs = info.usecs; bbt.valid = info.bbt.valid; bbt.bar = info.bbt.bar; bbt.beat = info.bbt.beat; bbt.tick = info.bbt.tick; bbt.barStartTick = info.bbt.barStartTick; bbt.beatsPerBar = info.bbt.beatsPerBar; bbt.beatType = info.bbt.beatType; bbt.ticksPerBeat = info.bbt.ticksPerBeat; bbt.beatsPerMinute = info.bbt.beatsPerMinute; return *this; } bool EngineTimeInfo::compareIgnoringRollingFrames(const EngineTimeInfo& timeInfo, const uint32_t maxFrames) const noexcept { if (timeInfo.playing != playing || timeInfo.bbt.valid != bbt.valid) return false; if (bbt.valid) { if (carla_isNotEqual(timeInfo.bbt.beatsPerBar, bbt.beatsPerBar)) return false; if (carla_isNotEqual(timeInfo.bbt.beatsPerMinute, bbt.beatsPerMinute)) return false; } // frame matches, nothing else to compare if (timeInfo.frame == frame) return true; // if we went back in time, so a case of reposition if (frame > timeInfo.frame) return false; // not playing, so don't bother checking transport // assume frame changed, likely playback has stopped if (! playing) return false; // if we are within expected bounds, assume we are rolling normally if (frame + maxFrames <= timeInfo.frame) return true; // out of bounds, another reposition return false; } bool EngineTimeInfo::operator==(const EngineTimeInfo& timeInfo) const noexcept { if (timeInfo.playing != playing || timeInfo.frame != frame || timeInfo.bbt.valid != bbt.valid) return false; if (! bbt.valid) return true; if (carla_isNotEqual(timeInfo.bbt.beatsPerBar, bbt.beatsPerBar)) return false; if (carla_isNotEqual(timeInfo.bbt.beatsPerMinute, bbt.beatsPerMinute)) return false; return true; } bool EngineTimeInfo::operator!=(const EngineTimeInfo& timeInfo) const noexcept { return !operator==(timeInfo); } // ----------------------------------------------------------------------- CARLA_BACKEND_END_NAMESPACE