diff --git a/README.md b/README.md index 54506c7..49a6789 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ implemented: + EFX bypass (on/off) + Amp CC messages (except for on/off) + Reverb CC messages + + Delay CC messages I'm using WinPCAP and tshark to snoop communication and plan to implement all features accessible from the Fender FUSE application. diff --git a/amp.cpp b/amp.cpp index 8ff1d10..353e3ea 100644 --- a/amp.cpp +++ b/amp.cpp @@ -3,13 +3,29 @@ #include "mustang.h" int -AmpCC::control_common1( int parm, int bucket, int value ) { - return amp->control_common1( parm, bucket, value ); +AmpCC::continuous_control( int parm5, int parm6, int parm7, int value ) { + Mustang::Cmd cmd; + cmd.state_index = AMP_STATE; + cmd.parm2 = 0x02; + cmd.parm5 = parm5; + cmd.parm6 = parm6; + cmd.parm7 = parm7; + cmd.value = value; + + return amp->continuous_control( cmd ); } int -AmpCC::control_common2( int parm, int bucket, int value ) { - return amp->control_common2( parm, bucket, value ); +AmpCC::discrete_control( int parm5, int parm6, int parm7, int value ) { + Mustang::Cmd cmd; + cmd.state_index = AMP_STATE; + cmd.parm2 = 0x02; + cmd.parm5 = parm5; + cmd.parm6 = parm6; + cmd.parm7 = parm7; + cmd.value = value; + + return amp->discrete_control( cmd ); } int @@ -68,96 +84,96 @@ AmpCC::dispatch( int cc, int value ) { int AmpCC::cc69( int value ) { - return control_common1( 0x01, 0x0c, value ); + return continuous_control( 0x01, 0x01, 0x0c, value ); } int AmpCC::cc70( int value ) { - return control_common1( 0x00, 0x0c, value ); + return continuous_control( 0x00, 0x00, 0x0c, value ); } int AmpCC::cc71( int value ) { - return control_common1( 0x04, 0x0c, value ); + return continuous_control( 0x04, 0x04, 0x0c, value ); } int AmpCC::cc72( int value ) { - return control_common1( 0x05, 0x0c, value ); + return continuous_control( 0x05, 0x05, 0x0c, value ); } int AmpCC::cc73( int value ) { - return control_common1( 0x06, 0x0c, value ); + return continuous_control( 0x06, 0x06, 0x0c, value ); } int AmpCC::cc74( int value ) { if ( value > 2 ) return 0; - return control_common2( 0x13, 0x8f, value ); + return discrete_control( 0x13, 0x13, 0x8f, value ); } int AmpCC::cc75( int value ) { - return control_common1( 0x0a, 0x0d, value ); + return continuous_control( 0x0a, 0x0a, 0x0d, value ); } int AmpCC::cc76( int value ) { if ( value > 4 ) return 0; - return control_common2( 0x0f, 0x90, value ); + return discrete_control( 0x0f, 0x0f, 0x90, value ); } int AmpCC::cc77( int value ) { if ( value < 1 || value > 12 ) return 0; - return control_common2( 0x11, 0x8e, value ); + return discrete_control( 0x11, 0x11, 0x8e, value ); } int AmpCC1::cc78( int value ) { - return control_common1( 0x07, 0x0c, value ); + return continuous_control( 0x07, 0x07, 0x0c, value ); } int AmpCC1::cc79( int value ) { - return control_common1( 0x02, 0x0c, value ); + return continuous_control( 0x02, 0x02, 0x0c, value ); } int AmpCC2::cc78( int value ) { - return control_common1( 0x02, 0x0c, value ); + return continuous_control( 0x02, 0x02, 0x0c, value ); } int AmpCC2::cc79( int value ) { - return control_common1( 0x03, 0x0c, value ); + return continuous_control( 0x03, 0x03, 0x0c, value ); } int AmpCC3::cc78( int value ) { - return control_common1( 0x07, 0x0c, value ); + return continuous_control( 0x07, 0x07, 0x0c, value ); } int AmpCC3::cc79( int value ) { - return control_common1( 0x03, 0x0c, value ); + return continuous_control( 0x03, 0x03, 0x0c, value ); } int AmpCC4::cc78( int value ) { - return control_common1( 0x07, 0x0c, value ); + return continuous_control( 0x07, 0x07, 0x0c, value ); } int AmpCC4::cc79( int value ) { - return control_common1( 0x03, 0x0c, value ); + return continuous_control( 0x03, 0x03, 0x0c, value ); } diff --git a/amp.h b/amp.h index 467a7ca..f064541 100644 --- a/amp.h +++ b/amp.h @@ -18,8 +18,8 @@ protected: // Only base class is friend of Mustang, so forward calls from // derived classes through these methods. - int control_common1( int parm, int bucket, int value ); - int control_common2( int parm, int bucket, int value ); + int continuous_control( int parm5, int parm6, int parm7, int value ); + int discrete_control( int parm5, int parm6, int parm7, int value ); public: AmpCC( Mustang * theAmp ) : amp(theAmp) {} diff --git a/delay.cpp b/delay.cpp new file mode 100644 index 0000000..224cb90 --- /dev/null +++ b/delay.cpp @@ -0,0 +1,229 @@ + +#include "delay.h" +#include "mustang.h" + +int +DelayCC::continuous_control( int parm5, int parm6, int parm7, int value ) { + Mustang::Cmd cmd; + cmd.state_index = DELAY_STATE; + cmd.parm2 = 0x05; + cmd.parm5 = parm5; + cmd.parm6 = parm6; + cmd.parm7 = parm7; + cmd.value = value; + + return amp->continuous_control( cmd ); +} + +int +DelayCC::discrete_control( int parm5, int parm6, int parm7, int value ) { + Mustang::Cmd cmd; + cmd.state_index = DELAY_STATE; + cmd.parm2 = 0x05; + cmd.parm5 = parm5; + cmd.parm6 = parm6; + cmd.parm7 = parm7; + cmd.value = value; + + return amp->discrete_control( cmd ); +} + +int +DelayCC::dispatch( int cc, int value ) { + + switch ( cc ) { + case 49: + // Level + return cc49( value ); + break; + case 50: + // Delay Time + return cc50( value ); + break; + case 51: + // Feedback / FFdbk + return cc51( value ); + break; + case 52: + // Brightness / Frequency / Release / RFdbk / Flutter + return cc52( value ); + break; + case 53: + // Attenuation / Resonance / Mode / Stereo / Threshold + // Tone / Brightness / Separation + return cc53( value ); + break; + case 54: + // Input Level / Stereo / Brightness + return cc54( value ); + break; + default: + return 0; + break; + } +} + +int +DelayCC::cc49( int value ) { + return continuous_control( 0x00, 0x00, 0x01, value ); +} + +int +DelayCC::cc50( int value ) { + return continuous_control( 0x01, 0x01, 0x06, value ); +} + + + +int +MonoDelayCC::cc51( int value ) { + return continuous_control( 0x02, 0x02, 0x01, value ); +} + +int +MonoDelayCC::cc52( int value ) { + return continuous_control( 0x03, 0x03, 0x01, value ); +} + +int +MonoDelayCC::cc53( int value ) { + return continuous_control( 0x04, 0x04, 0x01, value ); +} + + + +int +EchoFilterCC::cc51( int value ) { + return continuous_control( 0x02, 0x02, 0x01, value ); +} + +int +EchoFilterCC::cc52( int value ) { + return continuous_control( 0x03, 0x03, 0x01, value ); +} + +int +EchoFilterCC::cc53( int value ) { + return continuous_control( 0x04, 0x04, 0x01, value ); +} + +int +EchoFilterCC::cc54( int value ) { + return continuous_control( 0x05, 0x05, 0x01, value ); +} + + + +int +MultitapDelayCC::cc51( int value ) { + return continuous_control( 0x02, 0x02, 0x01, value ); +} + +int +MultitapDelayCC::cc52( int value ) { + return continuous_control( 0x03, 0x03, 0x01, value ); +} + +int +MultitapDelayCC::cc53( int value ) { + if ( value > 3 ) return 0; + return discrete_control( 0x04, 0x04, 0x8b, value ); +} + + + +int +PingPongDelayCC::cc51( int value ) { + return continuous_control( 0x02, 0x02, 0x01, value ); +} + +int +PingPongDelayCC::cc52( int value ) { + return continuous_control( 0x03, 0x03, 0x01, value ); +} + +int +PingPongDelayCC::cc53( int value ) { + return continuous_control( 0x04, 0x04, 0x01, value ); +} + + + +int +DuckingDelayCC::cc51( int value ) { + return continuous_control( 0x02, 0x02, 0x01, value ); +} + +int +DuckingDelayCC::cc52( int value ) { + return continuous_control( 0x03, 0x03, 0x01, value ); +} + +int +DuckingDelayCC::cc53( int value ) { + return continuous_control( 0x04, 0x04, 0x01, value ); +} + + + +int +ReverseDelayCC::cc51( int value ) { + return continuous_control( 0x02, 0x02, 0x01, value ); +} + +int +ReverseDelayCC::cc52( int value ) { + return continuous_control( 0x03, 0x03, 0x01, value ); +} + +int +ReverseDelayCC::cc53( int value ) { + return continuous_control( 0x04, 0x04, 0x01, value ); +} + + + +int +TapeDelayCC::cc51( int value ) { + return continuous_control( 0x02, 0x02, 0x01, value ); +} + +int +TapeDelayCC::cc52( int value ) { + return continuous_control( 0x03, 0x03, 0x01, value ); +} + +int +TapeDelayCC::cc53( int value ) { + return continuous_control( 0x04, 0x04, 0x01, value ); +} + +int +TapeDelayCC::cc54( int value ) { + return continuous_control( 0x05, 0x05, 0x01, value ); +} + + + +int +StereoTapeDelayCC::cc51( int value ) { + return continuous_control( 0x02, 0x02, 0x01, value ); +} + +int +StereoTapeDelayCC::cc52( int value ) { + return continuous_control( 0x03, 0x03, 0x01, value ); +} + +int +StereoTapeDelayCC::cc53( int value ) { + return continuous_control( 0x04, 0x05, 0x01, value ); +} + +int +StereoTapeDelayCC::cc54( int value ) { + return continuous_control( 0x05, 0x04, 0x01, value ); +} + + + diff --git a/delay.h b/delay.h new file mode 100644 index 0000000..ae89d51 --- /dev/null +++ b/delay.h @@ -0,0 +1,144 @@ +// -*-c++-*- + +#ifndef _DELAY_H +#define _DELAY_H + +class Mustang; + +class DelayCC { + +protected: + Mustang * amp; + + int continuous_control( int parm5, int parm6, int parm7, int value ); + int discrete_control( int parm5, int parm6, int parm7, int value ); + +public: + DelayCC( Mustang * theAmp ) : amp(theAmp) {} + + int dispatch( int cc, int value ); + +private: + // Level + virtual int cc49( int value ); + // Delay Time + virtual int cc50( int value ); + + virtual int cc51( int value ) { return 0;} + virtual int cc52( int value ) { return 0;} + virtual int cc53( int value ) { return 0;} + virtual int cc54( int value ) { return 0;} +}; + + +class MonoDelayCC : public DelayCC { +public: + MonoDelayCC( Mustang * theAmp ) : DelayCC(theAmp) {} +private: + // Feedback + virtual int cc51( int value ); + // Brightness + virtual int cc52( int value ); + // Attenuation + virtual int cc53( int value ); +}; + + +class EchoFilterCC : public DelayCC { +public: + EchoFilterCC( Mustang * theAmp ) : DelayCC(theAmp) {} +private: + // Feedback + virtual int cc51( int value ); + // Frequency + virtual int cc52( int value ); + // Resonance + virtual int cc53( int value ); + // Input Level + virtual int cc54( int value ); +}; + + +class MultitapDelayCC : public DelayCC { +public: + MultitapDelayCC( Mustang * theAmp ) : DelayCC(theAmp) {} +private: + // Feedback + virtual int cc51( int value ); + // Brightness + virtual int cc52( int value ); + // Mode + virtual int cc53( int value ); +}; + + +class PingPongDelayCC : public DelayCC { +public: + PingPongDelayCC( Mustang * theAmp ) : DelayCC(theAmp) {} +private: + // Feedback + virtual int cc51( int value ); + // Brightness + virtual int cc52( int value ); + // Stereo + virtual int cc53( int value ); +}; + + +class DuckingDelayCC : public DelayCC { +public: + DuckingDelayCC( Mustang * theAmp ) : DelayCC(theAmp) {} +private: + // Feedback + virtual int cc51( int value ); + // Release + virtual int cc52( int value ); + // Threshold + virtual int cc53( int value ); +}; + + +class ReverseDelayCC : public DelayCC { +public: + ReverseDelayCC( Mustang * theAmp ) : DelayCC(theAmp) {} +private: + // FFdbk + virtual int cc51( int value ); + // RFdbk + virtual int cc52( int value ); + // Tone + virtual int cc53( int value ); +}; + + +class TapeDelayCC : public DelayCC { +public: + TapeDelayCC( Mustang * theAmp ) : DelayCC(theAmp) {} +private: + // Feedback + virtual int cc51( int value ); + // Flutter + virtual int cc52( int value ); + // Brightness + virtual int cc53( int value ); + // Stereo + virtual int cc54( int value ); +}; + + +class StereoTapeDelayCC : public DelayCC { +public: + StereoTapeDelayCC( Mustang * theAmp ) : DelayCC(theAmp) {} +private: + // Feedback + virtual int cc51( int value ); + // Flutter + virtual int cc52( int value ); + // Separation + virtual int cc53( int value ); + // Brightness + virtual int cc54( int value ); +}; + + +#endif diff --git a/delay_defaults.h b/delay_defaults.h new file mode 100644 index 0000000..9351a67 --- /dev/null +++ b/delay_defaults.h @@ -0,0 +1,67 @@ +// Default settings for Mustang III (original) delay models +// +// This header is auto-generated from decoded pcap capture. + +static unsigned char mono_delay[] = { + 0x1c, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char mono_echo_filter[] = { + 0x1c, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x00, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char stereo_echo_filter[] = { + 0x1c, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xb3, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char multitap_delay[] = { + 0x1c, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x80, 0x66, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char ping_pong_delay[] = { + 0x1c, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x00, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char ducking_delay[] = { + 0x1c, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char reverse_delay[] = { + 0x1c, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x00, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char tape_delay[] = { + 0x1c, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2b, 0x00, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x1c, 0x00, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char stereo_tape_delay[] = { + 0x1c, 0x03, 0x08, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2a, 0x00, 0x06, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x88, 0x1c, 0x63, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + diff --git a/mustang.cpp b/mustang.cpp index 1222362..6076843 100644 --- a/mustang.cpp +++ b/mustang.cpp @@ -3,8 +3,14 @@ #include #include "magic.h" + +#include "amp.h" +#include "reverb.h" +#include "delay.h" + #include "amp_defaults.h" #include "reverb_defaults.h" +#include "delay_defaults.h" Mustang::Mustang() { @@ -130,9 +136,10 @@ int Mustang::start_amp(void) // Current preset name, amp settings, efx settings for(j = 0; j < 7; i++, j++) memcpy(curr_state[j], received_data[i], LENGTH); - updateAmp(); - curr_reverb = new ReverbCC( this ); - + updateAmpObj(); + updateReverbObj(); + updateDelayObj(); + return 0; } @@ -166,9 +173,9 @@ int Mustang::stop_amp() return 0; } -void Mustang::updateAmp(void) { +void Mustang::updateAmpObj(void) { - int curr = curr_state[AMP_STATE][AMPLIFIER]; + int curr = curr_state[AMP_STATE][MODEL]; switch (curr) { case F57_DELUXE_ID: case F57_CHAMP_ID: @@ -214,8 +221,62 @@ void Mustang::updateAmp(void) { } -void Mustang::updateReverb(void) { - // No-op for now +void Mustang::updateReverbObj(void) { + delete curr_reverb; + curr_reverb = new ReverbCC( this ); +} + + +void Mustang::updateDelayObj(void) { + + int curr = curr_state[DELAY_STATE][MODEL]; + + switch (curr) { + case MONO_DLY_ID: + delete curr_delay; + curr_delay = new MonoDelayCC(this); + break; + + case MONO_FILTER_ID: + case ST_FILTER_ID: + delete curr_delay; + curr_delay = new EchoFilterCC(this); + break; + + case MTAP_DLY_ID: + delete curr_delay; + curr_delay = new MultitapDelayCC(this); + break; + + case PONG_DLY_ID: + delete curr_delay; + curr_delay = new PingPongDelayCC(this); + break; + + case DUCK_DLY_ID: + delete curr_delay; + curr_delay = new DuckingDelayCC(this); + break; + + case REVERSE_DLY_ID: + delete curr_delay; + curr_delay = new ReverseDelayCC(this); + break; + + case TAPE_DLY_ID: + delete curr_delay; + curr_delay = new TapeDelayCC(this); + break; + + case ST_TAPE_DLY_ID: + delete curr_delay; + curr_delay = new StereoTapeDelayCC(this); + break; + + default: + fprintf( stderr, "W - Delay id %x not supported yet\n", curr ); + break; + } } @@ -252,69 +313,6 @@ int Mustang::effect_toggle(int cc, int value) return ret; } -int Mustang::control_common1(int parm, int bucket, int value) -{ - static unsigned short previous = 0; - - int ret, received; - unsigned char array[LENGTH]; - - memset(array, 0x00, LENGTH); - array[0] = 0x05; - array[1] = 0xc3; - array[2] = 0x02; - array[3] = curr_state[AMP_STATE][AMPLIFIER]; - // target parameter - array[5] = array[6] = parm; - // bucket - array[7] = bucket; - - // Scale and clamp to valid index range - int index = (int) ceil( (double)value * magic_scale_factor ); - if ( index > magic_max ) index = magic_max; - - unsigned short eff_value = magic_values[index]; - - // Try to prevent extra traffic if successive calls resolve to the same - // slot. - if (eff_value == previous) return 0; - previous = eff_value; - - array[9] = eff_value & 0xff; - array[10] = (eff_value >> 8) & 0xff; - - // Send command and flush reply - ret = libusb_interrupt_transfer(amp_hand, 0x01, array, LENGTH, &received, TMOUT); - libusb_interrupt_transfer(amp_hand, 0x81, array, LENGTH, &received, TMOUT); - - return ret; -} - - -int Mustang::control_common2(int parm, int bucket, int value) -{ - int ret, received; - unsigned char array[LENGTH]; - - memset(array, 0x00, LENGTH); - array[0] = 0x05; - array[1] = 0xc3; - array[2] = 0x02; - array[3] = curr_state[AMP_STATE][AMPLIFIER]; - // target parameter - array[5] = array[6] = parm; - // bucket - array[7] = bucket; - // value - array[9] = value; - - // Send command and flush reply - ret = libusb_interrupt_transfer(amp_hand, 0x01, array, LENGTH, &received, TMOUT); - libusb_interrupt_transfer(amp_hand, 0x81, array, LENGTH, &received, TMOUT); - - return ret; -} - int Mustang::setAmp( int ord ) { int ret, received; unsigned char scratch[LENGTH]; @@ -372,7 +370,7 @@ int Mustang::setAmp( int ord ) { // Copy to current setting store memcpy(curr_state[AMP_STATE], array, LENGTH); - updateAmp(); + updateAmpObj(); // Setup USB gain memset(scratch, 0x00, LENGTH); @@ -445,7 +443,62 @@ int Mustang::setReverb( int ord ) { // Copy to current setting store memcpy(curr_state[REVERB_STATE], array, LENGTH); - updateReverb(); + updateReverbObj(); + + return ret; +} + +int Mustang::setDelay( int ord ) { + int ret, received; + unsigned char scratch[LENGTH]; + + unsigned char *array; + + switch (ord) { + case 1: + array = mono_delay; + break; + case 2: + array = mono_echo_filter; + break; + case 3: + array = stereo_echo_filter; + break; + case 4: + array = multitap_delay; + break; + case 5: + array = ping_pong_delay; + break; + case 6: + array = ducking_delay; + break; + case 7: + array = reverse_delay; + break; + case 8: + array = tape_delay; + break; + case 9: + array = stereo_tape_delay; + break; + default: + fprintf( stderr, "W - Delay select %d not supported\n", ord ); + return 0; + } + + array[FXSLOT] = curr_state[DELAY_STATE][FXSLOT]; + + // Setup amp personality + ret = libusb_interrupt_transfer(amp_hand, 0x01, array, LENGTH, &received, TMOUT); + libusb_interrupt_transfer(amp_hand, 0x81, scratch, LENGTH, &received, TMOUT); + + ret = libusb_interrupt_transfer(amp_hand, 0x01, execute, LENGTH, &received, TMOUT); + libusb_interrupt_transfer(amp_hand, 0x81, scratch, LENGTH, &received, TMOUT); + + // Copy to current setting store + memcpy(curr_state[DELAY_STATE], array, LENGTH); + updateDelayObj(); return ret; } @@ -477,34 +530,27 @@ int Mustang::save_on_amp(char *name, int slot) return ret; } -int Mustang::efx_common1(int parm, int bucket, int type, int value) -{ - static unsigned short previous = 0; - +int Mustang::continuous_control( const Mustang::Cmd & cmd ) { int ret, received; unsigned char array[LENGTH]; memset(array, 0x00, LENGTH); array[0] = 0x05; array[1] = 0xc3; - array[2] = 0x06; - array[3] = curr_state[type][EFFECT]; - // target parameter - array[5] = array[6] = parm; - // bucket - array[7] = bucket; + array[2] = cmd.parm2; + array[3] = curr_state[cmd.state_index][MODEL]; + // target parameter + array[5] = cmd.parm5; + array[6] = cmd.parm6; + array[7] = cmd.parm7; + // Scale and clamp to valid index range - int index = (int) ceil( (double)value * magic_scale_factor ); + int index = (int) ceil( (double)cmd.value * magic_scale_factor ); if ( index > magic_max ) index = magic_max; unsigned short eff_value = magic_values[index]; - // Try to prevent extra traffic if successive calls resolve to the same - // slot. - if (eff_value == previous) return 0; - previous = eff_value; - array[9] = eff_value & 0xff; array[10] = (eff_value >> 8) & 0xff; @@ -516,6 +562,31 @@ int Mustang::efx_common1(int parm, int bucket, int type, int value) } +int Mustang::discrete_control( const Mustang::Cmd & cmd ) { + int ret, received; + unsigned char array[LENGTH]; + + memset(array, 0x00, LENGTH); + array[0] = 0x05; + array[1] = 0xc3; + array[2] = cmd.parm2; + array[3] = curr_state[cmd.state_index][MODEL]; + + array[5] = cmd.parm5; + array[6] = cmd.parm6; + array[7] = cmd.parm7; + + // Discrete value + array[9] = cmd.value; + + // Send command and flush reply + ret = libusb_interrupt_transfer(amp_hand, 0x01, array, LENGTH, &received, TMOUT); + libusb_interrupt_transfer(amp_hand, 0x81, array, LENGTH, &received, TMOUT); + + return ret; +} + + int Mustang::load_memory_bank( int slot ) { int ret, received; @@ -536,7 +607,7 @@ int Mustang::load_memory_bank( int slot ) if(i < 7) memcpy(curr_state[i], array, LENGTH); } - updateAmp(); + updateAmpObj(); return ret; } diff --git a/mustang.h b/mustang.h index ee4d56d..baf6513 100644 --- a/mustang.h +++ b/mustang.h @@ -9,8 +9,6 @@ #include #include "effects_enum.h" #include "data_structs.h" -#include "amp.h" -#include "reverb.h" // amp's VID and PID #define USB_VID 0x1ed8 @@ -74,6 +72,9 @@ #define FAMILY 2 #define ACTIVE_INVERT 3 +// Offset to current device model for any state structure +#define MODEL 16 + // Index into current state structure #define PRESET_NAME 0 #define AMP_STATE 1 @@ -96,7 +97,7 @@ #define DELAY_FAM 5 #define REVERB_FAM 6 -// Amp id values +// Amp model id values #define F57_DELUXE_ID 0x67 #define F59_BASSMAN_ID 0x64 #define F57_CHAMP_ID 0x7c @@ -116,7 +117,7 @@ #define BRIT_WATT_ID 0xff #define BRIT_COLOR_ID 0xfc -// Reverb id values +// Reverb model id values #define SM_HALL_ID 0x24 #define LG_HALL_ID 0x3a #define SM_ROOM_ID 0x26 @@ -128,20 +129,39 @@ #define SPRING_63_ID 0x21 #define SPRING_65_ID 0x0b -class Mustang { - friend class AmpCC; - friend class ReverbCC; +// Delay model id values +#define MONO_DLY_ID 0x16 +#define MONO_FILTER_ID 0x43 +#define ST_FILTER_ID 0x48 +#define MTAP_DLY_ID 0x44 +#define PONG_DLY_ID 0x45 +#define DUCK_DLY_ID 0x15 +#define REVERSE_DLY_ID 0x46 +#define TAPE_DLY_ID 0x2b +#define ST_TAPE_DLY_ID 0x2a + +class AmpCC; +class ReverbCC; +class DelayCC; + + +class Mustang { + friend class AmpCC; + friend class ReverbCC; + friend class DelayCC; + public: Mustang(); ~Mustang(); int start_amp(void); // initialize communication int stop_amp(void); // terminate communication int set_effect(struct fx_pedal_settings); + int setAmp( int ord ); - int setReverb( int ord ); - + int setDelay( int ord ); + int save_on_amp(char *, int); int load_memory_bank(int); int save_effects(int , char *, int , struct fx_pedal_settings *); @@ -150,8 +170,18 @@ public: AmpCC * getAmp( void ) { return curr_amp;} ReverbCC * getReverb( void ) { return curr_reverb;} + DelayCC * getDelay( void ) { return curr_delay;} - private: + struct Cmd { + int state_index; + int parm2; + int parm5; + int parm6; + int parm7; + int value; + }; + +private: libusb_device_handle *amp_hand; // handle for USB communication unsigned char execute[LENGTH]; // "apply" command sent after each instruction @@ -180,14 +210,14 @@ public: AmpCC * curr_amp; ReverbCC * curr_reverb; + DelayCC * curr_delay; - int control_common1(int parm, int bucket, int value); - int control_common2(int parm, int bucket, int value); + int continuous_control( const Mustang::Cmd & cmd ); + int discrete_control( const Mustang::Cmd & cmd ); - int efx_common1(int parm, int bucket, int type, int value); - - void updateAmp(void); - void updateReverb(void); + void updateAmpObj(void); + void updateReverbObj(void); + void updateDelayObj(void); }; #endif // MUSTANG_H diff --git a/mustang_midi.cpp b/mustang_midi.cpp index 01295cd..f343ccc 100644 --- a/mustang_midi.cpp +++ b/mustang_midi.cpp @@ -5,6 +5,11 @@ #include "mustang.h" +#include "amp.h" +#include "reverb.h" +#include "delay.h" + + static Mustang mustang; static int channel; @@ -47,14 +52,23 @@ void message_action( double deltatime, std::vector< unsigned char > *message, vo if ( cc >= 23 && cc <= 26 ) { rc = mustang.effect_toggle( cc, value ); } + // Set delay model + else if ( cc == 48 ) { + rc = mustang.setDelay( value ); + } + // Delay CC handler + else if ( cc >= 49 && cc <= 54 ) { + DelayCC *delayObj = mustang.getDelay(); + rc = delayObj->dispatch( cc, value ); + } // Set reverb model else if ( cc == 58 ) { rc = mustang.setReverb( value ); } // Reverb CC handler else if ( cc >= 59 && cc <= 63 ) { - ReverbCC *reverbModel = mustang.getReverb(); - rc = reverbModel->dispatch( cc, value ); + ReverbCC *reverbObj = mustang.getReverb(); + rc = reverbObj->dispatch( cc, value ); } // Set amp model else if ( cc == 68 ) { @@ -62,8 +76,8 @@ void message_action( double deltatime, std::vector< unsigned char > *message, vo } // Amp CC Handler else if ( cc >= 69 && cc <= 79 ) { - AmpCC *ampModel = mustang.getAmp(); - rc = ampModel->dispatch( cc, value ); + AmpCC *ampObj = mustang.getAmp(); + rc = ampObj->dispatch( cc, value ); } if ( rc ) { fprintf( stderr, "Error: CC#%d failed. RC = %d\n", cc, rc ); diff --git a/reverb.cpp b/reverb.cpp index d97fc07..516fdc8 100644 --- a/reverb.cpp +++ b/reverb.cpp @@ -3,8 +3,16 @@ #include "mustang.h" int -ReverbCC::efx_common1(int parm, int bucket, int type, int value) { - return amp->efx_common1( parm, bucket, type, value ); +ReverbCC::continuous_control( int parm5, int parm6, int parm7, int value ) { + Mustang::Cmd cmd; + cmd.state_index = REVERB_STATE; + cmd.parm2 = 0x06; + cmd.parm5 = parm5; + cmd.parm6 = parm6; + cmd.parm7 = parm7; + cmd.value = value; + + return amp->continuous_control( cmd ); } int @@ -39,26 +47,26 @@ ReverbCC::dispatch( int cc, int value ) { int ReverbCC::cc59( int value ) { - return efx_common1( 0x00, 0x0b, REVERB_STATE, value ); + return continuous_control( 0x00, 0x00, 0x0b, value ); } int ReverbCC::cc60( int value ) { - return efx_common1( 0x01, 0x0b, REVERB_STATE, value ); + return continuous_control( 0x01, 0x01, 0x0b, value ); } int ReverbCC::cc61( int value ) { - return efx_common1( 0x02, 0x0b, REVERB_STATE, value ); + return continuous_control( 0x02, 0x02, 0x0b, value ); } int ReverbCC::cc62( int value ) { - return efx_common1( 0x03, 0x0b, REVERB_STATE, value ); + return continuous_control( 0x03, 0x03, 0x0b, value ); } int ReverbCC::cc63( int value ) { - return efx_common1( 0x04, 0x0b, REVERB_STATE, value ); + return continuous_control( 0x04, 0x04, 0x0b, value ); } diff --git a/reverb.h b/reverb.h index 86a19fd..7404606 100644 --- a/reverb.h +++ b/reverb.h @@ -10,7 +10,7 @@ class ReverbCC { protected: Mustang * amp; - int efx_common1(int parm, int bucket, int type, int value); + int continuous_control( int parm5, int parm6, int parm7, int value ); public: ReverbCC( Mustang * theAmp ) : amp(theAmp) {}