mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-07 15:25:01 +01:00
move all code to construct MIDI messages into relevant Control/Strip/Surface object; remove MackieMidiBuilder
git-svn-id: svn://localhost/ardour2/branches/3.0@11895 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
parent
0860570c8c
commit
02c8ccf348
23 changed files with 374 additions and 569 deletions
|
|
@ -103,7 +103,7 @@ public:
|
||||||
|
|
||||||
virtual Led & led() { return _led; }
|
virtual Led & led() { return _led; }
|
||||||
|
|
||||||
virtual type_t type() const { return type_button; };
|
MidiByteArray zero() { return _led.set_state (off); }
|
||||||
|
|
||||||
MidiByteArray update_message () const;
|
MidiByteArray update_message () const;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,13 +22,11 @@
|
||||||
|
|
||||||
#include "controls.h"
|
#include "controls.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "mackie_midi_builder.h"
|
|
||||||
#include "surface.h"
|
#include "surface.h"
|
||||||
#include "control_group.h"
|
#include "control_group.h"
|
||||||
|
|
||||||
#include "button.h"
|
#include "button.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
#include "ledring.h"
|
|
||||||
#include "pot.h"
|
#include "pot.h"
|
||||||
#include "fader.h"
|
#include "fader.h"
|
||||||
#include "jog.h"
|
#include "jog.h"
|
||||||
|
|
@ -75,10 +73,6 @@ ostream & Mackie::operator << (ostream & os, const Mackie::Control & control)
|
||||||
os << " { ";
|
os << " { ";
|
||||||
os << "name: " << control.name();
|
os << "name: " << control.name();
|
||||||
os << ", ";
|
os << ", ";
|
||||||
os << "id: " << "0x" << setw(4) << setfill('0') << hex << control.id() << setfill(' ');
|
|
||||||
os << ", ";
|
|
||||||
os << "type: " << "0x" << setw(2) << setfill('0') << hex << control.type() << setfill(' ');
|
|
||||||
os << ", ";
|
|
||||||
os << "raw_id: " << "0x" << setw(2) << setfill('0') << hex << control.raw_id() << setfill(' ');
|
os << "raw_id: " << "0x" << setw(2) << setfill('0') << hex << control.raw_id() << setfill(' ');
|
||||||
os << ", ";
|
os << ", ";
|
||||||
os << "group: " << control.group().name();
|
os << "group: " << control.group().name();
|
||||||
|
|
@ -87,16 +81,6 @@ ostream & Mackie::operator << (ostream & os, const Mackie::Control & control)
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
Control*
|
|
||||||
Pot::factory (Surface& surface, int id, const char* name, Group& group)
|
|
||||||
{
|
|
||||||
Pot* p = new Pot (id, name, group);
|
|
||||||
surface.pots[id] = p;
|
|
||||||
surface.controls.push_back (p);
|
|
||||||
group.add (*p);
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
Control*
|
Control*
|
||||||
Jog::factory (Surface& surface, int id, const char* name, Group& group)
|
Jog::factory (Surface& surface, int id, const char* name, Group& group)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,30 +28,21 @@
|
||||||
#include "pbd/signals.h"
|
#include "pbd/signals.h"
|
||||||
|
|
||||||
#include "mackie_control_exception.h"
|
#include "mackie_control_exception.h"
|
||||||
|
#include "midi_byte_array.h"
|
||||||
|
|
||||||
namespace Mackie
|
namespace Mackie
|
||||||
{
|
{
|
||||||
|
|
||||||
class Strip;
|
class Strip;
|
||||||
class Group;
|
class Group;
|
||||||
class Led;
|
|
||||||
class Surface;
|
class Surface;
|
||||||
|
|
||||||
class Control
|
class Control
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum type_t {
|
|
||||||
type_led,
|
|
||||||
type_led_ring,
|
|
||||||
type_fader = 0xe0,
|
|
||||||
type_button = 0x90,
|
|
||||||
type_pot = 0xb0,
|
|
||||||
type_meter = 0xd0
|
|
||||||
};
|
|
||||||
|
|
||||||
enum base_id_t {
|
enum base_id_t {
|
||||||
fader_base_id = 0x0,
|
fader_base_id = 0xe0,
|
||||||
pot_base_id = 0x10,
|
pot_base_id = 0x30,
|
||||||
jog_base_id = 0x3c,
|
jog_base_id = 0x3c,
|
||||||
fader_touch_button_base_id = 0xe0,
|
fader_touch_button_base_id = 0xe0,
|
||||||
vselect_button_base_id = 0x20,
|
vselect_button_base_id = 0x20,
|
||||||
|
|
@ -65,12 +56,6 @@ public:
|
||||||
Control (int id, std::string name, Group& group);
|
Control (int id, std::string name, Group& group);
|
||||||
virtual ~Control() {}
|
virtual ~Control() {}
|
||||||
|
|
||||||
virtual Led & led() { throw MackieControlException ("no led available"); }
|
|
||||||
|
|
||||||
/// type() << 8 + midi id of the control. This
|
|
||||||
/// provides a unique id for any control on the surface.
|
|
||||||
int id() const { return (type() << 8) + _id; }
|
|
||||||
|
|
||||||
/// the value of the second bytes of the message. It's
|
/// the value of the second bytes of the message. It's
|
||||||
/// the id of the control, but only guaranteed to be
|
/// the id of the control, but only guaranteed to be
|
||||||
/// unique within the control type.
|
/// unique within the control type.
|
||||||
|
|
@ -78,19 +63,17 @@ public:
|
||||||
|
|
||||||
const std::string & name() const { return _name; }
|
const std::string & name() const { return _name; }
|
||||||
Group & group() const { return _group; }
|
Group & group() const { return _group; }
|
||||||
|
|
||||||
virtual bool accepts_feedback() const { return true; }
|
virtual bool accepts_feedback() const { return true; }
|
||||||
|
|
||||||
virtual type_t type() const = 0;
|
|
||||||
|
|
||||||
/// Return true if this control is the one and only Jog Wheel
|
|
||||||
virtual bool is_jog() const { return false; }
|
|
||||||
|
|
||||||
bool in_use () const;
|
bool in_use () const;
|
||||||
void set_in_use (bool);
|
void set_in_use (bool);
|
||||||
|
|
||||||
/// Keep track of the timeout so it can be updated with more incoming events
|
/// Keep track of the timeout so it can be updated with more incoming events
|
||||||
sigc::connection in_use_connection;
|
sigc::connection in_use_connection;
|
||||||
|
|
||||||
|
virtual MidiByteArray zero() = 0;
|
||||||
|
|
||||||
/** If we are doing an in_use timeout for a fader without touch, this
|
/** If we are doing an in_use timeout for a fader without touch, this
|
||||||
* is its touch button control; otherwise 0.
|
* is its touch button control; otherwise 0.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -34,3 +34,16 @@ Fader::factory (Surface& surface, int id, const char* name, Group& group)
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Fader::set_position (float normalized)
|
||||||
|
{
|
||||||
|
position = normalized;
|
||||||
|
return update_message ();
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Fader::update_message ()
|
||||||
|
{
|
||||||
|
int posi = int (0x3fff * position);
|
||||||
|
return MidiByteArray (3, raw_id(), posi & 0x7f, posi >> 7);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,19 @@ class Fader : public Control
|
||||||
public:
|
public:
|
||||||
Fader (int id, std::string name, Group & group)
|
Fader (int id, std::string name, Group & group)
|
||||||
: Control (id, name, group)
|
: Control (id, name, group)
|
||||||
|
, position (0.0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual type_t type() const { return type_fader; }
|
MidiByteArray set_position (float);
|
||||||
|
MidiByteArray zero() { return set_position (0.0); }
|
||||||
|
|
||||||
|
MidiByteArray update_message ();
|
||||||
|
|
||||||
static Control* factory (Surface&, int id, const char*, Group&);
|
static Control* factory (Surface&, int id, const char*, Group&);
|
||||||
|
|
||||||
|
private:
|
||||||
|
float position;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool is_jog() const { return true; }
|
MidiByteArray zero() { return MidiByteArray(); }
|
||||||
|
|
||||||
static Control* factory (Surface&, int id, const char*, Group&);
|
static Control* factory (Surface&, int id, const char*, Group&);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -36,9 +36,10 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
Led & led() { return *this; }
|
Led & led() { return *this; }
|
||||||
type_t type() const { return type_led; }
|
|
||||||
MidiByteArray set_state (LedState);
|
MidiByteArray set_state (LedState);
|
||||||
|
|
||||||
|
MidiByteArray zero() { return set_state (off); }
|
||||||
|
|
||||||
static Control* factory (Surface&, int id, const char*, Group&);
|
static Control* factory (Surface&, int id, const char*, Group&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2006,2007 John Anderson
|
|
||||||
Copyright (C) 2012 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_mackie_control_protocol_ledring_h__
|
|
||||||
#define __ardour_mackie_control_protocol_ledring_h__
|
|
||||||
|
|
||||||
#include "controls.h"
|
|
||||||
#include "led.h"
|
|
||||||
|
|
||||||
namespace Mackie {
|
|
||||||
|
|
||||||
class LedRing : public Led
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
LedRing (int id, std::string name, Group & group)
|
|
||||||
: Led (id, name, group)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual type_t type() const { return type_led_ring; }
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -55,7 +55,6 @@
|
||||||
|
|
||||||
#include "midi_byte_array.h"
|
#include "midi_byte_array.h"
|
||||||
#include "mackie_control_exception.h"
|
#include "mackie_control_exception.h"
|
||||||
#include "mackie_midi_builder.h"
|
|
||||||
#include "surface_port.h"
|
#include "surface_port.h"
|
||||||
#include "surface.h"
|
#include "surface.h"
|
||||||
|
|
||||||
|
|
@ -365,8 +364,6 @@ MackieControlProtocol::periodic ()
|
||||||
(*s)->periodic ();
|
(*s)->periodic ();
|
||||||
}
|
}
|
||||||
|
|
||||||
update_timecode_display();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,12 @@
|
||||||
#include "midi++/types.h"
|
#include "midi++/types.h"
|
||||||
|
|
||||||
#include "ardour/types.h"
|
#include "ardour/types.h"
|
||||||
|
|
||||||
#include "control_protocol/control_protocol.h"
|
#include "control_protocol/control_protocol.h"
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
#include "midi_byte_array.h"
|
#include "midi_byte_array.h"
|
||||||
#include "controls.h"
|
#include "controls.h"
|
||||||
#include "mackie_jog_wheel.h"
|
#include "mackie_jog_wheel.h"
|
||||||
#include "mackie_midi_builder.h"
|
|
||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
|
|
||||||
namespace MIDI {
|
namespace MIDI {
|
||||||
|
|
@ -371,8 +371,6 @@ class MackieControlProtocol
|
||||||
|
|
||||||
int _modifier_state;
|
int _modifier_state;
|
||||||
|
|
||||||
Mackie::MackieMidiBuilder builder;
|
|
||||||
|
|
||||||
typedef std::list<GSource*> PortSources;
|
typedef std::list<GSource*> PortSources;
|
||||||
PortSources port_sources;
|
PortSources port_sources;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
#include "ardour/session.h"
|
#include "ardour/session.h"
|
||||||
|
|
||||||
#include "mackie_jog_wheel.h"
|
#include "mackie_jog_wheel.h"
|
||||||
|
|
||||||
#include "mackie_control_protocol.h"
|
#include "mackie_control_protocol.h"
|
||||||
#include "surface_port.h"
|
#include "surface_port.h"
|
||||||
#include "controls.h"
|
#include "controls.h"
|
||||||
|
|
|
||||||
|
|
@ -1,276 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2006,2007 John Anderson
|
|
||||||
|
|
||||||
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 "mackie_midi_builder.h"
|
|
||||||
|
|
||||||
#include <typeinfo>
|
|
||||||
#include <sstream>
|
|
||||||
#include <iomanip>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
#include "pbd/compose.h"
|
|
||||||
|
|
||||||
#include "ardour/debug.h"
|
|
||||||
#include "controls.h"
|
|
||||||
#include "control_group.h"
|
|
||||||
|
|
||||||
#include "strip.h"
|
|
||||||
#include "button.h"
|
|
||||||
#include "led.h"
|
|
||||||
#include "ledring.h"
|
|
||||||
#include "pot.h"
|
|
||||||
#include "fader.h"
|
|
||||||
#include "jog.h"
|
|
||||||
#include "meter.h"
|
|
||||||
#include "midi_byte_array.h"
|
|
||||||
#include "surface.h"
|
|
||||||
#include "surface_port.h"
|
|
||||||
|
|
||||||
using namespace PBD;
|
|
||||||
using namespace Mackie;
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#define NUCLEUS_DEBUG 1
|
|
||||||
|
|
||||||
MIDI::byte MackieMidiBuilder::calculate_pot_value (midi_pot_mode mode, const ControlState & state)
|
|
||||||
{
|
|
||||||
// TODO do an exact calc for 0.50? To allow manually re-centering the port.
|
|
||||||
|
|
||||||
// center on or off
|
|
||||||
MIDI::byte retval = (state.pos > 0.45 && state.pos < 0.55 ? 1 : 0) << 6;
|
|
||||||
|
|
||||||
// mode
|
|
||||||
retval |= (mode << 4);
|
|
||||||
|
|
||||||
// value, but only if off hasn't explicitly been set
|
|
||||||
if (state.led_state != off)
|
|
||||||
retval += (int(state.pos * 10.0) + 1) & 0x0f; // 0b00001111
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::build_led_ring (const Pot & pot, const ControlState & state, midi_pot_mode mode )
|
|
||||||
{
|
|
||||||
return build_led_ring (pot.led_ring(), state, mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::build_led_ring (const LedRing & led_ring, const ControlState & state, midi_pot_mode mode)
|
|
||||||
{
|
|
||||||
// The other way of doing this:
|
|
||||||
// 0x30 + pot/ring number (0-7)
|
|
||||||
//, 0x30 + led_ring.ordinal() - 1
|
|
||||||
return MidiByteArray (3
|
|
||||||
// the control type
|
|
||||||
, midi_pot_id
|
|
||||||
// the id
|
|
||||||
, 0x20 + led_ring.raw_id()
|
|
||||||
// the value
|
|
||||||
, calculate_pot_value (mode, state)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::build_fader (const Fader & fader, float pos)
|
|
||||||
{
|
|
||||||
int posi = int (0x3fff * pos);
|
|
||||||
|
|
||||||
return MidiByteArray (3
|
|
||||||
, midi_fader_id | fader.raw_id()
|
|
||||||
// lower-order bits
|
|
||||||
, posi & 0x7f
|
|
||||||
// higher-order bits
|
|
||||||
, (posi >> 7)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::zero_strip (Surface& surface, const Strip & strip)
|
|
||||||
{
|
|
||||||
Group::Controls::const_iterator it = strip.controls().begin();
|
|
||||||
MidiByteArray retval;
|
|
||||||
|
|
||||||
for (; it != strip.controls().end(); ++it) {
|
|
||||||
Control & control = **it;
|
|
||||||
if (control.accepts_feedback())
|
|
||||||
retval << zero_control (control);
|
|
||||||
}
|
|
||||||
|
|
||||||
// These must have sysex headers
|
|
||||||
|
|
||||||
/* XXX: not sure about this check to only display stuff for strips of index < 8 */
|
|
||||||
if (strip.index() < 8) {
|
|
||||||
retval << strip_display_blank (surface, strip, 0);
|
|
||||||
retval << strip_display_blank (surface, strip, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::zero_control (Control & control)
|
|
||||||
{
|
|
||||||
switch (control.type()) {
|
|
||||||
case Control::type_button:
|
|
||||||
return control.led().set_state (off);
|
|
||||||
|
|
||||||
case Control::type_led:
|
|
||||||
return dynamic_cast<Led&>(control).set_state (off);
|
|
||||||
|
|
||||||
case Control::type_fader:
|
|
||||||
return build_fader ((Fader&)control, 0.0);
|
|
||||||
|
|
||||||
case Control::type_pot:
|
|
||||||
return build_led_ring (dynamic_cast<const Pot&> (control), off);
|
|
||||||
|
|
||||||
case Control::type_led_ring:
|
|
||||||
return build_led_ring (dynamic_cast<const LedRing&> (control), off);
|
|
||||||
|
|
||||||
case Control::type_meter:
|
|
||||||
return const_cast<Meter&>(dynamic_cast<const Meter&>(control)).update_message (0.0);
|
|
||||||
|
|
||||||
default:
|
|
||||||
ostringstream os;
|
|
||||||
os << "Unknown control type " << control << " in Strip::zero_control";
|
|
||||||
throw MackieControlException (os.str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char translate_seven_segment (char achar)
|
|
||||||
{
|
|
||||||
achar = toupper (achar);
|
|
||||||
if (achar >= 0x40 && achar <= 0x60)
|
|
||||||
return achar - 0x40;
|
|
||||||
else if (achar >= 0x21 && achar <= 0x3f)
|
|
||||||
return achar;
|
|
||||||
else
|
|
||||||
return 0x00;
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::two_char_display (const std::string & msg, const std::string & dots)
|
|
||||||
{
|
|
||||||
if (msg.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: msg must be exactly 2 characters");
|
|
||||||
if (dots.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: dots must be exactly 2 characters");
|
|
||||||
|
|
||||||
MidiByteArray bytes (6, 0xb0, 0x4a, 0x00, 0xb0, 0x4b, 0x00);
|
|
||||||
|
|
||||||
// chars are understood by the surface in right-to-left order
|
|
||||||
// could also exchange the 0x4a and 0x4b, above
|
|
||||||
bytes[5] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
|
|
||||||
bytes[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::two_char_display (unsigned int value, const std::string & /*dots*/)
|
|
||||||
{
|
|
||||||
ostringstream os;
|
|
||||||
os << setfill('0') << setw(2) << value % 100;
|
|
||||||
return two_char_display (os.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::strip_display_blank (Surface& surface, const Strip & strip, unsigned int line_number)
|
|
||||||
{
|
|
||||||
// 6 spaces, not 7 because strip_display adds a space where appropriate
|
|
||||||
return strip_display (surface, strip, line_number, " ");
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::strip_display (Surface& surface, const Strip & strip, unsigned int line_number, const std::string & line)
|
|
||||||
{
|
|
||||||
assert (line_number <= 1);
|
|
||||||
|
|
||||||
MidiByteArray retval;
|
|
||||||
uint32_t index = strip.index() % 8;
|
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display index: %1, line %2 = %3\n", strip.index(), line_number, line));
|
|
||||||
|
|
||||||
// sysex header
|
|
||||||
retval << surface.sysex_hdr();
|
|
||||||
|
|
||||||
// code for display
|
|
||||||
retval << 0x12;
|
|
||||||
// offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
|
|
||||||
retval << (index * 7 + (line_number * 0x38));
|
|
||||||
|
|
||||||
// ascii data to display
|
|
||||||
retval << line;
|
|
||||||
// pad with " " out to 6 chars
|
|
||||||
for (int i = line.length(); i < 6; ++i) {
|
|
||||||
retval << ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
// column spacer, unless it's the right-hand column
|
|
||||||
if (strip.index() < 7) {
|
|
||||||
retval << ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
// sysex trailer
|
|
||||||
retval << MIDI::eox;
|
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display midi: %1\n", retval));
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray MackieMidiBuilder::all_strips_display (SurfacePort & /*port*/, std::vector<std::string> & /*lines1*/, std::vector<std::string> & /*lines2*/)
|
|
||||||
{
|
|
||||||
MidiByteArray retval;
|
|
||||||
retval << 0x12 << 0;
|
|
||||||
// NOTE remember max 112 bytes per message, including sysex headers
|
|
||||||
retval << "Not working yet";
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
MidiByteArray
|
|
||||||
MackieMidiBuilder::timecode_display (Surface& surface, const std::string & timecode, const std::string & last_timecode)
|
|
||||||
{
|
|
||||||
// if there's no change, send nothing, not even sysex header
|
|
||||||
if (timecode == last_timecode) return MidiByteArray();
|
|
||||||
|
|
||||||
// length sanity checking
|
|
||||||
string local_timecode = timecode;
|
|
||||||
|
|
||||||
// truncate to 10 characters
|
|
||||||
if (local_timecode.length() > 10) {
|
|
||||||
local_timecode = local_timecode.substr (0, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad to 10 characters
|
|
||||||
while (local_timecode.length() < 10) {
|
|
||||||
local_timecode += " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the suffix of local_timecode that differs from last_timecode
|
|
||||||
std::pair<string::const_iterator,string::iterator> pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin());
|
|
||||||
|
|
||||||
MidiByteArray retval;
|
|
||||||
|
|
||||||
// sysex header
|
|
||||||
retval << surface.sysex_hdr();
|
|
||||||
|
|
||||||
// code for timecode display
|
|
||||||
retval << 0x10;
|
|
||||||
|
|
||||||
// translate characters. These are sent in reverse order of display
|
|
||||||
// hence the reverse iterators
|
|
||||||
string::reverse_iterator rend = reverse_iterator<string::iterator> (pp.second);
|
|
||||||
for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) {
|
|
||||||
retval << translate_seven_segment (*it);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sysex trailer
|
|
||||||
retval << MIDI::eox;
|
|
||||||
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright (C) 2006,2007 John Anderson
|
|
||||||
|
|
||||||
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 mackie_midi_builder_h
|
|
||||||
#define mackie_midi_builder_h
|
|
||||||
|
|
||||||
#include "midi_byte_array.h"
|
|
||||||
#include "types.h"
|
|
||||||
#include "controls.h"
|
|
||||||
|
|
||||||
namespace Mackie
|
|
||||||
{
|
|
||||||
|
|
||||||
class SurfacePort;
|
|
||||||
class Button;
|
|
||||||
class Meter;
|
|
||||||
class Fader;
|
|
||||||
class Jog;
|
|
||||||
class Pot;
|
|
||||||
class Led;
|
|
||||||
class LedRing;
|
|
||||||
|
|
||||||
/**
|
|
||||||
This knows how to build midi messages given a control and
|
|
||||||
a state.
|
|
||||||
*/
|
|
||||||
class MackieMidiBuilder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
MackieMidiBuilder () {}
|
|
||||||
~MackieMidiBuilder() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
The first byte of a midi message from the surface
|
|
||||||
will contain one of these, sometimes bitmasked
|
|
||||||
with the control id
|
|
||||||
*/
|
|
||||||
enum midi_types {
|
|
||||||
midi_fader_id = Control::type_fader,
|
|
||||||
midi_button_id = Control::type_button,
|
|
||||||
midi_pot_id = Control::type_pot,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
The LED rings have these modes.
|
|
||||||
*/
|
|
||||||
enum midi_pot_mode {
|
|
||||||
midi_pot_mode_dot = 0,
|
|
||||||
midi_pot_mode_boost_cut = 1,
|
|
||||||
midi_pot_mode_wrap = 2,
|
|
||||||
midi_pot_mode_spread = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
MidiByteArray build_led_ring (const Pot & pot, const ControlState &, midi_pot_mode mode = midi_pot_mode_dot);
|
|
||||||
MidiByteArray build_led_ring (const LedRing & led_ring, const ControlState &, midi_pot_mode mode = midi_pot_mode_dot);
|
|
||||||
|
|
||||||
MidiByteArray build_fader (const Fader & fader, float pos);
|
|
||||||
|
|
||||||
/// return bytes that will reset all controls to their zero positions
|
|
||||||
/// And blank the display for the strip. Pass Surface so we know which sysex header to use.
|
|
||||||
MidiByteArray zero_strip (Surface&, const Strip & strip);
|
|
||||||
|
|
||||||
// provide bytes to zero the given control
|
|
||||||
MidiByteArray zero_control (Control & control);
|
|
||||||
|
|
||||||
// display the first 2 chars of the msg in the 2 char display
|
|
||||||
// . is appended to the previous character, so A.B. would
|
|
||||||
// be two characters
|
|
||||||
MidiByteArray two_char_display (const std::string & msg, const std::string & dots = " ");
|
|
||||||
MidiByteArray two_char_display (unsigned int value, const std::string & dots = " ");
|
|
||||||
|
|
||||||
/**
|
|
||||||
Timecode display. Only the difference between timecode and last_timecode will
|
|
||||||
be encoded, to save midi bandwidth. If they're the same, an empty array will
|
|
||||||
be returned
|
|
||||||
*/
|
|
||||||
MidiByteArray timecode_display (Surface&, const std::string & timecode, const std::string & last_timecode = "");
|
|
||||||
|
|
||||||
/**
|
|
||||||
for displaying characters on the strip LCD
|
|
||||||
pass SurfacePort so we know which sysex header to use
|
|
||||||
*/
|
|
||||||
MidiByteArray strip_display (Surface &, const Strip & strip, unsigned int line_number, const std::string & line);
|
|
||||||
|
|
||||||
/// blank the strip LCD, ie write all spaces. Pass Surface so we know which sysex header to use.
|
|
||||||
MidiByteArray strip_display_blank (Surface&, const Strip & strip, unsigned int line_number);
|
|
||||||
|
|
||||||
/// for generating all strip names. Pass SurfacePort so we know which sysex header to use.
|
|
||||||
MidiByteArray all_strips_display (SurfacePort &, std::vector<std::string> & lines1, std::vector<std::string> & lines2);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static MIDI::byte calculate_pot_value (midi_pot_mode mode, const ControlState &);
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -95,3 +95,4 @@ Meter::update_message (float dB)
|
||||||
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,10 +35,10 @@ public:
|
||||||
, last_segment_value_sent (-1)
|
, last_segment_value_sent (-1)
|
||||||
, overload_on (false) {}
|
, overload_on (false) {}
|
||||||
|
|
||||||
virtual type_t type() const { return type_meter; }
|
|
||||||
|
|
||||||
MidiByteArray update_message (float dB);
|
MidiByteArray update_message (float dB);
|
||||||
|
|
||||||
|
MidiByteArray zero() { return update_message (-99999999.0); }
|
||||||
|
|
||||||
static Control* factory (Surface&, int id, const char*, Group&);
|
static Control* factory (Surface&, int id, const char*, Group&);
|
||||||
|
|
||||||
int last_segment_value_sent;
|
int last_segment_value_sent;
|
||||||
|
|
|
||||||
87
libs/surfaces/mackie/pot.cc
Normal file
87
libs/surfaces/mackie/pot.cc
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2006,2007 John Anderson
|
||||||
|
Copyright (C) 2012 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 <cmath>
|
||||||
|
|
||||||
|
#include "pot.h"
|
||||||
|
#include "surface.h"
|
||||||
|
#include "control_group.h"
|
||||||
|
|
||||||
|
using namespace Mackie;
|
||||||
|
|
||||||
|
Control*
|
||||||
|
Pot::factory (Surface& surface, int id, const char* name, Group& group)
|
||||||
|
{
|
||||||
|
Pot* p = new Pot (id, name, group);
|
||||||
|
surface.pots[id] = p;
|
||||||
|
surface.controls.push_back (p);
|
||||||
|
group.add (*p);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Pot::set_mode (Pot::Mode m)
|
||||||
|
{
|
||||||
|
mode = m;
|
||||||
|
return update_message ();
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Pot::set_onoff (bool onoff)
|
||||||
|
{
|
||||||
|
on = onoff;
|
||||||
|
return update_message ();
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Pot::set_value (float normalized)
|
||||||
|
{
|
||||||
|
value = normalized;
|
||||||
|
return update_message ();
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Pot::set_all (float val, bool onoff, Mode m)
|
||||||
|
{
|
||||||
|
value = val;
|
||||||
|
on = onoff;
|
||||||
|
mode = m;
|
||||||
|
return update_message ();
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Pot::update_message ()
|
||||||
|
{
|
||||||
|
// TODO do an exact calc for 0.50? To allow manually re-centering the port.
|
||||||
|
|
||||||
|
// center on or off
|
||||||
|
MIDI::byte msg = (value > 0.45 && value < 0.55 ? 1 : 0) << 6;
|
||||||
|
|
||||||
|
// mode
|
||||||
|
msg |= (mode << 4);
|
||||||
|
|
||||||
|
// value, but only if off hasn't explicitly been set
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
msg += (lrintf (value * 10.0) + 1) & 0x0f; // 0b00001111
|
||||||
|
}
|
||||||
|
|
||||||
|
return MidiByteArray (3, 0xb0, raw_id(), msg);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -2,25 +2,40 @@
|
||||||
#define __ardour_mackie_control_protocol_pot_h__
|
#define __ardour_mackie_control_protocol_pot_h__
|
||||||
|
|
||||||
#include "controls.h"
|
#include "controls.h"
|
||||||
#include "ledring.h"
|
|
||||||
|
|
||||||
namespace Mackie {
|
namespace Mackie {
|
||||||
|
|
||||||
class Pot : public Control
|
class Pot : public Control
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum Mode {
|
||||||
|
dot = 0,
|
||||||
|
boost_cut = 1,
|
||||||
|
wrap = 2,
|
||||||
|
spread = 3,
|
||||||
|
};
|
||||||
|
|
||||||
Pot (int id, std::string name, Group & group)
|
Pot (int id, std::string name, Group & group)
|
||||||
: Control (id, name, group)
|
: Control (id, name, group)
|
||||||
, _led_ring (id, name + "_ring", group) {}
|
, value (0.0)
|
||||||
|
, mode (dot)
|
||||||
|
, on (true) {}
|
||||||
|
|
||||||
virtual type_t type() const { return type_pot; }
|
MidiByteArray set_mode (Mode);
|
||||||
|
MidiByteArray set_value (float);
|
||||||
|
MidiByteArray set_onoff (bool);
|
||||||
|
MidiByteArray set_all (float, bool, Mode);
|
||||||
|
|
||||||
virtual const LedRing & led_ring() const {return _led_ring; }
|
MidiByteArray zero() { return set_value (0.0); }
|
||||||
|
|
||||||
|
MidiByteArray update_message ();
|
||||||
|
|
||||||
static Control* factory (Surface&, int id, const char*, Group&);
|
static Control* factory (Surface&, int id, const char*, Group&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
LedRing _led_ring;
|
float value;
|
||||||
|
Mode mode;
|
||||||
|
bool on;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,6 @@
|
||||||
#include "surface.h"
|
#include "surface.h"
|
||||||
#include "button.h"
|
#include "button.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
#include "ledring.h"
|
|
||||||
#include "pot.h"
|
#include "pot.h"
|
||||||
#include "fader.h"
|
#include "fader.h"
|
||||||
#include "jog.h"
|
#include "jog.h"
|
||||||
|
|
@ -110,12 +109,8 @@ void Strip::add (Control & control)
|
||||||
_fader_touch = reinterpret_cast<Button*>(&control);
|
_fader_touch = reinterpret_cast<Button*>(&control);
|
||||||
} else if (control.name() == "meter") {
|
} else if (control.name() == "meter") {
|
||||||
_meter = reinterpret_cast<Meter*>(&control);
|
_meter = reinterpret_cast<Meter*>(&control);
|
||||||
} else if (control.type() == Control::type_led || control.type() == Control::type_led_ring) {
|
|
||||||
// relax
|
|
||||||
} else {
|
} else {
|
||||||
ostringstream os;
|
// relax
|
||||||
os << "Strip::add: unknown control type " << control;
|
|
||||||
throw MackieControlException (os.str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,27 +303,24 @@ Strip::notify_all()
|
||||||
void
|
void
|
||||||
Strip::notify_solo_changed ()
|
Strip::notify_solo_changed ()
|
||||||
{
|
{
|
||||||
if (_route) {
|
if (_route && _solo) {
|
||||||
Button& button = solo();
|
_surface->write (_solo->led().set_state (_route->soloed() ? on : off));
|
||||||
_surface->write (button.led().set_state (_route->soloed() ? on : off));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Strip::notify_mute_changed ()
|
Strip::notify_mute_changed ()
|
||||||
{
|
{
|
||||||
if (_route) {
|
if (_route && _mute) {
|
||||||
Button & button = mute();
|
_surface->write (_mute->led().set_state (_route->muted() ? on : off));
|
||||||
_surface->write (button.led().set_state (_route->muted() ? on : off));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Strip::notify_record_enable_changed ()
|
Strip::notify_record_enable_changed ()
|
||||||
{
|
{
|
||||||
if (_route) {
|
if (_route && _recenable) {
|
||||||
Button & button = recenable();
|
_surface->write (_recenable->led().set_state (_route->record_enabled() ? on : off));
|
||||||
_surface->write (button.led().set_state (_route->record_enabled() ? on : off));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -350,19 +342,12 @@ Strip::notify_gain_changed (bool force_update)
|
||||||
if (_route) {
|
if (_route) {
|
||||||
Fader & fader = gain();
|
Fader & fader = gain();
|
||||||
|
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("route %1 gain change, update fader %2 on port %3 in-use ? %4\n",
|
|
||||||
_route->name(),
|
|
||||||
fader.raw_id(),
|
|
||||||
_surface->port().output_port().name(),
|
|
||||||
fader.in_use()));
|
|
||||||
if (!fader.in_use()) {
|
if (!fader.in_use()) {
|
||||||
float gain_value = gain_to_slider_position (_route->gain_control()->get_value());
|
float position = gain_to_slider_position (_route->gain_control()->get_value());
|
||||||
// check that something has actually changed
|
// check that something has actually changed
|
||||||
if (force_update || gain_value != _last_gain_written) {
|
if (force_update || position != _last_gain_written) {
|
||||||
_surface->write (builder.build_fader (fader, gain_value));
|
_surface->write (fader.set_position (position));
|
||||||
_last_gain_written = gain_value;
|
_last_gain_written = position;
|
||||||
} else {
|
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader not updated because gain still equals %1\n", gain_value));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -385,8 +370,8 @@ Strip::notify_property_changed (const PropertyChange& what_changed)
|
||||||
line1 = PBD::short_version (fullname, 6);
|
line1 = PBD::short_version (fullname, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
_surface->write (builder.strip_display (*_surface, *this, 0, line1));
|
_surface->write (display (0, line1));
|
||||||
_surface->write (builder.strip_display_blank (*_surface, *this, 1));
|
_surface->write (blank_display (1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -396,13 +381,16 @@ Strip::notify_panner_changed (bool force_update)
|
||||||
if (_route) {
|
if (_route) {
|
||||||
Pot & pot = vpot();
|
Pot & pot = vpot();
|
||||||
boost::shared_ptr<Panner> panner = _route->panner();
|
boost::shared_ptr<Panner> panner = _route->panner();
|
||||||
|
|
||||||
if (panner) {
|
if (panner) {
|
||||||
double pos = panner->position ();
|
double pos = panner->position ();
|
||||||
|
|
||||||
// cache the MidiByteArray here, because the mackie led control is much lower
|
// cache the MidiByteArray here, because the mackie led control is much lower
|
||||||
// resolution than the panner control. So we save lots of byte
|
// resolution than the panner control. So we save lots of byte
|
||||||
// sends in spite of more work on the comparison
|
// sends in spite of more work on the comparison
|
||||||
MidiByteArray bytes = builder.build_led_ring (pot, ControlState (on, pos), MackieMidiBuilder::midi_pot_mode_dot);
|
|
||||||
|
MidiByteArray bytes = pot.set_all (pos, true, Pot::dot);
|
||||||
|
|
||||||
// check that something has actually changed
|
// check that something has actually changed
|
||||||
if (force_update || bytes != _last_pan_written)
|
if (force_update || bytes != _last_pan_written)
|
||||||
{
|
{
|
||||||
|
|
@ -410,7 +398,7 @@ Strip::notify_panner_changed (bool force_update)
|
||||||
_last_pan_written = bytes;
|
_last_pan_written = bytes;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_surface->write (builder.zero_control (pot));
|
_surface->write (pot.zero());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -418,10 +406,16 @@ Strip::notify_panner_changed (bool force_update)
|
||||||
bool
|
bool
|
||||||
Strip::handle_button (SurfacePort & port, Control & control, ButtonState bs)
|
Strip::handle_button (SurfacePort & port, Control & control, ButtonState bs)
|
||||||
{
|
{
|
||||||
|
Button* button = dynamic_cast<Button*>(&control);
|
||||||
|
|
||||||
|
if (!button) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!_route) {
|
if (!_route) {
|
||||||
// no route so always switch the light off
|
// no route so always switch the light off
|
||||||
// because no signals will be emitted by a non-route
|
// because no signals will be emitted by a non-route
|
||||||
_surface->write (control.led().set_state (off));
|
_surface->write (button->led().set_state (off));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -500,3 +494,61 @@ Strip::update_meter ()
|
||||||
float dB = const_cast<PeakMeter&> (_route->peak_meter()).peak_power (0);
|
float dB = const_cast<PeakMeter&> (_route->peak_meter()).peak_power (0);
|
||||||
_surface->write (meter().update_message (dB));
|
_surface->write (meter().update_message (dB));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Strip::zero ()
|
||||||
|
{
|
||||||
|
MidiByteArray retval;
|
||||||
|
|
||||||
|
for (Group::Controls::const_iterator it = _controls.begin(); it != _controls.end(); ++it) {
|
||||||
|
retval << (*it)->zero ();
|
||||||
|
}
|
||||||
|
|
||||||
|
retval << blank_display (0);
|
||||||
|
retval << blank_display (1);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Strip::blank_display (uint32_t line_number)
|
||||||
|
{
|
||||||
|
return display (line_number, string());
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Strip::display (uint32_t line_number, const std::string& line)
|
||||||
|
{
|
||||||
|
assert (line_number <= 1);
|
||||||
|
|
||||||
|
MidiByteArray retval;
|
||||||
|
|
||||||
|
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip_display index: %1, line %2 = %3\n", _index, line_number, line));
|
||||||
|
|
||||||
|
// sysex header
|
||||||
|
retval << _surface->sysex_hdr();
|
||||||
|
|
||||||
|
// code for display
|
||||||
|
retval << 0x12;
|
||||||
|
// offset (0 to 0x37 first line, 0x38 to 0x6f for second line)
|
||||||
|
retval << (_index * 7 + (line_number * 0x38));
|
||||||
|
|
||||||
|
// ascii data to display
|
||||||
|
retval << line;
|
||||||
|
// pad with " " out to 6 chars
|
||||||
|
for (int i = line.length(); i < 6; ++i) {
|
||||||
|
retval << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
// column spacer, unless it's the right-hand column
|
||||||
|
if (_index < 7) {
|
||||||
|
retval << ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
// sysex trailer
|
||||||
|
retval << MIDI::eox;
|
||||||
|
|
||||||
|
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("MackieMidiBuilder::strip_display midi: %1\n", retval));
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,11 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "pbd/property_basics.h"
|
#include "pbd/property_basics.h"
|
||||||
|
#include "pbd/signals.h"
|
||||||
|
|
||||||
#include "control_group.h"
|
#include "control_group.h"
|
||||||
#include "mackie_midi_builder.h"
|
#include "types.h"
|
||||||
|
#include "midi_byte_array.h"
|
||||||
|
|
||||||
namespace ARDOUR {
|
namespace ARDOUR {
|
||||||
class Route;
|
class Route;
|
||||||
|
|
@ -21,6 +23,7 @@ class Button;
|
||||||
class Pot;
|
class Pot;
|
||||||
class Fader;
|
class Fader;
|
||||||
class Meter;
|
class Meter;
|
||||||
|
class SurfacePort;
|
||||||
|
|
||||||
struct StripControlDefinition {
|
struct StripControlDefinition {
|
||||||
const char* name;
|
const char* name;
|
||||||
|
|
@ -78,6 +81,10 @@ public:
|
||||||
|
|
||||||
void periodic ();
|
void periodic ();
|
||||||
|
|
||||||
|
MidiByteArray display (uint32_t line_number, const std::string&);
|
||||||
|
MidiByteArray blank_display (uint32_t line_number);
|
||||||
|
MidiByteArray zero ();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Button* _solo;
|
Button* _solo;
|
||||||
Button* _recenable;
|
Button* _recenable;
|
||||||
|
|
@ -91,8 +98,6 @@ private:
|
||||||
int _index;
|
int _index;
|
||||||
Surface* _surface;
|
Surface* _surface;
|
||||||
|
|
||||||
MackieMidiBuilder builder;
|
|
||||||
|
|
||||||
boost::shared_ptr<ARDOUR::Route> _route;
|
boost::shared_ptr<ARDOUR::Route> _route;
|
||||||
PBD::ScopedConnectionList route_connections;
|
PBD::ScopedConnectionList route_connections;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,12 @@
|
||||||
#include "surface_port.h"
|
#include "surface_port.h"
|
||||||
#include "surface.h"
|
#include "surface.h"
|
||||||
#include "strip.h"
|
#include "strip.h"
|
||||||
#include "mackie_midi_builder.h"
|
|
||||||
#include "mackie_control_protocol.h"
|
#include "mackie_control_protocol.h"
|
||||||
#include "mackie_jog_wheel.h"
|
#include "mackie_jog_wheel.h"
|
||||||
|
|
||||||
#include "strip.h"
|
#include "strip.h"
|
||||||
#include "button.h"
|
#include "button.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
#include "ledring.h"
|
|
||||||
#include "pot.h"
|
#include "pot.h"
|
||||||
#include "fader.h"
|
#include "fader.h"
|
||||||
#include "jog.h"
|
#include "jog.h"
|
||||||
|
|
@ -260,14 +258,6 @@ Surface::init_strips ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
Surface::display_timecode (const std::string & timecode, const std::string & timecode_last)
|
|
||||||
{
|
|
||||||
if (has_timecode_display()) {
|
|
||||||
_port->write (builder.timecode_display (*this, timecode, timecode_last));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float
|
float
|
||||||
Surface::scaled_delta (const ControlState & state, float current_speed)
|
Surface::scaled_delta (const ControlState & state, float current_speed)
|
||||||
{
|
{
|
||||||
|
|
@ -279,10 +269,10 @@ Surface::display_bank_start (uint32_t current_bank)
|
||||||
{
|
{
|
||||||
if (current_bank == 0) {
|
if (current_bank == 0) {
|
||||||
// send Ar. to 2-char display on the master
|
// send Ar. to 2-char display on the master
|
||||||
_port->write (builder.two_char_display ("Ar", ".."));
|
_port->write (two_char_display ("Ar", ".."));
|
||||||
} else {
|
} else {
|
||||||
// write the current first remote_id to the 2-char display
|
// write the current first remote_id to the 2-char display
|
||||||
_port->write (builder.two_char_display (current_bank));
|
_port->write (two_char_display (current_bank));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -292,7 +282,10 @@ Surface::blank_jog_ring ()
|
||||||
Control* control = controls_by_name["jog"];
|
Control* control = controls_by_name["jog"];
|
||||||
|
|
||||||
if (control) {
|
if (control) {
|
||||||
_port->write (builder.build_led_ring (*(dynamic_cast<Pot*> (control)), off));
|
Pot* pot = dynamic_cast<Pot*> (control);
|
||||||
|
if (pot) {
|
||||||
|
_port->write (pot->set_onoff (false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -423,8 +416,11 @@ Surface::handle_control_event (Control & control, const ControlState & state)
|
||||||
// the state of the controls on the surface is usually updated
|
// the state of the controls on the surface is usually updated
|
||||||
// from UI events.
|
// from UI events.
|
||||||
|
|
||||||
switch (control.type()) {
|
Fader* fader = dynamic_cast<Fader*> (&control);
|
||||||
case Control::type_fader:
|
Button* button = dynamic_cast<Button*> (&control);
|
||||||
|
Pot* pot = dynamic_cast<Pot*> (&control);
|
||||||
|
|
||||||
|
if (fader) {
|
||||||
// find the route in the route table for the id
|
// find the route in the route table for the id
|
||||||
// if the route isn't available, skip it
|
// if the route isn't available, skip it
|
||||||
// at which point the fader should just reset itself
|
// at which point the fader should just reset itself
|
||||||
|
|
@ -441,25 +437,25 @@ Surface::handle_control_event (Control & control, const ControlState & state)
|
||||||
// must echo bytes back to slider now, because
|
// must echo bytes back to slider now, because
|
||||||
// the notifier only works if the fader is not being
|
// the notifier only works if the fader is not being
|
||||||
// touched. Which it is if we're getting input.
|
// touched. Which it is if we're getting input.
|
||||||
_port->write (builder.build_fader ((Fader&)control, state.pos));
|
_port->write (fader->set_position (state.pos));
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
|
|
||||||
case Control::type_button:
|
if (button) {
|
||||||
if (strip) {
|
if (strip) {
|
||||||
strip->handle_button (*_port, control, state.button_state);
|
strip->handle_button (*_port, control, state.button_state);
|
||||||
} else {
|
} else {
|
||||||
// handle all non-strip buttons
|
// handle all non-strip buttons
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", control.id()));
|
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("global button %1\n", control.raw_id()));
|
||||||
_mcp.handle_button_event (*this, dynamic_cast<Button&>(control), state.button_state);
|
_mcp.handle_button_event (*this, dynamic_cast<Button&>(control), state.button_state);
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
}
|
||||||
|
|
||||||
// pot (jog wheel, external control)
|
// pot (jog wheel, external control)
|
||||||
case Control::type_pot:
|
|
||||||
|
if (pot) {
|
||||||
if (strip) {
|
if (strip) {
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip pot %1\n", control.id()));
|
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("strip pot %1\n", control.raw_id()));
|
||||||
if (route) {
|
if (route) {
|
||||||
boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
|
boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
|
||||||
// pan for mono input routes, or stereo linked panners
|
// pan for mono input routes, or stereo linked panners
|
||||||
|
|
@ -474,23 +470,21 @@ Surface::handle_control_event (Control & control, const ControlState & state)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// it's a pot for an umnapped route, so turn all the lights off
|
// it's a pot for an umnapped route, so turn all the lights off
|
||||||
_port->write (builder.build_led_ring (dynamic_cast<Pot &> (control), off));
|
Pot* pot = dynamic_cast<Pot*> (&control);
|
||||||
|
if (pot) {
|
||||||
|
_port->write (pot->set_onoff (false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (control.is_jog()) {
|
JogWheel* wheel = dynamic_cast<JogWheel*> (pot);
|
||||||
|
if (wheel) {
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", state.ticks));
|
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("Jog wheel moved %1\n", state.ticks));
|
||||||
if (_jog_wheel) {
|
wheel->jog_event (*_port, control, state);
|
||||||
_jog_wheel->jog_event (*_port, control, state);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("External controller moved %1\n", state.ticks));
|
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("External controller moved %1\n", state.ticks));
|
||||||
cout << "external controller" << state.ticks * state.sign << endl;
|
cout << "external controller" << state.ticks * state.sign << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -550,7 +544,7 @@ Surface::zero_all ()
|
||||||
|
|
||||||
// zero all strips
|
// zero all strips
|
||||||
for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
|
for (Strips::iterator it = strips.begin(); it != strips.end(); ++it) {
|
||||||
_port->write (builder.zero_strip (*this, **it));
|
_port->write ((*it)->zero());
|
||||||
}
|
}
|
||||||
|
|
||||||
// turn off global buttons and leds
|
// turn off global buttons and leds
|
||||||
|
|
@ -560,13 +554,13 @@ Surface::zero_all ()
|
||||||
for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
|
for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
|
||||||
Control & control = **it;
|
Control & control = **it;
|
||||||
if (!control.group().is_strip() && control.accepts_feedback()) {
|
if (!control.group().is_strip() && control.accepts_feedback()) {
|
||||||
_port->write (builder.zero_control (control));
|
_port->write (control.zero());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// any hardware-specific stuff
|
// any hardware-specific stuff
|
||||||
// clear 2-char display
|
// clear 2-char display
|
||||||
_port->write (builder.two_char_display ("LC"));
|
_port->write (two_char_display (" "));
|
||||||
|
|
||||||
// and the led ring for the master strip
|
// and the led ring for the master strip
|
||||||
blank_jog_ring ();
|
blank_jog_ring ();
|
||||||
|
|
@ -578,6 +572,7 @@ Surface::periodic ()
|
||||||
for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
|
for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
|
||||||
(*s)->periodic ();
|
(*s)->periodic ();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -591,22 +586,22 @@ Surface::jog_wheel_state_display (JogWheel::State state)
|
||||||
{
|
{
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case JogWheel::zoom:
|
case JogWheel::zoom:
|
||||||
_port->write (builder.two_char_display ("Zm"));
|
_port->write (two_char_display ("Zm"));
|
||||||
break;
|
break;
|
||||||
case JogWheel::scroll:
|
case JogWheel::scroll:
|
||||||
_port->write (builder.two_char_display ("Sc"));
|
_port->write (two_char_display ("Sc"));
|
||||||
break;
|
break;
|
||||||
case JogWheel::scrub:
|
case JogWheel::scrub:
|
||||||
_port->write (builder.two_char_display ("Sb"));
|
_port->write (two_char_display ("Sb"));
|
||||||
break;
|
break;
|
||||||
case JogWheel::shuttle:
|
case JogWheel::shuttle:
|
||||||
_port->write (builder.two_char_display ("Sh"));
|
_port->write (two_char_display ("Sh"));
|
||||||
break;
|
break;
|
||||||
case JogWheel::speed:
|
case JogWheel::speed:
|
||||||
_port->write (builder.two_char_display ("Sp"));
|
_port->write (two_char_display ("Sp"));
|
||||||
break;
|
break;
|
||||||
case JogWheel::select:
|
case JogWheel::select:
|
||||||
_port->write (builder.two_char_display ("Se"));
|
_port->write (two_char_display ("Se"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -625,3 +620,90 @@ Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
|
||||||
(*s)->set_route (*r);
|
(*s)->set_route (*r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char translate_seven_segment (char achar)
|
||||||
|
{
|
||||||
|
achar = toupper (achar);
|
||||||
|
if (achar >= 0x40 && achar <= 0x60)
|
||||||
|
return achar - 0x40;
|
||||||
|
else if (achar >= 0x21 && achar <= 0x3f)
|
||||||
|
return achar;
|
||||||
|
else
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Surface::two_char_display (const std::string & msg, const std::string & dots)
|
||||||
|
{
|
||||||
|
if (msg.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: msg must be exactly 2 characters");
|
||||||
|
if (dots.length() != 2) throw MackieControlException ("MackieMidiBuilder::two_char_display: dots must be exactly 2 characters");
|
||||||
|
|
||||||
|
MidiByteArray bytes (6, 0xb0, 0x4a, 0x00, 0xb0, 0x4b, 0x00);
|
||||||
|
|
||||||
|
// chars are understood by the surface in right-to-left order
|
||||||
|
// could also exchange the 0x4a and 0x4b, above
|
||||||
|
bytes[5] = translate_seven_segment (msg[0]) + (dots[0] == '.' ? 0x40 : 0x00);
|
||||||
|
bytes[2] = translate_seven_segment (msg[1]) + (dots[1] == '.' ? 0x40 : 0x00);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Surface::two_char_display (unsigned int value, const std::string & /*dots*/)
|
||||||
|
{
|
||||||
|
ostringstream os;
|
||||||
|
os << setfill('0') << setw(2) << value % 100;
|
||||||
|
return two_char_display (os.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Surface::display_timecode (const std::string & timecode, const std::string & timecode_last)
|
||||||
|
{
|
||||||
|
if (has_timecode_display()) {
|
||||||
|
_port->write (timecode_display (timecode, timecode_last));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MidiByteArray
|
||||||
|
Surface::timecode_display (const std::string & timecode, const std::string & last_timecode)
|
||||||
|
{
|
||||||
|
// if there's no change, send nothing, not even sysex header
|
||||||
|
if (timecode == last_timecode) return MidiByteArray();
|
||||||
|
|
||||||
|
// length sanity checking
|
||||||
|
string local_timecode = timecode;
|
||||||
|
|
||||||
|
// truncate to 10 characters
|
||||||
|
if (local_timecode.length() > 10) {
|
||||||
|
local_timecode = local_timecode.substr (0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pad to 10 characters
|
||||||
|
while (local_timecode.length() < 10) {
|
||||||
|
local_timecode += " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// find the suffix of local_timecode that differs from last_timecode
|
||||||
|
std::pair<string::const_iterator,string::iterator> pp = mismatch (last_timecode.begin(), last_timecode.end(), local_timecode.begin());
|
||||||
|
|
||||||
|
MidiByteArray retval;
|
||||||
|
|
||||||
|
// sysex header
|
||||||
|
retval << sysex_hdr();
|
||||||
|
|
||||||
|
// code for timecode display
|
||||||
|
retval << 0x10;
|
||||||
|
|
||||||
|
// translate characters. These are sent in reverse order of display
|
||||||
|
// hence the reverse iterators
|
||||||
|
string::reverse_iterator rend = reverse_iterator<string::iterator> (pp.second);
|
||||||
|
for (string::reverse_iterator it = local_timecode.rbegin(); it != rend; ++it) {
|
||||||
|
retval << translate_seven_segment (*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
// sysex trailer
|
||||||
|
retval << MIDI::eox;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
#include "controls.h"
|
#include "controls.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "mackie_midi_builder.h"
|
|
||||||
#include "mackie_jog_wheel.h"
|
#include "mackie_jog_wheel.h"
|
||||||
|
|
||||||
namespace MIDI {
|
namespace MIDI {
|
||||||
|
|
@ -33,7 +32,6 @@ class Fader;
|
||||||
class Jog;
|
class Jog;
|
||||||
class Pot;
|
class Pot;
|
||||||
class Led;
|
class Led;
|
||||||
class LedRing;
|
|
||||||
|
|
||||||
class Surface : public PBD::ScopedConnectionList
|
class Surface : public PBD::ScopedConnectionList
|
||||||
{
|
{
|
||||||
|
|
@ -132,6 +130,19 @@ public:
|
||||||
|
|
||||||
void handle_control_event (Mackie::Control & control, const Mackie::ControlState & state);
|
void handle_control_event (Mackie::Control & control, const Mackie::ControlState & state);
|
||||||
|
|
||||||
|
// display the first 2 chars of the msg in the 2 char display
|
||||||
|
// . is appended to the previous character, so A.B. would
|
||||||
|
// be two characters
|
||||||
|
MidiByteArray two_char_display (const std::string & msg, const std::string & dots = " ");
|
||||||
|
MidiByteArray two_char_display (unsigned int value, const std::string & dots = " ");
|
||||||
|
|
||||||
|
/**
|
||||||
|
Timecode display. Only the difference between timecode and last_timecode will
|
||||||
|
be encoded, to save midi bandwidth. If they're the same, an empty array will
|
||||||
|
be returned
|
||||||
|
*/
|
||||||
|
MidiByteArray timecode_display (const std::string & timecode, const std::string & last_timecode = "");
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void init_controls();
|
void init_controls();
|
||||||
void init_strips ();
|
void init_strips ();
|
||||||
|
|
@ -144,7 +155,6 @@ public:
|
||||||
bool _active;
|
bool _active;
|
||||||
bool _connected;
|
bool _connected;
|
||||||
Mackie::JogWheel* _jog_wheel;
|
Mackie::JogWheel* _jog_wheel;
|
||||||
MackieMidiBuilder builder;
|
|
||||||
|
|
||||||
void jog_wheel_state_display (Mackie::JogWheel::State state);
|
void jog_wheel_state_display (Mackie::JogWheel::State state);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,6 @@ class Strip;
|
||||||
class Group;
|
class Group;
|
||||||
class Pot;
|
class Pot;
|
||||||
class Led;
|
class Led;
|
||||||
class LedRing;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,10 @@ def build(bld):
|
||||||
led.cc
|
led.cc
|
||||||
mackie_control_protocol.cc
|
mackie_control_protocol.cc
|
||||||
mackie_jog_wheel.cc
|
mackie_jog_wheel.cc
|
||||||
mackie_midi_builder.cc
|
|
||||||
mcp_buttons.cc
|
mcp_buttons.cc
|
||||||
meter.cc
|
meter.cc
|
||||||
midi_byte_array.cc
|
midi_byte_array.cc
|
||||||
|
pot.cc
|
||||||
strip.cc
|
strip.cc
|
||||||
surface.cc
|
surface.cc
|
||||||
surface_port.cc
|
surface_port.cc
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue