Loading...
Searching...
No Matches
ASIOPortAudioInterface.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#include <QPushButton>
14
15#if __has_include(<pa_asio.h>)
16#include <pa_asio.h>
17
18#if !defined(WIN32_LEAN_AND_MEAN)
19#define WIN32_LEAN_AND_MEAN
20#endif
21#if !defined(NOMINMAX)
22#define NOMINMAX
23#endif
24#include <Windows.h>
25#endif
26
27namespace Audio
28{
29#if __has_include(<pa_asio.h>)
30class ASIOFactory final
31 : public QObject
32 , public AudioFactory
33{
34 SCORE_CONCRETE("6b34c6dd-8201-448f-859c-d014f8d01448")
35public:
36 std::vector<PortAudioCard> devices;
37
38 ASIOFactory() { rescan(); }
39
40 ~ASIOFactory() override { }
41 bool available() const noexcept override { return true; }
42 void
43 initialize(Audio::Settings::Model& set, const score::ApplicationContext& ctx) override
44 {
45 }
46
47 void rescan()
48 {
49 devices.clear();
50 PortAudioScope portaudio;
51
52 devices.push_back(PortAudioCard{{}, {}, QObject::tr("No device"), -1, 0, 0, {}});
53 for(int i = 0; i < Pa_GetHostApiCount(); i++)
54 {
55 auto hostapi = Pa_GetHostApiInfo(i);
56 if(hostapi->type == PaHostApiTypeId::paASIO)
57 {
58 for(int card = 0; card < hostapi->deviceCount; card++)
59 {
60 auto dev_idx = Pa_HostApiDeviceIndexToDeviceIndex(i, card);
61 auto dev = Pa_GetDeviceInfo(dev_idx);
62 auto raw_name = QString::fromUtf8(Pa_GetDeviceInfo(dev_idx)->name);
63
64 devices.push_back(PortAudioCard{
65 "ASIO", raw_name, raw_name, dev_idx, dev->maxInputChannels,
66 dev->maxOutputChannels, hostapi->type, dev->defaultSampleRate});
67 }
68 }
69 }
70 }
71
72 QString prettyName() const override { return QObject::tr("ASIO"); }
73 std::shared_ptr<ossia::audio_engine> make_engine(
74 const Audio::Settings::Model& set, const score::ApplicationContext& ctx) override
75 {
76 return std::make_shared<ossia::portaudio_engine>(
77 "ossia score", set.getCardIn().toStdString(), set.getCardOut().toStdString(),
78 set.getDefaultIn(), set.getDefaultOut(), set.getRate(), set.getBufferSize(),
79 paASIO);
80 }
81
82 void setCard(QComboBox* combo, QString val)
83 {
84 auto dev_it = ossia::find_if(
85 devices, [&](const PortAudioCard& d) { return d.raw_name == val; });
86 if(dev_it != devices.end())
87 {
88 combo->setCurrentIndex(dev_it->out_index);
89 }
90 }
91
92 QWidget* make_settings(
94 score::SettingsCommandDispatcher& m_disp, QWidget* parent) override
95 {
96 auto w = new QWidget{parent};
97 auto lay = new QFormLayout{w};
98
99 auto card_list = new QComboBox{w};
100 auto show_ui = new QPushButton{tr("Show Control Panel"), w};
101
102 // Disabled case
103 card_list->addItem(devices.front().pretty_name, 0);
104 devices.front().out_index = 0;
105
106 // Normal devices
107 for(std::size_t i = 1; i < devices.size(); i++)
108 {
109 auto& card = devices[i];
110 card_list->addItem(card.pretty_name, (int)i);
111 card.out_index = card_list->count() - 1;
112 }
113
114 using Model = Audio::Settings::Model;
115
116 {
117 lay->addRow(QObject::tr("Device"), card_list);
118
119 auto update_dev = [=, &m, &m_disp](const PortAudioCard& dev) {
120 if(dev.raw_name != m.getCardOut())
121 {
122 m_disp.submitDeferredCommand<Audio::Settings::SetModelCardIn>(m, dev.raw_name);
123 m_disp.submitDeferredCommand<Audio::Settings::SetModelCardOut>(
124 m, dev.raw_name);
125 m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultIn>(
126 m, dev.inputChan);
127 m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultOut>(
128 m, dev.outputChan);
129 }
130 };
131
132 QObject::connect(
133 card_list, SignalUtils::QComboBox_currentIndexChanged_int(), &v, [=](int i) {
134 auto& device = devices[card_list->itemData(i).toInt()];
135 update_dev(device);
136 });
137
138 if(m.getCardOut().isEmpty())
139 {
140 if(!devices.empty())
141 {
142 update_dev(devices.front());
143 }
144 }
145 else
146 {
147 setCard(card_list, m.getCardOut());
148 }
149 }
150
151 {
152 lay->addWidget(show_ui);
153 connect(show_ui, &QPushButton::clicked, this, [=] {
154 auto& dev = devices[card_list->itemData(card_list->currentIndex()).toInt()];
155 PortAudioScope portaudio;
156 PaAsio_ShowControlPanel(dev.dev_idx, GetActiveWindow());
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
169}
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