151 lines
3.6 KiB
C++
151 lines
3.6 KiB
C++
/*
|
|
* Copyright (C) 2016 Robin Gareus <robin@gareus.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#ifndef _ardour_surfaces_m2pad_h_
|
|
#define _ardour_surfaces_m2pad_h_
|
|
|
|
#include <stdint.h>
|
|
#include "pbd/signals.h"
|
|
|
|
#include <cmath>
|
|
|
|
namespace ArdourSurface {
|
|
|
|
class M2PadInterface
|
|
{
|
|
public:
|
|
M2PadInterface () {}
|
|
virtual ~M2PadInterface () {}
|
|
|
|
/* user API */
|
|
PBD::Signal<void(float)> pressed;
|
|
PBD::Signal<void()> released;
|
|
PBD::Signal<void(float)> aftertouch;
|
|
PBD::Signal<void(float, bool)> event;
|
|
PBD::Signal<void(float)> changed;
|
|
|
|
virtual uint16_t value () const { return 0; }
|
|
virtual float pressure () const { return 0.f; }
|
|
virtual void set_color (uint32_t rgba) {}
|
|
|
|
/* internal API - called from device thread */
|
|
virtual void set_value (uint16_t v) {}
|
|
|
|
virtual void color (uint8_t& r, uint8_t& g, uint8_t& b) const {
|
|
r = g = b = 0;
|
|
}
|
|
};
|
|
|
|
class M2Pad : public M2PadInterface
|
|
{
|
|
public:
|
|
M2Pad ()
|
|
: M2PadInterface ()
|
|
, _pressed (false)
|
|
, _pressure (0)
|
|
, _last (0)
|
|
, _cnt (0)
|
|
, _rgba (0)
|
|
{
|
|
for (int i = 0; i < 4; ++i) {
|
|
hist[i] = 0;
|
|
}
|
|
}
|
|
|
|
uint16_t value () const { return _raw; }
|
|
float pressure () const { return _pressure; }
|
|
|
|
void set_color (uint32_t rgba) { _rgba = rgba; }
|
|
|
|
void color (uint8_t& r, uint8_t& g, uint8_t& b) const
|
|
{
|
|
r = ((_rgba >> 24) & 0xff) >> 1;
|
|
g = ((_rgba >> 16) & 0xff) >> 1;
|
|
b = ((_rgba >> 8) & 0xff) >> 1;
|
|
}
|
|
|
|
void set_value (uint16_t v)
|
|
{
|
|
// bleed to neighboring pads...
|
|
static const uint16_t high = 159;
|
|
static const float low = 159 / 4095.f;
|
|
static const float mindelta = 32.f / 4096.f;
|
|
|
|
if (_raw != v) {
|
|
changed (v / 4095.f);
|
|
_raw = v;
|
|
}
|
|
|
|
// some pads never return to "0", and there's
|
|
// TODO map pressure from a min..max range,
|
|
// even hard hits rarely exceed 3400 or thereabouts.
|
|
// -> "pad sensitivity" config or "calibrate pads"
|
|
|
|
hist[_cnt] = v;
|
|
_cnt = (_cnt + 1) & 3;
|
|
|
|
if (_pressed) {
|
|
const float p = v / 4095.f;
|
|
_pressure += .1 * (p - _pressure);
|
|
if (_pressure < low) {
|
|
_pressure = 0;
|
|
_pressed = false;
|
|
released (); /* EMIT SIGNAL */
|
|
event (_pressure, true); /* EMIT SIGNAL */
|
|
} else {
|
|
if (std::fabsf (_last - _pressure) > mindelta) {
|
|
_last = _pressure;
|
|
aftertouch (_pressure); /* EMIT SIGNAL */
|
|
event (_pressure, false); /* EMIT SIGNAL */
|
|
}
|
|
}
|
|
} else {
|
|
bool above_thresh = true;
|
|
uint16_t max = 0;
|
|
for (int i = 0; i < 4; ++i) {
|
|
if (hist[i] < high) {
|
|
above_thresh = false;
|
|
break;
|
|
}
|
|
max = std::max (max, hist[i]);
|
|
}
|
|
if (above_thresh) {
|
|
_pressed = true;
|
|
_last = _pressure = max / 4095.f;
|
|
pressed (_pressure);
|
|
event (_pressure, true); /* EMIT SIGNAL */
|
|
}
|
|
}
|
|
}
|
|
|
|
protected:
|
|
bool _pressed;
|
|
float _pressure;
|
|
uint16_t _raw;
|
|
float _last;
|
|
uint16_t hist[4];
|
|
unsigned int _cnt;
|
|
uint32_t _rgba;
|
|
};
|
|
|
|
} /* namespace */
|
|
#endif /* _ardour_surfaces_m2pad_h_ */
|
|
|
|
|
|
|