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"
27 typedef Uint32 SDL_AudioDeviceID
;
30 #ifndef SDL_HINT_AUDIO_DEVICE_APP_NAME
31 # define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME"
34 #ifndef SDL_HINT_AUDIO_DEVICE_STREAM_NAME
35 # define SDL_HINT_AUDIO_DEVICE_STREAM_NAME "SDL_AUDIO_DEVICE_STREAM_NAME"
38 CARLA_BACKEND_START_NAMESPACE
40 // -------------------------------------------------------------------------------------------------------------------
43 static CarlaStringList gDeviceNames
;
45 // -------------------------------------------------------------------------------------------------------------------
47 static void initAudioDevicesIfNeeded()
49 static bool needsInit
= true;
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));
64 SDL_Init(SDL_INIT_AUDIO
);
68 // -------------------------------------------------------------------------------------------------------------------
71 class CarlaEngineSDL
: public CarlaEngine
79 fAudioIntBufOut(nullptr)
81 carla_debug("CarlaEngineSDL::CarlaEngineSDL()");
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");
108 SDL_AudioSpec requested
, received
;
109 carla_zeroStruct(requested
);
111 requested
.format
= AUDIO_F32SYS
;
113 requested
.format
= AUDIO_S16SYS
;
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;
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
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
);
136 fDeviceId
= SDL_OpenAudio(&requested
, &received
) == 0 ? 1 : 0;
141 setLastError(SDL_GetError());
145 if (received
.channels
== 0)
148 SDL_CloseAudioDevice(fDeviceId
);
153 setLastError("No output channels available");
157 if (! pData
->init(clientName
))
160 setLastError("Failed to init internal data");
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);
176 SDL_PauseAudioDevice(fDeviceId
, 0);
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);
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());
200 bool close() override
202 carla_debug("CarlaEngineSDL::close()");
207 // SDL_PauseAudioDevice(fDeviceId, 1);
209 SDL_CloseAudioDevice(fDeviceId
);
217 CarlaEngine::close();
219 pData
->graph
.destroy();
222 if (fAudioIntBufOut
!= nullptr)
224 for (uint i
=0; i
<fAudioOutCount
; ++i
)
225 delete[] fAudioIntBufOut
[i
];
226 delete[] fAudioIntBufOut
;
227 fAudioIntBufOut
= nullptr;
236 bool hasIdleOnMainThread() const noexcept override
241 bool isRunning() const noexcept override
243 return fDeviceId
!= 0 /*&& SDL_GetAudioDeviceStatus(fDeviceId) == SDL_AUDIO_PLAYING*/;
246 bool isOffline() const noexcept override
251 EngineType
getType() const noexcept override
253 return kEngineTypeSDL
;
256 const char* getCurrentDriverName() const noexcept override
261 // -------------------------------------------------------------------
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 // ---------------------------------------------------------------
279 // ---------------------------------------------------------------
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 // ---------------------------------------------------------------
296 if (sendHost
|| sendOSC
)
297 graph
->refresh(sendHost
, sendOSC
, true, fDeviceName
.buffer());
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
);
310 pData
->graph
.setUsingExternalHost(external
);
312 pData
->graph
.setUsingExternalOSC(external
);
315 return refreshExternalGraphPorts
<PatchbayGraph
>(pData
->graph
.getPatchbayGraph(), sendHost
, sendOSC
);
317 return CarlaEngine::patchbayRefresh(sendHost
, sendOSC
, false);
320 // -------------------------------------------------------------------
323 void handleAudioProcessCallback(uchar
* const stream
, const int len
)
326 CARLA_SAFE_ASSERT_RETURN(stream
!= nullptr,);
327 CARLA_SAFE_ASSERT_RETURN(len
> 0,);
331 float* const fstream
= (float*)stream
;
332 const uint ulen
= static_cast<uint
>(static_cast<uint
>(len
) / sizeof(float) / fAudioOutCount
);
335 int16_t* const istream
= (int16_t*)stream
;
336 const uint ulen
= static_cast<uint
>(static_cast<uint
>(len
) / sizeof(int16_t) / fAudioOutCount
);
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
);
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
)
358 fstream
[j
* fAudioOutCount
+ i
] = fAudioIntBufOut
[i
][j
];
361 istream
[j
* fAudioOutCount
+ i
] = lrintf(carla_fixedValue(-1.0f
, 1.0f
, fAudioIntBufOut
[i
][j
]) * 32767.0f
);
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
:
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
:
411 // -------------------------------------------------------------------
414 SDL_AudioDeviceID fDeviceId
;
416 // current device name
417 CarlaString fDeviceName
;
419 // deinterleaved buffers
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
);
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 };
455 static const CharStringListPtr deviceNames
= gDeviceNames
.toCharStringListPtr();
461 // -----------------------------------------
463 CARLA_BACKEND_END_NAMESPACE