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