2#include <Audio/AudioInterface.hpp>
3#include <Audio/Settings/Model.hpp>
4#include <Audio/Settings/View.hpp>
6#include <score/command/Dispatchers/SettingsCommandDispatcher.hpp>
7#include <score/tools/Bind.hpp>
8#include <score/widgets/SignalUtils.hpp>
10#include <ossia/detail/config.hpp>
12#include <ossia/audio/miniaudio_protocol.hpp>
15#include <QCoreApplication>
19#if OSSIA_ENABLE_MINIAUDIO
25 MiniAudioCard() =
default;
26 MiniAudioCard(
const MiniAudioCard&) =
default;
27 MiniAudioCard& operator=(
const MiniAudioCard&) =
default;
28 MiniAudioCard(MiniAudioCard&&) noexcept = default;
29 MiniAudioCard& operator=(MiniAudioCard&&) noexcept = default;
30 MiniAudioCard(QString n, ma_device_type t, ma_device_id
id)
37 ma_device_type type{};
39 ma_device_info info{};
42class GenericMiniAudioFactory
46 std::shared_ptr<ossia::miniaudio_context> m_context;
49 std::vector<MiniAudioCard> devices;
51 virtual ~GenericMiniAudioFactory()
override { }
53 std::shared_ptr<ossia::miniaudio_context> acquireContext()
57 m_context = std::make_shared<ossia::miniaudio_context>();
58 auto cfg = ma_context_config_init();
59 cfg.threadPriority = ma_thread_priority_realtime;
60 cfg.threadStackSize = 8388608;
61 ma_context_init(
nullptr, 0, &cfg, &m_context->context);
67 virtual bool compareDeviceId(
const ma_device_id&
id,
const QString& str)
const noexcept
70 compareDeviceId(
const ma_device_id&
id, std::string_view str)
const noexcept
72 virtual void setDeviceId(ma_device_id&
id,
const QString& str)
const noexcept = 0;
73 virtual QString deviceIdToString(
const ma_device_id&
id)
const noexcept = 0;
85 const MiniAudioCard* user_in{};
86 const MiniAudioCard* user_out{};
88 const MiniAudioCard* default_in{};
89 const MiniAudioCard* default_out{};
91 const MiniAudioCard* first_viable_in{};
92 const MiniAudioCard* first_viable_out{};
97 for(
auto& d : devices)
101 for(
int i = 0; i < d.info.nativeDataFormatCount; i++)
103 auto fmt = d.info.nativeDataFormats[i];
108 if(d.info.nativeDataFormatCount > 0 && d.info.nativeDataFormats[0].channels > 0)
113 if(d.type & ma_device_type::ma_device_type_capture)
115 if(compareDeviceId(d.id, set.getCardIn()))
118 first_viable_in = &d;
122 if(d.type & ma_device_type::ma_device_type_playback)
124 if(compareDeviceId(d.id, set.getCardOut()))
127 first_viable_out = &d;
134 const MiniAudioCard* card_in = user_in;
136 card_in = default_in;
138 card_in = first_viable_in;
139 if(set.getCardIn().startsWith(
"No device"))
142 const MiniAudioCard* card_out = user_out;
144 card_out = default_out;
146 card_out = first_viable_out;
147 if(set.getCardOut().startsWith(
"No device"))
153 set.setCardIn(deviceIdToString(card_in->id));
154 set.setDefaultIn(card_in->info.nativeDataFormats[0].channels);
159 set.setCardIn(devices.front().name);
166 set.setCardOut(deviceIdToString(card_out->id));
167 set.setDefaultOut(card_out->info.nativeDataFormats[0].channels);
169 int current_rate = set.getRate();
170 if(
int fmt_count = card_out->info.nativeDataFormatCount; fmt_count > 0)
172 auto* fmts = card_out->info.nativeDataFormats;
173 if(std::none_of(fmts, fmts + fmt_count, [current_rate](
auto fmt) {
174 return fmt.sampleRate == current_rate;
177 set.setRate(card_out->info.nativeDataFormats[0].sampleRate);
184 set.setCardOut(devices.front().name);
185 set.setDefaultOut(0);
193 auto ctx = acquireContext();
199 devices[0].name =
"No device";
200 memset(&devices[0].
id, 0,
sizeof(devices[0].
id));
201 memset(&devices[0].info, 0,
sizeof(devices[0].info));
203 setDeviceId(devices[0].
id,
"No device");
205 ma_context_enumerate_devices(
207 [](ma_context* ctx, ma_device_type dev_type,
const ma_device_info* dev_info,
208 void* data) -> ma_bool32 {
209 auto& self = *(GenericMiniAudioFactory*)data;
210 self.devices.emplace_back(dev_info->name, dev_type, dev_info->id);
215 for(std::size_t i = 1; i < devices.size(); i++)
217 auto& dev = devices[i];
218 ma_context_get_device_info(&m_context->context, dev.type, &dev.id, &dev.info);
222 std::shared_ptr<ossia::audio_engine> make_engine(
225 ma_device_id info_in;
226 ma_device_id info_out;
227 memset(&info_in, 0,
sizeof(info_in));
228 memset(&info_out, 0,
sizeof(info_out));
230 auto card_in = set.getCardIn().toStdString();
231 auto card_out = set.getCardOut().toStdString();
233 for(
auto& dev : this->devices)
235 if(compareDeviceId(dev.id, card_in))
237 if(compareDeviceId(dev.id, card_out))
241 return std::make_shared<ossia::miniaudio_engine>(
242 acquireContext(),
"ossia score", info_in, info_out, set.getDefaultIn(),
243 set.getDefaultOut(), set.getRate(), set.getBufferSize());
246 void setCard(QComboBox* combo, ma_device_type tp, QString val)
250 = ossia::find_if(devices, [&,
id = val.toStdString()](
const MiniAudioCard& d) {
252 return d.id.coreaudio == id && d.type == tp;
254 if(dev_it != devices.end())
256 int device_index = std::distance(devices.begin(), dev_it);
257 for(
int i = 0; i < combo->count(); i++)
260 if(combo->itemData(i).toInt() == device_index)
262 combo->setCurrentIndex(i);
268 combo->setCurrentIndex(0);
271 QWidget* make_settings(
296 auto w =
new QWidget{parent};
297 auto lay =
new QFormLayout{w};
299 auto card_list_in =
new QComboBox{w};
300 auto card_list_out =
new QComboBox{w};
303 card_list_in->addItem(devices.front().name +
" (capture)", 0);
304 card_list_out->addItem(devices.front().name +
" (playback)", 0);
307 for(std::size_t i = 1; i < devices.size(); i++)
310 auto& dev = devices[i];
312 if(dev.info.nativeDataFormatCount > 0)
314 if(dev.info.nativeDataFormats[0].channels > 0)
316 if(dev.type == ma_device_type_capture)
318 card_list_in->addItem(dev.name, (
int)i);
321 if(dev.type == ma_device_type_playback)
322 card_list_out->addItem(dev.name, (
int)i);
338 lay->addRow(QObject::tr(
"Capture"), card_list_in);
340 auto update_dev_in = [=, &m, &m_disp](
const MiniAudioCard& dev) {
341 if(!compareDeviceId(dev.id, m.getCardIn()))
343 m_disp.submitDeferredCommand<Audio::Settings::SetModelCardIn>(
344 m, deviceIdToString(dev.id));
345 m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultIn>(
346 m, dev.info.nativeDataFormats[0].channels);
351 card_list_in, SignalUtils::QComboBox_currentIndexChanged_int(), &v,
353 auto& device = devices[card_list_in->itemData(i).toInt()];
354 update_dev_in(device);
357 if(m.getCardIn().isEmpty())
361 update_dev_in(devices.front());
366 setCard(card_list_in, ma_device_type_capture, m.getCardIn());
371 lay->addRow(QObject::tr(
"Playback"), card_list_out);
373 auto update_dev_out = [=, &m, &m_disp](
const MiniAudioCard& dev) {
374 if(!compareDeviceId(dev.id, m.getCardOut()))
376 m_disp.submitDeferredCommand<Audio::Settings::SetModelCardOut>(
377 m, deviceIdToString(dev.id));
378 m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultOut>(
379 m, dev.info.nativeDataFormats[0].channels);
384 card_list_out, SignalUtils::QComboBox_currentIndexChanged_int(), &v,
386 auto& device = devices[card_list_out->itemData(i).toInt()];
387 update_dev_out(device);
390 if(m.getCardOut().isEmpty())
394 update_dev_out(devices.front());
399 setCard(card_list_out, ma_device_type_playback, m.getCardOut());
403 addBufferSizeWidget(*w, m, v);
404 addSampleRateWidget(*w, m, v);
406 con(m, &Model::changed, w, [=, &m] {
407 setCard(card_list_in, ma_device_type_capture, m.getCardIn());
408 setCard(card_list_out, ma_device_type_playback, m.getCardOut());
Definition score-plugin-audio/Audio/Settings/Model.hpp:22
Definition score-plugin-audio/Audio/Settings/View.hpp:19
Definition SettingsCommandDispatcher.hpp:10
Used to access all the application-wide state and structures.
Definition ApplicationContext.hpp:24