2#include <ossia/detail/config.hpp>
7#include <Audio/AudioInterface.hpp>
8#include <Audio/Settings/Model.hpp>
9#include <Audio/Settings/View.hpp>
11#include <score/command/Dispatchers/SettingsCommandDispatcher.hpp>
12#include <score/tools/Bind.hpp>
13#include <score/widgets/SignalUtils.hpp>
15#include <ossia/audio/miniaudio_protocol.hpp>
18#include <QCoreApplication>
20#if OSSIA_ENABLE_MINIAUDIO
21#include <ossia/audio/miniaudio_protocol.hpp>
23#include <magic_enum/magic_enum.hpp>
28 MiniAudioCard() =
default;
29 MiniAudioCard(
const MiniAudioCard&) =
default;
30 MiniAudioCard& operator=(
const MiniAudioCard&) =
default;
31 MiniAudioCard(MiniAudioCard&&) noexcept = default;
32 MiniAudioCard& operator=(MiniAudioCard&&) noexcept = default;
33 MiniAudioCard(QString n, ma_device_type t, ma_device_id
id)
40 ma_device_type type{};
42 ma_device_info info{};
45class CoreAudioFactory final
49 SCORE_CONCRETE(
"85115103-694a-4a3b-9274-76ef47aec5a9")
50 std::shared_ptr<ossia::miniaudio_context> m_context;
53 std::vector<MiniAudioCard> devices;
55 CoreAudioFactory() { rescan(); }
57 std::shared_ptr<ossia::miniaudio_context> acquireContext()
61 m_context = std::make_shared<ossia::miniaudio_context>();
62 auto cfg = ma_context_config_init();
63 cfg.threadPriority = ma_thread_priority_realtime;
64 cfg.threadStackSize = 8388608;
65 ma_context_init(
nullptr, 0, &cfg, &m_context->context);
71 ~CoreAudioFactory()
override { }
72 bool available() const noexcept
override {
return true; }
83 const MiniAudioCard* user_in{};
84 const MiniAudioCard* user_out{};
86 const MiniAudioCard* default_in{};
87 const MiniAudioCard* default_out{};
89 const MiniAudioCard* first_viable_in{};
90 const MiniAudioCard* first_viable_out{};
95 for(
auto& d : devices)
99 for(
int i = 0; i < d.info.nativeDataFormatCount; i++)
101 auto fmt = d.info.nativeDataFormats[i];
106 if(d.info.nativeDataFormatCount > 0 && d.info.nativeDataFormats[0].channels > 0)
111 if(d.type & ma_device_type::ma_device_type_capture)
113 if(d.id.coreaudio == set.getCardIn())
116 first_viable_in = &d;
120 if(d.type & ma_device_type::ma_device_type_playback)
122 if(d.id.coreaudio == set.getCardOut())
125 first_viable_out = &d;
132 const MiniAudioCard* card_in = user_in;
134 card_in = default_in;
136 card_in = first_viable_in;
137 if(set.getCardIn() ==
"No device")
140 const MiniAudioCard* card_out = user_out;
142 card_out = default_out;
144 card_out = first_viable_out;
145 if(set.getCardOut() ==
"No device")
151 set.setCardIn(card_in->id.coreaudio);
152 set.setDefaultIn(card_in->info.nativeDataFormats[0].channels);
157 set.setCardIn(devices.front().name);
164 set.setCardOut(card_out->id.coreaudio);
165 set.setDefaultOut(card_out->info.nativeDataFormats[0].channels);
167 int current_rate = set.getRate();
168 if(
int fmt_count = card_out->info.nativeDataFormatCount; fmt_count > 0)
170 auto* fmts = card_out->info.nativeDataFormats;
171 if(std::none_of(fmts, fmts + fmt_count, [current_rate](
auto fmt) {
172 return fmt.sampleRate == current_rate;
175 set.setRate(card_out->info.nativeDataFormats[0].sampleRate);
182 set.setCardOut(devices.front().name);
183 set.setDefaultOut(0);
191 auto ctx = acquireContext();
197 devices[0].name =
"No device";
198 memset(&devices[0].
id, 0,
sizeof(devices[0].
id));
199 memset(&devices[0].info, 0,
sizeof(devices[0].info));
201 ma_context_enumerate_devices(
203 [](ma_context* ctx, ma_device_type dev_type,
const ma_device_info* dev_info,
204 void* data) -> ma_bool32 {
205 auto& self = *(CoreAudioFactory*)data;
206 self.devices.emplace_back(dev_info->name, dev_type, dev_info->id);
211 for(std::size_t i = 1; i < devices.size(); i++)
213 auto& dev = devices[i];
214 ma_context_get_device_info(&m_context->context, dev.type, &dev.id, &dev.info);
218 QString prettyName()
const override {
return QObject::tr(
"CoreAudio"); }
219 std::shared_ptr<ossia::audio_engine> make_engine(
222 ma_device_id info_in;
223 ma_device_id info_out;
224 memset(&info_in, 0,
sizeof(info_in));
225 memset(&info_out, 0,
sizeof(info_out));
227 auto card_in = set.getCardIn().toStdString();
228 auto card_out = set.getCardOut().toStdString();
230 for(
auto& dev : this->devices)
232 if(dev.id.coreaudio == card_in)
234 if(dev.id.coreaudio == card_out)
238 return std::make_shared<ossia::miniaudio_engine>(
239 acquireContext(),
"ossia score", info_in, info_out, set.getDefaultIn(),
240 set.getDefaultOut(), set.getRate(), set.getBufferSize());
243 void setCard(QComboBox* combo, ma_device_type tp, QString val)
247 = ossia::find_if(devices, [&,
id = val.toStdString()](
const MiniAudioCard& d) {
249 return d.id.coreaudio == id && d.type == tp;
251 if(dev_it != devices.end())
253 int device_index = std::distance(devices.begin(), dev_it);
254 for(
int i = 0; i < combo->count(); i++)
257 if(combo->itemData(i).toInt() == device_index)
259 combo->setCurrentIndex(i);
265 combo->setCurrentIndex(0);
268 QWidget* make_settings(
293 auto w =
new QWidget{parent};
294 auto lay =
new QFormLayout{w};
296 auto card_list_in =
new QComboBox{w};
297 auto card_list_out =
new QComboBox{w};
300 card_list_in->addItem(devices.front().name +
"capture", 0);
301 card_list_out->addItem(devices.front().name +
"playback", 0);
304 for(std::size_t i = 1; i < devices.size(); i++)
307 auto& dev = devices[i];
309 if(dev.info.nativeDataFormatCount > 0)
311 if(dev.info.nativeDataFormats[0].channels > 0)
313 if(dev.type == ma_device_type_capture)
315 card_list_in->addItem(dev.name, (
int)i);
318 if(dev.type == ma_device_type_playback)
319 card_list_out->addItem(dev.name, (
int)i);
335 lay->addRow(QObject::tr(
"Capture"), card_list_in);
337 auto update_dev_in = [=, &m, &m_disp](
const MiniAudioCard& dev) {
338 if(dev.id.coreaudio != m.getCardIn())
340 m_disp.submitDeferredCommand<Audio::Settings::SetModelCardIn>(
341 m, dev.id.coreaudio);
342 m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultIn>(
343 m, dev.info.nativeDataFormats[0].channels);
348 card_list_in, SignalUtils::QComboBox_currentIndexChanged_int(), &v,
350 auto& device = devices[card_list_in->itemData(i).toInt()];
351 update_dev_in(device);
354 if(m.getCardIn().isEmpty())
358 update_dev_in(devices.front());
363 setCard(card_list_in, ma_device_type_capture, m.getCardIn());
368 lay->addRow(QObject::tr(
"Playback"), card_list_out);
370 auto update_dev_out = [=, &m, &m_disp](
const MiniAudioCard& dev) {
371 if(dev.id.coreaudio != m.getCardOut())
373 m_disp.submitDeferredCommand<Audio::Settings::SetModelCardOut>(
374 m, dev.id.coreaudio);
375 m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultOut>(
376 m, dev.info.nativeDataFormats[0].channels);
381 card_list_out, SignalUtils::QComboBox_currentIndexChanged_int(), &v,
383 auto& device = devices[card_list_out->itemData(i).toInt()];
384 update_dev_out(device);
387 if(m.getCardOut().isEmpty())
391 update_dev_out(devices.front());
396 setCard(card_list_out, ma_device_type_playback, m.getCardOut());
400 addBufferSizeWidget(*w, m, v);
401 addSampleRateWidget(*w, m, v);
403 con(m, &Model::changed, w, [=, &m] {
404 setCard(card_list_in, ma_device_type_capture, m.getCardIn());
405 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