#include <sys/time.h>
#include <time.h>
+#include "pbd/localtime_r.h"
+#include "pbd/timersub.h"
+
#include "midi++/parser.h"
- #include "midi++/manager.h"
+
+ #include "ardour/async_midi_port.h"
+ #include "ardour/midi_port.h"
+ #include "ardour/audioengine.h"
#include "midi_tracer.h"
#include "gui_thread.h"
--- /dev/null
- int write (const MIDI::byte *msg, size_t msglen, MIDI::timestamp_t timestamp);
+ /*
+ Copyright (C) 1998-2010 Paul Barton-Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+ #ifndef __libardour_async_midiport_h__
+ #define __libardour_async_midiport_h__
+
+ #include <string>
+ #include <iostream>
+
+ #include "pbd/xml++.h"
+ #include "pbd/crossthread.h"
+ #include "pbd/signals.h"
+ #include "pbd/ringbuffer.h"
+
+ #include "evoral/Event.hpp"
+ #include "evoral/EventRingBuffer.hpp"
+
+ #include "midi++/types.h"
+ #include "midi++/parser.h"
+ #include "midi++/port.h"
+
+ #include "ardour/midi_port.h"
+
+ namespace ARDOUR {
+
+ class AsyncMIDIPort : public ARDOUR::MidiPort, public MIDI::Port {
+
+ public:
+ AsyncMIDIPort (std::string const &, PortFlags);
+ ~AsyncMIDIPort ();
+
+ /* called from an RT context */
+
+ void cycle_start (pframes_t nframes);
+ void cycle_end (pframes_t nframes);
+
+ /* called from non-RT context */
+
+ void parse (framecnt_t timestamp);
- int selectable () const { return xthread.selectable(); }
++ int write (const MIDI::byte *msg, size_t msglen, MIDI::timestamp_t timestamp);
+ int read (MIDI::byte *buf, size_t bufsize);
+ void drain (int check_interval_usecs);
- Glib::Threads::Mutex output_fifo_lock;
- CrossThreadChannel xthread;
++ int selectable () const {
++#ifdef PLATFORM_WINDOWS
++ return false;
++#else
++ return xthread.selectable();
++#endif
++ }
+
+ static void set_process_thread (pthread_t);
+ static pthread_t get_process_thread () { return _process_thread; }
+ static bool is_process_thread();
+
+ private:
+ bool _currently_in_cycle;
+ MIDI::timestamp_t _last_write_timestamp;
+ RingBuffer< Evoral::Event<double> > output_fifo;
+ Evoral::EventRingBuffer<MIDI::timestamp_t> input_fifo;
++ Glib::Threads::Mutex output_fifo_lock;
++#ifndef PLATFORM_WINDOWS
++ CrossThreadChannel xthread;
++#endif
++
++ int create_port ();
++
++ /** Channel used to signal to the MidiControlUI that input has arrived */
++
++ std::string _connections;
++ PBD::ScopedConnection connect_connection;
++ PBD::ScopedConnection halt_connection;
++ void flush (void* jack_port_buffer);
++ void jack_halted ();
++ void make_connections ();
++ void init (std::string const &, Flags);
+
+ void flush_output_fifo (pframes_t);
+
+ static pthread_t _process_thread;
+ };
+
+ } // namespace ARDOUR
+
+ #endif /* __libardour_async_midiport_h__ */
--- /dev/null
- PBD::SearchPath backend_search_path ();
+ /*
+ Copyright (C) 2011 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+ #ifndef __ardour_backend_search_path_h__
+ #define __ardour_backend_search_path_h__
+
+ #include "pbd/search_path.h"
+
+ namespace ARDOUR {
+
+ /**
+ * return a SearchPath containing directories in which to look for
+ * backend plugins.
+ *
+ * If ARDOUR_BACKEND_PATH is defined then the SearchPath returned
+ * will contain only those directories specified in it, otherwise it will
+ * contain the user and system directories which may contain audio/MIDI
+ * backends.
+ */
++ PBD::Searchpath backend_search_path ();
+
+ } // namespace ARDOUR
+
+ #endif /* __ardour_backend_search_path_h__ */
extern const char* const templates_dir_name;
extern const char* const route_templates_dir_name;
extern const char* const surfaces_dir_name;
+extern const char* const ladspa_dir_name;
extern const char* const user_config_dir_name;
extern const char* const panner_dir_name;
+ extern const char* const backend_dir_name;
};
--- /dev/null
- template class EventRingBuffer<timestamp_t>;
+ /*
+ Copyright (C) 1998 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+ */
+
+ #include <iostream>
+
+ #include "pbd/error.h"
+ #include "pbd/stacktrace.h"
+
+ #include "midi++/types.h"
+
+ #include "ardour/async_midi_port.h"
+ #include "ardour/audioengine.h"
+ #include "ardour/midi_buffer.h"
+
+ using namespace MIDI;
+ using namespace ARDOUR;
+ using namespace std;
+ using namespace PBD;
+
+ namespace Evoral {
-AsyncMIDIPort::write (const byte * msg, size_t msglen, timestamp_t timestamp)
++ template class EventRingBuffer<MIDI::timestamp_t>;
+ }
+
+ pthread_t AsyncMIDIPort::_process_thread;
+
+ #define port_engine AudioEngine::instance()->port_engine()
+
+ AsyncMIDIPort::AsyncMIDIPort (string const & name, PortFlags flags)
+ : MidiPort (name, flags)
+ , MIDI::Port (name, MIDI::Port::Flags (0))
+ , _currently_in_cycle (false)
+ , _last_write_timestamp (0)
+ , output_fifo (512)
+ , input_fifo (1024)
+ , xthread (true)
+ {
+ }
+
+ AsyncMIDIPort::~AsyncMIDIPort ()
+ {
+ }
+
+ void
+ AsyncMIDIPort::flush_output_fifo (pframes_t nframes)
+ {
+ RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0 } };
+ size_t written;
+
+ output_fifo.get_read_vector (&vec);
+
+ MidiBuffer& mb (get_midi_buffer (nframes));
+
+ if (vec.len[0]) {
+ Evoral::Event<double>* evp = vec.buf[0];
+
+ for (size_t n = 0; n < vec.len[0]; ++n, ++evp) {
+ mb.push_back (evp->time(), evp->size(), evp->buffer());
+ }
+ }
+
+ if (vec.len[1]) {
+ Evoral::Event<double>* evp = vec.buf[1];
+
+ for (size_t n = 0; n < vec.len[1]; ++n, ++evp) {
+ mb.push_back (evp->time(), evp->size(), evp->buffer());
+ }
+ }
+
+ if ((written = vec.len[0] + vec.len[1]) != 0) {
+ output_fifo.increment_read_idx (written);
+ }
+ }
+
+ void
+ AsyncMIDIPort::cycle_start (pframes_t nframes)
+ {
+ _currently_in_cycle = true;
+ MidiPort::cycle_start (nframes);
+
+ /* dump anything waiting in the output FIFO at the start of the port
+ * buffer
+ */
+
+ if (ARDOUR::Port::sends_output()) {
+ flush_output_fifo (nframes);
+ }
+
+ /* copy incoming data from the port buffer into the input FIFO
+ and if necessary wakeup the reader
+ */
+
+ if (ARDOUR::Port::receives_input()) {
+ MidiBuffer& mb (get_midi_buffer (nframes));
+ pframes_t when = AudioEngine::instance()->sample_time_at_cycle_start();
+
+ for (MidiBuffer::iterator b = mb.begin(); b != mb.end(); ++b) {
+ input_fifo.write (when, (Evoral::EventType) 0, (*b).size(), (*b).buffer());
+ }
+
+ if (!mb.empty()) {
+ xthread.wakeup ();
+ }
+ }
+
+ }
+
+ void
+ AsyncMIDIPort::cycle_end (pframes_t nframes)
+ {
+ if (ARDOUR::Port::sends_output()) {
+ /* move any additional data from output FIFO into the port
+ buffer.
+ */
+ flush_output_fifo (nframes);
+ }
+
+ MidiPort::cycle_end (nframes);
+
+ _currently_in_cycle = false;
+ }
+
+ /** wait for the output FIFO to be emptied by successive process() callbacks.
+ *
+ * Cannot be called from a processing thread.
+ */
+ void
+ AsyncMIDIPort::drain (int check_interval_usecs)
+ {
+ RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
+
+ if (!AudioEngine::instance()->running() || AudioEngine::instance()->session() == 0) {
+ /* no more process calls - it will never drain */
+ return;
+ }
+
+
+ if (is_process_thread()) {
+ error << "Process thread called MIDI::AsyncMIDIPort::drain() - this cannot work" << endmsg;
+ return;
+ }
+
+ while (1) {
+ output_fifo.get_write_vector (&vec);
+ if (vec.len[0] + vec.len[1] >= output_fifo.bufsize() - 1) {
+ break;
+ }
+ usleep (check_interval_usecs);
+ }
+ }
+
+ int
- std::cerr << "attempting to write MIDI event of " << msglen << " bytes at time "
++AsyncMIDIPort::write (const MIDI::byte * msg, size_t msglen, MIDI::timestamp_t timestamp)
+ {
+ int ret = 0;
+
+ if (!ARDOUR::Port::sends_output()) {
+ return ret;
+ }
+
+ if (!is_process_thread()) {
+
+ /* this is the best estimate of "when" this MIDI data is being
+ * delivered
+ */
+
+ _parser->set_timestamp (AudioEngine::instance()->sample_time() + timestamp);
+ for (size_t n = 0; n < msglen; ++n) {
+ _parser->scanner (msg[n]);
+ }
+
+ Glib::Threads::Mutex::Lock lm (output_fifo_lock);
+ RingBuffer< Evoral::Event<double> >::rw_vector vec = { { 0, 0 }, { 0, 0} };
+
+ output_fifo.get_write_vector (&vec);
+
+ if (vec.len[0] + vec.len[1] < 1) {
+ error << "no space in FIFO for non-process thread MIDI write" << endmsg;
+ return 0;
+ }
+
+ if (vec.len[0]) {
+ if (!vec.buf[0]->owns_buffer()) {
+ vec.buf[0]->set_buffer (0, 0, true);
+ }
+ vec.buf[0]->set (msg, msglen, timestamp);
+ } else {
+ if (!vec.buf[1]->owns_buffer()) {
+ vec.buf[1]->set_buffer (0, 0, true);
+ }
+ vec.buf[1]->set (msg, msglen, timestamp);
+ }
+
+ output_fifo.increment_write_idx (1);
+
+ ret = msglen;
+
+ } else {
+
+ _parser->set_timestamp (AudioEngine::instance()->sample_time_at_cycle_start() + timestamp);
+ for (size_t n = 0; n < msglen; ++n) {
+ _parser->scanner (msg[n]);
+ }
+
+ if (timestamp >= _cycle_nframes) {
- byte buffer[input_fifo.capacity()];
++ std::cerr << "attempting to write MIDI event of " << msglen << " MIDI::bytes at time "
+ << timestamp << " of " << _cycle_nframes
+ << " (this will not work - needs a code fix)"
+ << std::endl;
+ }
+
+ /* This is the process thread, which makes checking
+ * _currently_in_cycle atomic and safe, since it is only
+ * set from cycle_start() and cycle_end(), also called
+ * only from the process thread.
+ */
+
+ if (_currently_in_cycle) {
+
+ MidiBuffer& mb (get_midi_buffer (_cycle_nframes));
+
+ if (timestamp == 0) {
+ timestamp = _last_write_timestamp;
+ }
+
+ if (mb.push_back (timestamp, msglen, msg)) {
+ ret = msglen;
+ _last_write_timestamp = timestamp;
+
+ } else {
+ cerr << "AsyncMIDIPort (" << ARDOUR::Port::name() << "): write of " << msglen << " @ " << timestamp << " failed\n" << endl;
+ PBD::stacktrace (cerr, 20);
+ ret = 0;
+ }
+ } else {
+ cerr << "write to JACK midi port failed: not currently in a process cycle." << endl;
+ PBD::stacktrace (cerr, 20);
+ }
+ }
+
+ return ret;
+ }
+
+
+ int
+ AsyncMIDIPort::read (MIDI::byte *, size_t)
+ {
+ if (!ARDOUR::Port::receives_input()) {
+ return 0;
+ }
+
+ timestamp_t time;
+ Evoral::EventType type;
+ uint32_t size;
++ MIDI::byte buffer[input_fifo.capacity()];
+
+ while (input_fifo.read (&time, &type, &size, buffer)) {
+ _parser->set_timestamp (time);
+ for (uint32_t i = 0; i < size; ++i) {
+ _parser->scanner (buffer[i]);
+ }
+ }
+
+ return 0;
+ }
+
+ void
+ AsyncMIDIPort::parse (framecnt_t)
+ {
+ MIDI::byte buf[1];
+
+ /* see ::read() to realize why buf is not used */
+ read (buf, sizeof (buf));
+ }
+
+ void
+ AsyncMIDIPort::set_process_thread (pthread_t thr)
+ {
+ _process_thread = thr;
+ }
+
+ bool
+ AsyncMIDIPort::is_process_thread()
+ {
+ return pthread_equal (pthread_self(), _process_thread);
+ }
+
#include "pbd/pthread_utils.h"
#include "pbd/stacktrace.h"
#include "pbd/unknown_type.h"
- #include "pbd/epa.h"
-#include <jack/weakjack.h>
-
#include "midi++/port.h"
- #include "midi++/jack_midi_port.h"
#include "midi++/mmc.h"
- #include "midi++/manager.h"
+ #include "ardour/async_midi_port.h"
#include "ardour/audio_port.h"
+ #include "ardour/audio_backend.h"
#include "ardour/audioengine.h"
+ #include "ardour/backend_search_path.h"
#include "ardour/buffer.h"
#include "ardour/cycle_timer.h"
#include "ardour/internal_send.h"
--- /dev/null
-SearchPath
+ /*
+ Copyright (C) 2013 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+ #include <glibmm/miscutils.h>
+
+ #include "ardour/backend_search_path.h"
+ #include "ardour/directory_names.h"
+ #include "ardour/filesystem_paths.h"
+
+ namespace {
+ const char * const backend_env_variable_name = "ARDOUR_BACKEND_PATH";
+ } // anonymous
+
+ using namespace PBD;
+
+ namespace ARDOUR {
+
- SearchPath spath(user_config_directory ());
++Searchpath
+ backend_search_path ()
+ {
- spath += SearchPath(Glib::getenv(backend_env_variable_name));
++ Searchpath spath(user_config_directory ());
+ spath += ardour_dll_directory ();
+ spath.add_subdirectory_to_paths(backend_dir_name);
+
++ spath += Searchpath(Glib::getenv(backend_env_variable_name));
+ return spath;
+ }
+
+ } // namespace ARDOUR
const char* const templates_dir_name = X_("templates");
const char* const route_templates_dir_name = X_("route_templates");
const char* const surfaces_dir_name = X_("surfaces");
+const char* const ladspa_dir_name = X_("ladspa");
const char* const panner_dir_name = X_("panners");
+ const char* const backend_dir_name = X_("backends");
/* these should end up using variants of PROGRAM_NAME */
#ifdef __APPLE__
// allow up to 4 digits for the output port number, plus the slash, suffix and extra space
- limit = name_size - _session.engine().client_name().length() - (suffix.length() + 5);
+ limit = name_size - AudioEngine::instance()->my_name().length() - (suffix.length() + 5);
- char buf1[name_size+1];
- char buf2[name_size+1];
+ std::vector<char> buf1(name_size+1);
+ std::vector<char> buf2(name_size+1);
/* colons are illegal in port names, so fix that */
*/
for (n = 1; n < 9999; ++n) {
- std::vector<char> buf(jack_port_name_size());
- size_t size = AudioEngine::instance()->port_name_size() + 1;
- char buf[size];
++ std::vector<char> buf (AudioEngine::instance()->port_name_size());
PortSet::iterator i = _ports.begin();
- snprintf (buf, size, _("%s %u"), base, n);
+ snprintf (&buf[0], jack_port_name_size(), _("%s %u"), base, n);
for ( ; i != _ports.end(); ++i) {
- if (i->name() == buf) {
+ if (string(i->name()) == string(&buf[0])) {
break;
}
}
if (ioc & IO_IN) {
+#ifndef PLATFORM_WINDOWS
CrossThreadChannel::drain (port->selectable());
+#endif
- DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", port->name()));
- framepos_t now = _session.engine().frame_time();
+ DEBUG_TRACE (DEBUG::MidiIO, string_compose ("data available on %1\n", ((ARDOUR::Port*)port)->name()));
+ framepos_t now = _session.engine().sample_time();
port->parse (now);
}
#include <unistd.h>
#include "pbd/error.h"
+#include "pbd/pthread_utils.h"
- #include "midi++/port.h"
+ #include "ardour/audioengine.h"
#include "ardour/debug.h"
- #include "ardour/slave.h"
+ #include "ardour/midi_buffer.h"
+ #include "ardour/midi_port.h"
#include "ardour/session.h"
- #include "ardour/audioengine.h"
+ #include "ardour/slave.h"
+#include <glibmm/timer.h>
+
#include "i18n.h"
using namespace std;
assert (msg_time < end_frame);
/* convert from session frames back to JACK frames using the transport speed */
- pframes_t const out_stamp = (msg_time - start_frame) / _transport_speed;
+ ARDOUR::pframes_t const out_stamp = (msg_time - start_frame) / _transport_speed;
assert (out_stamp < nframes);
- if (MIDI::Manager::instance()->mtc_output_port()->midimsg (mtc_msg, 2, out_stamp)) {
+ MidiBuffer& mb (_midi_ports->mtc_output_port()->get_midi_buffer(nframes));
+ if (!mb.push_back (out_stamp, 2, mtc_msg)) {
error << string_compose(_("Session: cannot send quarter-frame MTC message (%1)"), strerror (errno))
- << endmsg;
+ << endmsg;
return -1;
}
from waflib.extras import autowaf as autowaf
from waflib import Options
import os
+import sys
import re
import subprocess
+ import sys
# default state file version for this build
CURRENT_SESSION_FILE_VERSION = 3001
#include <map>
#include "timecode/time.h"
+
#include "pbd/error.h"
+
#include "midi++/mmc.h"
#include "midi++/port.h"
- #include "midi++/jack_midi_port.h"
#include "midi++/parser.h"
- #include "midi++/manager.h"
+#ifndef __INT_MAX__ // 'ssize_t' won't be defined yet
+typedef long ssize_t;
+#endif
+
using namespace std;
using namespace MIDI;
using namespace PBD;
#include "pbd/xml++.h"
#include "pbd/stacktrace.h"
+#include "midi++/types.h" // Added by JE - 06-01-2009. All instances of 'byte' changed to 'MIDI::byte' (for clarification)
+#include "midi++/port.h"
#include "midi++/channel.h"
+ #include "ardour/async_midi_port.h"
#include "ardour/automation_control.h"
#include "ardour/midi_ui.h"
#include "ardour/utils.h"