/* * Carla RtAudio Engine * Copyright (C) 2012-2013 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 COPYING file */ #ifdef WANT_RTAUDIO #include "carla_engine_internal.hpp" #include "carla_backend_utils.hpp" #include "carla_midi.h" #include "RtAudio.h" #include "RtMidi.h" CARLA_BACKEND_START_NAMESPACE #if 0 } // Fix editor indentation #endif // ------------------------------------------------------------------------------------------------------------------- // RtAudio Engine client class CarlaEngineRtAudioClient : public CarlaEngineClient { public: CarlaEngineRtAudioClient(const EngineType engineType, const ProcessMode processMode) : CarlaEngineClient(engineType, processMode) { qDebug("CarlaEngineRtAudioClient::CarlaEngineRtAudioClient(%s, %s)", EngineType2Str(engineType), ProcessMode2Str(processMode)); } ~CarlaEngineRtAudioClient() { qDebug("CarlaEngineRtAudioClient::~CarlaEngineRtAudioClient()"); } const CarlaEnginePort* addPort(const EnginePortType portType, const char* const name, const bool isInput) { qDebug("CarlaEngineRtAudioClient::addPort(%s, \"%s\", %s)", EnginePortType2Str(portType), name, bool2str(isInput)); switch (portType) { case kEnginePortTypeNull: break; case kEnginePortTypeAudio: return new CarlaEngineAudioPort(isInput, kProcessMode); case kEnginePortTypeEvent: return new CarlaEngineEventPort(isInput, kProcessMode); } qCritical("CarlaEngineRtAudioClient::addPort(%s, \"%s\", %s) - invalid type", EnginePortType2Str(portType), name, bool2str(isInput)); return nullptr; } private: CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudioClient) }; // ------------------------------------------------------------------------------------------------------------------- // RtAudio Engine class CarlaEngineRtAudio : public CarlaEngine { public: CarlaEngineRtAudio(RtAudio::Api api) : CarlaEngine(), audio(api) { qDebug("CarlaEngineRtAudio::CarlaEngineRtAudio(%i)", api); evIn = nullptr; evOut = nullptr; m_audioInterleaved = false; m_inBuf1 = nullptr; m_inBuf2 = nullptr; m_outBuf1 = nullptr; m_outBuf2 = nullptr; // just to make sure fOptions.forceStereo = true; fOptions.processMode = PROCESS_MODE_CONTINUOUS_RACK; } ~CarlaEngineRtAudio() { qDebug("CarlaEngineRtAudio::~CarlaEngineRtAudio()"); } // ------------------------------------- bool init(const char* const clientName) { qDebug("CarlaEngineRtAudio::init(\"%s\")", clientName); if (audio.getDeviceCount() < 1) { setLastError("No audio devices available for this driver"); return false; } fBufferSize = fOptions.preferredBufferSize; fSampleRate = fOptions.preferredSampleRate; RtAudio::StreamParameters iParams, oParams; iParams.deviceId = audio.getDefaultInputDevice(); oParams.deviceId = audio.getDefaultOutputDevice(); iParams.nChannels = 2; oParams.nChannels = 2; RtAudio::StreamOptions rtOptions; rtOptions.flags = RTAUDIO_MINIMIZE_LATENCY | RTAUDIO_HOG_DEVICE | RTAUDIO_SCHEDULE_REALTIME; rtOptions.numberOfBuffers = 2; rtOptions.streamName = clientName; rtOptions.priority = 85; if (audio.getCurrentApi() != RtAudio::LINUX_PULSE) { rtOptions.flags |= RTAUDIO_NONINTERLEAVED; m_audioInterleaved = false; if (audio.getCurrentApi() == RtAudio::LINUX_ALSA) rtOptions.flags |= RTAUDIO_ALSA_USE_DEFAULT; } else m_audioInterleaved = true; try { audio.openStream(&oParams, &iParams, RTAUDIO_FLOAT32, fSampleRate, &fBufferSize, carla_rtaudio_process_callback, this, &rtOptions); } catch (RtError& e) { setLastError(e.what()); return false; } try { audio.startStream(); } catch (RtError& e) { setLastError(e.what()); return false; } fSampleRate = audio.getStreamSampleRate(); m_inBuf1 = new float[fBufferSize]; m_inBuf2 = new float[fBufferSize]; m_outBuf1 = new float[fBufferSize]; m_outBuf2 = new float[fBufferSize]; //midiIn = new MidiInAlsa(clientName, 512); //midiIn->openVirtualPort("control-in"); //midiIn->openVirtualPort("midi-in"); //midiOut = new MidiOutAlsa(clientName); //midiOut->openVirtualPort("control-out"); //midiOut->openVirtualPort("midi-out"); fName = clientName; fName.toBasic(); CarlaEngine::init(fName); return true; } bool close() { qDebug("CarlaEngineRtAudio::close()"); CarlaEngine::close(); if (audio.isStreamRunning()) audio.stopStream(); if (audio.isStreamOpen()) audio.closeStream(); #if 0 if (midiIn) { midiIn->cancelCallback(); midiIn->closePort(); delete midiIn; midiIn = nullptr; } if (midiOut) { midiOut->closePort(); delete midiOut; midiOut = nullptr; } #endif if (m_inBuf1) { delete[] m_inBuf1; m_inBuf1 = nullptr; } if (m_inBuf2) { delete[] m_inBuf2; m_inBuf2 = nullptr; } if (m_outBuf1) { delete[] m_outBuf1; m_outBuf1 = nullptr; } if (m_outBuf2) { delete[] m_outBuf2; m_outBuf2 = nullptr; } return true; } bool isRunning() const { return audio.isStreamRunning(); } bool isOffline() const { return false; } EngineType type() const { return kEngineTypeRtAudio; } CarlaEngineClient* addClient(CarlaPlugin* const) { return new CarlaEngineRtAudioClient(kEngineTypeRtAudio, fOptions.processMode); } // ------------------------------------- protected: void handleProcessCallback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status) { if (maxPluginNumber() == 0) return; // get buffers from RtAudio float* insPtr = (float*)inputBuffer; float* outsPtr = (float*)outputBuffer; // assert buffers CARLA_ASSERT(insPtr); CARLA_ASSERT(outsPtr); // initialize audio input if (m_audioInterleaved) { for (unsigned int i=0; i < nframes*2; i++) { if (i % 2) m_inBuf2[i/2] = insPtr[i]; else m_inBuf1[i/2] = insPtr[i]; } } else { for (unsigned int i=0; i < nframes; i++) m_inBuf1[i] = insPtr[i]; for (unsigned int i=0, j=nframes; i < nframes; i++, j++) m_inBuf2[i] = insPtr[j]; } // create audio buffers float* inBuf[2] = { m_inBuf1, m_inBuf2 }; float* outBuf[2] = { m_outBuf1, m_outBuf2 }; // initialize events input //memset(rackEventsIn, 0, sizeof(EngineEvent)*MAX_EVENTS); { // TODO } processRack(inBuf, outBuf, nframes); // output audio if (m_audioInterleaved) { for (unsigned int i=0; i < nframes*2; i++) { if (i % 2) outsPtr[i] = m_outBuf2[i/2]; else outsPtr[i] = m_outBuf1[i/2]; } } else { for (unsigned int i=0; i < nframes; i++) outsPtr[i] = m_outBuf1[i]; for (unsigned int i=0, j=nframes; i < nframes; i++, j++) outsPtr[j] = m_outBuf2[i]; } // output control { // TODO } // output midi { // TODO } Q_UNUSED(streamTime); Q_UNUSED(status); } // ------------------------------------- private: RtAudio audio; ScopedPointer evIn; ScopedPointer evOut; bool m_audioInterleaved; float* m_inBuf1; float* m_inBuf2; float* m_outBuf1; float* m_outBuf2; static int carla_rtaudio_process_callback(void* outputBuffer, void* inputBuffer, unsigned int nframes, double streamTime, RtAudioStreamStatus status, void* userData) { CarlaEngineRtAudio* const _this_ = (CarlaEngineRtAudio*)userData; _this_->handleProcessCallback(outputBuffer, inputBuffer, nframes, streamTime, status); return 0; } CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineRtAudio) }; // ----------------------------------------- static std::vector rtApis; static void initRtApis() { static bool initiated = false; if (! initiated) { initiated = true; RtAudio::getCompiledApi(rtApis); } } CarlaEngine* CarlaEngine::newRtAudio(RtAudioApi api) { RtAudio::Api rtApi = RtAudio::UNSPECIFIED; switch (api) { case RTAUDIO_DUMMY: rtApi = RtAudio::RTAUDIO_DUMMY; break; case RTAUDIO_LINUX_ALSA: rtApi = RtAudio::LINUX_ALSA; break; case RTAUDIO_LINUX_PULSE: rtApi = RtAudio::LINUX_PULSE; break; case RTAUDIO_LINUX_OSS: rtApi = RtAudio::LINUX_OSS; break; case RTAUDIO_UNIX_JACK: rtApi = RtAudio::UNIX_JACK; break; case RTAUDIO_MACOSX_CORE: rtApi = RtAudio::MACOSX_CORE; break; case RTAUDIO_WINDOWS_ASIO: rtApi = RtAudio::WINDOWS_ASIO; break; case RTAUDIO_WINDOWS_DS: rtApi = RtAudio::WINDOWS_DS; break; } return new CarlaEngineRtAudio(rtApi); } unsigned int CarlaEngine::getRtAudioApiCount() { initRtApis(); return rtApis.size(); } const char* CarlaEngine::getRtAudioApiName(unsigned int index) { initRtApis(); if (index < rtApis.size()) { const RtAudio::Api& api(rtApis[index]); switch (api) { case RtAudio::UNSPECIFIED: return "Unspecified"; case RtAudio::LINUX_ALSA: return "ALSA"; case RtAudio::LINUX_PULSE: return "PulseAudio"; case RtAudio::LINUX_OSS: return "OSS"; case RtAudio::UNIX_JACK: return "JACK (RtAudio)"; case RtAudio::MACOSX_CORE: return "CoreAudio"; case RtAudio::WINDOWS_ASIO: return "ASIO"; case RtAudio::WINDOWS_DS: return "DirectSound"; case RtAudio::RTAUDIO_DUMMY: return "Dummy"; } } return nullptr; } // ----------------------------------------- CARLA_BACKEND_END_NAMESPACE #ifdef QTCREATOR_TEST int main() { return 0; } #endif #endif // CARLA_ENGINE_RTAUDIO