2008-06-02 17:41:35 -04:00
/*
2019-08-03 08:34:29 -04:00
* Copyright ( C ) 2006 - 2009 David Robillard < d @ drobilla . net >
* Copyright ( C ) 2008 - 2018 Paul Davis < paul @ linuxaudiosystems . com >
* Copyright ( C ) 2009 - 2012 Carl Hetherington < carl @ carlh . net >
* Copyright ( C ) 2014 - 2019 Robin Gareus < robin @ gareus . org >
* Copyright ( C ) 2015 - 2016 Len Ovens < len @ ovenwerks . net >
* Copyright ( C ) 2016 Tim Mayberry < mojofunk @ gmail . com >
*
* 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 .
*/
2008-06-02 17:41:35 -04:00
2009-12-30 07:41:10 -05:00
# include <stdint.h>
2010-01-01 13:14:32 -05:00
# include <cmath>
2008-06-02 17:41:35 -04:00
# include <climits>
2010-04-06 12:57:35 -04:00
# include <iostream>
2009-12-21 13:23:07 -05:00
2009-10-29 20:21:40 -04:00
# include "pbd/error.h"
2017-06-18 18:57:53 -04:00
# include "pbd/compose.h"
2012-06-26 15:10:11 -04:00
# include "pbd/stacktrace.h"
2016-08-29 20:57:18 -04:00
# include "pbd/types_convert.h"
2017-06-18 18:57:53 -04:00
# include "pbd/xml++.h"
2009-12-21 13:23:07 -05:00
2013-09-05 04:50:58 -04:00
# include "midi++/types.h" // Added by JE - 06-01-2009. All instances of 'byte' changed to 'MIDI::byte' (for clarification)
2009-10-29 20:21:40 -04:00
# include "midi++/port.h"
# include "midi++/channel.h"
2009-12-21 13:23:07 -05:00
2013-08-07 22:22:11 -04:00
# include "ardour/async_midi_port.h"
2009-10-29 20:21:40 -04:00
# include "ardour/automation_control.h"
2012-06-25 21:55:53 -04:00
# include "ardour/midi_ui.h"
2015-07-09 21:16:44 -04:00
# include "ardour/debug.h"
2008-06-02 17:41:35 -04:00
# include "midicontrollable.h"
2012-03-06 20:11:22 -05:00
# include "generic_midi_control_protocol.h"
2008-06-02 17:41:35 -04:00
2009-12-10 22:18:17 -05:00
using namespace std ;
2008-06-02 17:41:35 -04:00
using namespace MIDI ;
using namespace PBD ;
using namespace ARDOUR ;
2013-08-07 22:22:11 -04:00
MIDIControllable : : MIDIControllable ( GenericMidiControlProtocol * s , MIDI : : Parser & p , bool m )
2012-03-06 20:11:22 -05:00
: _surface ( s )
2013-08-07 22:22:11 -04:00
, _parser ( p )
2010-01-01 13:14:32 -05:00
, _momentary ( m )
2009-12-10 21:00:22 -05:00
{
2009-12-30 07:41:10 -05:00
_learned = false ; /* from URI */
2018-03-03 08:04:12 -05:00
_ctltype = Ctl_Momentary ;
2015-07-24 20:34:22 -04:00
_encoder = No_enc ;
2009-12-30 07:41:10 -05:00
setting = false ;
last_value = 0 ; // got a better idea ?
2018-06-21 22:28:11 -04:00
last_incoming = 256 ; // any out of band value
2011-12-14 10:57:48 -05:00
last_controllable_value = 0.0f ;
2009-12-30 07:41:10 -05:00
control_type = none ;
2015-11-23 12:26:23 -05:00
control_rpn = - 1 ;
control_nrpn = - 1 ;
2009-12-30 07:41:10 -05:00
_control_description = " MIDI Control: none " ;
2013-09-05 04:50:58 -04:00
control_additional = ( MIDI : : byte ) - 1 ;
2009-12-10 21:00:22 -05:00
}
2019-03-22 21:09:39 -04:00
MIDIControllable : : MIDIControllable ( GenericMidiControlProtocol * s , MIDI : : Parser & p , boost : : shared_ptr < PBD : : Controllable > c , bool m )
2012-03-06 20:11:22 -05:00
: _surface ( s )
2013-08-07 22:22:11 -04:00
, _parser ( p )
2010-01-01 13:14:32 -05:00
, _momentary ( m )
2009-12-10 21:00:22 -05:00
{
2019-03-22 21:09:39 -04:00
set_controllable ( c ) ;
2015-10-05 10:17:49 -04:00
2009-12-30 07:41:10 -05:00
_learned = true ; /* from controllable */
2018-03-03 08:04:12 -05:00
_ctltype = Ctl_Momentary ;
2015-07-24 20:34:22 -04:00
_encoder = No_enc ;
2009-12-30 07:41:10 -05:00
setting = false ;
last_value = 0 ; // got a better idea ?
2011-12-14 10:57:48 -05:00
last_controllable_value = 0.0f ;
2009-12-30 07:41:10 -05:00
control_type = none ;
2015-11-23 12:26:23 -05:00
control_rpn = - 1 ;
control_nrpn = - 1 ;
2009-12-30 07:41:10 -05:00
_control_description = " MIDI Control: none " ;
2013-09-05 04:50:58 -04:00
control_additional = ( MIDI : : byte ) - 1 ;
2009-12-10 21:00:22 -05:00
}
MIDIControllable : : ~ MIDIControllable ( )
{
2012-06-26 15:10:11 -04:00
drop_external_control ( ) ;
2009-12-10 21:00:22 -05:00
}
2009-12-30 07:41:10 -05:00
int
MIDIControllable : : init ( const std : : string & s )
2008-06-02 17:41:35 -04:00
{
2009-12-30 07:41:10 -05:00
_current_uri = s ;
2018-06-21 13:05:37 -04:00
return 0 ;
2008-06-02 17:41:35 -04:00
}
void
MIDIControllable : : midi_forget ( )
{
/* stop listening for incoming messages, but retain
our existing event + type information .
*/
2009-10-20 20:15:42 -04:00
2009-12-19 15:26:31 -05:00
midi_sense_connection [ 0 ] . disconnect ( ) ;
midi_sense_connection [ 1 ] . disconnect ( ) ;
midi_learn_connection . disconnect ( ) ;
}
2009-10-20 20:15:42 -04:00
2009-12-19 15:26:31 -05:00
void
MIDIControllable : : drop_external_control ( )
{
2009-12-30 07:41:10 -05:00
midi_forget ( ) ;
2015-11-23 12:26:23 -05:00
control_rpn = - 1 ;
control_nrpn = - 1 ;
2009-12-19 15:26:31 -05:00
control_type = none ;
2013-09-05 04:50:58 -04:00
control_additional = ( MIDI : : byte ) - 1 ;
2008-06-02 17:41:35 -04:00
}
2019-03-22 21:09:39 -04:00
boost : : shared_ptr < PBD : : Controllable >
MIDIControllable : : get_controllable ( ) const
{
2019-03-23 11:32:48 -04:00
return _controllable ;
2019-03-22 21:09:39 -04:00
}
2009-12-10 21:00:22 -05:00
void
2019-03-22 21:09:39 -04:00
MIDIControllable : : set_controllable ( boost : : shared_ptr < PBD : : Controllable > c )
2009-12-10 21:00:22 -05:00
{
2019-03-23 11:32:48 -04:00
Glib : : Threads : : Mutex : : Lock lm ( controllable_lock ) ;
if ( c & & c = = _controllable ) {
2012-06-28 11:38:07 -04:00
return ;
}
2015-10-05 10:17:49 -04:00
2019-03-23 11:32:48 -04:00
controllable_death_connections . drop_connections ( ) ;
2012-06-28 11:38:07 -04:00
2019-03-22 21:09:39 -04:00
if ( c ) {
2019-03-23 11:32:48 -04:00
_controllable = c ;
2019-05-04 17:33:27 -04:00
last_controllable_value = control_to_midi ( c - > get_value ( ) ) ;
2013-01-21 08:20:43 -05:00
} else {
2019-03-22 21:09:39 -04:00
_controllable . reset ( ) ;
2013-01-21 08:20:43 -05:00
last_controllable_value = 0.0f ; // is there a better value?
}
2013-01-17 09:48:17 -05:00
2018-06-21 22:28:11 -04:00
last_incoming = 256 ;
2019-03-22 21:09:39 -04:00
if ( c ) {
2019-03-23 11:32:48 -04:00
c - > DropReferences . connect ( controllable_death_connections , MISSING_INVALIDATOR ,
boost : : bind ( & MIDIControllable : : drop_controllable , this ) ,
MidiControlUI : : instance ( ) ) ;
2012-06-28 11:38:07 -04:00
}
2009-12-10 21:00:22 -05:00
}
2008-06-02 17:41:35 -04:00
void
MIDIControllable : : midi_rebind ( channel_t c )
{
if ( c > = 0 ) {
bind_midi ( c , control_type , control_additional ) ;
} else {
midi_forget ( ) ;
}
}
void
MIDIControllable : : learn_about_external_control ( )
{
drop_external_control ( ) ;
2013-08-07 22:22:11 -04:00
_parser . any . connect_same_thread ( midi_learn_connection , boost : : bind ( & MIDIControllable : : midi_receiver , this , _1 , _2 , _3 ) ) ;
2008-06-02 17:41:35 -04:00
}
void
MIDIControllable : : stop_learning ( )
{
midi_learn_connection . disconnect ( ) ;
}
2012-03-06 20:11:22 -05:00
int
2010-04-06 12:57:35 -04:00
MIDIControllable : : control_to_midi ( float val )
2009-02-18 17:30:06 -05:00
{
2019-03-23 11:32:48 -04:00
if ( ! _controllable ) {
2019-03-22 21:09:39 -04:00
return 0 ;
}
2019-03-23 11:32:48 -04:00
if ( _controllable - > is_gain_like ( ) ) {
return _controllable - > internal_to_interface ( val ) * max_value_for_type ( ) ;
2016-07-10 11:20:05 -04:00
}
2010-04-06 12:57:35 -04:00
2019-03-23 11:32:48 -04:00
float control_min = _controllable - > lower ( ) ;
float control_max = _controllable - > upper ( ) ;
2014-03-23 13:59:24 -04:00
float control_range = control_max - control_min ;
2009-02-18 17:30:06 -05:00
2019-03-23 11:32:48 -04:00
if ( _controllable - > is_toggle ( ) ) {
2012-12-18 20:28:41 -05:00
if ( val > = ( control_min + ( control_range / 2.0f ) ) ) {
return max_value_for_type ( ) ;
} else {
return 0 ;
}
2014-03-23 13:59:24 -04:00
} else {
2019-03-23 11:32:48 -04:00
boost : : shared_ptr < AutomationControl > actl = boost : : dynamic_pointer_cast < AutomationControl > ( _controllable ) ;
2014-03-23 13:59:24 -04:00
if ( actl ) {
control_min = actl - > internal_to_interface ( control_min ) ;
control_max = actl - > internal_to_interface ( control_max ) ;
control_range = control_max - control_min ;
val = actl - > internal_to_interface ( val ) ;
}
2012-12-18 20:28:41 -05:00
}
2015-07-21 02:15:53 -04:00
// fiddle value of max so value doesn't jump from 125 to 127 for 1.0
// otherwise decrement won't work.
return ( val - control_min ) / control_range * ( max_value_for_type ( ) - 1 ) ;
2009-02-18 17:30:06 -05:00
}
float
2012-03-06 20:11:22 -05:00
MIDIControllable : : midi_to_control ( int val )
2009-02-18 17:30:06 -05:00
{
2019-03-23 11:32:48 -04:00
if ( ! _controllable ) {
2019-03-22 21:09:39 -04:00
return 0 ;
}
2015-12-13 18:53:24 -05:00
/* fiddle with MIDI value so that we get an odd number of integer steps
and can thus represent " middle " precisely as 0.5 . this maps to
the range 0. . + 1.0 ( 0 to 126 )
*/
2010-11-29 12:56:08 -05:00
2015-12-13 18:53:24 -05:00
float fv = ( val = = 0 ? 0 : float ( val - 1 ) / ( max_value_for_type ( ) - 1 ) ) ;
2009-12-30 14:33:52 -05:00
2019-03-23 11:32:48 -04:00
if ( _controllable - > is_gain_like ( ) ) {
return _controllable - > interface_to_internal ( fv ) ;
2015-12-13 18:53:24 -05:00
}
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " Raw value %1 float %2 \n " , val , fv ) ) ;
2009-02-18 17:30:06 -05:00
2019-03-23 11:32:48 -04:00
float control_min = _controllable - > lower ( ) ;
float control_max = _controllable - > upper ( ) ;
2014-03-23 13:59:24 -04:00
float control_range = control_max - control_min ;
2015-12-13 18:53:24 -05:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " Min %1 Max %2 Range %3 \n " , control_min , control_max , control_range ) ) ;
2014-03-23 13:59:24 -04:00
2019-03-23 11:32:48 -04:00
boost : : shared_ptr < AutomationControl > actl = boost : : dynamic_pointer_cast < AutomationControl > ( _controllable ) ;
2014-03-23 13:59:24 -04:00
if ( actl ) {
if ( fv = = 0.f ) return control_min ;
if ( fv = = 1.f ) return control_max ;
control_min = actl - > internal_to_interface ( control_min ) ;
control_max = actl - > internal_to_interface ( control_max ) ;
control_range = control_max - control_min ;
return actl - > interface_to_internal ( ( fv * control_range ) + control_min ) ;
}
2012-03-06 20:11:22 -05:00
return ( fv * control_range ) + control_min ;
2009-02-18 17:30:06 -05:00
}
2009-10-20 20:15:42 -04:00
void
MIDIControllable : : midi_sense_note_on ( Parser & p , EventTwoBytes * tb )
2008-06-02 17:41:35 -04:00
{
midi_sense_note ( p , tb , true ) ;
}
2009-10-20 20:15:42 -04:00
void
MIDIControllable : : midi_sense_note_off ( Parser & p , EventTwoBytes * tb )
2008-06-02 17:41:35 -04:00
{
midi_sense_note ( p , tb , false ) ;
}
2012-06-25 08:33:13 -04:00
int
MIDIControllable : : lookup_controllable ( )
{
2018-06-21 13:05:37 -04:00
if ( _current_uri . empty ( ) ) {
2012-06-25 08:33:13 -04:00
return - 1 ;
}
2018-06-21 13:05:37 -04:00
boost : : shared_ptr < Controllable > c = _surface - > lookup_controllable ( _current_uri ) ;
2012-06-25 08:33:13 -04:00
if ( ! c ) {
return - 1 ;
}
2019-03-22 21:09:39 -04:00
set_controllable ( c ) ;
2012-06-25 08:33:13 -04:00
return 0 ;
}
2012-06-25 21:55:53 -04:00
void
2019-03-23 11:32:48 -04:00
MIDIControllable : : drop_controllable ( )
2012-06-25 21:55:53 -04:00
{
2019-03-23 11:32:48 -04:00
set_controllable ( boost : : shared_ptr < PBD : : Controllable > ( ) ) ;
2012-06-25 21:55:53 -04:00
}
2008-06-02 17:41:35 -04:00
void
2010-09-02 13:01:36 -04:00
MIDIControllable : : midi_sense_note ( Parser & , EventTwoBytes * msg , bool /*is_on*/ )
2008-06-02 17:41:35 -04:00
{
2019-03-23 11:32:48 -04:00
if ( ! _controllable ) {
2012-06-25 08:33:13 -04:00
if ( lookup_controllable ( ) ) {
return ;
}
2009-12-10 21:00:22 -05:00
}
2019-03-23 11:32:48 -04:00
assert ( _controllable ) ;
2019-03-22 21:09:39 -04:00
2019-03-23 11:32:48 -04:00
_surface - > maybe_start_touch ( _controllable ) ;
2017-01-23 07:25:56 -05:00
2019-03-23 11:32:48 -04:00
if ( ! _controllable - > is_toggle ( ) ) {
2012-07-23 09:31:51 -04:00
if ( control_additional = = msg - > note_number ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( msg - > velocity ) , Controllable : : UseGroup ) ;
2015-07-09 21:16:44 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " Note %1 value %2 %3 \n " , ( int ) msg - > note_number , ( float ) midi_to_control ( msg - > velocity ) , current_uri ( ) ) ) ;
2012-07-23 09:31:51 -04:00
}
2008-06-02 17:41:35 -04:00
} else {
2010-01-01 13:14:32 -05:00
if ( control_additional = = msg - > note_number ) {
2019-03-23 11:32:48 -04:00
float new_value = _controllable - > get_value ( ) > 0.5f ? 0.0f : 1.0f ;
_controllable - > set_value ( new_value , Controllable : : UseGroup ) ;
2015-07-09 21:16:44 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " Note %1 Value %2 %3 \n " , ( int ) msg - > note_number , ( float ) new_value , current_uri ( ) ) ) ;
2008-06-02 17:41:35 -04:00
}
}
2019-03-23 11:32:48 -04:00
last_value = ( MIDI : : byte ) ( _controllable - > get_value ( ) * 127.0 ) ; // to prevent feedback fights
2008-06-02 17:41:35 -04:00
}
void
MIDIControllable : : midi_sense_controller ( Parser & , EventTwoBytes * msg )
{
2019-03-23 11:32:48 -04:00
if ( ! _controllable ) {
2012-06-25 08:33:13 -04:00
if ( lookup_controllable ( ) ) {
return ;
}
2009-12-10 21:00:22 -05:00
}
2019-03-23 11:32:48 -04:00
assert ( _controllable ) ;
2012-06-26 16:49:52 -04:00
2019-03-23 11:32:48 -04:00
_surface - > maybe_start_touch ( _controllable ) ;
2009-02-18 18:54:41 -05:00
2008-06-02 17:41:35 -04:00
if ( control_additional = = msg - > controller_number ) {
2010-01-01 13:14:32 -05:00
2019-03-23 11:32:48 -04:00
if ( ! _controllable - > is_toggle ( ) ) {
2015-07-24 20:34:22 -04:00
if ( get_encoder ( ) = = No_enc ) {
2015-07-21 02:15:53 -04:00
float new_value = msg - > value ;
float max_value = max ( last_controllable_value , new_value ) ;
float min_value = min ( last_controllable_value , new_value ) ;
float range = max_value - min_value ;
float threshold = ( float ) _surface - > threshold ( ) ;
bool const in_sync = (
range < threshold & &
2019-03-23 11:32:48 -04:00
_controllable - > get_value ( ) < = midi_to_control ( max_value ) & &
_controllable - > get_value ( ) > = midi_to_control ( min_value )
2015-07-21 02:15:53 -04:00
) ;
/* If the surface is not motorised, we try to prevent jumps when
the MIDI controller and controllable are out of sync .
There might be a better way of doing this .
*/
if ( in_sync | | _surface - > motorised ( ) ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( new_value ) , Controllable : : UseGroup ) ;
2015-07-21 02:15:53 -04:00
}
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " MIDI CC %1 value %2 %3 \n " , ( int ) msg - > controller_number , ( float ) midi_to_control ( new_value ) , current_uri ( ) ) ) ;
last_controllable_value = new_value ;
} else {
int offset = ( msg - > value & 0x3f ) ;
2015-07-24 20:34:22 -04:00
switch ( get_encoder ( ) ) {
case Enc_L :
if ( msg - > value > 0x40 ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( last_value - offset + 1 ) , Controllable : : UseGroup ) ;
2015-07-24 20:34:22 -04:00
} else {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( last_value + offset + 1 ) , Controllable : : UseGroup ) ;
2015-07-24 20:34:22 -04:00
}
break ;
case Enc_R :
if ( msg - > value > 0x40 ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( last_value + offset + 1 ) , Controllable : : UseGroup ) ;
2015-07-24 20:34:22 -04:00
} else {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( last_value - offset + 1 ) , Controllable : : UseGroup ) ;
2015-07-24 20:34:22 -04:00
}
break ;
case Enc_2 :
if ( msg - > value > 0x40 ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( last_value - ( 0x7f - msg - > value ) + 1 ) , Controllable : : UseGroup ) ;
2015-07-24 20:34:22 -04:00
} else {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( last_value + offset + 1 ) , Controllable : : UseGroup ) ;
2015-07-24 20:34:22 -04:00
}
break ;
case Enc_B :
if ( msg - > value > 0x40 ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( last_value + offset + 1 ) , Controllable : : UseGroup ) ;
2015-07-24 20:34:22 -04:00
} else {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( midi_to_control ( last_value - ( 0x40 - offset ) ) , Controllable : : UseGroup ) ;
2015-07-24 20:34:22 -04:00
}
break ;
default :
break ;
2015-07-21 02:15:53 -04:00
}
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " MIDI CC %1 value %2 %3 \n " , ( int ) msg - > controller_number , ( int ) last_value , current_uri ( ) ) ) ;
2013-01-17 09:48:17 -05:00
2011-12-14 10:57:48 -05:00
}
2008-06-02 17:41:35 -04:00
} else {
2016-02-02 22:22:20 -05:00
2018-06-21 22:28:11 -04:00
switch ( get_ctltype ( ) ) {
case Ctl_Dial :
/* toggle value whenever direction of knob motion changes */
if ( last_incoming > 127 ) {
/* relax ... first incoming message */
} else {
if ( msg - > value > last_incoming ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( 1.0 , Controllable : : UseGroup ) ;
2018-06-21 22:28:11 -04:00
} else {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( 0.0 , Controllable : : UseGroup ) ;
2018-06-21 22:28:11 -04:00
}
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " dial Midi CC %1 value 1 %2 \n " , ( int ) msg - > controller_number , current_uri ( ) ) ) ;
}
last_incoming = msg - > value ;
break ;
case Ctl_Momentary :
/* toggle it if over 64, otherwise leave it alone. This behaviour that works with buttons which send a value > 64 each
* time they are pressed .
*/
if ( msg - > value > = 0x40 ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( _controllable - > get_value ( ) > = 0.5 ? 0.0 : 1.0 , Controllable : : UseGroup ) ;
2018-06-21 22:28:11 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " toggle Midi CC %1 value 1 %2 \n " , ( int ) msg - > controller_number , current_uri ( ) ) ) ;
}
break ;
case Ctl_Toggle :
/* toggle if value is over 64, otherwise turn it off. This is behaviour designed for buttons which send a value > 64 when pressed,
maintain state ( i . e . they know they were pressed ) and then send zero the next time .
*/
if ( msg - > value > = 0x40 ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( _controllable - > get_value ( ) > = 0.5 ? 0.0 : 1.0 , Controllable : : UseGroup ) ;
2018-06-21 22:28:11 -04:00
} else {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( 0.0 , Controllable : : NoGroup ) ;
2018-06-21 22:28:11 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " Midi CC %1 value 0 %2 \n " , ( int ) msg - > controller_number , current_uri ( ) ) ) ;
break ;
2018-03-03 08:04:12 -05:00
}
2008-06-02 17:41:35 -04:00
}
}
2019-03-23 11:32:48 -04:00
last_value = ( MIDI : : byte ) ( control_to_midi ( _controllable - > get_value ( ) ) ) ; // to prevent feedback fights
2008-06-02 17:41:35 -04:00
}
}
void
2013-09-05 04:50:58 -04:00
MIDIControllable : : midi_sense_program_change ( Parser & , MIDI : : byte msg )
2008-06-02 17:41:35 -04:00
{
2019-03-23 11:32:48 -04:00
if ( ! _controllable ) {
2012-06-25 08:33:13 -04:00
if ( lookup_controllable ( ) ) {
return ;
}
2009-12-10 21:00:22 -05:00
}
2017-01-23 07:25:56 -05:00
2019-03-23 11:32:48 -04:00
assert ( _controllable ) ;
2019-03-22 21:09:39 -04:00
2019-03-23 11:32:48 -04:00
_surface - > maybe_start_touch ( _controllable ) ;
2017-01-23 07:25:56 -05:00
2015-07-20 16:34:11 -04:00
if ( msg = = control_additional ) {
2008-06-02 17:41:35 -04:00
2019-03-23 11:32:48 -04:00
if ( ! _controllable - > is_toggle ( ) ) {
_controllable - > set_value ( 1.0 , Controllable : : UseGroup ) ;
2015-07-20 16:34:11 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " MIDI program %1 value 1.0 %3 \n " , ( int ) msg , current_uri ( ) ) ) ;
} else {
2019-03-23 11:32:48 -04:00
float new_value = _controllable - > get_value ( ) > 0.5f ? 0.0f : 1.0f ;
_controllable - > set_value ( new_value , Controllable : : UseGroup ) ;
2015-07-20 16:34:11 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " MIDI program %1 value %2 %3 \n " , ( int ) msg , ( float ) new_value , current_uri ( ) ) ) ;
}
2009-10-20 20:15:42 -04:00
}
2010-01-01 13:14:32 -05:00
2019-03-23 11:32:48 -04:00
last_value = ( MIDI : : byte ) ( _controllable - > get_value ( ) * 127.0 ) ; // to prevent feedback fights
2008-06-02 17:41:35 -04:00
}
void
2009-07-21 11:55:17 -04:00
MIDIControllable : : midi_sense_pitchbend ( Parser & , pitchbend_t pb )
2008-06-02 17:41:35 -04:00
{
2019-03-23 11:32:48 -04:00
if ( ! _controllable ) {
2012-06-25 08:33:13 -04:00
if ( lookup_controllable ( ) ) {
return ;
}
2009-12-10 21:00:22 -05:00
}
2019-03-23 11:32:48 -04:00
assert ( _controllable ) ;
2019-03-22 21:09:39 -04:00
2019-03-23 11:32:48 -04:00
_surface - > maybe_start_touch ( _controllable ) ;
2017-01-23 07:25:56 -05:00
2019-03-23 11:32:48 -04:00
if ( ! _controllable - > is_toggle ( ) ) {
2019-05-09 10:06:00 -04:00
float new_value = pb ;
float max_value = max ( last_controllable_value , new_value ) ;
float min_value = min ( last_controllable_value , new_value ) ;
float range = max_value - min_value ;
float threshold = 128.f * _surface - > threshold ( ) ;
bool const in_sync = (
range < threshold & &
_controllable - > get_value ( ) < = midi_to_control ( max_value ) & &
_controllable - > get_value ( ) > = midi_to_control ( min_value )
) ;
if ( in_sync | | _surface - > motorised ( ) ) {
_controllable - > set_value ( midi_to_control ( pb ) , Controllable : : UseGroup ) ;
}
2015-07-09 21:16:44 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " MIDI pitchbend %1 value %2 %3 \n " , ( int ) control_channel , ( float ) midi_to_control ( pb ) , current_uri ( ) ) ) ;
2019-05-09 10:06:00 -04:00
last_controllable_value = new_value ;
2010-01-01 13:14:32 -05:00
} else {
2015-07-09 21:27:08 -04:00
if ( pb > 8065.0f ) {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( 1 , Controllable : : UseGroup ) ;
2015-07-09 21:27:08 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " Midi pitchbend %1 value 1 %2 \n " , ( int ) control_channel , current_uri ( ) ) ) ;
} else {
2019-03-23 11:32:48 -04:00
_controllable - > set_value ( 0 , Controllable : : UseGroup ) ;
2015-07-09 21:27:08 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " Midi pitchbend %1 value 0 %2 \n " , ( int ) control_channel , current_uri ( ) ) ) ;
}
2010-01-01 13:14:32 -05:00
}
2008-06-02 17:41:35 -04:00
2019-03-23 11:32:48 -04:00
last_value = control_to_midi ( _controllable - > get_value ( ) ) ;
2009-10-20 20:15:42 -04:00
}
2008-06-02 17:41:35 -04:00
void
2013-09-05 04:50:58 -04:00
MIDIControllable : : midi_receiver ( Parser & , MIDI : : byte * msg , size_t /*len*/ )
2008-06-02 17:41:35 -04:00
{
/* we only respond to channel messages */
if ( ( msg [ 0 ] & 0xF0 ) < 0x80 | | ( msg [ 0 ] & 0xF0 ) > 0xE0 ) {
return ;
}
2015-07-20 15:37:53 -04:00
_surface - > check_used_event ( msg [ 0 ] , msg [ 1 ] ) ;
2008-06-02 17:41:35 -04:00
bind_midi ( ( channel_t ) ( msg [ 0 ] & 0xf ) , eventType ( msg [ 0 ] & 0xF0 ) , msg [ 1 ] ) ;
2019-03-23 11:32:48 -04:00
if ( _controllable ) {
_controllable - > LearningFinished ( ) ;
2012-06-25 08:33:13 -04:00
}
2008-06-02 17:41:35 -04:00
}
2015-11-23 12:26:23 -05:00
void
MIDIControllable : : rpn_value_change ( Parser & , uint16_t rpn , float val )
{
if ( control_rpn = = rpn ) {
2019-03-23 11:32:48 -04:00
if ( _controllable ) {
_controllable - > set_value ( val , Controllable : : UseGroup ) ;
2015-11-23 12:26:23 -05:00
}
}
}
void
MIDIControllable : : nrpn_value_change ( Parser & , uint16_t nrpn , float val )
{
if ( control_nrpn = = nrpn ) {
2019-03-23 11:32:48 -04:00
if ( _controllable ) {
_controllable - > set_value ( val , Controllable : : UseGroup ) ;
2015-11-23 12:26:23 -05:00
}
}
}
void
MIDIControllable : : rpn_change ( Parser & , uint16_t rpn , int dir )
{
if ( control_rpn = = rpn ) {
2019-03-23 11:32:48 -04:00
if ( _controllable ) {
2015-11-23 12:26:23 -05:00
/* XXX how to increment/decrement ? */
2019-03-23 11:32:48 -04:00
// _controllable->set_value (val);
2015-11-23 12:26:23 -05:00
}
}
}
void
MIDIControllable : : nrpn_change ( Parser & , uint16_t nrpn , int dir )
{
if ( control_nrpn = = nrpn ) {
2019-03-23 11:32:48 -04:00
if ( _controllable ) {
2015-11-23 12:26:23 -05:00
/* XXX how to increment/decrement ? */
2019-03-23 11:32:48 -04:00
// _controllable->set_value (val);
2015-11-23 12:26:23 -05:00
}
}
}
void
MIDIControllable : : bind_rpn_value ( channel_t chn , uint16_t rpn )
{
int chn_i = chn ;
drop_external_control ( ) ;
control_rpn = rpn ;
control_channel = chn ;
_parser . channel_rpn [ chn_i ] . connect_same_thread ( midi_sense_connection [ 0 ] , boost : : bind ( & MIDIControllable : : rpn_value_change , this , _1 , _2 , _3 ) ) ;
}
void
MIDIControllable : : bind_nrpn_value ( channel_t chn , uint16_t nrpn )
{
int chn_i = chn ;
drop_external_control ( ) ;
control_nrpn = nrpn ;
control_channel = chn ;
_parser . channel_nrpn [ chn_i ] . connect_same_thread ( midi_sense_connection [ 0 ] , boost : : bind ( & MIDIControllable : : rpn_value_change , this , _1 , _2 , _3 ) ) ;
}
void
MIDIControllable : : bind_nrpn_change ( channel_t chn , uint16_t nrpn )
{
int chn_i = chn ;
drop_external_control ( ) ;
control_nrpn = nrpn ;
control_channel = chn ;
_parser . channel_nrpn_change [ chn_i ] . connect_same_thread ( midi_sense_connection [ 0 ] , boost : : bind ( & MIDIControllable : : rpn_change , this , _1 , _2 , _3 ) ) ;
}
void
MIDIControllable : : bind_rpn_change ( channel_t chn , uint16_t rpn )
{
int chn_i = chn ;
drop_external_control ( ) ;
control_rpn = rpn ;
control_channel = chn ;
_parser . channel_rpn_change [ chn_i ] . connect_same_thread ( midi_sense_connection [ 0 ] , boost : : bind ( & MIDIControllable : : nrpn_change , this , _1 , _2 , _3 ) ) ;
}
2008-06-02 17:41:35 -04:00
void
MIDIControllable : : bind_midi ( channel_t chn , eventType ev , MIDI : : byte additional )
{
char buf [ 64 ] ;
drop_external_control ( ) ;
control_type = ev ;
control_channel = chn ;
control_additional = additional ;
int chn_i = chn ;
switch ( ev ) {
case MIDI : : off :
2013-08-07 22:22:11 -04:00
_parser . channel_note_off [ chn_i ] . connect_same_thread ( midi_sense_connection [ 0 ] , boost : : bind ( & MIDIControllable : : midi_sense_note_off , this , _1 , _2 ) ) ;
2008-06-02 17:41:35 -04:00
2010-01-01 13:14:32 -05:00
/* if this is a togglee, connect to noteOn as well,
2008-06-02 17:41:35 -04:00
and we ' ll toggle back and forth between the two .
*/
2010-01-01 13:14:32 -05:00
if ( _momentary ) {
2013-08-07 22:22:11 -04:00
_parser . channel_note_on [ chn_i ] . connect_same_thread ( midi_sense_connection [ 1 ] , boost : : bind ( & MIDIControllable : : midi_sense_note_on , this , _1 , _2 ) ) ;
2015-10-04 14:51:05 -04:00
}
2009-12-19 15:26:31 -05:00
2008-06-02 17:41:35 -04:00
_control_description = " MIDI control: NoteOff " ;
break ;
case MIDI : : on :
2013-08-07 22:22:11 -04:00
_parser . channel_note_on [ chn_i ] . connect_same_thread ( midi_sense_connection [ 0 ] , boost : : bind ( & MIDIControllable : : midi_sense_note_on , this , _1 , _2 ) ) ;
2010-01-01 13:14:32 -05:00
if ( _momentary ) {
2013-08-07 22:22:11 -04:00
_parser . channel_note_off [ chn_i ] . connect_same_thread ( midi_sense_connection [ 1 ] , boost : : bind ( & MIDIControllable : : midi_sense_note_off , this , _1 , _2 ) ) ;
2008-06-02 17:41:35 -04:00
}
_control_description = " MIDI control: NoteOn " ;
break ;
2015-10-05 10:17:49 -04:00
2008-06-02 17:41:35 -04:00
case MIDI : : controller :
2013-08-07 22:22:11 -04:00
_parser . channel_controller [ chn_i ] . connect_same_thread ( midi_sense_connection [ 0 ] , boost : : bind ( & MIDIControllable : : midi_sense_controller , this , _1 , _2 ) ) ;
2008-06-02 17:41:35 -04:00
snprintf ( buf , sizeof ( buf ) , " MIDI control: Controller %d " , control_additional ) ;
_control_description = buf ;
break ;
case MIDI : : program :
2013-08-07 22:22:11 -04:00
_parser . channel_program_change [ chn_i ] . connect_same_thread ( midi_sense_connection [ 0 ] , boost : : bind ( & MIDIControllable : : midi_sense_program_change , this , _1 , _2 ) ) ;
2010-01-01 13:14:32 -05:00
_control_description = " MIDI control: ProgramChange " ;
2008-06-02 17:41:35 -04:00
break ;
case MIDI : : pitchbend :
2013-08-07 22:22:11 -04:00
_parser . channel_pitchbend [ chn_i ] . connect_same_thread ( midi_sense_connection [ 0 ] , boost : : bind ( & MIDIControllable : : midi_sense_pitchbend , this , _1 , _2 ) ) ;
2010-01-01 13:14:32 -05:00
_control_description = " MIDI control: Pitchbend " ;
2008-06-02 17:41:35 -04:00
break ;
default :
break ;
}
2015-07-09 21:16:44 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " Controlable: bind_midi: %1 on Channel %2 value %3 \n " , _control_description , chn_i + 1 , ( int ) additional ) ) ;
2008-06-02 17:41:35 -04:00
}
MIDI : : byte *
2009-07-21 11:55:17 -04:00
MIDIControllable : : write_feedback ( MIDI : : byte * buf , int32_t & bufsize , bool /*force*/ )
2008-06-02 17:41:35 -04:00
{
2019-03-23 11:32:48 -04:00
Glib : : Threads : : Mutex : : Lock lm ( controllable_lock , Glib : : Threads : : TRY_LOCK ) ;
if ( ! lm . locked ( ) ) {
return buf ;
}
if ( ! _controllable | | ! _surface - > get_feedback ( ) ) {
2015-11-23 12:26:23 -05:00
return buf ;
}
2019-03-23 11:32:48 -04:00
float val = _controllable - > get_value ( ) ;
2015-11-23 12:26:23 -05:00
/* Note that when sending RPN/NPRN we do two things:
*
* always send MSB first , then LSB
* null / reset the parameter ID after sending .
*
* this follows recommendations found online , eg . http : //www.philrees.co.uk/nrpnq.htm
*/
if ( control_rpn > = 0 ) {
if ( bufsize < 13 ) {
return buf ;
}
int rpn_val = ( int ) lrintf ( val * 16384.0 ) ;
if ( last_value = = rpn_val ) {
return buf ;
}
* buf + + = ( 0xb0 ) | control_channel ;
* buf + + = 0x62 ;
* buf + + = ( int ) ( ( control_rpn ) > > 7 ) ;
* buf + + = 0x63 ;
* buf + + = ( int ) ( control_rpn & 0x7f ) ;
* buf + + = 0x06 ;
* buf + + = ( int ) ( rpn_val > > 7 ) ;
* buf + + = 0x26 ;
* buf + + = ( int ) ( rpn_val & 0x7f ) ;
* buf + + = 0x62 ;
* buf + + = 0x7f ;
* buf + + = 0x63 ;
* buf + + = 0x7f ;
bufsize - = 13 ;
last_value = rpn_val ;
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " MIDI out: RPN %1 Channel %2 Value %3 \n " , control_rpn , ( int ) control_channel , val ) ) ;
return buf ;
}
if ( control_nrpn > = 0 ) {
int rpn_val = ( int ) lrintf ( val * 16384.0 ) ;
if ( last_value = = rpn_val ) {
return buf ;
}
* buf + + = ( 0xb0 ) | control_channel ;
* buf + + = 0x64 ;
* buf + + = ( int ) ( ( control_rpn ) > > 7 ) ;
* buf + + = 0x65 ;
* buf + + = ( int ) ( control_rpn & 0x7f ) ;
* buf + + = 0x06 ;
* buf + + = ( int ) ( rpn_val > > 7 ) ;
* buf + + = 0x26 ;
* buf + + = ( int ) ( rpn_val & 0x7f ) ;
* buf + + = 0x64 ;
* buf + + = 0x7f ;
* buf + + = 0x65 ;
* buf + + = 0x7f ;
last_value = rpn_val ;
bufsize - = 13 ;
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " MIDI out: NRPN %1 Channel %2 Value %3 \n " , control_nrpn , ( int ) control_channel , val ) ) ;
return buf ;
}
if ( control_type = = none | | bufsize < = 2 ) {
2012-03-06 10:08:17 -05:00
return buf ;
}
2015-10-05 10:17:49 -04:00
2015-11-23 12:26:23 -05:00
int const gm = control_to_midi ( val ) ;
2010-01-01 13:14:32 -05:00
2012-03-06 10:08:17 -05:00
if ( gm = = last_value ) {
return buf ;
}
2009-10-20 20:15:42 -04:00
2019-03-23 12:35:26 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " Feedback: %1 %2 \n " , control_description ( ) , current_uri ( ) ) ) ;
2015-07-09 21:16:44 -04:00
2012-03-06 10:08:17 -05:00
* buf + + = ( 0xF0 & control_type ) | ( 0xF & control_channel ) ;
2015-07-09 21:23:14 -04:00
int ev_size = 3 ;
2012-03-06 10:08:17 -05:00
switch ( control_type ) {
case MIDI : : pitchbend :
* buf + + = int ( gm ) & 127 ;
* buf + + = ( int ( gm ) > > 7 ) & 127 ;
break ;
2015-07-09 21:23:14 -04:00
case MIDI : : program :
* buf + + = control_additional ; /* program number */
ev_size = 2 ;
break ;
2012-03-06 10:08:17 -05:00
default :
* buf + + = control_additional ; /* controller number */
* buf + + = gm ;
break ;
2008-06-02 17:41:35 -04:00
}
2015-07-09 21:16:44 -04:00
DEBUG_TRACE ( DEBUG : : GenericMidi , string_compose ( " MIDI out: Type %1 Channel %2 Bytes %3 %4 \n " , ( int ) control_type , ( int ) control_channel , ( int ) * ( buf - 2 ) , ( int ) * ( buf - 1 ) ) ) ;
2009-10-20 20:15:42 -04:00
2012-03-06 10:08:17 -05:00
last_value = gm ;
2015-07-09 21:23:14 -04:00
bufsize - = ev_size ;
2012-03-06 10:08:17 -05:00
2008-06-02 17:41:35 -04:00
return buf ;
}
2009-10-20 20:15:42 -04:00
int
MIDIControllable : : set_state ( const XMLNode & node , int /*version*/ )
2008-06-02 17:41:35 -04:00
{
int xx ;
2016-08-29 20:57:18 -04:00
std : : string str ;
if ( node . get_property ( " event " , str ) ) {
sscanf ( str . c_str ( ) , " 0x%x " , & xx ) ;
2008-06-02 17:41:35 -04:00
control_type = ( MIDI : : eventType ) xx ;
} else {
return - 1 ;
}
2016-08-29 20:57:18 -04:00
if ( node . get_property ( " channel " , xx ) ) {
control_channel = xx ;
2008-06-02 17:41:35 -04:00
} else {
return - 1 ;
}
2016-08-29 20:57:18 -04:00
if ( node . get_property ( " additional " , str ) ) {
sscanf ( str . c_str ( ) , " 0x%x " , & xx ) ;
2008-06-02 17:41:35 -04:00
control_additional = ( MIDI : : byte ) xx ;
} else {
return - 1 ;
}
bind_midi ( control_channel , control_type , control_additional ) ;
2009-10-20 20:15:42 -04:00
2008-06-02 17:41:35 -04:00
return 0 ;
}
XMLNode &
MIDIControllable : : get_state ( )
{
char buf [ 32 ] ;
2009-12-10 22:18:17 -05:00
XMLNode * node = new XMLNode ( " MIDIControllable " ) ;
2019-03-23 11:32:48 -04:00
if ( _current_uri . empty ( ) & & _controllable ) {
node - > set_property ( " id " , _controllable - > id ( ) ) ;
2010-11-27 12:43:32 -05:00
} else {
2016-08-29 20:57:18 -04:00
node - > set_property ( " uri " , _current_uri ) ;
}
2009-12-30 11:48:58 -05:00
2019-03-23 11:32:48 -04:00
if ( _controllable ) {
2009-12-10 22:18:17 -05:00
snprintf ( buf , sizeof ( buf ) , " 0x%x " , ( int ) control_type ) ;
2016-08-29 20:57:18 -04:00
node - > set_property ( " event " , ( const char * ) buf ) ;
node - > set_property ( " channel " , ( int16_t ) control_channel ) ;
2009-12-10 22:18:17 -05:00
snprintf ( buf , sizeof ( buf ) , " 0x%x " , ( int ) control_additional ) ;
2016-08-29 20:57:18 -04:00
node - > set_property ( " additional " , ( const char * ) buf ) ;
2009-12-10 22:18:17 -05:00
}
2008-06-02 17:41:35 -04:00
2009-12-10 22:18:17 -05:00
return * node ;
2008-06-02 17:41:35 -04:00
}
2012-03-06 10:08:17 -05:00
/** @return the maximum value for a control value transmitted
* using a given MIDI : : eventType .
*/
int
MIDIControllable : : max_value_for_type ( ) const
{
/* XXX: this is not complete */
2015-10-05 10:17:49 -04:00
2012-03-06 10:08:17 -05:00
if ( control_type = = MIDI : : pitchbend ) {
return 16383 ;
}
return 127 ;
}