Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions src/plugins/score-plugin-audio/Audio/ASIOInterface.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
#pragma once
#include <ossia/detail/config.hpp>
#include <Audio/AudioInterface.hpp>
#include <Audio/Settings/Model.hpp>
#include <Audio/Settings/View.hpp>

#include <score/command/Dispatchers/SettingsCommandDispatcher.hpp>
#include <score/tools/Bind.hpp>
#include <score/widgets/SignalUtils.hpp>

#include <QComboBox>
#include <QFormLayout>
#include <QPushButton>
#include <QLabel>

#if defined(OSSIA_ENABLE_ASIO)
#include <ossia/audio/asio_protocol.hpp>

extern AsioDrivers* asioDrivers;
namespace Audio
{
struct ASIOCard
{
QString name;
int driver_index{-1};
int inputChan{};
int outputChan{};
};

class NativeASIOFactory final
: public QObject
, public AudioFactory
{
SCORE_CONCRETE("2d21a3aa-f108-4e05-a3a0-8fd6b150bda5")
public:
std::vector<ASIOCard> devices;

NativeASIOFactory() { rescan(); }
~NativeASIOFactory() override { }

bool available() const noexcept override { return !devices.empty(); }

void
initialize(Audio::Settings::Model& set, const score::ApplicationContext& ctx) override
{
rescan();

if(!set.getCardOut().isEmpty())
{
// Check that the saved device still exists
auto it = std::find_if(
devices.begin(), devices.end(),
[&](const ASIOCard& d) { return d.name == set.getCardOut(); });
if(it != devices.end())
return;
}

// Auto-select first device
if(devices.size() > 1)
{
auto& dev = devices[1]; // [0] is "No device"
set.setCardIn(dev.name);
set.setCardOut(dev.name);
set.setDefaultIn(dev.inputChan);
set.setDefaultOut(dev.outputChan);
}
}

void rescan()
{
devices.clear();
devices.push_back(ASIOCard{QObject::tr("No device"), -1, 0, 0});

try
{
auto cards = ossia::asio_engine::enumerate_drivers();
for(auto& card : cards)
{
// To get channel counts, we must briefly load each driver
int ins = 0, outs = 0;
if(loadAsioDriver(const_cast<char*>(card.name.c_str())))
{
ASIODriverInfo info{};
info.asioVersion = 2;
if(ASIOInit(&info) == ASE_OK)
{
long numIn = 0, numOut = 0;
ASIOGetChannels(&numIn, &numOut);
ins = (int)numIn;
outs = (int)numOut;
ASIOExit();
}
if(asioDrivers)
asioDrivers->removeCurrentDriver();
}

devices.push_back(
ASIOCard{QString::fromStdString(card.name), card.driver_index, ins, outs});
}
}
catch(...)
{
}
}

QString prettyName() const override { return QObject::tr("ASIOSDK"); }

std::shared_ptr<ossia::audio_engine> make_engine(
const Audio::Settings::Model& set, const score::ApplicationContext& ctx) override
{
return std::make_shared<ossia::asio_engine>(
set.getCardOut().toStdString(), set.getDefaultIn(), set.getDefaultOut(),
set.getRate(), set.getBufferSize());
}

void setCard(QComboBox* combo, QString val)
{
for(int i = 0; i < combo->count(); i++)
{
if(combo->itemData(i).toString() == val)
{
combo->setCurrentIndex(i);
return;
}
}
}

QWidget* make_settings(
Audio::Settings::Model& m, Audio::Settings::View& v,
score::SettingsCommandDispatcher& m_disp, QWidget* parent) override
{
auto w = new QWidget{parent};
auto lay = new QFormLayout{w};

auto card_list = new QComboBox{w};
auto show_ui = new QPushButton{tr("Show Control Panel"), w};

// Populate device list
for(std::size_t i = 0; i < devices.size(); i++)
{
auto& card = devices[i];
card_list->addItem(card.name, card.name);
}

using Model = Audio::Settings::Model;

{
lay->addRow(QObject::tr("Device"), card_list);

auto update_dev = [=, &m, &m_disp](const ASIOCard& dev) {
if(dev.name != m.getCardOut())
{
m_disp.submitDeferredCommand<Audio::Settings::SetModelCardIn>(m, dev.name);
m_disp.submitDeferredCommand<Audio::Settings::SetModelCardOut>(m, dev.name);
m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultIn>(
m, dev.inputChan);
m_disp.submitDeferredCommand<Audio::Settings::SetModelDefaultOut>(
m, dev.outputChan);
}
};

QObject::connect(
card_list, SignalUtils::QComboBox_currentIndexChanged_int(), &v, [=](int i) {
if(i >= 0 && i < (int)devices.size())
{
update_dev(devices[i]);
}
});

if(m.getCardOut().isEmpty())
{
if(devices.size() > 1)
{
update_dev(devices[1]);
}
}
else
{
setCard(card_list, m.getCardOut());
}
}

{
lay->addWidget(show_ui);
connect(show_ui, &QPushButton::clicked, this, [=] {
int idx = card_list->currentIndex();
if(idx > 0 && idx < (int)devices.size())
{
ossia::asio_engine::open_control_panel(devices[idx].name.toStdString());
}
});
}

addBufferSizeWidget(*w, m, v);
addSampleRateWidget(*w, m, v);

con(m, &Model::changed, w, [=, &m] { setCard(card_list, m.getCardOut()); });
return w;
}
};

}

#endif
2 changes: 2 additions & 0 deletions src/plugins/score-plugin-audio/Audio/Settings/Model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ void Model::initDriver(Audio::AudioFactory::ConcreteKey val)
uid = "3533ee88-9a8d-486c-b20b-6c966cf4eaa0";
else if(env == "alsa_miniaudio")
uid = "e0c533da-a1f4-4795-90b5-a805cdfcb79f";
else if(env == "asio")
uid = "2d21a3aa-f108-4e05-a3a0-8fd6b150bda5";

if(!uid.isEmpty())
{
Expand Down
6 changes: 6 additions & 0 deletions src/plugins/score-plugin-audio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ set(HDRS
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/ALSAInterface.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/ALSAMiniAudioInterface.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/ALSAPortAudioInterface.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/ASIOInterface.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/ASIOPortAudioInterface.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/CoreAudioInterface.hpp"
"${CMAKE_CURRENT_SOURCE_DIR}/Audio/CoreAudioPortAudioInterface.hpp"
Expand Down Expand Up @@ -89,6 +90,11 @@ if(OSSIA_ENABLE_PIPEWIRE)
list(APPEND SCORE_FEATURES_LIST pipewire)
endif()

if(OSSIA_ENABLE_ASIO AND TARGET asio::sdk)
target_link_libraries(${PROJECT_NAME} PRIVATE $<BUILD_INTERFACE:asio::sdk>)
list(APPEND SCORE_FEATURES_LIST asio)
endif()

if(OSSIA_ENABLE_SDL)
target_link_libraries(${PROJECT_NAME} PRIVATE $<BUILD_INTERFACE:ossia::sdl2>)
list(APPEND SCORE_FEATURES_LIST sdl)
Expand Down
12 changes: 12 additions & 0 deletions src/plugins/score-plugin-audio/score_plugin_audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <Audio/ALSAInterface.hpp>
#include <Audio/ALSAMiniAudioInterface.hpp>
#include <Audio/ALSAPortAudioInterface.hpp>
#include <Audio/ASIOInterface.hpp>
#include <Audio/ASIOPortAudioInterface.hpp>
#include <Audio/AudioApplicationPlugin.hpp>
#include <Audio/AudioDevice.hpp>
Expand Down Expand Up @@ -151,6 +152,13 @@ std::vector<score::InterfaceBase*> score_plugin_audio::factories(
#if defined(__linux__) && defined(OSSIA_AUDIO_MINIAUDIO)
add_factories<FW<Audio::AudioFactory, Audio::ALSAMiniAudioFactory>>(vec, ctx, key);
return vec;
#endif
}
else if(forced_backend == "asio")
{
#if defined(OSSIA_AUDIO_ASIO)
add_factories<FW<Audio::AudioFactory, Audio::NativeASIOFactory>>(vec, ctx, key);
return vec;
#endif
}
else if(forced_backend == "dummy")
Expand All @@ -174,6 +182,10 @@ std::vector<score::InterfaceBase*> score_plugin_audio::factories(
,
Audio::ALSAFactory
#endif
#if defined(OSSIA_AUDIO_ASIO)
,
Audio::NativeASIOFactory
#endif
#if defined(OSSIA_AUDIO_PORTAUDIO)
#if __has_include(<pa_asio.h>)
,
Expand Down
Loading