Loading...
Searching...
No Matches
ALSAMiniAudioInterface.hpp
1#pragma once
2#if defined(__linux__)
3#include <Audio/AudioInterface.hpp>
4
5#include <ossia/audio/miniaudio_protocol.hpp>
6#if OSSIA_ENABLE_MINIAUDIO
7#include <Audio/GenericMiniAudioInterface.hpp>
8
9#include <ossia/audio/libasound.hpp>
10
11#include <cstdio>
12#include <string>
13namespace Audio
14{
15class ALSAMiniAudioFactory final : public GenericMiniAudioFactory
16{
17 SCORE_CONCRETE("e0c533da-a1f4-4795-90b5-a805cdfcb79f")
18public:
19 ALSAMiniAudioFactory() { }
20 ~ALSAMiniAudioFactory() override { }
21 QString prettyName() const override { return QObject::tr("ALSA (MiniAudio)"); }
22 bool available() const noexcept override
23 {
24 try
25 {
26 ossia::libasound::instance();
27 return true;
28 }
29 catch(...)
30 {
31 return false;
32 }
33 }
34
35 bool
36 compareDeviceId(const ma_device_id& id, const QString& str) const noexcept override
37 {
38 return compareDeviceId(id, std::string_view{str.toStdString()});
39 }
40 bool
41 compareDeviceId(const ma_device_id& id, std::string_view str) const noexcept override
42 {
43 return id.alsa == str || id.alsa == stableName(str);
44 }
45
46 // Exclusive access opens "hw:", with a period of the buffer size; shared
47 // access goes through dmix, whose period is used whatever the buffer size.
48 ma_share_mode shareMode() const noexcept override { return ma_share_mode_exclusive; }
49
50 void normalizeDeviceId(ma_device_id& id) const noexcept override
51 {
52 auto name = stableName(id.alsa);
53 if(!name.empty() && name.size() < sizeof(id.alsa))
54 {
55 std::fill_n(id.alsa, sizeof(id.alsa), 0);
56 std::copy_n(name.data(), name.size(), id.alsa);
57 }
58 }
59
64 static std::string stableName(std::string_view id)
65 {
66 int card = -1, dev = -1;
67 if(id.size() < 4 || id[0] != ':' || id.find("CARD=") != std::string_view::npos
68 || std::sscanf(std::string{id}.c_str(), ":%d,%d", &card, &dev) != 2)
69 return {};
70
71 auto& snd = ossia::libasound::instance();
72 snd_ctl_t* ctl{};
73 if(snd.ctl_open(&ctl, ("hw:" + std::to_string(card)).c_str(), SND_CTL_NONBLOCK) < 0)
74 return {};
75 snd_ctl_card_info_t* info{};
76 snd_alloca(&info, snd, ctl_card_info);
77 std::string res;
78 if(snd.ctl_card_info(ctl, info) == 0)
79 res = ":CARD=" + std::string{snd.ctl_card_info_get_id(info)}
80 + ",DEV=" + std::to_string(dev);
81 snd.ctl_close(ctl);
82 return res;
83 }
84 void setDeviceId(ma_device_id& id, const QString& str) const noexcept override
85 {
86 auto u = str.toUtf8();
87 std::fill_n(id.alsa, 256, 0);
88 std::copy_n(u.data(), std::min((int)u.size(), 255), id.alsa);
89 }
90
91 QString deviceIdToString(const ma_device_id& id) const noexcept override
92 {
93 return QString::fromUtf8(id.alsa);
94 }
95};
96}
97#endif
98#endif