diff --git a/50-mustang.rules b/50-mustang.rules new file mode 100644 index 0000000..18b3576 --- /dev/null +++ b/50-mustang.rules @@ -0,0 +1,11 @@ +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0004", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0005", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0006", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0007", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0010", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0011", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0012", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0013", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0014", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0015", GROUP="plugdev" +SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ATTRS{idVendor}=="1ed8", ATTRS{idProduct}=="0016", GROUP="plugdev" diff --git a/mustang.cpp b/mustang.cpp index b04c310..7b159be 100644 --- a/mustang.cpp +++ b/mustang.cpp @@ -4,6 +4,7 @@ #include "magic.h" #include "amp_defaults.h" +#include "reverb_defaults.h" Mustang::Mustang() { @@ -130,6 +131,7 @@ int Mustang::start_amp(void) for(j = 0; j < 7; i++, j++) memcpy(curr_state[j], received_data[i], LENGTH); updateAmp(); + curr_reverb = new ReverbCC( this ); return 0; } @@ -212,6 +214,11 @@ void Mustang::updateAmp(void) { } +void Mustang::updateReverb(void) { + // No-op for now +} + + int Mustang::effect_toggle(int state_index, int state) { int ret, received; @@ -379,6 +386,64 @@ int Mustang::setAmp( int ord ) { return ret; } +int Mustang::setReverb( int ord ) { + int ret, received; + unsigned char scratch[LENGTH]; + + unsigned char *array; + + switch (ord) { + case 1: + array = small_hall; + break; + case 2: + array = large_hall; + break; + case 3: + array = small_room; + break; + case 4: + array = large_room; + break; + case 5: + array = small_plate; + break; + case 6: + array = large_plate; + break; + case 7: + array = ambient; + break; + case 8: + array = arena; + break; + case 9: + array = spring_63; + break; + case 10: + array = spring_65; + break; + default: + fprintf( stderr, "W - Reverb select %d not supported\n", ord ); + return 0; + } + + array[FXSLOT] = curr_state[REVERB_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[REVERB_STATE], array, LENGTH); + updateReverb(); + + return ret; +} + int Mustang::save_on_amp(char *name, int slot) { int ret, received; @@ -406,6 +471,45 @@ 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 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; + + // 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::load_memory_bank( int slot ) { int ret, received; diff --git a/mustang.h b/mustang.h index 9362c30..91d8ab8 100644 --- a/mustang.h +++ b/mustang.h @@ -7,11 +7,10 @@ #include #include #include -//#include #include "effects_enum.h" #include "data_structs.h" -//#include #include "amp.h" +#include "reverb.h" // amp's VID and PID #define USB_VID 0x1ed8 @@ -117,6 +116,18 @@ #define BRIT_WATT_ID 0xff #define BRIT_COLOR_ID 0xfc +// Reverb id values +#define SM_HALL_ID 0x24 +#define LG_HALL_ID 0x3a +#define SM_ROOM_ID 0x26 +#define LG_ROOM_ID 0x3b +#define SM_PLATE_ID 0x4e +#define LG_PLATE_ID 0x4b +#define AMBIENT_ID 0x4c +#define ARENA_ID 0x4d +#define SPRING_63_ID 0x21 +#define SPRING_65_ID 0x0b + class Mustang { public: @@ -126,6 +137,9 @@ public: int stop_amp(void); // terminate communication int set_effect(struct fx_pedal_settings); int setAmp( int ord ); + + int setReverb( int ord ); + int save_on_amp(char *, int); int load_memory_bank(int); int save_effects(int , char *, int , struct fx_pedal_settings *); @@ -136,8 +150,11 @@ public: int control_common1(int parm, int bucket, int value); int control_common2(int parm, int bucket, int value); - AmpCC * getAmp( void ) { return curr_amp;} + int efx_common1(int parm, int bucket, int type, int value); + AmpCC * getAmp( void ) { return curr_amp;} + ReverbCC * getReverb( void ) { return curr_reverb;} + private: libusb_device_handle *amp_hand; // handle for USB communication unsigned char execute[LENGTH]; // "apply" command sent after each instruction @@ -166,8 +183,11 @@ public: unsigned char curr_state[7][LENGTH]; AmpCC * curr_amp; - + ReverbCC * curr_reverb; + void updateAmp(void); + void updateReverb(void); + }; #endif // MUSTANG_H diff --git a/mustang_midi.cpp b/mustang_midi.cpp index a6d8af6..18352ed 100644 --- a/mustang_midi.cpp +++ b/mustang_midi.cpp @@ -39,11 +39,12 @@ void message_action( double deltatime, std::vector< unsigned char > *message, vo case 0xb0: { // Control change - int rc; + int rc = 0; int cc = (*message)[1]; int value = (*message)[2]; AmpCC *ampModel = mustang.getAmp(); - + ReverbCC *reverbModel = mustang.getReverb(); + // Effects on/off if ( cc >= 23 && cc <= 26 ) { // Translate 23..26 --> 2..5 (current state index) @@ -53,6 +54,30 @@ void message_action( double deltatime, std::vector< unsigned char > *message, vo else if ( value > 63 && value <= 127 ) state = 1; rc = mustang.effect_toggle( index, state ); } + // Set reverb model + else if ( cc == 58 ) { + rc = mustang.setReverb( value ); + } + // Level + else if ( cc == 59 ) { + rc = reverbModel->cc59( value ); + } + // Decay + else if ( cc == 60 ) { + rc = reverbModel->cc60( value ); + } + // Dwell + else if ( cc == 61 ) { + rc = reverbModel->cc61( value ); + } + // Diffusion + else if ( cc == 62 ) { + rc = reverbModel->cc62( value ); + } + // Tone + else if ( cc == 63 ) { + rc = reverbModel->cc63( value ); + } // Set amp model else if ( cc == 68 ) { // fprintf( stderr, "DEBUG: %d\n", value ); diff --git a/reverb.cpp b/reverb.cpp new file mode 100644 index 0000000..1083e59 --- /dev/null +++ b/reverb.cpp @@ -0,0 +1,29 @@ + +#include "reverb.h" +#include "mustang.h" + +int +ReverbCC::cc59( int value ) { + return amp->efx_common1( 0x00, 0x0b, REVERB_STATE, value ); +} + +int +ReverbCC::cc60( int value ) { + return amp->efx_common1( 0x01, 0x0b, REVERB_STATE, value ); +} + +int +ReverbCC::cc61( int value ) { + return amp->efx_common1( 0x02, 0x0b, REVERB_STATE, value ); +} + +int +ReverbCC::cc62( int value ) { + return amp->efx_common1( 0x03, 0x0b, REVERB_STATE, value ); +} + +int +ReverbCC::cc63( int value ) { + return amp->efx_common1( 0x04, 0x0b, REVERB_STATE, value ); +} + diff --git a/reverb.h b/reverb.h new file mode 100644 index 0000000..401d16e --- /dev/null +++ b/reverb.h @@ -0,0 +1,30 @@ +// -*-c++-*- + +#ifndef _REVERB_H +#define _REVERB_H + +class Mustang; + +class ReverbCC { + +protected: + + Mustang * amp; + +public: + + ReverbCC( Mustang * theAmp ) : amp(theAmp) {} + + // Level + int cc59( int value ); + // Decay + int cc60( int value ); + // Dwell + int cc61( int value ); + // Diffusion + int cc62( int value ); + // Tone + int cc63( int value ); +}; + +#endif diff --git a/reverb_defaults.h b/reverb_defaults.h new file mode 100644 index 0000000..8bff376 --- /dev/null +++ b/reverb_defaults.h @@ -0,0 +1,74 @@ +// Default settings for Mustang III (original) reverb models +// +// This header is auto-generated from decoded pcap capture. + +static unsigned char small_hall[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x24, 0x00, 0x05, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6e, 0x5d, 0x6e, 0x80, 0x91, 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 large_hall[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x05, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4f, 0x3e, 0x80, 0x05, 0xb0, 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 small_room[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x26, 0x00, 0x05, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0x00 +}; + +static unsigned char large_room[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x05, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0x00 +}; + +static unsigned char small_plate[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4e, 0x00, 0x05, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0x00 +}; + +static unsigned char large_plate[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4b, 0x00, 0x05, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x80, 0x91, 0x80, 0xb6, 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 ambient[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x05, 0x00, 0x08, 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 arena[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4d, 0x00, 0x05, 0x00, 0x08, 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 spring_63[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x05, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 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, 0x00 +}; + +static unsigned char spring_65[] = { + 0x1c, 0x03, 0x09, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x05, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x8b, 0x49, 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, 0x00 +}; +