Rename a pipe method, add docs
[carla.git] / source / backend / engine / CarlaEngineSDL.cpp
blob3003f6e056d2474058bf2e9595bf3e4187d604f1
1 /*
2 * Carla Plugin Host
3 * Copyright (C) 2011-2022 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #include "CarlaEngineGraph.hpp"
19 #include "CarlaEngineInit.hpp"
20 #include "CarlaEngineInternal.hpp"
21 #include "CarlaStringList.hpp"
22 #include "CarlaBackendUtils.hpp"
24 #include <SDL.h>
26 #ifndef HAVE_SDL2
27 typedef Uint32 SDL_AudioDeviceID;
28 #endif
30 #ifndef SDL_HINT_AUDIO_DEVICE_APP_NAME
31 # define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME"
32 #endif
34 #ifndef SDL_HINT_AUDIO_DEVICE_STREAM_NAME
35 # define SDL_HINT_AUDIO_DEVICE_STREAM_NAME "SDL_AUDIO_DEVICE_STREAM_NAME"
36 #endif
38 CARLA_BACKEND_START_NAMESPACE
40 // -------------------------------------------------------------------------------------------------------------------
41 // Global static data
43 static CarlaStringList gDeviceNames;
45 // -------------------------------------------------------------------------------------------------------------------
47 static void initAudioDevicesIfNeeded()
49 static bool needsInit = true;
51 if (! needsInit)
52 return;
54 needsInit = false;
56 #ifdef HAVE_SDL2
57 SDL_InitSubSystem(SDL_INIT_AUDIO);
59 const int numDevices = SDL_GetNumAudioDevices(0);
61 for (int i=0; i<numDevices; ++i)
62 gDeviceNames.append(SDL_GetAudioDeviceName(i, 0));
63 #else
64 SDL_Init(SDL_INIT_AUDIO);
65 #endif
68 // -------------------------------------------------------------------------------------------------------------------
69 // RtAudio Engine
71 class CarlaEngineSDL : public CarlaEngine
73 public:
74 CarlaEngineSDL()
75 : CarlaEngine(),
76 fDeviceId(0),
77 fDeviceName(),
78 fAudioOutCount(0),
79 fAudioIntBufOut(nullptr)
81 carla_debug("CarlaEngineSDL::CarlaEngineSDL()");
83 // just to make sure
84 pData->options.transportMode = ENGINE_TRANSPORT_MODE_INTERNAL;
87 ~CarlaEngineSDL() override
89 CARLA_SAFE_ASSERT(fAudioOutCount == 0);
90 carla_debug("CarlaEngineSDL::~CarlaEngineSDL()");
93 // -------------------------------------
95 bool init(const char* const clientName) override
97 CARLA_SAFE_ASSERT_RETURN(fDeviceId == 0, false);
98 CARLA_SAFE_ASSERT_RETURN(fAudioOutCount == 0, false);
99 CARLA_SAFE_ASSERT_RETURN(clientName != nullptr && clientName[0] != '\0', false);
100 carla_debug("CarlaEngineSDL::init(\"%s\")", clientName);
102 if (pData->options.processMode != ENGINE_PROCESS_MODE_CONTINUOUS_RACK && pData->options.processMode != ENGINE_PROCESS_MODE_PATCHBAY)
104 setLastError("Invalid process mode");
105 return false;
108 SDL_AudioSpec requested, received;
109 carla_zeroStruct(requested);
110 #ifdef HAVE_SDL2
111 requested.format = AUDIO_F32SYS;
112 #else
113 requested.format = AUDIO_S16SYS;
114 #endif
115 requested.channels = 2;
116 requested.freq = static_cast<int>(pData->options.audioSampleRate);
117 requested.samples = static_cast<Uint16>(pData->options.audioBufferSize);
118 requested.callback = carla_sdl_process_callback;
119 requested.userdata = this;
121 #ifdef HAVE_SDL2
122 SDL_SetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME, clientName);
123 // SDL_SetHint(SDL_HINT_AUDIO_DEVICE_STREAM_NAME, );
124 SDL_SetHint(SDL_HINT_AUDIO_RESAMPLING_MODE, "2");
126 const char* const deviceName = pData->options.audioDevice != nullptr && pData->options.audioDevice[0] != '\0'
127 ? pData->options.audioDevice
128 : nullptr;
130 int flags = SDL_AUDIO_ALLOW_FREQUENCY_CHANGE;
131 if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
132 flags |= SDL_AUDIO_ALLOW_CHANNELS_CHANGE;
134 fDeviceId = SDL_OpenAudioDevice(deviceName, 0, &requested, &received, flags);
135 #else
136 fDeviceId = SDL_OpenAudio(&requested, &received) == 0 ? 1 : 0;
137 #endif
139 if (fDeviceId == 0)
141 setLastError(SDL_GetError());
142 return false;
145 if (received.channels == 0)
147 #ifdef HAVE_SDL2
148 SDL_CloseAudioDevice(fDeviceId);
149 #else
150 SDL_CloseAudio();
151 #endif
152 fDeviceId = 0;
153 setLastError("No output channels available");
154 return false;
157 if (! pData->init(clientName))
159 close();
160 setLastError("Failed to init internal data");
161 return false;
164 pData->bufferSize = received.samples;
165 pData->sampleRate = received.freq;
166 pData->initTime(pData->options.transportExtra);
168 fAudioOutCount = received.channels;
169 fAudioIntBufOut = new float*[fAudioOutCount];
170 for (uint i=0; i<fAudioOutCount; ++i)
171 fAudioIntBufOut[i] = new float[received.samples];
173 pData->graph.create(0, fAudioOutCount, 0, 0);
175 #ifdef HAVE_SDL2
176 SDL_PauseAudioDevice(fDeviceId, 0);
177 #else
178 SDL_PauseAudio(0);
179 #endif
180 carla_stdout("open fAudioOutCount %d %d %d | %d vs %d",
181 fAudioOutCount, received.samples, received.freq,
182 received.format, requested.format);
184 patchbayRefresh(true, false, false);
186 if (pData->options.processMode == ENGINE_PROCESS_MODE_PATCHBAY)
187 refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), false, false);
189 callback(true, true,
190 ENGINE_CALLBACK_ENGINE_STARTED,
192 pData->options.processMode,
193 pData->options.transportMode,
194 static_cast<int>(pData->bufferSize),
195 static_cast<float>(pData->sampleRate),
196 getCurrentDriverName());
197 return true;
200 bool close() override
202 carla_debug("CarlaEngineSDL::close()");
204 // close device
205 if (fDeviceId != 0)
207 // SDL_PauseAudioDevice(fDeviceId, 1);
208 #ifdef HAVE_SDL2
209 SDL_CloseAudioDevice(fDeviceId);
210 #else
211 SDL_CloseAudio();
212 #endif
213 fDeviceId = 0;
216 // clear engine data
217 CarlaEngine::close();
219 pData->graph.destroy();
221 // cleanup
222 if (fAudioIntBufOut != nullptr)
224 for (uint i=0; i<fAudioOutCount; ++i)
225 delete[] fAudioIntBufOut[i];
226 delete[] fAudioIntBufOut;
227 fAudioIntBufOut = nullptr;
230 fAudioOutCount = 0;
231 fDeviceName.clear();
233 return false;
236 bool hasIdleOnMainThread() const noexcept override
238 return true;
241 bool isRunning() const noexcept override
243 return fDeviceId != 0 /*&& SDL_GetAudioDeviceStatus(fDeviceId) == SDL_AUDIO_PLAYING*/;
246 bool isOffline() const noexcept override
248 return false;
251 EngineType getType() const noexcept override
253 return kEngineTypeSDL;
256 const char* getCurrentDriverName() const noexcept override
258 return "SDL";
261 // -------------------------------------------------------------------
262 // Patchbay
264 template<class Graph>
265 bool refreshExternalGraphPorts(Graph* const graph, const bool sendHost, const bool sendOSC)
267 CARLA_SAFE_ASSERT_RETURN(graph != nullptr, false);
269 char strBuf[STR_MAX+1U];
270 strBuf[STR_MAX] = '\0';
272 ExternalGraph& extGraph(graph->extGraph);
274 // ---------------------------------------------------------------
275 // clear last ports
277 extGraph.clear();
279 // ---------------------------------------------------------------
280 // fill in new ones
282 // Audio Out
283 for (uint i=0; i < fAudioOutCount; ++i)
285 std::snprintf(strBuf, STR_MAX, "playback_%i", i+1);
287 PortNameToId portNameToId;
288 portNameToId.setData(kExternalGraphGroupAudioOut, i+1, strBuf, "");
290 extGraph.audioPorts.outs.append(portNameToId);
293 // ---------------------------------------------------------------
294 // now refresh
296 if (sendHost || sendOSC)
297 graph->refresh(sendHost, sendOSC, true, fDeviceName.buffer());
299 return true;
302 bool patchbayRefresh(const bool sendHost, const bool sendOSC, const bool external) override
304 CARLA_SAFE_ASSERT_RETURN(pData->graph.isReady(), false);
306 if (pData->options.processMode == ENGINE_PROCESS_MODE_CONTINUOUS_RACK)
307 return refreshExternalGraphPorts<RackGraph>(pData->graph.getRackGraph(), sendHost, sendOSC);
309 if (sendHost)
310 pData->graph.setUsingExternalHost(external);
311 if (sendOSC)
312 pData->graph.setUsingExternalOSC(external);
314 if (external)
315 return refreshExternalGraphPorts<PatchbayGraph>(pData->graph.getPatchbayGraph(), sendHost, sendOSC);
317 return CarlaEngine::patchbayRefresh(sendHost, sendOSC, false);
320 // -------------------------------------------------------------------
322 protected:
323 void handleAudioProcessCallback(uchar* const stream, const int len)
325 // safety checks
326 CARLA_SAFE_ASSERT_RETURN(stream != nullptr,);
327 CARLA_SAFE_ASSERT_RETURN(len > 0,);
329 #ifdef HAVE_SDL2
330 // direct float type
331 float* const fstream = (float*)stream;
332 const uint ulen = static_cast<uint>(static_cast<uint>(len) / sizeof(float) / fAudioOutCount);
333 #else
334 // signed 16bit int
335 int16_t* const istream = (int16_t*)stream;
336 const uint ulen = static_cast<uint>(static_cast<uint>(len) / sizeof(int16_t) / fAudioOutCount);
337 #endif
339 const PendingRtEventsRunner prt(this, ulen, true);
341 // init our deinterleaved audio buffers
342 for (uint i=0, count=fAudioOutCount; i<count; ++i)
343 carla_zeroFloats(fAudioIntBufOut[i], ulen);
345 // initialize events
346 carla_zeroStructs(pData->events.in, kMaxEngineEventInternalCount);
347 carla_zeroStructs(pData->events.out, kMaxEngineEventInternalCount);
349 pData->graph.process(pData, nullptr, fAudioIntBufOut, ulen);
351 // interleave audio back
352 for (uint i=0; i < fAudioOutCount; ++i)
354 for (uint j=0; j < ulen; ++j)
356 #ifdef HAVE_SDL2
357 // direct float type
358 fstream[j * fAudioOutCount + i] = fAudioIntBufOut[i][j];
359 #else
360 // signed 16bit int
361 istream[j * fAudioOutCount + i] = lrintf(carla_fixedValue(-1.0f, 1.0f, fAudioIntBufOut[i][j]) * 32767.0f);
362 #endif
367 // -------------------------------------------------------------------
369 bool connectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
371 CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
372 carla_debug("CarlaEngineSDL::connectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
374 switch (connectionType)
376 case kExternalGraphConnectionAudioIn1:
377 case kExternalGraphConnectionAudioIn2:
378 case kExternalGraphConnectionAudioOut1:
379 case kExternalGraphConnectionAudioOut2:
380 return CarlaEngine::connectExternalGraphPort(connectionType, portId, portName);
382 case kExternalGraphConnectionMidiInput:
383 case kExternalGraphConnectionMidiOutput:
384 break;
387 return false;
390 bool disconnectExternalGraphPort(const uint connectionType, const uint portId, const char* const portName) override
392 CARLA_SAFE_ASSERT_RETURN(connectionType != 0 || (portName != nullptr && portName[0] != '\0'), false);
393 carla_debug("CarlaEngineSDL::disconnectExternalGraphPort(%u, %u, \"%s\")", connectionType, portId, portName);
395 switch (connectionType)
397 case kExternalGraphConnectionAudioIn1:
398 case kExternalGraphConnectionAudioIn2:
399 case kExternalGraphConnectionAudioOut1:
400 case kExternalGraphConnectionAudioOut2:
401 return CarlaEngine::disconnectExternalGraphPort(connectionType, portId, portName);
403 case kExternalGraphConnectionMidiInput:
404 case kExternalGraphConnectionMidiOutput:
405 break;
408 return false;
411 // -------------------------------------------------------------------
413 private:
414 SDL_AudioDeviceID fDeviceId;
416 // current device name
417 CarlaString fDeviceName;
419 // deinterleaved buffers
420 uint fAudioOutCount;
421 float** fAudioIntBufOut;
423 #define handlePtr ((CarlaEngineSDL*)userData)
425 static void carla_sdl_process_callback(void* userData, uchar* stream, int len)
427 handlePtr->handleAudioProcessCallback(stream, len);
430 #undef handlePtr
432 CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(CarlaEngineSDL)
435 // -----------------------------------------
437 namespace EngineInit {
439 CarlaEngine* newSDL()
441 initAudioDevicesIfNeeded();
442 return new CarlaEngineSDL();
445 const char* const* getSDLDeviceNames()
447 initAudioDevicesIfNeeded();
449 if (gDeviceNames.count() == 0)
451 static const char* deviceNames[] = { "Default", nullptr };
452 return deviceNames;
455 static const CharStringListPtr deviceNames = gDeviceNames.toCharStringListPtr();
456 return deviceNames;
461 // -----------------------------------------
463 CARLA_BACKEND_END_NAMESPACE