Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <fcntl.h>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <iomanip>
-#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <float.h>
#include <sys/time.h>
#include "ardour/location.h"
#include "ardour/midi_ui.h"
#include "ardour/panner.h"
+#include "ardour/panner_shell.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/tempo.h"
#include "ardour/types.h"
+#include "ardour/audioengine.h"
#include "mackie_control_protocol.h"
using namespace Mackie;
using namespace PBD;
-using boost::shared_ptr;
-
#include "i18n.h"
MackieMidiBuilder builder;
, _surface (0)
, _jog_wheel (*this)
, _timecode_type (ARDOUR::AnyTime::BBT)
+ , _input_bundle (new ARDOUR::Bundle (_("Mackie Control In"), true))
+ , _output_bundle (new ARDOUR::Bundle (_("Mackie Control Out"), false))
+ , _gui (0)
{
DEBUG_TRACE (DEBUG::MackieControl, "MackieControlProtocol::MackieControlProtocol\n");
}
// predicate for sort call in get_sorted_routes
struct RouteByRemoteId
{
- bool operator () (const shared_ptr<Route> & a, const shared_ptr<Route> & b) const
+ bool operator () (const boost::shared_ptr<Route> & a, const boost::shared_ptr<Route> & b) const
{
return a->remote_control_id() < b->remote_control_id();
}
for (; it != end && it != sorted.end(); ++it, ++i)
{
boost::shared_ptr<Route> route = *it;
+
+ assert (surface().strips[i]);
Strip & strip = *surface().strips[i];
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("remote id %1 connecting %2 to %3 with port %4\n",
}
bool
-MackieControlProtocol::handle_strip_button (Control & control, ButtonState bs, boost::shared_ptr<Route> route)
+MackieControlProtocol::handle_strip_button (SurfacePort & port, Control & control, ButtonState bs, boost::shared_ptr<Route> route)
{
bool state = false;
if (control.name() == "recenable")
{
state = !route->record_enabled();
- route->set_record_enable (state, this);
+ route->set_record_enabled (state, this);
}
else if (control.name() == "mute")
{
if (control.name() == "fader_touch")
{
state = bs == press;
- control.strip().gain().in_use (state);
+ control.strip().gain().set_in_use (state);
+
+ if (ARDOUR::Config->get_mackie_emulation() == "bcf" && state) {
+ /* BCF faders don't support touch, so add a timeout to reset
+ their `in_use' state.
+ */
+ port.add_in_use_timeout (control.strip().gain(), &control.strip().fader_touch());
+ }
}
return state;
boost::shared_ptr<Route> mr = master_route ();
if (mr) {
- master_route_signal = shared_ptr<RouteSignal> (new RouteSignal (mr, *this, master_strip(), mcu_port()));
+ master_route_signal = boost::shared_ptr<RouteSignal> (new RouteSignal (mr, *this, master_strip(), mcu_port()));
// update strip from route
master_route_signal->notify_all();
}
}
void
-MackieControlProtocol::add_port (MIDI::Port & midi_port, int number)
-{
- DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1,%2,%3\n", midi_port.name(), midi_port.device(), midi_port.type()));
-
- if (string (midi_port.device()) == string ("ardour") && midi_port.type() == MIDI::Port::ALSA_Sequencer) {
- throw MackieControlException ("The Mackie MCU driver will not use a port with device=ardour");
- } else if (midi_port.type() == MIDI::Port::ALSA_Sequencer) {
- throw MackieControlException ("alsa/sequencer ports don't work with the Mackie MCU driver right now");
- } else {
- MackiePort * sport = new MackiePort (*this, midi_port, number);
- _ports.push_back (sport);
-
- sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport));
- sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport));
- sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
- }
+MackieControlProtocol::add_port (MIDI::Port & midi_input_port, MIDI::Port & midi_output_port, int number)
+{
+ DEBUG_TRACE (DEBUG::MackieControl, string_compose ("add port %1 %2\n", midi_input_port.name(), midi_output_port.name()));
+
+ MackiePort * sport = new MackiePort (*this, midi_input_port, midi_output_port, number);
+ _ports.push_back (sport);
+
+ sport->init_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_init, this, sport));
+ sport->active_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_active, this, sport));
+ sport->inactive_event.connect_same_thread (port_connections, boost::bind (&MackieControlProtocol::handle_port_inactive, this, sport));
+
+ _input_bundle->add_channel (
+ midi_input_port.name(),
+ ARDOUR::DataType::MIDI,
+ session->engine().make_port_name_non_relative (midi_input_port.name())
+ );
+
+ _output_bundle->add_channel (
+ midi_output_port.name(),
+ ARDOUR::DataType::MIDI,
+ session->engine().make_port_name_non_relative (midi_output_port.name())
+ );
}
void
MackieControlProtocol::create_ports()
{
MIDI::Manager * mm = MIDI::Manager::instance();
- MIDI::Port * midi_port = mm->port (default_port_name);
+ MIDI::Port * midi_input_port = mm->add_port (
+ new MIDI::Port (string_compose (_("%1 in"), default_port_name), MIDI::Port::IsInput, session->engine().jack())
+ );
+ MIDI::Port * midi_output_port = mm->add_port (
+ new MIDI::Port (string_compose (_("%1 out"), default_port_name), MIDI::Port::IsOutput, session->engine().jack())
+ );
- // open main port
+ /* Create main port */
- if (midi_port == 0) {
+ if (!midi_input_port->ok() || !midi_output_port->ok()) {
ostringstream os;
- os << string_compose (_("no MIDI port named \"%1\" exists - Mackie control disabled"), default_port_name);
+ os << _("Mackie control MIDI ports could not be created; Mackie control disabled");
error << os.str() << endmsg;
throw MackieControlException (os.str());
}
- add_port (*midi_port, 0);
+ add_port (*midi_input_port, *midi_output_port, 0);
- // open extender ports. Up to 9. Should be enough.
- // could also use mm->get_midi_ports()
+ /* Create extender ports */
- string ext_port_base = "mcu_xt_";
-
- for (int index = 1; index <= 9; ++index) {
- ostringstream os;
- os << ext_port_base << index;
- MIDI::Port * midi_port = mm->port (os.str());
- if (midi_port != 0) {
- add_port (*midi_port, index);
+ for (int index = 1; index <= Config->get_mackie_extenders(); ++index) {
+ MIDI::Port * midi_input_port = mm->add_port (
+ new MIDI::Port (string_compose (_("mcu_xt_%1 in"), index), MIDI::Port::IsInput, session->engine().jack())
+ );
+ MIDI::Port * midi_output_port = mm->add_port (
+ new MIDI::Port (string_compose (_("mcu_xt_%1 out"), index), MIDI::Port::IsOutput, session->engine().jack())
+ );
+ if (midi_input_port->ok() && midi_output_port->ok()) {
+ add_port (*midi_input_port, *midi_output_port, index);
}
}
}
-shared_ptr<Route>
+boost::shared_ptr<Route>
MackieControlProtocol::master_route()
{
return session->master_out ();
// at which point the fader should just reset itself
if (route != 0)
{
- route->gain_control()->set_value (state.pos);
+ route->gain_control()->set_value (slider_position_to_gain (state.pos));
+
+ if (ARDOUR::Config->get_mackie_emulation() == "bcf") {
+ /* reset the timeout while we're still moving the fader */
+ port.add_in_use_timeout (control, control.in_use_touch_control);
+ }
// must echo bytes back to slider now, because
// the notifier only works if the fader is not being
if (control.group().is_strip()) {
// strips
if (route != 0) {
- handle_strip_button (control, state.button_state, route);
+ handle_strip_button (port, control, state.button_state, route);
} else {
// no route so always switch the light off
// because no signals will be emitted by a non-route
} else if (control.group().is_master()) {
// master fader touch
if (route != 0) {
- handle_strip_button (control, state.button_state, route);
+ handle_strip_button (port, control, state.button_state, route);
}
} else {
// handle all non-strip buttons
// pot (jog wheel, external control)
case Control::type_pot:
if (control.group().is_strip()) {
- if (route != 0 && route->panner())
- {
+ if (route) {
+ boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
// pan for mono input routes, or stereo linked panners
- if (route->panner()->npanners() == 1 || (route->panner()->npanners() == 2 && route->panner()->linked()))
- {
- // assume pan for now
- float xpos;
- route->panner()->streampanner (0).get_effective_position (xpos);
-
- // calculate new value, and trim
- xpos += state.delta * state.sign;
- if (xpos > 1.0)
- xpos = 1.0;
- else if (xpos < 0.0)
- xpos = 0.0;
-
- route->panner()->streampanner (0).set_position (xpos);
+ if (panner) {
+ double p = panner->position ();
+
+ // calculate new value, and adjust
+ p += state.delta * state.sign;
+ p = min (1.0, p);
+ p = max (0.0, p);
+ panner->set_position (p);
}
}
else
Fader & fader = route_signal->strip().gain();
if (!fader.in_use())
{
- float gain_value = route_signal->route()->gain_control()->get_value();
+ float gain_value = gain_to_slider_position (route_signal->route()->gain_control()->get_value());
// check that something has actually changed
if (force_update || gain_value != route_signal->last_gain_written())
{
try
{
Strip & strip = route_signal->strip();
- if (!strip.is_master())
+
+ /* XXX: not sure about this check to only display stuff for strips of index < 8 */
+ if (!strip.is_master() && strip.index() < 8)
{
string line1;
string fullname = route_signal->route()->name();
{
Pot & pot = route_signal->strip().vpot();
boost::shared_ptr<Panner> panner = route_signal->route()->panner();
- if ((panner && panner->npanners() == 1) || (panner->npanners() == 2 && panner->linked()))
- {
- float pos;
- route_signal->route()->panner()->streampanner(0).get_effective_position (pos);
+ if (panner) {
+ double pos = panner->position ();
// cache the MidiByteArray here, because the mackie led control is much lower
// resolution than the panner control. So we save lots of byte
}
string
-MackieControlProtocol::format_bbt_timecode (nframes_t now_frame)
+MackieControlProtocol::format_bbt_timecode (framepos_t now_frame)
{
- BBT_Time bbt_time;
+ Timecode::BBT_Time bbt_time;
session->bbt_time (now_frame, bbt_time);
// According to the Logic docs
subdiv = 3;
}
- uint32_t subdivisions = bbt_time.ticks / uint32_t (Meter::ticks_per_beat / subdiv);
- uint32_t ticks = bbt_time.ticks % uint32_t (Meter::ticks_per_beat / subdiv);
+ uint32_t subdivisions = bbt_time.ticks / uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv);
+ uint32_t ticks = bbt_time.ticks % uint32_t (Timecode::BBT_Time::ticks_per_beat / subdiv);
os << setw(2) << setfill('0') << subdivisions + 1;
os << setw(3) << setfill('0') << ticks;
}
string
-MackieControlProtocol::format_timecode_timecode (nframes_t now_frame)
+MackieControlProtocol::format_timecode_timecode (framepos_t now_frame)
{
Timecode::Time timecode;
session->timecode_time (now_frame, timecode);
if (surface().has_timecode_display())
{
// do assignment here so current_frame is fixed
- nframes_t current_frame = session->transport_frame();
+ framepos_t current_frame = session->transport_frame();
string timecode;
switch (_timecode_type)
if (sorted.size() > route_table.size())
{
int new_initial = _current_initial_bank - route_table.size();
- if (new_initial < 0) new_initial = 0;
- if (new_initial != int (_current_initial_bank))
- {
+ if (new_initial < 0) {
+ new_initial = 0;
+ }
+
+ if (new_initial != int (_current_initial_bank)) {
session->set_dirty();
switch_banks (new_initial);
}
Sorted sorted = get_sorted_routes();
if (sorted.size() > route_table.size()) {
uint32_t delta = sorted.size() - (route_table.size() + _current_initial_bank);
- if (delta > route_table.size()) delta = route_table.size();
+
+ if (delta > route_table.size()) {
+ delta = route_table.size();
+ }
+
if (delta > 0) {
session->set_dirty();
switch_banks (_current_initial_bank + delta);
{
// cut'n'paste from LocationUI::add_new_location()
string markername;
- nframes_t where = session->audible_frame();
+ framepos_t where = session->audible_frame();
session->locations()->next_available_name(markername,"mcu");
- Location *location = new Location (where, where, markername, Location::IsMark);
+ Location *location = new Location (*session, where, where, markername, Location::IsMark);
session->begin_reversible_command (_("add marker"));
XMLNode &before = session->locations()->get_state();
session->locations()->add (location, true);
return off;
}
+list<boost::shared_ptr<ARDOUR::Bundle> >
+MackieControlProtocol::bundles ()
+{
+ list<boost::shared_ptr<ARDOUR::Bundle> > b;
+ b.push_back (_input_bundle);
+ b.push_back (_output_bundle);
+ return b;
+}