mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-06 06:44:57 +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 type_t type() const { return type_button; };
|
||||
MidiByteArray zero() { return _led.set_state (off); }
|
||||
|
||||
MidiByteArray update_message () const;
|
||||
|
||||
|
|
|
|||
|
|
@ -22,13 +22,11 @@
|
|||
|
||||
#include "controls.h"
|
||||
#include "types.h"
|
||||
#include "mackie_midi_builder.h"
|
||||
#include "surface.h"
|
||||
#include "control_group.h"
|
||||
|
||||
#include "button.h"
|
||||
#include "led.h"
|
||||
#include "ledring.h"
|
||||
#include "pot.h"
|
||||
#include "fader.h"
|
||||
#include "jog.h"
|
||||
|
|
@ -75,10 +73,6 @@ ostream & Mackie::operator << (ostream & os, const Mackie::Control & control)
|
|||
os << " { ";
|
||||
os << "name: " << control.name();
|
||||
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 << ", ";
|
||||
os << "group: " << control.group().name();
|
||||
|
|
@ -87,16 +81,6 @@ ostream & Mackie::operator << (ostream & os, const Mackie::Control & control)
|
|||
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*
|
||||
Jog::factory (Surface& surface, int id, const char* name, Group& group)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -28,30 +28,21 @@
|
|||
#include "pbd/signals.h"
|
||||
|
||||
#include "mackie_control_exception.h"
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class Strip;
|
||||
class Group;
|
||||
class Led;
|
||||
class Surface;
|
||||
|
||||
class Control
|
||||
{
|
||||
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 {
|
||||
fader_base_id = 0x0,
|
||||
pot_base_id = 0x10,
|
||||
fader_base_id = 0xe0,
|
||||
pot_base_id = 0x30,
|
||||
jog_base_id = 0x3c,
|
||||
fader_touch_button_base_id = 0xe0,
|
||||
vselect_button_base_id = 0x20,
|
||||
|
|
@ -65,12 +56,6 @@ public:
|
|||
Control (int id, std::string name, Group& group);
|
||||
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 id of the control, but only guaranteed to be
|
||||
/// unique within the control type.
|
||||
|
|
@ -78,19 +63,17 @@ public:
|
|||
|
||||
const std::string & name() const { return _name; }
|
||||
Group & group() const { return _group; }
|
||||
|
||||
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;
|
||||
void set_in_use (bool);
|
||||
|
||||
/// Keep track of the timeout so it can be updated with more incoming events
|
||||
sigc::connection in_use_connection;
|
||||
|
||||
virtual MidiByteArray zero() = 0;
|
||||
|
||||
/** If we are doing an in_use timeout for a fader without touch, this
|
||||
* is its touch button control; otherwise 0.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -34,3 +34,16 @@ Fader::factory (Surface& surface, int id, const char* name, Group& group)
|
|||
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:
|
||||
Fader (int id, std::string name, Group & 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&);
|
||||
|
||||
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&);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,9 +36,10 @@ public:
|
|||
}
|
||||
|
||||
Led & led() { return *this; }
|
||||
type_t type() const { return type_led; }
|
||||
MidiByteArray set_state (LedState);
|
||||
|
||||
MidiByteArray zero() { return set_state (off); }
|
||||
|
||||
static Control* factory (Surface&, int id, const char*, Group&);
|
||||
|
||||
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 "mackie_control_exception.h"
|
||||
#include "mackie_midi_builder.h"
|
||||
#include "surface_port.h"
|
||||
#include "surface.h"
|
||||
|
||||
|
|
@ -365,8 +364,6 @@ MackieControlProtocol::periodic ()
|
|||
(*s)->periodic ();
|
||||
}
|
||||
|
||||
update_timecode_display();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,12 +33,12 @@
|
|||
#include "midi++/types.h"
|
||||
|
||||
#include "ardour/types.h"
|
||||
|
||||
#include "control_protocol/control_protocol.h"
|
||||
|
||||
#include "types.h"
|
||||
#include "midi_byte_array.h"
|
||||
#include "controls.h"
|
||||
#include "mackie_jog_wheel.h"
|
||||
#include "mackie_midi_builder.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace MIDI {
|
||||
|
|
@ -371,8 +371,6 @@ class MackieControlProtocol
|
|||
|
||||
int _modifier_state;
|
||||
|
||||
Mackie::MackieMidiBuilder builder;
|
||||
|
||||
typedef std::list<GSource*> PortSources;
|
||||
PortSources port_sources;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
#include "ardour/session.h"
|
||||
|
||||
#include "mackie_jog_wheel.h"
|
||||
|
||||
#include "mackie_control_protocol.h"
|
||||
#include "surface_port.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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ public:
|
|||
, last_segment_value_sent (-1)
|
||||
, overload_on (false) {}
|
||||
|
||||
virtual type_t type() const { return type_meter; }
|
||||
|
||||
MidiByteArray update_message (float dB);
|
||||
|
||||
MidiByteArray zero() { return update_message (-99999999.0); }
|
||||
|
||||
static Control* factory (Surface&, int id, const char*, Group&);
|
||||
|
||||
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__
|
||||
|
||||
#include "controls.h"
|
||||
#include "ledring.h"
|
||||
|
||||
namespace Mackie {
|
||||
|
||||
class Pot : public Control
|
||||
{
|
||||
public:
|
||||
enum Mode {
|
||||
dot = 0,
|
||||
boost_cut = 1,
|
||||
wrap = 2,
|
||||
spread = 3,
|
||||
};
|
||||
|
||||
Pot (int id, std::string name, Group & 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&);
|
||||
|
||||
private:
|
||||
LedRing _led_ring;
|
||||
private:
|
||||
float value;
|
||||
Mode mode;
|
||||
bool on;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
#include "surface.h"
|
||||
#include "button.h"
|
||||
#include "led.h"
|
||||
#include "ledring.h"
|
||||
#include "pot.h"
|
||||
#include "fader.h"
|
||||
#include "jog.h"
|
||||
|
|
@ -110,12 +109,8 @@ void Strip::add (Control & control)
|
|||
_fader_touch = reinterpret_cast<Button*>(&control);
|
||||
} else if (control.name() == "meter") {
|
||||
_meter = reinterpret_cast<Meter*>(&control);
|
||||
} else if (control.type() == Control::type_led || control.type() == Control::type_led_ring) {
|
||||
// relax
|
||||
} else {
|
||||
ostringstream os;
|
||||
os << "Strip::add: unknown control type " << control;
|
||||
throw MackieControlException (os.str());
|
||||
// relax
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -308,27 +303,24 @@ Strip::notify_all()
|
|||
void
|
||||
Strip::notify_solo_changed ()
|
||||
{
|
||||
if (_route) {
|
||||
Button& button = solo();
|
||||
_surface->write (button.led().set_state (_route->soloed() ? on : off));
|
||||
if (_route && _solo) {
|
||||
_surface->write (_solo->led().set_state (_route->soloed() ? on : off));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Strip::notify_mute_changed ()
|
||||
{
|
||||
if (_route) {
|
||||
Button & button = mute();
|
||||
_surface->write (button.led().set_state (_route->muted() ? on : off));
|
||||
if (_route && _mute) {
|
||||
_surface->write (_mute->led().set_state (_route->muted() ? on : off));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Strip::notify_record_enable_changed ()
|
||||
{
|
||||
if (_route) {
|
||||
Button & button = recenable();
|
||||
_surface->write (button.led().set_state (_route->record_enabled() ? on : off));
|
||||
if (_route && _recenable) {
|
||||
_surface->write (_recenable->led().set_state (_route->record_enabled() ? on : off));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -350,19 +342,12 @@ Strip::notify_gain_changed (bool force_update)
|
|||
if (_route) {
|
||||
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()) {
|
||||
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
|
||||
if (force_update || gain_value != _last_gain_written) {
|
||||
_surface->write (builder.build_fader (fader, gain_value));
|
||||
_last_gain_written = gain_value;
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("fader not updated because gain still equals %1\n", gain_value));
|
||||
if (force_update || position != _last_gain_written) {
|
||||
_surface->write (fader.set_position (position));
|
||||
_last_gain_written = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -385,8 +370,8 @@ Strip::notify_property_changed (const PropertyChange& what_changed)
|
|||
line1 = PBD::short_version (fullname, 6);
|
||||
}
|
||||
|
||||
_surface->write (builder.strip_display (*_surface, *this, 0, line1));
|
||||
_surface->write (builder.strip_display_blank (*_surface, *this, 1));
|
||||
_surface->write (display (0, line1));
|
||||
_surface->write (blank_display (1));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -396,13 +381,16 @@ Strip::notify_panner_changed (bool force_update)
|
|||
if (_route) {
|
||||
Pot & pot = vpot();
|
||||
boost::shared_ptr<Panner> panner = _route->panner();
|
||||
|
||||
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
|
||||
// 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
|
||||
if (force_update || bytes != _last_pan_written)
|
||||
{
|
||||
|
|
@ -410,7 +398,7 @@ Strip::notify_panner_changed (bool force_update)
|
|||
_last_pan_written = bytes;
|
||||
}
|
||||
} else {
|
||||
_surface->write (builder.zero_control (pot));
|
||||
_surface->write (pot.zero());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -418,10 +406,16 @@ Strip::notify_panner_changed (bool force_update)
|
|||
bool
|
||||
Strip::handle_button (SurfacePort & port, Control & control, ButtonState bs)
|
||||
{
|
||||
Button* button = dynamic_cast<Button*>(&control);
|
||||
|
||||
if (!button) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_route) {
|
||||
// no route so always switch the light off
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
@ -500,3 +494,61 @@ Strip::update_meter ()
|
|||
float dB = const_cast<PeakMeter&> (_route->peak_meter()).peak_power (0);
|
||||
_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 "pbd/property_basics.h"
|
||||
#include "pbd/signals.h"
|
||||
|
||||
#include "control_group.h"
|
||||
#include "mackie_midi_builder.h"
|
||||
#include "types.h"
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
namespace ARDOUR {
|
||||
class Route;
|
||||
|
|
@ -21,6 +23,7 @@ class Button;
|
|||
class Pot;
|
||||
class Fader;
|
||||
class Meter;
|
||||
class SurfacePort;
|
||||
|
||||
struct StripControlDefinition {
|
||||
const char* name;
|
||||
|
|
@ -78,6 +81,10 @@ public:
|
|||
|
||||
void periodic ();
|
||||
|
||||
MidiByteArray display (uint32_t line_number, const std::string&);
|
||||
MidiByteArray blank_display (uint32_t line_number);
|
||||
MidiByteArray zero ();
|
||||
|
||||
private:
|
||||
Button* _solo;
|
||||
Button* _recenable;
|
||||
|
|
@ -91,8 +98,6 @@ private:
|
|||
int _index;
|
||||
Surface* _surface;
|
||||
|
||||
MackieMidiBuilder builder;
|
||||
|
||||
boost::shared_ptr<ARDOUR::Route> _route;
|
||||
PBD::ScopedConnectionList route_connections;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,14 +17,12 @@
|
|||
#include "surface_port.h"
|
||||
#include "surface.h"
|
||||
#include "strip.h"
|
||||
#include "mackie_midi_builder.h"
|
||||
#include "mackie_control_protocol.h"
|
||||
#include "mackie_jog_wheel.h"
|
||||
|
||||
#include "strip.h"
|
||||
#include "button.h"
|
||||
#include "led.h"
|
||||
#include "ledring.h"
|
||||
#include "pot.h"
|
||||
#include "fader.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
|
||||
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) {
|
||||
// send Ar. to 2-char display on the master
|
||||
_port->write (builder.two_char_display ("Ar", ".."));
|
||||
_port->write (two_char_display ("Ar", ".."));
|
||||
} else {
|
||||
// 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"];
|
||||
|
||||
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
|
||||
// from UI events.
|
||||
|
||||
switch (control.type()) {
|
||||
case Control::type_fader:
|
||||
Fader* fader = dynamic_cast<Fader*> (&control);
|
||||
Button* button = dynamic_cast<Button*> (&control);
|
||||
Pot* pot = dynamic_cast<Pot*> (&control);
|
||||
|
||||
if (fader) {
|
||||
// find the route in the route table for the id
|
||||
// if the route isn't available, skip it
|
||||
// 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
|
||||
// the notifier only works if the fader is not being
|
||||
// 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) {
|
||||
strip->handle_button (*_port, control, state.button_state);
|
||||
} else {
|
||||
// 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);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
// pot (jog wheel, external control)
|
||||
case Control::type_pot:
|
||||
|
||||
if (pot) {
|
||||
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) {
|
||||
boost::shared_ptr<Panner> panner = route->panner_shell()->panner();
|
||||
// pan for mono input routes, or stereo linked panners
|
||||
|
|
@ -474,23 +470,21 @@ Surface::handle_control_event (Control & control, const ControlState & state)
|
|||
}
|
||||
} else {
|
||||
// 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 {
|
||||
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));
|
||||
if (_jog_wheel) {
|
||||
_jog_wheel->jog_event (*_port, control, state);
|
||||
}
|
||||
wheel->jog_event (*_port, control, state);
|
||||
} else {
|
||||
DEBUG_TRACE (DEBUG::MackieControl, string_compose ("External controller moved %1\n", state.ticks));
|
||||
cout << "external controller" << state.ticks * state.sign << endl;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -550,7 +544,7 @@ Surface::zero_all ()
|
|||
|
||||
// zero all strips
|
||||
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
|
||||
|
|
@ -560,13 +554,13 @@ Surface::zero_all ()
|
|||
for (Controls::iterator it = controls.begin(); it != controls.end(); ++it) {
|
||||
Control & control = **it;
|
||||
if (!control.group().is_strip() && control.accepts_feedback()) {
|
||||
_port->write (builder.zero_control (control));
|
||||
_port->write (control.zero());
|
||||
}
|
||||
}
|
||||
|
||||
// any hardware-specific stuff
|
||||
// clear 2-char display
|
||||
_port->write (builder.two_char_display ("LC"));
|
||||
_port->write (two_char_display (" "));
|
||||
|
||||
// and the led ring for the master strip
|
||||
blank_jog_ring ();
|
||||
|
|
@ -578,6 +572,7 @@ Surface::periodic ()
|
|||
for (Strips::iterator s = strips.begin(); s != strips.end(); ++s) {
|
||||
(*s)->periodic ();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -591,22 +586,22 @@ Surface::jog_wheel_state_display (JogWheel::State state)
|
|||
{
|
||||
switch (state) {
|
||||
case JogWheel::zoom:
|
||||
_port->write (builder.two_char_display ("Zm"));
|
||||
_port->write (two_char_display ("Zm"));
|
||||
break;
|
||||
case JogWheel::scroll:
|
||||
_port->write (builder.two_char_display ("Sc"));
|
||||
_port->write (two_char_display ("Sc"));
|
||||
break;
|
||||
case JogWheel::scrub:
|
||||
_port->write (builder.two_char_display ("Sb"));
|
||||
_port->write (two_char_display ("Sb"));
|
||||
break;
|
||||
case JogWheel::shuttle:
|
||||
_port->write (builder.two_char_display ("Sh"));
|
||||
_port->write (two_char_display ("Sh"));
|
||||
break;
|
||||
case JogWheel::speed:
|
||||
_port->write (builder.two_char_display ("Sp"));
|
||||
_port->write (two_char_display ("Sp"));
|
||||
break;
|
||||
case JogWheel::select:
|
||||
_port->write (builder.two_char_display ("Se"));
|
||||
_port->write (two_char_display ("Se"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -625,3 +620,90 @@ Surface::map_routes (const vector<boost::shared_ptr<Route> >& routes)
|
|||
(*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 "types.h"
|
||||
#include "mackie_midi_builder.h"
|
||||
#include "mackie_jog_wheel.h"
|
||||
|
||||
namespace MIDI {
|
||||
|
|
@ -33,7 +32,6 @@ class Fader;
|
|||
class Jog;
|
||||
class Pot;
|
||||
class Led;
|
||||
class LedRing;
|
||||
|
||||
class Surface : public PBD::ScopedConnectionList
|
||||
{
|
||||
|
|
@ -132,6 +130,19 @@ public:
|
|||
|
||||
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:
|
||||
void init_controls();
|
||||
void init_strips ();
|
||||
|
|
@ -144,7 +155,6 @@ public:
|
|||
bool _active;
|
||||
bool _connected;
|
||||
Mackie::JogWheel* _jog_wheel;
|
||||
MackieMidiBuilder builder;
|
||||
|
||||
void jog_wheel_state_display (Mackie::JogWheel::State state);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ class Strip;
|
|||
class Group;
|
||||
class Pot;
|
||||
class Led;
|
||||
class LedRing;
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@ def build(bld):
|
|||
led.cc
|
||||
mackie_control_protocol.cc
|
||||
mackie_jog_wheel.cc
|
||||
mackie_midi_builder.cc
|
||||
mcp_buttons.cc
|
||||
meter.cc
|
||||
midi_byte_array.cc
|
||||
pot.cc
|
||||
strip.cc
|
||||
surface.cc
|
||||
surface_port.cc
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue