1 //=========================================================
2 // MusE
3 // Linux Music Editor
4 // $Id: alsamidi.cpp,v 1.8.2.7 2009/11/19 04:20:33 terminator356 Exp $
5 // (C) Copyright 2000-2001 Werner Schweer (ws@seh.de)
6 // (C) Copyright 2011, 2015 Tim E. Real (terminator356 on sourceforge)
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; version 2 of
11 // the License, or (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, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 //
22 //=========================================================
23
24 #include "alsamidi.h"
25
26 #ifdef ALSA_SUPPORT
27
28 #include <stdio.h>
29
30 #include "globals.h"
31 #include "midi_consts.h"
32 #include "../midiport.h"
33 #include "../midiseq.h"
34 #include "../midictrl.h"
35 #include "../audio.h"
36 #include "minstrument.h"
37 #include "utils.h"
38 #include "helper.h"
39 #include "audiodev.h"
40 #include "xml.h"
41 #include "part.h"
42 #include "gconfig.h"
43 #include "track.h"
44 #include "song.h"
45 #include "muse_atomic.h"
46 #include "lock_free_buffer.h"
47 #include "evdata.h"
48
49 #include <QApplication>
50
51 // Enable debugging:
52 //#define ALSA_DEBUG 1
53
54 // For debugging output: Uncomment the fprintf section.
55 #define DEBUG_PRST_ROUTES(dev, format, args...) // fprintf(dev, format, ##args);
56
57 namespace MusECore {
58 muse_atomic_t atomicAlsaMidiScanPending;
59
60 static int alsaSeqFdi = -1;
61 static int alsaSeqFdo = -1;
62
63 snd_seq_t* alsaSeq = 0;
64 static snd_seq_addr_t musePort;
65 static snd_seq_addr_t announce_adr;
66
67 //---------------------------------------------------------
68 // createAlsaMidiDevice
69 // If name parameter is blank, creates a new (locally) unique one.
70 //---------------------------------------------------------
71
createAlsaMidiDevice(QString name,int rwflags)72 MidiDevice* MidiAlsaDevice::createAlsaMidiDevice(QString name, int rwflags) // 1:Writable 2: Readable 3: Writable + Readable
73 {
74 int ni = 0;
75 if(name.isEmpty())
76 {
77 for( ; ni < 65536; ++ni)
78 {
79 name = QString("alsa-midi-") + QString::number(ni);
80 if(!MusEGlobal::midiDevices.find(name))
81 break;
82 }
83 }
84 if(ni >= 65536)
85 {
86 fprintf(stderr, "MusE: createAlsaMidiDevice failed! Can't find an unused midi device name 'alsa-midi-[0-65535]'.\n");
87 return 0;
88 }
89
90 snd_seq_addr_t a;
91
92 // From seq.h: "Special client (port) ids SND_SEQ_ADDRESS_UNKNOWN 253 = unknown source"
93 // Hopefully we can use that as a 'valid' marker here. We can't use zero.
94 a.client = SND_SEQ_ADDRESS_UNKNOWN;
95 a.port = SND_SEQ_ADDRESS_UNKNOWN;
96
97 MidiAlsaDevice* dev = new MidiAlsaDevice(a, name);
98
99 dev->setrwFlags(rwflags);
100 MusEGlobal::midiDevices.add(dev);
101 return dev;
102 }
103
104
105 //---------------------------------------------------------
106 // MidiAlsaDevice
107 //---------------------------------------------------------
108
MidiAlsaDevice(const snd_seq_addr_t & a,const QString & n)109 MidiAlsaDevice::MidiAlsaDevice(const snd_seq_addr_t& a, const QString& n)
110 : MidiDevice(n)
111 {
112 // _playEventFifo = new LockFreeBuffer<MidiPlayEvent>(8192);
113 adr = a;
114 }
115
~MidiAlsaDevice()116 MidiAlsaDevice::~MidiAlsaDevice()
117 {
118 // if(_playEventFifo)
119 // delete _playEventFifo;
120 }
121
122 //---------------------------------------------------------
123 // selectWfd
124 //---------------------------------------------------------
125
selectWfd()126 int MidiAlsaDevice::selectWfd()
127 {
128 return alsaSeqFdo;
129 }
130
131 //---------------------------------------------------------
132 // open
133 //---------------------------------------------------------
134
open()135 QString MidiAlsaDevice::open()
136 {
137 _openFlags &= _rwFlags; // restrict to available bits
138
139 if(!alsaSeq)
140 {
141 _state = QString("Unavailable");
142 return _state;
143 }
144
145 snd_seq_port_info_t *pinfo = NULL;
146 snd_seq_port_subscribe_t* subs = NULL;
147
148 DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Getting port info: address: %d:%d\n", adr.client, adr.port);
149 if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN)
150 {
151 snd_seq_port_info_alloca(&pinfo);
152 int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo);
153 if(rv < 0)
154 {
155 fprintf(stderr, "MidiAlsaDevice::open Error getting port info: address: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv));
156 _state = QString(snd_strerror(rv));
157 return _state;
158 }
159 DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open: address: %d:%d\n", adr.client, adr.port);
160 // Allocated on stack, no need to call snd_seq_port_subscribe_free() later.
161 snd_seq_port_subscribe_alloca(&subs);
162 }
163
164 QString estr;
165 int wer = 0;
166 int rer = 0;
167
168 if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN)
169 {
170
171 int cap = snd_seq_port_info_get_capability(pinfo);
172
173 #ifdef ALSA_DEBUG
174 fprintf(stderr, "MidiAlsaDevice::open cap:%d\n", cap);
175 #endif
176
177 // subscribe for writing
178 if (_openFlags & 1)
179 {
180 if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE)
181 {
182 snd_seq_port_subscribe_set_sender(subs, &musePort);
183 snd_seq_port_subscribe_set_dest(subs, &adr);
184 DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Checking write subscription: address: %d:%d\n", adr.client, adr.port);
185 // Not already subscribed (or error)? Then try subscribing.
186 if(snd_seq_get_port_subscription(alsaSeq, subs) < 0)
187 {
188 //int error = snd_seq_subscribe_port(alsaSeq, subs);
189 wer = snd_seq_subscribe_port(alsaSeq, subs);
190 //if (error < 0)
191 if(wer < 0)
192 //return QString("Play: ")+QString(snd_strerror(error));
193 estr += (QString("Play: ") + QString(snd_strerror(wer)) + QString(" "));
194 }
195 }
196 if(!wer && (cap & SND_SEQ_PORT_CAP_WRITE))
197 _writeEnable = true;
198 }
199
200 // subscribe for reading
201 if (_openFlags & 2)
202 {
203 if(cap & SND_SEQ_PORT_CAP_SUBS_READ)
204 {
205 snd_seq_port_subscribe_set_dest(subs, &musePort);
206 snd_seq_port_subscribe_set_sender(subs, &adr);
207 DEBUG_PRST_ROUTES(stderr, "MidiAlsaDevice::open Checking read subscription: address: %d:%d\n", adr.client, adr.port);
208 // Not already subscribed (or error)? Then try subscribing.
209 if(snd_seq_get_port_subscription(alsaSeq, subs) < 0)
210 {
211 //int error = snd_seq_subscribe_port(alsaSeq, subs);
212 rer = snd_seq_subscribe_port(alsaSeq, subs);
213 //if (error < 0)
214 if(rer < 0)
215 //return QString("Rec: ") + QString(snd_strerror(error));
216 estr += (QString("Rec: ") + QString(snd_strerror(rer)));
217 }
218 }
219 if(!rer && (cap & SND_SEQ_PORT_CAP_READ))
220 _readEnable = true;
221 }
222 }
223 else
224 {
225 _state = QString("Unavailable");
226 return _state;
227 }
228
229 if(wer < 0 || rer < 0)
230 {
231 _state = estr;
232 return _state;
233 }
234
235 _state = QString("OK");
236 return _state;
237 }
238
239 //---------------------------------------------------------
240 // close
241 //---------------------------------------------------------
242
close()243 void MidiAlsaDevice::close()
244 {
245 if(!alsaSeq)
246 {
247 _state = QString("Unavailable");
248 return;
249 }
250
251 snd_seq_port_info_t *pinfo;
252 snd_seq_port_subscribe_t* subs;
253 if(adr.client != SND_SEQ_ADDRESS_UNKNOWN && adr.port != SND_SEQ_ADDRESS_UNKNOWN)
254 {
255
256 snd_seq_port_info_alloca(&pinfo);
257 int rv = snd_seq_get_any_port_info(alsaSeq, adr.client, adr.port, pinfo);
258 if(rv < 0)
259 {
260 fprintf(stderr, "MidiAlsaDevice::close Error getting port info: adr: %d:%d: %s\n", adr.client, adr.port, snd_strerror(rv));
261 _state = QString("Error on close");
262 return;
263 }
264 // Allocated on stack, no need to call snd_seq_port_subscribe_free() later.
265 snd_seq_port_subscribe_alloca(&subs);
266 }
267
268 if(adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN)
269 {
270 _readEnable = false;
271 _writeEnable = false;
272 _state = QString("Unavailable");
273 }
274 else
275
276 {
277 int wer = 0;
278 int rer = 0;
279
280 int cap = snd_seq_port_info_get_capability(pinfo);
281
282 #ifdef ALSA_DEBUG
283 fprintf(stderr, "MidiAlsaDevice::close cap:%d\n", cap);
284 #endif
285
286 // This function appears to be called only by MidiPort::setMidiDevice(),
287 // which closes then opens the device.
288 // Because the open flags are set BEFORE setMidiDevice() is called, we must ignore the flags.
289 //
290 // NOTE: Tested: The read unsubscribe works ok but not the write.
291 // As viewed in say, qjackctl, the connection is clearly lost,
292 // but strangely the events are still accepted, ie, playback notes
293 // are still heard etc. Tried an alsa midi device AND external fluidsynth inst.
294 //
295 // Also, jack running and with jack midi disabled, we get messages like
296 // MidiAlsaDevice::0x84512c0 putEvent(): midi write error: No such device
297 // dst 16:0
298 // only sometimes (not when playing notes), but with jack midi turned on,
299 // we don't get the messages. With jack stopped we get the messages
300 // no matter if jack midi is turned on or not. Tim.
301
302 //if (_openFlags & 1) {
303 //if (!(_openFlags & 1))
304 {
305 if(cap & SND_SEQ_PORT_CAP_SUBS_WRITE)
306 {
307 snd_seq_port_subscribe_set_sender(subs, &musePort);
308 snd_seq_port_subscribe_set_dest(subs, &adr);
309
310 // Already subscribed? Then unsubscribe.
311 if(!snd_seq_get_port_subscription(alsaSeq, subs))
312 {
313 wer = snd_seq_unsubscribe_port(alsaSeq, subs);
314 //if(!wer)
315 // _writeEnable = false;
316 //else
317 if(wer < 0)
318 fprintf(stderr, "MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for writing: %s\n", adr.client, adr.port, snd_strerror(wer));
319 }
320 //else
321 //_writeEnable = false;
322 }
323 _writeEnable = false;
324 }
325
326 //if (_openFlags & 2) {
327 //if (!(_openFlags & 2))
328 {
329 if(cap & SND_SEQ_PORT_CAP_SUBS_READ)
330 {
331 snd_seq_port_subscribe_set_dest(subs, &musePort);
332 snd_seq_port_subscribe_set_sender(subs, &adr);
333
334 // Already subscribed? Then unsubscribe.
335 if(!snd_seq_get_port_subscription(alsaSeq, subs))
336 {
337 rer = snd_seq_unsubscribe_port(alsaSeq, subs);
338 //if(!rer)
339 // _readEnable = false;
340 //else
341 if(rer < 0)
342 fprintf(stderr, "MidiAlsaDevice::close Error unsubscribing alsa midi port %d:%d for reading: %s\n", adr.client, adr.port, snd_strerror(rer));
343 }
344 //else
345 // _readEnable = false;
346 }
347 _readEnable = false;
348 }
349 _state = QString("Closed");
350 }
351 }
352
353 //---------------------------------------------------------
354 // writeRouting
355 //---------------------------------------------------------
356
writeRouting(int level,Xml & xml) const357 void MidiAlsaDevice::writeRouting(int level, Xml& xml) const
358 {
359 // If this device is not actually in use by the song, do not write any routes.
360 // This prevents bogus routes from being saved and propagated in the med file. Tim.
361 if(midiPort() == -1)
362 return;
363
364 QString s;
365 for (ciRoute r = _outRoutes.begin(); r != _outRoutes.end(); ++r)
366 {
367 if((r->type == Route::TRACK_ROUTE && r->track) || (r->type != Route::TRACK_ROUTE && !r->name().isEmpty()))
368 {
369 s = "Route";
370 if(r->channel != -1)
371 s += QString(" channel=\"%1\"").arg(r->channel);
372 xml.tag(level++, s.toLatin1().constData());
373 xml.tag(level, "source devtype=\"%d\" name=\"%s\"/", MidiDevice::ALSA_MIDI, Xml::xmlString(name()).toLatin1().constData());
374 s = "dest";
375 if(r->type == Route::MIDI_DEVICE_ROUTE)
376 s += QString(" devtype=\"%1\" name=\"%2\"/").arg(r->device->deviceType()).arg(Xml::xmlString(r->name()));
377 else if(r->type == Route::TRACK_ROUTE)
378 s += QString(" track=\"%1\"").arg(MusEGlobal::song->tracks()->index(r->track));
379 else
380 s += QString(" type=\"%1\" name=\"%2\"/").arg(r->type).arg(Xml::xmlString(r->name()));
381 xml.tag(level, s.toLatin1().constData());
382 xml.etag(level--, "Route");
383 }
384 }
385 }
386
387 //---------------------------------------------------------
388 // putAlsaEvent
389 // return false if event is delivered
390 //---------------------------------------------------------
391
putAlsaEvent(snd_seq_event_t * event)392 bool MidiAlsaDevice::putAlsaEvent(snd_seq_event_t* event)
393 {
394 if (MusEGlobal::midiOutputTrace) {
395 fprintf(stderr, "ALSA MidiOut driver: <%s>: ", name().toLatin1().constData());
396 dump(event);
397 }
398
399 if(!_writeEnable || !alsaSeq || adr.client == SND_SEQ_ADDRESS_UNKNOWN || adr.port == SND_SEQ_ADDRESS_UNKNOWN)
400 return true;
401
402 int error;
403
404 #ifdef ALSA_DEBUG
405 fprintf(stderr, "MidiAlsaDevice::putAlsaEvent\n");
406 #endif
407
408 do {
409 error = snd_seq_event_output_direct(alsaSeq, event);
410 int len = snd_seq_event_length(event);
411 if (error == len) {
412 // printf(".");fflush(stdout);
413 return false;
414 }
415 if (error < 0) {
416 if (error == -12) {
417 return true;
418 }
419 else {
420 fprintf(stderr, "MidiAlsaDevice::%p putAlsaEvent(): midi write error: %s\n",
421 this, snd_strerror(error));
422 fprintf(stderr, " dst %d:%d\n", adr.client, adr.port);
423 //exit(-1);
424 }
425 }
426 else
427 fprintf(stderr, "MidiAlsaDevice::putAlsaEvent(): midi write returns %d, expected %d: %s\n",
428 error, len, snd_strerror(error));
429 } while (error == -12);
430 return true;
431 }
432
433 //---------------------------------------------------------
434 // processEvent
435 // return false if event is delivered
436 //---------------------------------------------------------
437
processEvent(const MidiPlayEvent & ev)438 bool MidiAlsaDevice::processEvent(const MidiPlayEvent& ev)
439 {
440 if (MusEGlobal::midiOutputTrace) {
441 fprintf(stderr, "ALSA MidiOut pre-driver: <%s>: ", name().toLatin1().constData());
442 dumpMPEvent(&ev);
443 }
444
445 int chn = ev.channel();
446 int a = ev.dataA();
447 int b = ev.dataB();
448
449 snd_seq_event_t event;
450 snd_seq_ev_clear(&event);
451
452 event.queue = SND_SEQ_QUEUE_DIRECT;
453 event.source = musePort;
454 event.dest = adr;
455
456 MidiInstrument::NoteOffMode nom = MidiInstrument::NoteOffAll; // Default to NoteOffAll in case of no port.
457 const int mport = midiPort();
458 if(mport != -1)
459 {
460 if(MidiInstrument* mi = MusEGlobal::midiPorts[mport].instrument())
461 nom = mi->noteOffMode();
462 }
463
464 switch(ev.type())
465 {
466 case ME_NOTEON:
467
468 if(b == 0)
469 {
470 // Handle zero-velocity note ons. Technically this is an error because internal midi paths
471 // are now all 'note-off' without zero-vel note ons - they're converted to note offs.
472 // Nothing should be setting a Note type Event's on velocity to zero.
473 // But just in case... If we get this warning, it means there is still code to change.
474 fprintf(stderr, "MidiAlsaDevice::processEvent: Warning: Zero-vel note on: time:%d type:%d (ME_NOTEON) ch:%d A:%d B:%d\n",
475 ev.time(), ev.type(), chn, a, b);
476 switch(nom)
477 {
478 // Instrument uses note offs. Convert to zero-vel note off.
479 case MidiInstrument::NoteOffAll:
480 if(MusEGlobal::midiOutputTrace)
481 fprintf(stderr, "MidiOut: Alsa: Following event will be converted to zero-velocity note off:\n");
482 snd_seq_ev_set_noteoff(&event, chn, a, 0);
483 break;
484
485 // Instrument uses no note offs at all. Send as-is.
486 case MidiInstrument::NoteOffNone:
487 // Instrument converts all note offs to zero-vel note ons. Send as-is.
488 case MidiInstrument::NoteOffConvertToZVNoteOn:
489 snd_seq_ev_set_noteon(&event, chn, a, b);
490 break;
491 }
492 }
493 else
494
495 snd_seq_ev_set_noteon(&event, chn, a, b);
496 break;
497 case ME_NOTEOFF:
498
499 switch(nom)
500 {
501 // Instrument uses note offs. Send as-is.
502 case MidiInstrument::NoteOffAll:
503 snd_seq_ev_set_noteoff(&event, chn, a, b);
504 break;
505
506 // Instrument uses no note offs at all. Send nothing. Eat up the event - return false.
507 case MidiInstrument::NoteOffNone:
508 return false;
509
510 // Instrument converts all note offs to zero-vel note ons. Convert to zero-vel note on.
511 case MidiInstrument::NoteOffConvertToZVNoteOn:
512 if(MusEGlobal::midiOutputTrace)
513 fprintf(stderr, "MidiOut: Alsa: Following event will be converted to zero-velocity note on:\n");
514 snd_seq_ev_set_noteon(&event, chn, a, 0);
515 break;
516 }
517 break;
518 case ME_PROGRAM:
519 {
520 _curOutParamNums[chn].resetParamNums(); // Probably best to reset.
521 _curOutParamNums[chn].setPROG(a);
522 snd_seq_ev_set_pgmchange(&event, chn, a);
523 }
524 break;
525 case ME_PITCHBEND:
526 snd_seq_ev_set_pitchbend(&event, chn, a);
527 break;
528 case ME_POLYAFTER:
529 snd_seq_ev_set_keypress(&event, chn, a, b);
530 break;
531 case ME_AFTERTOUCH:
532 snd_seq_ev_set_chanpress(&event, chn, a);
533 break;
534 case ME_SYSEX:
535 {
536 // Probably best to reset all.
537 resetCurOutParamNums();
538
539 // Stage the event data - is it OK to proceed?
540 const size_t len = sysExOutProcessor()->stageEvData(ev.eventData(), ev.time());
541 if(len > 0)
542 {
543 unsigned char buf[len];
544 if(sysExOutProcessor()->getCurChunk(buf, MusEGlobal::sampleRate))
545 {
546 snd_seq_ev_set_sysex(&event, len, buf);
547 // NOTE: Don't move this out, 'buf' would go out of scope.
548 return putAlsaEvent(&event);
549 }
550 }
551 return true;
552 }
553 break;
554 case ME_SONGPOS:
555 event.data.control.value = a;
556 event.type = SND_SEQ_EVENT_SONGPOS;
557 break;
558 case ME_CLOCK:
559 event.type = SND_SEQ_EVENT_CLOCK;
560 break;
561 case ME_START:
562 event.type = SND_SEQ_EVENT_START;
563 break;
564 case ME_CONTINUE:
565 event.type = SND_SEQ_EVENT_CONTINUE;
566 break;
567 case ME_STOP:
568 event.type = SND_SEQ_EVENT_STOP;
569 break;
570 case ME_CONTROLLER:
571 {
572 int a = ev.dataA();
573 int b = ev.dataB();
574 int chn = ev.channel();
575
576 if(a == CTRL_PITCH)
577 snd_seq_ev_set_pitchbend(&event, chn, b);
578 else if((a | 0xff) == CTRL_POLYAFTER)
579 snd_seq_ev_set_keypress(&event, chn, a & 0x7f, b & 0x7f);
580 else if(a == CTRL_AFTERTOUCH)
581 snd_seq_ev_set_chanpress(&event, chn, b);
582 else if(a == CTRL_PROGRAM) {
583 _curOutParamNums[chn].resetParamNums(); // Probably best to reset.
584 int hb = (b >> 16) & 0xff;
585 int lb = (b >> 8) & 0xff;
586 int pr = b & 0xff;
587 _curOutParamNums[chn].setCurrentProg(pr, lb, hb);
588 if(hb != 0xff)
589 {
590 snd_seq_ev_set_controller(&event, chn, CTRL_HBANK, hb);
591 if(putAlsaEvent(&event))
592 return true;
593 }
594 if(lb != 0xff)
595 {
596 snd_seq_ev_set_controller(&event, chn, CTRL_LBANK, lb);
597 if(putAlsaEvent(&event))
598 return true;
599 }
600 if(pr != 0xff)
601 {
602 snd_seq_ev_set_pgmchange(&event, chn, pr);
603 if(putAlsaEvent(&event))
604 return true;
605 }
606 return false;
607 }
608
609 // Set this to 1 if ALSA cannot handle RPN NRPN etc.
610 // NOTE: Although ideally it should be 0, there are problems with
611 // letting ALSA do the 'composition' of the messages in putMidiEvent() -
612 // chiefly that ALSA does not handle 7-bit (N)RPN controllers.
613 // This define is kept because it is important to understand, try, and see
614 // the difference between the two techniques, and possibly make it work...
615 // Also see the corresponding define in MidiAlsaDevice::putMidiEvent().
616 #if 0
617 snd_seq_ev_set_controller(&event, chn, a, b);
618 #else
619
620 else if (a < CTRL_14_OFFSET) { // 7 Bit Controller
621 if(a == CTRL_HRPN)
622 _curOutParamNums[chn].setRPNH(b);
623 else if(a == CTRL_LRPN)
624 _curOutParamNums[chn].setRPNL(b);
625 else if(a == CTRL_HNRPN)
626 _curOutParamNums[chn].setNRPNH(b);
627 else if(a == CTRL_LNRPN)
628 _curOutParamNums[chn].setNRPNL(b);
629 else if(a == CTRL_HBANK)
630 {
631 _curOutParamNums[chn].setBANKH(b);
632 _curOutParamNums[chn].resetParamNums(); // Probably best to reset.
633 }
634 else if(a == CTRL_LBANK)
635 {
636 _curOutParamNums[chn].setBANKL(b);
637 _curOutParamNums[chn].resetParamNums(); // Probably best to reset.
638 }
639 else if(a == CTRL_RESET_ALL_CTRL)
640 _curOutParamNums[chn].resetParamNums(); // Probably best to reset.
641
642 snd_seq_ev_set_controller(&event, chn, a, b);
643 }
644 else if (a < CTRL_RPN_OFFSET) { // 14 bit high resolution controller
645 int ctrlH = (a >> 8) & 0x7f;
646 int ctrlL = a & 0x7f;
647 #if 0
648 int dataH = (b >> 7) & 0x7f;
649 int dataL = b & 0x7f;
650 snd_seq_ev_set_controller(&event, chn, ctrlH, dataH);
651 if(putAlsaEvent(&event))
652 return true;
653 snd_seq_ev_set_controller(&event, chn, ctrlL, dataL);
654 return putAlsaEvent(&event);
655 #else
656 snd_seq_event_t ev;
657 snd_seq_ev_clear(&ev);
658 ev.queue = SND_SEQ_QUEUE_DIRECT;
659 ev.source = musePort;
660 ev.dest = adr;
661 int n = (ctrlH << 7) + ctrlL;
662 snd_seq_ev_set_controller(&ev, chn, n, b);
663 ev.type = SND_SEQ_EVENT_CONTROL14;
664 return putAlsaEvent(&ev);
665 #endif
666
667 }
668 else if (a < CTRL_NRPN_OFFSET) { // RPN 7-Bit Controller
669 int ctrlH = (a >> 8) & 0x7f;
670 int ctrlL = a & 0x7f;
671 int data = b & 0x7f;
672 if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers)
673 {
674 _curOutParamNums[chn].setRPNL(ctrlL);
675 snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, ctrlL);
676 if(putAlsaEvent(&event))
677 return true;
678 }
679 if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers)
680 {
681 _curOutParamNums[chn].setRPNH(ctrlH);
682 snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, ctrlH);
683 if(putAlsaEvent(&event))
684 return true;
685 }
686 if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers)
687 {
688 _curOutParamNums[chn].setDATAH(data);
689 snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, data);
690 if(putAlsaEvent(&event))
691 return true;
692 }
693
694 // Select null parameters so that subsequent data controller
695 // events do not upset the last *RPN controller. Tim.
696 if(MusEGlobal::config.midiSendNullParameters)
697 {
698 _curOutParamNums[chn].setRPNH(0x7f);
699 snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, 0x7f);
700 if(putAlsaEvent(&event))
701 return true;
702
703 _curOutParamNums[chn].setRPNL(0x7f);
704 snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, 0x7f);
705 if(putAlsaEvent(&event))
706 return true;
707 }
708 return false;
709 }
710 else if (a < CTRL_INTERNAL_OFFSET) { // NRPN 7-Bit Controller
711 int ctrlH = (a >> 8) & 0x7f;
712 int ctrlL = a & 0x7f;
713 int data = b & 0x7f;
714 if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers)
715 {
716 _curOutParamNums[chn].setNRPNL(ctrlL);
717 snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, ctrlL);
718 if(putAlsaEvent(&event))
719 return true;
720 }
721 if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers)
722 {
723 _curOutParamNums[chn].setNRPNH(ctrlH);
724 snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, ctrlH);
725 if(putAlsaEvent(&event))
726 return true;
727 }
728 if(data != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers)
729 {
730 _curOutParamNums[chn].setDATAH(data);
731 snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, data);
732 if(putAlsaEvent(&event))
733 return true;
734 }
735
736 if(MusEGlobal::config.midiSendNullParameters)
737 {
738 _curOutParamNums[chn].setNRPNH(0x7f);
739 snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, 0x7f);
740 if(putAlsaEvent(&event))
741 return true;
742
743 _curOutParamNums[chn].setNRPNL(0x7f);
744 snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, 0x7f);
745 if(putAlsaEvent(&event))
746 return true;
747 }
748 return false;
749 }
750 else if (a < CTRL_RPN14_OFFSET) // Unaccounted for internal controller
751 return true;
752 else if (a < CTRL_NRPN14_OFFSET) { // RPN14 Controller
753 int ctrlH = (a >> 8) & 0x7f;
754 int ctrlL = a & 0x7f;
755 int dataH = (b >> 7) & 0x7f;
756 int dataL = b & 0x7f;
757 if(ctrlL != _curOutParamNums[chn].RPNL || !MusEGlobal::config.midiOptimizeControllers)
758 {
759 _curOutParamNums[chn].setRPNL(ctrlL);
760 snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, ctrlL);
761 if(putAlsaEvent(&event))
762 return true;
763 }
764 if(ctrlH != _curOutParamNums[chn].RPNH || !MusEGlobal::config.midiOptimizeControllers)
765 {
766 _curOutParamNums[chn].setRPNH(ctrlH);
767 snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, ctrlH);
768 if(putAlsaEvent(&event))
769 return true;
770 }
771 if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers)
772 {
773 _curOutParamNums[chn].setDATAH(dataH);
774 snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, dataH);
775 if(putAlsaEvent(&event))
776 return true;
777 }
778 if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers)
779 {
780 _curOutParamNums[chn].setDATAL(dataL);
781 snd_seq_ev_set_controller(&event, chn, CTRL_LDATA, dataL);
782 if(putAlsaEvent(&event))
783 return true;
784 }
785
786 if(MusEGlobal::config.midiSendNullParameters)
787 {
788 _curOutParamNums[chn].setRPNH(0x7f);
789 snd_seq_ev_set_controller(&event, chn, CTRL_HRPN, 0x7f);
790 if(putAlsaEvent(&event))
791 return true;
792
793 _curOutParamNums[chn].setRPNL(0x7f);
794 snd_seq_ev_set_controller(&event, chn, CTRL_LRPN, 0x7f);
795 if(putAlsaEvent(&event))
796 return true;
797 }
798 return false;
799 }
800 else if (a < CTRL_NONE_OFFSET) { // NRPN14 Controller
801 int ctrlH = (a >> 8) & 0x7f;
802 int ctrlL = a & 0x7f;
803 #if 0
804 int dataH = (b >> 7) & 0x7f;
805 int dataL = b & 0x7f;
806 if(ctrlL != _curOutParamNums[chn].NRPNL || !MusEGlobal::config.midiOptimizeControllers)
807 {
808 _curOutParamNums[chn].setNRPNL(ctrlL);
809 snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, ctrlL);
810 if(putAlsaEvent(&event))
811 return true;
812 }
813 if(ctrlH != _curOutParamNums[chn].NRPNH || !MusEGlobal::config.midiOptimizeControllers)
814 {
815 _curOutParamNums[chn].setNRPNH(ctrlH);
816 snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, ctrlH);
817 if(putAlsaEvent(&event))
818 return true;
819 }
820 if(dataH != _curOutParamNums[chn].DATAH || !MusEGlobal::config.midiOptimizeControllers)
821 {
822 _curOutParamNums[chn].setDATAH(dataH);
823 snd_seq_ev_set_controller(&event, chn, CTRL_HDATA, dataH);
824 if(putAlsaEvent(&event))
825 return true;
826 }
827 if(dataL != _curOutParamNums[chn].DATAL || !MusEGlobal::config.midiOptimizeControllers)
828 {
829 _curOutParamNums[chn].setDATAL(dataL);
830 snd_seq_ev_set_controller(&event, chn, CTRL_LDATA, dataL);
831 if(putAlsaEvent(&event))
832 return true;
833 }
834
835 #else
836 int n = (ctrlH << 7) + ctrlL;
837 snd_seq_ev_set_controller(&event, chn, n, b);
838 event.type = SND_SEQ_EVENT_NONREGPARAM;
839 if(putAlsaEvent(&event))
840 return true;
841 #endif
842
843 if(MusEGlobal::config.midiSendNullParameters)
844 {
845 _curOutParamNums[chn].setNRPNH(0x7f);
846 snd_seq_ev_set_controller(&event, chn, CTRL_HNRPN, 0x7f);
847 if(putAlsaEvent(&event))
848 return true;
849
850 _curOutParamNums[chn].setNRPNL(0x7f);
851 snd_seq_ev_set_controller(&event, chn, CTRL_LNRPN, 0x7f);
852 if(putAlsaEvent(&event))
853 return true;
854 }
855 return false;
856 }
857 else {
858 fprintf(stderr, "MidiAlsaDevice::processEvent: unknown controller type 0x%x\n", a);
859 return true;
860 }
861 #endif
862 }
863 break; // ME_CONTROLLER
864
865 default:
866 if(MusEGlobal::debugMsg)
867 fprintf(stderr, "MidiAlsaDevice::processEvent(): event type %d not implemented\n", ev.type());
868 return true;
869 }
870
871 return putAlsaEvent(&event);
872 }
873
874 //---------------------------------------------------------
875 // processMidi
876 // Called from ALSA midi sequencer thread only.
877 //---------------------------------------------------------
878
processMidi(unsigned int curFrame)879 void MidiAlsaDevice::processMidi(unsigned int curFrame)
880 {
881 // Get the state of the stop flag.
882 const bool do_stop = stopFlag();
883
884 //--------------------------------------------------------------------------------
885 // For now we stop ALL ring buffer processing until any sysex transmission is finished.
886 // TODO FIXME Some realtime events ARE allowed while sending a sysex. Let that happen below...
887 //--------------------------------------------------------------------------------
888
889 SysExOutputProcessor* sop = sysExOutProcessor();
890 switch(sop->state())
891 {
892 case SysExOutputProcessor::Clear:
893 // Proceed with normal ring buffer processing, below...
894 break;
895
896 case SysExOutputProcessor::Sending:
897 {
898 // Current chunk is meant for a future cycle?
899 if(sop->curChunkFrame() > curFrame)
900 break;
901
902 const size_t len = sop->curChunkSize();
903 if(len > 0)
904 {
905 unsigned char buf[len];
906 if(sop->getCurChunk(buf, MusEGlobal::sampleRate))
907 {
908 snd_seq_event_t event;
909 snd_seq_ev_clear(&event);
910 event.queue = SND_SEQ_QUEUE_DIRECT;
911 event.source = musePort;
912 event.dest = adr;
913 snd_seq_ev_set_sysex(&event, len, buf);
914 putAlsaEvent(&event);
915 }
916 }
917 else
918 {
919 fprintf(stderr, "Error: MidiAlsaDevice::processMidi(): curChunkSize is zero while state is Sending\n");
920 // Should not happen. Protection against accidental zero chunk size while sending.
921 // Let's just clear the thing.
922 sop->clear();
923 }
924 }
925 break;
926
927 case SysExOutputProcessor::Finished:
928 {
929 // Wait for the last chunk to transmit.
930 if(sop->curChunkFrame() > curFrame)
931 break;
932 // Now we are truly done. Clear or reset the processor, which
933 // sets the state to Clear. Prefer reset for speed but clear is OK,
934 // the EvData reference will already have been released.
935 //sop->reset();
936 sop->clear();
937 }
938 break;
939 }
940
941 MidiPlayEvent buf_ev;
942
943 // Transfer the user lock-free buffer events to the user sorted multi-set.
944 // False = don't use the size snapshot, but update it.
945 const unsigned int usr_buf_sz = eventBuffers(UserBuffer)->getSize(false);
946 for(unsigned int i = 0; i < usr_buf_sz; ++i)
947 {
948 if(eventBuffers(UserBuffer)->get(buf_ev))
949 _outUserEvents.insert(buf_ev);
950 }
951
952 // Transfer the playback lock-free buffer events to the playback sorted multi-set.
953 const unsigned int pb_buf_sz = eventBuffers(PlaybackBuffer)->getSize(false);
954 for(unsigned int i = 0; i < pb_buf_sz; ++i)
955 {
956 // Are we stopping? Just remove the item.
957 if(do_stop)
958 eventBuffers(PlaybackBuffer)->remove();
959 // Otherwise get the item.
960 else if(eventBuffers(PlaybackBuffer)->get(buf_ev))
961 _outPlaybackEvents.insert(buf_ev);
962 }
963
964 // Are we stopping?
965 if(do_stop)
966 {
967 // Transport has stopped, purge ALL further scheduled playback events now.
968 _outPlaybackEvents.clear();
969 // Reset the flag.
970 setStopFlag(false);
971 }
972
973 iMPEvent impe_pb = _outPlaybackEvents.begin();
974 iMPEvent impe_us = _outUserEvents.begin();
975 bool using_pb;
976
977 while(1)
978 {
979 if(impe_pb != _outPlaybackEvents.end() && impe_us != _outUserEvents.end())
980 using_pb = *impe_pb < *impe_us;
981 else if(impe_pb != _outPlaybackEvents.end())
982 using_pb = true;
983 else if(impe_us != _outUserEvents.end())
984 using_pb = false;
985 else break;
986
987 const MidiPlayEvent& e = using_pb ? *impe_pb : *impe_us;
988
989 #ifdef ALSA_DEBUG
990 fprintf(stderr, "INFO: MidiAlsaDevice::processMidi() evTime:%u curFrame:%u\n", e.time(), curFrame);
991 #endif
992
993 // Event is meant for next cycle?
994 if(e.time() > curFrame)
995 {
996 #ifdef ALSA_DEBUG
997 fprintf(stderr, " alsa play event is for future:%lu, breaking loop now\n", e.time());
998 #endif
999 break;
1000 }
1001
1002 // Is the sysex processor not in a Clear state?
1003 // Only realtime events are allowed to be sent while a sysex is in progress.
1004 // The delayed events list is pre-allocated with reserve().
1005 // According to http://en.cppreference.com/w/cpp/container/vector/clear,
1006 // clear() shall not change the capacity. That statement references here:
1007 // https://stackoverflow.com/questions/18467624/what-does-the-standard-say-
1008 // about-how-calling-clear-on-a-vector-changes-the-capac/18467916#18467916
1009 // But http://www.cplusplus.com/reference/vector/vector/clear
1010 // has not been updated to clarify this situation.
1011 if(sop->state() != SysExOutputProcessor::Clear)
1012 {
1013 // Is it a realtime message?
1014 if(e.type() >= 0xf8 && e.type() <= 0xff)
1015 // Process it now.
1016 processEvent(e);
1017 else
1018 // Store it for later.
1019 _sysExOutDelayedEvents->push_back(e);
1020 }
1021 else
1022 {
1023 // Process any delayed events.
1024 const unsigned int sz = _sysExOutDelayedEvents->size();
1025 for(unsigned int i = 0; i < sz; ++i)
1026 processEvent(_sysExOutDelayedEvents->at(i));
1027
1028 // Let's check that capacity out of curiosity...
1029 const unsigned int cap = _sysExOutDelayedEvents->capacity();
1030 // Done with the delayed event list. Clear it.
1031 _sysExOutDelayedEvents->clear();
1032
1033 // Throw up a developer warning if things are fishy.
1034 if(_sysExOutDelayedEvents->capacity() != cap)
1035 fprintf(stderr, "WARNING: MidiAlsaDevice::processMidi() delayed events vector "
1036 "capacity:%u is not the same as before clear:%u\n",
1037 (unsigned int)_sysExOutDelayedEvents->capacity(), cap);
1038
1039 // If processEvent fails, although we would like to not miss events by keeping them
1040 // until next cycle and trying again, that can lead to a large backup of events
1041 // over a long time. So we'll just... miss them.
1042 processEvent(e);
1043 }
1044
1045 // Successfully processed event. Remove it from FIFO.
1046 // C++11.
1047 if(using_pb)
1048 impe_pb = _outPlaybackEvents.erase(impe_pb);
1049 else
1050 impe_us = _outUserEvents.erase(impe_us);
1051 }
1052 }
1053
1054 //---------------------------------------------------------
1055 // initMidiAlsa
1056 // return true on error
1057 //---------------------------------------------------------
1058
initMidiAlsa()1059 bool initMidiAlsa()
1060 {
1061 // The sequencer is required for ALSA midi. Initialize it if not already done.
1062 // But do not start the sequencer thread yet... the caller may want to do that a bit later.
1063 MusECore::initMidiSequencer();
1064
1065 if(alsaSeq)
1066 {
1067 DEBUG_PRST_ROUTES(stderr, "initMidiAlsa: alsaSeq already initialized, ignoring\n");
1068 return false;
1069 }
1070
1071 muse_atomic_init(&atomicAlsaMidiScanPending);
1072 muse_atomic_set(&atomicAlsaMidiScanPending, 0);
1073
1074 if (MusEGlobal::debugMsg)
1075 fprintf(stderr, "initMidiAlsa\n");
1076 int error = snd_seq_open(&alsaSeq, "hw", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
1077 if (error < 0) {
1078 fprintf(stderr, "Could not open ALSA sequencer: %s\n",
1079 snd_strerror(error));
1080 return true;
1081 }
1082
1083 const int inCap = SND_SEQ_PORT_CAP_SUBS_READ;
1084 const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE;
1085
1086 snd_seq_client_info_t *cinfo;
1087 snd_seq_client_info_alloca(&cinfo);
1088 snd_seq_client_info_set_client(cinfo, -1);
1089
1090 while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) {
1091 const char* cname = snd_seq_client_info_get_name(cinfo);
1092 //fprintf(stderr, "ALSA client name: %s\n", cname);
1093
1094 // Put Midi Through and user clients after others. Insert other unwanted clients here: // p4.0.41
1095 if(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT || strcmp("Midi Through", cname) == 0)
1096 continue;
1097
1098 snd_seq_port_info_t *pinfo;
1099 snd_seq_port_info_alloca(&pinfo);
1100 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1101 snd_seq_port_info_set_port(pinfo, -1);
1102
1103 while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) {
1104 unsigned int capability = snd_seq_port_info_get_capability(pinfo);
1105 if (capability & SND_SEQ_PORT_CAP_NO_EXPORT) // Ignore ports like "qjackctl" or "port". p4.0.41
1106 continue;
1107 if ((capability & outCap) == 0) {
1108 const char *name = snd_seq_port_info_get_name(pinfo);
1109 if (strcmp("Timer", name) == 0 ||
1110 strcmp("Announce", name) == 0 ||
1111 strcmp("Receiver", name) == 0)
1112 continue;
1113 }
1114 snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo);
1115
1116 const QString dev_name(snd_seq_port_info_get_name(pinfo));
1117 MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI);
1118 const bool dev_found = dev;
1119 if(dev_found)
1120 {
1121 DEBUG_PRST_ROUTES(stderr, "initMidiAlsa device found:%p %s\n", dev, snd_seq_port_info_get_name(pinfo));
1122 // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device?
1123 dev->setAddressClient(adr.client);
1124 dev->setAddressPort(adr.port);
1125 // The state should be 'Unavailable', change it to 'Closed' (or 'Open' below).
1126 //if(dev->midiPort() == -1)
1127 dev->setState("Closed");
1128 }
1129 else
1130 dev = new MidiAlsaDevice(adr, QString(dev_name));
1131 //MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo)));
1132 int flags = 0;
1133 if (capability & outCap)
1134 flags |= 1;
1135 if (capability & inCap)
1136 flags |= 2;
1137 dev->setrwFlags(flags);
1138 if (MusEGlobal::debugMsg)
1139 fprintf(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n",
1140 snd_seq_port_info_get_name(pinfo),
1141 adr.client, adr.port,
1142 flags, capability);
1143 DEBUG_PRST_ROUTES(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n",
1144 snd_seq_port_info_get_name(pinfo),
1145 adr.client, adr.port,
1146 flags, capability);
1147 if(dev_found)
1148 {
1149 // // The device should be closed right now. Open it if necessary,
1150 // // which will also change the state to 'Open'.
1151 // if(dev->midiPort() != -1)
1152 // dev->open();
1153 }
1154 else
1155 MusEGlobal::midiDevices.add(dev);
1156 }
1157 }
1158
1159 snd_seq_client_info_set_client(cinfo, -1); // Reset
1160 while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) {
1161 const char* cname = snd_seq_client_info_get_name(cinfo);
1162 //fprintf(stderr, "ALSA client name: %s\n", cname);
1163
1164 bool is_thru = (strcmp("Midi Through", cname) == 0);
1165 // Put Midi Through and user clients after others. Insert other unwanted clients here: // p4.0.41
1166 if( !(snd_seq_client_info_get_type(cinfo) == SND_SEQ_USER_CLIENT || is_thru) )
1167 continue;
1168
1169 snd_seq_port_info_t *pinfo;
1170 snd_seq_port_info_alloca(&pinfo);
1171 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1172 snd_seq_port_info_set_port(pinfo, -1);
1173
1174 while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) {
1175 unsigned int capability = snd_seq_port_info_get_capability(pinfo);
1176 if (capability & SND_SEQ_PORT_CAP_NO_EXPORT) // Ignore ports like "qjackctl" or "port". p4.0.41
1177 continue;
1178 if ((capability & outCap) == 0) {
1179 const char *name = snd_seq_port_info_get_name(pinfo);
1180 if (strcmp("Timer", name) == 0 ||
1181 strcmp("Announce", name) == 0 ||
1182 strcmp("Receiver", name) == 0)
1183 continue;
1184 }
1185 snd_seq_addr_t adr = *snd_seq_port_info_get_addr(pinfo);
1186 const QString dev_name(snd_seq_port_info_get_name(pinfo));
1187 MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI);
1188 const bool dev_found = dev;
1189 if(dev_found)
1190 {
1191 DEBUG_PRST_ROUTES(stderr, "initMidiAlsa device found:%p %s\n", dev, snd_seq_port_info_get_name(pinfo));
1192 // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device?
1193 dev->setAddressClient(adr.client);
1194 dev->setAddressPort(adr.port);
1195 // The state should be 'Unavailable', change it to 'Closed' (or 'Open' below).
1196 //if(dev->midiPort() == -1)
1197 dev->setState("Closed");
1198 }
1199 else
1200 dev = new MidiAlsaDevice(adr, dev_name);
1201 //MidiAlsaDevice* dev = new MidiAlsaDevice(adr, QString(snd_seq_port_info_get_name(pinfo)));
1202 int flags = 0;
1203 if (capability & outCap)
1204 flags |= 1;
1205 if (capability & inCap)
1206 flags |= 2;
1207 dev->setrwFlags(flags);
1208 if(is_thru) // Don't auto-open Midi Through.
1209 dev->setOpenFlags(0);
1210 if (MusEGlobal::debugMsg)
1211 fprintf(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n",
1212 snd_seq_port_info_get_name(pinfo),
1213 adr.client, adr.port,
1214 flags, capability);
1215 DEBUG_PRST_ROUTES(stderr, "ALSA port add: <%s>, %d:%d flags %d 0x%0x\n",
1216 snd_seq_port_info_get_name(pinfo),
1217 adr.client, adr.port,
1218 flags, capability);
1219 if(dev_found)
1220 {
1221 // // The device should be closed right now. Open it if necessary,
1222 // // which will also change the state to 'Open'.
1223 // if(dev->midiPort() != -1)
1224 // dev->open();
1225 }
1226 else
1227 MusEGlobal::midiDevices.add(dev);
1228 }
1229 }
1230
1231
1232 //snd_seq_set_client_name(alsaSeq, "MusE Sequencer");
1233 error = snd_seq_set_client_name(alsaSeq, MusEGlobal::audioDevice->clientName());
1234 if (error < 0) {
1235 fprintf(stderr, "Alsa: Set client name failed: %s", snd_strerror(error));
1236 return true;
1237 }
1238
1239 int ci = snd_seq_poll_descriptors_count(alsaSeq, POLLIN);
1240 int co = snd_seq_poll_descriptors_count(alsaSeq, POLLOUT);
1241
1242 if (ci > 1 || co > 1) {
1243 fprintf(stderr, "ALSA midi: cannot handle more than one poll fd\n");
1244 abort();
1245 }
1246
1247 struct pollfd pfdi[ci];
1248 struct pollfd pfdo[co];
1249 snd_seq_poll_descriptors(alsaSeq, pfdi, ci, POLLIN);
1250 snd_seq_poll_descriptors(alsaSeq, pfdo, co, POLLOUT);
1251 alsaSeqFdo = pfdo[0].fd;
1252 alsaSeqFdi = pfdi[0].fd;
1253
1254 int port = snd_seq_create_simple_port(alsaSeq, "MusE Port 0",
1255 inCap | outCap | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE,
1256 SND_SEQ_PORT_TYPE_APPLICATION);
1257 if (port < 0) {
1258 perror("create port");
1259 exit(1);
1260 }
1261 musePort.port = port;
1262 musePort.client = snd_seq_client_id(alsaSeq);
1263
1264 //-----------------------------------------
1265 // subscribe to "Announce"
1266 // this enables callbacks for any
1267 // alsa port changes
1268 //-----------------------------------------
1269
1270 //snd_seq_addr_t aadr;
1271 announce_adr.client = SND_SEQ_CLIENT_SYSTEM;
1272 announce_adr.port = SND_SEQ_PORT_SYSTEM_ANNOUNCE;
1273
1274 snd_seq_port_subscribe_t* subs;
1275 snd_seq_port_subscribe_alloca(&subs);
1276 snd_seq_port_subscribe_set_dest(subs, &musePort);
1277 snd_seq_port_subscribe_set_sender(subs, &announce_adr);
1278 error = snd_seq_subscribe_port(alsaSeq, subs);
1279 if (error < 0) {
1280 fprintf(stderr, "Alsa: Subscribe System failed: %s", snd_strerror(error));
1281 return true;
1282 }
1283
1284
1285 // The ALSA devices should be closed right now. Open them if necessary,
1286 // which will also change their states to 'Open'.
1287 for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i)
1288 {
1289 MidiDevice* d = *i;
1290 switch(d->deviceType())
1291 {
1292 case MidiDevice::ALSA_MIDI:
1293 if(d->midiPort() != -1)
1294 d->open();
1295 break;
1296
1297 case MidiDevice::JACK_MIDI:
1298 case MidiDevice::SYNTH_MIDI:
1299 break;
1300 }
1301 }
1302
1303 return false;
1304 }
1305
1306
1307 //---------------------------------------------------------
1308 // exitMidiAlsa
1309 //---------------------------------------------------------
1310
exitMidiAlsa()1311 void exitMidiAlsa()
1312 {
1313 if(alsaSeq)
1314 {
1315 int error = 0;
1316 snd_seq_port_subscribe_t* subs;
1317 // Allocated on stack, no need to call snd_seq_port_subscribe_free() later.
1318 snd_seq_port_subscribe_alloca(&subs);
1319
1320 //snd_seq_port_info_t *pinfo;
1321 //snd_seq_port_info_alloca(&pinfo);
1322 //snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1323 //snd_seq_port_info_set_addr(pinfo, &announce_adr);
1324
1325 snd_seq_port_subscribe_set_dest(subs, &musePort);
1326 snd_seq_port_subscribe_set_sender(subs, &announce_adr);
1327
1328 // Already subscribed? Then unsubscribe.
1329 if(!snd_seq_get_port_subscription(alsaSeq, subs))
1330 {
1331 error = snd_seq_unsubscribe_port(alsaSeq, subs);
1332 if(error < 0)
1333 fprintf(stderr, "MusE: exitMidiAlsa: Error unsubscribing alsa midi Announce port %d:%d for reading: %s\n", announce_adr.client, announce_adr.port, snd_strerror(error));
1334 }
1335
1336 error = snd_seq_delete_simple_port(alsaSeq, musePort.port);
1337 if(error < 0)
1338 fprintf(stderr, "MusE: Could not delete ALSA simple port: %s\n", snd_strerror(error));
1339
1340 error = snd_seq_close(alsaSeq);
1341 if(error < 0)
1342 fprintf(stderr, "MusE: Could not close ALSA sequencer: %s\n", snd_strerror(error));
1343
1344 muse_atomic_destroy(&atomicAlsaMidiScanPending);
1345 }
1346 else
1347 fprintf(stderr, "initMidiAlsa: alsaSeq already exited, ignoring\n");
1348
1349 alsaSeq = 0;
1350 // Be sure to call MusEGlobal::midiSeq->msgUpdatePollFd() or midiSeq->updatePollFd()
1351 // for this to take effect.
1352 alsaSeqFdi = -1;
1353 alsaSeqFdo = -1;
1354 }
1355
1356
1357 //---------------------------------------------------------
1358 // setAlsaClientName
1359 //---------------------------------------------------------
1360
setAlsaClientName(const char * name)1361 void setAlsaClientName(const char* name)
1362 {
1363 #ifdef ALSA_DEBUG
1364 fprintf(stderr, "setAlsaClientName: %s seq:%p\n", name, alsaSeq);
1365 #endif
1366
1367 if(!alsaSeq)
1368 return;
1369
1370 int error = snd_seq_set_client_name(alsaSeq, name);
1371 if (error < 0)
1372 fprintf(stderr, "setAlsaClientName: failed: %s", snd_strerror(error));
1373 }
1374
1375 struct AlsaPort {
1376 snd_seq_addr_t adr;
1377 char* name;
1378 int flags;
AlsaPortMusECore::AlsaPort1379 AlsaPort(snd_seq_addr_t a, const char* s, int f) {
1380 adr = a;
1381 name = strdup(s);
1382 flags = f;
1383 }
1384 //~AlsaPort() { if(name) free(name); }
1385 };
1386
1387 static std::list<AlsaPort> portList;
1388
1389 //---------------------------------------------------------
1390 // alsaScanMidiPorts
1391 //---------------------------------------------------------
1392
alsaScanMidiPorts()1393 void alsaScanMidiPorts()
1394 {
1395 #ifdef ALSA_DEBUG
1396 fprintf(stderr, "alsa scan midi ports\n");
1397 #endif
1398 DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts\n");
1399
1400 bool idling = false;
1401 portList.clear();
1402
1403 if(!alsaSeq)
1404 {
1405 // Reset this now.
1406 muse_atomic_set(&atomicAlsaMidiScanPending, 0);
1407
1408 // Check for devices to disable
1409 for(iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i)
1410 {
1411 MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i);
1412 if(d == 0)
1413 continue;
1414
1415 DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts stopped: disabling device:%p %s\n",
1416 d, d->name().toLatin1().constData());
1417 if(!idling)
1418 {
1419 // Not much choice but to idle both the audio and midi threads since
1420 // midi does not idle while audio messages are being processed.
1421 MusEGlobal::audio->msgIdle(true);
1422 idling = true;
1423 }
1424
1425 //operations.add(PendingOperationItem(d, SND_SEQ_ADDRESS_UNKNOWN, SND_SEQ_ADDRESS_UNKNOWN, PendingOperationItem::ModifyMidiDeviceAddress));
1426 d->adr.client = SND_SEQ_ADDRESS_UNKNOWN;
1427 d->adr.port = SND_SEQ_ADDRESS_UNKNOWN;
1428 // Close to reset some device members.
1429 d->close();
1430 // Update the port's state
1431 d->setState("Unavailable");
1432 if(d->midiPort() != -1)
1433 MusEGlobal::midiPorts[d->midiPort()].setState(d->state());
1434 }
1435
1436 if(idling)
1437 {
1438 MusEGlobal::audio->msgIdle(false);
1439 // Update the GUI.
1440 MusEGlobal::song->update(SC_CONFIG);
1441 }
1442 return;
1443 }
1444
1445 QString state;
1446 const int inCap = SND_SEQ_PORT_CAP_SUBS_READ;
1447 const int outCap = SND_SEQ_PORT_CAP_SUBS_WRITE;
1448
1449 snd_seq_client_info_t* cinfo;
1450 snd_seq_client_info_alloca(&cinfo);
1451 snd_seq_client_info_set_client(cinfo, 0);
1452
1453 while (snd_seq_query_next_client(alsaSeq, cinfo) >= 0) {
1454 snd_seq_port_info_t *pinfo;
1455 snd_seq_port_info_alloca(&pinfo);
1456 snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
1457 snd_seq_port_info_set_port(pinfo, -1);
1458 while (snd_seq_query_next_port(alsaSeq, pinfo) >= 0) {
1459 unsigned int capability = snd_seq_port_info_get_capability(pinfo);
1460 if (capability & SND_SEQ_PORT_CAP_NO_EXPORT) // Ignore ports like "qjackctl" or "port". p4.0.41
1461 continue;
1462 if (((capability & outCap) == 0)
1463 && ((capability & inCap) == 0))
1464 continue;
1465 snd_seq_addr_t adr;
1466 const char* name;
1467 adr = *snd_seq_port_info_get_addr(pinfo);
1468 name = snd_seq_port_info_get_name(pinfo);
1469 if (adr.client == musePort.client && adr.port == musePort.port)
1470 continue;
1471 int flags = 0;
1472 if (capability & outCap)
1473 flags |= 1;
1474 if (capability & inCap)
1475 flags |= 2;
1476 // fprintf(stderr, "ALSA port add: <%s>, flags %d\n", name, flags);
1477 portList.push_back(AlsaPort(adr, name, flags));
1478 }
1479 }
1480
1481 // Reset this now.
1482 muse_atomic_set(&atomicAlsaMidiScanPending, 0);
1483
1484 //
1485 // check for devices to delete
1486 //
1487 for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) {
1488 MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i);
1489 if (d == 0)
1490 continue;
1491
1492 std::list<AlsaPort>::iterator k = portList.begin();
1493 for (; k != portList.end(); ++k) {
1494 if (k->adr.client == d->adr.client
1495 && k->adr.port == d->adr.port) {
1496 break;
1497 }
1498 // Search by name if either of the client or port are 0.
1499 if(strcmp(k->name, d->name().toLatin1().constData()) == 0 &&
1500 ((d->adr.client == SND_SEQ_ADDRESS_UNKNOWN && d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) ||
1501 (d->adr.client == SND_SEQ_ADDRESS_UNKNOWN && d->adr.port == k->adr.port) ||
1502 (d->adr.port == SND_SEQ_ADDRESS_UNKNOWN && d->adr.client == k->adr.client)))
1503 break;
1504 }
1505 if (k == portList.end()) {
1506 DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts nulling adr op:ModifyMidiDeviceAddress device:%p %s\n",
1507 d, d->name().toLatin1().constData());
1508 if(!idling)
1509 {
1510 // Not much choice but to idle both the audio and midi threads since
1511 // midi does not idle while audio messages are being processed.
1512 MusEGlobal::audio->msgIdle(true);
1513 idling = true;
1514 }
1515
1516 //operations.add(PendingOperationItem(d, SND_SEQ_ADDRESS_UNKNOWN, SND_SEQ_ADDRESS_UNKNOWN, PendingOperationItem::ModifyMidiDeviceAddress));
1517 d->adr.client = SND_SEQ_ADDRESS_UNKNOWN;
1518 d->adr.port = SND_SEQ_ADDRESS_UNKNOWN;
1519 // Close to reset some device members.
1520 d->close();
1521 // Update the port's state
1522 d->setState("Unavailable");
1523 if(d->midiPort() != -1)
1524 MusEGlobal::midiPorts[d->midiPort()].setState(d->state());
1525 }
1526 }
1527
1528
1529 //
1530 // check for devices to add
1531 //
1532 // TODO: Possibly auto-add them to available midi ports.
1533 //
1534 for (std::list<AlsaPort>::iterator k = portList.begin(); k != portList.end(); ++k) {
1535 iMidiDevice i = MusEGlobal::midiDevices.begin();
1536 for (;i != MusEGlobal::midiDevices.end(); ++i) {
1537 MidiAlsaDevice* d = dynamic_cast<MidiAlsaDevice*>(*i);
1538 if (d == 0)
1539 continue;
1540 DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts add: checking port:%s client:%d port:%d device:%p %s client:%d port:%d\n",
1541 k->name, k->adr.client, k->adr.port, d, d->name().toLatin1().constData(), d->adr.client, d->adr.port);
1542 if (k->adr.client == d->adr.client && k->adr.port == d->adr.port)
1543 break;
1544
1545 if((d->adr.client == SND_SEQ_ADDRESS_UNKNOWN || d->adr.port == SND_SEQ_ADDRESS_UNKNOWN) && strcmp(k->name, d->name().toLatin1().constData()) == 0)
1546 {
1547 if(d->adr.client != SND_SEQ_ADDRESS_UNKNOWN && d->adr.client != k->adr.client)
1548 {
1549 DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts: k->name:%s d->adr.client:%u != k->adr.client:%u", k->name, d->adr.client, k->adr.client);
1550 //continue;
1551 }
1552 if(d->adr.port != SND_SEQ_ADDRESS_UNKNOWN && d->adr.port != k->adr.port)
1553 {
1554 DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts: k->name:%s d->adr.port:%u != k->adr.port:%u", k->name, d->adr.port, k->adr.port);
1555 //continue;
1556 }
1557 //if(d->adr.client == SND_SEQ_ADDRESS_UNKNOWN)
1558 // d->adr.client = k->adr.client;
1559 //if(d->adr.port == SND_SEQ_ADDRESS_UNKNOWN)
1560 // d->adr.port = k->adr.port;
1561 DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts modifying adr op:ModifyMidiDeviceAddress device:%p %s client:%d port:%d\n",
1562 d, d->name().toLatin1().constData(), k->adr.client, k->adr.port);
1563
1564 if(!idling)
1565 {
1566 MusEGlobal::audio->msgIdle(true);
1567 idling = true;
1568 }
1569
1570 //operations.add(PendingOperationItem(d, k->adr.client, k->adr.port, PendingOperationItem::ModifyMidiDeviceAddress));
1571 // FIXME: Re-subscribe to any ports for now, need a time delay to implement unsubscribe-exit events
1572 //operations.add(PendingOperationItem(d, k->flags, k->flags, PendingOperationItem::ModifyMidiDeviceFlags));
1573 d->setAddressClient(k->adr.client);
1574 d->setAddressPort(k->adr.port);
1575 d->setrwFlags(k->flags);
1576 // FIXME: Re-subscribe to any ports for now, need a time delay to implement unsubscribe-exit events
1577 d->setOpenFlags(k->flags);
1578 if(d->midiPort() < 0)
1579 // Keep the device closed and update the state.
1580 d->setState("Closed");
1581 else
1582 // Re-subscribe, open and update the port's state.
1583 MusEGlobal::midiPorts[d->midiPort()].setState(d->open());
1584 break;
1585 }
1586
1587 }
1588 if (i == MusEGlobal::midiDevices.end()) {
1589
1590 if(!idling)
1591 {
1592 MusEGlobal::audio->msgIdle(true);
1593 idling = true;
1594 }
1595
1596 // add device
1597
1598 const QString dev_name(k->name);
1599 MidiDevice* dev = MusEGlobal::midiDevices.find(dev_name, MidiDevice::ALSA_MIDI);
1600 const bool dev_found = dev;
1601 if(dev_found)
1602 {
1603 // TODO: Hm, something more than this? Maybe ultimately will have to destroy/recreate the device?
1604 dev->setAddressClient(k->adr.client);
1605 dev->setAddressPort(k->adr.port);
1606 }
1607 else
1608 dev = new MidiAlsaDevice(k->adr, dev_name);
1609 //MidiAlsaDevice* dev = new MidiAlsaDevice(k->adr, QString(k->name));
1610 dev->setrwFlags(k->flags);
1611 DEBUG_PRST_ROUTES(stderr, "alsaScanMidiPorts op:AddMidiDevice adding:%p %s adr.client:%d adr.port:%d\n",
1612 dev, dev->name().toLatin1().constData(), k->adr.client, k->adr.port);
1613
1614 //operations.add(PendingOperationItem(MusEGlobal::midiDevices, dev, PendingOperationItem::AddMidiDevice));
1615 //MusEGlobal::midiDevices.addOperation(dev, operations);
1616 if(!dev_found)
1617 MusEGlobal::midiDevices.add(dev);
1618 // Subscribe
1619 //dev->open();
1620 }
1621 }
1622
1623 //if(!operations.empty())
1624 //{
1625 // if(!idling)
1626 // MusEGlobal::audio->msgIdle(true);
1627 // //MusEGlobal::audio->msgExecutePendingOperations(operations, true);
1628 // // Execute directly both stages
1629 // operations.executeNonRTStage();
1630 // operations.executeRTStage();
1631 if(idling)
1632 {
1633 MusEGlobal::audio->msgIdle(false);
1634 // Update the GUI.
1635 MusEGlobal::song->update(SC_CONFIG);
1636 }
1637 //}
1638 }
1639
1640 //---------------------------------------------------------
1641 // alsaSelectRfd
1642 //---------------------------------------------------------
1643
alsaSelectRfd()1644 int alsaSelectRfd()
1645 {
1646 return alsaSeqFdi;
1647 }
1648
1649 //---------------------------------------------------------
1650 // alsaSelectWfd
1651 //---------------------------------------------------------
1652
alsaSelectWfd()1653 int alsaSelectWfd()
1654 {
1655 return alsaSeqFdo;
1656 }
1657
1658 //---------------------------------------------------------
1659 // processInput
1660 //---------------------------------------------------------
1661
alsaProcessMidiInput()1662 void alsaProcessMidiInput()
1663 {
1664 unsigned frame_ts = MusEGlobal::audio->curFrame();
1665
1666 DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput()\n");
1667
1668 if(!alsaSeq)
1669 return;
1670
1671 MidiRecordEvent event;
1672 snd_seq_event_t* ev;
1673
1674 for (;;)
1675 {
1676 int rv = snd_seq_event_input(alsaSeq, &ev);
1677 // fprintf(stderr, "AlsaInput %d\n", rv);
1678 if (rv < 0) {
1679 // fprintf(stderr, "AlsaMidi: read error %s\n", snd_strerror(rv));
1680 return;
1681 }
1682
1683 if (MusEGlobal::midiInputTrace) {
1684 switch(ev->type)
1685 {
1686 // Ignore some flooding events like clock.
1687 case SND_SEQ_EVENT_CLOCK:
1688 break;
1689
1690 default:
1691 fprintf(stderr, "ALSA MidiIn driver: ");
1692 MidiAlsaDevice::dump(ev);
1693 break;
1694 }
1695 }
1696
1697 switch(ev->type) {
1698 case SND_SEQ_EVENT_PORT_SUBSCRIBED:
1699 DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_SUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n",
1700 ev->data.connect.sender.client, ev->data.connect.sender.port,
1701 ev->data.connect.dest.client, ev->data.connect.dest.port);
1702 // MusEGlobal::audio->midiPortsChanged(); // signal gui
1703 // snd_seq_free_event(ev);
1704 // return;
1705 if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1706 {
1707 muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1708 MusEGlobal::audio->sendMsgToGui('P');
1709 }
1710 snd_seq_free_event(ev);
1711 if(rv == 0)
1712 return;
1713 continue;
1714
1715 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
1716 DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_UNSUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n",
1717 ev->data.connect.sender.client, ev->data.connect.sender.port,
1718 ev->data.connect.dest.client, ev->data.connect.dest.port);
1719 // MusEGlobal::audio->midiPortsChanged(); // signal gui
1720 // snd_seq_free_event(ev);
1721 // return;
1722 if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1723 {
1724 muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1725 MusEGlobal::audio->sendMsgToGui('P');
1726 }
1727 snd_seq_free_event(ev);
1728 if(rv == 0)
1729 return;
1730 continue;
1731
1732 case SND_SEQ_EVENT_CLIENT_START:
1733 DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_CLIENT_START adr: %d:%d\n",
1734 ev->data.addr.client, ev->data.addr.port);
1735
1736 //alsaScanMidiPorts();
1737 // MusEGlobal::audio->midiPortsChanged(); // signal gui
1738 // snd_seq_free_event(ev);
1739 // return;
1740 if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1741 {
1742 muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1743 MusEGlobal::audio->sendMsgToGui('P');
1744 }
1745 snd_seq_free_event(ev);
1746 if(rv == 0)
1747 return;
1748 continue;
1749
1750 case SND_SEQ_EVENT_CLIENT_EXIT:
1751 DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_CLIENT_EXIT adr: %d:%d\n",
1752 ev->data.addr.client, ev->data.addr.port);
1753 //snd_seq_free_event(ev);
1754 // return;
1755 // on first start of a software synthesizer we only
1756 // get CLIENT_START event and no PORT_START, why?
1757
1758 //alsaScanMidiPorts();
1759 // MusEGlobal::audio->midiPortsChanged(); // signal gui
1760 // snd_seq_free_event(ev);
1761 // return;
1762 if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1763 {
1764 muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1765 MusEGlobal::audio->sendMsgToGui('P');
1766 }
1767 snd_seq_free_event(ev);
1768 if(rv == 0)
1769 return;
1770 continue;
1771
1772 case SND_SEQ_EVENT_PORT_START:
1773 DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_START adr: %d:%d\n",
1774 ev->data.addr.client, ev->data.addr.port);
1775
1776 //alsaScanMidiPorts();
1777 // MusEGlobal::audio->midiPortsChanged(); // signal gui
1778 // snd_seq_free_event(ev);
1779 // return;
1780 if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1781 {
1782 muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1783 MusEGlobal::audio->sendMsgToGui('P');
1784 }
1785 snd_seq_free_event(ev);
1786 if(rv == 0)
1787 return;
1788 continue;
1789
1790 case SND_SEQ_EVENT_PORT_EXIT:
1791 DEBUG_PRST_ROUTES(stderr, "alsaProcessMidiInput SND_SEQ_EVENT_PORT_EXIT adr: %d:%d\n",
1792 ev->data.addr.client, ev->data.addr.port);
1793 //alsaScanMidiPorts();
1794 // MusEGlobal::audio->midiPortsChanged(); // signal gui
1795 // snd_seq_free_event(ev);
1796 // return;
1797 if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1798 {
1799 muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1800 MusEGlobal::audio->sendMsgToGui('P');
1801 }
1802 snd_seq_free_event(ev);
1803 if(rv == 0)
1804 return;
1805 continue;
1806
1807 // case SND_SEQ_EVENT_PORT_SUBSCRIBED:
1808 // case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
1809 // case SND_SEQ_EVENT_CLIENT_START:
1810 // case SND_SEQ_EVENT_CLIENT_EXIT:
1811 // case SND_SEQ_EVENT_PORT_START:
1812 // case SND_SEQ_EVENT_PORT_EXIT:
1813 // if(muse_atomic_read(&atomicAlsaMidiScanPending) == 0)
1814 // {
1815 // muse_atomic_set(&atomicAlsaMidiScanPending, 1);
1816 // MusEGlobal::audio->sendMsgToGui('P');
1817 // }
1818 // snd_seq_free_event(ev);
1819 // if(rv == 0)
1820 // return;
1821 // continue;
1822 }
1823
1824 int curPort = -1;
1825 MidiAlsaDevice* mdev = 0;
1826 //
1827 // find real source device
1828 //
1829 for (iMidiDevice i = MusEGlobal::midiDevices.begin(); i != MusEGlobal::midiDevices.end(); ++i) {
1830 if((*i)->deviceType() != MidiDevice::ALSA_MIDI)
1831 continue;
1832 MidiAlsaDevice* d = static_cast<MidiAlsaDevice*>(*i);
1833 if(d->adr.client == ev->source.client
1834 && d->adr.port == ev->source.port) {
1835 curPort = d->midiPort();
1836 mdev = d;
1837 }
1838 }
1839
1840 if (mdev == 0 || curPort == -1) {
1841 if (MusEGlobal::debugMsg) {
1842 fprintf(stderr, "no port %d:%d found for received alsa event\n",
1843 ev->source.client, ev->source.port);
1844 }
1845 snd_seq_free_event(ev);
1846 //return;
1847 if(rv == 0)
1848 return;
1849 continue;
1850 }
1851
1852 event.setType(0); // mark as unused
1853 event.setPort(curPort);
1854 event.setB(0);
1855
1856 //MidiInstrument* instr = MusEGlobal::midiPorts[curPort].inputInstrument();
1857
1858 switch(ev->type)
1859 {
1860 case SND_SEQ_EVENT_NOTEON:
1861 if(ev->data.note.velocity == 0)
1862 {
1863 // Convert zero-velocity note ons to note offs as per midi spec.
1864 event.setChannel(ev->data.note.channel);
1865 event.setType(ME_NOTEOFF);
1866 event.setA(ev->data.note.note);
1867 event.setB(ev->data.note.velocity);
1868 }
1869 else
1870 {
1871 event.setChannel(ev->data.note.channel);
1872 event.setType(ME_NOTEON);
1873 event.setA(ev->data.note.note);
1874 event.setB(ev->data.note.velocity);
1875 }
1876 break;
1877
1878 case SND_SEQ_EVENT_NOTEOFF:
1879 event.setChannel(ev->data.note.channel);
1880 event.setType(ME_NOTEOFF);
1881 event.setA(ev->data.note.note);
1882 event.setB(ev->data.note.velocity);
1883 break;
1884
1885 case SND_SEQ_EVENT_KEYPRESS:
1886 event.setChannel(ev->data.note.channel);
1887 event.setType(ME_POLYAFTER);
1888 event.setA(ev->data.note.note);
1889 event.setB(ev->data.note.velocity);
1890 break;
1891
1892 case SND_SEQ_EVENT_CHANPRESS:
1893 event.setChannel(ev->data.control.channel);
1894 event.setType(ME_AFTERTOUCH);
1895 event.setA(ev->data.control.value);
1896 break;
1897
1898 case SND_SEQ_EVENT_PGMCHANGE:
1899 event.setChannel(ev->data.control.channel);
1900 event.setType(ME_PROGRAM);
1901 event.setA(ev->data.control.value);
1902 break;
1903
1904 case SND_SEQ_EVENT_PITCHBEND:
1905 event.setChannel(ev->data.control.channel);
1906 event.setType(ME_PITCHBEND);
1907 event.setA(ev->data.control.value);
1908 break;
1909
1910 case SND_SEQ_EVENT_CONTROLLER:
1911 event.setChannel(ev->data.control.channel);
1912 event.setType(ME_CONTROLLER);
1913 event.setA(ev->data.control.param);
1914 event.setB(ev->data.control.value);
1915 break;
1916
1917 case SND_SEQ_EVENT_CLOCK:
1918 if(MusEGlobal::audio && MusEGlobal::audio->isRunning())
1919 mdev->midiClockInput(frame_ts);
1920 break;
1921
1922 case SND_SEQ_EVENT_START:
1923 #ifdef ALSA_DEBUG
1924 if(MusEGlobal::midiInputTrace)
1925 fprintf(stderr, "alsaProcessMidiInput: start port:%d curFrame:%u\n", curPort, frame_ts);
1926 #endif
1927 MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_START);
1928 break;
1929
1930 case SND_SEQ_EVENT_CONTINUE:
1931 MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_CONTINUE);
1932 break;
1933
1934 case SND_SEQ_EVENT_STOP:
1935 MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_STOP);
1936 break;
1937
1938 case SND_SEQ_EVENT_TICK:
1939 MusEGlobal::midiSyncContainer.realtimeSystemInput(curPort, ME_TICK);
1940 break;
1941
1942 case SND_SEQ_EVENT_SYSEX:
1943 {
1944 EvData ed;
1945 const unsigned char* p = (unsigned char*)ev->data.ext.ptr;
1946
1947 // Process the input. Create the event data only if finished.
1948 if(mdev->sysExInProcessor()->processInput(
1949 &ed, p, ev->data.ext.len, frame_ts) != SysExInputProcessor::Finished)
1950 break;
1951
1952 #ifdef ALSA_DEBUG
1953 fprintf(stderr, "alsaProcessMidiInput: SysEx: frame_ts:%u startFrame:%u\n",
1954 frame_ts, (unsigned int)mdev->sysExInProcessor()->startFrame());
1955 #endif
1956
1957 // Finished composing the sysex data.
1958 // Mark the frame timestamp as the frame at which the sysex started.
1959 frame_ts = mdev->sysExInProcessor()->startFrame();
1960 event.setType(ME_SYSEX);
1961 event.setData(ed);
1962 }
1963 break;
1964 case SND_SEQ_EVENT_PORT_SUBSCRIBED:
1965 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED: // write port is released
1966 break;
1967 case SND_SEQ_EVENT_SONGPOS:
1968 MusEGlobal::midiSyncContainer.setSongPosition(curPort, ev->data.control.value);
1969 break;
1970 case SND_SEQ_EVENT_SENSING:
1971 break;
1972 case SND_SEQ_EVENT_QFRAME:
1973 MusEGlobal::midiSyncContainer.mtcInputQuarter(curPort, ev->data.control.value);
1974 break;
1975 // case SND_SEQ_EVENT_CLIENT_START:
1976 // case SND_SEQ_EVENT_CLIENT_EXIT:
1977 // case SND_SEQ_EVENT_CLIENT_CHANGE:
1978 // case SND_SEQ_EVENT_PORT_CHANGE:
1979 // case SND_SEQ_EVENT_SONGSEL:
1980 // case SND_SEQ_EVENT_TIMESIGN:
1981 // case SND_SEQ_EVENT_KEYSIGN:
1982 // case SND_SEQ_EVENT_SETPOS_TICK:
1983 // case SND_SEQ_EVENT_SETPOS_TIME:
1984 // case SND_SEQ_EVENT_TEMPO:
1985 // case SND_SEQ_EVENT_TUNE_REQUEST:
1986 // case SND_SEQ_EVENT_RESET:
1987
1988 // case SND_SEQ_EVENT_NOTE:
1989 // case SND_SEQ_EVENT_CONTROL14:
1990 // case SND_SEQ_EVENT_NONREGPARAM:
1991 // fprintf(stderr, "ALSA Midi input: NONREGPARAM ch:%u param:%u value:%d\n",
1992 // ev->data.control.channel,
1993 // ev->data.control.param,
1994 // ev->data.control.value);
1995 // event.setChannel(ev->data.control.channel);
1996 // event.setType(ME_CONTROLLER);
1997 // event.setA(ev->data.control.param);
1998 // event.setB(ev->data.control.value);
1999 // break;
2000 // case SND_SEQ_EVENT_REGPARAM:
2001 default:
2002 fprintf(stderr, "ALSA Midi input: type %d not handled\n", ev->type);
2003 break;
2004 }
2005 if(event.type())
2006 {
2007 // TODO: Tested, but record resolution not so good. Switch to wall clock based separate list in MidiDevice.
2008 event.setTime(frame_ts);
2009 event.setTick(MusEGlobal::lastExtMidiSyncTick);
2010
2011 mdev->recordEvent(event);
2012 }
2013
2014 snd_seq_free_event(ev);
2015 if (rv == 0)
2016 break;
2017 }
2018 }
2019
2020 //---------------------------------------------------------
2021 // dump
2022 // static
2023 //---------------------------------------------------------
2024
dump(const snd_seq_event_t * ev)2025 void MidiAlsaDevice::dump(const snd_seq_event_t* ev)
2026 {
2027 switch(ev->type)
2028 {
2029 case SND_SEQ_EVENT_PORT_SUBSCRIBED:
2030 fprintf(stderr, "SND_SEQ_EVENT_PORT_SUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n",
2031 ev->data.connect.sender.client, ev->data.connect.sender.port,
2032 ev->data.connect.dest.client, ev->data.connect.dest.port);
2033 break;
2034
2035 case SND_SEQ_EVENT_PORT_UNSUBSCRIBED:
2036 fprintf(stderr, "SND_SEQ_EVENT_PORT_UNSUBSCRIBED sender adr: %d:%d dest adr: %d:%d\n",
2037 ev->data.connect.sender.client, ev->data.connect.sender.port,
2038 ev->data.connect.dest.client, ev->data.connect.dest.port);
2039 break;
2040
2041 case SND_SEQ_EVENT_CLIENT_START:
2042 fprintf(stderr, "SND_SEQ_EVENT_CLIENT_START adr: %d:%d\n",
2043 ev->data.addr.client, ev->data.addr.port);
2044 break;
2045
2046 case SND_SEQ_EVENT_CLIENT_EXIT:
2047 fprintf(stderr, "SND_SEQ_EVENT_CLIENT_EXIT adr: %d:%d\n",
2048 ev->data.addr.client, ev->data.addr.port);
2049 break;
2050
2051 case SND_SEQ_EVENT_PORT_START:
2052 fprintf(stderr, "SND_SEQ_EVENT_PORT_START adr: %d:%d\n",
2053 ev->data.addr.client, ev->data.addr.port);
2054 break;
2055
2056 case SND_SEQ_EVENT_PORT_EXIT:
2057 fprintf(stderr, "SND_SEQ_EVENT_PORT_EXIT adr: %d:%d\n",
2058 ev->data.addr.client, ev->data.addr.port);
2059 break;
2060
2061 case SND_SEQ_EVENT_CONTROLLER:
2062 fprintf(stderr, "SND_SEQ_EVENT_CONTROLLER chan:%u param:%u value:%d\n",
2063 ev->data.control.channel, ev->data.control.param, ev->data.control.value);
2064 break;
2065
2066 case SND_SEQ_EVENT_NOTE:
2067 fprintf(stderr, "SND_SEQ_EVENT_NOTE chan:%u note:%u velocity:%u off_velocity:%u duration:%u\n",
2068 ev->data.note.channel, ev->data.note.note, ev->data.note.velocity, ev->data.note.off_velocity, ev->data.note.duration);
2069 break;
2070
2071 case SND_SEQ_EVENT_NOTEON:
2072 fprintf(stderr, "SND_SEQ_EVENT_NOTEON chan:%u note:%u velocity:%u\n",
2073 ev->data.note.channel, ev->data.note.note, ev->data.note.velocity);
2074 break;
2075
2076 case SND_SEQ_EVENT_NOTEOFF:
2077 fprintf(stderr, "SND_SEQ_EVENT_NOTEOFF chan:%u note:%u velocity:%u\n",
2078 ev->data.note.channel, ev->data.note.note, ev->data.note.velocity);
2079 break;
2080
2081 case SND_SEQ_EVENT_KEYPRESS:
2082 fprintf(stderr, "SND_SEQ_EVENT_KEYPRESS chan:%u note:%u velocity:%u\n",
2083 ev->data.note.channel, ev->data.note.note, ev->data.note.velocity);
2084 break;
2085
2086 case SND_SEQ_EVENT_CHANPRESS:
2087 fprintf(stderr, "SND_SEQ_EVENT_CHANPRESS chan:%u value:%d\n",
2088 ev->data.control.channel, ev->data.control.value);
2089 break;
2090
2091 case SND_SEQ_EVENT_PGMCHANGE:
2092 fprintf(stderr, "SND_SEQ_EVENT_PGMCHANGE chan:%u value:%d\n",
2093 ev->data.control.channel, ev->data.control.value);
2094 break;
2095
2096 case SND_SEQ_EVENT_PITCHBEND:
2097 fprintf(stderr, "SND_SEQ_EVENT_PITCHBEND chan:%u value:%d\n",
2098 ev->data.control.channel, ev->data.control.value);
2099 break;
2100
2101 case SND_SEQ_EVENT_CLOCK:
2102 fprintf(stderr, "SND_SEQ_EVENT_CLOCK\n");
2103 break;
2104
2105 case SND_SEQ_EVENT_START:
2106 fprintf(stderr, "SND_SEQ_EVENT_START\n");
2107 break;
2108
2109 case SND_SEQ_EVENT_CONTINUE:
2110 fprintf(stderr, "SND_SEQ_EVENT_CONTINUE\n");
2111 break;
2112
2113 case SND_SEQ_EVENT_STOP:
2114 fprintf(stderr, "SND_SEQ_EVENT_STOP\n");
2115 break;
2116
2117 case SND_SEQ_EVENT_TICK:
2118 fprintf(stderr, "SND_SEQ_EVENT_TICK\n");
2119 break;
2120
2121 case SND_SEQ_EVENT_SYSEX:
2122 fprintf(stderr, "SND_SEQ_EVENT_SYSEX len:%u data: ", ev->data.ext.len);
2123 for(unsigned int i = 0; i < ev->data.ext.len && i < 16; ++i)
2124 fprintf(stderr, "%0x ", ((unsigned char*)ev->data.ext.ptr)[i]);
2125 if(ev->data.ext.len >= 16)
2126 fprintf(stderr, "...");
2127 fprintf(stderr, "\n");
2128 break;
2129
2130 case SND_SEQ_EVENT_SONGPOS:
2131 fprintf(stderr, "SND_SEQ_EVENT_SONGPOS value:%d\n",
2132 ev->data.control.value);
2133 break;
2134
2135 case SND_SEQ_EVENT_SENSING:
2136 fprintf(stderr, "SND_SEQ_EVENT_SENSING\n");
2137 break;
2138
2139 case SND_SEQ_EVENT_QFRAME:
2140 fprintf(stderr, "SND_SEQ_EVENT_QFRAME value:%d\n",
2141 ev->data.control.value);
2142 break;
2143 case SND_SEQ_EVENT_CONTROL14:
2144 fprintf(stderr, "SND_SEQ_EVENT_CONTROL14 ch:%u param:%u value:%d\n",
2145 ev->data.control.channel,
2146 ev->data.control.param,
2147 ev->data.control.value);
2148 break;
2149
2150 case SND_SEQ_EVENT_NONREGPARAM:
2151 fprintf(stderr, "SND_SEQ_EVENT_NONREGPARAM ch:%u param:%u value:%d\n",
2152 ev->data.control.channel,
2153 ev->data.control.param,
2154 ev->data.control.value);
2155 break;
2156
2157 case SND_SEQ_EVENT_REGPARAM:
2158 fprintf(stderr, "SND_SEQ_EVENT_REGPARAM ch:%u param:%u value:%d\n",
2159 ev->data.control.channel,
2160 ev->data.control.param,
2161 ev->data.control.value);
2162 break;
2163
2164 // case SND_SEQ_EVENT_CLIENT_CHANGE:
2165 // case SND_SEQ_EVENT_PORT_CHANGE:
2166 // case SND_SEQ_EVENT_SONGSEL:
2167 // case SND_SEQ_EVENT_TIMESIGN:
2168 // case SND_SEQ_EVENT_KEYSIGN:
2169 // case SND_SEQ_EVENT_SETPOS_TICK:
2170 // case SND_SEQ_EVENT_SETPOS_TIME:
2171 // case SND_SEQ_EVENT_TEMPO:
2172 // case SND_SEQ_EVENT_TUNE_REQUEST:
2173 // case SND_SEQ_EVENT_RESET:
2174
2175 default:
2176 fprintf(stderr, "ALSA dump event: unknown type:%u\n", ev->type);
2177 break;
2178 }
2179 }
2180
2181 } // namespace MusECore
2182
2183 #else // ALSA_SUPPORT
2184
2185 namespace MusECore {
2186
initMidiAlsa()2187 bool initMidiAlsa() { return false; }
exitMidiAlsa()2188 void exitMidiAlsa() {}
alsaSelectRfd()2189 int alsaSelectRfd() { return -1; }
alsaSelectWfd()2190 int alsaSelectWfd() { return -1; }
alsaProcessMidiInput()2191 void alsaProcessMidiInput() { }
alsaScanMidiPorts()2192 void alsaScanMidiPorts() { }
setAlsaClientName(const char *)2193 void setAlsaClientName(const char*) { }
2194 }
2195
2196 #endif // ALSA_SUPPORT
2197