hydrogen 1.2.6
AlsaMidiDriver.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2025 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY, without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see https://www.gnu.org/licenses
20 *
21 */
22
24
25#if defined(H2CORE_HAVE_ALSA) || _DOXYGEN_
26
28#include <core/Hydrogen.h>
30
31#include <core/Globals.h>
32#include <core/EventQueue.h>
33
34#include <pthread.h>
35#include <core/Basics/Note.h>
38
39#ifdef H2CORE_HAVE_LASH
41#endif
42
43namespace H2Core
44{
45
47
49
50snd_seq_t *seq_handle = nullptr;
51int npfd;
52struct pollfd *pfd;
56
57
58void* alsaMidiDriver_thread( void* param )
59{
60 Base * __object = ( Base * )param;
61 AlsaMidiDriver *pDriver = ( AlsaMidiDriver* )param;
62 __INFOLOG( "starting" );
63
64 if ( seq_handle != nullptr ) {
65 __ERRORLOG( "seq_handle != NULL" );
66 pthread_exit( nullptr );
67 }
68
69 int err;
70 if ( ( err = snd_seq_open( &seq_handle, "hw", SND_SEQ_OPEN_DUPLEX, 0 ) ) < 0 ) {
71 __ERRORLOG( QString( "Error opening ALSA sequencer: %1" ).arg( QString::fromLocal8Bit(snd_strerror(err)) ) );
72 pthread_exit( nullptr );
73 }
74
75 snd_seq_set_client_name( seq_handle, "Hydrogen" );
76
77 if ( ( portId = snd_seq_create_simple_port( seq_handle,
78 "Hydrogen Midi-In",
79 SND_SEQ_PORT_CAP_WRITE |
80 SND_SEQ_PORT_CAP_SUBS_WRITE,
81 SND_SEQ_PORT_TYPE_APPLICATION
82 )
83 ) < 0 ) {
84 __ERRORLOG( "Error creating sequencer port." );
85 pthread_exit( nullptr );
86 }
87
88 if ( ( outPortId = snd_seq_create_simple_port( seq_handle,
89 "Hydrogen Midi-Out",
90 SND_SEQ_PORT_CAP_READ |
91 SND_SEQ_PORT_CAP_SUBS_READ,
92 SND_SEQ_PORT_TYPE_APPLICATION
93 )
94 ) < 0 ) {
95 __ERRORLOG( "Error creating sequencer port." );
96 pthread_exit( nullptr );
97 }
98
99 clientId = snd_seq_client_id( seq_handle );
100
101#ifdef H2CORE_HAVE_LASH
102 if ( Preferences::get_instance()->useLash() ){
103 LashClient* lashClient = LashClient::get_instance();
104 if (lashClient && lashClient->isConnected())
105 {
106 lashClient->setAlsaClientId((unsigned char) clientId);
107 }
108 }
109#endif
110
111
112 int m_local_addr_inport = portId;
113 int m_local_addr_outport = outPortId;
114 int m_local_addr_client = clientId;
115
116 QString sPortName = Preferences::get_instance()->m_sMidiPortName;
117 int m_dest_addr_port = -1;
118 int m_dest_addr_client = -1;
119 pDriver->getPortInfo( sPortName, m_dest_addr_client, m_dest_addr_port );
120 __INFOLOG( "MIDI input port name: " + sPortName );
121 __INFOLOG( QString( "MIDI input addr client: %1").arg( m_dest_addr_client ) );
122 __INFOLOG( QString( "MIDI input addr port: %1").arg( m_dest_addr_port ) );
123
124 if ( ( m_dest_addr_port != -1 ) && ( m_dest_addr_client != -1 ) ) {
125 snd_seq_port_subscribe_t *subs;
126 snd_seq_port_subscribe_alloca( &subs );
127 snd_seq_addr_t sender, dest;
128
129 sender.client = m_dest_addr_client;
130 sender.port = m_dest_addr_port;
131 dest.client = m_local_addr_client;
132 dest.port = m_local_addr_inport;
133
134 /* set in and out ports */
135 snd_seq_port_subscribe_set_sender( subs, &sender );
136 snd_seq_port_subscribe_set_dest( subs, &dest );
137
138 /* subscribe */
139 int ret = snd_seq_subscribe_port( seq_handle, subs );
140 if ( ret < 0 ) {
141 __ERRORLOG( QString( "snd_seq_subscribe_port(%1:%2) error" ).arg( m_dest_addr_client ).arg( m_dest_addr_port ) );
142 }
143 }
144
145 __INFOLOG( QString( "Midi input port at %1:%2" ).arg( clientId ).arg( portId ) );
146
147 //Connect output port to predefined output
149 m_dest_addr_port = -1;
150 m_dest_addr_client = -1;
151 pDriver->getPortInfo( sPortName, m_dest_addr_client, m_dest_addr_port );
152 __INFOLOG( "MIDI output port name: " + sPortName );
153 __INFOLOG( QString( "MIDI output addr client: %1").arg( m_dest_addr_client ) );
154 __INFOLOG( QString( "MIDI output addr port: %1").arg( m_dest_addr_port ) );
155
156 if ( ( m_dest_addr_port != -1 ) && ( m_dest_addr_client != -1 ) ) {
157 snd_seq_port_subscribe_t *subs;
158 snd_seq_port_subscribe_alloca( &subs );
159 snd_seq_addr_t sender, dest;
160
161 sender.client = m_local_addr_client;
162 sender.port = m_local_addr_outport;
163 dest.client = m_dest_addr_client;
164 dest.port = m_dest_addr_port;
165
166 /* set in and out ports */
167 snd_seq_port_subscribe_set_sender( subs, &sender );
168 snd_seq_port_subscribe_set_dest( subs, &dest );
169
170 /* subscribe */
171 int ret = snd_seq_subscribe_port( seq_handle, subs );
172 if ( ret < 0 ) {
173 __ERRORLOG( QString( "snd_seq_subscribe_port(%1:%2) error" ).arg( m_dest_addr_client ).arg( m_dest_addr_port ) );
174 }
175 }
176
177 __INFOLOG( QString( "Midi output port at %1:%2" ).arg( clientId ).arg( outPortId ) );
178
179
180 npfd = snd_seq_poll_descriptors_count( seq_handle, POLLIN );
181 pfd = ( struct pollfd* )alloca( npfd * sizeof( struct pollfd ) );
182 snd_seq_poll_descriptors( seq_handle, pfd, npfd, POLLIN );
183
184 __INFOLOG( "MIDI Thread INIT" );
185 while ( isMidiDriverRunning ) {
186 if ( poll( pfd, npfd, 100 ) > 0 ) {
187 pDriver->midi_action( seq_handle );
188 }
189 }
190 snd_seq_close ( seq_handle );
191 seq_handle = nullptr;
192 __INFOLOG( "MIDI Thread DESTROY" );
193
194 pthread_exit( nullptr );
195 return nullptr;
196}
197
198
199
200
203{
204// infoLog("INIT");
205}
206
207
208
209
211{
212 if ( isMidiDriverRunning ) {
213 close();
214 }
215// infoLog("DESTROY");
216}
217
219{
220 // start main thread
221 isMidiDriverRunning = true;
222 pthread_attr_t attr;
223 pthread_attr_init( &attr );
224 pthread_create( &midiDriverThread, &attr, alsaMidiDriver_thread, ( void* )this );
225}
226
227
228
229
231{
232 isMidiDriverRunning = false;
233 pthread_join( midiDriverThread, nullptr );
234}
235
236
237
238
239
241{
242 auto pAudioEngine = Hydrogen::get_instance()->getAudioEngine();
243 if ( ( pAudioEngine->getState() != AudioEngine::State::Ready ) &&
244 ( pAudioEngine->getState() != AudioEngine::State::Playing ) ) {
245// ERRORLOG( "Skipping midi event! Audio Engine not ready." );
246 return;
247 }
248
249// bool useMidiTransport = true;
250
251 snd_seq_event_t *ev;
252 do {
253 if ( !seq_handle ) {
254 break;
255 }
256 snd_seq_event_input( seq_handle, &ev );
257
258 if ( m_bActive && ev != nullptr ) {
259
260 MidiMessage msg;
261
262 switch ( ev->type ) {
263 case SND_SEQ_EVENT_NOTEON:
265 msg.m_nData1 = ev->data.note.note;
266 msg.m_nData2 = ev->data.note.velocity;
267 msg.m_nChannel = ev->data.control.channel;
268 break;
269
270 case SND_SEQ_EVENT_NOTEOFF:
272 msg.m_nData1 = ev->data.note.note;
273 msg.m_nData2 = ev->data.note.velocity;
274 msg.m_nChannel = ev->data.control.channel;
275 break;
276
277 case SND_SEQ_EVENT_CONTROLLER:
279 msg.m_nData1 = ev->data.control.param;
280 msg.m_nData2 = ev->data.control.value;
281 msg.m_nChannel = ev->data.control.channel;
282 break;
283
284 case SND_SEQ_EVENT_PGMCHANGE:
286 msg.m_nData1 = ev->data.control.value;
287 msg.m_nChannel = ev->data.control.channel;
288 break;
289
290 case SND_SEQ_EVENT_KEYPRESS:
292 msg.m_nData1 = ev->data.note.note;
293 msg.m_nData2 = ev->data.note.velocity;
294 msg.m_nChannel = ev->data.control.channel;
295 break;
296
297 case SND_SEQ_EVENT_CHANPRESS:
299 msg.m_nData1 = ev->data.control.param;
300 msg.m_nData2 = ev->data.control.value;
301 msg.m_nChannel = ev->data.control.channel;
302 break;
303
304 case SND_SEQ_EVENT_PITCHBEND:
306 msg.m_nData1 = ev->data.control.param;
307 msg.m_nData2 = ev->data.control.value;
308 msg.m_nChannel = ev->data.control.channel;
309 break;
310
311 case SND_SEQ_EVENT_SYSEX: {
313 snd_midi_event_t *seq_midi_parser;
314 if ( snd_midi_event_new( 32, &seq_midi_parser ) ) {
315 ERRORLOG( "Error creating midi event parser" );
316 }
317 unsigned char midi_event_buffer[ 256 ];
318 int _bytes_read = snd_midi_event_decode( seq_midi_parser, midi_event_buffer, 32, ev );
319
320 for ( int i = 0; i < _bytes_read; ++i ) {
321 msg.m_sysexData.push_back( midi_event_buffer[ i ] );
322 }
323 }
324 break;
325
326 case SND_SEQ_EVENT_QFRAME:
328 msg.m_nData1 = ev->data.control.value;
329 msg.m_nData2 = ev->data.control.param;
330 break;
331
332 case SND_SEQ_EVENT_SONGPOS:
334 msg.m_nData1 = ev->data.control.value;
335 msg.m_nData2 = ev->data.control.param;
336 break;
337
338 case SND_SEQ_EVENT_SONGSEL:
340 msg.m_nData1 = ev->data.control.value;
341 msg.m_nData2 = ev->data.control.param;
342 break;
343
344 case SND_SEQ_EVENT_TUNE_REQUEST:
346 msg.m_nData1 = ev->data.control.value;
347 msg.m_nData2 = ev->data.control.param;
348 break;
349
350 case SND_SEQ_EVENT_CLOCK:
352 break;
353
354 case SND_SEQ_EVENT_START:
356 break;
357
358 case SND_SEQ_EVENT_CONTINUE:
360 break;
361
362 case SND_SEQ_EVENT_STOP:
364 break;
365
366 case SND_SEQ_EVENT_SENSING:
368 break;
369
370 case SND_SEQ_EVENT_RESET:
372 break;
373
374 case SND_SEQ_EVENT_CLIENT_EXIT:
375 INFOLOG( "SND_SEQ_EVENT_CLIENT_EXIT" );
376 break;
377
378 case SND_SEQ_EVENT_PORT_SUBSCRIBED:
379 INFOLOG( "SND_SEQ_EVENT_PORT_SUBSCRIBED" );
380 break;
381
382 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
383 INFOLOG( "SND_SEQ_EVENT_PORT_UNSUBSCRIBED" );
384 break;
385
386 default:
387 WARNINGLOG( QString( "Unknown MIDI Event. type = %1" ).arg( ( int )ev->type ) );
388 }
389 if ( msg.m_type != MidiMessage::UNKNOWN ) {
390 handleMidiMessage( msg );
391 }
392 }
393 snd_seq_free_event( ev );
394 } while ( snd_seq_event_input_pending( seq_handle, 0 ) > 0 );
395}
396
398{
399 std::vector<QString> inputList;
400
401 if ( seq_handle == nullptr ) {
402 return inputList;
403 }
404
405 snd_seq_client_info_t *cinfo; // client info
406 snd_seq_port_info_t *pinfo; // port info
407
408 snd_seq_client_info_alloca( &cinfo );
409 snd_seq_client_info_set_client( cinfo, -1 );
410
411 /* while the next client one the sequencer is available */
412 while ( snd_seq_query_next_client( seq_handle, cinfo ) >= 0 ) {
413 // get client from cinfo
414 int client = snd_seq_client_info_get_client( cinfo );
415
416 // fill pinfo
417 snd_seq_port_info_alloca( &pinfo );
418 snd_seq_port_info_set_client( pinfo, client );
419 snd_seq_port_info_set_port( pinfo, -1 );
420
421 // while the next port is available
422 while ( snd_seq_query_next_port( seq_handle, pinfo ) >= 0 ) {
423
424 /* get its capability */
425 int cap = snd_seq_port_info_get_capability( pinfo );
426
427 if ( snd_seq_client_id( seq_handle ) != snd_seq_port_info_get_client( pinfo ) && snd_seq_port_info_get_client( pinfo ) != 0 ) {
428 // output ports
429 if (
430 ( cap & SND_SEQ_PORT_CAP_SUBS_WRITE ) != 0 &&
431 snd_seq_client_id( seq_handle ) != snd_seq_port_info_get_client( pinfo )
432 ) {
433 INFOLOG( snd_seq_port_info_get_name( pinfo ) );
434 inputList.push_back( snd_seq_port_info_get_name( pinfo ) );
435 //info.m_nClient = snd_seq_port_info_get_client(pinfo);
436 //info.m_nPort = snd_seq_port_info_get_port(pinfo);
437 }
438 }
439 }
440 }
441
442 return inputList;
443}
444
445
447{
448 std::vector<QString> outputList;
449
450 if ( seq_handle == nullptr ) {
451 return outputList;
452 }
453
454 snd_seq_client_info_t *cinfo; // client info
455 snd_seq_port_info_t *pinfo; // port info
456
457 snd_seq_client_info_alloca( &cinfo );
458 snd_seq_client_info_set_client( cinfo, -1 );
459
460 /* while the next client one the sequencer is available */
461 while ( snd_seq_query_next_client( seq_handle, cinfo ) >= 0 ) {
462 // get client from cinfo
463 int client = snd_seq_client_info_get_client( cinfo );
464
465 // fill pinfo
466 snd_seq_port_info_alloca( &pinfo );
467 snd_seq_port_info_set_client( pinfo, client );
468 snd_seq_port_info_set_port( pinfo, -1 );
469
470 // while the next port is available
471 while ( snd_seq_query_next_port( seq_handle, pinfo ) >= 0 ) {
472
473 /* get its capability */
474 int cap = snd_seq_port_info_get_capability( pinfo );
475
476 if ( snd_seq_client_id( seq_handle ) != snd_seq_port_info_get_client( pinfo ) && snd_seq_port_info_get_client( pinfo ) != 0 ) {
477 // output ports
478 if (
479 ( cap & SND_SEQ_PORT_CAP_SUBS_READ ) != 0 &&
480 snd_seq_client_id( seq_handle ) != snd_seq_port_info_get_client( pinfo )
481 ) {
482 INFOLOG( snd_seq_port_info_get_name( pinfo ) );
483 outputList.push_back( snd_seq_port_info_get_name( pinfo ) );
484 //info.m_nClient = snd_seq_port_info_get_client(pinfo);
485 //info.m_nPort = snd_seq_port_info_get_port(pinfo);
486 }
487 }
488 }
489 }
490
491 return outputList;
492}
493
494void AlsaMidiDriver::getPortInfo( const QString& sPortName, int& nClient, int& nPort )
495{
496 if ( seq_handle == nullptr ) {
497 ERRORLOG( "seq_handle = NULL " );
498 return;
499 }
500
501 if ( sPortName == Preferences::getNullMidiPort() ) {
502 nClient = -1;
503 nPort = -1;
504 return;
505 }
506
507 snd_seq_client_info_t *cinfo; // client info
508 snd_seq_port_info_t *pinfo; // port info
509
510 snd_seq_client_info_alloca( &cinfo );
511 snd_seq_client_info_set_client( cinfo, -1 );
512
513 /* while the next client one the sequencer is available */
514 while ( snd_seq_query_next_client( seq_handle, cinfo ) >= 0 ) {
515 // get client from cinfo
516 int client = snd_seq_client_info_get_client( cinfo );
517
518 // fill pinfo
519 snd_seq_port_info_alloca( &pinfo );
520 snd_seq_port_info_set_client( pinfo, client );
521 snd_seq_port_info_set_port( pinfo, -1 );
522
523 // while the next port is avail
524 while ( snd_seq_query_next_port( seq_handle, pinfo ) >= 0 ) {
525 int cap = snd_seq_port_info_get_capability( pinfo );
526 if ( snd_seq_client_id( seq_handle ) != snd_seq_port_info_get_client( pinfo ) && snd_seq_port_info_get_client( pinfo ) != 0 ) {
527 // output ports
528 if (
529 ( cap & SND_SEQ_PORT_CAP_SUBS_READ ) != 0 &&
530 snd_seq_client_id( seq_handle ) != snd_seq_port_info_get_client( pinfo )
531 ) {
532 QString sName = snd_seq_port_info_get_name( pinfo );
533 if ( sName == sPortName ) {
534 nClient = snd_seq_port_info_get_client( pinfo );
535 nPort = snd_seq_port_info_get_port( pinfo );
536
537 INFOLOG( QString( "nClient %1" ).arg( nClient ) );
538 INFOLOG( QString( "nPort %1" ).arg( nPort ) );
539 return;
540 }
541 }
542 }
543 }
544 }
545 ERRORLOG( "Midi port " + sPortName + " not found" );
546}
547
549{
550 if ( seq_handle == nullptr ) {
551 ERRORLOG( "seq_handle = NULL " );
552 return;
553 }
554
555 int channel = pNote->get_instrument()->get_midi_out_channel();
556 if (channel < 0) {
557 return;
558 }
559 int key = pNote->get_midi_key();
560 int velocity = pNote->get_midi_velocity();
561
562 snd_seq_event_t ev;
563
564 //Note off
565 snd_seq_ev_clear(&ev);
566 snd_seq_ev_set_source(&ev, outPortId);
567 snd_seq_ev_set_subs(&ev);
568 snd_seq_ev_set_direct(&ev);
569 snd_seq_ev_set_noteoff(&ev, channel, key, velocity);
570 snd_seq_event_output(seq_handle, &ev);
571 snd_seq_drain_output(seq_handle);
572
573 //Note on
574 //snd_seq_event_input(seq_handle, &ev);
575 snd_seq_ev_clear(&ev);
576 snd_seq_ev_set_source(&ev, outPortId);
577 snd_seq_ev_set_subs(&ev);
578 snd_seq_ev_set_direct(&ev);
579 //snd_seq_event_output_direct( seq_handle, ev );
580
581 snd_seq_ev_set_noteon(&ev, channel, key, velocity);
582 snd_seq_event_output(seq_handle, &ev);
583
584 //snd_seq_free_event(ev);
585 snd_seq_drain_output(seq_handle);
586}
587
588
589void AlsaMidiDriver::handleOutgoingControlChange( int param, int value, int channel )
590{
591 snd_seq_event_t ev;
592 snd_seq_ev_clear(&ev);
593
594 ev.type = SND_SEQ_EVENT_CONTROLLER;
595
596 snd_seq_ev_set_source(&ev, outPortId);
597 snd_seq_ev_set_subs(&ev);
598 snd_seq_ev_set_direct(&ev);
599
600 ev.data.control.param = param;
601 ev.data.control.value = value;
602 ev.data.control.channel = channel;
603
604 snd_seq_event_output_direct(seq_handle, &ev);
605}
606
607void AlsaMidiDriver::handleQueueNoteOff( int channel, int key, int velocity )
608{
609 if ( seq_handle == nullptr ) {
610 ERRORLOG( "seq_handle = NULL " );
611 return;
612 }
613
614// channel = pNote->get_instrument()->get_midi_out_channel();
615 if (channel < 0) {
616 return;
617 }
618
619// key = (pNote->m_noteKey.m_nOctave +3 ) * 12 + pNote->m_noteKey.m_key;
620// int velocity = pNote->get_midi_velocity();
621
622 snd_seq_event_t ev;
623
624 //Note off
625 snd_seq_ev_clear(&ev);
626 snd_seq_ev_set_source(&ev, outPortId);
627 snd_seq_ev_set_subs(&ev);
628 snd_seq_ev_set_direct(&ev);
629 snd_seq_ev_set_noteoff(&ev, channel, key, velocity);
630 snd_seq_event_output(seq_handle, &ev);
631 snd_seq_drain_output(seq_handle);
632}
633
635{
636 if ( seq_handle == nullptr ) {
637 ERRORLOG( "seq_handle = NULL " );
638 return;
639 }
640
641 auto instList = Hydrogen::get_instance()->getSong()->getInstrumentList();
642
643 unsigned int numInstruments = instList->size();
644 for (int index = 0; index < numInstruments; ++index) {
645 auto curInst = instList->get(index);
646
647 int channel = curInst->get_midi_out_channel();
648 if (channel < 0) {
649 continue;
650 }
651 int key = curInst->get_midi_out_note();
652
653 snd_seq_event_t ev;
654
655 //Note off
656 snd_seq_ev_clear(&ev);
657 snd_seq_ev_set_source(&ev, outPortId);
658 snd_seq_ev_set_subs(&ev);
659 snd_seq_ev_set_direct(&ev);
660 snd_seq_ev_set_noteoff(&ev, channel, key, 0);
661 snd_seq_event_output(seq_handle, &ev);
662 snd_seq_drain_output(seq_handle);
663 }
664}
665
666};
667
668#endif // H2CORE_HAVE_ALSA
669
#define __ERRORLOG(x)
Definition Object.h:254
#define INFOLOG(x)
Definition Object.h:240
#define WARNINGLOG(x)
Definition Object.h:241
#define __INFOLOG(x)
Definition Object.h:252
#define ERRORLOG(x)
Definition Object.h:242
Alsa Midi Driver Based on Matthias Nagorni alsa sequencer example.
void getPortInfo(const QString &sPortName, int &nClient, int &nPort)
virtual void open() override
virtual std::vector< QString > getInputPortList() override
virtual void close() override
virtual void handleQueueNoteOff(int channel, int key, int velocity) override
virtual std::vector< QString > getOutputPortList() override
virtual void handleQueueAllNoteOff() override
void midi_action(snd_seq_t *seq_handle)
virtual void handleOutgoingControlChange(int param, int value, int channel) override
virtual void handleQueueNote(Note *pNote) override
@ Playing
Transport is rolling.
@ Ready
Ready to process audio.
Base class.
Definition Object.h:62
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:123
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:84
AudioEngine * getAudioEngine() const
Definition Hydrogen.h:663
void handleMidiMessage(const MidiMessage &msg)
Definition MidiInput.cpp:51
MidiMessageType m_type
Definition MidiCommon.h:93
std::vector< unsigned char > m_sysexData
Definition MidiCommon.h:97
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:101
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:499
int get_midi_key() const
return scaled key for midi output, !
Definition Note.h:681
int get_midi_velocity() const
midi velocity accessor
Definition Note.h:690
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
QString m_sMidiOutputPortName
static QString getNullMidiPort()
Choice of m_sMidiPortName and m_sMidiOutputPortName in case no port/device was selected.
bool isConnected()
static LashClient * get_instance()
Definition LashClient.h:39
void setAlsaClientId(unsigned char id)
snd_seq_t * seq_handle
bool isMidiDriverRunning
pthread_t midiDriverThread
void * alsaMidiDriver_thread(void *param)
struct pollfd * pfd