break out ALSA related functions into libardouralsautil
[ardour.git] / libs / backends / alsa / alsa_audiobackend.cc
1 /*
2  * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
3  * Copyright (C) 2013 Paul Davis
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include <regex.h>
21 #include <sys/mman.h>
22 #include <sys/time.h>
23
24 #include <glibmm.h>
25
26 #include "alsa_audiobackend.h"
27 #include "rt_thread.h"
28
29 #include "pbd/compose.h"
30 #include "pbd/error.h"
31 #include "pbd/file_utils.h"
32 #include "ardour/port_manager.h"
33 #include "ardour/system_exec.h"
34 #include "ardouralsautil/devicelist.h"
35 #include "i18n.h"
36
37 using namespace ARDOUR;
38
39 static std::string s_instance_name;
40 size_t AlsaAudioBackend::_max_buffer_size = 8192;
41
42 AlsaAudioBackend::AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info)
43         : AudioBackend (e, info)
44         , _pcmi (0)
45         , _run (false)
46         , _active (false)
47         , _freewheeling (false)
48         , _audio_device("")
49         , _midi_device("")
50         , _samplerate (48000)
51         , _samples_per_period (1024)
52         , _periods_per_cycle (2)
53         , _dsp_load (0)
54         , _n_inputs (0)
55         , _n_outputs (0)
56         , _systemic_input_latency (0)
57         , _systemic_output_latency (0)
58         , _processed_samples (0)
59 {
60         _instance_name = s_instance_name;
61         pthread_mutex_init (&_port_callback_mutex, 0);
62 }
63
64 AlsaAudioBackend::~AlsaAudioBackend ()
65 {
66         pthread_mutex_destroy (&_port_callback_mutex);
67 }
68
69 /* AUDIOBACKEND API */
70
71 std::string
72 AlsaAudioBackend::name () const
73 {
74         return X_("ALSA");
75 }
76
77 bool
78 AlsaAudioBackend::is_realtime () const
79 {
80         return true;
81 }
82
83 std::vector<AudioBackend::DeviceStatus>
84 AlsaAudioBackend::enumerate_devices () const
85 {
86         std::vector<AudioBackend::DeviceStatus> s;
87         std::map<std::string, std::string> devices;
88         get_alsa_audio_device_names(devices);
89         for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
90                 s.push_back (DeviceStatus (i->first, true));
91         }
92         return s;
93 }
94
95 static void acquire_device(const char* device_name)
96 {
97         /* This is  quick hack, ideally we'll link against libdbus and implement a dbus-listener
98          * that owns the device. here we try to get away by just requesting it and then block it...
99          * (pulseaudio periodically checks anyway)
100          *
101          * dbus-send --session --print-reply --type=method_call --dest=org.freedesktop.ReserveDevice1.Audio2 /org/freedesktop/ReserveDevice1/Audio2 org.freedesktop.ReserveDevice1.RequestRelease int32:4
102          * -> should not return  'boolean false'
103          */
104         int device_number = card_to_num(device_name);
105         if (device_number < 0) return;
106
107         std::string dbus_send_path;
108         if (PBD::find_file_in_search_path (PBD::Searchpath(Glib::getenv("PATH")), X_("dbus-send"), dbus_send_path)) {
109                 char **argp;
110                 char tmp[128];
111                 argp=(char**) calloc(8,sizeof(char*));
112                 argp[0] = strdup(dbus_send_path.c_str());
113                 argp[1] = strdup("--session");
114                 argp[2] = strdup("--print-reply"); // need to wait for the receiver to act.
115                 argp[3] = strdup("--type=method_call");
116                 snprintf(tmp, sizeof(tmp), "--dest=org.freedesktop.ReserveDevice1.Audio%d", device_number);
117                 argp[4] = strdup(tmp);
118                 snprintf(tmp, sizeof(tmp), "/org/freedesktop/ReserveDevice1/Audio%d", device_number);
119                 argp[5] = strdup(tmp);
120                 argp[6] = strdup("org.freedesktop.ReserveDevice1.RequestRelease");
121                 argp[7] = strdup("int32:4294967296");
122                 snprintf(tmp, sizeof(tmp), "string:'org.freedesktop.ReserveDevice1.Audio%d'", device_number);
123                 argp[7] = strdup(tmp);
124                 argp[8] = 0;
125
126                 ARDOUR::SystemExec process (dbus_send_path, argp);
127                 if (!process.start(1)) {
128                         process.wait();
129                 }
130         }
131 }
132
133 std::vector<float>
134 AlsaAudioBackend::available_sample_rates (const std::string&) const
135 {
136         std::vector<float> sr;
137         sr.push_back (8000.0);
138         sr.push_back (22050.0);
139         sr.push_back (24000.0);
140         sr.push_back (44100.0);
141         sr.push_back (48000.0);
142         sr.push_back (88200.0);
143         sr.push_back (96000.0);
144         sr.push_back (176400.0);
145         sr.push_back (192000.0);
146         return sr;
147 }
148
149 std::vector<uint32_t>
150 AlsaAudioBackend::available_buffer_sizes (const std::string&) const
151 {
152         std::vector<uint32_t> bs;
153         bs.push_back (32);
154         bs.push_back (64);
155         bs.push_back (128);
156         bs.push_back (256);
157         bs.push_back (512);
158         bs.push_back (1024);
159         bs.push_back (2048);
160         bs.push_back (4096);
161         bs.push_back (8192);
162         return bs;
163 }
164
165 uint32_t
166 AlsaAudioBackend::available_input_channel_count (const std::string&) const
167 {
168         return 128; // TODO query current device
169 }
170
171 uint32_t
172 AlsaAudioBackend::available_output_channel_count (const std::string&) const
173 {
174         return 128; // TODO query current device
175 }
176
177 bool
178 AlsaAudioBackend::can_change_sample_rate_when_running () const
179 {
180         return false;
181 }
182
183 bool
184 AlsaAudioBackend::can_change_buffer_size_when_running () const
185 {
186         return false;
187 }
188
189 int
190 AlsaAudioBackend::set_device_name (const std::string& d)
191 {
192         _audio_device = d;
193         return 0;
194 }
195
196 int
197 AlsaAudioBackend::set_sample_rate (float sr)
198 {
199         if (sr <= 0) { return -1; }
200         _samplerate = sr;
201         engine.sample_rate_change (sr);
202         return 0;
203 }
204
205 int
206 AlsaAudioBackend::set_buffer_size (uint32_t bs)
207 {
208         if (bs <= 0 || bs >= _max_buffer_size) {
209                 return -1;
210         }
211         _samples_per_period = bs;
212         engine.buffer_size_change (bs);
213         return 0;
214 }
215
216 int
217 AlsaAudioBackend::set_interleaved (bool yn)
218 {
219         if (!yn) { return 0; }
220         return -1;
221 }
222
223 int
224 AlsaAudioBackend::set_input_channels (uint32_t cc)
225 {
226         _n_inputs = cc;
227         return 0;
228 }
229
230 int
231 AlsaAudioBackend::set_output_channels (uint32_t cc)
232 {
233         _n_outputs = cc;
234         return 0;
235 }
236
237 int
238 AlsaAudioBackend::set_systemic_input_latency (uint32_t sl)
239 {
240         _systemic_input_latency = sl;
241         return 0;
242 }
243
244 int
245 AlsaAudioBackend::set_systemic_output_latency (uint32_t sl)
246 {
247         _systemic_output_latency = sl;
248         return 0;
249 }
250
251 /* Retrieving parameters */
252 std::string
253 AlsaAudioBackend::device_name () const
254 {
255         return _audio_device;
256 }
257
258 float
259 AlsaAudioBackend::sample_rate () const
260 {
261         return _samplerate;
262 }
263
264 uint32_t
265 AlsaAudioBackend::buffer_size () const
266 {
267         return _samples_per_period;
268 }
269
270 bool
271 AlsaAudioBackend::interleaved () const
272 {
273         return false;
274 }
275
276 uint32_t
277 AlsaAudioBackend::input_channels () const
278 {
279         return _n_inputs;
280 }
281
282 uint32_t
283 AlsaAudioBackend::output_channels () const
284 {
285         return _n_outputs;
286 }
287
288 uint32_t
289 AlsaAudioBackend::systemic_input_latency () const
290 {
291         return _systemic_input_latency;
292 }
293
294 uint32_t
295 AlsaAudioBackend::systemic_output_latency () const
296 {
297         return _systemic_output_latency;
298 }
299
300 /* MIDI */
301 std::vector<std::string>
302 AlsaAudioBackend::enumerate_midi_options () const
303 {
304         std::vector<std::string> m;
305         m.push_back (_("-None-"));
306         std::map<std::string, std::string> devices;
307         get_alsa_rawmidi_device_names(devices);
308
309         for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
310                 m.push_back (i->first);
311         }
312         if (m.size() > 2) {
313                 m.push_back (_("-All-"));
314         }
315         return m;
316 }
317
318 int
319 AlsaAudioBackend::set_midi_option (const std::string& opt)
320 {
321         _midi_device = opt;
322         return 0;
323 }
324
325 std::string
326 AlsaAudioBackend::midi_option () const
327 {
328         return _midi_device;
329 }
330
331 /* State Control */
332
333 static void * pthread_process (void *arg)
334 {
335         AlsaAudioBackend *d = static_cast<AlsaAudioBackend *>(arg);
336         d->main_process_thread ();
337         pthread_exit (0);
338         return 0;
339 }
340
341 int
342 AlsaAudioBackend::_start (bool for_latency_measurement)
343 {
344         if (_active || _run) {
345                 PBD::error << _("AlsaAudioBackend: already active.") << endmsg;
346                 return -1;
347         }
348
349         if (_ports.size()) {
350                 PBD::warning << _("AlsaAudioBackend: recovering from unclean shutdown, port registry is not empty.") << endmsg;
351                 _system_inputs.clear();
352                 _system_outputs.clear();
353                 _system_midi_in.clear();
354                 _system_midi_out.clear();
355                 _ports.clear();
356         }
357
358         assert(_rmidi_in.size() == 0);
359         assert(_rmidi_out.size() == 0);
360         assert(_pcmi == 0);
361
362         std::string alsa_device;
363         std::map<std::string, std::string> devices;
364         get_alsa_audio_device_names(devices);
365         for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
366                 if (i->first == _audio_device) {
367                         alsa_device = i->second;
368                         break;
369                 }
370         }
371
372         acquire_device(alsa_device.c_str());
373         _pcmi = new Alsa_pcmi (alsa_device.c_str(), alsa_device.c_str(), 0, _samplerate, _samples_per_period, _periods_per_cycle, 0);
374         switch (_pcmi->state ()) {
375                 case 0: /* OK */ break;
376                 case -1: PBD::error << _("AlsaAudioBackend: failed to open device.") << endmsg; break;
377                 case -2: PBD::error << _("AlsaAudioBackend: failed to allocate parameters.") << endmsg; break;
378                 case -3: PBD::error << _("AlsaAudioBackend: cannot set requested sample rate.") << endmsg; break;
379                 case -4: PBD::error << _("AlsaAudioBackend: cannot set requested period size.") << endmsg; break;
380                 case -5: PBD::error << _("AlsaAudioBackend: cannot set requested number of periods.") << endmsg; break;
381                 case -6: PBD::error << _("AlsaAudioBackend: unsupported sample format.") << endmsg; break;
382                 default: PBD::error << _("AlsaAudioBackend: initialization failed.") << endmsg; break;
383         }
384         if (_pcmi->state ()) {
385                 delete _pcmi; _pcmi = 0;
386                 return -1;
387         }
388
389 #ifndef NDEBUG
390         _pcmi->printinfo ();
391 #endif
392
393         if (_n_outputs != _pcmi->nplay ()) {
394                 if (_n_outputs == 0) {
395                  _n_outputs = _pcmi->nplay ();
396                 } else {
397                  _n_outputs = std::min (_n_outputs, _pcmi->nplay ());
398                 }
399                 PBD::warning << _("AlsaAudioBackend: adjusted output channel count to match device.") << endmsg;
400         }
401
402         if (_n_inputs != _pcmi->ncapt ()) {
403                 if (_n_inputs == 0) {
404                  _n_inputs = _pcmi->ncapt ();
405                 } else {
406                  _n_inputs = std::min (_n_inputs, _pcmi->ncapt ());
407                 }
408                 PBD::warning << _("AlsaAudioBackend: adjusted input channel count to match device.") << endmsg;
409         }
410
411         if (_pcmi->fsize() != _samples_per_period) {
412                 _samples_per_period = _pcmi->fsize();
413                 PBD::warning << _("AlsaAudioBackend: samples per period does not match.") << endmsg;
414         }
415
416         if (_pcmi->fsamp() != _samplerate) {
417                 _samplerate = _pcmi->fsamp();
418                 engine.sample_rate_change (_samplerate);
419                 PBD::warning << _("AlsaAudioBackend: sample rate does not match.") << endmsg;
420         }
421
422         if (for_latency_measurement) {
423                 _systemic_input_latency = 0;
424                 _systemic_output_latency = 0;
425         }
426
427         register_system_midi_ports();
428
429         if (register_system_audio_ports()) {
430                 PBD::error << _("AlsaAudioBackend: failed to register system ports.") << endmsg;
431                 delete _pcmi; _pcmi = 0;
432                 return -1;
433         }
434
435         if (engine.reestablish_ports ()) {
436                 PBD::error << _("AlsaAudioBackend: Could not re-establish ports.") << endmsg;
437                 delete _pcmi; _pcmi = 0;
438                 return -1;
439         }
440
441         engine.buffer_size_change (_samples_per_period);
442         engine.reconnect_ports ();
443         _run = true;
444
445         if (_realtime_pthread_create (SCHED_FIFO, -20,
446                                 &_main_thread, pthread_process, this))
447         {
448                 if (pthread_create (&_main_thread, NULL, pthread_process, this))
449                 {
450                         PBD::error << _("AlsaAudioBackend: failed to create process thread.") << endmsg;
451                         delete _pcmi; _pcmi = 0;
452                         _run = false;
453                         return -1;
454                 } else {
455                         PBD::warning << _("AlsaAudioBackend: cannot acquire realtime permissions.") << endmsg;
456                 }
457         }
458
459         int timeout = 5000;
460         while (!_active && --timeout > 0) { Glib::usleep (1000); }
461
462         if (timeout == 0 || !_active) {
463                 PBD::error << _("AlsaAudioBackend: failed to start process thread.") << endmsg;
464                 delete _pcmi; _pcmi = 0;
465                 _run = false;
466                 return -1;
467         }
468
469         return 0;
470 }
471
472 int
473 AlsaAudioBackend::stop ()
474 {
475         void *status;
476         if (!_active) {
477                 return 0;
478         }
479
480         _run = false;
481         if (pthread_join (_main_thread, &status)) {
482                 PBD::error << _("AlsaAudioBackend: failed to terminate.") << endmsg;
483                 return -1;
484         }
485
486         while (!_rmidi_out.empty ()) {
487                 AlsaRawMidiIO *m = _rmidi_out.back ();
488                 m->stop();
489                 _rmidi_out.pop_back ();
490                 delete m;
491         }
492         while (!_rmidi_in.empty ()) {
493                 AlsaRawMidiIO *m = _rmidi_in.back ();
494                 m->stop();
495                 _rmidi_in.pop_back ();
496                 delete m;
497         }
498
499         unregister_system_ports();
500         delete _pcmi; _pcmi = 0;
501         return (_active == false) ? 0 : -1;
502 }
503
504 int
505 AlsaAudioBackend::freewheel (bool onoff)
506 {
507         if (onoff == _freewheeling) {
508                 return 0;
509         }
510         _freewheeling = onoff;
511         engine.freewheel_callback (onoff);
512         return 0;
513 }
514
515 float
516 AlsaAudioBackend::dsp_load () const
517 {
518         return 100.f * _dsp_load;
519 }
520
521 size_t
522 AlsaAudioBackend::raw_buffer_size (DataType t)
523 {
524         switch (t) {
525                 case DataType::AUDIO:
526                         return _samples_per_period * sizeof(Sample);
527                 case DataType::MIDI:
528                         return _max_buffer_size; // XXX not really limited
529         }
530         return 0;
531 }
532
533 /* Process time */
534 pframes_t
535 AlsaAudioBackend::sample_time ()
536 {
537         return _processed_samples;
538 }
539
540 pframes_t
541 AlsaAudioBackend::sample_time_at_cycle_start ()
542 {
543         return _processed_samples;
544 }
545
546 pframes_t
547 AlsaAudioBackend::samples_since_cycle_start ()
548 {
549         return 0;
550 }
551
552
553 void *
554 AlsaAudioBackend::alsa_process_thread (void *arg)
555 {
556         ThreadData* td = reinterpret_cast<ThreadData*> (arg);
557         boost::function<void ()> f = td->f;
558         delete td;
559         f ();
560         return 0;
561 }
562
563 int
564 AlsaAudioBackend::create_process_thread (boost::function<void()> func)
565 {
566         pthread_t thread_id;
567         pthread_attr_t attr;
568         size_t stacksize = 100000;
569
570         pthread_attr_init (&attr);
571         pthread_attr_setstacksize (&attr, stacksize);
572         ThreadData* td = new ThreadData (this, func, stacksize);
573
574         if (pthread_create (&thread_id, &attr, alsa_process_thread, td)) {
575                 PBD::error << _("AudioEngine: cannot create process thread.") << endmsg;
576                 pthread_attr_destroy (&attr);
577                 return -1;
578         }
579         pthread_attr_destroy (&attr);
580
581         _threads.push_back (thread_id);
582         return 0;
583 }
584
585 int
586 AlsaAudioBackend::join_process_threads ()
587 {
588         int rv = 0;
589
590         for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
591         {
592                 void *status;
593                 if (pthread_join (*i, &status)) {
594                         PBD::error << _("AudioEngine: cannot terminate process thread.") << endmsg;
595                         rv -= 1;
596                 }
597         }
598         _threads.clear ();
599         return rv;
600 }
601
602 bool
603 AlsaAudioBackend::in_process_thread ()
604 {
605         for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
606         {
607                 if (pthread_equal (*i, pthread_self ()) != 0) {
608                         return true;
609                 }
610         }
611         return false;
612 }
613
614 uint32_t
615 AlsaAudioBackend::process_thread_count ()
616 {
617         return _threads.size ();
618 }
619
620 void
621 AlsaAudioBackend::update_latencies ()
622 {
623 }
624
625 /* PORTENGINE API */
626
627 void*
628 AlsaAudioBackend::private_handle () const
629 {
630         return NULL;
631 }
632
633 const std::string&
634 AlsaAudioBackend::my_name () const
635 {
636         return _instance_name;
637 }
638
639 bool
640 AlsaAudioBackend::available () const
641 {
642         return _run && _active;
643 }
644
645 uint32_t
646 AlsaAudioBackend::port_name_size () const
647 {
648         return 256;
649 }
650
651 int
652 AlsaAudioBackend::set_port_name (PortEngine::PortHandle port, const std::string& name)
653 {
654         if (!valid_port (port)) {
655                 PBD::error << _("AlsaBackend::set_port_name: Invalid Port(s)") << endmsg;
656                 return -1;
657         }
658         return static_cast<AlsaPort*>(port)->set_name (_instance_name + ":" + name);
659 }
660
661 std::string
662 AlsaAudioBackend::get_port_name (PortEngine::PortHandle port) const
663 {
664         if (!valid_port (port)) {
665                 PBD::error << _("AlsaBackend::get_port_name: Invalid Port(s)") << endmsg;
666                 return std::string ();
667         }
668         return static_cast<AlsaPort*>(port)->name ();
669 }
670
671 PortEngine::PortHandle
672 AlsaAudioBackend::get_port_by_name (const std::string& name) const
673 {
674         PortHandle port = (PortHandle) find_port (name);
675         return port;
676 }
677
678 int
679 AlsaAudioBackend::get_ports (
680                 const std::string& port_name_pattern,
681                 DataType type, PortFlags flags,
682                 std::vector<std::string>& port_names) const
683 {
684         int rv = 0;
685         regex_t port_regex;
686         bool use_regexp = false;
687         if (port_name_pattern.size () > 0) {
688                 if (!regcomp (&port_regex, port_name_pattern.c_str (), REG_EXTENDED|REG_NOSUB)) {
689                         use_regexp = true;
690                 }
691         }
692         for (size_t i = 0; i < _ports.size (); ++i) {
693                 AlsaPort* port = _ports[i];
694                 if ((port->type () == type) && (port->flags () & flags)) {
695                         if (!use_regexp || !regexec (&port_regex, port->name ().c_str (), 0, NULL, 0)) {
696                                 port_names.push_back (port->name ());
697                                 ++rv;
698                         }
699                 }
700         }
701         if (use_regexp) {
702                 regfree (&port_regex);
703         }
704         return rv;
705 }
706
707 DataType
708 AlsaAudioBackend::port_data_type (PortEngine::PortHandle port) const
709 {
710         if (!valid_port (port)) {
711                 return DataType::NIL;
712         }
713         return static_cast<AlsaPort*>(port)->type ();
714 }
715
716 PortEngine::PortHandle
717 AlsaAudioBackend::register_port (
718                 const std::string& name,
719                 ARDOUR::DataType type,
720                 ARDOUR::PortFlags flags)
721 {
722         if (name.size () == 0) { return 0; }
723         if (flags & IsPhysical) { return 0; }
724         return add_port (_instance_name + ":" + name, type, flags);
725 }
726
727 PortEngine::PortHandle
728 AlsaAudioBackend::add_port (
729                 const std::string& name,
730                 ARDOUR::DataType type,
731                 ARDOUR::PortFlags flags)
732 {
733         assert(name.size ());
734         if (find_port (name)) {
735                 PBD::error << _("AlsaBackend::register_port: Port already exists:")
736                                 << " (" << name << ")" << endmsg;
737                 return 0;
738         }
739         AlsaPort* port = NULL;
740         switch (type) {
741                 case DataType::AUDIO:
742                         port = new AlsaAudioPort (*this, name, flags);
743                         break;
744                 case DataType::MIDI:
745                         port = new AlsaMidiPort (*this, name, flags);
746                         break;
747                 default:
748                         PBD::error << _("AlsaBackend::register_port: Invalid Data Type.") << endmsg;
749                         return 0;
750         }
751
752         _ports.push_back (port);
753
754         return port;
755 }
756
757 void
758 AlsaAudioBackend::unregister_port (PortEngine::PortHandle port_handle)
759 {
760         if (!valid_port (port_handle)) {
761                 PBD::error << _("AlsaBackend::unregister_port: Invalid Port.") << endmsg;
762         }
763         AlsaPort* port = static_cast<AlsaPort*>(port_handle);
764         std::vector<AlsaPort*>::iterator i = std::find (_ports.begin (), _ports.end (), static_cast<AlsaPort*>(port_handle));
765         if (i == _ports.end ()) {
766                 PBD::error << _("AlsaBackend::unregister_port: Failed to find port") << endmsg;
767                 return;
768         }
769         disconnect_all(port_handle);
770         _ports.erase (i);
771         delete port;
772 }
773
774 int
775 AlsaAudioBackend::register_system_audio_ports()
776 {
777         LatencyRange lr;
778
779         const int a_ins = _n_inputs > 0 ? _n_inputs : 2;
780         const int a_out = _n_outputs > 0 ? _n_outputs : 2;
781
782         /* audio ports */
783         lr.min = lr.max = _samples_per_period * _periods_per_cycle + _systemic_input_latency;
784         for (int i = 1; i <= a_ins; ++i) {
785                 char tmp[64];
786                 snprintf(tmp, sizeof(tmp), "system:capture_%d", i);
787                 PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
788                 if (!p) return -1;
789                 set_latency_range (p, false, lr);
790                 _system_inputs.push_back(static_cast<AlsaPort*>(p));
791         }
792
793         lr.min = lr.max = _samples_per_period * _periods_per_cycle + _systemic_output_latency;
794         for (int i = 1; i <= a_out; ++i) {
795                 char tmp[64];
796                 snprintf(tmp, sizeof(tmp), "system:playback_%d", i);
797                 PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
798                 if (!p) return -1;
799                 set_latency_range (p, false, lr);
800                 _system_outputs.push_back(static_cast<AlsaPort*>(p));
801         }
802         return 0;
803 }
804
805 int
806 AlsaAudioBackend::register_system_midi_ports()
807 {
808         LatencyRange lr;
809         std::vector<std::string> devices;
810
811         if (_midi_device == _("-None-")) {
812                 return 0;
813         }
814         else if (_midi_device == _("-All-")) {
815                 std::map<std::string, std::string> devmap;
816                 get_alsa_rawmidi_device_names(devmap);
817                 for (std::map<std::string, std::string>::const_iterator i = devmap.begin (); i != devmap.end(); ++i) {
818                         devices.push_back (i->second);
819                 }
820         } else {
821                 std::map<std::string, std::string> devmap;
822                 get_alsa_rawmidi_device_names(devmap);
823                 for (std::map<std::string, std::string>::const_iterator i = devmap.begin (); i != devmap.end(); ++i) {
824                         if (i->first == _midi_device) {
825                                 devices.push_back (i->second);
826                                 break;
827                         }
828                 }
829         }
830
831         for (std::vector<std::string>::const_iterator i = devices.begin (); i != devices.end (); ++i) {
832
833                 AlsaRawMidiOut *mout = new AlsaRawMidiOut (i->c_str());
834                 if (mout->state ()) {
835                         PBD::warning << string_compose (
836                                         _("AlsaRawMidiOut: failed to open midi device '%1'."), *i)
837                                 << endmsg;
838                         delete mout;
839                 } else {
840                         mout->setup_timing(_samples_per_period, _samplerate);
841                         mout->sync_time (g_get_monotonic_time());
842                         if (mout->start ()) {
843                                 PBD::warning << string_compose (
844                                                 _("AlsaRawMidiOut: failed to start midi device '%1'."), *i)
845                                         << endmsg;
846                                 delete mout;
847                         } else {
848                                 _rmidi_out.push_back (mout);
849                         }
850                 }
851
852                 AlsaRawMidiIn *midin = new AlsaRawMidiIn (i->c_str());
853                 if (midin->state ()) {
854                         PBD::warning << string_compose (
855                                         _("AlsaRawMidiIn: failed to open midi device '%1'."), *i)
856                                 << endmsg;
857                         delete midin;
858                 } else {
859                         midin->setup_timing(_samples_per_period, _samplerate);
860                         midin->sync_time (g_get_monotonic_time());
861                         if (midin->start ()) {
862                                 PBD::warning << string_compose (
863                                                 _("AlsaRawMidiIn: failed to start midi device '%1'."), *i)
864                                         << endmsg;
865                                 delete midin;
866                         } else {
867                                 _rmidi_in.push_back (midin);
868                         }
869                 }
870         }
871
872         const int m_ins = _rmidi_in.size();
873         const int m_out = _rmidi_out.size();
874
875         lr.min = lr.max = _samples_per_period + _systemic_input_latency;
876         for (int i = 1; i <= m_ins; ++i) {
877                 char tmp[64];
878                 snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", i);
879                 PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
880                 if (!p) return -1;
881                 set_latency_range (p, false, lr);
882                 _system_midi_in.push_back(static_cast<AlsaPort*>(p));
883         }
884
885         lr.min = lr.max = _samples_per_period + _systemic_output_latency;
886         for (int i = 1; i <= m_out; ++i) {
887                 char tmp[64];
888                 snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", i);
889                 PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
890                 if (!p) return -1;
891                 set_latency_range (p, false, lr);
892                 _system_midi_out.push_back(static_cast<AlsaPort*>(p));
893         }
894
895         return 0;
896 }
897
898 void
899 AlsaAudioBackend::unregister_system_ports()
900 {
901         size_t i = 0;
902         _system_inputs.clear();
903         _system_outputs.clear();
904         _system_midi_in.clear();
905         _system_midi_out.clear();
906         while (i <  _ports.size ()) {
907                 AlsaPort* port = _ports[i];
908                 if (port->is_physical () && port->is_terminal ()) {
909                         port->disconnect_all ();
910                         _ports.erase (_ports.begin() + i);
911                 } else {
912                         ++i;
913                 }
914         }
915 }
916
917 int
918 AlsaAudioBackend::connect (const std::string& src, const std::string& dst)
919 {
920         AlsaPort* src_port = find_port (src);
921         AlsaPort* dst_port = find_port (dst);
922
923         if (!src_port) {
924                 PBD::error << _("AlsaBackend::connect: Invalid Source port:")
925                                 << " (" << src <<")" << endmsg;
926                 return -1;
927         }
928         if (!dst_port) {
929                 PBD::error << _("AlsaBackend::connect: Invalid Destination port:")
930                         << " (" << dst <<")" << endmsg;
931                 return -1;
932         }
933         return src_port->connect (dst_port);
934 }
935
936 int
937 AlsaAudioBackend::disconnect (const std::string& src, const std::string& dst)
938 {
939         AlsaPort* src_port = find_port (src);
940         AlsaPort* dst_port = find_port (dst);
941
942         if (!src_port || !dst_port) {
943                 PBD::error << _("AlsaBackend::disconnect: Invalid Port(s)") << endmsg;
944                 return -1;
945         }
946         return src_port->disconnect (dst_port);
947 }
948
949 int
950 AlsaAudioBackend::connect (PortEngine::PortHandle src, const std::string& dst)
951 {
952         AlsaPort* dst_port = find_port (dst);
953         if (!valid_port (src)) {
954                 PBD::error << _("AlsaBackend::connect: Invalid Source Port Handle") << endmsg;
955                 return -1;
956         }
957         if (!dst_port) {
958                 PBD::error << _("AlsaBackend::connect: Invalid Destination Port")
959                         << " (" << dst << ")" << endmsg;
960                 return -1;
961         }
962         return static_cast<AlsaPort*>(src)->connect (dst_port);
963 }
964
965 int
966 AlsaAudioBackend::disconnect (PortEngine::PortHandle src, const std::string& dst)
967 {
968         AlsaPort* dst_port = find_port (dst);
969         if (!valid_port (src) || !dst_port) {
970                 PBD::error << _("AlsaBackend::disconnect: Invalid Port(s)") << endmsg;
971                 return -1;
972         }
973         return static_cast<AlsaPort*>(src)->disconnect (dst_port);
974 }
975
976 int
977 AlsaAudioBackend::disconnect_all (PortEngine::PortHandle port)
978 {
979         if (!valid_port (port)) {
980                 PBD::error << _("AlsaBackend::disconnect_all: Invalid Port") << endmsg;
981                 return -1;
982         }
983         static_cast<AlsaPort*>(port)->disconnect_all ();
984         return 0;
985 }
986
987 bool
988 AlsaAudioBackend::connected (PortEngine::PortHandle port, bool /* process_callback_safe*/)
989 {
990         if (!valid_port (port)) {
991                 PBD::error << _("AlsaBackend::disconnect_all: Invalid Port") << endmsg;
992                 return false;
993         }
994         return static_cast<AlsaPort*>(port)->is_connected ();
995 }
996
997 bool
998 AlsaAudioBackend::connected_to (PortEngine::PortHandle src, const std::string& dst, bool /*process_callback_safe*/)
999 {
1000         AlsaPort* dst_port = find_port (dst);
1001         if (!valid_port (src) || !dst_port) {
1002                 PBD::error << _("AlsaBackend::connected_to: Invalid Port") << endmsg;
1003                 return false;
1004         }
1005         return static_cast<AlsaPort*>(src)->is_connected (dst_port);
1006 }
1007
1008 bool
1009 AlsaAudioBackend::physically_connected (PortEngine::PortHandle port, bool /*process_callback_safe*/)
1010 {
1011         if (!valid_port (port)) {
1012                 PBD::error << _("AlsaBackend::physically_connected: Invalid Port") << endmsg;
1013                 return false;
1014         }
1015         return static_cast<AlsaPort*>(port)->is_physically_connected ();
1016 }
1017
1018 int
1019 AlsaAudioBackend::get_connections (PortEngine::PortHandle port, std::vector<std::string>& names, bool /*process_callback_safe*/)
1020 {
1021         if (!valid_port (port)) {
1022                 PBD::error << _("AlsaBackend::get_connections: Invalid Port") << endmsg;
1023                 return -1;
1024         }
1025
1026         assert (0 == names.size ());
1027
1028         const std::vector<AlsaPort*>& connected_ports = static_cast<AlsaPort*>(port)->get_connections ();
1029
1030         for (std::vector<AlsaPort*>::const_iterator i = connected_ports.begin (); i != connected_ports.end (); ++i) {
1031                 names.push_back ((*i)->name ());
1032         }
1033
1034         return (int)names.size ();
1035 }
1036
1037 /* MIDI */
1038 int
1039 AlsaAudioBackend::midi_event_get (
1040                 pframes_t& timestamp,
1041                 size_t& size, uint8_t** buf, void* port_buffer,
1042                 uint32_t event_index)
1043 {
1044         assert (buf && port_buffer);
1045         AlsaMidiBuffer& source = * static_cast<AlsaMidiBuffer*>(port_buffer);
1046         if (event_index >= source.size ()) {
1047                 return -1;
1048         }
1049         AlsaMidiEvent * const event = source[event_index].get ();
1050
1051         timestamp = event->timestamp ();
1052         size = event->size ();
1053         *buf = event->data ();
1054         return 0;
1055 }
1056
1057 int
1058 AlsaAudioBackend::midi_event_put (
1059                 void* port_buffer,
1060                 pframes_t timestamp,
1061                 const uint8_t* buffer, size_t size)
1062 {
1063         assert (buffer && port_buffer);
1064         AlsaMidiBuffer& dst = * static_cast<AlsaMidiBuffer*>(port_buffer);
1065         if (dst.size () && (pframes_t)dst.back ()->timestamp () > timestamp) {
1066                 fprintf (stderr, "AlsaMidiBuffer: it's too late for this event. %d > %d\n",
1067                                 (pframes_t)dst.back ()->timestamp (), timestamp);
1068                 return -1;
1069         }
1070         dst.push_back (boost::shared_ptr<AlsaMidiEvent>(new AlsaMidiEvent (timestamp, buffer, size)));
1071         return 0;
1072 }
1073
1074 uint32_t
1075 AlsaAudioBackend::get_midi_event_count (void* port_buffer)
1076 {
1077         assert (port_buffer);
1078         return static_cast<AlsaMidiBuffer*>(port_buffer)->size ();
1079 }
1080
1081 void
1082 AlsaAudioBackend::midi_clear (void* port_buffer)
1083 {
1084         assert (port_buffer);
1085         AlsaMidiBuffer * buf = static_cast<AlsaMidiBuffer*>(port_buffer);
1086         assert (buf);
1087         buf->clear ();
1088 }
1089
1090 /* Monitoring */
1091
1092 bool
1093 AlsaAudioBackend::can_monitor_input () const
1094 {
1095         return false;
1096 }
1097
1098 int
1099 AlsaAudioBackend::request_input_monitoring (PortEngine::PortHandle, bool)
1100 {
1101         return -1;
1102 }
1103
1104 int
1105 AlsaAudioBackend::ensure_input_monitoring (PortEngine::PortHandle, bool)
1106 {
1107         return -1;
1108 }
1109
1110 bool
1111 AlsaAudioBackend::monitoring_input (PortEngine::PortHandle)
1112 {
1113         return false;
1114 }
1115
1116 /* Latency management */
1117
1118 void
1119 AlsaAudioBackend::set_latency_range (PortEngine::PortHandle port, bool for_playback, LatencyRange latency_range)
1120 {
1121         if (!valid_port (port)) {
1122                 PBD::error << _("AlsaPort::set_latency_range (): invalid port.") << endmsg;
1123         }
1124         static_cast<AlsaPort*>(port)->set_latency_range (latency_range, for_playback);
1125 }
1126
1127 LatencyRange
1128 AlsaAudioBackend::get_latency_range (PortEngine::PortHandle port, bool for_playback)
1129 {
1130         if (!valid_port (port)) {
1131                 PBD::error << _("AlsaPort::get_latency_range (): invalid port.") << endmsg;
1132                 LatencyRange r;
1133                 r.min = 0;
1134                 r.max = 0;
1135                 return r;
1136         }
1137         return static_cast<AlsaPort*>(port)->latency_range (for_playback);
1138 }
1139
1140 /* Discovering physical ports */
1141
1142 bool
1143 AlsaAudioBackend::port_is_physical (PortEngine::PortHandle port) const
1144 {
1145         if (!valid_port (port)) {
1146                 PBD::error << _("AlsaPort::port_is_physical (): invalid port.") << endmsg;
1147                 return false;
1148         }
1149         return static_cast<AlsaPort*>(port)->is_physical ();
1150 }
1151
1152 void
1153 AlsaAudioBackend::get_physical_outputs (DataType type, std::vector<std::string>& port_names)
1154 {
1155         for (size_t i = 0; i < _ports.size (); ++i) {
1156                 AlsaPort* port = _ports[i];
1157                 if ((port->type () == type) && port->is_input () && port->is_physical ()) {
1158                         port_names.push_back (port->name ());
1159                 }
1160         }
1161 }
1162
1163 void
1164 AlsaAudioBackend::get_physical_inputs (DataType type, std::vector<std::string>& port_names)
1165 {
1166         for (size_t i = 0; i < _ports.size (); ++i) {
1167                 AlsaPort* port = _ports[i];
1168                 if ((port->type () == type) && port->is_output () && port->is_physical ()) {
1169                         port_names.push_back (port->name ());
1170                 }
1171         }
1172 }
1173
1174 ChanCount
1175 AlsaAudioBackend::n_physical_outputs () const
1176 {
1177         int n_midi = 0;
1178         int n_audio = 0;
1179         for (size_t i = 0; i < _ports.size (); ++i) {
1180                 AlsaPort* port = _ports[i];
1181                 if (port->is_output () && port->is_physical ()) {
1182                         switch (port->type ()) {
1183                                 case DataType::AUDIO: ++n_audio; break;
1184                                 case DataType::MIDI: ++n_midi; break;
1185                                 default: break;
1186                         }
1187                 }
1188         }
1189         ChanCount cc;
1190         cc.set (DataType::AUDIO, n_audio);
1191         cc.set (DataType::MIDI, n_midi);
1192         return cc;
1193 }
1194
1195 ChanCount
1196 AlsaAudioBackend::n_physical_inputs () const
1197 {
1198         int n_midi = 0;
1199         int n_audio = 0;
1200         for (size_t i = 0; i < _ports.size (); ++i) {
1201                 AlsaPort* port = _ports[i];
1202                 if (port->is_input () && port->is_physical ()) {
1203                         switch (port->type ()) {
1204                                 case DataType::AUDIO: ++n_audio; break;
1205                                 case DataType::MIDI: ++n_midi; break;
1206                                 default: break;
1207                         }
1208                 }
1209         }
1210         ChanCount cc;
1211         cc.set (DataType::AUDIO, n_audio);
1212         cc.set (DataType::MIDI, n_midi);
1213         return cc;
1214 }
1215
1216 /* Getting access to the data buffer for a port */
1217
1218 void*
1219 AlsaAudioBackend::get_buffer (PortEngine::PortHandle port, pframes_t nframes)
1220 {
1221         assert (port);
1222         assert (valid_port (port));
1223         return static_cast<AlsaPort*>(port)->get_buffer (nframes);
1224 }
1225
1226 /* Engine Process */
1227 void *
1228 AlsaAudioBackend::main_process_thread ()
1229 {
1230         AudioEngine::thread_init_callback (this);
1231         _active = true;
1232         _processed_samples = 0;
1233
1234         uint64_t clock1, clock2;
1235         clock1 = g_get_monotonic_time();
1236         _pcmi->pcm_start ();
1237         int no_proc_errors = 0;
1238
1239         while (_run) {
1240                 long nr;
1241                 bool xrun = false;
1242                 if (!_freewheeling) {
1243                         nr = _pcmi->pcm_wait ();
1244
1245                         if (_pcmi->state () > 0) {
1246                                 ++no_proc_errors;
1247                                 xrun = true;
1248                         }
1249                         if (_pcmi->state () < 0 || no_proc_errors > 50) {
1250                                 PBD::error << _("AlsaAudioBackend: I/O error. Audio Process Terminated.") << endmsg;
1251                                 break;
1252                         }
1253                         while (nr >= (long)_samples_per_period) {
1254                                 uint32_t i = 0;
1255                                 clock1 = g_get_monotonic_time();
1256                                 no_proc_errors = 0;
1257
1258                                 _pcmi->capt_init (_samples_per_period);
1259                                 for (std::vector<AlsaPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it, ++i) {
1260                                         _pcmi->capt_chan (i, (float*)((*it)->get_buffer(_samples_per_period)), _samples_per_period);
1261                                 }
1262                                 _pcmi->capt_done (_samples_per_period);
1263
1264                                 /* de-queue midi*/
1265                                 i = 0;
1266                                 for (std::vector<AlsaPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it, ++i) {
1267                                         assert (_rmidi_in.size() > i);
1268                                         AlsaRawMidiIn *rm = static_cast<AlsaRawMidiIn*>(_rmidi_in.at(i));
1269                                         void *bptr = (*it)->get_buffer(0);
1270                                         pframes_t time;
1271                                         uint8_t data[64]; // match MaxAlsaRawEventSize in alsa_rawmidi.cc
1272                                         size_t size = sizeof(data);
1273                                         midi_clear(bptr);
1274                                         while (rm->recv_event (time, data, size)) {
1275                                                 midi_event_put(bptr, time, data, size);
1276                                                 size = sizeof(data);
1277                                         }
1278                                         rm->sync_time (clock1);
1279                                 }
1280
1281                                 for (std::vector<AlsaPort*>::const_iterator it = _system_outputs.begin (); it != _system_outputs.end (); ++it) {
1282                                         memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample));
1283                                 }
1284
1285                                 if (engine.process_callback (_samples_per_period)) {
1286                                         _pcmi->pcm_stop ();
1287                                         _active = false;
1288                                         return 0;
1289                                 }
1290
1291                                 /* queue midi*/
1292                                 i = 0;
1293                                 for (std::vector<AlsaPort*>::const_iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) {
1294                                         assert (_rmidi_out.size() > i);
1295                                         AlsaRawMidiOut *rm = static_cast<AlsaRawMidiOut*>(_rmidi_out.at(i));
1296                                         const AlsaMidiBuffer *src = static_cast<const AlsaMidiBuffer*>((*it)->get_buffer(0));
1297                                         rm->sync_time (clock1); // ?? use clock pre DSP load?
1298                                         for (AlsaMidiBuffer::const_iterator mit = src->begin (); mit != src->end (); ++mit) {
1299                                                 rm->send_event ((*mit)->timestamp(), (*mit)->data(), (*mit)->size());
1300                                         }
1301                                 }
1302
1303                                 /* write back audio */
1304                                 i = 0;
1305                                 _pcmi->play_init (_samples_per_period);
1306                                 for (std::vector<AlsaPort*>::const_iterator it = _system_outputs.begin (); it != _system_outputs.end (); ++it, ++i) {
1307                                         _pcmi->play_chan (i, (const float*)(*it)->get_buffer (_samples_per_period), _samples_per_period);
1308                                 }
1309                                 for (; i < _pcmi->nplay (); ++i) {
1310                                         _pcmi->clear_chan (i, _samples_per_period);
1311                                 }
1312                                 _pcmi->play_done (_samples_per_period);
1313                                 nr -= _samples_per_period;
1314                                 _processed_samples += _samples_per_period;
1315
1316                                 /* calculate DSP load */
1317                                 clock2 = g_get_monotonic_time();
1318                                 const int64_t elapsed_time = clock2 - clock1;
1319                                 const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate;
1320                                 _dsp_load = elapsed_time / (float) nomial_time;
1321                         }
1322
1323                         if (xrun && (_pcmi->capt_xrun() > 0 || _pcmi->play_xrun() > 0)) {
1324                                 engine.Xrun ();
1325 #if 0
1326                                 fprintf(stderr, "ALSA x-run read: %.1f ms, write: %.1f ms\n",
1327                                                 _pcmi->capt_xrun() * 1000.0, _pcmi->play_xrun() * 1000.0);
1328 #endif
1329                         }
1330                 } else {
1331                         // Freewheelin'
1332                         for (std::vector<AlsaPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it) {
1333                                 memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample));
1334                         }
1335                         for (std::vector<AlsaPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it) {
1336                                 static_cast<AlsaMidiBuffer*>((*it)->get_buffer(0))->clear ();
1337                         }
1338
1339                         if (engine.process_callback (_samples_per_period)) {
1340                                 _pcmi->pcm_stop ();
1341                                 return 0;
1342                         }
1343                         _dsp_load = 1.0;
1344                         Glib::usleep (100); // don't hog cpu
1345                 }
1346
1347                 if (!pthread_mutex_trylock (&_port_callback_mutex)) {
1348                         while (!_port_connection_queue.empty ()) {
1349                                 PortConnectData *c = _port_connection_queue.back ();
1350                                 manager.connect_callback (c->a, c->b, c->c);
1351                                 _port_connection_queue.pop_back ();
1352                                 delete c;
1353                         }
1354                         pthread_mutex_unlock (&_port_callback_mutex);
1355                 }
1356
1357         }
1358         _pcmi->pcm_stop ();
1359         _active = false;
1360         if (_run) {
1361                 engine.halted_callback("ALSA I/O error.");
1362         }
1363         return 0;
1364 }
1365
1366
1367 /******************************************************************************/
1368
1369 static boost::shared_ptr<AlsaAudioBackend> _instance;
1370
1371 static boost::shared_ptr<AudioBackend> backend_factory (AudioEngine& e);
1372 static int instantiate (const std::string& arg1, const std::string& /* arg2 */);
1373 static int deinstantiate ();
1374 static bool already_configured ();
1375
1376 static ARDOUR::AudioBackendInfo _descriptor = {
1377         "Alsa",
1378         instantiate,
1379         deinstantiate,
1380         backend_factory,
1381         already_configured,
1382 };
1383
1384 static boost::shared_ptr<AudioBackend>
1385 backend_factory (AudioEngine& e)
1386 {
1387         if (!_instance) {
1388                 _instance.reset (new AlsaAudioBackend (e, _descriptor));
1389         }
1390         return _instance;
1391 }
1392
1393 static int
1394 instantiate (const std::string& arg1, const std::string& /* arg2 */)
1395 {
1396         s_instance_name = arg1;
1397         return 0;
1398 }
1399
1400 static int
1401 deinstantiate ()
1402 {
1403         _instance.reset ();
1404         return 0;
1405 }
1406
1407 static bool
1408 already_configured ()
1409 {
1410         return false;
1411 }
1412
1413 extern "C" ARDOURBACKEND_API ARDOUR::AudioBackendInfo* descriptor ()
1414 {
1415         return &_descriptor;
1416 }
1417
1418
1419 /******************************************************************************/
1420 AlsaPort::AlsaPort (AlsaAudioBackend &b, const std::string& name, PortFlags flags)
1421         : _alsa_backend (b)
1422         , _name  (name)
1423         , _flags (flags)
1424 {
1425         _capture_latency_range.min = 0;
1426         _capture_latency_range.max = 0;
1427         _playback_latency_range.min = 0;
1428         _playback_latency_range.max = 0;
1429 }
1430
1431 AlsaPort::~AlsaPort () {
1432         disconnect_all ();
1433 }
1434
1435
1436 int AlsaPort::connect (AlsaPort *port)
1437 {
1438         if (!port) {
1439                 PBD::error << _("AlsaPort::connect (): invalid (null) port") << endmsg;
1440                 return -1;
1441         }
1442
1443         if (type () != port->type ()) {
1444                 PBD::error << _("AlsaPort::connect (): wrong port-type") << endmsg;
1445                 return -1;
1446         }
1447
1448         if (is_output () && port->is_output ()) {
1449                 PBD::error << _("AlsaPort::connect (): cannot inter-connect output ports.") << endmsg;
1450                 return -1;
1451         }
1452
1453         if (is_input () && port->is_input ()) {
1454                 PBD::error << _("AlsaPort::connect (): cannot inter-connect input ports.") << endmsg;
1455                 return -1;
1456         }
1457
1458         if (this == port) {
1459                 PBD::error << _("AlsaPort::connect (): cannot self-connect ports.") << endmsg;
1460                 return -1;
1461         }
1462
1463         if (is_connected (port)) {
1464 #if 0 // don't bother to warn about this for now. just ignore it
1465                 PBD::error << _("AlsaPort::connect (): ports are already connected:")
1466                         << " (" << name () << ") -> (" << port->name () << ")"
1467                         << endmsg;
1468 #endif
1469                 return -1;
1470         }
1471
1472         _connect (port, true);
1473         return 0;
1474 }
1475
1476
1477 void AlsaPort::_connect (AlsaPort *port, bool callback)
1478 {
1479         _connections.push_back (port);
1480         if (callback) {
1481                 port->_connect (this, false);
1482                 _alsa_backend.port_connect_callback (name(),  port->name(), true);
1483         }
1484 }
1485
1486 int AlsaPort::disconnect (AlsaPort *port)
1487 {
1488         if (!port) {
1489                 PBD::error << _("AlsaPort::disconnect (): invalid (null) port") << endmsg;
1490                 return -1;
1491         }
1492
1493         if (!is_connected (port)) {
1494                 PBD::error << _("AlsaPort::disconnect (): ports are not connected:")
1495                         << " (" << name () << ") -> (" << port->name () << ")"
1496                         << endmsg;
1497                 return -1;
1498         }
1499         _disconnect (port, true);
1500         return 0;
1501 }
1502
1503 void AlsaPort::_disconnect (AlsaPort *port, bool callback)
1504 {
1505         std::vector<AlsaPort*>::iterator it = std::find (_connections.begin (), _connections.end (), port);
1506
1507         assert (it != _connections.end ());
1508
1509         _connections.erase (it);
1510
1511         if (callback) {
1512                 port->_disconnect (this, false);
1513                 _alsa_backend.port_connect_callback (name(),  port->name(), false);
1514         }
1515 }
1516
1517
1518 void AlsaPort::disconnect_all ()
1519 {
1520         while (!_connections.empty ()) {
1521                 _connections.back ()->_disconnect (this, false);
1522                 _alsa_backend.port_connect_callback (name(),  _connections.back ()->name(), false);
1523                 _connections.pop_back ();
1524         }
1525 }
1526
1527 bool
1528 AlsaPort::is_connected (const AlsaPort *port) const
1529 {
1530         return std::find (_connections.begin (), _connections.end (), port) != _connections.end ();
1531 }
1532
1533 bool AlsaPort::is_physically_connected () const
1534 {
1535         for (std::vector<AlsaPort*>::const_iterator it = _connections.begin (); it != _connections.end (); ++it) {
1536                 if ((*it)->is_physical ()) {
1537                         return true;
1538                 }
1539         }
1540         return false;
1541 }
1542
1543 /******************************************************************************/
1544
1545 AlsaAudioPort::AlsaAudioPort (AlsaAudioBackend &b, const std::string& name, PortFlags flags)
1546         : AlsaPort (b, name, flags)
1547 {
1548         memset (_buffer, 0, sizeof (_buffer));
1549         mlock(_buffer, sizeof (_buffer));
1550 }
1551
1552 AlsaAudioPort::~AlsaAudioPort () { }
1553
1554 void* AlsaAudioPort::get_buffer (pframes_t n_samples)
1555 {
1556         if (is_input ()) {
1557                 std::vector<AlsaPort*>::const_iterator it = get_connections ().begin ();
1558                 if (it == get_connections ().end ()) {
1559                         memset (_buffer, 0, n_samples * sizeof (Sample));
1560                 } else {
1561                         AlsaAudioPort const * source = static_cast<const AlsaAudioPort*>(*it);
1562                         assert (source && source->is_output ());
1563                         memcpy (_buffer, source->const_buffer (), n_samples * sizeof (Sample));
1564                         while (++it != get_connections ().end ()) {
1565                                 source = static_cast<const AlsaAudioPort*>(*it);
1566                                 assert (source && source->is_output ());
1567                                 Sample* dst = buffer ();
1568                                 const Sample* src = source->const_buffer ();
1569                                 for (uint32_t s = 0; s < n_samples; ++s, ++dst, ++src) {
1570                                         *dst += *src;
1571                                 }
1572                         }
1573                 }
1574         }
1575         return _buffer;
1576 }
1577
1578
1579 AlsaMidiPort::AlsaMidiPort (AlsaAudioBackend &b, const std::string& name, PortFlags flags)
1580         : AlsaPort (b, name, flags)
1581 {
1582         _buffer.clear ();
1583 }
1584
1585 AlsaMidiPort::~AlsaMidiPort () { }
1586
1587 struct MidiEventSorter {
1588         bool operator() (const boost::shared_ptr<AlsaMidiEvent>& a, const boost::shared_ptr<AlsaMidiEvent>& b) {
1589                 return *a < *b;
1590         }
1591 };
1592
1593 void* AlsaMidiPort::get_buffer (pframes_t /* nframes */)
1594 {
1595         if (is_input ()) {
1596                 _buffer.clear ();
1597                 for (std::vector<AlsaPort*>::const_iterator i = get_connections ().begin ();
1598                                 i != get_connections ().end ();
1599                                 ++i) {
1600                         const AlsaMidiBuffer src = static_cast<const AlsaMidiPort*>(*i)->const_buffer ();
1601                         for (AlsaMidiBuffer::const_iterator it = src.begin (); it != src.end (); ++it) {
1602                                 _buffer.push_back (boost::shared_ptr<AlsaMidiEvent>(new AlsaMidiEvent (**it)));
1603                         }
1604                 }
1605                 std::sort (_buffer.begin (), _buffer.end (), MidiEventSorter());
1606         }
1607         return &_buffer;
1608 }
1609
1610 AlsaMidiEvent::AlsaMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size)
1611         : _size (size)
1612         , _timestamp (timestamp)
1613         , _data (0)
1614 {
1615         if (size > 0) {
1616                 _data = (uint8_t*) malloc (size);
1617                 memcpy (_data, data, size);
1618         }
1619 }
1620
1621 AlsaMidiEvent::AlsaMidiEvent (const AlsaMidiEvent& other)
1622         : _size (other.size ())
1623         , _timestamp (other.timestamp ())
1624         , _data (0)
1625 {
1626         if (other.size () && other.const_data ()) {
1627                 _data = (uint8_t*) malloc (other.size ());
1628                 memcpy (_data, other.const_data (), other.size ());
1629         }
1630 };
1631
1632 AlsaMidiEvent::~AlsaMidiEvent () {
1633         free (_data);
1634 };