mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-28 09:27:39 +01:00
src-tree cleanup: separate surfaces from libraries
libardourcp and now libardour_midisurface are not control surfaces, but helper libraries for those. They need to be deployed to the library folder (shared between ctrl surfaces) and not scanned as ctrl surfaces at runtime.
This commit is contained in:
parent
22007bf882
commit
d521c2ede6
20 changed files with 8 additions and 11 deletions
113
libs/ctrl-interface/midi_surface/midi_byte_array.cc
Normal file
113
libs/ctrl-interface/midi_surface/midi_byte_array.cc
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Paul Davis <paul@linuxaudiosystems.com>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
MidiByteArray::MidiByteArray (size_t size, MIDI::byte array[])
|
||||
: std::vector<MIDI::byte>()
|
||||
{
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
push_back (array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
MidiByteArray::MidiByteArray (size_t count, MIDI::byte first, ...)
|
||||
: vector<MIDI::byte>()
|
||||
{
|
||||
push_back (first);
|
||||
va_list var_args;
|
||||
va_start (var_args, first);
|
||||
for (size_t i = 1; i < count; ++i)
|
||||
{
|
||||
MIDI::byte b = va_arg (var_args, int);
|
||||
push_back (b);
|
||||
}
|
||||
va_end (var_args);
|
||||
}
|
||||
|
||||
|
||||
void MidiByteArray::copy (size_t count, MIDI::byte * arr)
|
||||
{
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
push_back (arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
MidiByteArray & operator << (MidiByteArray & mba, const MIDI::byte & b)
|
||||
{
|
||||
mba.push_back (b);
|
||||
return mba;
|
||||
}
|
||||
|
||||
MidiByteArray & operator << (MidiByteArray & mba, const MidiByteArray & barr)
|
||||
{
|
||||
std::back_insert_iterator<MidiByteArray> bit (mba);
|
||||
copy (barr.begin(), barr.end(), bit);
|
||||
return mba;
|
||||
}
|
||||
|
||||
std::ostream & operator << (std::ostream & os, const MidiByteArray & mba)
|
||||
{
|
||||
os << "[";
|
||||
char fill = os.fill('0');
|
||||
for (MidiByteArray::const_iterator it = mba.begin(); it != mba.end(); ++it) {
|
||||
if (it != mba.begin()) os << " ";
|
||||
os << std::hex << std::setw(2) << (int)*it;
|
||||
}
|
||||
os.fill (fill);
|
||||
os << std::dec;
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
MidiByteArray & operator << (MidiByteArray & mba, const std::string & st)
|
||||
{
|
||||
/* note that this assumes that "st" is ASCII encoded
|
||||
*/
|
||||
|
||||
mba.insert (mba.end(), st.begin(), st.end());
|
||||
return mba;
|
||||
}
|
||||
|
||||
bool
|
||||
MidiByteArray::compare_n (const MidiByteArray& other, MidiByteArray::size_type n) const
|
||||
{
|
||||
MidiByteArray::const_iterator us = begin();
|
||||
MidiByteArray::const_iterator them = other.begin();
|
||||
|
||||
while (n && us != end() && them != other.end()) {
|
||||
if ((*us) != (*them)) {
|
||||
return false;
|
||||
}
|
||||
--n;
|
||||
++us;
|
||||
++them;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
449
libs/ctrl-interface/midi_surface/midi_surface.cc
Normal file
449
libs/ctrl-interface/midi_surface/midi_surface.cc
Normal file
|
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "pbd/debug.h"
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
#include "ardour/async_midi_port.h"
|
||||
#include "ardour/audioengine.h"
|
||||
#include "ardour/bundle.h"
|
||||
#include "ardour/debug.h"
|
||||
#include "ardour/midiport_manager.h"
|
||||
#include "ardour/midi_port.h"
|
||||
#include "ardour/session.h"
|
||||
|
||||
#include "midi_surface.h"
|
||||
|
||||
using namespace ARDOUR;
|
||||
using namespace Glib;
|
||||
using namespace PBD;
|
||||
|
||||
#include "pbd/abstract_ui.cc" // instantiate template
|
||||
|
||||
MIDISurface::MIDISurface (ARDOUR::Session& s, std::string const & namestr, std::string const & port_prefix, bool use_pad_filter)
|
||||
: ControlProtocol (s, namestr)
|
||||
, AbstractUI<MidiSurfaceRequest> (namestr)
|
||||
, with_pad_filter (use_pad_filter)
|
||||
, _in_use (false)
|
||||
, port_name_prefix (port_prefix)
|
||||
, _connection_state (ConnectionState (0))
|
||||
{
|
||||
}
|
||||
|
||||
MIDISurface::~MIDISurface ()
|
||||
{
|
||||
/* leave it all up to derived classes, because ordering it hard. */
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::port_setup ()
|
||||
{
|
||||
ports_acquire ();
|
||||
|
||||
if (!input_port_name().empty() || !output_port_name().empty()) {
|
||||
ARDOUR::AudioEngine::instance()->PortRegisteredOrUnregistered.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::port_registration_handler, this), this);
|
||||
}
|
||||
ARDOUR::AudioEngine::instance()->PortConnectedOrDisconnected.connect (port_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::connection_handler, this, _1, _2, _3, _4, _5), this);
|
||||
|
||||
port_registration_handler ();
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::drop ()
|
||||
{
|
||||
/* do this before stopping the event loop, so that we don't get any notifications */
|
||||
port_connections.drop_connections ();
|
||||
stop_using_device ();
|
||||
device_release ();
|
||||
ports_release ();
|
||||
}
|
||||
|
||||
int
|
||||
MIDISurface::ports_acquire ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, "acquiring ports\n");
|
||||
|
||||
/* setup ports */
|
||||
|
||||
_async_in = AudioEngine::instance()->register_input_port (DataType::MIDI, string_compose (X_("%1 in"), port_name_prefix), true);
|
||||
_async_out = AudioEngine::instance()->register_output_port (DataType::MIDI, string_compose (X_("%1 out"), port_name_prefix), true);
|
||||
|
||||
if (_async_in == 0 || _async_out == 0) {
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, "cannot register ports\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* We do not add our ports to the input/output bundles because we don't
|
||||
* want users wiring them by hand. They could use JACK tools if they
|
||||
* really insist on that (and use JACK)
|
||||
*/
|
||||
|
||||
_input_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in).get();
|
||||
_output_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_out).get();
|
||||
|
||||
/* Create a shadow port where, depending on the state of the surface,
|
||||
* we will make pad note on/off events appear. The surface code will
|
||||
* automatically this port to the first selected MIDI track.
|
||||
*/
|
||||
|
||||
if (with_pad_filter) {
|
||||
boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->add_shadow_port (string_compose (_("%1 Pads"), port_name_prefix), boost::bind (&MIDISurface::pad_filter, this, _1, _2));
|
||||
boost::shared_ptr<MidiPort> shadow_port = boost::dynamic_pointer_cast<AsyncMIDIPort>(_async_in)->shadow_port();
|
||||
|
||||
if (shadow_port) {
|
||||
|
||||
_output_bundle.reset (new ARDOUR::Bundle (_("Push 2 Pads"), false));
|
||||
|
||||
_output_bundle->add_channel (
|
||||
shadow_port->name(),
|
||||
ARDOUR::DataType::MIDI,
|
||||
session->engine().make_port_name_non_relative (shadow_port->name())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
session->BundleAddedOrRemoved ();
|
||||
|
||||
connect_to_parser ();
|
||||
|
||||
/* Connect input port to event loop */
|
||||
|
||||
AsyncMIDIPort* asp;
|
||||
|
||||
asp = dynamic_cast<AsyncMIDIPort*> (_input_port);
|
||||
asp->xthread().set_receive_handler (sigc::bind (sigc::mem_fun (this, &MIDISurface::midi_input_handler), _input_port));
|
||||
asp->xthread().attach (main_loop()->get_context());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::ports_release ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, "releasing ports\n");
|
||||
|
||||
/* wait for button data to be flushed */
|
||||
AsyncMIDIPort* asp;
|
||||
asp = dynamic_cast<AsyncMIDIPort*> (_output_port);
|
||||
asp->drain (10000, 500000);
|
||||
|
||||
{
|
||||
Glib::Threads::Mutex::Lock em (AudioEngine::instance()->process_lock());
|
||||
AudioEngine::instance()->unregister_port (_async_in);
|
||||
AudioEngine::instance()->unregister_port (_async_out);
|
||||
}
|
||||
|
||||
_async_in.reset ((ARDOUR::Port*) 0);
|
||||
_async_out.reset ((ARDOUR::Port*) 0);
|
||||
_input_port = 0;
|
||||
_output_port = 0;
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::port_registration_handler ()
|
||||
{
|
||||
if (!_async_in || !_async_out) {
|
||||
/* ports not registered yet */
|
||||
return;
|
||||
}
|
||||
|
||||
if (_async_in->connected() && _async_out->connected()) {
|
||||
/* don't waste cycles here */
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> in;
|
||||
std::vector<std::string> out;
|
||||
|
||||
AudioEngine::instance()->get_ports (string_compose (".*%1", input_port_name()), DataType::MIDI, PortFlags (IsPhysical|IsOutput), in);
|
||||
AudioEngine::instance()->get_ports (string_compose (".*%1", output_port_name()), DataType::MIDI, PortFlags (IsPhysical|IsInput), out);
|
||||
|
||||
if (!in.empty() && !out.empty()) {
|
||||
if (!_async_in->connected()) {
|
||||
AudioEngine::instance()->connect (_async_in->name(), in.front());
|
||||
}
|
||||
if (!_async_out->connected()) {
|
||||
AudioEngine::instance()->connect (_async_out->name(), out.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MIDISurface::connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, "FaderPort::connection_handler start\n");
|
||||
if (!_input_port || !_output_port) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ni = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_in)->name());
|
||||
std::string no = ARDOUR::AudioEngine::instance()->make_port_name_non_relative (boost::shared_ptr<ARDOUR::Port>(_async_out)->name());
|
||||
|
||||
if (ni == name1 || ni == name2) {
|
||||
if (yn) {
|
||||
_connection_state |= InputConnected;
|
||||
} else {
|
||||
_connection_state &= ~InputConnected;
|
||||
}
|
||||
} else if (no == name1 || no == name2) {
|
||||
if (yn) {
|
||||
_connection_state |= OutputConnected;
|
||||
} else {
|
||||
_connection_state &= ~OutputConnected;
|
||||
}
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, string_compose ("Connections between %1 and %2 changed, but I ignored it\n", name1, name2));
|
||||
/* not our ports */
|
||||
return false;
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, string_compose ("our ports changed connection state: %1 -> %2 connected ? %3\n",
|
||||
name1, name2, yn));
|
||||
|
||||
if ((_connection_state & (InputConnected|OutputConnected)) == (InputConnected|OutputConnected)) {
|
||||
|
||||
/* XXX this is a horrible hack. Without a short sleep here,
|
||||
something prevents the device wakeup messages from being
|
||||
sent and/or the responses from being received.
|
||||
*/
|
||||
|
||||
g_usleep (100000);
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, "device now connected for both input and output\n");
|
||||
|
||||
/* may not have the device open if it was just plugged
|
||||
in. Really need USB device detection rather than MIDI port
|
||||
detection for this to work well.
|
||||
*/
|
||||
|
||||
device_acquire ();
|
||||
begin_using_device ();
|
||||
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, "Device disconnected (input or output or both) or not yet fully connected\n");
|
||||
stop_using_device ();
|
||||
}
|
||||
|
||||
ConnectionChange (); /* emit signal for our GUI */
|
||||
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, "connection_handler end\n");
|
||||
|
||||
return true; /* connection status changed */
|
||||
}
|
||||
|
||||
boost::shared_ptr<Port>
|
||||
MIDISurface::output_port()
|
||||
{
|
||||
return _async_out;
|
||||
}
|
||||
|
||||
boost::shared_ptr<Port>
|
||||
MIDISurface::input_port()
|
||||
{
|
||||
return _async_in;
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::write (const MidiByteArray& data)
|
||||
{
|
||||
/* immediate delivery */
|
||||
_output_port->write (&data[0], data.size(), 0);
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::write (MIDI::byte const * data, size_t size)
|
||||
{
|
||||
_output_port->write (data, size, 0);
|
||||
}
|
||||
|
||||
bool
|
||||
MIDISurface::midi_input_handler (IOCondition ioc, MIDI::Port* port)
|
||||
{
|
||||
if (ioc & ~IO_IN) {
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, "MIDI port closed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ioc & IO_IN) {
|
||||
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, string_compose ("something happened on %1\n", port->name()));
|
||||
|
||||
AsyncMIDIPort* asp = dynamic_cast<AsyncMIDIPort*>(port);
|
||||
if (asp) {
|
||||
asp->clear ();
|
||||
}
|
||||
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, string_compose ("data available on %1\n", port->name()));
|
||||
if (_in_use) {
|
||||
samplepos_t now = AudioEngine::instance()->sample_time();
|
||||
port->parse (now);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::connect_to_parser ()
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, string_compose ("Connecting to signals on port %2\n", _input_port->name()));
|
||||
|
||||
MIDI::Parser* p = _input_port->parser();
|
||||
|
||||
/* Incoming sysex */
|
||||
p->sysex.connect_same_thread (*this, boost::bind (&MIDISurface::handle_midi_sysex, this, _1, _2, _3));
|
||||
/* V-Pot messages are Controller */
|
||||
p->controller.connect_same_thread (*this, boost::bind (&MIDISurface::handle_midi_controller_message, this, _1, _2));
|
||||
/* Button messages are NoteOn */
|
||||
p->note_on.connect_same_thread (*this, boost::bind (&MIDISurface::handle_midi_note_on_message, this, _1, _2));
|
||||
/* Button messages are NoteOn but libmidi++ sends note-on w/velocity = 0 as note-off so catch them too */
|
||||
p->note_off.connect_same_thread (*this, boost::bind (&MIDISurface::handle_midi_note_on_message, this, _1, _2));
|
||||
/* Fader messages are Pitchbend */
|
||||
p->channel_pitchbend[0].connect_same_thread (*this, boost::bind (&MIDISurface::handle_midi_pitchbend_message, this, _1, _2));
|
||||
|
||||
p->poly_pressure.connect_same_thread (*this, boost::bind (&MIDISurface::handle_midi_polypressure_message, this, _1, _2));
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::thread_init ()
|
||||
{
|
||||
pthread_set_name (event_loop_name().c_str());
|
||||
|
||||
PBD::notify_event_loops_about_thread_creation (pthread_self(), event_loop_name(), 2048);
|
||||
ARDOUR::SessionEvent::create_per_thread_pool (event_loop_name(), 128);
|
||||
|
||||
set_thread_priority ();
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::connect_session_signals()
|
||||
{
|
||||
// receive routes added
|
||||
//session->RouteAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MackieControlProtocol::notify_routes_added, this, _1), this);
|
||||
// receive VCAs added
|
||||
//session->vca_manager().VCAAdded.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::notify_vca_added, this, _1), this);
|
||||
|
||||
// receive record state toggled
|
||||
session->RecordStateChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::notify_record_state_changed, this), this);
|
||||
// receive transport state changed
|
||||
session->TransportStateChange.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::notify_transport_state_changed, this), this);
|
||||
session->TransportLooped.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::notify_loop_state_changed, this), this);
|
||||
// receive punch-in and punch-out
|
||||
Config->ParameterChanged.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::notify_parameter_changed, this, _1), this);
|
||||
session->config.ParameterChanged.connect (session_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::notify_parameter_changed, this, _1), this);
|
||||
// receive rude solo changed
|
||||
session->SoloActive.connect(session_connections, MISSING_INVALIDATOR, boost::bind (&MIDISurface::notify_solo_active_changed, this, _1), this);
|
||||
}
|
||||
|
||||
XMLNode&
|
||||
MIDISurface::get_state() const
|
||||
{
|
||||
XMLNode& node (ControlProtocol::get_state());
|
||||
XMLNode* child;
|
||||
|
||||
child = new XMLNode (X_("Input"));
|
||||
child->add_child_nocopy (_async_in->get_state());
|
||||
node.add_child_nocopy (*child);
|
||||
child = new XMLNode (X_("Output"));
|
||||
child->add_child_nocopy (_async_out->get_state());
|
||||
node.add_child_nocopy (*child);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
int
|
||||
MIDISurface::set_state (const XMLNode & node, int version)
|
||||
{
|
||||
DEBUG_TRACE (DEBUG::MIDISurface, string_compose ("MIDISurface::set_state: active %1\n", active()));
|
||||
|
||||
if (ControlProtocol::set_state (node, version)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
XMLNode* child;
|
||||
|
||||
if ((child = node.child (X_("Input"))) != 0) {
|
||||
XMLNode* portnode = child->child (Port::state_node_name.c_str());
|
||||
if (portnode) {
|
||||
portnode->remove_property ("name");
|
||||
_async_in->set_state (*portnode, version);
|
||||
}
|
||||
}
|
||||
|
||||
if ((child = node.child (X_("Output"))) != 0) {
|
||||
XMLNode* portnode = child->child (Port::state_node_name.c_str());
|
||||
if (portnode) {
|
||||
portnode->remove_property ("name");
|
||||
_async_out->set_state (*portnode, version);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
MIDISurface::do_request (MidiSurfaceRequest * req)
|
||||
{
|
||||
if (req->type == CallSlot) {
|
||||
|
||||
call_slot (MISSING_INVALIDATOR, req->the_slot);
|
||||
|
||||
} else if (req->type == Quit) {
|
||||
|
||||
stop_using_device ();
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
MIDISurface::begin_using_device ()
|
||||
{
|
||||
_in_use = true;
|
||||
connect_session_signals ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
MIDISurface::stop_using_device ()
|
||||
{
|
||||
session_connections.drop_connections ();
|
||||
_in_use = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::list<boost::shared_ptr<ARDOUR::Bundle> >
|
||||
MIDISurface::bundles ()
|
||||
{
|
||||
std::list<boost::shared_ptr<ARDOUR::Bundle> > b;
|
||||
|
||||
if (_output_bundle) {
|
||||
b.push_back (_output_bundle);
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
void*
|
||||
MIDISurface::request_factory (uint32_t num_requests)
|
||||
{
|
||||
/* AbstractUI<T>::request_buffer_factory() is a template method only
|
||||
instantiated in this source module. To provide something visible for
|
||||
use in the interface/descriptor, we have this static method that is
|
||||
template-free.
|
||||
*/
|
||||
return request_buffer_factory (num_requests);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Paul Davis <paul@linuxaudiosystems.com>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
#ifndef midi_byte_array_h
|
||||
#define midi_byte_array_h
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/shared_array.hpp>
|
||||
|
||||
//#include <midi++/types.h>
|
||||
namespace MIDI {
|
||||
typedef unsigned char byte;
|
||||
}
|
||||
|
||||
/**
|
||||
To make building arrays of bytes easier. Thusly:
|
||||
|
||||
MidiByteArray mba;
|
||||
mba << 0xf0 << 0x00 << 0xf7;
|
||||
|
||||
MidiByteArray buf;
|
||||
buf << mba;
|
||||
|
||||
MidiByteArray direct( 3, 0xf0, 0x00, 0xf7 );
|
||||
|
||||
cout << mba << endl;
|
||||
cout << buf << endl;
|
||||
cout << direct << endl;
|
||||
|
||||
will all result in "f0 00 f7" being output to stdout
|
||||
*/
|
||||
class MidiByteArray : public std::vector<MIDI::byte>
|
||||
{
|
||||
public:
|
||||
MidiByteArray() : std::vector<MIDI::byte>() {}
|
||||
|
||||
MidiByteArray( size_t count, MIDI::byte array[] );
|
||||
|
||||
bool compare_n (const MidiByteArray& other, MidiByteArray::size_type len) const;
|
||||
|
||||
/**
|
||||
Accepts a preceding count, and then a list of bytes
|
||||
*/
|
||||
MidiByteArray( size_t count, MIDI::byte first, ... );
|
||||
|
||||
/// copy the given number of bytes from the given array
|
||||
void copy( size_t count, MIDI::byte arr[] );
|
||||
};
|
||||
|
||||
/// append the given byte to the end of the array
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const MIDI::byte & b );
|
||||
|
||||
/// append the given string to the end of the array
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const std::string & );
|
||||
|
||||
/// append the given array to the end of this array
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const MidiByteArray & barr );
|
||||
|
||||
/// output the bytes as hex to the given stream
|
||||
std::ostream & operator << ( std::ostream & os, const MidiByteArray & mba );
|
||||
|
||||
#endif
|
||||
134
libs/ctrl-interface/midi_surface/midi_surface/midi_surface.h
Normal file
134
libs/ctrl-interface/midi_surface/midi_surface/midi_surface.h
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* Copyright (C) 2022 Robin Gareus <robin@gareus.org>
|
||||
*
|
||||
* 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.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#define ABSTRACT_UI_EXPORTS
|
||||
#include "pbd/abstract_ui.h"
|
||||
|
||||
#include "control_protocol/control_protocol.h"
|
||||
#include "midi++/types.h"
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
namespace MIDI {
|
||||
class Parser;
|
||||
class Port;
|
||||
}
|
||||
|
||||
namespace ARDOUR {
|
||||
class Bundle;
|
||||
class Port;
|
||||
class MidiBuffer;
|
||||
}
|
||||
|
||||
struct MidiSurfaceRequest : public BaseUI::BaseRequestObject {
|
||||
public:
|
||||
MidiSurfaceRequest () {}
|
||||
~MidiSurfaceRequest () {}
|
||||
};
|
||||
|
||||
class MIDISurface : public ARDOUR::ControlProtocol
|
||||
, public AbstractUI<MidiSurfaceRequest>
|
||||
{
|
||||
public:
|
||||
MIDISurface (ARDOUR::Session&, std::string const & name, std::string const & port_name_prefix, bool use_pad_filter);
|
||||
~MIDISurface ();
|
||||
|
||||
static void* request_factory (uint32_t num_requests);
|
||||
|
||||
boost::shared_ptr<ARDOUR::Port> input_port();
|
||||
boost::shared_ptr<ARDOUR::Port> output_port();
|
||||
|
||||
// Bundle to represent our input ports
|
||||
boost::shared_ptr<ARDOUR::Bundle> _input_bundle;
|
||||
// Bundle to represent our output ports
|
||||
boost::shared_ptr<ARDOUR::Bundle> _output_bundle;
|
||||
|
||||
ARDOUR::Session & get_session() { return *session; }
|
||||
|
||||
virtual std::string input_port_name () const = 0;
|
||||
virtual std::string output_port_name () const = 0;
|
||||
|
||||
void write (const MidiByteArray&);
|
||||
void write (MIDI::byte const *, size_t);
|
||||
|
||||
XMLNode& get_state() const;
|
||||
int set_state (const XMLNode & node, int version);
|
||||
|
||||
std::list<boost::shared_ptr<ARDOUR::Bundle> > bundles ();
|
||||
|
||||
PBD::Signal0<void> ConnectionChange;
|
||||
|
||||
CONTROL_PROTOCOL_THREADS_NEED_TEMPO_MAP_DECL();
|
||||
|
||||
protected:
|
||||
bool with_pad_filter;
|
||||
bool _in_use;
|
||||
std::string port_name_prefix;
|
||||
MIDI::Port* _input_port;
|
||||
MIDI::Port* _output_port;
|
||||
|
||||
boost::shared_ptr<ARDOUR::Port> _async_in;
|
||||
boost::shared_ptr<ARDOUR::Port> _async_out;
|
||||
|
||||
void do_request (MidiSurfaceRequest*);
|
||||
|
||||
virtual void connect_to_parser ();
|
||||
virtual void handle_midi_pitchbend_message (MIDI::Parser&, MIDI::pitchbend_t) {}
|
||||
virtual void handle_midi_polypressure_message (MIDI::Parser&, MIDI::EventTwoBytes*) {}
|
||||
virtual void handle_midi_controller_message (MIDI::Parser&, MIDI::EventTwoBytes*) {}
|
||||
virtual void handle_midi_note_on_message (MIDI::Parser&, MIDI::EventTwoBytes*) {}
|
||||
virtual void handle_midi_note_off_message (MIDI::Parser&, MIDI::EventTwoBytes*) {}
|
||||
virtual void handle_midi_sysex (MIDI::Parser&, MIDI::byte *, size_t) {}
|
||||
|
||||
virtual bool midi_input_handler (Glib::IOCondition ioc, MIDI::Port* port);
|
||||
|
||||
virtual void thread_init ();
|
||||
|
||||
PBD::ScopedConnectionList session_connections;
|
||||
|
||||
virtual void connect_session_signals ();
|
||||
virtual void notify_record_state_changed () {}
|
||||
virtual void notify_transport_state_changed () {}
|
||||
virtual void notify_loop_state_changed () {}
|
||||
virtual void notify_parameter_changed (std::string) {}
|
||||
virtual void notify_solo_active_changed (bool) {}
|
||||
|
||||
virtual void port_registration_handler ();
|
||||
virtual bool pad_filter (ARDOUR::MidiBuffer& in, ARDOUR::MidiBuffer& out) const { return false; }
|
||||
|
||||
enum ConnectionState {
|
||||
InputConnected = 0x1,
|
||||
OutputConnected = 0x2
|
||||
};
|
||||
|
||||
int _connection_state;
|
||||
|
||||
virtual bool connection_handler (boost::weak_ptr<ARDOUR::Port>, std::string name1, boost::weak_ptr<ARDOUR::Port>, std::string name2, bool yn);
|
||||
PBD::ScopedConnectionList port_connections;
|
||||
|
||||
virtual int ports_acquire ();
|
||||
virtual void ports_release ();
|
||||
|
||||
virtual int begin_using_device ();
|
||||
virtual int stop_using_device ();
|
||||
virtual int device_acquire () = 0;
|
||||
virtual void device_release () = 0;
|
||||
|
||||
void drop ();
|
||||
void port_setup ();
|
||||
};
|
||||
33
libs/ctrl-interface/midi_surface/wscript
Normal file
33
libs/ctrl-interface/midi_surface/wscript
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#!/usr/bin/env python
|
||||
from waflib.extras import autowaf as autowaf
|
||||
import os
|
||||
|
||||
# Mandatory variables
|
||||
top = '.'
|
||||
out = 'build'
|
||||
|
||||
def options(opt):
|
||||
autowaf.set_options(opt)
|
||||
|
||||
def configure(conf):
|
||||
pass
|
||||
|
||||
def build(bld):
|
||||
obj = bld(features = 'cxx cxxshlib')
|
||||
obj.source = '''
|
||||
midi_surface.cc
|
||||
midi_byte_array.cc
|
||||
'''
|
||||
obj.export_includes = ['.', './midi_surface']
|
||||
obj.defines = [ 'PACKAGE="ardour_midisurface"' ]
|
||||
obj.defines += [ 'ARDOURSURFACE_DLL_EXPORTS' ]
|
||||
obj.defines += [ 'VERSIONSTRING="' + bld.env['VERSION'] + '"' ]
|
||||
obj.includes = ['.', './midi_surface']
|
||||
obj.name = 'libardour_midisurface'
|
||||
obj.target = 'ardour_midisurface'
|
||||
obj.uselib = 'GLIB GLIBMM SIGCPP XML OSX'
|
||||
obj.use = 'libardour libardour_cp libpbd libevoral libmidipp libtemporal'
|
||||
obj.install_path = bld.env['LIBDIR']
|
||||
|
||||
def shutdown():
|
||||
autowaf.shutdown()
|
||||
Loading…
Add table
Add a link
Reference in a new issue