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

« « « Anklang Documentation
Loading...
Searching...
No Matches
driver-alsa.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 "datautils.hh"
4#include "platform.hh"
5#include "strings.hh"
6#include "internal.hh"
7#include <limits.h> // LONG_MAX
8#include <atomic>
9#include <cmath>
10
11#define PDEBUG(...) Ase::debug ("alsa", "PCM: " __VA_ARGS__)
12#define MDEBUG(...) Ase::debug ("alsa", "MIDI: " __VA_ARGS__)
13
14#define ALSAQUIET(expr) ({ silence_error_handler++; auto __ret_ = (expr); silence_error_handler--; __ret_; })
15#define WITH_MIDI_POLL 0
16
17#define alsa_alloca0(struc) ({ struc##_t *ptr = (struc##_t*) alloca (struc##_sizeof()); memset (ptr, 0, struc##_sizeof()); ptr; })
18#define return_error(reason, aerror, ERRMEMB) do { \
19 const Error __ase_error = ase_error_from_errno (-aerror, Ase::Error::ERRMEMB); \
20 Ase::debug ("alsa", "%s: %s: %s", alsadev_, reason, \
21 ase_error_blurb (__ase_error)); \
22 return __ase_error; \
23 } while (0)
24
25/* Notes on the ALSA API:
26 * - Increase buffer_size to minimize dropouts.
27 * - Decrease buffer_size to minimize latency.
28 * - The start threshold specifies that the device starts if that many frames become available for playback.
29 * - The stop threshold specifies that the device stops if that many frames become available for write.
30 * Contrary to the ALSA documentation, stop_threshold=boundary fails to keep the device running on
31 * underruns (on 64bit, on 32bit libasound it used to work), but LONG_MAX or 2*buffer_size will do.
32 * - For all practical purposes, snd_pcm_sw_params_get_boundary() represents "+Infinity" in the ALSA API.
33 * It's normally the largest multiple of the buffer size that fits LONG_MAX, but may be broken on 64bit.
34 * - The avail_min specifies the application wakeup point if that many (free) frames become available,
35 * many cards only support powers of 2.
36 * - The "sub unit direction" indicates rounding direction, ALSA refuses to set exact values (dir=0)
37 * if the underlying device has a fractional offset (for e.g. period size). This usually happens when
38 * the plughw device is used for remixing, e.g. 256 * 44100/48000 = 235.2.
39 * - Due to fractional period sizes, the effective buffer size can differ from n_periods * period_size,
40 * so the desired buffer size is best set via set_period_size_near + set_periods_near.
41 * - With silence_threshold=period_size and silence_size=period_size, an extra 0-value filled period
42 * is inserted while the last period is played. This causes clicks early but can reduce the risk of
43 * future underruns.
44 * Driver implementation:
45 * - During high load, we need 1 period playing while rendering the next period.
46 * - Due to jitter, we might be late writing the next period, so we're better off with one extra period.
47 * - If we're very fast generating periods, we ideally have enough buffer space to write without blocking.
48 * Thus, we need 1 playing period, 1 extra period, 1 unfilled period, i.e. 3 periods of buffer size and
49 * we need avail_min to match the period size.
50 */
51
52#if __has_include(<alsa/asoundlib.h>)
53#include <alsa/asoundlib.h>
54
55// for non-little endian, SND_PCM_FORMAT_S16_LE and other places will need fixups
56static_assert (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__, "endianess unimplemented");
57
58namespace Ase {
59
60static std::string hex_str (uint len, const uint8 *d);
61
62static String
63chars2string (const char *s)
64{
65 return s ? s : "";
66}
67
68static String
69cxfree (char *mallocedstring, const char *fallback = "")
70{
71 if (mallocedstring)
72 {
73 const String string = mallocedstring;
74 free (mallocedstring);
75 return string;
76 }
77 return fallback;
78}
79
80static std::string
81substitute_string (const std::string &from, const std::string &to, const std::string &input)
82{
83 std::string::size_type l = 0;
84 std::string target;
85 for (std::string::size_type i = input.find (from, 0); i != std::string::npos; l = i + 1, i = input.find (from, l))
86 {
87 target.append (input, l, i - l);
88 target.append (to);
89 }
90 target.append (input, l, input.size() - l);
91 return target;
92}
93
94static std::atomic<int> silence_error_handler = { 0 };
95
96static void
97ase_handle_alsa_error (const char *file, int line, const char *function, int err, const char *fmt, ...)
98{
99 if (silence_error_handler != 0)
100 return;
101 constexpr const int MAXBUFFER = 8 * 1024;
102 char buffer[MAXBUFFER + 2] = { 0, }, *b = buffer, *e = b + MAXBUFFER;
103 va_list argv;
104 va_start (argv, fmt);
105 const int plen = vsnprintf (b, e - b, fmt, argv);
106 (void) plen;
107 va_end (argv);
108 String s;
109 if (file && line > 0 && function)
110 s = string_format ("%s:%u:%s: %s", file, line, function, b);
111 else if (file && line > 0)
112 s = string_format ("%s:%u: %s", file, line, b);
113 else if (file && function)
114 s = string_format ("%s:%s: %s", file, function, b);
115 else if (file || function)
116 s = string_format ("%s: %s", file ? file : function, b);
117 Ase::debug ("alsa", "Error: %s", s);
118}
119
120static snd_output_t *snd_output = nullptr; // used for debugging
121
122static void
123init_lib_alsa()
124{
125 static const bool ASE_USED initialized = [] {
126 snd_lib_error_set_handler (ase_handle_alsa_error);
127 return snd_output_stdio_attach (&snd_output, stderr, 0);
128 } ();
129}
130
132mixer_elem_get_channels (snd_mixer_elem_t *const mel,
133 const std::function<int (snd_mixer_elem_t*, snd_mixer_selem_channel_id_t)> &mixer_has_channel,
134 bool eligible)
135{
137 if (eligible)
138 for (snd_mixer_selem_channel_id_t i = SND_MIXER_SCHN_MONO; i <= SND_MIXER_SCHN_LAST;
139 i = snd_mixer_selem_channel_id_t (i + 1))
140 if (mixer_has_channel (mel, i))
141 channels.push_back (i);
142 return channels;
143}
144
145static String
146mixer_info (const String &card_hw, const String &mixer_name, const String &long_name)
147{
148 snd_mixer_t *mixer = nullptr;
149 snd_mixer_selem_regopt regopt = { .ver = 1, .abstract = SND_MIXER_SABSTRACT_NONE, .device = card_hw.c_str() };
150 snd_mixer_selem_id_t *selem_id = nullptr;
151 String hints;
152 int bseen = 0, iseen = 0, oseen = 0, maxin = 0, maxout = 0;
153 int err = snd_mixer_open (&mixer, 0);
154 if (err < 0) goto error_out;
155 err = snd_mixer_selem_register (mixer, &regopt, nullptr);
156 if (err < 0) goto error_out;
157 err = snd_mixer_load (mixer);
158 if (err < 0) goto error_out;
159 err = snd_mixer_selem_id_malloc (&selem_id);
160 if (err < 0) goto error_out;
161 PDEBUG ("CARD(%s): %s [%s]", card_hw, long_name, mixer_name);
162 PDEBUG ("M-------- %-6s %2u %s %s - %s", "MIXER", snd_mixer_get_count (mixer), card_hw, mixer_name, long_name);
163 for (snd_mixer_elem_t *mel = snd_mixer_first_elem (mixer); mel; mel = snd_mixer_elem_next (mel))
164 {
165 if (snd_mixer_elem_get_type (mel) != SND_MIXER_ELEM_SIMPLE)
166 continue;
167 int v = 0;
168 const snd_mixer_selem_channel_id_t c0 = SND_MIXER_SCHN_MONO;
169 const bool cv = snd_mixer_selem_has_capture_volume (mel);
170 const bool pv = snd_mixer_selem_has_playback_volume (mel);
171 const bool S = snd_mixer_selem_has_common_switch (mel) || snd_mixer_selem_has_playback_switch (mel) || snd_mixer_selem_has_capture_switch (mel);
172 const bool e = snd_mixer_selem_is_enumerated (mel); // snd_mixer_selem_is_enum_playback snd_mixer_selem_is_enum_capture
173 const char *tname = pv ? cv ? "INOUT" : "OUT" : cv ? "IN" : S ? "SWITCH" : e ? "ENUM" : "-";
174 const bool a = snd_mixer_selem_is_active (mel);
175 const bool j = snd_mixer_selem_has_playback_volume_joined (mel) || snd_mixer_selem_has_capture_volume_joined (mel);
176 const bool m = snd_mixer_selem_has_playback_switch (mel) && snd_mixer_selem_get_playback_switch (mel, c0, &v) == 0 && !v;
177 const bool M = m && snd_mixer_selem_has_playback_switch_joined (mel);
178 const bool c = snd_mixer_selem_has_capture_switch (mel) && snd_mixer_selem_get_capture_switch (mel, c0, &v) == 0 && v;
179 const bool C = c && snd_mixer_selem_has_capture_switch_joined (mel);
180 const bool x = snd_mixer_selem_has_capture_switch_exclusive (mel);
181 const auto p = mixer_elem_get_channels (mel, snd_mixer_selem_has_playback_channel, pv);
182 const auto d = mixer_elem_get_channels (mel, snd_mixer_selem_has_capture_channel, cv);
183 maxout = std::max (maxout, int (p.size()));
184 maxin = std::max (maxin, int (d.size()));
185 if (cv && pv)
186 bseen++;
187 else if (pv)
188 oseen++;
189 else if (cv)
190 iseen++;
191 long l = 0;
192 String val;
193 for (const auto ch : p)
194 if ((ch == 0 || !snd_mixer_selem_has_playback_volume_joined (mel)) &&
195 0 == snd_mixer_selem_get_playback_volume (mel, ch, &l))
196 val += (val.empty() ? "" : ",") + string_from_type (l);
197 for (const auto ch : d)
198 if ((ch == 0 || !snd_mixer_selem_has_capture_volume_joined (mel)) &&
199 0 == snd_mixer_selem_get_capture_volume (mel, ch, &l))
200 val += (val.empty() ? "" : ",") + string_from_type (l);
201 val = val.empty() ? "" : ": " + val;
202 PDEBUG ("-%c%c%c%c%c%c%c%c %-6s %2u %s%s",
203 cv ? 'r' : '-', pv ? 'w' : '-', a ? '-' : 'i',
204 j ? 'j' : '-',
205 M ? 'M' : m ? 'm' : '-',
206 e ? 'e' : '-',
207 C ? 'C' : c ? 'c' : '-',
208 x ? 'x' : '-',
209 tname, p.size() + d.size(),
210 snd_mixer_selem_get_name (mel),
211 val);
212 }
213 if (maxout > 2)
214 hints += (hints.empty() ? "" : ", ") + String ("surround");
215 if (oseen == 1 && bseen + iseen == 1)
216 hints += (hints.empty() ? "" : ", ") + String ("headset");
217 if (oseen + bseen == 0 && iseen >= 1)
218 hints += (hints.empty() ? "" : ", ") + String ("recorder");
219 if (maxin > 2)
220 hints += (hints.empty() ? "" : ", ") + String ("multi-track");
221 if (!hints.empty())
222 PDEBUG ("(%s)", hints);
223 error_out:
224 if (mixer)
225 snd_mixer_close (mixer);
226 if (selem_id)
227 snd_mixer_selem_id_free (selem_id);
228 return hints;
229}
230
231static void
232list_alsa_drivers (Driver::EntryVec &entries)
233{
234 init_lib_alsa();
235 // discover virtual (non-hw) devices
236 bool seen_plughw = false; // maybe needed to resample at device boundaries
237 void **nhints = nullptr;
238 if (ALSAQUIET (snd_device_name_hint (-1, "pcm", &nhints)) >= 0) // ignore alsa.conf parsing errors
239 {
240 String name, desc, ioid;
241 for (void **hint = nhints; *hint; hint++)
242 {
243 name = cxfree (snd_device_name_get_hint (*hint, "NAME")); // full ALSA device name
244 desc = cxfree (snd_device_name_get_hint (*hint, "DESC")); // card_name + pcm_name + alsa.conf-description
245 ioid = cxfree (snd_device_name_get_hint (*hint, "IOID"), "Duplex"); // one of: "Duplex", "Input", "Output"
246 seen_plughw = seen_plughw || strncmp (name.c_str(), "plughw:", 7) == 0;
247 if (name == "pulse")
248 {
249 PDEBUG ("DISCOVER: %s - %s - %s", name, ioid, substitute_string ("\n", " ", desc));
250 Driver::Entry entry;
251 entry.devid = name;
252 entry.device_name = desc;
253 entry.device_info = "Routing via the PulseAudio sound system";
254 entry.notice = "Note: PulseAudio routing is not realtime capable";
255 entry.readonly = "Input" == ioid;
256 entry.writeonly = "Output" == ioid;
257 entry.priority = Driver::PULSE;
258 entries.push_back (entry);
259 }
260 }
261 snd_device_name_free_hint (nhints);
262 }
263 // discover hardware cards
264 snd_ctl_card_info_t *cinfo = alsa_alloca0 (snd_ctl_card_info);
265 int cindex = -1;
266 while (snd_card_next (&cindex) == 0 && cindex >= 0)
267 {
268 snd_ctl_card_info_clear (cinfo);
269 snd_ctl_t *chandle = nullptr;
270 const String card_hw = string_format ("hw:CARD=%u", cindex);
271 if (snd_ctl_open (&chandle, card_hw.c_str(), SND_CTL_NONBLOCK) < 0 || !chandle)
272 continue;
273 if (snd_ctl_card_info (chandle, cinfo) < 0)
274 {
275 snd_ctl_close (chandle);
276 continue;
277 }
278 const String card_id = chars2string (snd_ctl_card_info_get_id (cinfo));
279 const String card_driver = chars2string (snd_ctl_card_info_get_driver (cinfo));
280 const String card_name = chars2string (snd_ctl_card_info_get_name (cinfo));
281 const String card_longname = chars2string (snd_ctl_card_info_get_longname (cinfo));
282 const String card_mixername = chars2string (snd_ctl_card_info_get_mixername (cinfo));
283 // PDEBUG ("DISCOVER: CARD: %s - %s - %s [%s] - %s", card_id, card_driver, card_name, card_mixername, card_longname);
284 const String mixer_keywords = mixer_info (card_hw, card_mixername, card_longname);
285 const String mixer_options = ":" + string_join (":", string_split_any (mixer_keywords, " ,")) + ":";
286 // discover PCM hardware
287 snd_pcm_info_t *wpi = alsa_alloca0 (snd_pcm_info);
288 snd_pcm_info_t *rpi = alsa_alloca0 (snd_pcm_info);
289 int dindex = -1;
290 while (snd_ctl_pcm_next_device (chandle, &dindex) == 0 && dindex >= 0)
291 {
292 snd_pcm_info_set_device (wpi, dindex);
293 snd_pcm_info_set_subdevice (wpi, 0);
294 snd_pcm_info_set_stream (wpi, SND_PCM_STREAM_PLAYBACK);
295 const bool writable = snd_ctl_pcm_info (chandle, wpi) == 0;
296 snd_pcm_info_set_device (rpi, dindex);
297 snd_pcm_info_set_subdevice (rpi, 0);
298 snd_pcm_info_set_stream (rpi, SND_PCM_STREAM_CAPTURE);
299 const bool readable = snd_ctl_pcm_info (chandle, rpi) == 0;
300 const auto pcmclass = snd_pcm_info_get_class (writable ? wpi : rpi);
301 if (!writable && !readable)
302 continue;
303 const int total_playback_subdevices = writable ? snd_pcm_info_get_subdevices_count (wpi) : 0;
304 const int avail_playback_subdevices = writable ? snd_pcm_info_get_subdevices_avail (wpi) : 0;
305 String wdevs, rdevs;
306 if (total_playback_subdevices && total_playback_subdevices != avail_playback_subdevices)
307 wdevs = string_format ("%u*playback (%u busy)", total_playback_subdevices, total_playback_subdevices - avail_playback_subdevices);
308 else if (total_playback_subdevices)
309 wdevs = string_format ("%u*playback", total_playback_subdevices);
310 const int total_capture_subdevices = readable ? snd_pcm_info_get_subdevices_count (rpi) : 0;
311 const int avail_capture_subdevices = readable ? snd_pcm_info_get_subdevices_avail (rpi) : 0;
312 if (total_capture_subdevices && total_capture_subdevices != avail_capture_subdevices)
313 rdevs = string_format ("%u*capture (%u busy)", total_capture_subdevices, total_capture_subdevices - avail_capture_subdevices);
314 else if (total_capture_subdevices)
315 rdevs = string_format ("%u*capture", total_capture_subdevices);
316 const String joiner = !wdevs.empty() && !rdevs.empty() ? " + " : "";
317 Driver::Entry entry;
318 entry.devid = string_format ("hw:CARD=%s,DEV=%u", card_id, dindex);
319 const bool is_usb = string_startswith (chars2string (snd_pcm_info_get_id (writable ? wpi : rpi)), "USB Audio");
320 entry.device_name = chars2string (snd_pcm_info_get_name (writable ? wpi : rpi));
321 entry.device_name += " - " + card_name;
322 entry.hints = mixer_keywords;
323 if (card_name != card_mixername && !card_mixername.empty())
324 entry.device_name += " [" + card_mixername + "]";
325 if (pcmclass == SND_PCM_CLASS_GENERIC)
326 entry.capabilities = readable && writable ? "Full-Duplex Audio" : readable ? "Audio Input" : "Audio Output";
327 else // pcmclass == SND_PCM_CLASS_MODEM // other SND_PCM_CLASS_ types are unused
328 entry.capabilities = readable && writable ? "Full-Duplex Modem" : readable ? "Modem Input" : "Modem Output";
329 entry.capabilities += ", streams: " + wdevs + joiner + rdevs;
330 if (!string_startswith (card_longname, card_name + " at "))
331 entry.device_info = card_longname;
332 entry.readonly = !writable;
333 entry.writeonly = !readable;
334 // entry.modem = pcmclass == SND_PCM_CLASS_MODEM;
335 entry.priority = (is_usb ? Driver::ALSA_USB : Driver::ALSA_KERN) + Driver::WCARD * cindex + Driver::WDEV * dindex;
336 entry.priority &= string_option_check (mixer_options, "surround") ? ~Driver::SURROUND : ~0; // bonus
337 entry.priority &= string_option_check (mixer_options, "headset") ? ~Driver::HEADSET : ~0; // bonus
338 entry.priority &= string_option_check (mixer_options, "recorder") ? ~Driver::RECORDER : ~0; // bonus
339 entries.push_back (entry);
340 PDEBUG ("DISCOVER: %s - %s", entry.devid, entry.device_name);
341 }
342 snd_ctl_close (chandle);
343 }
344}
345
346// == AlsaPcmDriver ==
347class AlsaPcmDriver : public PcmDriver {
348 snd_pcm_t *read_handle_ = nullptr;
349 snd_pcm_t *write_handle_ = nullptr;
350 uint mix_freq_ = 0;
351 uint n_channels_ = 0;
352 uint n_periods_ = 0;
353 int period_size_ = 0; // count in frames
354 int16 *period_buffer_ = nullptr;
355 uint read_write_count_ = 0;
356 String alsadev_;
357public:
358 explicit AlsaPcmDriver (const String &driver, const String &devid) : PcmDriver (driver, devid) {}
359 static PcmDriverP
360 create (const String &devid)
361 {
362 auto pdriverp = std::make_shared<AlsaPcmDriver> (kvpair_key (devid), kvpair_value (devid));
363 return pdriverp;
364 }
365 ~AlsaPcmDriver()
366 {
367 if (read_handle_)
368 snd_pcm_close (read_handle_);
369 if (write_handle_)
370 snd_pcm_close (write_handle_);
371 delete[] period_buffer_;
372 }
373 uint
374 pcm_n_channels () const override
375 {
376 return n_channels_;
377 }
378 uint
379 pcm_mix_freq () const override
380 {
381 return mix_freq_;
382 }
383 uint
384 pcm_block_length () const override
385 {
386 return period_size_;
387 }
388 virtual void
389 close () override
390 {
391 assert_return (opened());
392 PDEBUG ("CLOSE: %s: r=%d w=%d", alsadev_, !!read_handle_, !!write_handle_);
393 if (read_handle_)
394 {
395 snd_pcm_drop (read_handle_);
396 snd_pcm_close (read_handle_);
397 read_handle_ = nullptr;
398 }
399 if (write_handle_)
400 {
401 snd_pcm_nonblock (write_handle_, 0);
402 snd_pcm_drain (write_handle_);
403 snd_pcm_close (write_handle_);
404 write_handle_ = nullptr;
405 }
406 delete[] period_buffer_;
407 period_buffer_ = nullptr;
408 flags_ &= ~size_t (Flags::OPENED | Flags::READABLE | Flags::WRITABLE);
409 alsadev_ = "";
410 }
411 virtual Error
412 open (IODir iodir, const PcmDriverConfig &config) override
413 {
414 Error error = open (devid_, iodir, config);
415 if (error != Error::NONE && strncmp ("hw:", devid_.c_str(), 3) == 0)
416 error = open ("plug" + devid_, iodir, config);
417 return error;
418 }
419 Error
420 open (const String &alsadev, IODir iodir, const PcmDriverConfig &config)
421 {
422 assert_return (!opened(), Error::INTERNAL);
423 int aerror = 0;
424 alsadev_ = alsadev;
425 // setup request
426 const bool require_readable = iodir == READONLY || iodir == READWRITE;
427 const bool require_writable = iodir == WRITEONLY || iodir == READWRITE;
428 flags_ |= Flags::READABLE * require_readable;
429 flags_ |= Flags::WRITABLE * require_writable;
430 n_channels_ = config.n_channels;
431 // try open
432 if (!aerror && require_readable)
433 aerror = snd_pcm_open (&read_handle_, alsadev_.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
434 if (!aerror && require_writable)
435 aerror = snd_pcm_open (&write_handle_, alsadev_.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
436 // try setup
437 const uint period_size = config.block_length;
438 Error error = !aerror ? Error::NONE : ase_error_from_errno (-aerror, Error::FILE_OPEN_FAILED);
439 uint rh_freq = config.mix_freq, rh_n_periods = 2, rh_period_size = period_size;
440 if (!aerror && read_handle_)
441 error = alsa_device_setup (read_handle_, config.latency_ms, &rh_freq, &rh_n_periods, &rh_period_size);
442 uint wh_freq = config.mix_freq, wh_n_periods = 2, wh_period_size = period_size;
443 if (!aerror && write_handle_)
444 error = alsa_device_setup (write_handle_, config.latency_ms, &wh_freq, &wh_n_periods, &wh_period_size);
445 // check duplex
446 if (!error && read_handle_ && write_handle_)
447 {
448 const bool linked = snd_pcm_link (read_handle_, write_handle_) == 0;
449 if (rh_freq != wh_freq || rh_n_periods != wh_n_periods || rh_period_size != wh_period_size || !linked)
450 error = Error::DEVICES_MISMATCH;
451 PDEBUG ("OPEN: %s: %s: %f==%f && %d*%d==%d*%d && linked==%d", alsadev_,
452 error != 0 ? "MISMATCH" : "LINKED", rh_freq, wh_freq, rh_n_periods, rh_period_size, wh_n_periods, wh_period_size, linked);
453 }
454 mix_freq_ = read_handle_ ? rh_freq : wh_freq;
455 n_periods_ = read_handle_ ? rh_n_periods : wh_n_periods;
456 period_size_ = read_handle_ ? rh_period_size : wh_period_size;
457 if (!error && (!read_handle_ || !write_handle_))
458 PDEBUG ("OPEN: %s: %s: mix=%.1fHz n=%d period=%d", alsadev_,
459 read_handle_ ? "READONLY" : "WRITEONLY", mix_freq_, n_periods_, period_size_);
460 if (!error) {
461 aerror = snd_pcm_prepare (read_handle_ ? read_handle_ : write_handle_);
462 error = !aerror ? Error::NONE : ase_error_from_errno (-aerror, Error::FILE_OPEN_FAILED);
463 }
464 // finish opening or shutdown
465 if (!error)
466 {
467 period_buffer_ = new int16[period_size_ * n_channels_];
468 flags_ |= Flags::OPENED;
469 }
470 else
471 {
472 if (read_handle_)
473 snd_pcm_close (read_handle_);
474 read_handle_ = nullptr;
475 if (write_handle_)
476 snd_pcm_close (write_handle_);
477 write_handle_ = nullptr;
478 }
479 PDEBUG ("OPEN: %s: opening readable=%d writable=%d: %s", alsadev_, readable(), writable(), ase_error_blurb (error));
480 if (error != 0)
481 alsadev_ = "";
482 return error;
483 }
484 Error
485 alsa_device_setup (snd_pcm_t *phandle, uint latency_ms, uint *mix_freq, uint *n_periodsp, uint *period_sizep)
486 {
487 // turn on blocking behaviour since we may end up in read() with an unfilled buffer
488 if (int aerror = snd_pcm_nonblock (phandle, 0); aerror < 0)
489 return_error ("snd_pcm_nonblock", aerror, FILE_OPEN_FAILED);
490 // setup hardware configuration
491 snd_pcm_hw_params_t *hparams = alsa_alloca0 (snd_pcm_hw_params);
492 if (int aerror = snd_pcm_hw_params_any (phandle, hparams); aerror < 0) // alsa=pulse returns aerror=+1
493 return_error ("snd_pcm_hw_params_any", aerror, FILE_OPEN_FAILED);
494 if (int aerror = snd_pcm_hw_params_set_channels (phandle, hparams, n_channels_); aerror < 0)
495 return_error ("snd_pcm_hw_params_set_channels", aerror, DEVICE_CHANNELS);
496 if (int aerror = snd_pcm_hw_params_set_access (phandle, hparams, SND_PCM_ACCESS_RW_INTERLEAVED); aerror < 0)
497 return_error ("snd_pcm_hw_params_set_access", aerror, DEVICE_FORMAT);
498 if (int aerror = snd_pcm_hw_params_set_format (phandle, hparams, SND_PCM_FORMAT_S16_LE); aerror < 0)
499 return_error ("snd_pcm_hw_params_set_format", aerror, DEVICE_FORMAT);
500 // sample_rate
501 uint rate = *mix_freq;
502 if (int aerror = snd_pcm_hw_params_set_rate (phandle, hparams, rate, 0); aerror < 0)
503 return_error ("snd_pcm_hw_params_set_rate", aerror, DEVICE_FREQUENCY);
504 if (rate != *mix_freq)
505 return_error ("snd_pcm_hw_params_set_rate", -EINVAL, DEVICE_FREQUENCY);
506 PDEBUG ("SETUP: %s: rate: %d", alsadev_, rate);
507 // fragment size
508 snd_pcm_uframes_t period_min = 2, period_max = 1048576;
509 snd_pcm_hw_params_get_period_size_min (hparams, &period_min, nullptr);
510 snd_pcm_hw_params_get_period_size_max (hparams, &period_max, nullptr);
511 const snd_pcm_uframes_t latency_frames = rate * latency_ms / 1000; // full IO latency in frames
512 snd_pcm_uframes_t period_size = 32; // sizes < 32 are infeasible with most hw
513 if (alsadev_ == "pulse") // pulseaudio cannot do super low latency
514 period_size = MAX (period_size, 384);
515 while (period_size + 16 <= latency_frames / 3)
516 period_size += 16; // maximize period_size as long as 3 fit the latency
517 period_size = CLAMP (period_size, period_min, period_max);
518 period_size = MIN (period_size, *period_sizep); // MAX_BLOCK_SIZE constraint
519 int dir = 0;
520 if (int aerror = snd_pcm_hw_params_set_period_size_near (phandle, hparams, &period_size, &dir); aerror < 0)
521 return_error ("snd_pcm_hw_params_set_period_size_near", aerror, DEVICE_LATENCY);
522 PDEBUG ("SETUP: %s: period_size: %d (dir=%+d, min=%d max=%d)", alsadev_,
523 period_size, dir, period_min, period_max);
524 // fragment count
525 const uint want_nperiods = latency_ms == 0 ? 2 : CLAMP (latency_frames / period_size, 2, 1023) + 1;
526 uint nperiods = want_nperiods;
527 if (int aerror = snd_pcm_hw_params_set_periods_near (phandle, hparams, &nperiods, nullptr); aerror < 0)
528 return_error ("snd_pcm_hw_params_set_periods", aerror, DEVICE_LATENCY);
529 PDEBUG ("SETUP: %s: n_periods: %d (requested: %d)", alsadev_, nperiods, want_nperiods);
530 if (int aerror = snd_pcm_hw_params (phandle, hparams); aerror < 0)
531 return_error ("snd_pcm_hw_params", aerror, FILE_OPEN_FAILED);
532 // verify hardware settings
533 snd_pcm_uframes_t buffer_size_min = 0, buffer_size_max = 0, buffer_size = 0;
534 if (int aerror = snd_pcm_hw_params_get_buffer_size (hparams, &buffer_size); aerror < 0)
535 return_error ("snd_pcm_hw_params_get_buffer_size", aerror, DEVICE_BUFFER);
536 if (int aerror = snd_pcm_hw_params_get_buffer_size_min (hparams, &buffer_size_min); aerror < 0)
537 return_error ("snd_pcm_hw_params_get_buffer_size_min", aerror, DEVICE_BUFFER);
538 if (int aerror = snd_pcm_hw_params_get_buffer_size_max (hparams, &buffer_size_max); aerror < 0)
539 return_error ("snd_pcm_hw_params_get_buffer_size_max", aerror, DEVICE_BUFFER);
540 PDEBUG ("SETUP: %s: buffer_size: %d (min=%d, max=%d)", alsadev_, buffer_size, buffer_size_min, buffer_size_max);
541 // setup software configuration
542 snd_pcm_sw_params_t *sparams = alsa_alloca0 (snd_pcm_sw_params);
543 if (int aerror = snd_pcm_sw_params_current (phandle, sparams); aerror < 0)
544 return_error ("snd_pcm_sw_params_current", aerror, FILE_OPEN_FAILED);
545 if (int aerror = snd_pcm_sw_params_set_start_threshold (phandle, sparams, (buffer_size / period_size) * period_size); aerror < 0)
546 return_error ("snd_pcm_sw_params_set_start_threshold", aerror, DEVICE_BUFFER);
547 snd_pcm_uframes_t availmin = 0;
548 if (int aerror = snd_pcm_sw_params_set_avail_min (phandle, sparams, period_size); aerror < 0)
549 return_error ("snd_pcm_sw_params_set_avail_min", aerror, DEVICE_LATENCY);
550 if (int aerror = snd_pcm_sw_params_get_avail_min (sparams, &availmin); aerror < 0)
551 return_error ("snd_pcm_sw_params_get_avail_min", aerror, DEVICE_LATENCY);
552 PDEBUG ("SETUP: %s: avail_min: %d", alsadev_, availmin);
553 if (int aerror = snd_pcm_sw_params_set_stop_threshold (phandle, sparams, LONG_MAX); aerror < 0) // keep going on underruns
554 return_error ("snd_pcm_sw_params_set_stop_threshold", aerror, DEVICE_BUFFER);
555 snd_pcm_uframes_t stopthreshold = 0;
556 if (int aerror = snd_pcm_sw_params_get_stop_threshold (sparams, &stopthreshold); aerror < 0)
557 return_error ("snd_pcm_sw_params_get_stop_threshold", aerror, DEVICE_BUFFER);
558 PDEBUG ("SETUP: %s: stop_threshold: %d", alsadev_, stopthreshold);
559 if (int aerror = snd_pcm_sw_params_set_silence_threshold (phandle, sparams, 0); aerror < 0) // avoid early dropouts
560 return_error ("snd_pcm_sw_params_set_silence_threshold", aerror, DEVICE_BUFFER);
561 if (int aerror = snd_pcm_sw_params_set_silence_size (phandle, sparams, LONG_MAX); aerror < 0) // silence past frames
562 return_error ("snd_pcm_sw_params_set_silence_size", aerror, DEVICE_BUFFER);
563 if (int aerror = snd_pcm_sw_params (phandle, sparams); aerror < 0)
564 return_error ("snd_pcm_sw_params", aerror, FILE_OPEN_FAILED);
565 // return values
566 *mix_freq = rate;
567 *n_periodsp = nperiods;
568 *period_sizep = period_size;
569 PDEBUG ("SETUP: %s: OPEN: r=%d w=%d n_channels=%d sample_freq=%d nperiods=%u period=%u (%u) bufsz=%u",
570 alsadev_, phandle == read_handle_, phandle == write_handle_,
571 n_channels_, *mix_freq, *n_periodsp, *period_sizep,
572 nperiods * period_size, buffer_size);
573 // snd_pcm_dump (phandle, snd_output);
574 return Error::NONE;
575 }
576 void
577 pcm_retrigger ()
578 {
579 silence_error_handler++;
580 PDEBUG ("RETRIGGER: %s: retriggering device (r=%s w=%s)...",
581 alsadev_, !read_handle_ ? "<CLOSED>" : snd_pcm_state_name (snd_pcm_state (read_handle_)),
582 !write_handle_ ? "<CLOSED>" : snd_pcm_state_name (snd_pcm_state (write_handle_)));
583 snd_pcm_prepare (read_handle_ ? read_handle_ : write_handle_);
584 // first, clear io buffers
585 if (read_handle_)
586 snd_pcm_drop (read_handle_);
587 if (write_handle_)
588 snd_pcm_drain (write_handle_); // write_handle_ must be blocking
589 // prepare for playback/capture
590 int aerror = snd_pcm_prepare (read_handle_ ? read_handle_ : write_handle_);
591 if (aerror) // this really should not fail
592 printerr ("ALSA: %s: failed to prepare for io: %s\n", __func__, snd_strerror (-aerror));
593 // fill playback buffer with silence
594 if (write_handle_)
595 {
596 const size_t needed_zeros = period_size_ / 2; // sizeof (int16) / sizeof (float)
597 assert_return (needed_zeros <= AUDIO_BLOCK_FLOAT_ZEROS_SIZE);
598 const float *zeros = const_float_zeros;
599 for (size_t i = 0; i < n_periods_; i++)
600 {
601 int n;
602 do
603 n = snd_pcm_writei (write_handle_, zeros, period_size_);
604 while (n == -EAGAIN); // retry on signals
605 // printerr ("%s: written=%d, left: %d / %d\n", __func__, n, snd_pcm_avail (write_handle_), n_periods_ * period_size_);
606 }
607 }
608 silence_error_handler--;
609 }
610 virtual bool
611 pcm_check_io (int64 *timeoutp) override
612 {
613 if (0)
614 {
615 snd_pcm_state_t ws = SND_PCM_STATE_DISCONNECTED, rs = SND_PCM_STATE_DISCONNECTED;
616 snd_pcm_status_t *stat = alsa_alloca0 (snd_pcm_status);
617 if (read_handle_)
618 {
619 snd_pcm_status (read_handle_, stat);
620 rs = snd_pcm_state (read_handle_);
621 }
622 int rn = snd_pcm_status_get_avail (stat);
623 if (write_handle_)
624 {
625 snd_pcm_status (write_handle_, stat);
626 ws = snd_pcm_state (write_handle_);
627 }
628 int wn = snd_pcm_status_get_avail (stat);
629 printerr ("ALSA: check_io: read=%4u/%4u (%s) write=%4u/%4u (%s) block=%u: %s\n",
630 rn, period_size_ * n_periods_, snd_pcm_state_name (rs),
631 wn, period_size_ * n_periods_, snd_pcm_state_name (ws),
632 period_size_, rn >= period_size_ ? "true" : "false");
633 }
634 // quick check for data availability
635 int n_frames_avail = snd_pcm_avail_update (read_handle_ ? read_handle_ : write_handle_);
636 if (n_frames_avail < 0 || // error condition, probably an underrun (-EPIPE)
637 (n_frames_avail == 0 && // check RUNNING state
638 snd_pcm_state (read_handle_ ? read_handle_ : write_handle_) != SND_PCM_STATE_RUNNING))
639 pcm_retrigger();
640 if (n_frames_avail < period_size_)
641 {
642 // not enough data? sync with hardware pointer
643 snd_pcm_hwsync (read_handle_ ? read_handle_ : write_handle_);
644 n_frames_avail = snd_pcm_avail_update (read_handle_ ? read_handle_ : write_handle_);
645 n_frames_avail = MAX (n_frames_avail, 0);
646 }
647 // check whether data can be processed
648 if (n_frames_avail >= period_size_)
649 return true; // need processing
650 // calculate timeout until processing is possible or needed
651 const uint diff_frames = period_size_ - n_frames_avail;
652 *timeoutp = diff_frames * 1000 / mix_freq_;
653 return false;
654 }
655 void
656 pcm_latency (uint *rlatency, uint *wlatency) const override
657 {
658 snd_pcm_sframes_t rdelay, wdelay;
659 if (!read_handle_ || snd_pcm_delay (read_handle_, &rdelay) < 0)
660 rdelay = 0;
661 if (!write_handle_ || snd_pcm_delay (write_handle_, &wdelay) < 0)
662 wdelay = 0;
663 const int buffer_length = n_periods_ * period_size_; // buffer size chosen by ALSA based on latency request
664 // return total latency in frames
665 *rlatency = CLAMP (rdelay, 0, buffer_length);
666 *wlatency = CLAMP (wdelay, 0, buffer_length);
667 }
668 virtual size_t
669 pcm_read (size_t n, float *values) override
670 {
671 assert_return (n == period_size_ * n_channels_, 0);
672 float *dest = values;
673 size_t n_left = period_size_;
674 const size_t n_values = n_left * n_channels_;
675
676 read_write_count_ += 1;
677 do
678 {
679 ssize_t n_frames = snd_pcm_readi (read_handle_, period_buffer_, n_left);
680 if (n_frames < 0) // errors during read, could be underrun (-EPIPE)
681 {
682 PDEBUG ("READ: %s: read() error: %s", alsadev_, snd_strerror (n_frames));
683 silence_error_handler++;
684 snd_pcm_prepare (read_handle_); // force retrigger
685 silence_error_handler--;
686 n_frames = n_left;
687 const size_t frame_size = n_channels_ * sizeof (period_buffer_[0]);
688 memset (period_buffer_, 0, n_frames * frame_size);
689 }
690 if (dest) // ignore dummy reads()
691 {
692 convert_samples (n_frames * n_channels_, const_cast<const int16_t*> (period_buffer_), dest, __BYTE_ORDER__);
693 dest += n_frames * n_channels_;
694 }
695 n_left -= n_frames;
696 }
697 while (n_left);
698
699 return n_values;
700 }
701 virtual void
702 pcm_write (size_t n, const float *values) override
703 {
704 assert_return (n == period_size_ * n_channels_);
705 if (read_handle_ && read_write_count_ < 1)
706 {
707 silence_error_handler++; // silence ALSA about -EPIPE
708 snd_pcm_forward (read_handle_, period_size_);
709 silence_error_handler--;
710 read_write_count_ += 1;
711 }
712 read_write_count_ -= 1;
713 const float *floats = values;
714 size_t n_left = period_size_; // in frames
715 while (n_left)
716 {
717 convert_clip_samples (n_left * n_channels_, floats, period_buffer_, __BYTE_ORDER__);
718 floats += n_left * n_channels_;
719 ssize_t n = 0; // in frames
720 n = snd_pcm_writei (write_handle_, period_buffer_, n_left);
721 if (n < 0) // errors during write, could be overrun (-EPIPE)
722 {
723 PDEBUG ("WRITE: %s: write() error: %s", alsadev_, snd_strerror (n));
724 silence_error_handler++;
725 snd_pcm_prepare (write_handle_); // force retrigger
726 silence_error_handler--;
727 return;
728 }
729 n_left -= n;
730 }
731 }
732};
733
734static const String alsa_pcm_driverid = PcmDriver::register_driver ("alsa",
735 AlsaPcmDriver::create,
736 [] (Driver::EntryVec &entries) {
737 list_alsa_drivers (entries);
738 });
739
740// == AlsaSeqMidiDriver ==
741class AlsaSeqMidiDriver : public MidiDriver {
742 using PortSubscribe = std::unique_ptr<snd_seq_port_subscribe_t, decltype (&snd_seq_port_subscribe_free)>;
743 snd_seq_t *seq_ = nullptr;
744 int queue_ = -1, iport_ = -1, total_fds_ = 0;
745 snd_midi_event_t *evparser_ = nullptr;
746 PortSubscribe subs_;
747 bool mdebug_ = false;
748 PortSubscribe
749 make_port_subscribe (snd_seq_port_subscribe_t *other = nullptr)
750 {
751 snd_seq_port_subscribe_t *subs = nullptr;
752 snd_seq_port_subscribe_malloc (&subs);
753 if (!subs)
754 return { nullptr, snd_seq_port_subscribe_free };
755 if (other)
756 snd_seq_port_subscribe_copy (subs, other);
757 return { subs, snd_seq_port_subscribe_free };
758 }
759public:
760 static MidiDriverP
761 create (const String &devid)
762 {
763 auto mdriverp = std::make_shared<AlsaSeqMidiDriver> (kvpair_key (devid), kvpair_value (devid));
764 return mdriverp;
765 }
766 explicit
767 AlsaSeqMidiDriver (const String &driver, const String &devid) :
768 MidiDriver (driver, devid), subs_ { nullptr, nullptr }
769 {}
770 ~AlsaSeqMidiDriver()
771 {
772 cleanup();
773 }
774 Error
775 initialize (const std::string &myname) // setup seq_, queue_
776 {
777 // https://www.alsa-project.org/alsa-doc/alsa-lib/seq.html
778 assert_return (seq_ == nullptr, Error::INTERNAL);
779 assert_return (queue_ == -1, Error::INTERNAL);
780 int aerror = 0;
781 if (!aerror)
782 aerror = snd_seq_open (&seq_, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
783 if (!aerror)
784 aerror = snd_seq_set_client_name (seq_, myname.c_str());
785 if (!aerror)
786 queue_ = snd_seq_alloc_named_queue (seq_, (myname + " SeqQueue").c_str());
787 snd_seq_queue_tempo_t *qtempo = alsa_alloca0 (snd_seq_queue_tempo);
788 snd_seq_queue_tempo_set_tempo (qtempo, 60 * 1000000 / 480); // 480 BPM in µs
789 snd_seq_queue_tempo_set_ppq (qtempo, 1920); // pulse per quarter note
790 if (!aerror)
791 aerror = snd_seq_set_queue_tempo (seq_, queue_, qtempo);
792 if (!aerror)
793 snd_seq_start_queue (seq_, queue_, nullptr);
794 if (!aerror)
795 aerror = snd_seq_drain_output (seq_);
796 if (aerror)
797 MDEBUG ("SndSeq: %s: initialization failed: %s", myname, snd_strerror (-aerror));
798 else
799 MDEBUG ("SndSeq: %s: queue started: %.5f", myname, queue_now());
800 return ase_error_from_errno (-aerror, Error::FILE_OPEN_FAILED);
801 }
802 static std::string
803 normalize (const std::string &string)
804 {
805 std::string normalized;
806 auto is_identifier_char = [] (int ch) {
807 return ( (ch >= 'A' && ch <= 'Z') ||
808 (ch >= 'a' && ch <= 'z') ||
809 (ch >= '0' && ch <= '9') ||
810 ch == '_' || ch == '$' );
811 };
812 for (size_t i = 0; i < string.size() && string[i]; ++i)
813 if (is_identifier_char (string[i]))
814 normalized += string[i];
815 else if (normalized.size() && normalized[normalized.size() - 1] != '-')
816 normalized += '-';
817 return normalized;
818 }
819 static std::string
820 make_devid (int card, uint type, const std::string &clientname, int client, uint caps)
821 {
822 if (0 == (type & SND_SEQ_PORT_TYPE_MIDI_GENERIC))
823 return ""; // not suitable
824 std::string devid;
825 if ((type & SND_SEQ_PORT_TYPE_SYNTHESIZER) && (type & SND_SEQ_PORT_TYPE_HARDWARE))
826 devid = "hwsynth:";
827 else if ((type & SND_SEQ_PORT_TYPE_SYNTHESIZER) && (type & SND_SEQ_PORT_TYPE_SOFTWARE))
828 devid = "softsynth:";
829 else if (type & SND_SEQ_PORT_TYPE_SYNTHESIZER)
830 devid = "synth:";
831 else if (type & SND_SEQ_PORT_TYPE_APPLICATION)
832 devid = "midiapp:";
833 else if (type & SND_SEQ_PORT_TYPE_HARDWARE)
834 devid = "hwmidi:";
835 else if (type & SND_SEQ_PORT_TYPE_SOFTWARE)
836 devid = "softmidi:";
837 else // MIDI_GENERIC
838 devid = "gmidi:";
839 std::string cardid;
840 if (card >= 0)
841 {
842 snd_ctl_t *chandle = nullptr;
843 const auto sbuf = string_format ("hw:CARD=%u", card);
844 if (snd_ctl_open (&chandle, sbuf.c_str(), SND_CTL_NONBLOCK) >= 0 && chandle)
845 {
846 snd_ctl_card_info_t *cinfo = alsa_alloca0 (snd_ctl_card_info);
847 if (snd_ctl_card_info (chandle, cinfo) >= 0)
848 {
849 const char *cid = snd_ctl_card_info_get_id (cinfo);
850 if (cid)
851 cardid = cid;
852 }
853 snd_ctl_close (chandle);
854 }
855 }
856 if (!cardid.empty())
857 devid += normalize (cardid);
858 else if (!clientname.empty())
859 devid += normalize (clientname);
860 else // unlikely, ALSA makes up *some* clientname
861 return ""; // not suitable
862 // devid += "."; devid += itos (port);
863 return devid;
864 }
865 bool
866 enumerate (Driver::EntryVec *entries, snd_seq_port_info_t *sinfo = nullptr, const std::string &selector = "", uint need_caps = 0)
867 {
868 assert_return (seq_ != nullptr, false);
869 snd_seq_client_info_t *cinfo = alsa_alloca0 (snd_seq_client_info);
870 snd_seq_client_info_set_client (cinfo, -1);
871 while (snd_seq_query_next_client (seq_, cinfo) == 0)
872 {
873 const int client = snd_seq_client_info_get_client (cinfo);
874 if (client == 0)
875 continue; // System Sequencer
876 snd_seq_port_info_t *pinfo = alsa_alloca0 (snd_seq_port_info);
877 snd_seq_port_info_set_client (pinfo, client);
878 snd_seq_port_info_set_port (pinfo, -1);
879 while (snd_seq_query_next_port (seq_, pinfo) == 0)
880 {
881 const uint tmask = SND_SEQ_PORT_TYPE_MIDI_GENERIC |
882 SND_SEQ_PORT_TYPE_SYNTHESIZER |
883 SND_SEQ_PORT_TYPE_APPLICATION;
884 const int type = snd_seq_port_info_get_type (pinfo);
885 if (0 == (type & tmask))
886 continue;
887 const uint cmask = SND_SEQ_PORT_CAP_READ |
888 SND_SEQ_PORT_CAP_WRITE |
889 SND_SEQ_PORT_CAP_DUPLEX;
890 const int caps = snd_seq_port_info_get_capability (pinfo);
891 if (0 == (caps & cmask) || need_caps != (need_caps & caps))
892 continue;
893 const int card = snd_seq_client_info_get_card (cinfo);
894 const std::string clientname = snd_seq_client_info_get_name (cinfo);
895 std::string devportid = make_devid (card, type, clientname, client, caps);
896 if (devportid.empty())
897 continue; // device needs to be uniquely identifiable
898 const int cport = snd_seq_port_info_get_port (pinfo);
899 devportid += "." + string_from_int (cport);
900 if (entries)
901 {
902 std::string cardname, longname;
903 if (card >= 0)
904 {
905 char *str = nullptr;
906 if (snd_card_get_longname (card, &str) == 0 && str)
907 longname = str;
908 if (str)
909 free (str);
910 if (snd_card_get_name (card, &str) == 0 && str)
911 cardname = str;
912 if (str)
913 free (str);
914 }
915 const bool is_usb = longname.find (" at usb-") != std::string::npos;
916 const bool is_kern = snd_seq_client_info_get_type (cinfo) == SND_SEQ_KERNEL_CLIENT;
917 const bool is_thru = is_kern && clientname == "Midi Through";
918 const std::string devname = string_capitalize (clientname, 1, false);
919 Driver::Entry entry;
920 entry.devid = devportid;
921 entry.device_name = string_capitalize (snd_seq_port_info_get_name (pinfo), 1, false);
922 if (!string_startswith (entry.device_name, devname))
923 entry.device_name = devname + " " + entry.device_name;
924 if (!cardname.empty())
925 entry.device_name += " - " + cardname;
926 if (caps & SND_SEQ_PORT_CAP_DUPLEX)
927 entry.capabilities = "Full-Duplex MIDI";
928 else if ((caps & SND_SEQ_PORT_CAP_READ) && (caps & SND_SEQ_PORT_CAP_WRITE))
929 entry.capabilities = "MIDI In-Out";
930 else if (caps & SND_SEQ_PORT_CAP_READ)
931 entry.capabilities = "MIDI Output";
932 else if (caps & SND_SEQ_PORT_CAP_WRITE)
933 entry.capabilities = "MIDI Input";
934 if (!string_startswith (longname, cardname + " at "))
935 entry.device_info = longname;
936 if (type & SND_SEQ_PORT_TYPE_APPLICATION || !is_kern)
937 entry.notice = "Note: MIDI device is provided by an application";
938 entry.readonly = (caps & SND_SEQ_PORT_CAP_READ) && !(caps & SND_SEQ_PORT_CAP_WRITE);
939 entry.writeonly = (caps & SND_SEQ_PORT_CAP_WRITE) && !(caps & SND_SEQ_PORT_CAP_READ);
940 entry.priority = is_thru ? Driver::MIDI_THRU :
941 is_usb ? Driver::ALSA_USB :
942 is_kern ? Driver::ALSA_KERN :
943 Driver::ALSA_USER;
944 entry.priority += Driver::WCARD * MAX (0, card);
945 entry.priority += Driver::WDEV * client; // priorize HW over software apps
946 entry.priority += Driver::WSUB * cport;
947 entries->push_back (entry);
948 MDEBUG ("DISCOVER: %s - %s", entry.devid, entry.device_name);
949 }
950 const bool match = selector == devportid;
951 if (match && sinfo)
952 {
953 snd_seq_port_info_copy (sinfo, pinfo);
954 return true;
955 }
956 }
957 }
958 return false;
959 }
960 static void
961 list_drivers (Driver::EntryVec &entries)
962 {
963 AlsaSeqMidiDriver smd ("?", "");
964 if (smd.initialize (program_alias() + " Probing") == Error::NONE)
965 smd.enumerate (&entries);
966 }
967 virtual Error // setup iport_ subs_ evparser_ total_fds_
968 open (IODir iodir) override
969 {
970 // initial sequencer setup
971 assert_return (iport_ == -1, Error::INTERNAL);
972 assert_return (!subs_, Error::INTERNAL);
973 assert_return (!evparser_, Error::INTERNAL);
974 assert_return (total_fds_ == 0, Error::INTERNAL);
975 PortSubscribe psub = make_port_subscribe();
976 assert_return (!!psub, Error::NO_MEMORY);
977 const std::string myname = program_alias();
978 Error error = seq_ ? Error::NONE : initialize (myname);
979 if (error != Error::NONE)
980 return error;
981 assert_return (queue_ >= -1, Error::INTERNAL);
982 // find devid_
983 const bool require_readable = iodir == READONLY || iodir == READWRITE;
984 const bool require_writable = iodir == WRITEONLY || iodir == READWRITE;
985 const uint caps = require_writable * (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ) +
986 require_readable * (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE);
987 snd_seq_port_info_t *pinfo = alsa_alloca0 (snd_seq_port_info);
988 const bool match_devid = enumerate (nullptr, pinfo, devid_, caps);
989 if (!match_devid)
990 return Error::DEVICE_NOT_AVAILABLE;
991 flags_ |= Flags::READABLE * require_readable;
992 flags_ |= Flags::WRITABLE * require_writable;
993 // create port for subscription
994 snd_seq_port_info_t *minfo = alsa_alloca0 (snd_seq_port_info);
995 snd_seq_port_info_set_port (minfo, 0); // desired port number
996 snd_seq_port_info_set_port_specified (minfo, true);
997 snd_seq_port_info_set_name (minfo, (myname + " LSP-0").c_str()); // Local Subscription Port
998 snd_seq_port_info_set_type (minfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION);
999 const int intracaps = SND_SEQ_PORT_CAP_NO_EXPORT | SND_SEQ_PORT_CAP_WRITE;
1000 snd_seq_port_info_set_capability (minfo, intracaps);
1001 snd_seq_port_info_set_midi_channels (minfo, 16);
1002 snd_seq_port_info_set_timestamping (minfo, true);
1003 snd_seq_port_info_set_timestamp_real (minfo, true);
1004 snd_seq_port_info_set_timestamp_queue (minfo, queue_);
1005 int aerror = snd_seq_create_port (seq_, minfo);
1006 if (!aerror)
1007 {
1008 iport_ = snd_seq_port_info_get_port (minfo);
1009 if (iport_ < 0)
1010 aerror = -ENOMEM;
1011 }
1012 // subscribe to port
1013 snd_seq_addr_t qaddr = {};
1014 qaddr.client = snd_seq_port_info_get_client (pinfo);
1015 qaddr.port = snd_seq_port_info_get_port (pinfo);
1016 snd_seq_port_subscribe_set_sender (&*psub, &qaddr);
1017 qaddr.client = snd_seq_client_id (seq_);
1018 qaddr.port = iport_; // receiver
1019 snd_seq_port_subscribe_set_dest (&*psub, &qaddr);
1020 snd_seq_port_subscribe_set_queue (&*psub, queue_);
1021 snd_seq_port_subscribe_set_time_update (&*psub, true);
1022 snd_seq_port_subscribe_set_time_real (&*psub, true);
1023 if (!aerror)
1024 aerror = snd_seq_subscribe_port (seq_, &*psub);
1025 if (!aerror)
1026 {
1027 subs_ = make_port_subscribe (&*psub);
1028 if (!subs_)
1029 aerror = -ENOMEM;
1030 }
1031 // setup event parser
1032 if (!aerror)
1033 snd_seq_drain_output (seq_);
1034 if (!aerror)
1035 aerror = snd_midi_event_new (1024, &evparser_);
1036 if (!aerror)
1037 {
1038 snd_midi_event_init (evparser_);
1039 snd_midi_event_no_status (evparser_, true);
1040 }
1041 // done, cleanup
1042 if (!aerror)
1043 {
1044 // Note, this *only* sets up polling for MIDI reading...
1045 total_fds_ = snd_seq_poll_descriptors_count (seq_, POLLIN);
1046 if (total_fds_ > 0)
1047 {
1048 struct pollfd *pfds = (struct pollfd*) alloca (total_fds_ * sizeof (struct pollfd));
1049 if (snd_seq_poll_descriptors (seq_, pfds, total_fds_, POLLIN) > 0)
1050 {
1051#if WITH_MIDI_POLL
1052 AseTrans *trans = ase_trans_open ();
1053 static_assert (sizeof (struct pollfd) == sizeof (GPollFD));
1054 AseJob *job = ase_job_add_poll (pollin_func, (void*) this, pollfree_func, total_fds_, (const GPollFD*) pfds);
1055 ase_trans_add (trans, job);
1056 ase_trans_commit (trans);
1057#endif
1058 }
1059 else
1060 total_fds_ = 0;
1061 }
1062 flags_ |= Flags::OPENED;
1063 }
1064 error = !aerror ? Error::NONE : ase_error_from_errno (-aerror, Error::FILE_OPEN_FAILED);
1065 MDEBUG ("SndSeq: %s: opening readable=%d writable=%d: %s", devid_, readable(), writable(), ase_error_blurb (error));
1066 if (error != Error::NONE)
1067 cleanup();
1068 else
1069 mdebug_ = debug_key_enabled ("midievent");
1070 return error;
1071 }
1072 void
1073 cleanup()
1074 {
1075 if (total_fds_ > 0)
1076 {
1077 total_fds_ = 0;
1078#if WITH_MIDI_POLL
1079 AseTrans *trans = ase_trans_open ();
1080 AseJob *job = ase_job_remove_poll (pollin_func, (void*) this);
1081 ase_trans_add (trans, job);
1082 ase_trans_commit (trans);
1083#endif
1084 }
1085 if (evparser_)
1086 {
1087 snd_midi_event_free (evparser_);
1088 evparser_ = nullptr;
1089 }
1090 if (subs_)
1091 {
1092 snd_seq_unsubscribe_port (seq_, &*subs_);
1093 subs_.reset();
1094 }
1095 if (iport_ >= 0)
1096 {
1097 snd_seq_delete_port (seq_, iport_);
1098 iport_ = -1;
1099 }
1100 if (queue_ >= 0)
1101 {
1102 snd_seq_free_queue (seq_, queue_);
1103 queue_ = -1;
1104 }
1105 if (seq_)
1106 {
1107 snd_seq_close (seq_);
1108 seq_ = nullptr;
1109 }
1110 }
1111 virtual void
1112 close () override
1113 {
1114 assert_return (opened());
1115 cleanup();
1116 MDEBUG ("SndSeq: %s: CLOSE: r=%d w=%d", devid_, readable(), writable());
1117 flags_ &= ~size_t (Flags::OPENED | Flags::READABLE | Flags::WRITABLE);
1118 mdebug_ = false;
1119 }
1120#if WITH_MIDI_POLL
1121 static void
1122 pollfree_func (void *data)
1123 {
1124 // AlsaSeqMidiDriver *thisp = (AlsaSeqMidiDriver*) data;
1125 }
1126#endif
1127 double
1128 queue_now ()
1129 {
1130 union { uint64_t u64[16]; char c[1]; } stbuf = {};
1131 assert_return (snd_seq_queue_status_sizeof() <= sizeof (stbuf), NAN);
1132 snd_seq_queue_status_t *stat = (snd_seq_queue_status_t*) &stbuf;
1133 int aerror = snd_seq_get_queue_status (seq_, queue_, stat);
1134 if (!aerror)
1135 {
1136 const snd_seq_real_time_t *rt = snd_seq_queue_status_get_real_time (stat);
1137 return rt->tv_sec + 1e-9 * rt->tv_nsec;
1138 }
1139 return NAN;
1140 }
1141 bool
1142 has_events () override
1143 {
1144 assert_return (opened(), false);
1145 const bool pull_fifo = true;
1146 return snd_seq_event_input_pending (seq_, pull_fifo) > 0;
1147 }
1148 uint
1149 fetch_events (MidiEventOutput &estream, double samplerate) override
1150 {
1151 assert_return (!!evparser_, 0);
1152 const size_t old_size = estream.size();
1153 // receive
1154 snd_seq_event_t *ev = nullptr;
1155 const double now = queue_now();
1156 const auto mkid = [] (uint note, uint channel) {
1157 return (channel + 1) * 128 + note;
1158 };
1159 bool must_sort = false;
1160 const auto add = [&] (MidiEventOutput &estream, const snd_seq_event_t *ev, const MidiEvent &event) {
1161 const double t = ev->time.time.tv_sec + 1e-9 * ev->time.time.tv_nsec;
1162 const double diff = t - now;
1163 int64_t frames = diff * samplerate;
1164 if (event.type == event.NOTE_OFF)
1165 { // guard against devices with out-of-order events
1166 const auto last_frame = estream.last_frame();
1167 frames = std::max (frames, last_frame);
1168 }
1169 int16_t frame_delay = CLAMP (frames, -2048, 0); // ignore future scheduling, only account for delays
1170 must_sort |= estream.append_unsorted (frame_delay, event);
1171 };
1172 int r;
1173 while (r = snd_seq_event_input (seq_, &ev), r >= 0)
1174 switch (ev->type)
1175 {
1176 case SND_SEQ_EVENT_NOTEON:
1177 add (estream, ev,
1178 make_note_on (ev->data.note.channel, ev->data.note.note,
1179 ev->data.note.velocity * (1.0 / 127.0), 0,
1180 mkid (ev->data.note.note, ev->data.note.channel)));
1181 break;
1182 case SND_SEQ_EVENT_NOTEOFF:
1183 add (estream, ev,
1184 make_note_off (ev->data.note.channel, ev->data.note.note,
1185 ev->data.note.velocity * (1.0 / 127.0), 0,
1186 mkid (ev->data.note.note, ev->data.note.channel)));
1187 break;
1188 case SND_SEQ_EVENT_KEYPRESS:
1189 add (estream, ev,
1190 make_aftertouch (ev->data.note.channel, ev->data.note.note,
1191 ev->data.note.velocity * (1.0 / 127.0), 0,
1192 mkid (ev->data.note.note, ev->data.note.channel)));
1193 break;
1194 case SND_SEQ_EVENT_CONTROLLER:
1195 add (estream, ev,
1196 make_control8 (ev->data.control.channel, ev->data.control.param,
1197 ev->data.control.value));
1198 break;
1199 case SND_SEQ_EVENT_PGMCHANGE:
1200 add (estream, ev,
1201 make_program (ev->data.control.channel, ev->data.control.value));
1202 break;
1203 case SND_SEQ_EVENT_CHANPRESS:
1204 add (estream, ev,
1205 make_pressure (ev->data.control.channel, ev->data.control.value * (1.0 / 127.0)));
1206 break;
1207 case SND_SEQ_EVENT_PITCHBEND:
1208 add (estream, ev,
1209 make_pitch_bend (ev->data.control.channel,
1210 ev->data.control.value *
1211 (ev->data.control.value < 0 ? 1.0 / 8192.0 : 1.0 / 8191.0)));
1212 break;
1213 case SND_SEQ_EVENT_SYSEX:
1214 MDEBUG ("%+4d ch=%-2u SYSEX: %s",
1215 int (samplerate * (ev->time.time.tv_sec + 1e-9 * ev->time.time.tv_nsec - now)),
1216 ev->data.control.channel, hex_str (ev->data.ext.len, (const uint8*) ev->data.ext.ptr));
1217 break;
1218 case SND_SEQ_EVENT_CLOCK:
1219 // skip debug message
1220 break;
1221 case SND_SEQ_EVENT_CONTROL14:
1222 case SND_SEQ_EVENT_NONREGPARAM:
1223 case SND_SEQ_EVENT_REGPARAM:
1224 case SND_SEQ_EVENT_NOTE: // unhandled, duration usually too long for MidiEvent.frame
1225 default:
1226 MDEBUG ("%+4d ch=%-2u SND_SEQ_EVENT_... %u",
1227 int (samplerate * (ev->time.time.tv_sec + 1e-9 * ev->time.time.tv_nsec - now)),
1228 ev->data.control.channel, ev->type);
1229 break;
1230 // DEPRECATED: snd_seq_free_event (ev);
1231 }
1232 if (r < 0 && r != -EAGAIN) // -ENOSPC - sequencer FIFO overran
1233 MDEBUG ("SndSeq: %s: snd_seq_event_input: %s", devid_, snd_strerror (r));
1234 if (ASE_UNLIKELY (mdebug_))
1235 for (size_t i = old_size; i < estream.size(); i++)
1236 MDEBUG ("%s", (estream.begin() + i)->to_string());
1237 if (must_sort) // guard against devices with out-of-order events
1238 estream.ensure_order();
1239 return estream.size() - old_size;
1240 }
1241};
1242
1243static const String alsa_seqmidi_driverid = MidiDriver::register_driver ("alsa",
1244 AlsaSeqMidiDriver::create,
1245 AlsaSeqMidiDriver::list_drivers);
1246
1247static std::string
1248hex_str (uint len, const uint8 *d)
1249{
1250 std::string s;
1251 for (uint i = 0; i < 16 && i < len; i++)
1252 {
1253 if (!i)
1254 s += string_format ("%02x", d[i]);
1255 else
1256 s += string_format (" %02x", d[i]);
1257 }
1258 if (len > 16)
1259 s += "…";
1260 return s;
1261}
1262
1263} // Ase
1264
1265#endif // __has_include(<alsa/asoundlib.h>)
#define ENOMEM
T append(T... args)
T c_str(T... args)
close
#define ASE_UNLIKELY(expr)
Compiler hint to optimize for expr evaluating to false.
Definition cxxaux.hh:46
T empty(T... args)
T find(T... args)
free
stat
#define assert_return(expr,...)
Return from the current function if expr is unmet and issue an assertion warning.
Definition internal.hh:29
#define MIN(a, b)
Yield minimum of a and b.
Definition internal.hh:55
#define CLAMP(v, mi, ma)
Yield v clamped to [mi … ma].
Definition internal.hh:58
#define MAX(a, b)
Yield maximum of a and b.
Definition internal.hh:52
T max(T... args)
memset
String normalize(const String &path)
Convert path to normal form.
Definition path.cc:86
The Anklang C++ API namespace.
Definition api.hh:9
std::string string_format(const char *format, const Args &...args) __attribute__((__format__(__printf__
Format a string similar to sprintf(3) with support for std::string and std::ostringstream convertible...
bool debug_key_enabled(const char *conditional)
Check if conditional is enabled by $ASE_DEBUG.
Definition utils.cc:38
String string_join(const String &junctor, const StringS &strvec)
Definition strings.cc:452
int16_t int16
A 16-bit signed integer.
Definition cxxaux.hh:27
float const_float_zeros[AUDIO_BLOCK_FLOAT_ZEROS_SIZE]
Block of const floats allof value 0.
Definition datautils.cc:7
uint8_t uint8
An 8-bit unsigned integer.
Definition cxxaux.hh:22
bool string_option_check(const String &optionlist, const String &feature)
Check if an option is set/unset in an options list string.
Definition strings.cc:1412
String string_capitalize(const String &str, size_t maxn, bool rest_tolower)
Capitalize words, so the first letter is upper case, the rest lower case.
Definition strings.cc:186
Error
Enum representing Error states.
Definition api.hh:22
String string_from_int(int64 value)
Convert a 64bit signed integer into a string.
Definition strings.cc:604
StringS string_split_any(const String &string, const String &splitchars, size_t maxn)
Definition strings.cc:365
String program_alias()
Retrieve the program name as used for logging or debug messages.
Definition platform.cc:849
std::string String
Convenience alias for std::string.
Definition cxxaux.hh:35
uint32_t uint
Provide 'uint' as convenience type.
Definition cxxaux.hh:18
String string_from_type(Type value)
Create a string from a templated argument value, such as bool, int, double.
Definition strings.hh:115
bool string_startswith(const String &string, const String &fragment)
Returns whether string starts with fragment.
Definition strings.cc:846
open
T size(T... args)
typedef int16_t
strncmp
typedef ssize_t
va_start
vsnprintf
T ws(T... args)