Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
driver.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "driver.hh"
3#include "path.hh"
4#include "platform.hh"
5#include "datautils.hh"
6#include "strings.hh"
7#include "internal.hh"
8#include <algorithm>
9
10#define DDEBUG(...) Ase::debug ("driver", __VA_ARGS__)
11
12namespace Ase {
13
14// == Driver ==
15Driver::Driver (const String &driver, const String &devid) :
16 driver_ (driver), devid_ (devid)
17{}
18
19Driver::~Driver ()
20{}
21
24Driver::devid () const
25{
26 return devid_.empty() ? driver_ : driver_ + "=" + devid_;
27}
28
31Driver::priority_string (uint priority)
32{
33 StringS b;
34 if (priority & SURROUND) b.push_back ("SURROUND");
35 if (priority & HEADSET) b.push_back ("HEADSET");
36 if (priority & RECORDER) b.push_back ("RECORDER");
37 if (priority & MIDI_THRU) b.push_back ("MIDI_THRU");
38 if (priority & JACK) b.push_back ("JACK");
39 if (priority & ALSA_USB) b.push_back ("ALSA_USB");
40 if (priority & ALSA_KERN) b.push_back ("ALSA_KERN");
41 if (priority & OSS) b.push_back ("OSS");
42 if (priority & PULSE) b.push_back ("PULSE");
43 if (priority & ALSA_USER) b.push_back ("ALSA_USER");
44 if (priority & PSEUDO) b.push_back ("PSEUDO");
45 if (priority & PAUTO) b.push_back ("PAUTO");
46 if (priority & PNULL) b.push_back ("PNULL");
47 if (priority & WCARD) b.push_back ("WCARD");
48 if (priority & WDEV) b.push_back ("WDEV");
49 if (priority & WSUB) b.push_back ("WSUB");
50 return string_join ("|", b);
51}
52
53// == loaders ==
54using RegisteredLoaderFunc = Error (*) ();
56 const char *const what;
57 const RegisteredLoaderFunc func;
58};
60static RegisteredLoaderVector& registered_loaders() { static RegisteredLoaderVector lv; return lv; }
61static bool registered_loaders_executed = false;
62
64bool*
65register_driver_loader (const char *staticwhat, Error (*loader) ())
66{
67 assert_return (loader != NULL, nullptr);
68 assert_return (registered_loaders_executed == false, nullptr);
69 RegisteredLoaderVector &lv = registered_loaders();
70 lv.push_back ({ staticwhat, loader });
71 return &registered_loaders_executed;
72}
73
75void
77{
78 assert_return (registered_loaders_executed == false);
79 registered_loaders_executed = true;
80 for (auto &loader : registered_loaders())
81 {
82 Error error = loader.func ();
83 if (error != Error::NONE)
84 printerr ("ASE: %s: loading failed: %s\n", loader.what, ase_error_blurb (error));
85 }
86}
87
88// == RegisteredDriver ==
89template<typename DriverP>
91 const String driver_id_;
92 std::function<DriverP (const String&)> create_;
93 std::function<void (Driver::EntryVec&)> list_;
95
97 registered_driver_vector()
98 {
99 static RegisteredDriverVector registered_driver_vector_;
100 return registered_driver_vector_;
101 }
102
103 static DriverP
104 open (const String &devid, Driver::IODir iodir, Error *errorp,
105 const std::function<Error (DriverP, Driver::IODir)> &opener)
106 {
107 std::function<DriverP (const String&)> create;
108 const String driverid = kvpair_key (devid);
109 for (const auto &driver : registered_driver_vector())
110 if (driver.driver_id_ == driverid)
111 {
112 create = driver.create_;
113 break;
114 }
115 Error error = Error::DEVICE_NOT_AVAILABLE;
116 DriverP driver = create ? create (devid) : NULL;
117 if (driver)
118 {
119 error = opener (driver, iodir);
120 if (errorp)
121 *errorp = error;
122 if (error == Error::NONE)
123 {
124 assert_return (driver->opened() == true, nullptr);
125 assert_return (!(iodir & Driver::READONLY) || driver->readable(), nullptr);
126 assert_return (!(iodir & Driver::WRITEONLY) || driver->writable(), nullptr);
127 }
128 else
129 driver = nullptr;
130 }
131 else if (errorp)
132 *errorp = error;
133 return driver;
134 }
135
136 static String
137 register_driver (const String &driverid,
138 const std::function<DriverP (const String&)> &create,
139 const std::function<void (Driver::EntryVec&)> &list)
140 {
141 auto &vec = registered_driver_vector();
142 RegisteredDriver rd = { driverid, create, list, };
143 vec.push_back (rd);
144 return driverid;
145 }
146 static Driver::EntryVec
147 list_drivers (const Driver::EntryVec &pseudos)
148 {
149 Driver::EntryVec entries;
150 std::copy (pseudos.begin(), pseudos.end(), std::back_inserter (entries));
151 auto &vec = registered_driver_vector();
152 for (const auto &rd : vec)
153 {
154 Driver::EntryVec dentries;
155 rd.list_ (dentries);
156 for (auto &entry : dentries)
157 entry.devid = entry.devid.empty() ? rd.driver_id_ : rd.driver_id_ + "=" + entry.devid;
158 entries.insert (entries.end(), std::make_move_iterator (dentries.begin()), std::make_move_iterator (dentries.end()));
159 }
160 std::sort (entries.begin(), entries.end(), [] (const Driver::Entry &a, const Driver::Entry &b) {
161 return a.priority < b.priority;
162 });
163 return entries;
164 }
165};
166
167// == PcmDriver ==
168PcmDriver::PcmDriver (const String &driver, const String &devid) :
169 Driver (driver, devid)
170{}
171
173PcmDriverP
174PcmDriver::open (const String &devid, IODir desired, IODir required, const PcmDriverConfig &config, Error *ep)
175{
176 auto opener = [&config] (PcmDriverP d, IODir iodir) {
177 return d->open (iodir, config);
178 };
179 if (devid == "auto")
180 {
181 for (const auto &entry : list_drivers())
182 if (entry.priority < PSEUDO && // ignore pseudo devices during auto-selection
183 !(entry.priority & 0x0000ffff)) // ignore secondary devices during auto-selection
184 {
185 PcmDriverP pcm_driver = RegisteredDriver<PcmDriverP>::open (entry.devid, desired, ep, opener);
186 log ("PcmDriver::open: devid=%s: %s\n", entry.devid, ase_error_blurb (*ep));
187 if (!pcm_driver && required && desired != required) {
188 pcm_driver = RegisteredDriver<PcmDriverP>::open (entry.devid, required, ep, opener);
189 log ("PcmDriver::open: devid=%s: %s\n", entry.devid, ase_error_blurb (*ep));
190 }
191 if (pcm_driver)
192 return pcm_driver;
193 }
194 }
195 else
196 {
197 PcmDriverP pcm_driver = RegisteredDriver<PcmDriverP>::open (devid, desired, ep, opener);
198 if (!pcm_driver && required && desired != required)
199 pcm_driver = RegisteredDriver<PcmDriverP>::open (devid, required, ep, opener);
200 if (pcm_driver)
201 return pcm_driver;
202 }
203 return nullptr;
204}
205
206String
207PcmDriver::register_driver (const String &driverid,
208 const std::function<PcmDriverP (const String&)> &create,
209 const std::function<void (EntryVec&)> &list)
210{
211 return RegisteredDriver<PcmDriverP>::register_driver (driverid, create, list);
212}
213
214Driver::EntryVec
215PcmDriver::list_drivers ()
216{
217 Driver::Entry entry;
218 entry.devid = "auto";
219 entry.device_name = _("Automatic driver selection");
220 entry.device_info = _("Selects the first available PCM card or sound server");
221 entry.readonly = false;
222 entry.writeonly = false;
223 entry.priority = Driver::PAUTO;
224 Driver::EntryVec pseudos;
225 pseudos.push_back (entry);
226 return RegisteredDriver<PcmDriverP>::list_drivers (pseudos);
227}
228
229// == MidiDriver ==
230MidiDriver::MidiDriver (const String &driver, const String &devid) :
231 Driver (driver, devid)
232{}
233
234MidiDriverP
235MidiDriver::open (const String &devid, IODir iodir, Error *ep)
236{
237 auto opener = [] (MidiDriverP d, IODir iodir) {
238 return d->open (iodir);
239 };
240 if (devid == "auto")
241 {
242 for (const auto &entry : list_drivers())
243 if (entry.priority < PSEUDO) // ignore pseudo devices during auto-selection
244 {
245 MidiDriverP midi_driver = RegisteredDriver<MidiDriverP>::open (entry.devid, iodir, ep, opener);
246 log ("MidiDriver::open: devid=%s: %s\n", entry.devid, ase_error_blurb (*ep));
247 if (midi_driver)
248 return midi_driver;
249 }
250 }
251 else
252 {
253 MidiDriverP midi_driver = RegisteredDriver<MidiDriverP>::open (devid, iodir, ep, opener);
254 if (midi_driver)
255 return midi_driver;
256 }
257 return nullptr;
258}
259
260String
261MidiDriver::register_driver (const String &driverid,
262 const std::function<MidiDriverP (const String&)> &create,
263 const std::function<void (EntryVec&)> &list)
264{
265 return RegisteredDriver<MidiDriverP>::register_driver (driverid, create, list);
266}
267
268Driver::EntryVec
269MidiDriver::list_drivers ()
270{
271 Driver::Entry entry;
272 entry.devid = "auto";
273 entry.device_name = _("Automatic MIDI driver selection");
274 entry.device_info = _("Selects the first available MIDI device");
275 entry.readonly = false;
276 entry.writeonly = false;
277 entry.priority = Driver::PAUTO;
278 Driver::EntryVec pseudos;
279 pseudos.push_back (entry);
280 return RegisteredDriver<MidiDriverP>::list_drivers (pseudos);
281}
282
283// == NullPcmDriver ==
284class NullPcmDriver : public PcmDriver {
285 uint n_channels_ = 0;
286 uint mix_freq_ = 0;
287 uint block_size_ = 0;
288 int64 resumetime_ = 0;
289public:
290 explicit NullPcmDriver (const String &driver, const String &devid) : PcmDriver (driver, devid) {}
291 static PcmDriverP
292 create (const String &devid)
293 {
294 auto pdriverp = std::make_shared<NullPcmDriver> (kvpair_key (devid), kvpair_value (devid));
295 return pdriverp;
296 }
297 uint
298 pcm_n_channels () const override
299 {
300 return n_channels_;
301 }
302 uint
303 pcm_mix_freq () const override
304 {
305 return mix_freq_;
306 }
307 uint
308 pcm_block_length () const override
309 {
310 return block_size_;
311 }
312 void
313 pcm_latency (uint *rlatency, uint *wlatency) const override
314 {
315 *rlatency = mix_freq_ / 10;
316 *wlatency = mix_freq_ / 10;
317 }
318 virtual void
319 close () override
320 {
321 assert_return (opened());
322 flags_ &= ~size_t (Flags::OPENED | Flags::READABLE | Flags::WRITABLE);
323 }
324 virtual Error
325 open (IODir iodir, const PcmDriverConfig &config) override
326 {
327 assert_return (!opened(), Error::INTERNAL);
328 // setup request
329 const bool require_readable = iodir == READONLY || iodir == READWRITE;
330 const bool require_writable = iodir == WRITEONLY || iodir == READWRITE;
331 flags_ |= Flags::READABLE * require_readable;
332 flags_ |= Flags::WRITABLE * require_writable;
333 n_channels_ = config.n_channels;
334 mix_freq_ = config.mix_freq;
335 block_size_ = config.block_length;
336 flags_ |= Flags::OPENED;
337 DDEBUG ("NULL-PCM: opening with freq=%f channels=%d: %s", mix_freq_, n_channels_, ase_error_blurb (Error::NONE));
338 return Error::NONE;
339 }
340 virtual bool
341 pcm_check_io (int64 *timeout_usecs) override
342 {
343 int64 current_usecs = timestamp_realtime();
344 if (resumetime_ > current_usecs)
345 {
346 *timeout_usecs = resumetime_ - current_usecs;
347 return false;
348 }
349 resumetime_ = current_usecs;
350 return true;
351 }
352 virtual void
353 pcm_write (size_t n, const float *values) override
354 {
355 const int64 busy_usecs = n * 1000000 / (mix_freq_ * n_channels_);
356 resumetime_ += busy_usecs;
357 }
358 virtual size_t
359 pcm_read (size_t n, float *values) override
360 {
361 floatfill (values, 0.0, n);
362 return n;
363 }
364 static void
365 list_drivers (Driver::EntryVec &entries)
366 {
367 Driver::Entry entry;
368 entry.devid = ""; // "null"
369 entry.device_name = "Null PCM Driver";
370 entry.device_info = _("Discard all PCM output and provide zeros as PCM input");
371 entry.notice = "Warning: The Null driver has no playback timing support";
372 entry.readonly = false;
373 entry.writeonly = false;
374 entry.priority = Driver::PNULL;
375 entries.push_back (entry);
376 }
377};
378
379static const String null_pcm_driverid = PcmDriver::register_driver ("null", NullPcmDriver::create, NullPcmDriver::list_drivers);
380
381// == NullMidiDriver ==
383public:
384 explicit NullMidiDriver (const String &driver, const String &devid) : MidiDriver (driver, devid) {}
385 static MidiDriverP
386 create (const String &devid)
387 {
388 auto pdriverp = std::make_shared<NullMidiDriver> (kvpair_key (devid), kvpair_value (devid));
389 return pdriverp;
390 }
391 virtual void
392 close () override
393 {
394 assert_return (opened());
395 flags_ &= ~size_t (Flags::OPENED | Flags::READABLE | Flags::WRITABLE);
396 }
397 virtual Error
398 open (IODir iodir) override
399 {
400 assert_return (!opened(), Error::INTERNAL);
401 // setup request
402 const bool require_readable = iodir == READONLY || iodir == READWRITE;
403 const bool require_writable = iodir == WRITEONLY || iodir == READWRITE;
404 flags_ |= Flags::READABLE * require_readable;
405 flags_ |= Flags::WRITABLE * require_writable;
406 flags_ |= Flags::OPENED;
407 DDEBUG ("NULL-MIDI: opening: %s", ase_error_blurb (Error::NONE));
408 return Error::NONE;
409 }
410 bool
411 has_events () override
412 {
413 return false;
414 }
415 uint
416 fetch_events (MidiEventOutput&, double) override
417 {
418 return 0;
419 }
420 static void
421 list_drivers (Driver::EntryVec &entries)
422 {
423 Driver::Entry entry;
424 entry.devid = ""; // "null"
425 entry.device_name = "Null MIDI Driver";
426 entry.device_info = _("Discard all MIDI events");
427 entry.readonly = false;
428 entry.writeonly = false;
429 entry.priority = Driver::PNULL;
430 entries.push_back (entry);
431 }
432};
433
434static const String null_midi_driverid = MidiDriver::register_driver ("null", NullMidiDriver::create, NullMidiDriver::list_drivers);
435
436} // Ase
437
438// == jackdriver.so ==
439#include <dlfcn.h>
440
441static Ase::Error
442try_load_libasejack ()
443{
444 using namespace Ase;
445 const std::string libasejack = string_format ("%s/lib/jackdriver.so", anklang_runpath (RPath::INSTALLDIR));
446 if (Path::check (libasejack, "fr"))
447 {
448 void *dlhandle = dlopen (libasejack.c_str(), RTLD_LOCAL | RTLD_NOW); // no API import
449 const char *err = dlerror();
450 DDEBUG ("%s: dlopen: %s", libasejack, dlhandle ? "OK" : err ? err : "unknown dlerror");
451 }
452 return Error::NONE;
453}
454
455static bool *asejack_loaded = Ase::register_driver_loader ("asejack", try_load_libasejack);
T back_inserter(T... args)
T c_str(T... args)
Base class for a PCM and MIDI devices.
Definition driver.hh:29
String devid() const
Return a string which uniquely identifies this driver and device.
Definition driver.cc:24
Base class for a MIDI devices.
Definition driver.hh:74
A stream of writable MidiEvent structures.
Definition midievent.hh:96
Base class for a PCM devices.
Definition driver.hh:99
T copy(T... args)
dlerror
dlopen
T empty(T... args)
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define _(...)
Retrieve the translation of a C or C++ string.
Definition internal.hh:18
log
T make_move_iterator(T... args)
The Anklang C++ API namespace.
Definition api.hh:9
void floatfill(float *dst, float f, size_t n)
Fill n values of dst with f.
Definition datautils.hh:29
String string_join(const String &junctor, const StringS &strvec)
Definition strings.cc:452
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:29
Error
Enum representing Error states.
Definition api.hh:22
const char * ase_error_blurb(Error error)
Describe Error condition.
Definition server.cc:227
bool * register_driver_loader(const char *staticwhat, Error(*loader)())
Register loader callbacks at static constructor time.
Definition driver.cc:65
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
void load_registered_drivers()
Load all registered drivers.
Definition driver.cc:76
uint64 timestamp_realtime()
Return the current time as uint64 in µseconds.
Definition platform.cc:578
Driver information for PCM and MIDI handling.
Definition driver.hh:16
PCM device configuration.
Definition driver.hh:91
open
T push_back(T... args)
T sort(T... args)