/* * Carla Engine utils * Copyright (C) 2011-2022 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. */ #ifndef CARLA_ENGINE_UTILS_HPP_INCLUDED #define CARLA_ENGINE_UTILS_HPP_INCLUDED #include "CarlaEngine.hpp" #include "CarlaUtils.hpp" #include "CarlaMIDI.h" #include "water/midi/MidiBuffer.h" CARLA_BACKEND_START_NAMESPACE // ----------------------------------------------------------------------- // Maximum internal pre-allocated events const ushort kMaxEngineEventInternalCount = 2048; // ----------------------------------------------------------------------- static inline const char* EngineType2Str(const EngineType type) noexcept { switch (type) { case kEngineTypeNull: return "kEngineTypeNull"; case kEngineTypeJack: return "kEngineTypeJack"; case kEngineTypeJuce: return "kEngineTypeJuce"; case kEngineTypeRtAudio: return "kEngineTypeRtAudio"; case kEngineTypeSDL: return "kEngineTypeSDL"; case kEngineTypePlugin: return "kEngineTypePlugin"; case kEngineTypeBridge: return "kEngineTypeBridge"; case kEngineTypeDummy: return "kEngineTypeDummy"; } carla_stderr("CarlaBackend::EngineType2Str(%i) - invalid type", type); return nullptr; } static inline const char* EnginePortType2Str(const EnginePortType type) noexcept { switch (type) { case kEnginePortTypeNull: return "kEnginePortTypeNull"; case kEnginePortTypeAudio: return "kEnginePortTypeAudio"; case kEnginePortTypeCV: return "kEnginePortTypeCV"; case kEnginePortTypeEvent: return "kEnginePortTypeEvent"; } carla_stderr("CarlaBackend::EnginePortType2Str(%i) - invalid type", type); return nullptr; } static inline const char* EngineEventType2Str(const EngineEventType type) noexcept { switch (type) { case kEngineEventTypeNull: return "kEngineEventTypeNull"; case kEngineEventTypeControl: return "kEngineEventTypeControl"; case kEngineEventTypeMidi: return "kEngineEventTypeMidi"; } carla_stderr("CarlaBackend::EngineEventType2Str(%i) - invalid type", type); return nullptr; } static inline const char* EngineControlEventType2Str(const EngineControlEventType type) noexcept { switch (type) { case kEngineControlEventTypeNull: return "kEngineNullEvent"; case kEngineControlEventTypeParameter: return "kEngineControlEventTypeParameter"; case kEngineControlEventTypeMidiBank: return "kEngineControlEventTypeMidiBank"; case kEngineControlEventTypeMidiProgram: return "kEngineControlEventTypeMidiProgram"; case kEngineControlEventTypeAllSoundOff: return "kEngineControlEventTypeAllSoundOff"; case kEngineControlEventTypeAllNotesOff: return "kEngineControlEventTypeAllNotesOff"; } carla_stderr("CarlaBackend::EngineControlEventType2Str(%i) - invalid type", type); return nullptr; } // ----------------------------------------------------------------------- static inline void fillEngineEventsFromWaterMidiBuffer(EngineEvent engineEvents[kMaxEngineEventInternalCount], const water::MidiBuffer& midiBuffer) { const uint8_t* midiData; int numBytes, sampleNumber; ushort engineEventIndex = 0; for (ushort i=0; i < kMaxEngineEventInternalCount; ++i) { const EngineEvent& engineEvent(engineEvents[i]); if (engineEvent.type != kEngineEventTypeNull) continue; engineEventIndex = i; break; } for (water::MidiBuffer::Iterator midiBufferIterator(midiBuffer); midiBufferIterator.getNextEvent(midiData, numBytes, sampleNumber) && engineEventIndex < kMaxEngineEventInternalCount;) { CARLA_SAFE_ASSERT_CONTINUE(numBytes > 0); CARLA_SAFE_ASSERT_CONTINUE(sampleNumber >= 0); CARLA_SAFE_ASSERT_CONTINUE(numBytes < 0xFF /* uint8_t max */); EngineEvent& engineEvent(engineEvents[engineEventIndex++]); engineEvent.time = static_cast(sampleNumber); engineEvent.fillFromMidiData(static_cast(numBytes), midiData, 0); } } // ----------------------------------------------------------------------- static inline void fillWaterMidiBufferFromEngineEvents(water::MidiBuffer& midiBuffer, const EngineEvent engineEvents[kMaxEngineEventInternalCount]) { uint8_t size = 0; uint8_t mdata[3] = { 0, 0, 0 }; uint8_t mdataTmp[EngineMidiEvent::kDataSize]; const uint8_t* mdataPtr; for (ushort i=0; i < kMaxEngineEventInternalCount; ++i) { const EngineEvent& engineEvent(engineEvents[i]); /**/ if (engineEvent.type == kEngineEventTypeNull) { break; } else if (engineEvent.type == kEngineEventTypeControl) { const EngineControlEvent& ctrlEvent(engineEvent.ctrl); size = ctrlEvent.convertToMidiData(engineEvent.channel, mdata); mdataPtr = mdata; } else if (engineEvent.type == kEngineEventTypeMidi) { const EngineMidiEvent& midiEvent(engineEvent.midi); size = midiEvent.size; CARLA_SAFE_ASSERT_CONTINUE(size > 0); if (size > EngineMidiEvent::kDataSize) { CARLA_SAFE_ASSERT_CONTINUE(midiEvent.dataExt != nullptr); mdataPtr = midiEvent.dataExt; } else { // set first byte mdataTmp[0] = static_cast(midiEvent.data[0] | (engineEvent.channel & MIDI_CHANNEL_BIT)); if (size > 1) { // copy rest carla_copy(mdataTmp+1, midiEvent.data+1, size-1U); } // done mdataPtr = mdataTmp; } } else { continue; } if (size > 0) midiBuffer.addEvent(mdataPtr, static_cast(size), static_cast(engineEvent.time)); } } // ------------------------------------------------------------------- // Helper classes class ScopedEngineEnvironmentLocker { public: ScopedEngineEnvironmentLocker(CarlaEngine* const engine) noexcept; ~ScopedEngineEnvironmentLocker() noexcept; private: CarlaEngine::ProtectedData* const pData; CARLA_PREVENT_HEAP_ALLOCATION CARLA_DECLARE_NON_COPYABLE(ScopedEngineEnvironmentLocker) }; // ----------------------------------------------------------------------- CARLA_BACKEND_END_NAMESPACE #endif // CARLA_ENGINE_UTILS_HPP_INCLUDED