1616namespace libremidi ::alsa_seq
1717{
1818
19+ struct client_info
20+ {
21+ std::string client_name;
22+ int client_id{};
23+ std::optional<int > card{};
24+ };
25+
1926struct port_info
2027{
2128 std::string client_name;
@@ -93,6 +100,21 @@ class observer_impl
93100 }
94101 }
95102
103+ std::optional<client_info>
104+ get_client_info (int client, snd_seq_client_info_t & cinfo) const noexcept
105+ {
106+ client_info p;
107+ p.client_id = client;
108+
109+ if (auto name = snd.seq .client_info_get_name (&cinfo))
110+ p.client_name = name;
111+
112+ if (int card = snd.seq .client_info_get_card (&cinfo); card >= 0 )
113+ p.card = card;
114+
115+ return p;
116+ }
117+
96118 std::optional<port_info> get_info (
97119 int client, int port, snd_seq_client_info_t & cinfo,
98120 snd_seq_port_info_t & pinfo) const noexcept
@@ -281,19 +303,43 @@ class observer_impl
281303 }
282304 }
283305
284- void handle_event (const snd_seq_event_t & ev)
306+ void handle_event_direct (const snd_seq_event_t & ev)
285307 {
286308 switch (ev.type )
287309 {
310+ case SND_SEQ_EVENT_CLIENT_START: {
311+ // TODO
312+ break ;
313+ }
314+ case SND_SEQ_EVENT_CLIENT_EXIT: {
315+ // TODO
316+ break ;
317+ }
318+ case SND_SEQ_EVENT_CLIENT_CHANGE: {
319+ // TODO
320+ break ;
321+ }
322+ #if __has_include(<alsa/ump.h>)
323+ case SND_SEQ_EVENT_UMP_EP_CHANGE: {
324+ // TODO
325+ break ;
326+ }
327+ case SND_SEQ_EVENT_UMP_BLOCK_CHANGE: {
328+ // TODO
329+ break ;
330+ }
331+ #endif
288332 case SND_SEQ_EVENT_PORT_START: {
289- register_port (ev.data .addr .client , ev.data .addr .port );
333+ this -> register_port (ev.data .addr .client , ev.data .addr .port );
290334 break ;
291335 }
292336 case SND_SEQ_EVENT_PORT_EXIT: {
293- unregister_port (ev.data .addr .client , ev.data .addr .port );
337+ this -> unregister_port (ev.data .addr .client , ev.data .addr .port );
294338 break ;
295339 }
296340 case SND_SEQ_EVENT_PORT_CHANGE:
341+ // TODO
342+ break ;
297343 default :
298344 break ;
299345 }
@@ -328,38 +374,83 @@ class observer_threaded : public observer_impl<ConfigurationImpl>
328374 {
329375 // Create relevant descriptors
330376 auto & snd = alsa_data::snd;
377+
378+ // 1. Descriptor count
331379 const auto n = snd.seq .poll_descriptors_count (this ->seq , POLLIN);
332- descriptors_.resize (n + 1 );
333- snd.seq .poll_descriptors (this ->seq , descriptors_.data (), n, POLLIN);
334- descriptors_.back () = this ->termination_event ;
380+ int total_descriptors = n;
381+ total_descriptors++; // eventfd for terminating the thread
382+
383+ // 2. Create storage
384+ descriptors.resize (total_descriptors);
385+
386+ // 3. Store descriptors
387+ snd.seq .poll_descriptors (this ->seq , descriptors.data (), n, POLLIN);
388+ descriptors[n] = this ->termination_event ;
335389
336390 // Start the listening thread
337- thread = std::thread{[this ] {
391+ thread = std::thread{[this , n ] {
338392 auto & snd = alsa_data::snd;
339393 const auto period
340394 = std::chrono::duration_cast<std::chrono::milliseconds>(this ->configuration .poll_period )
341395 .count ();
342396 for (;;)
343397 {
344- int err = poll (descriptors_ .data (), descriptors_ .size (), static_cast <int32_t >(period));
398+ int err = poll (descriptors .data (), descriptors .size (), static_cast <int32_t >(period));
345399 if (err >= 0 )
346400 {
347401 // We got our stop-thread signal
348- if (descriptors_. back () .revents & POLLIN)
402+ if (descriptors[n] .revents & POLLIN)
349403 break ;
350404
405+ // Put ALSA event in our queue
351406 snd_seq_event_t * ev{};
352407 event_handle handle{snd};
353408 while (snd.seq .event_input (this ->seq , &ev) >= 0 )
354409 {
355410 handle.reset (ev);
356- this ->handle_event (*ev);
411+ this ->handle_event_delayed (*ev);
412+ }
413+
414+ // Process the events in a deferred way.
415+ // This is because udev takes some milliseconds to populate its field after a
416+ // port was added
417+ auto tm = std::chrono::steady_clock::now ();
418+ for (auto it = queued_events.begin (); it != queued_events.end ();)
419+ {
420+ if ((tm - it->second ) >= this ->configuration .poll_period )
421+ {
422+ this ->handle_event_direct (it->first );
423+ it = queued_events.erase (it);
424+ }
425+ else
426+ {
427+ break ;
428+ }
357429 }
358430 }
359431 }
360432 }};
361433 }
362434
435+ void handle_event_delayed (const snd_seq_event_t & ev)
436+ {
437+ switch (ev.type )
438+ {
439+ case SND_SEQ_EVENT_CLIENT_START:
440+ case SND_SEQ_EVENT_CLIENT_EXIT:
441+ case SND_SEQ_EVENT_CLIENT_CHANGE:
442+ case SND_SEQ_EVENT_UMP_EP_CHANGE:
443+ case SND_SEQ_EVENT_UMP_BLOCK_CHANGE:
444+ case SND_SEQ_EVENT_PORT_START:
445+ case SND_SEQ_EVENT_PORT_EXIT:
446+ case SND_SEQ_EVENT_PORT_CHANGE:
447+ queued_events.emplace_back (ev, std::chrono::steady_clock::now ());
448+ break ;
449+ default :
450+ break ;
451+ }
452+ }
453+
363454 ~observer_threaded ()
364455 {
365456 termination_event.notify ();
@@ -370,7 +461,8 @@ class observer_threaded : public observer_impl<ConfigurationImpl>
370461
371462 eventfd_notifier termination_event{};
372463 std::thread thread;
373- std::vector<pollfd> descriptors_;
464+ std::vector<pollfd> descriptors;
465+ std::vector<std::pair<snd_seq_event_t , std::chrono::steady_clock::time_point>> queued_events;
374466};
375467
376468template <typename ConfigurationImpl>
@@ -382,7 +474,7 @@ class observer_manual : public observer_impl<ConfigurationImpl>
382474 {
383475 this ->configuration .manual_poll (
384476 poll_parameters{.addr = this ->vaddr , .callback = [this ](const auto & v) {
385- this ->handle_event (v);
477+ this ->handle_event_direct (v);
386478 return 0 ;
387479 }});
388480 }
0 commit comments