Loading...
Searching...
No Matches
CoreAudioPortAudioInterface.hpp
1#pragma once
2#include <Audio/AudioInterface.hpp>
3#include <Audio/PortAudioInterface.hpp>
4#include <Audio/Settings/Model.hpp>
5#include <Audio/Settings/View.hpp>
6
7#include <score/command/Dispatchers/SettingsCommandDispatcher.hpp>
8#include <score/tools/Bind.hpp>
9#include <score/widgets/SignalUtils.hpp>
10
11#include <QComboBox>
12#include <QFormLayout>
13#if __has_include(<pa_mac_core.h>)
14#include <pa_mac_core.h>
15#endif
16
17namespace Audio
18{
19#if __has_include(<pa_mac_core.h>)
20class CoreAudioPortAudioFactory final
21 : public QObject
22 , public AudioFactory
23{
24 SCORE_CONCRETE("e75cb711-613f-4f15-834f-398ab1807470")
25public:
26 std::vector<PortAudioCard> devices;
27
28 CoreAudioPortAudioFactory() { rescan(); }
29
30 ~CoreAudioPortAudioFactory() override { }
31 bool available() const noexcept override { return true; }
32 void
33 initialize(Audio::Settings::Model& set, const score::ApplicationContext& ctx) override
34 {
35 auto device_in = ossia::find_if(devices, [&](const PortAudioCard& dev) {
36 return dev.raw_name == set.getCardIn() && dev.hostapi != paInDevelopment;
37 });
38 auto device_out = ossia::find_if(devices, [&](const PortAudioCard& dev) {
39 return dev.raw_name == set.getCardOut() && dev.hostapi != paInDevelopment;
40 });
41
42 if(device_in == devices.end() || device_out == devices.end())
43 {
44 auto default_device_in = ossia::find_if(devices, [&](const PortAudioCard& dev) {
45 return dev.inputChan > 0 && dev.defaultDevice;
46 });
47 auto default_device_out = ossia::find_if(devices, [&](const PortAudioCard& dev) {
48 return dev.outputChan > 0 && dev.defaultDevice;
49 });
50
51 if(default_device_in != devices.end())
52 {
53 set.setCardIn(default_device_in->raw_name);
54 set.setDefaultIn(default_device_in->inputChan);
55 }
56 else
57 {
58 set.setCardIn(devices.back().raw_name);
59 set.setDefaultIn(devices.back().inputChan);
60 }
61
62 if(default_device_out != devices.end())
63 {
64 set.setCardOut(default_device_out->raw_name);
65 set.setDefaultOut(default_device_out->outputChan);
66 set.setRate(default_device_out->rate);
67 }
68 else
69 {
70 set.setCardOut(devices.back().raw_name);
71 set.setDefaultOut(devices.back().outputChan);
72 set.setRate(devices.back().rate);
73 }
74
75 set.changed();
76 }
77 else
78 {
79 if(device_out != devices.end())
80 {
81 set.setDefaultIn(device_out->inputChan);
82 set.setDefaultOut(device_out->outputChan);
83 set.setRate(device_out->rate);
84
85 set.changed();
86 }
87 }
88 }
89
90 void rescan()
91 {
92 devices.clear();
93 PortAudioScope portaudio;
94
95 devices.push_back(PortAudioCard{{}, {}, QObject::tr("No device"), -1, 0, 0, {}});
96 for(int i = 0; i < Pa_GetHostApiCount(); i++)
97 {
98 auto hostapi = Pa_GetHostApiInfo(i);
99 if(hostapi->type == PaHostApiTypeId::paCoreAudio)
100 {
101 for(int card = 0; card < hostapi->deviceCount; card++)
102 {
103 auto dev_idx = Pa_HostApiDeviceIndexToDeviceIndex(i, card);
104 auto dev = Pa_GetDeviceInfo(dev_idx);
105 auto raw_name = QString::fromUtf8(Pa_GetDeviceInfo(dev_idx)->name);
106
107 devices.push_back(PortAudioCard{
108 "CoreAudio", raw_name, raw_name, dev_idx, dev->maxInputChannels,
109 dev->maxOutputChannels, hostapi->type, dev->defaultSampleRate,
110 hostapi->defaultInputDevice == dev_idx
111 || hostapi->defaultOutputDevice == dev_idx});
112 }
113 }
114 }
115 }
116
117 QString prettyName() const override { return QObject::tr("CoreAudio"); }
118 std::shared_ptr<ossia::audio_engine> make_engine(
119 const Audio::Settings::Model& set, const score::ApplicationContext& ctx) override
120 {
121 return std::make_shared<ossia::portaudio_engine>(
122 "ossia score", set.getCardIn().toStdString(), set.getCardOut().toStdString(),
123 set.getDefaultIn(), set.getDefaultOut(), set.getRate(), set.getBufferSize(),
124 paCoreAudio);
125 }
126
127 void setCard(QComboBox* combo, QString val)
128 {
129 auto dev_it = ossia::find_if(
130 devices, [&](const PortAudioCard& d) { return d.raw_name == val; });
131 if(dev_it != devices.end())
132 {
133 combo->setCurrentIndex(dev_it->out_index);
134 }
135 }
136
137 QWidget* make_settings(
139 score::SettingsCommandDispatcher& m_disp, QWidget* parent) override
140 {
141 auto w = new QWidget{parent};
142 auto lay = new QFormLayout{w};
143
144 auto card_list = new QComboBox{w};
145
146 // Disabled case
147 card_list->addItem(devices.front().pretty_name, 0);
148 devices.front().out_index = 0;
149
150 // Normal devices
151 for(std::size_t i = 1; i < devices.size(); i++)
152 {
153 auto& card = devices[i];
154 card_list->addItem(card.pretty_name, (int)i);
155 card.out_index = card_list->count() - 1;
156 }
157
158 using Model = Audio::Settings::Model;
159
160 {
161 lay->addRow(QObject::tr("Device"), card_list);
162
163 auto update_dev = [=, &m, &m_disp](const PortAudioCard& dev) {
164 if(dev.raw_name != m.getCardOut())
165 {
166 m_disp.submitDeferredCommand<Audio::Settings::SetModelCardIn>(m, dev.raw_name);
167 m_disp.submitDeferredCommand<Audio::Settings::SetModelCardOut>(
168 m, dev.raw_name);
169 m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultIn>(
170 m, dev.inputChan);
171 m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultOut>(
172 m, dev.outputChan);
173 }
174 };
175
176 QObject::connect(
177 card_list, SignalUtils::QComboBox_currentIndexChanged_int(), &v, [=](int i) {
178 auto& device = devices[card_list->itemData(i).toInt()];
179 update_dev(device);
180 });
181
182 if(m.getCardOut().isEmpty())
183 {
184 if(!devices.empty())
185 {
186 update_dev(devices.front());
187 }
188 }
189 else
190 {
191 setCard(card_list, m.getCardOut());
192 }
193 }
194
195 addBufferSizeWidget(*w, m, v);
196 addSampleRateWidget(*w, m, v);
197
198 con(m, &Model::changed, w, [=, &m] { setCard(card_list, m.getCardOut()); });
199 return w;
200 }
201};
202#endif
203
204}
Definition score-plugin-audio/Audio/Settings/Model.hpp:22
Definition score-plugin-audio/Audio/Settings/View.hpp:19
Definition SettingsCommandDispatcher.hpp:10
STL namespace.
Used to access all the application-wide state and structures.
Definition ApplicationContext.hpp:24