From df67030396ab284d2dee6dc90d8098d9f08bb750 Mon Sep 17 00:00:00 2001 From: hirsch Date: Sun, 17 Jul 2016 20:44:54 -0400 Subject: [PATCH] Implement v2 models. Add test fixture. Make MIDI port normal to zero --- README.md | 44 ++++++- amp.h | 40 ++++++- amp_defaults.h | 37 ++++++ delay.h | 2 + mod_defaults.h | 23 ++++ mustang.cpp | 174 +++++++++++++++++++++------- mustang.h | 35 +++++- mustang_bridge_start | 2 +- mustang_midi.cpp | 15 ++- stomp_defaults.h | 39 +++++++ test.py | 267 +++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 623 insertions(+), 55 deletions(-) create mode 100755 test.py diff --git a/README.md b/README.md index 34b3dbf..0e7e20f 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,30 @@ Allow Fender Mustang series guitar amplifiers to be controlled by MIDI messages +# What's New + +The command line parameter for MIDI controller port is now assumed to +start at 0 rather than 1 in order to match the way Linux ALSA +enumerates devices (see 'Run' below). + +The program has been updated to run as a non-privileged daemon +process. You can still invoke it on the command line, but there will +be no output to the console and it no longer responds to keypress +input. Enter Ctrl-C (SIGINT) to exit. + +I have added the first version of a runtime framework that starts and +stops the program automatically based on attached MIDI devices. This +seems to be working on a Beaglebone SBC, but has not been extensively +tested or documented yet. There is a small amount of customization +required to account for your specific amp model and MIDI controller +interface. Technically oriented users can probably work this out, +otherwise wait a bit until I can refine the packaging and arrange to +have the various pieces configured from a common setup file. + +I'm currently working on implementation of amp and effects models +added in the Mustang v2 products and hope to have that checked in +soon. + # Introduction The intent is to implement the published MIDI spec for the Fender @@ -106,12 +130,28 @@ Both the amplifier and MIDI source should be connected first, then: ``` $ mustang_midi midi_port# midi_listen_channel# ``` -NOTE: RPi and BBG are a bit fussy about enumeration of new USB +NOTE1: I'm not sure about other platforms, but on Linux the MIDI +port number is equivalent to the ALSA card index. I had originally +treated port as 1..n, but since ALSA (and JACK? Not sure..) starts at +0, this has now been changed. You can find the card index for your +controller by connecting it to the computer and examining the +pseudo-file, e.g.: + +$ cat /proc/asound/cards + 0 [PCH ]: HDA-Intel - HDA Intel PCH + HDA Intel PCH at 0xf7530000 irq 30 + 1 [Interface ]: USB-Audio - USB MS1x1 MIDI Interface + M-Audio USB MS1x1 MIDI Interface at usb-0000:00:14.0-1, full speed + +To accept MIDI messages from devices behind the M-Audio interface you +would now specify '1' as the MIDI port value. + +NOTE2: RPi and BBG are a bit fussy about enumeration of new USB devices. If you are not getting proper communication, quit the program and try replugging both the Fender amp and MIDI controller **after** those devices are powered up. -NOTE2: I've had success using a passive USB hub with the single USB on +NOTE3: I've had success using a passive USB hub with the single USB on the BBG, but YMMV since most USB<->5Pin MIDI converters draw some degree of bus power. A powered hub might be necessary in some situations. diff --git a/amp.h b/amp.h index cc61406..770c33e 100644 --- a/amp.h +++ b/amp.h @@ -10,6 +10,7 @@ class Mustang; // F65 Deluxe // F65 Princeton // F65 Twin +// 60s Thrift // class AmpCC { @@ -51,13 +52,21 @@ private: } // Cabinet virtual int cc77( int value ) { - if ( value < 1 || value > 12 ) return 0; - else return discrete_control( 0x11, 0x11, 0x8e, value ); + if ( value > 12 ) return 0; + else return discrete_control( 0x11, 0x11, 0x8e, value ); } // Dummy in base class virtual int cc78( int value ) { return 0;} virtual int cc79( int value ) { return 0;} + + // Noise Gate Custom Threshold + virtual int cc90( int value ) { + if ( value > 9 ) return 0; + else return discrete_control( 0x10, 0x10, 0x86, value ); + } + // Noise Gate Custom Depth + virtual int cc91( int value ) { return continuous_control( 0x09, 0x09, 0x0c, value );} }; @@ -104,6 +113,7 @@ private: // British 80s // American 90s // Metal 2000 +// British Watt // class AmpCC4 : public AmpCC { public: @@ -122,11 +132,37 @@ class AmpCC5 : public AmpCC { public: AmpCC5( Mustang * theAmp ) : AmpCC(theAmp) {} private: + // No sag / bias + virtual int cc74( int value ) { return 0;} + virtual int cc75( int value ) { return 0;} + // No pres / master virtual int cc78( int value ) { return 0;} virtual int cc79( int value ) { return 0;} }; +// British Color +// +class AmpCC6 : public AmpCC { +public: + AmpCC6( Mustang * theAmp ) : AmpCC(theAmp) {} +private: + // Master Volume + virtual int cc79( int value ) { return continuous_control( 0x03, 0x03, 0x0c, value );} +}; + + +// F57 Twin +// +class AmpCC7 : public AmpCC { +public: + AmpCC7( Mustang * theAmp ) : AmpCC(theAmp) {} +private: + // Presence + virtual int cc78( int value ) { return continuous_control( 0x07, 0x07, 0x0c, value );} +}; + + // Null Amp // class NullAmpCC : public AmpCC { diff --git a/amp_defaults.h b/amp_defaults.h index 1e6a53f..2afad2e 100644 --- a/amp_defaults.h +++ b/amp_defaults.h @@ -93,3 +93,40 @@ static unsigned char metal_2k[] = { 0x00, 0x08, 0x08, 0x01, 0x00, 0x01, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +// V2 Only: + +static unsigned char studio_preamp[] = { + 0x1c, 0x03, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x0d, 0x0d, 0x0d, 0x00, + 0x00, 0x00, 0x0d, 0x01, 0x00, 0x01, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char sixties_thrift[] = { + 0x1c, 0x03, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xac, 0x81, 0x81, 0x81, 0xac, 0x81, 0x9d, 0x81, 0x81, 0x81, 0x81, 0x81, 0x0f, 0x0f, 0x0f, 0x00, + 0x00, 0x01, 0x0f, 0x01, 0x00, 0x01, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char brit_watts[] = { + 0x1c, 0x03, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0x9d, 0x81, 0xff, 0xba, 0x98, 0xa6, 0x87, 0x81, 0x81, 0x81, 0x81, 0x11, 0x11, 0x11, 0x00, + 0x00, 0x0a, 0x11, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char brit_color[] = { + 0x1c, 0x03, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xac, 0x8c, 0x81, 0x81, 0x9d, 0x64, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x10, 0x10, 0x10, 0x01, + 0x00, 0x08, 0x10, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static unsigned char f57_twin[] = { + 0x1c, 0x03, 0x05, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xac, 0x4e, 0x81, 0x81, 0xac, 0x7b, 0x9d, 0xbd, 0x81, 0x81, 0x81, 0x81, 0x0e, 0x0e, 0x0e, 0x00, + 0x00, 0x09, 0x0e, 0x01, 0x00, 0x01, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + diff --git a/delay.h b/delay.h index 9e8f6fc..dbf6f0f 100644 --- a/delay.h +++ b/delay.h @@ -65,6 +65,8 @@ class MultitapDelayCC : public DelayCC { public: MultitapDelayCC( Mustang * theAmp ) : DelayCC(theAmp) {} private: + // Delay Time + virtual int cc50( int value ) { return continuous_control( 0x01, 0x01, 0x08, value );} // Feedback virtual int cc51( int value ) { return continuous_control( 0x02, 0x02, 0x01, value );} // Brightness diff --git a/mod_defaults.h b/mod_defaults.h index 5bbf888..ada0acf 100644 --- a/mod_defaults.h +++ b/mod_defaults.h @@ -82,3 +82,26 @@ static unsigned char pitch_shifter[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +// V2 Only: + +static unsigned char mod_wah[] = { + 0x1c, 0x03, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf4, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x81, 0x01, 0xff, 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, 0x00 +}; + +static unsigned char mod_touch_wah[] = { + 0x1c, 0x03, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf5, 0x00, 0x04, 0x01, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xed, 0x81, 0x07, 0xff, 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, 0x00 +}; + +static unsigned char diatonic_pitch_shift[] = { + 0x1c, 0x03, 0x07, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x10, 0x04, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x56, 0x09, 0x04, 0x05, 0xc8, 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 +}; + diff --git a/mustang.cpp b/mustang.cpp index 24b73d9..e5c4e41 100644 --- a/mustang.cpp +++ b/mustang.cpp @@ -16,11 +16,25 @@ #include "mod_defaults.h" #include "stomp_defaults.h" +Mustang::usb_id Mustang::ids[] = { + { OLD_USB_PID, 0x03, false }, + { NEW_USB_PID, 0xc1, false }, + { V2_USB_PID, 0x03, false }, + { MINI_USB_PID, 0x03, false }, + { FLOOR_USB_PID, 0x03, false }, + { BRONCO40_USB_PID, 0x03, false }, + { V2_III_PID, 0xc1, true }, + { V2_IV_PID, 0xc1, true }, + { 0, 0x00, false } +}; + + Mustang::Mustang() { amp_hand = NULL; curr_amp = NULL; tuner_active = false; + isV2 = false; // "apply efect" command memset(execute, 0x00, LENGTH); @@ -46,6 +60,7 @@ Mustang::~Mustang() int Mustang::start_amp(void) { int ret, received; + static int init_value = -1; unsigned char array[LENGTH]; if(amp_hand == NULL) @@ -55,19 +70,20 @@ int Mustang::start_amp(void) if (ret) return ret; - // get handle for the device - if((amp_hand = libusb_open_device_with_vid_pid(NULL, USB_VID, OLD_USB_PID)) == NULL) - if((amp_hand = libusb_open_device_with_vid_pid(NULL, USB_VID, NEW_USB_PID)) == NULL) - if((amp_hand = libusb_open_device_with_vid_pid(NULL, USB_VID, V2_USB_PID)) == NULL) - if((amp_hand = libusb_open_device_with_vid_pid(NULL, USB_VID, MINI_USB_PID)) == NULL) - if((amp_hand = libusb_open_device_with_vid_pid(NULL, USB_VID, FLOOR_USB_PID)) == NULL) - if((amp_hand = libusb_open_device_with_vid_pid(NULL, USB_VID, BRONCO40_USB_PID)) == NULL) - if((amp_hand = libusb_open_device_with_vid_pid(NULL, USB_VID, V2_III_PID)) == NULL) - if((amp_hand = libusb_open_device_with_vid_pid(NULL, USB_VID, V2_IV_PID)) == NULL) - { - libusb_exit(NULL); - return -100; - } + for ( int idx=0; ids[idx].pid != 0; idx++ ) { + if ( (amp_hand = libusb_open_device_with_vid_pid(NULL, USB_VID, ids[idx].pid)) != NULL ) { + init_value = ids[idx].init_value; + isV2 = ids[idx].isV2; + break; + } + } + + if ( init_value < 0 ) { + // No amp found + libusb_exit( NULL ); + fprintf( stderr, "S - No Mustang USB device found\n" ); + return -100; + } // detach kernel driver ret = libusb_kernel_driver_active(amp_hand, 0); @@ -99,13 +115,7 @@ int Mustang::start_amp(void) memset(array, 0x00, LENGTH); array[0] = 0x1a; - - // This seems model specific - // Perhaps for Mustang II? - // array[1] = 0x03; - - // Correct value for Mustang III - array[1] = 0xc1; + array[1] = init_value; libusb_interrupt_transfer(amp_hand, 0x01, array, LENGTH, &received, TMOUT); libusb_interrupt_transfer(amp_hand, 0x81, array, LENGTH, &received, TMOUT); @@ -230,8 +240,11 @@ int Mustang::tunerMode( int value ) void Mustang::updateAmpObj(void) { int curr = curr_state[AMP_STATE][MODEL]; + AmpCC * new_amp = NULL; + switch (curr) { case 0: + // No amp break; case F57_DELUXE_ID: @@ -239,42 +252,51 @@ void Mustang::updateAmpObj(void) { case F65_DELUXE_ID: case F65_PRINCETON_ID: case F65_TWIN_ID: - delete curr_amp; - curr_amp = new AmpCC(this); + case S60S_THRIFT_ID: + new_amp = new AmpCC(this); break; case F59_BASSMAN_ID: case BRIT_70S_ID: - delete curr_amp; - curr_amp = new AmpCC1(this); + new_amp = new AmpCC1(this); break; case F_SUPERSONIC_ID: - delete curr_amp; - curr_amp = new AmpCC2(this); + new_amp = new AmpCC2(this); break; case BRIT_60S_ID: - delete curr_amp; - curr_amp = new AmpCC3(this); + new_amp = new AmpCC3(this); break; case BRIT_80S_ID: case US_90S_ID: case METAL_2K_ID: - delete curr_amp; - curr_amp = new AmpCC4(this); + case BRIT_WATT_ID: + new_amp = new AmpCC4(this); break; case STUDIO_PREAMP_ID: - delete curr_amp; - curr_amp = new AmpCC5(this); + new_amp = new AmpCC5(this); + break; + + case BRIT_COLOR_ID: + new_amp = new AmpCC6(this); + break; + + case F57_TWIN_ID: + new_amp = new AmpCC7(this); break; default: fprintf( stderr, "W - Amp id %x not supported yet\n", curr ); break; } + + if ( (new_amp!=NULL) && (new_amp!=curr_amp) ) { + delete curr_amp; + curr_amp = new_amp; + } } @@ -297,7 +319,7 @@ void Mustang::updateReverbObj(void) { void Mustang::updateDelayObj(void) { int curr = curr_state[DELAY_STATE][MODEL]; - + switch (curr) { case 0: break; @@ -538,8 +560,32 @@ int Mustang::setAmp( int ord ) { array = metal_2k; break; default: - fprintf( stderr, "W - Amp select %d not yet supported\n", ord ); - return 0; + if ( isV2 ) { + switch (ord) { + case 13: + array = studio_preamp; + break; + case 14: + array = f57_twin; + break; + case 15: + array = sixties_thrift; + break; + case 16: + array = brit_watts; + break; + case 17: + array = brit_color; + break; + default: + fprintf( stderr, "W - Amp select %d not supported\n", ord ); + return 0; + } + } + else { + fprintf( stderr, "W - Amp select %d not supported\n", ord ); + return 0; + } } // Setup amp personality @@ -740,8 +786,26 @@ int Mustang::setMod( int ord ) { array = pitch_shifter; break; default: - fprintf( stderr, "W - Mod select %d not supported\n", ord ); - return 0; + if ( isV2 ) { + switch (ord) { + case 12: + array = mod_wah; + break; + case 13: + array = mod_touch_wah; + break; + case 14: + array = diatonic_pitch_shift; + break; + default: + fprintf( stderr, "W - Mod select %d not supported\n", ord ); + return 0; + } + } + else { + fprintf( stderr, "W - Mod select %d not supported\n", ord ); + return 0; + } } array[FXSLOT] = curr_state[MOD_STATE][FXSLOT]; @@ -785,7 +849,13 @@ int Mustang::setStomp( int ord ) { array = fuzz; break; case 5: - array = fuzz_touch_wah; + if ( isV2 ) { + fprintf( stderr, "W - Stomp select %d not supported\n", ord ); + return 0; + } + else { + array = fuzz_touch_wah; + } break; case 6: array = simple_comp; @@ -794,8 +864,32 @@ int Mustang::setStomp( int ord ) { array = compressor; break; default: - fprintf( stderr, "W - Stomp select %d not supported\n", ord ); - return 0; + if ( isV2 ) { + switch (ord) { + case 8: + array = ranger_boost; + break; + case 9: + array = green_box; + break; + case 10: + array = orange_box; + break; + case 11: + array = black_box; + break; + case 12: + array = big_fuzz; + break; + default: + fprintf( stderr, "W - Stomp select %d not supported\n", ord ); + return 0; + } + } + else { + fprintf( stderr, "W - Stomp select %d not supported\n", ord ); + return 0; + } } array[FXSLOT] = curr_state[STOMP_STATE][FXSLOT]; diff --git a/mustang.h b/mustang.h index 0a64182..6cfc4a0 100644 --- a/mustang.h +++ b/mustang.h @@ -33,7 +33,7 @@ // for USB communication #define TMOUT 500 #define LENGTH 64 -//#define NANO_SEC_SLEEP 10000000 + // effect array fields #define DSP 2 @@ -110,8 +110,9 @@ #define BRIT_80S_ID 0x5e #define US_90S_ID 0x5d #define METAL_2K_ID 0x6d -#define STUDIO_PREAMP_ID 0xf1 +// v2 amp only +#define STUDIO_PREAMP_ID 0xf1 #define F57_TWIN_ID 0xf6 #define S60S_THRIFT_ID 0xf9 #define BRIT_WATT_ID 0xff @@ -153,15 +154,30 @@ #define PHASER_ID 0x4f #define PITCH_SHIFT_ID 0x1f +// v2 mod only +#define M_WAH_ID 0xf4 +#define M_TOUCH_WAH_ID 0xf5 +#define DIA_PSHIFT_ID 0x1f + // Stomp model id values #define OVERDRIVE_ID 0x3c #define WAH_ID 0x49 #define TOUCH_WAH_ID 0x4a #define FUZZ_ID 0x1a + +// This is not present in v2: #define FUZZ_TWAH_ID 0x1c + #define SIMPLE_COMP_ID 0x88 #define COMP_ID 0x07 +// v2 stomp only +#define RANGE_BOOST_ID 0x03 +#define GREEN_BOX_ID 0xba +#define ORANGE_BOX_ID 0x10 +#define BLACK_BOX_ID 0x11 +#define BIG_FUZZ_ID 0x0f + class AmpCC; class ReverbCC; @@ -213,9 +229,24 @@ public: }; private: + + struct usb_id { + // product id + int pid; + // magic value for init packet + int init_value; + // v2? + bool isV2; + }; + + // For device probe + static usb_id ids[]; + libusb_device_handle *amp_hand; // handle for USB communication unsigned char execute[LENGTH]; // "apply" command sent after each instruction + bool isV2; + // Current state of effects. Read from amp initially and maintained // as we change it. Major index is DSP# - 6, where: // diff --git a/mustang_bridge_start b/mustang_bridge_start index 17cb2c7..bbd4518 100755 --- a/mustang_bridge_start +++ b/mustang_bridge_start @@ -22,7 +22,7 @@ mustang_vid = 0x1ed8 mustang_pid = 0x0016 # Controller MIDI device -midi_device = 2 +midi_device = 1 # MIDI listen channel midi_channel = 1 diff --git a/mustang_midi.cpp b/mustang_midi.cpp index ee147fd..96bb152 100644 --- a/mustang_midi.cpp +++ b/mustang_midi.cpp @@ -19,9 +19,7 @@ void message_action( double deltatime, std::vector< unsigned char > *message, vo #if 0 unsigned int nBytes = message->size(); if ( nBytes > 0 ) { - for ( unsigned int i=0; iat(0), (int)message->at(1), (int)message->at(2) ); } #endif @@ -124,6 +122,7 @@ void message_action( double deltatime, std::vector< unsigned char > *message, vo default: break; } + } // void errorcallback( RtError::Type type, const std::string & detail, void *userData ) { @@ -131,7 +130,9 @@ void message_action( double deltatime, std::vector< unsigned char > *message, vo // } void usage() { - std::cerr << "Usage: prog usb_port midi_channel\n"; + fprintf( stderr, "Usage: mustang_midi \n" ); + fprintf( stderr, " port = 0..n, channel = 1..16\n" ); + exit( 1 ); } @@ -140,7 +141,7 @@ int main( int argc, const char **argv ) { char *endptr; errno = 0; - int port = (int) strtol( argv[1], &endptr, 10 ) - 1; + int port = (int) strtol( argv[1], &endptr, 10 ); if ( endptr == argv[0] ) usage(); if ( port < 0 ) usage(); @@ -163,10 +164,8 @@ int main( int argc, const char **argv ) { delete input_handler; return 8; } - - // n.b. Midi port rank on the host system is 1..n, but this method - // is normal to 0. input_handler->openPort( port ); + // input_handler->openVirtualPort( "TestPort" ); input_handler->setCallback( &message_action ); // Don't want sysex, timing, active sense diff --git a/stomp_defaults.h b/stomp_defaults.h index 1ce9539..396261b 100644 --- a/stomp_defaults.h +++ b/stomp_defaults.h @@ -33,6 +33,8 @@ static unsigned char fuzz[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +// Fuzz Touch Wah in original only! + static unsigned char fuzz_touch_wah[] = { 0x1c, 0x03, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -54,3 +56,40 @@ static unsigned char compressor[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +// V2 Only: + +static unsigned char ranger_boost[] = { + 0x1c, 0x03, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0xba, 0x01, 0x9b, 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, 0x00 +}; + +static unsigned char green_box[] = { + 0x1c, 0x03, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0xb1, 0x8c, 0xff, 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, 0x00 +}; + +static unsigned char orange_box[] = { + 0x1c, 0x03, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x81, 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, 0x00, 0x00 +}; + +static unsigned char black_box[] = { + 0x1c, 0x03, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x81, 0x56, 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, 0x00, 0x00 +}; + +static unsigned char big_fuzz[] = { + 0x1c, 0x03, 0x06, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x01, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xac, 0xac, 0x73, 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, 0x00, 0x00 +}; + diff --git a/test.py b/test.py new file mode 100755 index 0000000..64db057 --- /dev/null +++ b/test.py @@ -0,0 +1,267 @@ +#!/usr/bin/python + +# Use aconnectgui to wire Midi Through Port-0 out to +# mustang bridge virtual input port name + +# OR + +# hirsch@z87:~$ aconnect -o +# client 14: 'Midi Through' [type=kernel] +# 0 'Midi Through Port-0' +# client 128: 'RtMidi Input Client' [type=user] +# 0 'TestPort ' +# +# Given the above, open RtMidi as: 'RtMidi Input Client 128:0' +# +# outport = mido.open_output('Midi Through 14:0') + +from time import sleep +import sys +import itertools as it +import mido +mido.set_backend('mido.backends.rtmidi') + +pc = mido.Message('program_change') +cc = mido.Message('control_change') + +def analog_send( outport, sleeptime=0.25 ): + for value in [ 0, 32, 64, 96, 127, 96, 64, 32, 0 ]: + cc.value = value + outport.send( cc ) + sleep( sleeptime ) + +def discrete_send( outport, max ): + for value in it.chain( range(0,max+1), range(max,-1,-1) ): + cc.value = value + outport.send( cc ) + sleep( 0.25 ) + +# Screen 1 is the same for all models +def amp_screen1( outport ): + for i in range( 69, 74 ): + cc.control = i + analog_send( outport ) + +# Some variation in screen 2 across models +def amp_screen2( outport, bias_sag, extra ): + if bias_sag: + cc.control = 74 + discrete_send( outport, 2 ) + cc.control = 75 + analog_send( outport ) + + cc.control = 76 + discrete_send( outport, 4 ) + + cc.control = 77 + discrete_send( outport, 12 ) + + if extra: + cc.control = 78 + analog_send( outport ) + cc.control = 79 + analog_send( outport ) + +# Step through all amp models +def amp_select( outport ): + cc.control = 68 + for i in range( 0, 13 ): + cc.value = i + outport.send( cc ) + sleep( 0.5 ) + +def run_amp_test( struct, outport ): + raw_input( "Hit ENTER to run amp model select test...\n" ) + amp_select( outport ) + + for i in range( 0, len(struct) ): + amp_rec = struct[i] + + raw_input( "Hit ENTER to run parm edit check for %s\n" % amp_rec[0] ) + cc.control = 68 + cc.value = amp_rec[1] + outport.send( cc ) + + raw_input( "Enter amp edit mode on Mustang and hit ENTER to proceed...\n" ) + amp_screen1( outport ) + + raw_input( "Step to amp edit screen 2 and hit ENTER...\n" ) + amp_screen2( outport, amp_rec[2], amp_rec[3] ) + +# Step through all reverb models +def reverb_select( outport ): + cc.control = 58 + for i in range( 0, 11 ): + cc.value = i + outport.send( cc ) + sleep( 0.5 ) + +def run_reverb_test( outport ): + raw_input( "Hit ENTER to run reverb model select test...\n" ) + reverb_select( outport ) + + raw_input( "Enter Reverb edit mode and hit ENTER...\n" ) + for i in range ( 59, 64 ): + cc.control = i + analog_send( outport ) + sleep( 0.25 ) + +# Step through all delay models +def delay_select( outport ): + cc.control = 48 + for i in range( 0, 10 ): + cc.value = i + outport.send( cc ) + sleep( 0.5 ) + +def run_delay_test( struct, outport ): + raw_input( "Hit ENTER to run delay model select test...\n" ) + delay_select( outport ) + + for i in range( 0, len(struct) ): + delay_rec = struct[i] + + raw_input( "Hit ENTER to run parm edit check for %s\n" % delay_rec[0] ) + cc.control = 48 + cc.value = delay_rec[1] + outport.send( cc ) + + raw_input( "Enter delay edit mode on Mustang and hit ENTER to proceed...\n" ) + for i in range( 49, 54 ): + cc.control = i + # Multitap Delay has a single discrete control + if i==53 and delay_rec[2]: + discrete_send( outport, 3 ) + else: + analog_send( outport ) + + if delay_rec[3]: + analog_send( outport ) + +# Step through all mod models +def mod_select( outport ): + cc.control = 38 + for i in range( 0, 12 ): + cc.value = i + outport.send( cc ) + sleep( 0.5 ) + +def run_mod_test( struct, outport ): + raw_input( "Hit ENTER to run mod model select test...\n" ) + mod_select( outport ) + + for i in range( 0, len(struct) ): + mod_rec = struct[i] + + raw_input( "Hit ENTER to run parm edit check for %s\n" % mod_rec[0] ) + cc.control = 38 + cc.value = mod_rec[1] + outport.send( cc ) + + raw_input( "Enter mod edit mode on Mustang and hit ENTER to proceed...\n" ) + for i in range( 39, 44 ): + cc.control = i + if (i==42 and mod_rec[2]) or (i==43 and mod_rec[3]): + discrete_send( outport, 1 ) + else: + analog_send( outport ) + +# Step through all stomp models +def stomp_select( outport ): + cc.control = 28 + for i in range( 0, 8 ): + cc.value = i + outport.send( cc ) + sleep( 0.5 ) + +def run_stomp_test( struct, outport ): + raw_input( "Hit ENTER to run stomp model select test...\n" ) + stomp_select( outport ) + + for i in range( 0, len(struct) ): + stomp_rec = struct[i] + + raw_input( "Hit ENTER to run parm edit check for %s\n" % stomp_rec[0] ) + cc.control = 28 + cc.value = stomp_rec[1] + outport.send( cc ) + + raw_input( "Enter stomp edit mode on Mustang and hit ENTER to proceed...\n" ) + for i in range( 29, 34 ): + cc.control = i + if i==33 and stomp_rec[2]: + discrete_send( outport, 1 ) + elif i==29 and stomp_rec[3]: + discrete_send( outport, 3 ) + break + else: + analog_send( outport ) + +# Step through program changes +def program_change_test( outport ): + raw_input( "Hit ENTER to run program change test...\n" ) + for i in ( 0, 25, 75, 99, 60, 40, 20, 0 ): + pc.program = i + outport.send( pc ) + sleep( 0.5 ) + +args = sys.argv + +if not len(args) == 3: + print "Usage: test.py \n" + sys.exit( 1 ) + +try: + pc.channel = cc.channel = int( args[2] ) - 1 +except ValueError: + print "Arg2 must be numeric!\n" + sys.exit( 1 ) + +outport = mido.open_output( args[1] ) + +program_change_test( outport ) + +# Model # 33 Dig? +stomp_test = ( + ( "Overdrive", 1, False, False ), + ( "Wah", 2, True, False ), + ( "Simple Comp", 6, False, True ), + ( "Comp", 7, False, False ) + ) + +run_stomp_test( stomp_test, outport ) + +# Model # 42 Dig? 43 Dig? +mod_test = ( + ( "Sine Chorus", 1, False, False ), + ( "Vibratone", 5, False, False ), + ( "Vintage Trem", 6, False, False ), + ( "Ring Mod", 8, True, False ), + ( "Phaser", 10, False, True ), + ( "Pitch Shift", 11, False, False ) + ) + +run_mod_test( mod_test, outport ) + +run_reverb_test( outport ) + +# Model # 53 Dig?, Has 54? +delay_test = ( + ( "Mono Delay", 1, False, False ), + ( "Multitap", 4, True, False ), + ( "Tape Delay", 8, False, False ) + ) + +run_delay_test( delay_test, outport ) + +# Model, #, Bias/Sag?, Extra? +amp_test = ( + ( "Fender 65 Twin", 6, True, False ), + ( "Fender SuperSonic", 7, True, True ), + ( "British 60s", 8, True, True ), + ( "British 70s", 9, True, True ), + ( "British 80s", 10, True, True ) + ) + +run_amp_test( amp_test, outport ) +