"merge" (i.e. wholesale import) 2.0-ongoing Mackie code and then fix to compile in 3.0 context
git-svn-id: svn://localhost/ardour2/branches/3.0@4315 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
18
libs/surfaces/mackie/README
Normal file
18
libs/surfaces/mackie/README
Normal file
@@ -0,0 +1,18 @@
|
||||
For usage, see
|
||||
|
||||
http://www.ardour.org/files/manual/sn-mackie.html
|
||||
|
||||
unfortunately it's a bit outdated, so to get the latest manual, go to the manual
|
||||
directory in the ardour source tree, and say "make html" to build the latest.
|
||||
Then point your favourite browser at tmp/index.html
|
||||
|
||||
NOTES:
|
||||
|
||||
* support for alsa/sequencer ports is currently broken. We're working on it.
|
||||
|
||||
* you'll need to make port changes in etc/ardour2/ardour_system.rc and
|
||||
etc/ardour2/ardour.rc, otherwise they don't stay changed in ~/.ardour2/ardour.rc
|
||||
|
||||
|
||||
John Anderson
|
||||
panic@semiosix.com
|
||||
@@ -14,12 +14,16 @@ mackie = env.Clone()
|
||||
|
||||
domain = 'ardour_mackie'
|
||||
|
||||
mackie.Append(DOMAIN = domain, MAJOR = 1, MINOR = 0, MICRO = 0)
|
||||
mackie.Append(DOMAIN = domain, MAJOR = 1, MINOR = 1, MICRO = 0)
|
||||
mackie.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"")
|
||||
mackie.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
|
||||
mackie.Append(PACKAGE = domain)
|
||||
mackie.Append(POTFILE = domain + '.pot')
|
||||
|
||||
if mackie['DEBUG'] == 1:
|
||||
mackie.Append(CXXFLAGS="-DDEBUG")
|
||||
mackie.Append(CXXFLAGS="-DPORT_DEBUG")
|
||||
|
||||
if mackie['IS_OSX']:
|
||||
mackie.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
|
||||
|
||||
@@ -27,17 +31,21 @@ mackie_files=Split("""
|
||||
interface.cc
|
||||
midi_byte_array.cc
|
||||
controls.cc
|
||||
surface_port.cc
|
||||
dummy_port.cc
|
||||
mackie_port.cc
|
||||
route_signal.cc
|
||||
mackie_midi_builder.cc
|
||||
mackie_button_handler.cc
|
||||
mackie_control_protocol_poll.cc
|
||||
surface_port.cc
|
||||
mackie_port.cc
|
||||
types.cc
|
||||
surface.cc
|
||||
mackie_control_protocol.cc
|
||||
bcf_surface.cc
|
||||
bcf_surface_generated.cc
|
||||
mackie_surface.cc
|
||||
mackie_surface_generated.cc
|
||||
mackie_jog_wheel.cc
|
||||
""")
|
||||
|
||||
mackie.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
|
||||
@@ -53,7 +61,6 @@ mackie.Merge ([
|
||||
libraries['sigc2'],
|
||||
libraries['pbd'],
|
||||
libraries['midi++2'],
|
||||
libraries['evoral'],
|
||||
libraries['xml'],
|
||||
libraries['glib2'],
|
||||
libraries['glibmm2'],
|
||||
@@ -72,7 +79,7 @@ if mackie['SURFACES']:
|
||||
Default(libardour_mackie)
|
||||
if env['NLS']:
|
||||
i18n (mackie, mackie_files, env)
|
||||
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour3','surfaces'), libardour_mackie))
|
||||
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2','surfaces'), libardour_mackie))
|
||||
|
||||
env.Alias('tarball', env.Distribute (env['DISTTREE'],
|
||||
[ 'SConscript' ] +
|
||||
|
||||
@@ -1,20 +1,48 @@
|
||||
* how long can UI signal callbacks take to execute? What happens if they block?
|
||||
where ENSURE_CORRECT_THREAD is a macro that is modelled on ENSURE_GUI_THREAD
|
||||
if the handler is not called in the "correct thread", it will use a pseudo-RT-safe-enough technique to get the correct thread to recall "handler" later on, and return.
|
||||
|
||||
* jog with transport rolling doesn't work properly. My use of ScrollTimeline also doesn't work.
|
||||
* make loop button sensitive to current transport state
|
||||
* make sure rew button can go past the previous if pressed twice, relatively quickly.
|
||||
* finish button mapping. Only shifted buttons left for bcf.
|
||||
* implement handle_port_inactive properly
|
||||
* two bcf doesn't work
|
||||
* remappable buttons (OSC or Surfax?)
|
||||
* 7/1 configurable to 8
|
||||
* need an object that can encapsulate different port types, ie BCF vs MCU. Not at the surface level.
|
||||
* finish button implementations.
|
||||
* concurrency for bank switching? And make sure "old" events aren't sent to "new" faders
|
||||
* TODOs in code
|
||||
* removal of a route results in a strip that isn't dead, but doesn't have any effect on the session
|
||||
* use i18n. see string_compose
|
||||
* docs in manual, including button assignment diagram
|
||||
|
||||
MCU
|
||||
---
|
||||
* if mackie wheel moves too fast, it's ignored.
|
||||
* gain/panner display in second line
|
||||
* metering on second line
|
||||
* per-strip signal led
|
||||
* midi bandwidth?
|
||||
* Zoom buttons, from Jean-Martin Barbut. In fact I'm a bit desappointed with those functions, because I was used
|
||||
to use the 4 "N-E-S-W" buttons to zoom in and out with horizontal
|
||||
buttons and zoom in/out on track height with vertical buttons. That is
|
||||
with the zoom button in. with the zoom button off, it selects tracks
|
||||
with vertical buttons and selects regions with horizontal buttons. This
|
||||
was quite useful. The fact that the zoom button switches the wheel to
|
||||
zoom is fine, but it would be great to also have the track/region
|
||||
selection and the track height available from those buttons. I.e : you
|
||||
select a track with the vertical arrows and you increase/decrease the
|
||||
eight of the track with the zoom btn on. This combined with the region
|
||||
selection allows to zoom in a region (vertical and horizontal zoom) very
|
||||
easily.
|
||||
|
||||
|
||||
Later
|
||||
-----
|
||||
* how long can UI signal callbacks take to execute? What happens if they block?
|
||||
where ENSURE_CORRECT_THREAD is a macro that is modelled on ENSURE_GUI_THREAD
|
||||
if the handler is not called in the "correct thread", it will use a pseudo-RT-safe-enough technique to get the correct thread to recall "handler" later on, and return.
|
||||
* alsa/sequencer ports unstable. possibly problems with use of ::poll
|
||||
* use glib::Timer instead of mine. Actually don't because it's not very useable.
|
||||
* crash when mmc port set to mcu?
|
||||
* remove commented couts
|
||||
* Perhaps MackieControlProtocol shouldn't implement MackieButtonHandler
|
||||
* Need a HostAdapter class to encapsulate ardour calls
|
||||
* talk to route plugins
|
||||
* check for excessiveness (ie too many events making other subsystems work too hard)
|
||||
* Queueing of writes?
|
||||
* Generic surface code to common location
|
||||
* bulk remote id changes cause too many surface updates. use Config->remote_model.
|
||||
@@ -25,21 +53,16 @@ Later
|
||||
* mix busses and/or a "bus-only" bank/mode
|
||||
* what about surfaces like Mackie C4 and BCR2000?
|
||||
|
||||
Need UI integration
|
||||
-------------------
|
||||
UI integration
|
||||
--------------
|
||||
|
||||
* maybe use current snap state for jog wheel and ffwd/rew
|
||||
* Some indication on the UI of currently bank-switched-in routes?
|
||||
Useful for surfaces that don't have a scribble strip.
|
||||
* use current zoom setting and snap state for shuttle wheel
|
||||
|
||||
Actual Mackie
|
||||
-------------
|
||||
* docs claim that unit will send a host query on init.
|
||||
* test Mackie surface object. Apparently led rings don't work. Stereo busses?
|
||||
* timecode & 55 char displays
|
||||
* midi bandwidth
|
||||
|
||||
Bugs
|
||||
----
|
||||
|
||||
* definitely something wrong with remote_id assignment on session create
|
||||
(master strip assigned 0).
|
||||
* when using alsa/sequencer, some midi events intended for mcu port end up being
|
||||
read by the seq port.
|
||||
* MIDI::Port::type() returns _type, which is undefined. It might be in the descriptor
|
||||
* auditioner doesn't connect to master 1 and master 2 - it loses the spaces.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
#ifndef mackie_surface_bcf_h
|
||||
#define mackie_surface_bcf_h
|
||||
/*
|
||||
Generated by scripts/generate-surface.rb
|
||||
Initially generated by scripts/generate-surface.rb
|
||||
*/
|
||||
|
||||
#include "surface.h"
|
||||
@@ -20,6 +20,15 @@ public:
|
||||
|
||||
virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
|
||||
virtual void init_controls();
|
||||
|
||||
virtual void display_bank_start( SurfacePort & port, MackieMidiBuilder & builder, uint32_t current_bank );
|
||||
virtual void zero_all( SurfacePort & port, MackieMidiBuilder & builder );
|
||||
virtual void blank_jog_ring( SurfacePort & port, MackieMidiBuilder & builder );
|
||||
virtual bool has_timecode_display() const { return false; }
|
||||
|
||||
virtual float scrub_scaling_factor() { return 50.0; }
|
||||
virtual float scaled_delta( const ControlState & state, float current_speed );
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
1461
libs/surfaces/mackie/bcf_surface_generated.cc
Normal file
1461
libs/surfaces/mackie/bcf_surface_generated.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,76 @@ Strip::Strip( const std::string & name, int index )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
TODO could optimise this to use enum, but it's only
|
||||
called during the protocol class instantiation.
|
||||
|
||||
generated using
|
||||
|
||||
irb -r controls.rb
|
||||
sf=Surface.new
|
||||
sf.parse
|
||||
controls = sf.groups.find{|x| x[0] =~ /strip/}.each{|x| puts x[1]}
|
||||
controls[1].each {|x| puts "\telse if ( control.name() == \"#{x.name}\" )\n\t{\n\t\t_#{x.name} = reinterpret_cast<#{x.class.name}*>(&control);\n\t}\n"}
|
||||
*/
|
||||
void Strip::add( Control & control )
|
||||
{
|
||||
Group::add( control );
|
||||
if ( control.name() == "gain" )
|
||||
{
|
||||
_gain = reinterpret_cast<Fader*>(&control);
|
||||
}
|
||||
else if ( control.name() == "vpot" )
|
||||
{
|
||||
_vpot = reinterpret_cast<Pot*>(&control);
|
||||
}
|
||||
else if ( control.name() == "recenable" )
|
||||
{
|
||||
_recenable = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "solo" )
|
||||
{
|
||||
_solo = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "mute" )
|
||||
{
|
||||
_mute = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "select" )
|
||||
{
|
||||
_select = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "vselect" )
|
||||
{
|
||||
_vselect = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "fader_touch" )
|
||||
{
|
||||
_fader_touch = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.type() == Control::type_led || control.type() == Control::type_led_ring )
|
||||
{
|
||||
// do nothing
|
||||
cout << "Strip::add not adding " << control << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
ostringstream os;
|
||||
os << "Strip::add: unknown control type " << control;
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
}
|
||||
|
||||
Control::Control( int id, int ordinal, std::string name, Group & group )
|
||||
: _id( id )
|
||||
, _ordinal( ordinal )
|
||||
, _name( name )
|
||||
, _group( group )
|
||||
, _in_use( false )
|
||||
, _in_use_timeout( 250 )
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
generated with
|
||||
|
||||
@@ -107,3 +177,58 @@ Button & Strip::fader_touch()
|
||||
throw MackieControlException( "fader_touch is null" );
|
||||
return *_fader_touch;
|
||||
}
|
||||
|
||||
bool Control::in_use() const
|
||||
{
|
||||
return _in_use;
|
||||
}
|
||||
|
||||
Control & Control::in_use( bool rhs )
|
||||
{
|
||||
_in_use = rhs;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ostream & Mackie::operator << ( ostream & os, const Mackie::Control & control )
|
||||
{
|
||||
os << typeid( control ).name();
|
||||
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 << "ordinal: " << dec << control.ordinal();
|
||||
os << ", ";
|
||||
os << "group: " << control.group().name();
|
||||
os << " }";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream & Mackie::operator << ( std::ostream & os, const Strip & strip )
|
||||
{
|
||||
os << typeid( strip ).name();
|
||||
os << " { ";
|
||||
os << "has_solo: " << boolalpha << strip.has_solo();
|
||||
os << ", ";
|
||||
os << "has_recenable: " << boolalpha << strip.has_recenable();
|
||||
os << ", ";
|
||||
os << "has_mute: " << boolalpha << strip.has_mute();
|
||||
os << ", ";
|
||||
os << "has_select: " << boolalpha << strip.has_select();
|
||||
os << ", ";
|
||||
os << "has_vselect: " << boolalpha << strip.has_vselect();
|
||||
os << ", ";
|
||||
os << "has_fader_touch: " << boolalpha << strip.has_fader_touch();
|
||||
os << ", ";
|
||||
os << "has_vpot: " << boolalpha << strip.has_vpot();
|
||||
os << ", ";
|
||||
os << "has_gain: " << boolalpha << strip.has_gain();
|
||||
os << " }";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#ifndef mackie_controls_h
|
||||
#define mackie_controls_h
|
||||
|
||||
#include <sigc++/sigc++.h>
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -83,6 +85,9 @@ class Fader;
|
||||
class Strip : public Group
|
||||
{
|
||||
public:
|
||||
/**
|
||||
\param is the index of the strip. 0-based.
|
||||
*/
|
||||
Strip( const std::string & name, int index );
|
||||
|
||||
virtual bool is_strip() const
|
||||
@@ -92,10 +97,11 @@ public:
|
||||
|
||||
virtual void add( Control & control );
|
||||
|
||||
/// This is the index of the strip
|
||||
/// This is the index of the strip. zero-based.
|
||||
int index() const { return _index; }
|
||||
|
||||
/// This is for Surface only
|
||||
/// index is zero-based
|
||||
void index( int rhs ) { _index = rhs; }
|
||||
|
||||
Button & solo();
|
||||
@@ -107,14 +113,14 @@ public:
|
||||
Pot & vpot();
|
||||
Fader & gain();
|
||||
|
||||
bool has_solo() { return _solo != 0; }
|
||||
bool has_recenable() { return _recenable != 0; }
|
||||
bool has_mute() { return _mute != 0; }
|
||||
bool has_select() { return _select != 0; }
|
||||
bool has_vselect() { return _vselect != 0; }
|
||||
bool has_fader_touch() { return _fader_touch != 0; }
|
||||
bool has_vpot() { return _vpot != 0; }
|
||||
bool has_gain() { return _gain != 0; }
|
||||
bool has_solo() const { return _solo != 0; }
|
||||
bool has_recenable() const { return _recenable != 0; }
|
||||
bool has_mute() const { return _mute != 0; }
|
||||
bool has_select() const { return _select != 0; }
|
||||
bool has_vselect() const { return _vselect != 0; }
|
||||
bool has_fader_touch() const { return _fader_touch != 0; }
|
||||
bool has_vpot() const { return _vpot != 0; }
|
||||
bool has_gain() const { return _gain != 0; }
|
||||
|
||||
private:
|
||||
Button * _solo;
|
||||
@@ -128,6 +134,8 @@ private:
|
||||
int _index;
|
||||
};
|
||||
|
||||
std::ostream & operator << ( std::ostream &, const Strip & );
|
||||
|
||||
class MasterStrip : public Strip
|
||||
{
|
||||
public:
|
||||
@@ -151,13 +159,9 @@ class Led;
|
||||
class Control
|
||||
{
|
||||
public:
|
||||
enum type_t { type_fader, type_button, type_pot, type_led, type_led_ring };
|
||||
|
||||
Control( int id, int ordinal, std::string name, Group & group )
|
||||
: _id( id ), _ordinal( ordinal ), _name( name ), _group( group )
|
||||
{
|
||||
}
|
||||
enum type_t { type_led, type_led_ring, type_fader = 0xe0, type_button = 0x90, type_pot = 0xb0 };
|
||||
|
||||
Control( int id, int ordinal, std::string name, Group & group );
|
||||
virtual ~Control() {}
|
||||
|
||||
virtual const Led & led() const
|
||||
@@ -165,17 +169,20 @@ public:
|
||||
throw MackieControlException( "no led available" );
|
||||
}
|
||||
|
||||
/// The midi id of the control
|
||||
/// type() << 8 + midi id of the control. This
|
||||
/// provides a unique id for any control on the surface.
|
||||
int id() const
|
||||
{
|
||||
return _id;
|
||||
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.
|
||||
int raw_id() const { return _id; }
|
||||
|
||||
/// The 1-based number of the control
|
||||
int ordinal() const
|
||||
{
|
||||
return _ordinal;
|
||||
}
|
||||
int ordinal() const { return _ordinal; }
|
||||
|
||||
const std::string & name() const
|
||||
{
|
||||
@@ -204,11 +211,32 @@ public:
|
||||
|
||||
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; }
|
||||
|
||||
/**
|
||||
Return true if the controlis in use, or false otherwise. For buttons
|
||||
this returns true if the button is currently being held down. For
|
||||
faders, the touch button has not been released. For pots, this returns
|
||||
true from the first move event until a timeout after the last move event.
|
||||
*/
|
||||
virtual bool in_use() const;
|
||||
virtual Control & in_use( bool );
|
||||
|
||||
/// The timeout value for this control. Normally defaulted to 250ms, but
|
||||
/// certain controls (ie jog wheel) may want to override it.
|
||||
virtual unsigned int in_use_timeout() { return _in_use_timeout; }
|
||||
|
||||
/// Keep track of the timeout so it can be updated with more incoming events
|
||||
sigc::connection in_use_connection;
|
||||
|
||||
private:
|
||||
int _id;
|
||||
int _ordinal;
|
||||
std::string _name;
|
||||
Group & _group;
|
||||
bool _in_use;
|
||||
unsigned int _in_use_timeout;
|
||||
};
|
||||
|
||||
std::ostream & operator << ( std::ostream & os, const Control & control );
|
||||
@@ -218,18 +246,10 @@ class Fader : public Control
|
||||
public:
|
||||
Fader( int id, int ordinal, std::string name, Group & group )
|
||||
: Control( id, ordinal, name, group )
|
||||
, _touch( false )
|
||||
{
|
||||
}
|
||||
|
||||
bool touch() const { return _touch; }
|
||||
|
||||
void touch( bool yn ) { _touch = yn; }
|
||||
|
||||
virtual type_t type() const { return type_fader; }
|
||||
|
||||
private:
|
||||
bool _touch;
|
||||
};
|
||||
|
||||
class Led : public Control
|
||||
@@ -260,7 +280,7 @@ public:
|
||||
}
|
||||
|
||||
virtual type_t type() const { return type_button; };
|
||||
|
||||
|
||||
private:
|
||||
Led _led;
|
||||
};
|
||||
@@ -296,6 +316,17 @@ private:
|
||||
LedRing _led_ring;
|
||||
};
|
||||
|
||||
class Jog : public Pot
|
||||
{
|
||||
public:
|
||||
Jog( int id, int ordinal, std::string name, Group & group )
|
||||
: Pot( id, ordinal, name, group )
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool is_jog() const { return true; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
58
libs/surfaces/mackie/dummy_port.cc
Normal file
58
libs/surfaces/mackie/dummy_port.cc
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "dummy_port.h"
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
#include <midi++/port.h>
|
||||
#include <midi++/types.h>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace Mackie;
|
||||
using namespace std;
|
||||
|
||||
DummyPort::DummyPort()
|
||||
{
|
||||
}
|
||||
|
||||
DummyPort::~DummyPort()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void DummyPort::open()
|
||||
{
|
||||
cout << "DummyPort::open" << endl;
|
||||
}
|
||||
|
||||
|
||||
void DummyPort::close()
|
||||
{
|
||||
cout << "DummyPort::close" << endl;
|
||||
}
|
||||
|
||||
|
||||
MidiByteArray DummyPort::read()
|
||||
{
|
||||
cout << "DummyPort::read" << endl;
|
||||
return MidiByteArray();
|
||||
}
|
||||
|
||||
|
||||
void DummyPort::write( const MidiByteArray & mba )
|
||||
{
|
||||
cout << "DummyPort::write " << mba << endl;
|
||||
}
|
||||
|
||||
MidiByteArray empty_midi_byte_array;
|
||||
|
||||
const MidiByteArray & DummyPort::sysex_hdr() const
|
||||
{
|
||||
cout << "DummyPort::sysex_hdr" << endl;
|
||||
return empty_midi_byte_array;
|
||||
}
|
||||
|
||||
int DummyPort::strips() const
|
||||
{
|
||||
cout << "DummyPort::strips" << endl;
|
||||
return 0;
|
||||
}
|
||||
60
libs/surfaces/mackie/dummy_port.h
Normal file
60
libs/surfaces/mackie/dummy_port.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright (C) 2008 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 dummy_port_h
|
||||
#define dummy_port_h
|
||||
|
||||
#include "surface_port.h"
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
namespace MIDI {
|
||||
class Port;
|
||||
}
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
/**
|
||||
A Dummy Port, to catch things that shouldn't be sent.
|
||||
*/
|
||||
class DummyPort : public SurfacePort
|
||||
{
|
||||
public:
|
||||
DummyPort();
|
||||
virtual ~DummyPort();
|
||||
|
||||
// when this is successful, active() should return true
|
||||
virtual void open();
|
||||
|
||||
// subclasses should call this before doing their own close
|
||||
virtual void close();
|
||||
|
||||
/// read bytes from the port. They'll either end up in the
|
||||
/// parser, or if that's not active they'll be returned
|
||||
virtual MidiByteArray read();
|
||||
|
||||
/// an easier way to output bytes via midi
|
||||
virtual void write( const MidiByteArray & );
|
||||
|
||||
virtual const MidiByteArray & sysex_hdr() const;
|
||||
virtual int strips() const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -64,9 +64,22 @@ new_mackie_protocol (ControlProtocolDescriptor* descriptor, Session* s)
|
||||
void
|
||||
delete_mackie_protocol (ControlProtocolDescriptor* descriptor, ControlProtocol* cp)
|
||||
{
|
||||
delete cp;
|
||||
try
|
||||
{
|
||||
delete cp;
|
||||
}
|
||||
catch ( exception & e )
|
||||
{
|
||||
cout << "Exception caught trying to destroy MackieControlProtocol: " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This is called on startup to check whether the lib should be loaded.
|
||||
|
||||
So anything that can be changed in the UI should not be used here to
|
||||
prevent loading of the lib.
|
||||
*/
|
||||
bool
|
||||
probe_mackie_protocol (ControlProtocolDescriptor* descriptor)
|
||||
{
|
||||
@@ -79,7 +92,11 @@ static ControlProtocolDescriptor mackie_descriptor = {
|
||||
ptr : 0,
|
||||
module : 0,
|
||||
mandatory : 0,
|
||||
supports_feedback : true,
|
||||
// actually, the surface does support feedback, but all this
|
||||
// flag does is show a submenu on the UI, which is useless for the mackie
|
||||
// because feedback is always on. In any case, who'd want to use the
|
||||
// mcu without the motorised sliders doing their thing?
|
||||
supports_feedback : false,
|
||||
probe : probe_mackie_protocol,
|
||||
initialize : new_mackie_protocol,
|
||||
destroy : delete_mackie_protocol
|
||||
|
||||
@@ -689,3 +689,23 @@ LedState MackieButtonHandler::global_solo_release( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::drop_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::drop_release( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::save_press( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
LedState MackieButtonHandler::save_release( Button & button )
|
||||
{
|
||||
return default_button_press( button );
|
||||
}
|
||||
|
||||
@@ -220,6 +220,12 @@ public:
|
||||
|
||||
virtual LedState global_solo_press( Button & );
|
||||
virtual LedState global_solo_release( Button & );
|
||||
|
||||
virtual LedState drop_press( Button & );
|
||||
virtual LedState drop_release( Button & );
|
||||
|
||||
virtual LedState save_press( Button & );
|
||||
virtual LedState save_release( Button & );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,9 +32,12 @@
|
||||
#include <control_protocol/control_protocol.h>
|
||||
#include "midi_byte_array.h"
|
||||
#include "controls.h"
|
||||
#include "dummy_port.h"
|
||||
#include "route_signal.h"
|
||||
#include "mackie_button_handler.h"
|
||||
#include "mackie_port.h"
|
||||
#include "mackie_jog_wheel.h"
|
||||
#include "timer.h"
|
||||
|
||||
namespace MIDI {
|
||||
class Port;
|
||||
@@ -93,14 +96,16 @@ class MackieControlProtocol
|
||||
/// Signal handler for Route::record_enable_changed
|
||||
void notify_record_enable_changed( Mackie::RouteSignal * );
|
||||
/// Signal handler for Route::gain_changed ( from IO )
|
||||
void notify_gain_changed( Mackie::RouteSignal * );
|
||||
void notify_gain_changed( Mackie::RouteSignal *, bool force_update = true );
|
||||
/// Signal handler for Route::name_change
|
||||
void notify_name_changed( Mackie::RouteSignal * );
|
||||
/// Signal handler from Panner::Change
|
||||
void notify_panner_changed( Mackie::RouteSignal * );
|
||||
void notify_panner_changed( Mackie::RouteSignal *, bool force_update = true );
|
||||
/// Signal handler for new routes added
|
||||
void notify_route_added( ARDOUR::Session::RouteList & );
|
||||
|
||||
/// Signal handler for Route::active_changed
|
||||
void notify_active_changed( Mackie::RouteSignal * );
|
||||
|
||||
void notify_remote_id_changed();
|
||||
|
||||
/// rebuild the current bank. Called on route added/removed and
|
||||
@@ -116,12 +121,15 @@ class MackieControlProtocol
|
||||
void notify_parameter_changed( const char * );
|
||||
void notify_solo_active_changed( bool );
|
||||
|
||||
// this is called to generate the midi to send in response to
|
||||
// a button press.
|
||||
/// Turn smpte on and beats off, or vice versa, depending
|
||||
/// on state of _timecode_type
|
||||
void update_smpte_beats_led();
|
||||
|
||||
/// this is called to generate the midi to send in response to a button press.
|
||||
void update_led( Mackie::Button & button, Mackie::LedState );
|
||||
|
||||
// calls update_led, but looks up the button by name
|
||||
void update_global_button( const std::string & name, Mackie::LedState );
|
||||
void update_global_led( const std::string & name, Mackie::LedState );
|
||||
|
||||
// transport button handler methods from MackieButtonHandler
|
||||
virtual Mackie::LedState frm_left_press( Mackie::Button & );
|
||||
@@ -183,6 +191,28 @@ class MackieControlProtocol
|
||||
virtual Mackie::LedState marker_press( Mackie::Button & );
|
||||
virtual Mackie::LedState marker_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState drop_press( Mackie::Button & );
|
||||
virtual Mackie::LedState drop_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState save_press( Mackie::Button & );
|
||||
virtual Mackie::LedState save_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState smpte_beats_press( Mackie::Button & );
|
||||
virtual Mackie::LedState smpte_beats_release( Mackie::Button & );
|
||||
|
||||
// jog wheel states
|
||||
virtual Mackie::LedState zoom_press( Mackie::Button & );
|
||||
virtual Mackie::LedState zoom_release( Mackie::Button & );
|
||||
|
||||
virtual Mackie::LedState scrub_press( Mackie::Button & );
|
||||
virtual Mackie::LedState scrub_release( Mackie::Button & );
|
||||
|
||||
/// This is the main MCU port, ie not an extender port
|
||||
/// Only for use by JogWheel
|
||||
const Mackie::SurfacePort & mcu_port() const;
|
||||
Mackie::SurfacePort & mcu_port();
|
||||
ARDOUR::Session & get_session() { return *session; }
|
||||
|
||||
protected:
|
||||
// create instances of MackiePort, depending on what's found in ardour.rc
|
||||
void create_ports();
|
||||
@@ -221,10 +251,6 @@ class MackieControlProtocol
|
||||
// delete all RouteSignal objects connecting Routes to Strips
|
||||
void clear_route_signals();
|
||||
|
||||
/// This is the main MCU port, ie not an extender port
|
||||
const Mackie::MackiePort & mcu_port() const;
|
||||
Mackie::MackiePort & mcu_port();
|
||||
|
||||
typedef std::vector<Mackie::RouteSignal*> RouteSignals;
|
||||
RouteSignals route_signals;
|
||||
|
||||
@@ -260,14 +286,17 @@ class MackieControlProtocol
|
||||
automation from the currently active routes and
|
||||
timecode displays.
|
||||
*/
|
||||
void poll_automation ();
|
||||
void poll_session_data();
|
||||
|
||||
// called from poll_automation to figure out which automations need to be sent
|
||||
void update_automation( Mackie::RouteSignal & );
|
||||
|
||||
|
||||
// also called from poll_automation to update timecode display
|
||||
void update_timecode_display();
|
||||
|
||||
std::string format_bbt_timecode( nframes_t now_frame );
|
||||
std::string format_smpte_timecode( nframes_t now_frame );
|
||||
|
||||
/**
|
||||
notification that the port is about to start it's init sequence.
|
||||
We must make sure that before this exits, the port is being polled
|
||||
@@ -293,6 +322,9 @@ class MackieControlProtocol
|
||||
typedef vector<Mackie::MackiePort*> MackiePorts;
|
||||
MackiePorts _ports;
|
||||
|
||||
/// Sometimes the real port goes away, and we want to contain the breakage
|
||||
Mackie::DummyPort _dummy_port;
|
||||
|
||||
// the thread that polls the ports for incoming midi data
|
||||
pthread_t thread;
|
||||
|
||||
@@ -321,6 +353,20 @@ class MackieControlProtocol
|
||||
int nfds;
|
||||
|
||||
bool _transport_previously_rolling;
|
||||
|
||||
// timer for two quick marker left presses
|
||||
Mackie::Timer _frm_left_last;
|
||||
|
||||
Mackie::JogWheel _jog_wheel;
|
||||
|
||||
// Timer for controlling midi bandwidth used by automation polls
|
||||
Mackie::Timer _automation_last;
|
||||
|
||||
// last written timecode string
|
||||
std::string _timecode_last;
|
||||
|
||||
// Which timecode are we displaying? BBT or SMPTE
|
||||
ARDOUR::AnyTime::Type _timecode_type;
|
||||
};
|
||||
|
||||
#endif // ardour_mackie_control_protocol_h
|
||||
|
||||
@@ -28,7 +28,15 @@ const char * MackieControlProtocol::default_port_name = "mcu";
|
||||
|
||||
bool MackieControlProtocol::probe()
|
||||
{
|
||||
return MIDI::Manager::instance()->port( default_port_name ) != 0;
|
||||
if ( MIDI::Manager::instance()->port( default_port_name ) == 0 )
|
||||
{
|
||||
error << "No port called mcu. Add it to ardour.rc." << endmsg;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void * MackieControlProtocol::monitor_work()
|
||||
@@ -52,8 +60,8 @@ void * MackieControlProtocol::monitor_work()
|
||||
update_ports();
|
||||
}
|
||||
}
|
||||
// poll for automation data from the routes
|
||||
poll_automation();
|
||||
// poll for session data that needs to go to the unit
|
||||
poll_session_data();
|
||||
}
|
||||
catch ( exception & e )
|
||||
{
|
||||
@@ -71,30 +79,51 @@ void * MackieControlProtocol::monitor_work()
|
||||
|
||||
void MackieControlProtocol::update_ports()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
cout << "MackieControlProtocol::update_ports" << endl;
|
||||
#endif
|
||||
if ( _ports_changed )
|
||||
{
|
||||
Glib::Mutex::Lock ul( update_mutex );
|
||||
// yes, this is a double-test locking paradigm, or whatever it's called
|
||||
// because we don't *always* need to acquire the lock for the first test
|
||||
#ifdef DEBUG
|
||||
cout << "MackieControlProtocol::update_ports lock acquired" << endl;
|
||||
#endif
|
||||
if ( _ports_changed )
|
||||
{
|
||||
// create new pollfd structures
|
||||
if ( pfd != 0 ) delete[] pfd;
|
||||
// TODO This might be a memory leak. How does thread cancellation cleanup work?
|
||||
if ( pfd != 0 )
|
||||
{
|
||||
delete[] pfd;
|
||||
pfd = 0;
|
||||
}
|
||||
pfd = new pollfd[_ports.size()];
|
||||
#ifdef DEBUG
|
||||
cout << "pfd: " << pfd << endl;
|
||||
#endif
|
||||
nfds = 0;
|
||||
|
||||
for( MackiePorts::iterator it = _ports.begin(); it != _ports.end(); ++it )
|
||||
{
|
||||
//cout << "adding port " << (*it)->port().name() << " to pollfd" << endl;
|
||||
// add the port any handler
|
||||
(*it)->connect_any();
|
||||
#ifdef DEBUG
|
||||
cout << "adding pollfd for port " << (*it)->port().name() << " to pollfd " << nfds << endl;
|
||||
#endif
|
||||
pfd[nfds].fd = (*it)->port().selectable();
|
||||
pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
|
||||
++nfds;
|
||||
}
|
||||
_ports_changed = false;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
cout << "MackieControlProtocol::update_ports signal" << endl;
|
||||
#endif
|
||||
update_cond.signal();
|
||||
}
|
||||
#ifdef DEBUG
|
||||
cout << "MackieControlProtocol::update_ports finish" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MackieControlProtocol::read_ports()
|
||||
@@ -127,12 +156,14 @@ bool MackieControlProtocol::poll_ports()
|
||||
if ( nfds < 1 )
|
||||
{
|
||||
lock.release();
|
||||
//cout << "poll_ports no ports" << endl;
|
||||
#ifdef DEBUG
|
||||
cout << "poll_ports no ports" << endl;
|
||||
#endif
|
||||
usleep( no_ports_sleep * 1000 );
|
||||
return false;
|
||||
}
|
||||
|
||||
int retval = poll( pfd, nfds, timeout );
|
||||
int retval = ::poll( pfd, nfds, timeout );
|
||||
if ( retval < 0 )
|
||||
{
|
||||
// gdb at work, perhaps
|
||||
@@ -179,14 +210,21 @@ void MackieControlProtocol::handle_port_active( SurfacePort * port )
|
||||
// finally update session state to the surface
|
||||
// TODO but this is also done in set_active, and
|
||||
// in fact update_surface won't execute unless
|
||||
#ifdef DEBUG
|
||||
cout << "update_surface in handle_port_active" << endl;
|
||||
#endif
|
||||
// _active == true
|
||||
//cout << "update_surface in handle_port_active" << endl;
|
||||
update_surface();
|
||||
}
|
||||
|
||||
void MackieControlProtocol::handle_port_init( Mackie::SurfacePort * sport )
|
||||
{
|
||||
//cout << "MackieControlProtocol::handle_port_init" << endl;
|
||||
#ifdef DEBUG
|
||||
cout << "MackieControlProtocol::handle_port_init" << endl;
|
||||
#endif
|
||||
_ports_changed = true;
|
||||
update_ports();
|
||||
#ifdef DEBUG
|
||||
cout << "MackieControlProtocol::handle_port_init finish" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
194
libs/surfaces/mackie/mackie_jog_wheel.cc
Normal file
194
libs/surfaces/mackie/mackie_jog_wheel.cc
Normal file
@@ -0,0 +1,194 @@
|
||||
#include <cmath>
|
||||
|
||||
#include "mackie_jog_wheel.h"
|
||||
|
||||
#include "mackie_control_protocol.h"
|
||||
#include "surface_port.h"
|
||||
#include "controls.h"
|
||||
#include "surface.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using namespace Mackie;
|
||||
using std::isnan;
|
||||
|
||||
JogWheel::JogWheel( MackieControlProtocol & mcp )
|
||||
: _mcp( mcp )
|
||||
, _transport_speed( 4.0 )
|
||||
, _transport_direction( 0 )
|
||||
, _shuttle_speed( 0.0 )
|
||||
{
|
||||
}
|
||||
|
||||
JogWheel::State JogWheel::jog_wheel_state() const
|
||||
{
|
||||
if ( !_jog_wheel_states.empty() )
|
||||
return _jog_wheel_states.top();
|
||||
else
|
||||
return scroll;
|
||||
}
|
||||
|
||||
void JogWheel::zoom_event( SurfacePort & port, Control & control, const ControlState & state )
|
||||
{
|
||||
}
|
||||
|
||||
void JogWheel::scrub_event( SurfacePort & port, Control & control, const ControlState & state )
|
||||
{
|
||||
}
|
||||
|
||||
void JogWheel::speed_event( SurfacePort & port, Control & control, const ControlState & state )
|
||||
{
|
||||
}
|
||||
|
||||
void JogWheel::scroll_event( SurfacePort & port, Control & control, const ControlState & state )
|
||||
{
|
||||
}
|
||||
|
||||
void JogWheel::jog_event( SurfacePort & port, Control & control, const ControlState & state )
|
||||
{
|
||||
// TODO use current snap-to setting?
|
||||
switch ( jog_wheel_state() )
|
||||
{
|
||||
case scroll:
|
||||
_mcp.ScrollTimeline( state.delta * state.sign );
|
||||
break;
|
||||
|
||||
case zoom:
|
||||
// Chunky Zoom.
|
||||
// TODO implement something similar to ScrollTimeline which
|
||||
// ends up in Editor::control_scroll for smoother zooming.
|
||||
if ( state.sign > 0 )
|
||||
for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomIn();
|
||||
else
|
||||
for ( unsigned int i = 0; i < state.ticks; ++i ) _mcp.ZoomOut();
|
||||
break;
|
||||
|
||||
case speed:
|
||||
// locally, _transport_speed is an positive value
|
||||
_transport_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() );
|
||||
|
||||
// make sure no weirdness gets to the session
|
||||
if ( _transport_speed < 0 || isnan( _transport_speed ) )
|
||||
{
|
||||
_transport_speed = 0.0;
|
||||
}
|
||||
|
||||
// translate _transport_speed speed to a signed transport velocity
|
||||
_mcp.get_session().request_transport_speed( transport_speed() * transport_direction() );
|
||||
break;
|
||||
|
||||
case scrub:
|
||||
{
|
||||
if ( state.sign != 0 )
|
||||
{
|
||||
add_scrub_interval( _scrub_timer.restart() );
|
||||
// x clicks per second => speed == 1.0
|
||||
float speed = _mcp.surface().scrub_scaling_factor() / average_scrub_interval() * state.ticks;
|
||||
_mcp.get_session().request_transport_speed( speed * state.sign );
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have a stop event
|
||||
check_scrubbing();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case shuttle:
|
||||
_shuttle_speed = _mcp.get_session().transport_speed();
|
||||
_shuttle_speed += _mcp.surface().scaled_delta( state, _mcp.get_session().transport_speed() );
|
||||
_mcp.get_session().request_transport_speed( _shuttle_speed );
|
||||
break;
|
||||
|
||||
case select:
|
||||
cout << "JogWheel select not implemented" << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void JogWheel::check_scrubbing()
|
||||
{
|
||||
// if the last elapsed is greater than the average + std deviation, then stop
|
||||
if ( !_scrub_intervals.empty() && _scrub_timer.elapsed() > average_scrub_interval() + std_dev_scrub_interval() )
|
||||
{
|
||||
_mcp.get_session().request_transport_speed( 0.0 );
|
||||
_scrub_intervals.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void JogWheel::push( State state )
|
||||
{
|
||||
_jog_wheel_states.push( state );
|
||||
}
|
||||
|
||||
void JogWheel::pop()
|
||||
{
|
||||
if ( _jog_wheel_states.size() > 0 )
|
||||
{
|
||||
_jog_wheel_states.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void JogWheel::zoom_state_toggle()
|
||||
{
|
||||
if ( jog_wheel_state() == zoom )
|
||||
pop();
|
||||
else
|
||||
push( zoom );
|
||||
}
|
||||
|
||||
JogWheel::State JogWheel::scrub_state_cycle()
|
||||
{
|
||||
State top = jog_wheel_state();
|
||||
if ( top == scrub )
|
||||
{
|
||||
// stop scrubbing and go to shuttle
|
||||
pop();
|
||||
push( shuttle );
|
||||
_shuttle_speed = 0.0;
|
||||
}
|
||||
else if ( top == shuttle )
|
||||
{
|
||||
// default to scroll, or the last selected
|
||||
pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
// start with scrub
|
||||
push( scrub );
|
||||
}
|
||||
|
||||
return jog_wheel_state();
|
||||
}
|
||||
|
||||
void JogWheel::add_scrub_interval( unsigned long elapsed )
|
||||
{
|
||||
if ( _scrub_intervals.size() > 5 )
|
||||
{
|
||||
_scrub_intervals.pop_front();
|
||||
}
|
||||
_scrub_intervals.push_back( elapsed );
|
||||
}
|
||||
|
||||
float JogWheel::average_scrub_interval()
|
||||
{
|
||||
float sum = 0.0;
|
||||
for ( std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it )
|
||||
{
|
||||
sum += *it;
|
||||
}
|
||||
return sum / _scrub_intervals.size();
|
||||
}
|
||||
|
||||
float JogWheel::std_dev_scrub_interval()
|
||||
{
|
||||
float average = average_scrub_interval();
|
||||
|
||||
// calculate standard deviation
|
||||
float sum = 0.0;
|
||||
for ( std::deque<unsigned long>::iterator it = _scrub_intervals.begin(); it != _scrub_intervals.end(); ++it )
|
||||
{
|
||||
sum += pow( *it - average, 2 );
|
||||
}
|
||||
return sqrt( sum / _scrub_intervals.size() -1 );
|
||||
}
|
||||
102
libs/surfaces/mackie/mackie_jog_wheel.h
Normal file
102
libs/surfaces/mackie/mackie_jog_wheel.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef mackie_jog_wheel
|
||||
#define mackie_jog_wheel
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
#include <stack>
|
||||
#include <deque>
|
||||
#include <queue>
|
||||
|
||||
class MackieControlProtocol;
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class SurfacePort;
|
||||
class Control;
|
||||
class ControlState;
|
||||
|
||||
/**
|
||||
A jog wheel can be used to control many things. This
|
||||
handles all of the states and state transitions.
|
||||
|
||||
Mainly it exists to avoid putting a bunch of messy
|
||||
stuff in MackieControlProtocol.
|
||||
|
||||
But it doesn't really know who it is, with stacks, queues and various
|
||||
boolean state variables.
|
||||
*/
|
||||
class JogWheel
|
||||
{
|
||||
public:
|
||||
enum State { scroll, zoom, speed, scrub, shuttle, select };
|
||||
|
||||
JogWheel( MackieControlProtocol & mcp );
|
||||
|
||||
/// As the wheel turns...
|
||||
void jog_event( SurfacePort & port, Control & control, const ControlState & state );
|
||||
|
||||
// These are for incoming button presses that change the internal state
|
||||
// but they're not actually used at the moment.
|
||||
void zoom_event( SurfacePort & port, Control & control, const ControlState & state );
|
||||
void scrub_event( SurfacePort & port, Control & control, const ControlState & state );
|
||||
void speed_event( SurfacePort & port, Control & control, const ControlState & state );
|
||||
void scroll_event( SurfacePort & port, Control & control, const ControlState & state );
|
||||
|
||||
/// Return the current jog wheel mode, which defaults to Scroll
|
||||
State jog_wheel_state() const;
|
||||
|
||||
/// The current transport speed for ffwd and rew. Can be
|
||||
/// set by wheel when they're pressed.
|
||||
float transport_speed() const { return _transport_speed; }
|
||||
|
||||
/// one of -1,0,1
|
||||
int transport_direction() const { return _transport_direction; }
|
||||
void transport_direction( int rhs ) { _transport_direction = rhs; }
|
||||
|
||||
void push( State state );
|
||||
void pop();
|
||||
|
||||
/// Turn zoom mode on and off
|
||||
void zoom_state_toggle();
|
||||
|
||||
/**
|
||||
Cycle scrub -> shuttle -> previous
|
||||
*/
|
||||
State scrub_state_cycle();
|
||||
|
||||
/// Check to see when the last scrub event was
|
||||
/// And stop scrubbing if it was too long ago.
|
||||
/// Intended to be called from a periodic timer of
|
||||
/// some kind.
|
||||
void check_scrubbing();
|
||||
|
||||
protected:
|
||||
void add_scrub_interval( unsigned long elapsed );
|
||||
float average_scrub_interval();
|
||||
float std_dev_scrub_interval();
|
||||
|
||||
private:
|
||||
MackieControlProtocol & _mcp;
|
||||
|
||||
/// transport speed for ffwd and rew, controller by jog
|
||||
float _transport_speed;
|
||||
int _transport_direction;
|
||||
|
||||
/// Speed for shuttle
|
||||
float _shuttle_speed;
|
||||
|
||||
/// a stack for keeping track of states
|
||||
std::stack<State> _jog_wheel_states;
|
||||
|
||||
/// So we know how fast to set the transport speed while scrubbing
|
||||
Timer _scrub_timer;
|
||||
|
||||
/// to keep track of what the current scrub rate is
|
||||
/// so we can calculate a moving average
|
||||
std::deque<unsigned long> _scrub_intervals;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -20,9 +20,11 @@
|
||||
#include <typeinfo>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <algorithm>
|
||||
|
||||
#include "controls.h"
|
||||
#include "midi_byte_array.h"
|
||||
#include "mackie_port.h"
|
||||
|
||||
using namespace Mackie;
|
||||
using namespace std;
|
||||
@@ -44,12 +46,12 @@ MIDI::byte MackieMidiBuilder::calculate_pot_value( midi_pot_mode mode, const Con
|
||||
return retval;
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state )
|
||||
MidiByteArray MackieMidiBuilder::build_led_ring( const Pot & pot, const ControlState & state, midi_pot_mode mode )
|
||||
{
|
||||
return build_led_ring( pot.led_ring(), state );
|
||||
return build_led_ring( pot.led_ring(), state, mode );
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const ControlState & state )
|
||||
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)
|
||||
@@ -58,9 +60,9 @@ MidiByteArray MackieMidiBuilder::build_led_ring( const LedRing & led_ring, const
|
||||
// the control type
|
||||
, midi_pot_id
|
||||
// the id
|
||||
, 0x20 + led_ring.id()
|
||||
, 0x20 + led_ring.raw_id()
|
||||
// the value
|
||||
, calculate_pot_value( midi_pot_mode_dot, state )
|
||||
, calculate_pot_value( mode, state )
|
||||
);
|
||||
}
|
||||
|
||||
@@ -82,7 +84,7 @@ MidiByteArray MackieMidiBuilder::build_led( const Led & led, LedState ls )
|
||||
|
||||
return MidiByteArray ( 3
|
||||
, midi_button_id
|
||||
, led.id()
|
||||
, led.raw_id()
|
||||
, state
|
||||
);
|
||||
}
|
||||
@@ -92,7 +94,7 @@ MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos )
|
||||
int posi = int( 0x3fff * pos );
|
||||
|
||||
return MidiByteArray ( 3
|
||||
, midi_fader_id | fader.id()
|
||||
, midi_fader_id | fader.raw_id()
|
||||
// lower-order bits
|
||||
, posi & 0x7f
|
||||
// higher-order bits
|
||||
@@ -100,7 +102,7 @@ MidiByteArray MackieMidiBuilder::build_fader( const Fader & fader, float pos )
|
||||
);
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::zero_strip( const Strip & strip )
|
||||
MidiByteArray MackieMidiBuilder::zero_strip( SurfacePort & port, const Strip & strip )
|
||||
{
|
||||
Group::Controls::const_iterator it = strip.controls().begin();
|
||||
MidiByteArray retval;
|
||||
@@ -110,6 +112,10 @@ MidiByteArray MackieMidiBuilder::zero_strip( const Strip & strip )
|
||||
if ( control.accepts_feedback() )
|
||||
retval << zero_control( control );
|
||||
}
|
||||
|
||||
// These must have sysex headers
|
||||
retval << strip_display_blank( port, strip, 0 );
|
||||
retval << strip_display_blank( port, strip, 1 );
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -171,3 +177,98 @@ MidiByteArray MackieMidiBuilder::two_char_display( unsigned int value, const std
|
||||
os << setfill('0') << setw(2) << value % 100;
|
||||
return two_char_display( os.str() );
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::strip_display_blank( SurfacePort & port, const Strip & strip, unsigned int line_number )
|
||||
{
|
||||
// 6 spaces, not 7 because strip_display adds a space where appropriate
|
||||
return strip_display( port, strip, line_number, " " );
|
||||
}
|
||||
|
||||
MidiByteArray MackieMidiBuilder::strip_display( SurfacePort & port, const Strip & strip, unsigned int line_number, const std::string & line )
|
||||
{
|
||||
if ( line_number > 1 )
|
||||
{
|
||||
throw runtime_error( "line_number must be 0 or 1" );
|
||||
}
|
||||
|
||||
if ( strip.index() > 7 )
|
||||
{
|
||||
throw runtime_error( "strip.index() must be between 0 and 7" );
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
cout << "MackieMidiBuilder::strip_display index: " << strip.index() << ", line " << line_number << ": " << line << endl;
|
||||
#endif
|
||||
|
||||
MidiByteArray retval;
|
||||
|
||||
// sysex header
|
||||
retval << port.sysex_hdr();
|
||||
|
||||
// code for display
|
||||
retval << 0x12;
|
||||
// offset (0 to 0x37 first line, 0x38 to 0x6f for second line )
|
||||
retval << ( strip.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;
|
||||
|
||||
#ifdef DEBUG
|
||||
cout << "MackieMidiBuilder::strip_display midi: " << retval << endl;
|
||||
#endif
|
||||
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( SurfacePort & port, 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 << port.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;
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
#include "types.h"
|
||||
#include "controls.h"
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
class SurfacePort;
|
||||
|
||||
/**
|
||||
This knows how to build midi messages given a control and
|
||||
a state.
|
||||
@@ -37,9 +40,9 @@ public:
|
||||
with the control id
|
||||
*/
|
||||
enum midi_types {
|
||||
midi_fader_id = 0xe0
|
||||
, midi_button_id = 0x90
|
||||
, midi_pot_id = 0xb0
|
||||
midi_fader_id = Control::type_fader
|
||||
, midi_button_id = Control::type_button
|
||||
, midi_pot_id = Control::type_pot
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -52,8 +55,8 @@ public:
|
||||
, midi_pot_mode_spread = 3
|
||||
};
|
||||
|
||||
MidiByteArray build_led_ring( const Pot & pot, const ControlState & );
|
||||
MidiByteArray build_led_ring( const LedRing & led_ring, const ControlState & );
|
||||
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_led( const Led & led, LedState ls );
|
||||
MidiByteArray build_led( const Button & button, LedState ls );
|
||||
@@ -61,7 +64,8 @@ public:
|
||||
MidiByteArray build_fader( const Fader & fader, float pos );
|
||||
|
||||
/// return bytes that will reset all controls to their zero positions
|
||||
MidiByteArray zero_strip( const Strip & strip );
|
||||
/// And blank the display for the strip. Pass SurfacePort so we know which sysex header to use.
|
||||
MidiByteArray zero_strip( SurfacePort &, const Strip & strip );
|
||||
|
||||
// provide bytes to zero the given control
|
||||
MidiByteArray zero_control( const Control & control );
|
||||
@@ -72,6 +76,25 @@ public:
|
||||
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( SurfacePort &, 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( SurfacePort &, const Strip & strip, unsigned int line_number, const std::string & line );
|
||||
|
||||
/// blank the strip LCD, ie write all spaces. Pass SurfacePort so we know which sysex header to use.
|
||||
MidiByteArray strip_display_blank( SurfacePort &, 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 & );
|
||||
};
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "controls.h"
|
||||
#include "surface.h"
|
||||
|
||||
#include <glibmm/main.h>
|
||||
|
||||
#include <midi++/types.h>
|
||||
#include <midi++/port.h>
|
||||
#include <sigc++/sigc++.h>
|
||||
@@ -49,14 +51,20 @@ MackiePort::MackiePort( MackieControlProtocol & mcp, MIDI::Port & port, int numb
|
||||
, _emulation( none )
|
||||
, _initialising( true )
|
||||
{
|
||||
//cout << "MackiePort::MackiePort" <<endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::MackiePort" <<endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
MackiePort::~MackiePort()
|
||||
{
|
||||
//cout << "~MackiePort" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "~MackiePort" << endl;
|
||||
#endif
|
||||
close();
|
||||
//cout << "~MackiePort finished" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "~MackiePort finished" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
int MackiePort::strips() const
|
||||
@@ -83,7 +91,9 @@ int MackiePort::strips() const
|
||||
// should really be in MackiePort
|
||||
void MackiePort::open()
|
||||
{
|
||||
//cout << "MackiePort::open " << *this << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::open " << *this << endl;
|
||||
#endif
|
||||
_sysex = port().input()->sysex.connect( ( mem_fun (*this, &MackiePort::handle_midi_sysex) ) );
|
||||
|
||||
// make sure the device is connected
|
||||
@@ -92,14 +102,18 @@ void MackiePort::open()
|
||||
|
||||
void MackiePort::close()
|
||||
{
|
||||
//cout << "MackiePort::close" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::close" << endl;
|
||||
#endif
|
||||
|
||||
// disconnect signals
|
||||
_any.disconnect();
|
||||
_sysex.disconnect();
|
||||
|
||||
// TODO emit a "closing" signal?
|
||||
//cout << "MackiePort::close finished" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::close finished" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
const MidiByteArray & MackiePort::sysex_hdr() const
|
||||
@@ -113,57 +127,6 @@ const MidiByteArray & MackiePort::sysex_hdr() const
|
||||
return mackie_sysex_hdr;
|
||||
}
|
||||
|
||||
Control & MackiePort::lookup_control( const MidiByteArray & bytes )
|
||||
{
|
||||
Control * control = 0;
|
||||
int midi_id = -1;
|
||||
MIDI::byte midi_type = bytes[0] & 0xf0; //0b11110000
|
||||
switch( midi_type )
|
||||
{
|
||||
// fader
|
||||
case MackieMidiBuilder::midi_fader_id:
|
||||
midi_id = bytes[0] & 0x0f;
|
||||
control = _mcp.surface().faders[midi_id];
|
||||
if ( control == 0 )
|
||||
{
|
||||
ostringstream os;
|
||||
os << "control for fader" << midi_id << " is null";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
break;
|
||||
|
||||
// button
|
||||
case MackieMidiBuilder::midi_button_id:
|
||||
midi_id = bytes[1];
|
||||
control = _mcp.surface().buttons[midi_id];
|
||||
if ( control == 0 )
|
||||
{
|
||||
ostringstream os;
|
||||
os << "control for button" << midi_id << " is null";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
break;
|
||||
|
||||
// pot (jog wheel, external control)
|
||||
case MackieMidiBuilder::midi_pot_id:
|
||||
midi_id = bytes[1] & 0x1f;
|
||||
control = _mcp.surface().pots[midi_id];
|
||||
if ( control == 0 )
|
||||
{
|
||||
ostringstream os;
|
||||
os << "control for button" << midi_id << " is null";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
ostringstream os;
|
||||
os << "Cannot find control for " << bytes;
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
return *control;
|
||||
}
|
||||
|
||||
MidiByteArray calculate_challenge_response( MidiByteArray::iterator begin, MidiByteArray::iterator end )
|
||||
{
|
||||
MidiByteArray l;
|
||||
@@ -186,7 +149,9 @@ MidiByteArray calculate_challenge_response( MidiByteArray::iterator begin, MidiB
|
||||
MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes )
|
||||
{
|
||||
// handle host connection query
|
||||
//cout << "host connection query: " << bytes << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "host connection query: " << bytes << endl;
|
||||
#endif
|
||||
|
||||
if ( bytes.size() != 18 )
|
||||
{
|
||||
@@ -207,7 +172,9 @@ MidiByteArray MackiePort::host_connection_query( MidiByteArray & bytes )
|
||||
// not used right now
|
||||
MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & bytes )
|
||||
{
|
||||
//cout << "host_connection_confirmation: " << bytes << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "host_connection_confirmation: " << bytes << endl;
|
||||
#endif
|
||||
|
||||
// decode host connection confirmation
|
||||
if ( bytes.size() != 14 )
|
||||
@@ -224,17 +191,20 @@ MidiByteArray MackiePort::host_connection_confirmation( const MidiByteArray & by
|
||||
|
||||
void MackiePort::probe_emulation( const MidiByteArray & bytes )
|
||||
{
|
||||
//cout << "MackiePort::probe_emulation: " << bytes.size() << ", " << bytes << endl;
|
||||
string version_string;
|
||||
for ( int i = 6; i < 11; ++i ) version_string.append( 1, (char)bytes[i] );
|
||||
//cout << "version_string: " << version_string << endl;
|
||||
#if 0
|
||||
cout << "MackiePort::probe_emulation: " << bytes.size() << ", " << bytes << endl;
|
||||
|
||||
MidiByteArray version_string;
|
||||
for ( int i = 6; i < 11; ++i ) version_string << bytes[i];
|
||||
cout << "version_string: " << version_string << endl;
|
||||
#endif
|
||||
|
||||
// TODO investigate using serial number. Also, possibly size of bytes might
|
||||
// give an indication. Also, apparently MCU sends non-documented messages
|
||||
// sometimes.
|
||||
if (!_initialising)
|
||||
{
|
||||
cout << "MackiePort::probe_emulation out of sequence." << endl;
|
||||
//cout << "MackiePort::probe_emulation out of sequence." << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -243,11 +213,15 @@ void MackiePort::probe_emulation( const MidiByteArray & bytes )
|
||||
|
||||
void MackiePort::init()
|
||||
{
|
||||
//cout << "MackiePort::init" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::init" << endl;
|
||||
#endif
|
||||
init_mutex.lock();
|
||||
_initialising = true;
|
||||
|
||||
//cout << "MackiePort::lock acquired" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::init lock acquired" << endl;
|
||||
#endif
|
||||
// emit pre-init signal
|
||||
init_event();
|
||||
|
||||
@@ -263,13 +237,19 @@ void MackiePort::init()
|
||||
|
||||
void MackiePort::finalise_init( bool yn )
|
||||
{
|
||||
//cout << "MackiePort::finalise_init" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::finalise_init" << endl;
|
||||
#endif
|
||||
bool emulation_ok = false;
|
||||
|
||||
// probing doesn't work very well, so just use a config variable
|
||||
// to set the emulation mode
|
||||
// TODO This might have to be specified on a per-port basis
|
||||
// in the config file
|
||||
// if an mcu and a bcf are needed to work as one surface
|
||||
if ( _emulation == none )
|
||||
{
|
||||
// TODO same as code in mackie_control_protocol.cc
|
||||
if ( ARDOUR::Config->get_mackie_emulation() == "bcf" )
|
||||
{
|
||||
_emulation = bcf2000;
|
||||
@@ -296,11 +276,39 @@ void MackiePort::finalise_init( bool yn )
|
||||
active_event();
|
||||
|
||||
// start handling messages from controls
|
||||
_any = port().input()->any.connect( ( mem_fun (*this, &MackiePort::handle_midi_any) ) );
|
||||
connect_any();
|
||||
}
|
||||
_initialising = false;
|
||||
init_cond.signal();
|
||||
init_mutex.unlock();
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::finalise_init lock released" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MackiePort::connect_any()
|
||||
{
|
||||
/*
|
||||
Doesn't work because there isn't an == operator for slots
|
||||
MIDI::Signal::slot_list_type slots = port().input()->any.slots();
|
||||
|
||||
if ( find( slots.begin(), slots.end(), mem_fun( *this, &MackiePort::handle_midi_any ) ) == slots.end() )
|
||||
*/
|
||||
// TODO but this will break if midi tracing is turned on
|
||||
if ( port().input()->any.empty() )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
cout << "connect input parser " << port().input() << " to handle_midi_any" << endl;
|
||||
#endif
|
||||
_any = port().input()->any.connect( mem_fun( *this, &MackiePort::handle_midi_any ) );
|
||||
#ifdef DEBUG
|
||||
cout << "input parser any connections: " << port().input()->any.size() << endl;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "MackiePort::connect_any already connected" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool MackiePort::wait_for_init()
|
||||
@@ -308,18 +316,26 @@ bool MackiePort::wait_for_init()
|
||||
Glib::Mutex::Lock lock( init_mutex );
|
||||
while ( _initialising )
|
||||
{
|
||||
//cout << "MackiePort::wait_for_active waiting" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::wait_for_active waiting" << endl;
|
||||
#endif
|
||||
init_cond.wait( init_mutex );
|
||||
//cout << "MackiePort::wait_for_active released" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::wait_for_active released" << endl;
|
||||
#endif
|
||||
}
|
||||
//cout << "MackiePort::wait_for_active returning" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "MackiePort::wait_for_active returning" << endl;
|
||||
#endif
|
||||
return SurfacePort::active();
|
||||
}
|
||||
|
||||
void MackiePort::handle_midi_sysex (MIDI::Parser & parser, MIDI::byte * raw_bytes, size_t count )
|
||||
{
|
||||
MidiByteArray bytes( count, raw_bytes );
|
||||
//cout << "handle_midi_sysex: " << bytes << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "handle_midi_sysex: " << bytes << endl;
|
||||
#endif
|
||||
switch( bytes[5] )
|
||||
{
|
||||
case 0x01:
|
||||
@@ -342,16 +358,99 @@ void MackiePort::handle_midi_sysex (MIDI::Parser & parser, MIDI::byte * raw_byte
|
||||
}
|
||||
}
|
||||
|
||||
Control & MackiePort::lookup_control( MIDI::byte * bytes, size_t count )
|
||||
{
|
||||
// Don't instantiate a MidiByteArray here unless it's needed for exceptions.
|
||||
// Reason being that this method is called for every single incoming
|
||||
// midi event, and it needs to be as efficient as possible.
|
||||
Control * control = 0;
|
||||
MIDI::byte midi_type = bytes[0] & 0xf0; //0b11110000
|
||||
switch( midi_type )
|
||||
{
|
||||
// fader
|
||||
case MackieMidiBuilder::midi_fader_id:
|
||||
{
|
||||
int midi_id = bytes[0] & 0x0f;
|
||||
control = _mcp.surface().faders[midi_id];
|
||||
if ( control == 0 )
|
||||
{
|
||||
MidiByteArray mba( count, bytes );
|
||||
ostringstream os;
|
||||
os << "control for fader" << bytes << " id " << midi_id << " is null";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// button
|
||||
case MackieMidiBuilder::midi_button_id:
|
||||
control = _mcp.surface().buttons[bytes[1]];
|
||||
if ( control == 0 )
|
||||
{
|
||||
MidiByteArray mba( count, bytes );
|
||||
ostringstream os;
|
||||
os << "control for button " << bytes << " is null";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
break;
|
||||
|
||||
// pot (jog wheel, external control)
|
||||
case MackieMidiBuilder::midi_pot_id:
|
||||
control = _mcp.surface().pots[bytes[1]];
|
||||
if ( control == 0 )
|
||||
{
|
||||
MidiByteArray mba( count, bytes );
|
||||
ostringstream os;
|
||||
os << "control for rotary " << mba << " is null";
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
MidiByteArray mba( count, bytes );
|
||||
ostringstream os;
|
||||
os << "Cannot find control for " << bytes;
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
return *control;
|
||||
}
|
||||
|
||||
bool MackiePort::handle_control_timeout_event ( Control * control )
|
||||
{
|
||||
// empty control_state
|
||||
ControlState control_state;
|
||||
control->in_use( false );
|
||||
control_event( *this, *control, control_state );
|
||||
|
||||
// only call this method once from the timer
|
||||
return false;
|
||||
}
|
||||
|
||||
// converts midi messages into control_event signals
|
||||
// it might be worth combining this with lookup_control
|
||||
// because they have similar logic flows.
|
||||
void MackiePort::handle_midi_any (MIDI::Parser & parser, MIDI::byte * raw_bytes, size_t count )
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MidiByteArray bytes( count, raw_bytes );
|
||||
cout << "MackiePort::handle_midi_any " << bytes << endl;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
// ignore sysex messages
|
||||
if ( bytes[0] == MIDI::sysex ) return;
|
||||
if ( raw_bytes[0] == MIDI::sysex ) return;
|
||||
|
||||
Control & control = lookup_control( bytes );
|
||||
// sanity checking
|
||||
if ( count != 3 )
|
||||
{
|
||||
ostringstream os;
|
||||
MidiByteArray mba( count, raw_bytes );
|
||||
os << "MackiePort::handle_midi_any needs 3 bytes, but received " << mba;
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
|
||||
Control & control = lookup_control( raw_bytes, count );
|
||||
control.in_use( true );
|
||||
|
||||
// This handles incoming bytes. Outgoing bytes
|
||||
// are sent by the signal handlers.
|
||||
@@ -359,41 +458,74 @@ void MackiePort::handle_midi_any (MIDI::Parser & parser, MIDI::byte * raw_bytes,
|
||||
{
|
||||
// fader
|
||||
case Control::type_fader:
|
||||
{
|
||||
// for a BCF2000, max is 7f for high-order byte and 0x70 for low-order byte
|
||||
// According to the Logic docs, these should both be 0x7f.
|
||||
// Although it does mention something about only the top-order
|
||||
// 10 bits out of 14 being used
|
||||
int midi_pos = ( bytes[2] << 7 ) + bytes[1];
|
||||
control_event( *this, control, float(midi_pos) / float(0x3fff) );
|
||||
}
|
||||
break;
|
||||
{
|
||||
// only the top-order 10 bits out of 14 are used
|
||||
int midi_pos = ( ( raw_bytes[2] << 7 ) + raw_bytes[1] ) >> 4;
|
||||
|
||||
// in_use is set by the MackieControlProtocol::handle_strip_button
|
||||
|
||||
// relies on implicit ControlState constructor
|
||||
control_event( *this, control, float(midi_pos) / float(0x3ff) );
|
||||
}
|
||||
break;
|
||||
|
||||
// button
|
||||
case Control::type_button:
|
||||
control_event( *this, control, bytes[2] == 0x7f ? press : release );
|
||||
{
|
||||
ControlState control_state( raw_bytes[2] == 0x7f ? press : release );
|
||||
control.in_use( control_state.button_state == press );
|
||||
control_event( *this, control, control_state );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// pot (jog wheel, external control)
|
||||
case Control::type_pot:
|
||||
{
|
||||
ControlState state;
|
||||
|
||||
// bytes[2] & 0b01000000 (0x40) give sign
|
||||
int sign = ( bytes[2] & 0x40 ) == 0 ? 1 : -1;
|
||||
// bytes[2] & 0b00111111 (0x3f) gives delta
|
||||
state.ticks = ( bytes[2] & 0x3f) * sign;
|
||||
state.delta = float( state.ticks ) / float( 0x3f );
|
||||
|
||||
control_event( *this, control, state );
|
||||
}
|
||||
{
|
||||
ControlState state;
|
||||
|
||||
// bytes[2] & 0b01000000 (0x40) give sign
|
||||
state.sign = ( raw_bytes[2] & 0x40 ) == 0 ? 1 : -1;
|
||||
// bytes[2] & 0b00111111 (0x3f) gives delta
|
||||
state.ticks = ( raw_bytes[2] & 0x3f);
|
||||
state.delta = float( state.ticks ) / float( 0x3f );
|
||||
|
||||
/*
|
||||
Pots only emit events when they move, not when they
|
||||
stop moving. So to get a stop event, we need to use a timeout.
|
||||
*/
|
||||
// this is set to false ...
|
||||
control.in_use( true );
|
||||
|
||||
// ... by this timeout
|
||||
|
||||
// first disconnect any previous timeouts
|
||||
control.in_use_connection.disconnect();
|
||||
|
||||
// now connect a new timeout to call handle_control_timeout_event
|
||||
sigc::slot<bool> timeout_slot = sigc::bind(
|
||||
mem_fun( *this, &MackiePort::handle_control_timeout_event )
|
||||
, &control
|
||||
);
|
||||
control.in_use_connection = Glib::signal_timeout().connect(
|
||||
timeout_slot
|
||||
, control.in_use_timeout()
|
||||
);
|
||||
|
||||
// emit the control event
|
||||
control_event( *this, control, state );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
cerr << "Do not understand control type " << control;
|
||||
}
|
||||
}
|
||||
catch( MackieControlException & e )
|
||||
{
|
||||
//cout << bytes << ' ' << e.what() << endl;
|
||||
MidiByteArray bytes( count, raw_bytes );
|
||||
cout << bytes << ' ' << e.what() << endl;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
cout << "finished MackiePort::handle_midi_any " << bytes << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -55,12 +55,12 @@ public:
|
||||
virtual const MidiByteArray & sysex_hdr() const;
|
||||
|
||||
/// Handle device initialisation
|
||||
void handle_midi_sysex( MIDI::Parser &, MIDI::byte *, size_t );
|
||||
void handle_midi_sysex( MIDI::Parser &, MIDI::byte *, size_t count );
|
||||
|
||||
/// Handle all control messags
|
||||
void handle_midi_any( MIDI::Parser &, MIDI::byte *, size_t );
|
||||
void handle_midi_any( MIDI::Parser &, MIDI::byte *, size_t count );
|
||||
|
||||
Control & lookup_control( const MidiByteArray & bytes );
|
||||
Control & lookup_control( MIDI::byte *, size_t count );
|
||||
|
||||
/// return the number of strips associated with this port
|
||||
virtual int strips() const;
|
||||
@@ -71,6 +71,10 @@ public:
|
||||
|
||||
emulation_t emulation() const { return _emulation; }
|
||||
|
||||
/// Connect the any signal from the parser to handle_midi_any
|
||||
/// unless it's already connected
|
||||
void connect_any();
|
||||
|
||||
protected:
|
||||
/**
|
||||
The initialisation sequence is fairly complex. First a lock is acquired
|
||||
@@ -105,6 +109,10 @@ protected:
|
||||
*/
|
||||
void probe_emulation( const MidiByteArray & bytes );
|
||||
|
||||
/// Handle timeout events set for controls that don't emit
|
||||
/// an off event
|
||||
bool handle_control_timeout_event ( Control * );
|
||||
|
||||
private:
|
||||
MackieControlProtocol & _mcp;
|
||||
port_type_t _port_type;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,6 @@ namespace Mackie
|
||||
{
|
||||
|
||||
class MackieButtonHandler;
|
||||
|
||||
class MackieSurface : public Surface
|
||||
{
|
||||
public:
|
||||
@@ -20,6 +19,12 @@ public:
|
||||
|
||||
virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button );
|
||||
virtual void init_controls();
|
||||
|
||||
virtual bool has_timecode_display() const { return true; }
|
||||
virtual void display_timecode( SurfacePort &, MackieMidiBuilder &, const std::string & timecode, const std::string & timecode_last );
|
||||
|
||||
virtual float scrub_scaling_factor() { return 100.0; }
|
||||
virtual float scaled_delta( const ControlState & state, float current_speed );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
1507
libs/surfaces/mackie/mackie_surface_generated.cc
Normal file
1507
libs/surfaces/mackie/mackie_surface_generated.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -24,6 +24,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
#include <iomanip>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -96,3 +97,12 @@ ostream & operator << ( ostream & os, const MidiByteArray & mba )
|
||||
os << "]";
|
||||
return os;
|
||||
}
|
||||
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const std::string & st )
|
||||
{
|
||||
for ( string::const_iterator it = st.begin(); it != st.end(); ++it )
|
||||
{
|
||||
mba << *it;
|
||||
}
|
||||
return mba;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,9 @@ public:
|
||||
/// append the given byte to the end of the array
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const MIDI::byte & b );
|
||||
|
||||
/// append the given string to the end of the array
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const std::string & );
|
||||
|
||||
/// append the given array to the end of this array
|
||||
MidiByteArray & operator << ( MidiByteArray & mba, const MidiByteArray & barr );
|
||||
|
||||
|
||||
@@ -20,37 +20,41 @@
|
||||
#include <ardour/route.h>
|
||||
#include <ardour/track.h>
|
||||
#include <ardour/panner.h>
|
||||
#include <ardour/types.h>
|
||||
|
||||
#include "mackie_control_protocol.h"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace Mackie;
|
||||
using namespace std;
|
||||
|
||||
void RouteSignal::connect()
|
||||
{
|
||||
back_insert_iterator<Connections> cins = back_inserter( _connections );
|
||||
|
||||
if ( _strip.has_solo() )
|
||||
_solo_changed_connection = _route.solo_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_solo_changed ), this ) );
|
||||
cins = _route.solo_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_solo_changed ), this ) );
|
||||
|
||||
if ( _strip.has_mute() )
|
||||
_mute_changed_connection = _route.mute_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_mute_changed ), this ) );
|
||||
cins = _route.mute_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_mute_changed ), this ) );
|
||||
|
||||
if ( _strip.has_gain() )
|
||||
_gain_changed_connection = _route.gain_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_gain_changed ), this ) );
|
||||
cins = _route.gain_control()->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_gain_changed ), this, true ) );
|
||||
|
||||
_name_changed_connection = _route.NameChanged.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_name_changed ), this ) );
|
||||
cins = _route.NameChanged.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_name_changed ), this ) );
|
||||
|
||||
if ( _route.panner().npanners() == 1 )
|
||||
cins = _route.panner().Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), this, true ) );
|
||||
for ( unsigned int i = 0; i < _route.panner().npanners(); ++i )
|
||||
{
|
||||
_panner_changed_connection = _route.panner().pan_control(0)->Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), this ) );
|
||||
cins = _route.panner().streampanner (i).Changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_panner_changed ), this, true ) );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_record_enable_changed_connection =
|
||||
dynamic_cast<ARDOUR::Track&>( _route ).rec_enable_control()->Changed
|
||||
.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_record_enable_changed ), this ) )
|
||||
cins = dynamic_cast<ARDOUR::Track&>( _route )
|
||||
.rec_enable_control()
|
||||
->Changed
|
||||
.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_record_enable_changed ), this ) )
|
||||
;
|
||||
}
|
||||
catch ( std::bad_cast & )
|
||||
@@ -59,24 +63,28 @@ void RouteSignal::connect()
|
||||
// with can't be record-enabled
|
||||
}
|
||||
|
||||
// TODO this works when a currently-banked route is made inactive, but not
|
||||
// when a route is activated which should be currently banked.
|
||||
cins = _route.active_changed.connect( sigc::bind ( mem_fun ( _mcp, &MackieControlProtocol::notify_active_changed ), this ) );
|
||||
|
||||
// TODO
|
||||
// active_changed
|
||||
// SelectedChanged
|
||||
// RemoteControlIDChanged. Better handled at Session level.
|
||||
}
|
||||
|
||||
void RouteSignal::disconnect()
|
||||
{
|
||||
_solo_changed_connection.disconnect();
|
||||
_mute_changed_connection.disconnect();
|
||||
_gain_changed_connection.disconnect();
|
||||
_name_changed_connection.disconnect();
|
||||
_panner_changed_connection.disconnect();
|
||||
_record_enable_changed_connection.disconnect();
|
||||
for ( Connections::iterator it = _connections.begin(); it != _connections.end(); ++it )
|
||||
{
|
||||
it->disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
void RouteSignal::notify_all()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
cout << "RouteSignal::notify_all for " << _strip << endl;
|
||||
#endif
|
||||
if ( _strip.has_solo() )
|
||||
_mcp.notify_solo_changed( this );
|
||||
|
||||
@@ -93,4 +101,7 @@ void RouteSignal::notify_all()
|
||||
|
||||
if ( _strip.has_recenable() )
|
||||
_mcp.notify_record_enable_changed( this );
|
||||
#ifdef DEBUG
|
||||
cout << "RouteSignal::notify_all finish" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
|
||||
#include <sigc++/sigc++.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "midi_byte_array.h"
|
||||
|
||||
class MackieControlProtocol;
|
||||
|
||||
namespace ARDOUR {
|
||||
@@ -30,7 +34,7 @@ namespace Mackie
|
||||
{
|
||||
|
||||
class Strip;
|
||||
class MackiePort;
|
||||
class SurfacePort;
|
||||
|
||||
/**
|
||||
This class is intended to easily create and destroy the set of
|
||||
@@ -41,8 +45,8 @@ class MackiePort;
|
||||
class RouteSignal
|
||||
{
|
||||
public:
|
||||
RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip, MackiePort & port )
|
||||
: _route( route ), _mcp( mcp ), _strip( strip ), _port( port )
|
||||
RouteSignal( ARDOUR::Route & route, MackieControlProtocol & mcp, Strip & strip, SurfacePort & port )
|
||||
: _route( route ), _mcp( mcp ), _strip( strip ), _port( port ), _last_gain_written(0.0)
|
||||
{
|
||||
connect();
|
||||
}
|
||||
@@ -60,20 +64,27 @@ public:
|
||||
|
||||
const ARDOUR::Route & route() const { return _route; }
|
||||
Strip & strip() { return _strip; }
|
||||
MackiePort & port() { return _port; }
|
||||
SurfacePort & port() { return _port; }
|
||||
|
||||
float last_gain_written() const { return _last_gain_written; }
|
||||
void last_gain_written( float other ) { _last_gain_written = other; }
|
||||
|
||||
const MidiByteArray & last_pan_written() const { return _last_pan_written; }
|
||||
void last_pan_written( const MidiByteArray & other ) { _last_pan_written = other; }
|
||||
|
||||
private:
|
||||
ARDOUR::Route & _route;
|
||||
MackieControlProtocol & _mcp;
|
||||
Strip & _strip;
|
||||
MackiePort & _port;
|
||||
SurfacePort & _port;
|
||||
|
||||
sigc::connection _solo_changed_connection;
|
||||
sigc::connection _mute_changed_connection;
|
||||
sigc::connection _record_enable_changed_connection;
|
||||
sigc::connection _gain_changed_connection;
|
||||
sigc::connection _name_changed_connection;
|
||||
sigc::connection _panner_changed_connection;
|
||||
typedef std::vector<sigc::connection> Connections;
|
||||
Connections _connections;
|
||||
|
||||
// Last written values for the gain and pan, to avoid overloading
|
||||
// the midi connection to the surface
|
||||
float _last_gain_written;
|
||||
MidiByteArray _last_pan_written;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -20,13 +20,13 @@ button,1,assignment,io,1,1,0x28
|
||||
button,1,assignment,sends,1,1,0x5a
|
||||
button,1,assignment,pan,1,1,0x59
|
||||
button,1,assignment,plugin,1,1,0x57
|
||||
button,1,assignment,eq,1,1,0x58
|
||||
button,1,assignment,dyn,1,1,0x2d
|
||||
button,1,functions,drop,1,1,0x58 # was eq
|
||||
button,1,assignment,zoom,1,1,0x2d
|
||||
button,1,bank,left,1,0,0x2e
|
||||
button,1,bank,right,1,0,0x2f
|
||||
button,1,bank,channel_left,1,0,0x30
|
||||
button,1,bank,channel_right,1,0,0x31
|
||||
button,1,,flip,1,1,0x32
|
||||
button,1,,scrub,1,1,0x32
|
||||
button,1,,edit,1,1,0x56
|
||||
|
||||
button,1,display,name_value,1,0,0x34
|
||||
@@ -54,12 +54,13 @@ button,1,modifiers,cmd_alt,1,0,0x49
|
||||
button,1,automation,on,1,1,0x4a
|
||||
button,1,automation,rec_ready,1,1,0x4b
|
||||
button,1,functions,undo,1,1,0x4c
|
||||
button,1,automation,snapshot,1,1,0x4d
|
||||
button,1,automation,snapshot,1,1,0x5f
|
||||
button,1,functions,redo,1,1,0x4f
|
||||
button,1,functions,marker,1,1,0x47
|
||||
button,1,functions,enter,1,1,0x51
|
||||
button,1,functions,cancel,1,0,0x52
|
||||
button,1,functions,mixer,1,0,0x53
|
||||
button,1,functions,save,1,0,0x4d
|
||||
|
||||
# transport buttons
|
||||
button,1,transport,frm_left,1,1,0x5b
|
||||
@@ -78,8 +79,8 @@ button,1,cursor,"cursor_up",1,0,0x60
|
||||
button,1,cursor,"cursor_down",1,0,0x61
|
||||
button,1,cursor,"cursor_left",1,0,0x62
|
||||
button,1,cursor,"cursor_right",1,0,0x63
|
||||
button,1,,"zoom",1,1,0x64
|
||||
button,1,,"scrub",1,1,0x65
|
||||
button,1,,"dyn",1,1,0x64
|
||||
button,1,,"flip",1,1,0x65
|
||||
button,1,user,"user_a",1,0,0x66
|
||||
button,1,user,"user_b",1,0,0x67
|
||||
|
||||
|
||||
|
@@ -15,8 +15,10 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
this_dir = File.dirname(__FILE__)
|
||||
|
||||
require 'faster_csv'
|
||||
require 'mackie.rb'
|
||||
require "#{this_dir}/mackie.rb"
|
||||
|
||||
class Control
|
||||
attr_accessor :id, :led, :group, :name, :ordinal, :switch
|
||||
@@ -191,6 +193,12 @@ class Surface
|
||||
end
|
||||
|
||||
# add the new control to the various lookups
|
||||
# but first print a warning if the id is duplicated
|
||||
if @controls_by_id.has_key?( row.id ) && control.group.class != Strip
|
||||
duplicated = @controls_by_id[row.id]
|
||||
puts "duplicate id #{control.id}:#{control.name} of #{duplicated.id}:#{duplicated.name}"
|
||||
end
|
||||
|
||||
@controls_by_id[row.id] = control
|
||||
@controls << control
|
||||
group << control
|
||||
|
||||
@@ -18,6 +18,11 @@
|
||||
require 'controls.rb'
|
||||
require 'mackie.rb'
|
||||
|
||||
if ARGV.size != 2
|
||||
puts "#$0 /dev/snd/midiXXXX control-file.csv"
|
||||
exit 1
|
||||
end
|
||||
|
||||
while !File.exist? ARGV[0]
|
||||
sleep 0.010
|
||||
end
|
||||
@@ -30,46 +35,14 @@ puts ""
|
||||
file = File.open ARGV[0], 'r+'
|
||||
mck = Mackie.new( file )
|
||||
|
||||
# send device query
|
||||
response = mck.sysex( "\x00" )
|
||||
puts "response: #{response.to_hex}"
|
||||
|
||||
# decode host connection query
|
||||
status = response[0]
|
||||
if status != 1
|
||||
puts "expected 01, got " + response.to_hex.inspect
|
||||
exit(1)
|
||||
end
|
||||
serial = response[1..7]
|
||||
challenge = response[8..11]
|
||||
puts <<EOF
|
||||
serial: #{serial.to_hex.inspect}
|
||||
challenge: #{challenge.to_hex.inspect}
|
||||
EOF
|
||||
|
||||
# send host connection reply
|
||||
response = mck.sysex( "\x02" + serial.pack('C*') + challenge.pack('C*') )
|
||||
|
||||
# decode host connection confirmation
|
||||
status = response[0]
|
||||
if status != 3
|
||||
puts "expected 03, got " + response.to_hex.inspect
|
||||
exit(1)
|
||||
end
|
||||
|
||||
serial = response[1..7]
|
||||
puts <<EOF
|
||||
serial: #{serial.to_hex.inspect}
|
||||
EOF
|
||||
|
||||
# faders to minimum. bcf2000 doesn't respond
|
||||
#file.write( hdr + "\x61\xf7" )
|
||||
mck.write_sysex "\x61"
|
||||
|
||||
# all leds off. bcf2000 doesn't respond
|
||||
#file.write( hdr + "\x62\xf7" )
|
||||
mck.write_sysex "\x62"
|
||||
|
||||
# get version. comes back as ASCII bytes
|
||||
version = mck.sysex( "\x13\x00" )
|
||||
version = mck.sysex "\x13\x00"
|
||||
puts "version: #{version.map{|x| x.chr}}"
|
||||
|
||||
# write a welcome message. bcf2000 responds with exact
|
||||
@@ -126,7 +99,7 @@ while bytes = mck.file.read( 3 )
|
||||
control = sf.midis[midi_type][control_id]
|
||||
|
||||
print " Control Type: %-7s, " % sf.types[midi_type]
|
||||
print "id: %4i" % control_id
|
||||
print "id: %4x" % control_id
|
||||
print ", control: %15s" % ( control ? control.name : "nil control" )
|
||||
print ", %15s" % ( control ? control.group.name : "nil group" )
|
||||
print "\n"
|
||||
|
||||
BIN
libs/surfaces/mackie/scripts/mackie-dump.midi
Normal file
BIN
libs/surfaces/mackie/scripts/mackie-dump.midi
Normal file
Binary file not shown.
@@ -25,17 +25,8 @@ while !File.exist? ARGV[0]
|
||||
end
|
||||
|
||||
file = File.open( ARGV[0], 'r+' )
|
||||
mck = Mackie.new( file )
|
||||
|
||||
# faders to minimum. bcf2000 doesn't respond
|
||||
mck.write_sysex "\x61"
|
||||
|
||||
# all leds off. bcf2000 doesn't respond
|
||||
mck.write_sysex "\x62"
|
||||
|
||||
# get version. comes back as ASCII bytes
|
||||
version = mck.sysex "\x13\x00"
|
||||
puts "version: #{version.map{|x| x.chr}}"
|
||||
#mck = Mackie.new( file )
|
||||
device = false
|
||||
|
||||
# respond to control movements
|
||||
while bytes = file.read( 3 )
|
||||
@@ -121,7 +112,7 @@ while bytes = file.read( 3 )
|
||||
end
|
||||
|
||||
# output bytes
|
||||
if output
|
||||
if device && output
|
||||
#sleep 0.1
|
||||
puts "sending: %02.x %02.x %02.x" % [ output[0], output[1], output[2] ]
|
||||
begin
|
||||
|
||||
@@ -52,17 +52,29 @@ void Mackie::<%= sf.name %>Surface::init_controls()
|
||||
% end
|
||||
|
||||
// initialise controls
|
||||
Control * control = 0;
|
||||
Fader * fader = 0;
|
||||
Pot * pot = 0;
|
||||
Button * button = 0;
|
||||
Led * led = 0;
|
||||
|
||||
% sf.controls.each do |control|
|
||||
<%-
|
||||
variable_name = control.class.name.downcase
|
||||
class_name =
|
||||
if control.name == 'jog'
|
||||
'Jog'
|
||||
else
|
||||
control.class.name
|
||||
end
|
||||
-%>
|
||||
group = groups["<%=control.group.name%>"];
|
||||
control = new <%= control.class.name %> ( <%= control.id %>, <%= control.ordinal %>, "<%=control.name%>", *group );
|
||||
<%=control.class.name.downcase%>s[0x<%=control.id.to_hex %>] = control;
|
||||
controls.push_back( control );
|
||||
<%= variable_name %> = new <%= class_name %> ( <%= control.id %>, <%= control.ordinal %>, "<%=control.name%>", *group );
|
||||
<%= variable_name %>s[0x<%=control.id.to_hex %>] = <%= variable_name %>;
|
||||
controls.push_back( <%= variable_name %> );
|
||||
<%- if control.group.class != Strip -%>
|
||||
controls_by_name["<%= control.name %>"] = control;
|
||||
controls_by_name["<%= control.name %>"] = <%= variable_name %>;
|
||||
<%- end -%>
|
||||
group->add( *control );
|
||||
group->add( *<%= variable_name %> );
|
||||
|
||||
% end
|
||||
}
|
||||
@@ -82,7 +94,7 @@ void Mackie::<%= sf.name %>Surface::handle_button( MackieButtonHandler & mbh, Bu
|
||||
buttons = sf.controls.find_all{|x| x.class == Button && x.group.class != Strip}
|
||||
buttons.each do |button|
|
||||
%>
|
||||
case 0x<%= button.id.to_hex %>: // <%= button.name %>
|
||||
case 0x<%= ( button.class.midi_zero_byte << 8 | button.id ).to_hex %>: // <%= button.name %>
|
||||
switch ( bs ) {
|
||||
case press: ls = mbh.<%= button.name %>_press( button ); break;
|
||||
case release: ls = mbh.<%= button.name %>_release( button ); break;
|
||||
|
||||
@@ -4,6 +4,6 @@ require 'controls.rb'
|
||||
require 'pp'
|
||||
|
||||
sf = Surface.new
|
||||
sf.parse
|
||||
sf.parse( ARGV[0] )
|
||||
sf.types.each{|k,v| puts "%02.x #{v}" % k}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <typeinfo>
|
||||
|
||||
using namespace std;
|
||||
using namespace Mackie;
|
||||
@@ -15,8 +14,14 @@ Surface::Surface( uint32_t max_strips, uint32_t unit_strips )
|
||||
|
||||
void Surface::init()
|
||||
{
|
||||
#ifdef DEBUG
|
||||
cout << "Surface::init" << endl;
|
||||
#endif
|
||||
init_controls();
|
||||
init_strips( _max_strips, _unit_strips );
|
||||
#ifdef DEBUG
|
||||
cout << "Surface::init finish" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
Surface::~Surface()
|
||||
@@ -52,6 +57,8 @@ void Surface::init_strips( uint32_t max_strips, uint32_t unit_strips )
|
||||
// shallow copy existing strip
|
||||
// which works because the controls
|
||||
// have the same ids across units
|
||||
// TODO this needs to be a deep copy because
|
||||
// controls hold state now - in_use
|
||||
Strip * strip = new Strip( *strips[i % unit_strips] );
|
||||
|
||||
// update the relevant values
|
||||
@@ -64,80 +71,3 @@ void Surface::init_strips( uint32_t max_strips, uint32_t unit_strips )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ostream & Mackie::operator << ( ostream & os, const Mackie::Control & control )
|
||||
{
|
||||
os << typeid( control ).name();
|
||||
os << " { ";
|
||||
os << "name: " << control.name();
|
||||
os << ", ";
|
||||
os << "id: " << "0x" << setw(2) << setfill('0') << hex << control.id() << setfill(' ');
|
||||
os << ", ";
|
||||
os << "ordinal: " << dec << control.ordinal();
|
||||
os << ", ";
|
||||
os << "group: " << control.group().name();
|
||||
os << " }";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
TODO could optimise this to use enum, but it's only
|
||||
called during the protocol class instantiation.
|
||||
|
||||
generated using
|
||||
|
||||
irb -r controls.rb
|
||||
sf=Surface.new
|
||||
sf.parse
|
||||
controls = sf.groups.find{|x| x[0] =~ /strip/}.each{|x| puts x[1]}
|
||||
controls[1].each {|x| puts "\telse if ( control.name() == \"#{x.name}\" )\n\t{\n\t\t_#{x.name} = reinterpret_cast<#{x.class.name}*>(&control);\n\t}\n"}
|
||||
*/
|
||||
void Strip::add( Control & control )
|
||||
{
|
||||
Group::add( control );
|
||||
if ( control.name() == "gain" )
|
||||
{
|
||||
_gain = reinterpret_cast<Fader*>(&control);
|
||||
}
|
||||
else if ( control.name() == "vpot" )
|
||||
{
|
||||
_vpot = reinterpret_cast<Pot*>(&control);
|
||||
}
|
||||
else if ( control.name() == "recenable" )
|
||||
{
|
||||
_recenable = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "solo" )
|
||||
{
|
||||
_solo = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "mute" )
|
||||
{
|
||||
_mute = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "select" )
|
||||
{
|
||||
_select = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "vselect" )
|
||||
{
|
||||
_vselect = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.name() == "fader_touch" )
|
||||
{
|
||||
_fader_touch = reinterpret_cast<Button*>(&control);
|
||||
}
|
||||
else if ( control.type() == Control::type_led || control.type() == Control::type_led_ring )
|
||||
{
|
||||
// do nothing
|
||||
cout << "Strip::add not adding " << control << endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
ostringstream os;
|
||||
os << "Strip::add: unknown control type " << control;
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Mackie
|
||||
{
|
||||
|
||||
class MackieButtonHandler;
|
||||
class SurfacePort;
|
||||
class MackieMidiBuilder;
|
||||
|
||||
/**
|
||||
This represents an entire control surface, made up of Groups,
|
||||
@@ -53,11 +55,13 @@ public:
|
||||
These are alternative addressing schemes
|
||||
They use maps because the indices aren't always
|
||||
0-based.
|
||||
|
||||
Indexed by raw_id not by id. @see Control for the distinction.
|
||||
*/
|
||||
std::map<int,Control*> faders;
|
||||
std::map<int,Control*> pots;
|
||||
std::map<int,Control*> buttons;
|
||||
std::map<int,Control*> leds;
|
||||
std::map<int,Fader*> faders;
|
||||
std::map<int,Pot*> pots;
|
||||
std::map<int,Button*> buttons;
|
||||
std::map<int,Led*> leds;
|
||||
|
||||
/// no strip controls in here because they usually
|
||||
/// have the same names.
|
||||
@@ -79,7 +83,38 @@ public:
|
||||
|
||||
/// map button ids to calls to press_ and release_ in mbh
|
||||
virtual void handle_button( MackieButtonHandler & mbh, ButtonState bs, Button & button ) = 0;
|
||||
|
||||
public:
|
||||
/// display an indicator of the first switched-in Route. Do nothing by default.
|
||||
virtual void display_bank_start( SurfacePort &, MackieMidiBuilder &, uint32_t current_bank ) {};
|
||||
|
||||
/// called from MackieControlPRotocol::zero_all to turn things off
|
||||
virtual void zero_all( SurfacePort &, MackieMidiBuilder & ) {};
|
||||
|
||||
/// turn off leds around the jog wheel. This is for surfaces that use a pot
|
||||
/// pretending to be a jog wheel.
|
||||
virtual void blank_jog_ring( SurfacePort &, MackieMidiBuilder & ) {};
|
||||
|
||||
virtual bool has_timecode_display() const = 0;
|
||||
virtual void display_timecode( SurfacePort &, MackieMidiBuilder &, const std::string & timecode, const std::string & timecode_last ) {};
|
||||
|
||||
public:
|
||||
/**
|
||||
This is used to calculate the clicks per second that define
|
||||
a transport speed of 1.0 for the jog wheel. 100.0 is 10 clicks
|
||||
per second, 50.5 is 5 clicks per second.
|
||||
*/
|
||||
virtual float scrub_scaling_factor() = 0;
|
||||
|
||||
/**
|
||||
The scaling factor function for speed increase and decrease. At
|
||||
low transport speeds this should return a small value, for high transport
|
||||
speeds, this should return an exponentially larger value. This provides
|
||||
high definition control at low speeds and quick speed changes to/from
|
||||
higher speeds.
|
||||
*/
|
||||
virtual float scaled_delta( const ControlState & state, float current_speed ) = 0;
|
||||
|
||||
protected:
|
||||
virtual void init_controls() = 0;
|
||||
virtual void init_strips( uint32_t max_strips, uint32_t unit_strips );
|
||||
|
||||
@@ -35,18 +35,27 @@
|
||||
using namespace std;
|
||||
using namespace Mackie;
|
||||
|
||||
SurfacePort::SurfacePort()
|
||||
: _port( 0 ), _number( 0 ), _active( false )
|
||||
{
|
||||
}
|
||||
|
||||
SurfacePort::SurfacePort( MIDI::Port & port, int number )
|
||||
: _port( port ), _number( number ), _active( false )
|
||||
: _port( &port ), _number( number ), _active( false )
|
||||
{
|
||||
}
|
||||
|
||||
SurfacePort::~SurfacePort()
|
||||
{
|
||||
//cout << "~SurfacePort::SurfacePort()" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "~SurfacePort::SurfacePort()" << endl;
|
||||
#endif
|
||||
// make sure another thread isn't reading or writing as we close the port
|
||||
Glib::RecMutex::Lock lock( _rwlock );
|
||||
_active = false;
|
||||
//cout << "~SurfacePort::SurfacePort() finished" << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "~SurfacePort::SurfacePort() finished" << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
// wrapper for one day when strerror_r is working properly
|
||||
@@ -67,24 +76,29 @@ MidiByteArray SurfacePort::read()
|
||||
if ( !active() ) return retval;
|
||||
|
||||
// return nothing read if the lock isn't acquired
|
||||
#if 0
|
||||
Glib::RecMutex::Lock lock( _rwlock, Glib::TRY_LOCK );
|
||||
|
||||
if ( !lock.locked() )
|
||||
{
|
||||
//cout << "SurfacePort::read not locked" << endl;
|
||||
cout << "SurfacePort::read not locked" << endl;
|
||||
return retval;
|
||||
}
|
||||
|
||||
// check active again - destructor sequence
|
||||
if ( !active() ) return retval;
|
||||
#endif
|
||||
|
||||
// read port and copy to return value
|
||||
int nread = port().read (buf, sizeof (buf));
|
||||
int nread = port().read( buf, sizeof (buf) );
|
||||
|
||||
if (nread >= 0) {
|
||||
retval.copy( nread, buf );
|
||||
if ((size_t) nread == sizeof (buf))
|
||||
{
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "SurfacePort::read recursive" << endl;
|
||||
#endif
|
||||
retval << read();
|
||||
}
|
||||
}
|
||||
@@ -101,13 +115,17 @@ MidiByteArray SurfacePort::read()
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
}
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "SurfacePort::read: " << retval << endl;
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
void SurfacePort::write( const MidiByteArray & mba )
|
||||
{
|
||||
//if ( mba[0] == 0xf0 ) cout << "SurfacePort::write: " << mba << endl;
|
||||
//cout << "SurfacePort::write: " << mba << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "SurfacePort::write: " << mba << endl;
|
||||
#endif
|
||||
|
||||
// check active before and after lock - to make sure
|
||||
// that the destructor doesn't destroy the mutex while
|
||||
@@ -116,21 +134,26 @@ void SurfacePort::write( const MidiByteArray & mba )
|
||||
Glib::RecMutex::Lock lock( _rwlock );
|
||||
if ( !active() ) return;
|
||||
|
||||
int count = port().write( mba.bytes().get(), mba.size(), 0 );
|
||||
int count = port().write( mba.bytes().get(), mba.size(), 0);
|
||||
if ( count != (int)mba.size() )
|
||||
{
|
||||
if ( errno != EAGAIN )
|
||||
if ( errno == 0 )
|
||||
{
|
||||
cout << "port overflow on " << port().name() << ". Did not write all of " << mba << endl;
|
||||
}
|
||||
else if ( errno != EAGAIN )
|
||||
{
|
||||
ostringstream os;
|
||||
os << "Surface: couldn't write to port " << port().name();
|
||||
os << ": " << errno << fetch_errmsg( errno );
|
||||
os << ", error: " << fetch_errmsg( errno ) << "(" << errno << ")";
|
||||
|
||||
cout << os.str();
|
||||
cout << os.str() << endl;
|
||||
inactive_event();
|
||||
throw MackieControlException( os.str() );
|
||||
}
|
||||
}
|
||||
//if ( mba[0] == 0xf0 ) cout << "SurfacePort::write " << count << endl;
|
||||
#ifdef PORT_DEBUG
|
||||
cout << "SurfacePort::wrote " << count << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void SurfacePort::write_sysex( const MidiByteArray & mba )
|
||||
@@ -147,22 +170,6 @@ void SurfacePort::write_sysex( MIDI::byte msg )
|
||||
write( buf );
|
||||
}
|
||||
|
||||
// This should be moved to midi++ at some point
|
||||
ostream & operator << ( ostream & os, const MIDI::Port & port )
|
||||
{
|
||||
os << "device: " << port.device();
|
||||
os << "; ";
|
||||
os << "name: " << port.name();
|
||||
os << "; ";
|
||||
os << "type: " << port.type();
|
||||
os << "; ";
|
||||
os << "mode: " << port.mode();
|
||||
os << "; ";
|
||||
os << "ok: " << port.ok();
|
||||
os << "; ";
|
||||
return os;
|
||||
}
|
||||
|
||||
ostream & Mackie::operator << ( ostream & os, const SurfacePort & port )
|
||||
{
|
||||
os << "{ ";
|
||||
|
||||
@@ -48,20 +48,20 @@ public:
|
||||
|
||||
/// read bytes from the port. They'll either end up in the
|
||||
/// parser, or if that's not active they'll be returned
|
||||
MidiByteArray read();
|
||||
virtual MidiByteArray read();
|
||||
|
||||
/// an easier way to output bytes via midi
|
||||
void write( const MidiByteArray & );
|
||||
virtual void write( const MidiByteArray & );
|
||||
|
||||
/// write a sysex message
|
||||
void write_sysex( const MidiByteArray & mba );
|
||||
void write_sysex( MIDI::byte msg );
|
||||
|
||||
// return the correct sysex header for this port
|
||||
/// return the correct sysex header for this port
|
||||
virtual const MidiByteArray & sysex_hdr() const = 0;
|
||||
|
||||
MIDI::Port & port() { return _port; }
|
||||
const MIDI::Port & port() const { return _port; }
|
||||
MIDI::Port & port() { return *_port; }
|
||||
const MIDI::Port & port() const { return *_port; }
|
||||
|
||||
// all control notofications are sent from here
|
||||
sigc::signal<void, SurfacePort &, Control &, const ControlState &> control_event;
|
||||
@@ -85,8 +85,12 @@ public:
|
||||
virtual bool active() const { return _active; }
|
||||
virtual void active( bool yn ) { _active = yn; }
|
||||
|
||||
protected:
|
||||
/// Only for use by DummyPort
|
||||
SurfacePort();
|
||||
|
||||
private:
|
||||
MIDI::Port & _port;
|
||||
MIDI::Port * _port;
|
||||
int _number;
|
||||
bool _active;
|
||||
|
||||
|
||||
136
libs/surfaces/mackie/timer.h
Normal file
136
libs/surfaces/mackie/timer.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright (C) 1998, 1999, 2000, 2007 John Anderson
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library 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 Library General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef timer_h
|
||||
#define timer_h
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "windows.h"
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
/**
|
||||
millisecond timer class.
|
||||
*/
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
|
||||
/**
|
||||
start the timer running if true, or just create the
|
||||
object if false.
|
||||
*/
|
||||
Timer( bool shouldStart = true )
|
||||
{
|
||||
if ( shouldStart )
|
||||
start();
|
||||
}
|
||||
|
||||
/**
|
||||
Start the timer running. Return the current timestamp, in milliseconds
|
||||
*/
|
||||
unsigned long start()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_start = (unsigned long)::GetTickCount();
|
||||
#else
|
||||
gettimeofday ( &_start, 0 );
|
||||
#endif
|
||||
running = true;
|
||||
#ifdef _WIN32
|
||||
return _start;
|
||||
#else
|
||||
return ( _start.tv_sec * 1000000 + _start.tv_usec ) / 1000;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
returns the number of milliseconds since start
|
||||
also stops the timer running
|
||||
*/
|
||||
unsigned long stop()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
_stop = (unsigned long)::GetTickCount();
|
||||
#else
|
||||
gettimeofday ( &_stop, 0 );
|
||||
#endif
|
||||
running = false;
|
||||
return elapsed();
|
||||
}
|
||||
|
||||
/**
|
||||
returns the number of milliseconds since start
|
||||
*/
|
||||
unsigned long elapsed() const
|
||||
{
|
||||
if ( running )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD current = ::GetTickCount();
|
||||
return current - _start;
|
||||
#else
|
||||
struct timeval current;
|
||||
gettimeofday ( ¤t, 0 );
|
||||
return (
|
||||
( current.tv_sec * 1000000 + current.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
|
||||
) / 1000
|
||||
;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return _stop - _start;
|
||||
#else
|
||||
return (
|
||||
( _stop.tv_sec * 1000000 + _stop.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
|
||||
) / 1000
|
||||
;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Call stop and then start. Return the value from stop.
|
||||
*/
|
||||
unsigned long restart()
|
||||
{
|
||||
unsigned long retval = stop();
|
||||
start();
|
||||
return retval;
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
unsigned long _start;
|
||||
unsigned long _stop;
|
||||
#else
|
||||
struct timeval _start;
|
||||
struct timeval _stop;
|
||||
#endif
|
||||
bool running;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -2,8 +2,28 @@
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
LedState on( LedState::on );
|
||||
LedState off( LedState::off );
|
||||
LedState flashing( LedState::flashing );
|
||||
LedState none( LedState::none );
|
||||
LedState on( LedState::on );
|
||||
LedState off( LedState::off );
|
||||
LedState flashing( LedState::flashing );
|
||||
LedState none( LedState::none );
|
||||
|
||||
std::ostream & operator << ( std::ostream & os, const ControlState & cs )
|
||||
{
|
||||
os << "ControlState { ";
|
||||
os << "pos: " << cs.pos;
|
||||
os << ", ";
|
||||
os << "sign: " << cs.sign;
|
||||
os << ", ";
|
||||
os << "delta: " << cs.delta;
|
||||
os << ", ";
|
||||
os << "ticks: " << cs.ticks;
|
||||
os << ", ";
|
||||
os << "led_state: " << cs.led_state.state();
|
||||
os << ", ";
|
||||
os << "button_state: " << cs.button_state;
|
||||
os << " }";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#ifndef mackie_types_h
|
||||
#define mackie_types_h
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace Mackie
|
||||
{
|
||||
|
||||
@@ -62,23 +64,34 @@ enum ButtonState { neither = -1, release = 0, press = 1 };
|
||||
*/
|
||||
struct ControlState
|
||||
{
|
||||
ControlState(): pos(0.0), delta(0.0), button_state(neither) {}
|
||||
ControlState(): pos(0.0), sign(0), delta(0.0), ticks(0), led_state(off), button_state(neither) {}
|
||||
|
||||
ControlState( LedState ls ): pos(0.0), delta(0.0), led_state(ls), button_state(neither) {}
|
||||
|
||||
// Note that this sets both pos and delta to the flt value
|
||||
ControlState( LedState ls, float flt ): pos(flt), delta(flt), ticks(0), led_state(ls), button_state(neither) {}
|
||||
ControlState( float flt ): pos(flt), delta(flt), ticks(0), led_state(none), button_state(neither) {}
|
||||
ControlState( float flt, int tcks ): pos(flt), delta(flt), ticks(tcks), led_state(none), button_state(neither) {}
|
||||
ControlState( float flt, unsigned int tcks ): pos(flt), delta(flt), ticks(tcks), led_state(none), button_state(neither) {}
|
||||
ControlState( ButtonState bs ): pos(0.0), delta(0.0), ticks(0), led_state(none), button_state(bs) {}
|
||||
|
||||
/// For faders. Between 0 and 1.
|
||||
float pos;
|
||||
|
||||
/// For pots. Sign. Either -1 or 1;
|
||||
int sign;
|
||||
|
||||
/// For pots. Signed value of total movement. Between 0 and 1
|
||||
float delta;
|
||||
int ticks;
|
||||
|
||||
/// For pots. Unsigned number of ticks. Usually between 1 and 16.
|
||||
unsigned int ticks;
|
||||
|
||||
LedState led_state;
|
||||
ButtonState button_state;
|
||||
};
|
||||
|
||||
std::ostream & operator << ( std::ostream &, const ControlState & );
|
||||
|
||||
class Control;
|
||||
class Fader;
|
||||
class Button;
|
||||
|
||||
Reference in New Issue
Block a user