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