From e909293a6cae27fe3b99e1a72f62db13015e94ac Mon Sep 17 00:00:00 2001 From: Nero Date: Fri, 28 Aug 2020 19:05:45 -0400 Subject: [PATCH] v0.1.0 Updated Fas Sync, Added Fas Looper Control --- library.properties | 2 +- src/BMC-Api.h | 8 + src/BMC-Version.h | 4 +- src/BMC.cpp | 18 +- src/BMC.debug.cpp | 7 + src/BMC.editor.cpp | 3 +- src/BMC.h | 22 +- src/BMC.hardware.buttons.cpp | 2 + src/BMC.hardware.ledEvents.cpp | 15 + src/editor/BMC-Editor.h | 1 + src/editor/BMC-Editor.midi.global.cpp | 3 + .../beatbuddy}/BMC-BeatBuddy.h | 0 src/{addon => sync/fas}/BMC-Fas-Def.h | 27 +- src/{addon => sync/fas}/BMC-Fas-Struct.h | 43 +- src/sync/fas/BMC-Fas.cpp | 464 ++++++++++++ src/{addon => sync/fas}/BMC-Fas.h | 705 ++++++------------ src/{utility => sync/helix}/BMC-Helix.h | 0 .../BMC-Kemper.h => sync/kemp/BMC-Kemp.h} | 2 +- src/utility/BMC-Callbacks.h | 4 + src/utility/BMC-Def.h | 24 + src/utility/BMC-Globals.h | 9 +- src/utility/BMC-Settings.h | 3 + 22 files changed, 848 insertions(+), 518 deletions(-) rename src/{utility => sync/beatbuddy}/BMC-BeatBuddy.h (100%) rename src/{addon => sync/fas}/BMC-Fas-Def.h (99%) rename src/{addon => sync/fas}/BMC-Fas-Struct.h (84%) create mode 100644 src/sync/fas/BMC-Fas.cpp rename src/{addon => sync/fas}/BMC-Fas.h (56%) rename src/{utility => sync/helix}/BMC-Helix.h (100%) rename src/{addon/BMC-Kemper.h => sync/kemp/BMC-Kemp.h} (98%) diff --git a/library.properties b/library.properties index 5eb99d7..4273958 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=BMC -version=0.0.19 +version=0.1.0 author=Nero Rox maintainer=Nero Rox sentence=Badass MIDI Controller (BMC), a fully featured MIDI Controller with a Desktop Editor App. For 32-bit Teensy Only diff --git a/src/BMC-Api.h b/src/BMC-Api.h index c507aff..0583e9d 100644 --- a/src/BMC-Api.h +++ b/src/BMC-Api.h @@ -299,6 +299,14 @@ class BMCApi : public BMC { void onFasTunerReceived(void (*fptr)(BMCTunerData& data)){ callback.fasTunerReceived = fptr; } + // triggered when the FAS Looper has been activated or stopped + void onFasLooperStateChange(void (*fptr)(bool state)){ + callback.fasLooperStateChange = fptr; + } + // triggered when the FAS Looper data has been received + void onFasLooperReceived(void (*fptr)(uint8_t data, uint8_t position)){ + callback.fasLooperReceived = fptr; + } // triggered when FAS CPU usage has been received void onFasCpuReceived(void (*fptr)(uint8_t n)){ callback.fasCpuReceived = fptr; diff --git a/src/BMC-Version.h b/src/BMC-Version.h index 1b0b74f..9c5b0f4 100644 --- a/src/BMC-Version.h +++ b/src/BMC-Version.h @@ -22,8 +22,8 @@ // BMC Version stored in EEPROM (for editor usage) #define BMC_VERSION_MAJ 0 -#define BMC_VERSION_MIN 0 -#define BMC_VERSION_PATCH 19 +#define BMC_VERSION_MIN 1 +#define BMC_VERSION_PATCH 0 //16 bits unsigned, LSB byte is minor, MSB byte is major #define BMC_VERSION ((BMC_VERSION_MAJ<<8) | BMC_VERSION_MIN) diff --git a/src/BMC.cpp b/src/BMC.cpp index 2564cda..ed5d31b 100644 --- a/src/BMC.cpp +++ b/src/BMC.cpp @@ -22,7 +22,7 @@ BMC::BMC(): ,helix(midi) #endif #ifdef BMC_USE_FAS - ,fas(midi) + ,fas(midi, globals) #endif #ifdef BMC_USE_KEMPER ,kemper(midi) @@ -112,6 +112,10 @@ void BMC::begin(){ delay(100); + #ifdef BMC_USE_FAS + fas.begin(); + #endif + // this flag will allow BMC to execute some code only the first time update() runs // yes that code can run here instead HOWEVER by letting that code execute // after the first loop we are allowing other classes with a begin() method @@ -180,20 +184,20 @@ void BMC::update(){ callback.midUpdate(); } - #ifdef BMC_USE_HELIX +#ifdef BMC_USE_HELIX helix.update(); - #endif +#endif - #ifdef BMC_USE_FAS +#ifdef BMC_USE_FAS fas.update(); if(fas.connectionStateChanged()){ editor.utilitySendFasState(fas.getConnectedDeviceId()); } - #endif +#endif - #ifdef BMC_USE_KEMPER +#ifdef BMC_USE_KEMPER kemper.update(); - #endif +#endif // Read/Update the hardware: buttons, leds, pots, encoders readHardware(); diff --git a/src/BMC.debug.cpp b/src/BMC.debug.cpp index 1645dc9..94012d0 100644 --- a/src/BMC.debug.cpp +++ b/src/BMC.debug.cpp @@ -57,6 +57,7 @@ void BMC::readDebug(){ #ifdef BMC_USE_FAS BMC_PRINTLN("fasConnection = Toggle the connection state of a FAS device"); + BMC_PRINTLN("fasDebug = Toggle Additional debug information of synced FAS device"); #endif BMC_PRINTLN("storageDebug = Prints the time it takes to read/write/clear EEPROM everytime the actions happens"); @@ -84,6 +85,12 @@ void BMC::readDebug(){ fas.connect(); } printDebugHeader(debugInput); + + } else if(BMC_STR_MATCH(debugInput,"fasDebug")){ + printDebugHeader(debugInput); + BMC_PRINTLN("FAS Debug:",globals.toggleFasDebug()); + printDebugHeader(debugInput); + #endif } else if(BMC_STR_MATCH(debugInput,"storageDebug")){ printDebugHeader(debugInput); diff --git a/src/BMC.editor.cpp b/src/BMC.editor.cpp index b79e8bc..e861c70 100644 --- a/src/BMC.editor.cpp +++ b/src/BMC.editor.cpp @@ -82,7 +82,6 @@ void BMC::assignStoreData(){ } void BMC::assignSettings(){ // Set the global settings - valueTyper.setOffset(settings.getTyperOffSet()); midiClock.setMaster(settings.getMasterClock()); midiActiveSense.setOutputPorts(settings.getListenerPorts()); midi.setListenerEnable(settings.getIncomingListenerEnabled()); @@ -91,6 +90,8 @@ void BMC::assignSettings(){ midi.setClockListenerPort(settings.getClockInputPortBit()); editor.setChainingPort(settings.getChainingPort()); + valueTyper.setOffset(settings.getTyperOffSet()); + midi.setRouting(BMC_USB, settings.getUsbRouting()); #ifdef BMC_MIDI_SERIAL_A_ENABLED midi.setRouting(BMC_SERIAL_A, settings.getSerialARouting()); diff --git a/src/BMC.h b/src/BMC.h index 26c9297..ee88f61 100644 --- a/src/BMC.h +++ b/src/BMC.h @@ -47,24 +47,20 @@ #include "midi/BMC-MidiActiveSense.h" #include "editor/BMC-Editor.h" - - -#ifdef BMC_USE_BEATBUDDY - #include "utility/BMC-BeatBuddy.h" +#if defined(BMC_USE_FAS) + #include "sync/fas/BMC-Fas.h" #endif -#ifdef BMC_USE_HELIX - #include "utility/BMC-Helix.h" +#if defined(BMC_USE_KEMPER) + #include "sync/kemp/BMC-Kemp.h" #endif -#ifdef BMC_USE_FAS - #include "addon/BMC-Fas-Def.h" - #include "addon/BMC-Fas-Struct.h" - #include "addon/BMC-Fas.h" +#if defined(BMC_USE_BEATBUDDY) + #include "sync/beatbuddy/BMC-BeatBuddy.h" #endif -#ifdef BMC_USE_KEMPER - #include "addon/BMC-Kemper.h" +#if defined(BMC_USE_HELIX) + #include "sync/helix/BMC-Helix.h" #endif #if defined(BMC_MUX_AVAILABLE) @@ -223,7 +219,7 @@ class BMC { #endif #if defined(BMC_USE_KEMPER) -// handles Kemper devices see src/addon/BMC-Kemper.h +// handles Kemper devices see src/sync/BMC-Kemper.h BMCKemper kemper; #endif diff --git a/src/BMC.hardware.buttons.cpp b/src/BMC.hardware.buttons.cpp index 49d4566..3cf6ec4 100644 --- a/src/BMC.hardware.buttons.cpp +++ b/src/BMC.hardware.buttons.cpp @@ -621,6 +621,8 @@ void BMC::handleGlobalButton(uint8_t index, uint8_t t_trigger){ } else { fas.connect(); } + } else if(byteA>=5){// looper + fas.looperControl(byteA-5); } break; case BMC_BUTTON_EVENT_TYPE_FAS_PRESET: diff --git a/src/BMC.hardware.ledEvents.cpp b/src/BMC.hardware.ledEvents.cpp index 7ba8395..72a5825 100644 --- a/src/BMC.hardware.ledEvents.cpp +++ b/src/BMC.hardware.ledEvents.cpp @@ -141,6 +141,21 @@ uint8_t BMC::handleLedEvent(uint8_t index, uint32_t event, uint8_t ledType){ return fas.isTunerActive(); } else if(byteA==2){ return fas.tempoReceived() ? BMC_PULSE_LED_EVENT : BMC_IGNORE_LED_EVENT; + } else if(byteA==3){ + return fas.looperStatus(BMC_FAS_LOOPER_STATE_PLAYING); + } else if(byteA==4){ + return fas.looperStatus(BMC_FAS_LOOPER_STATE_RECORDING); + } else if(byteA==5){ + return fas.looperStatus(BMC_FAS_LOOPER_STATE_OVERDUBBING); + } else if(byteA==6){ // recording OR looping + return fas.looperStatus(BMC_FAS_LOOPER_STATE_OVERDUBBING) || + fas.looperStatus(BMC_FAS_LOOPER_STATE_RECORDING); + } else if(byteA==7){ + return fas.looperStatus(BMC_FAS_LOOPER_STATE_REVERSED); + } else if(byteA==8){ + return fas.looperStatus(BMC_FAS_LOOPER_STATE_HALF); + } else if(byteA==9){ // stopped but has a track recorded + return fas.looperStopped() && fas.looperTrackRecorded(); } break; case BMC_LED_EVENT_TYPE_FAS_PRESET: diff --git a/src/editor/BMC-Editor.h b/src/editor/BMC-Editor.h index 86b93d0..60dfa9c 100644 --- a/src/editor/BMC-Editor.h +++ b/src/editor/BMC-Editor.h @@ -43,6 +43,7 @@ class BMCEditor { BMC-Editor.cpp */ BMCEditor(bmcStore& t_store, BMCMidi& t_midi, BMCSettings& t_settings, BMCMessenger& t_messenger); + void begin(); void update(); bool readyToReload(); diff --git a/src/editor/BMC-Editor.midi.global.cpp b/src/editor/BMC-Editor.midi.global.cpp index 80e4d1a..f41cbc8 100644 --- a/src/editor/BMC-Editor.midi.global.cpp +++ b/src/editor/BMC-Editor.midi.global.cpp @@ -261,6 +261,9 @@ void BMCEditor::globalBuildInfoMessage(){// BMC_GLOBALF_BUILD_INFO #ifdef BMC_USE_POT_TOE_SWITCH bitWrite(buildData,17,1); #endif + #ifdef BMC_USE_FAS3 + bitWrite(buildData,18,1); + #endif // remove after out of beta bitWrite(buildData, 31, 1); diff --git a/src/utility/BMC-BeatBuddy.h b/src/sync/beatbuddy/BMC-BeatBuddy.h similarity index 100% rename from src/utility/BMC-BeatBuddy.h rename to src/sync/beatbuddy/BMC-BeatBuddy.h diff --git a/src/addon/BMC-Fas-Def.h b/src/sync/fas/BMC-Fas-Def.h similarity index 99% rename from src/addon/BMC-Fas-Def.h rename to src/sync/fas/BMC-Fas-Def.h index ab0b18d..90082f2 100644 --- a/src/addon/BMC-Fas-Def.h +++ b/src/sync/fas/BMC-Fas-Def.h @@ -10,12 +10,13 @@ #ifndef BMC_FAS_DEF_H #define BMC_FAS_DEF_H - // Fractal Compatible Device IDs #define BMC_FAS_DEVICE_ID_AXE_FX_II 0x03 #define BMC_FAS_DEVICE_ID_AXE_FX_II_XL 0x06 #define BMC_FAS_DEVICE_ID_AXE_FX_II_XL_PLUS 0x07 #define BMC_FAS_DEVICE_ID_AX8 0x08 +#define BMC_FAS_DEVICE_ID_AXE_FX_III 0x10 +#define BMC_FAS_DEVICE_ID_FM3 0x11 // Fractal Block IDs #define BMC_FAS_BLOCK_CPR 100 @@ -147,6 +148,30 @@ #ifndef BMC_FAS_CC_SCENE_DEC #define BMC_FAS_CC_SCENE_DEC 124 #endif +#ifndef BMC_FAS_CC_LOOPER_RECORD + #define BMC_FAS_CC_LOOPER_RECORD 28 +#endif +#ifndef BMC_FAS_CC_LOOPER_PLAY + #define BMC_FAS_CC_LOOPER_PLAY 29 +#endif +#ifndef BMC_FAS_CC_LOOPER_ONCE + #define BMC_FAS_CC_LOOPER_ONCE 30 +#endif +#ifndef BMC_FAS_CC_LOOPER_DUB + #define BMC_FAS_CC_LOOPER_DUB 31 +#endif +#ifndef BMC_FAS_CC_LOOPER_REVERSE + #define BMC_FAS_CC_LOOPER_REVERSE 32 +#endif +#ifndef BMC_FAS_CC_LOOPER_BYPASS + #define BMC_FAS_CC_LOOPER_BYPASS 33 +#endif +#ifndef BMC_FAS_CC_LOOPER_HALF + #define BMC_FAS_CC_LOOPER_HALF 120 +#endif +#ifndef BMC_FAS_CC_LOOPER_UNDO + #define BMC_FAS_CC_LOOPER_UNDO 121 +#endif // BLOCK PARAMETERS diff --git a/src/addon/BMC-Fas-Struct.h b/src/sync/fas/BMC-Fas-Struct.h similarity index 84% rename from src/addon/BMC-Fas-Struct.h rename to src/sync/fas/BMC-Fas-Struct.h index 0205722..73a525d 100644 --- a/src/addon/BMC-Fas-Struct.h +++ b/src/sync/fas/BMC-Fas-Struct.h @@ -5,6 +5,10 @@ See LICENSE file in the project root for full license information. Wrapper to control/read from Fractal Audio Devices + + Currently only supports Axe Fx II/XL/+ and AX8 + + Support for Axe Fx 3 and FM3 is planned for the future */ #ifndef BMC_FAS_STRUCT_H #define BMC_FAS_STRUCT_H @@ -13,7 +17,7 @@ #if defined(BMC_USE_FAS) -#include "addon/BMC-Fas-Def.h" +#include "sync/fas/BMC-Fas-Def.h" // Fractal Device ID, only AX8 supported at the moment // ------------------------ @@ -28,6 +32,7 @@ // 0x08 AX8 // 0x0A FX8 mk2 // 0x10 Axe-Fx III +// 0x11 FM3 struct BMCFasBlocks { uint8_t id = 0; @@ -123,23 +128,40 @@ struct BMCFasBlockStates { } }; struct BMCFasLooper { + bool enabled = false; uint8_t data; uint8_t position; void set(uint8_t t_data, uint8_t t_position){ data = t_data; position = t_position; } - bool recording(){ return bitRead(data, 0); } - bool play(){ return bitRead(data, 1); } - bool once(){ return bitRead(data, 2); } - bool overdub(){ return bitRead(data, 3); } - bool reverse(){ return bitRead(data, 4); } - bool half(){ return bitRead(data, 5); } - bool undo(){ return bitRead(data, 6); } + void changeState(bool value){ + enabled = value; + } + bool getStates(uint8_t bit=255){ + if(bit<=6){ + return bitRead(data, bit); + } else { + return data; + } + return false; + } + bool isEnabled(){ return enabled; } + bool recording(){ return bitRead(data, BMC_FAS_LOOPER_STATE_RECORDING); } + bool playing(){ return bitRead(data, BMC_FAS_LOOPER_STATE_PLAYING); } + bool once(){ return bitRead(data, BMC_FAS_LOOPER_STATE_ONCE); } + bool overdubbing(){ return bitRead(data, BMC_FAS_LOOPER_STATE_OVERDUBBING); } + bool reversed(){ return bitRead(data, BMC_FAS_LOOPER_STATE_REVERSED); } + bool half(){ return bitRead(data, BMC_FAS_LOOPER_STATE_HALF); } + bool undo(){ return bitRead(data, BMC_FAS_LOOPER_STATE_UNDO); } + + uint8_t getPosition(){ return position; } void reset(){ data = 0; position = 0; + // do not reset the enabled as it's controller by BMC Settings. + //enabled = false; } }; @@ -156,7 +178,6 @@ struct BMCFasData { BMCFasBlockStates blocks; BMCFasLooper looper; - uint16_t parameters[8]; uint16_t parametersX[8]; uint16_t parametersY[8]; @@ -219,7 +240,9 @@ struct BMCFasData { } return 255; } - + bool getLooperState(){ + return looper.isEnabled(); + } void setIdAndPort(uint8_t t_id, uint8_t t_port){ diff --git a/src/sync/fas/BMC-Fas.cpp b/src/sync/fas/BMC-Fas.cpp new file mode 100644 index 0000000..a41eb29 --- /dev/null +++ b/src/sync/fas/BMC-Fas.cpp @@ -0,0 +1,464 @@ +#include "sync/fas/BMC-Fas.h" + +#if defined(BMC_USE_FAS) + +BMCFas::BMCFas(BMCMidi& t_midi, BMCGlobals& t_globals): + midi(t_midi), + globals(t_globals) +{ + device.reset(); + findDeviceTimer.stop(); +} +void BMCFas::begin(){ + BMC_INFO("FAS Sync Version 1.0"); + flags.on(BMC_FAS_FLAG_DEVICE_SEARCH); + findDeviceTimer.start(1000); +} +void BMCFas::update(){ + // search for response from FAS device, try up to 10 times every 5 seconds + // this message will be broadcast on all MIDI ports except USB and BLE (if available) + if(flags.read(BMC_FAS_FLAG_DEVICE_SEARCH)){ + if(attempts<10 && findDeviceTimer.complete()){ +#ifdef BMC_DEBUG + if(attempts==0){ + delay(1); + BMC_PRINTLN(""); + BMC_PRINTLN(""); + BMC_INFO("FAS Starting Device Search"); + } +#endif + BMC_PRINTLN(" >> Looking for FAS Device << "); + sendDeviceSearch(); + findDeviceTimer.start(5000); + attempts++; + } + return; + } + if(connected()){ + // if connected + if(connectionLost.complete()){ + // if the timer for connection lost was received + if(flags.read(BMC_FAS_FLAG_CONNECTION_LOST)){ + // if we had already tried one more time to wait for a conection + // then we can assume we lost the connection + BMC_PRINTLN("No response from FAS, connection lost..."); + disconnect(); + } else { + // in this case we waited for 3 seconds for a new message to be received + // so we'll turn this flag on to wait for a new message one last time + flags.on(BMC_FAS_FLAG_CONNECTION_LOST); + sendBasicSysEx(BMC_FAS_FUNC_ID_CPU); + connectionLost.start(BMC_FAS_CONNECTION_LOST_TIMEOUT); + } + } + } + if(tunerTimeout.complete()){ + flags.off(BMC_FAS_FLAG_TUNER_ACTIVE); + if(midi.callback.fasTunerStateChange){ + midi.callback.fasTunerStateChange(false); + } + BMC_PRINTLN("--> FAS TUNER: OFF"); + } + if(looperTimeout.complete()){ + flags.off(BMC_FAS_FLAG_LOOPER_ACTIVE); + if(midi.callback.fasLooperStateChange){ + midi.callback.fasLooperStateChange(false); + } + BMC_PRINTLN("--> FAS LOOPER: STOPPED"); + } + if(!connected()&&!syncing()){ + if(startSyncTimer.complete()){ + startSyncTimer.start(3000); + sendBasicSysEx(BMC_FAS_FUNC_ID_FIRMWARE); + } + } + presetSyncQueue(); +} +// parse incoming Sysex Messages +bool BMCFas::incoming(BMCMidiMessage& message){ + if(flags.read(BMC_FAS_FLAG_DEVICE_SEARCH) && findDeviceTimer.active()){ + if(isFractMessage(message)){ + device.setIdAndPort(message.sysex[4], message.getPort()); + flags.off(BMC_FAS_FLAG_DEVICE_SEARCH); + attempts = 0; + findDeviceTimer.stop(); + startSyncTimer.start(250); + timeConnectionStart(); + BMC_INFO("FAS FOUND DEVICE", debugPrintDeviceName(device.getId()), BMCTools::getPortName(device.getPort())); + } + return false; + } + if(!isFractMessage(message) || !isValidPort(message.getPort())){ + return false; + } +#ifdef BMC_DEBUG + debugPrintFasMessageInfo(message); +#endif + + timeConnectionStart(); + + // messages that don't have a Checksum + switch(message.sysex[5]){ + case BMC_FAS_FUNC_ID_MIDI_TEMPO_BEAT:{ + if(!connected() || !isFractMessage(message, 5)){ + return false; + } + flags.on(BMC_FAS_FLAG_TEMPO_RECEIVED); + } + return true; + case BMC_FAS_FUNC_ID_TUNER_INFO:{ + if(!connected() || !isFractMessage(message, 10)){ + return false; + } + tunerData.note = message.sysex[6]; + tunerData.stringNumber = message.sysex[7]; + tunerData.pitch = map((message.sysex[8]&0x7F), 0, 127, -63, 64); + tunerNote(tunerData.note, tunerData.noteName); + tunerTimeout.start(250); + if(!flags.read(BMC_FAS_FLAG_TUNER_ACTIVE)){ + if(midi.callback.fasTunerStateChange){ + midi.callback.fasTunerStateChange(true); + } + flags.on(BMC_FAS_FLAG_TUNER_ACTIVE); + BMC_PRINTLN("--> FAS TUNER: ON"); + } + if(midi.callback.fasTunerReceived){ + midi.callback.fasTunerReceived(tunerData); + } + } + return true; + case BMC_FAS_FUNC_ID_LOOPER:{ + if(!connected() || !isFractMessage(message, 9)){ + return false; + } + uint8_t data = message.sysex[6]; + uint8_t position = message.sysex[7]; + device.looper.set(data, position); + + if(data>0){ + looperTimeout.start(1000); + if(!flags.read(BMC_FAS_FLAG_LOOPER_ACTIVE)){ + if(midi.callback.fasLooperStateChange){ + midi.callback.fasLooperStateChange(true); + } + flags.on(BMC_FAS_FLAG_LOOPER_ACTIVE); + BMC_PRINTLN("--> FAS LOOPER: ACTIVE", data); + } + if(device.looper.getStates(BMC_FAS_LOOPER_STATE_RECORDING)){ + flags.on(BMC_FAS_FLAG_LOOPER_TRACK_AVAILABLE); + } + } + if(midi.callback.fasLooperReceived){ + // only send the position if it's not doing an initial recording. + midi.callback.fasLooperReceived(data, bitRead(data,0)?0:position); + } +#if defined(BMC_DEBUG) + if(globals.getFasDebug()){ + BMC_PRINTLN("--> FAS Looper Info", data, position); + } +#endif + } + return true; + case BMC_FAS_FUNC_ID_BLOCKS_DATA:{ + if(!connected() || !isFractMessage(message, 5)){ + return false; + } + // crc used to know if there's a change within the blocks + uint8_t crc = device.blocks.getCRC(); + device.blocks.reset(); + BMC_PRINTLN("--> FAS BLOCKS RECEIVED:", (message.size()-8)/5); + for(uint8_t i = 6; i < message.size()-2; i+=5){ + uint32_t block = message.get32BitsLSBFirst(i); + bool isEngaged = bitRead(block, 0); + bool isX = bitRead(block, 1); + uint8_t blockId = (block>>24) & 0xFF; + #ifdef BMC_DEBUG + if(globals.getFasDebug()){ + uint8_t bypassCC = (block>>8) & 0x7F; + uint8_t xyCC = (block>>16) & 0x7F; + if(blockId>=100 && blockId<=170){ + BMC_PRINTLN("--> FAS BLOCK:", blocksGlobalData[blockId-100].name, blockId, bypassCC, xyCC, isEngaged?"ON":"OFF", isX?"X":"Y"); + } + } + #endif + device.blocks.set(blockId, isEngaged, !isX); + } + if(midi.callback.fasBlocksChange){ + device.blocks.createCRC(); + if(crc!=device.blocks.getCRC()){ + midi.callback.fasBlocksChange(); + } + } + if(flags.toggleIfTrue(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS)){ + resyncTimer.start(BMC_FAS_RESYNC_QUEUE_TIMEOUT); + } + } + return true; + } + // the rest require a valid CRC + if(isValidFasFunction(message.sysex[5]) && !message.validateChecksum()){ + BMC_PRINTLN("!!! FAS Received Bad CRC !!!"); + // TODO: add code to resend the message that returned the bad CRC + return false; + } + switch(message.sysex[5]){ + case BMC_FAS_FUNC_ID_RESYNC:{ + device.paramReset(); + BMC_WARN("FAS RE-SYNC RECEIVED"); + receivedReSync(true); + } + + return true; + case BMC_FAS_FUNC_ID_FIRMWARE:{ + if(connected() || !isFractMessage(message, 10) || syncing()){ + return false; + } + device.version = (message.get7Bits(6)<<8) | message.get7Bits(7); + flags.on(BMC_FAS_FLAG_SYNCING); + startSyncTimer.stop(); + sendBasicSysEx(BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL); + BMC_PRINTLN("--> FAS FIRMWARE:", device.version); + } + return true; + case BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL:{ + if(connected() || !isFractMessage(message, 9) || !syncing()){ + return false; + } + // turn on looper data when connecting + // we'll use this as a way to track the state of the connection + sendBasicSysEx(BMC_FAS_FUNC_ID_LOOPER, true); + device.channel = message.get7Bits(6)+1; + BMC_PRINTLN("--> FAS MIDI CHANNEL:", device.channel); + flags.on(BMC_FAS_FLAG_CONNECTED); + flags.on(BMC_FAS_FLAG_CONNECTION_CHANGED); + flags.off(BMC_FAS_FLAG_SYNCING); + if(midi.callback.fasConnection){ + midi.callback.fasConnection(true); + } + receivedReSync(); + flags.on(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); + } + return true; + case BMC_FAS_FUNC_ID_BLOCK_PARAM:{ + if(!connected() || !isFractMessage(message, 15)){ + return false; + } + uint8_t blockId = message.get8BitsLSBFirst(6); + uint8_t paramId = message.get8BitsLSBFirst(8); + uint16_t value = message.get16BitsLSBFirst(10); + if(paramId==255){ + device.blocks.set(blockId, (value==0), device.blocks.isY(blockId)); + if(midi.callback.fasBlocksChange && device.blocks.isEngaged(blockId) != (value==0)){ + midi.callback.fasBlocksChange(); + } + BMC_PRINTLN("--> FAS BLOCK Bypass:", blockId, value==0?"engaged":"bypassed"); + } else { + device.paramReceived(blockId, paramId, value); + requestSyncParameters(); + if(midi.callback.fasBlockParameterReceived){ + uint8_t strLen = message.size()-(18+2); + char str[strLen] = ""; + message.getStringFromSysEx(18, str, strLen); + midi.callback.fasBlockParameterReceived(blockId, paramId, value, str, strLen); + BMC_PRINTLN("--> FAS Block Parameter Received: Block:", blockId, + "Param:", paramId, "Value:", value, "Str:", str, strLen); + } + } + } + return true; + case BMC_FAS_FUNC_ID_CPU:{ + if(!connected() || !isFractMessage(message, 9)){ + return false; + } + uint8_t value = message.get7Bits(6); +#if defined(BMC_DEBUG) + if(globals.getFasDebug()){ + BMC_PRINTLN("--> FAS CPU:", value, "%"); + } +#endif + if(midi.callback.fasCpuReceived){ + midi.callback.fasCpuReceived(value); + } + } + return true; + case BMC_FAS_FUNC_ID_PRESET_NAME:{ + if(!connected() || !isFractMessage(message, 30)){ + return false; + } + strcpy(device.presetName, ""); + message.getStringFromSysEx(6, device.presetName, 32); + BMC_PRINTLN("--> FAS PRESET NAME:", device.presetName); + if(flags.toggleIfTrue(BMC_FAS_FLAG_SYNC_EXPECTING_PRESET_NAME)){ + resyncTimer.start(BMC_FAS_RESYNC_QUEUE_TIMEOUT); + } + if(midi.callback.fasPresetName){ + midi.callback.fasPresetName(device.presetName); + } + } + return true; + case BMC_FAS_FUNC_ID_GET_PRESET_NUMBER:{ + if(!connected() || !isFractMessage(message, 10)){ + return false; + } + uint16_t value = message.get14Bits(6); + if(device.preset!=value){ + device.paramReset(); + if(midi.callback.fasPresetChange){ + midi.callback.fasPresetChange(value); + } + } + device.preset = value; + BMC_PRINTLN("--> FAS PRESET NUMBER:", debugPrintPreset()); + if(flags.toggleIfTrue(BMC_FAS_FLAG_SYNC_EXPECTING_PRESET)){ + resyncTimer.start(BMC_FAS_RESYNC_TIMEOUT); + } else { + receivedReSync(); + flags.off(BMC_FAS_FLAG_SYNC_EXPECTING_PRESET); + flags.on(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); + resyncTimer.start(BMC_FAS_RESYNC_TIMEOUT); + } + } + return true; + case BMC_FAS_FUNC_ID_SCENE_NUMBER:{ + if(!connected() || !isFractMessage(message, 9)){ + return false; + } + uint8_t value = message.get7Bits(6); + if(device.scene!=value){ + flags.on(BMC_FAS_FLAG_SYNC_PARAMETER_SYNC_BEGIN); + requestSyncParameters(); + if(midi.callback.fasSceneChange){ + midi.callback.fasSceneChange(value); + } + } + device.scene = value; + BMC_PRINTLN("--> FAS SCENE NUMBER:", device.scene+1); + if(flags.toggleIfTrue(BMC_FAS_FLAG_SYNC_EXPECTING_SCENE)){ + resyncTimer.start(BMC_FAS_RESYNC_QUEUE_TIMEOUT); + if(device.id==BMC_FAS_DEVICE_ID_AX8){ + flags.off(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); + } + } else { + if(device.id!=BMC_FAS_DEVICE_ID_AX8){ + flags.off(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); + resyncTimer.start(BMC_FAS_RESYNC_QUEUE_TIMEOUT); + } + } + } + return true; + case BMC_FAS_FUNC_ID_BLOCK_XY:{ + if(!connected() || !isFractMessage(message, 11)){ + return false; + } + uint8_t blockId = message.get8BitsLSBFirst(6); + uint8_t xy = message.get7Bits(8); + device.blocks.set(blockId, device.blocks.isEngaged(blockId), xy); + if(midi.callback.fasBlocksChange && device.blocks.isY(blockId)!=xy){ + midi.callback.fasBlocksChange(); + } + BMC_PRINTLN("--> FAS BLOCK XY:", blockId, xy?"Y":"X"); + } + return true; + case BMC_FAS_FUNC_ID_GENERAL_PURPOSE:{ + if(!isFractMessage(message, 10)){ + return false; + } + BMC_PRINTLN("--> FAS GENERAL PURPOSE FUNC:", message.get7Bits(6),"CODE:", message.get7Bits(7)); + } + return true; + } + return false; +} + + + + + + + +// DEBUGGING + +#ifdef BMC_DEBUG +String BMCFas::debugPrintDeviceName(uint8_t id){ + switch(id){ + case BMC_FAS_DEVICE_ID_AXE_FX_II: return "Axe Fx II"; + case BMC_FAS_DEVICE_ID_AXE_FX_II_XL: return "Axe Fx II XL"; + case BMC_FAS_DEVICE_ID_AXE_FX_II_XL_PLUS: return "Axe Fx II XL+"; + case BMC_FAS_DEVICE_ID_AX8: return "AX8"; + case BMC_FAS_DEVICE_ID_AXE_FX_III: return "Axe Fx III"; + case BMC_FAS_DEVICE_ID_FM3: return "FM3"; + } + return ""; +} +String BMCFas::debugPrintPreset(){ + char buff[30]; + switch(device.id){ + case BMC_FAS_DEVICE_ID_AXE_FX_II: + case BMC_FAS_DEVICE_ID_AXE_FX_II_XL: + case BMC_FAS_DEVICE_ID_AXE_FX_II_XL_PLUS:{ + uint8_t preset = (device.preset & 0x7F)+1; + uint8_t bank = ((device.preset>>7) & 0x7F); + bank = constrain(bank, 0, 25); + sprintf(buff, "(%03u) BANK: %c | PRESET: %03u", device.preset, (char) bmcAlphabet[bank], preset); + String output(buff); + return output; + } + case BMC_FAS_DEVICE_ID_AX8:{ + uint8_t preset = (device.preset & 0x07)+1; + uint8_t bank = (device.preset>>3)+1; + sprintf(buff, "(%03u) BANK: %02u | PRESET: %01u", device.preset, bank, preset); + String output(buff); + return output; + } + } + return ""; +} +void BMCFas::debugPrintFasMessageInfo(BMCMidiMessage& message){ + char str[50]; + switch(message.sysex[5]){ + case BMC_FAS_FUNC_ID_BLOCK_PARAM: strcpy(str, "BMC_FAS_FUNC_ID_BLOCK_PARAM");break; + case BMC_FAS_FUNC_ID_FIRMWARE: strcpy(str, "BMC_FAS_FUNC_ID_FIRMWARE");break; + case BMC_FAS_FUNC_ID_TUNER_INFO: strcpy(str, "BMC_FAS_FUNC_ID_TUNER_INFO");break; + case BMC_FAS_FUNC_ID_BLOCKS_DATA: strcpy(str, "BMC_FAS_FUNC_ID_BLOCKS_DATA");break; + case BMC_FAS_FUNC_ID_PRESET_NAME: strcpy(str, "BMC_FAS_FUNC_ID_PRESET_NAME");break; + case BMC_FAS_FUNC_ID_MIDI_TEMPO_BEAT: strcpy(str, "BMC_FAS_FUNC_ID_MIDI_TEMPO_BEAT");break; + case BMC_FAS_FUNC_ID_BLOCK_XY: strcpy(str, "BMC_FAS_FUNC_ID_BLOCK_XY");break; + case BMC_FAS_FUNC_ID_CPU: strcpy(str, "BMC_FAS_FUNC_ID_CPU");break; + case BMC_FAS_FUNC_ID_GET_PRESET_NUMBER: strcpy(str, "BMC_FAS_FUNC_ID_GET_PRESET_NUMBER");break; + case BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL: strcpy(str, "BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL");break; + case BMC_FAS_FUNC_ID_RESYNC: strcpy(str, "BMC_FAS_FUNC_ID_RESYNC");break; + case BMC_FAS_FUNC_ID_LOOPER: strcpy(str, "BMC_FAS_FUNC_ID_LOOPER");break; + case BMC_FAS_FUNC_ID_SCENE_NUMBER: strcpy(str, "BMC_FAS_FUNC_ID_SCENE_NUMBER");break; + case BMC_FAS_FUNC_ID_SET_PRESET_NUMBER: strcpy(str, "BMC_FAS_FUNC_ID_SET_PRESET_NUMBER");break; + case BMC_FAS_FUNC_ID_DISCONNECT: strcpy(str, "BMC_FAS_FUNC_ID_DISCONNECT");break; + case BMC_FAS_FUNC_ID_GENERAL_PURPOSE: strcpy(str, "BMC_FAS_FUNC_ID_GENERAL_PURPOSE");break; + default: strcpy(str, "UNUSED"); + } + if(globals.getFasDebug()){ + BMC_PRINTLN("--> FAS MESSAGE RECEIVED, ID:", message.sysex[5], str, "SIZE:", message.size()); + } +} +bool BMCFas::isValidFasFunction(uint8_t funcId){ + switch(funcId){ + case BMC_FAS_FUNC_ID_BLOCK_PARAM: + case BMC_FAS_FUNC_ID_FIRMWARE: + case BMC_FAS_FUNC_ID_BLOCKS_DATA: + case BMC_FAS_FUNC_ID_TUNER_INFO: + case BMC_FAS_FUNC_ID_PRESET_NAME: + case BMC_FAS_FUNC_ID_BLOCK_XY: + case BMC_FAS_FUNC_ID_CPU: + case BMC_FAS_FUNC_ID_GET_PRESET_NUMBER: + case BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL: + case BMC_FAS_FUNC_ID_RESYNC: + case BMC_FAS_FUNC_ID_LOOPER: + case BMC_FAS_FUNC_ID_SCENE_NUMBER: + case BMC_FAS_FUNC_ID_SET_PRESET_NUMBER: + case BMC_FAS_FUNC_ID_DISCONNECT: + case BMC_FAS_FUNC_ID_GENERAL_PURPOSE: + return true; + } + return false; +} +#endif + +#endif diff --git a/src/addon/BMC-Fas.h b/src/sync/fas/BMC-Fas.h similarity index 56% rename from src/addon/BMC-Fas.h rename to src/sync/fas/BMC-Fas.h index befc705..c4e6203 100644 --- a/src/addon/BMC-Fas.h +++ b/src/sync/fas/BMC-Fas.h @@ -6,6 +6,7 @@ Wrapper to some sync/control Fractal Audio Guitar Processors In this Class as BLOCK is an EFFECT like Delay, Reverb, Amp, etc. + Fractal Sysex Anatomy 0: 0xF0 start of sysex 1: 0x00 sysex id @@ -20,37 +21,50 @@ Standard FAS response is minimum 9 bytes, without checksum it's 8 For Axe Fx 2 users: for ideal syncing you should turn off the MIDI THRU OFF, - also to use the tuner functions you must set "SEND REALTIME SYSEX" to ALL or TUNER + also set "SEND REALTIME SYSEX" to ALL if you want to use the tuner and tempo beat led + + Currently only supports Axe Fx II/XL/+ and AX8 + + Support for Axe Fx III and FM3 is planned for the future */ #ifndef BMC_FAS_H #define BMC_FAS_H + #include "utility/BMC-Def.h" + #if defined(BMC_USE_FAS) -#include "addon/BMC-Fas-Def.h" -#include "addon/BMC-Fas-Struct.h" + +#include "midi/BMC-Midi.h" +#include "sync/fas/BMC-Fas-Def.h" +#include "sync/fas/BMC-Fas-Struct.h" #define BMC_FAS_FLAG_DEVICE_SEARCH 0 #define BMC_FAS_FLAG_CONNECTED 1 #define BMC_FAS_FLAG_CONNECTION_CHANGED 2 #define BMC_FAS_FLAG_SYNCING 3 #define BMC_FAS_FLAG_TUNER_ACTIVE 4 -#define BMC_FAS_FLAG_SYNC_EXPECTING_PRESET 5 -#define BMC_FAS_FLAG_SYNC_EXPECTING_PRESET_NAME 6 -#define BMC_FAS_FLAG_SYNC_EXPECTING_SCENE 7 -#define BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS 8 -#define BMC_FAS_FLAG_SYNC_EXPECTING_PARAMETERS 9 -#define BMC_FAS_FLAG_SYNC_PARAMETER_SYNC_BEGIN 10 -#define BMC_FAS_FLAG_TEMPO_RECEIVED 11 - -#define BMC_FAS_RESYNC_TIMEOUT 1000 -#define BMC_FAS_RESYNC_QUEUE_TIMEOUT 175 - +#define BMC_FAS_FLAG_LOOPER_ACTIVE 5 +#define BMC_FAS_FLAG_SYNC_EXPECTING_PRESET 6 +#define BMC_FAS_FLAG_SYNC_EXPECTING_PRESET_NAME 7 +#define BMC_FAS_FLAG_SYNC_EXPECTING_SCENE 8 +#define BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS 9 +#define BMC_FAS_FLAG_SYNC_EXPECTING_PARAMETERS 10 +#define BMC_FAS_FLAG_SYNC_PARAMETER_SYNC_BEGIN 11 +#define BMC_FAS_FLAG_TEMPO_RECEIVED 12 +#define BMC_FAS_FLAG_LOOPER_TRACK_AVAILABLE 13 +#define BMC_FAS_FLAG_CONNECTION_LOST 14 + +#define BMC_FAS_RESYNC_TIMEOUT 250 +#define BMC_FAS_RESYNC_QUEUE_TIMEOUT 250 +#define BMC_FAS_CONNECTION_LOST_TIMEOUT 5000 + +// function id list for Axe Fx II and AX8 #define BMC_FAS_FUNC_ID_BLOCK_PARAM 0x02 #define BMC_FAS_FUNC_ID_FIRMWARE 0x08 #define BMC_FAS_FUNC_ID_TUNER_INFO 0x0D #define BMC_FAS_FUNC_ID_BLOCKS_DATA 0x0E #define BMC_FAS_FUNC_ID_PRESET_NAME 0x0F -#define BMC_FAS_FUNC_ID_MIDI_TEMPO_BEAT 0x10 // unused +#define BMC_FAS_FUNC_ID_MIDI_TEMPO_BEAT 0x10 #define BMC_FAS_FUNC_ID_BLOCK_XY 0x11 #define BMC_FAS_FUNC_ID_CPU 0x13 #define BMC_FAS_FUNC_ID_GET_PRESET_NUMBER 0x14 @@ -62,105 +76,59 @@ #define BMC_FAS_FUNC_ID_DISCONNECT 0x42 #define BMC_FAS_FUNC_ID_GENERAL_PURPOSE 0x64 -//#define BMC_FAS_DEBUG - -#if defined(BMC_FAS_DEBUG) && !defined(BMC_DEBUG) - #undef BMC_FAS_DEBUG -#endif +// for Axe Fx 3 +#define BMC_FAS_FUNC_ID_BLOCK_PARAM 0x02 +#define BMC_FAS_FUNC_ID_FIRMWARE 0x08 +#define BMC_FAS_FUNC_ID_TUNER_INFO 0x0D +#define BMC_FAS_FUNC_ID_BLOCKS_DATA 0x0E +#define BMC_FAS_FUNC_ID_PRESET_NAME 0x0F +#define BMC_FAS_FUNC_ID_MIDI_TEMPO_BEAT 0x10 +#define BMC_FAS_FUNC_ID_BLOCK_CHANNEL 0x11 +#define BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL 0x17 +#define BMC_FAS_FUNC_ID_RESYNC 0x21 +#define BMC_FAS_FUNC_ID_LOOPER 0x23 +#define BMC_FAS_FUNC_ID_SCENE_NUMBER 0x29 +#define BMC_FAS_FUNC_ID_SET_PRESET_NUMBER 0x3C +#define BMC_FAS_FUNC_ID_DISCONNECT 0x42 +#define BMC_FAS_FUNC_ID_GENERAL_PURPOSE 0x64 class BMCFas { private: BMCMidi& midi; + BMCGlobals& globals; BMCFlags flags; BMCTimer findDeviceTimer; BMCTimer startSyncTimer; BMCTimer resyncTimer; BMCTimer tunerTimeout; + BMCTimer looperTimeout; + BMCTimer connectionLost; BMCFasData device; BMCTunerData tunerData; uint8_t attempts = 0; -#ifdef BMC_DEBUG - void debugPrintFasMessageInfo(BMCMidiMessage& message){ - char str[50]; - switch(message.sysex[5]){ - case BMC_FAS_FUNC_ID_BLOCK_PARAM: strcpy(str, "BMC_FAS_FUNC_ID_BLOCK_PARAM");break; - case BMC_FAS_FUNC_ID_FIRMWARE: strcpy(str, "BMC_FAS_FUNC_ID_FIRMWARE");break; - case BMC_FAS_FUNC_ID_TUNER_INFO: strcpy(str, "BMC_FAS_FUNC_ID_TUNER_INFO");break; - case BMC_FAS_FUNC_ID_BLOCKS_DATA: strcpy(str, "BMC_FAS_FUNC_ID_BLOCKS_DATA");break; - case BMC_FAS_FUNC_ID_PRESET_NAME: strcpy(str, "BMC_FAS_FUNC_ID_PRESET_NAME");break; - case BMC_FAS_FUNC_ID_MIDI_TEMPO_BEAT: strcpy(str, "BMC_FAS_FUNC_ID_MIDI_TEMPO_BEAT");break; - case BMC_FAS_FUNC_ID_BLOCK_XY: strcpy(str, "BMC_FAS_FUNC_ID_BLOCK_XY");break; - case BMC_FAS_FUNC_ID_CPU: strcpy(str, "BMC_FAS_FUNC_ID_CPU");break; - case BMC_FAS_FUNC_ID_GET_PRESET_NUMBER: strcpy(str, "BMC_FAS_FUNC_ID_GET_PRESET_NUMBER");break; - case BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL: strcpy(str, "BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL");break; - case BMC_FAS_FUNC_ID_RESYNC: strcpy(str, "BMC_FAS_FUNC_ID_RESYNC");break; - case BMC_FAS_FUNC_ID_LOOPER: strcpy(str, "BMC_FAS_FUNC_ID_LOOPER");break; - case BMC_FAS_FUNC_ID_SCENE_NUMBER: strcpy(str, "BMC_FAS_FUNC_ID_SCENE_NUMBER");break; - case BMC_FAS_FUNC_ID_SET_PRESET_NUMBER: strcpy(str, "BMC_FAS_FUNC_ID_SET_PRESET_NUMBER");break; - case BMC_FAS_FUNC_ID_DISCONNECT: strcpy(str, "BMC_FAS_FUNC_ID_DISCONNECT");break; - case BMC_FAS_FUNC_ID_GENERAL_PURPOSE: strcpy(str, "BMC_FAS_FUNC_ID_GENERAL_PURPOSE");break; - default: strcpy(str, "UNUSED"); - } - BMC_PRINTLN("--> FAS MESSAGE RECEIVED, ID:", message.sysex[5], str, "SIZE:", message.size()); - } - bool isValidFasFunction(uint8_t funcId){ - switch(funcId){ - case BMC_FAS_FUNC_ID_BLOCK_PARAM: - case BMC_FAS_FUNC_ID_FIRMWARE: - case BMC_FAS_FUNC_ID_BLOCKS_DATA: - case BMC_FAS_FUNC_ID_TUNER_INFO: - case BMC_FAS_FUNC_ID_PRESET_NAME: - case BMC_FAS_FUNC_ID_BLOCK_XY: - case BMC_FAS_FUNC_ID_CPU: - case BMC_FAS_FUNC_ID_GET_PRESET_NUMBER: - case BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL: - case BMC_FAS_FUNC_ID_RESYNC: - case BMC_FAS_FUNC_ID_LOOPER: - case BMC_FAS_FUNC_ID_SCENE_NUMBER: - case BMC_FAS_FUNC_ID_SET_PRESET_NUMBER: - case BMC_FAS_FUNC_ID_DISCONNECT: - case BMC_FAS_FUNC_ID_GENERAL_PURPOSE: - return true; - } - return false; + bool isAxe3(uint8_t id){ + return (id==BMC_FAS_DEVICE_ID_AXE_FX_III || id==BMC_FAS_DEVICE_ID_FM3); } + +#ifdef BMC_DEBUG + String debugPrintDeviceName(uint8_t id); + String debugPrintPreset(); + void debugPrintFasMessageInfo(BMCMidiMessage& message); + bool isValidFasFunction(uint8_t funcId); #endif -public: - BMCFas(BMCMidi& t_midi):midi(t_midi){ - device.reset(); - flags.on(BMC_FAS_FLAG_DEVICE_SEARCH); - findDeviceTimer.start(3000); - BMC_PRINTLN("FAS Sync Version 1.0"); - } - // PUBLIC - void update(){ - // search for response from FAS device, try up to 10 times every 5 seconds - // this message will be broadcast on all MIDI ports except USB and BLE (if available) - if(flags.read(BMC_FAS_FLAG_DEVICE_SEARCH)){ - if(attempts<10 && findDeviceTimer.complete()){ - BMC_PRINTLN(">> Looking for FAS Device <<"); - sendDeviceSearch(); - findDeviceTimer.start(5000); - attempts++; - } - return; - } - if(tunerTimeout.complete()){ - flags.off(BMC_FAS_FLAG_TUNER_ACTIVE); - if(midi.callback.fasTunerStateChange){ - midi.callback.fasTunerStateChange(false); - } - BMC_PRINTLN("--> FAS TUNER OFF"); - } - if(!connected()&&!syncing()){ - if(startSyncTimer.complete()){ - startSyncTimer.start(3000); - requestFirmware(); - } - } - presetSyncQueue(); + void timeConnectionStart(){ + flags.off(BMC_FAS_FLAG_CONNECTION_LOST); + connectionLost.start(BMC_FAS_CONNECTION_LOST_TIMEOUT); } + +public: + BMCFas(BMCMidi& t_midi, BMCGlobals& t_globals); + + void begin(); + void update(); + bool incoming(BMCMidiMessage& message); + void setSyncedParameter(uint8_t slot, uint8_t block, uint8_t parameter){ device.paramSet(slot, block, parameter); } @@ -182,9 +150,7 @@ class BMCFas { device.paramReceived(block, param, value); controlBlockParameter(block, param, value, true); } - bool incoming(BMCMidiMessage& message){ - return parseincoming(message); - } + bool isTunerActive(){ return flags.read(BMC_FAS_FLAG_TUNER_ACTIVE); } @@ -194,18 +160,114 @@ class BMCFas { void getTunerData(BMCTunerData& buff){ buff = tunerData; } - void toggleTuner(){ - sendControlChange(BMC_FAS_CC_TUNER, isTunerActive()?0:127); - } - void tapTempo(){ - sendControlChange(BMC_FAS_CC_TAP_TEMPO, 127); - } bool connected(){ return flags.read(BMC_FAS_FLAG_CONNECTED); } bool syncing(){ return flags.read(BMC_FAS_FLAG_SYNCING); } + bool looperEnable(bool value){ + if(value!=device.getLooperState()){ + device.looper.changeState(value); + sendBasicSysEx(BMC_FAS_FUNC_ID_LOOPER, value?1:0); + return true; + } + return false; + } + void looperControl(uint8_t cmd){ + if(!connected()){ + return; + } + switch(cmd){ + case BMC_FAS_LOOPER_CONTROL_STOP:{ + if(device.looper.getStates()>0){ + sendControlChange(BMC_FAS_CC_LOOPER_RECORD, 0); + sendControlChange(BMC_FAS_CC_LOOPER_PLAY, 0); + sendControlChange(BMC_FAS_CC_LOOPER_DUB, 0); + } + break; + } + case BMC_FAS_LOOPER_CONTROL_RECORD:{ + uint8_t state = device.looper.getStates(BMC_FAS_LOOPER_STATE_RECORDING)?0:127; + sendControlChange(BMC_FAS_CC_LOOPER_RECORD, state); + break; + } + case BMC_FAS_LOOPER_CONTROL_PLAY:{ + uint8_t state = device.looper.getStates(BMC_FAS_LOOPER_STATE_PLAYING)?0:127; + sendControlChange(BMC_FAS_CC_LOOPER_PLAY, state); + break; + } + case BMC_FAS_LOOPER_CONTROL_ONCE:{ + uint8_t state = device.looper.getStates(BMC_FAS_LOOPER_STATE_ONCE)?0:127; + sendControlChange(BMC_FAS_CC_LOOPER_ONCE, state); + break; + } + case BMC_FAS_LOOPER_CONTROL_OVERDUB:{ + uint8_t state = device.looper.getStates(BMC_FAS_LOOPER_STATE_OVERDUBBING)?0:127; + sendControlChange(BMC_FAS_CC_LOOPER_DUB, state); + break; + } + case BMC_FAS_LOOPER_CONTROL_REVERSE:{ + uint8_t state = device.looper.getStates(BMC_FAS_LOOPER_STATE_REVERSED)?0:127; + sendControlChange(BMC_FAS_CC_LOOPER_REVERSE, state); + break; + } + case BMC_FAS_LOOPER_CONTROL_HALF:{ + uint8_t state = device.looper.getStates(BMC_FAS_LOOPER_STATE_HALF)?0:127; + sendControlChange(BMC_FAS_CC_LOOPER_HALF, state); + break; + } + case BMC_FAS_LOOPER_CONTROL_UNDO:{ + uint8_t state = device.looper.getStates(BMC_FAS_LOOPER_STATE_UNDO)?0:127; + sendControlChange(BMC_FAS_CC_LOOPER_UNDO, state); + break; + } + case BMC_FAS_LOOPER_CONTROL_REC_PLAY_DUB: + case BMC_FAS_LOOPER_CONTROL_REC_DUB_PLAY:{ + //cmd==BMC_FAS_LOOPER_CONTROL_REC_DUB_PLAY + if(device.looper.getStates()==0){ + if(flags.read(BMC_FAS_FLAG_LOOPER_TRACK_AVAILABLE)){ + sendControlChange(BMC_FAS_CC_LOOPER_PLAY, 127); + } else { + sendControlChange(BMC_FAS_CC_LOOPER_RECORD, 127); + } + } else if(device.looper.getStates(BMC_FAS_LOOPER_STATE_RECORDING)){ + if(cmd==BMC_FAS_LOOPER_CONTROL_REC_DUB_PLAY){ + sendControlChange(BMC_FAS_CC_LOOPER_PLAY, 127); + sendControlChange(BMC_FAS_CC_LOOPER_DUB, 127); + } else { + sendControlChange(BMC_FAS_CC_LOOPER_PLAY, 127); + } + } else if(device.looper.getStates(BMC_FAS_LOOPER_STATE_PLAYING)){ + if(device.looper.getStates(BMC_FAS_LOOPER_STATE_OVERDUBBING)){ + sendControlChange(BMC_FAS_CC_LOOPER_DUB, 0); + } else { + sendControlChange(BMC_FAS_CC_LOOPER_DUB, 127); + } + } + break; + } + case BMC_FAS_LOOPER_CONTROL_CLEAR:{ + flags.off(BMC_FAS_FLAG_LOOPER_TRACK_AVAILABLE); + if(device.looper.getStates()>0){ + looperControl(BMC_FAS_LOOPER_CONTROL_STOP); + } + break; + } + } + } + bool looperGetState(){ + return device.looper.isEnabled(); + } + bool looperStatus(uint8_t cmd=255){ + return device.looper.getStates(cmd); + } + bool looperTrackRecorded(){ + return flags.read(BMC_FAS_FLAG_LOOPER_TRACK_AVAILABLE); + } + bool looperStopped(){ + return device.looper.getStates()==0; + } bool connect(){ if(connected()){ return false; @@ -221,13 +283,16 @@ class BMCFas { return false; } BMC_PRINTLN("--> FAS Disconnect"); - sendDisconnect(); + // turn off looper stream if it's on + looperEnable(false); + sendBasicSysEx(BMC_FAS_FUNC_ID_DISCONNECT); device.reset(); flags.reset(); findDeviceTimer.stop(); startSyncTimer.stop(); resyncTimer.stop(); tunerTimeout.stop(); + looperTimeout.stop(); tunerData.reset(); device.reset(); attempts = 0; @@ -272,6 +337,15 @@ class BMCFas { bool isBlockX(uint8_t blockId){ return !isBlockY(blockId); } + + // Toggle the tuner state + void toggleTuner(){ + sendControlChange(BMC_FAS_CC_TUNER, isTunerActive()?0:127); + } + // send a tap tempo cc + void tapTempo(){ + sendControlChange(BMC_FAS_CC_TAP_TEMPO, 127); + } void sendSetTempo(uint16_t tempo){ if(tempo<30 || tempo>250){ return; @@ -408,84 +482,6 @@ class BMCFas { true // should it trigger MIDI Out activity ); } - - // parse incoming Sysex Messages - bool parseincoming(BMCMidiMessage& message){ - if(flags.read(BMC_FAS_FLAG_DEVICE_SEARCH) && findDeviceTimer.active()){ - if(isFractMessage(message)){ - device.setIdAndPort(message.sysex[4], message.getPort()); - flags.off(BMC_FAS_FLAG_DEVICE_SEARCH); - attempts = 0; - findDeviceTimer.stop(); - startSyncTimer.start(250); - BMC_PRINTLN("*** FAS FOUND DEVICE", device.getId(), device.getPort()); - } - return false; - } - if(!isFractMessage(message) || !isValidPort(message.getPort())){ - return false; - } - -#ifdef BMC_DEBUG - debugPrintFasMessageInfo(message); -#endif - - // messages that don't have a Checksum - switch(message.sysex[5]){ - case BMC_FAS_FUNC_ID_MIDI_TEMPO_BEAT: - receivedTempoBeat(message); - return true; - case BMC_FAS_FUNC_ID_TUNER_INFO: - receivedTunerInfo(message); - return true; - case BMC_FAS_FUNC_ID_LOOPER: - receivedLooperInfo(message); - return true; - case BMC_FAS_FUNC_ID_BLOCKS_DATA: - receivedBlocksStates(message); - return true; - } - // the rest require a valid CRC - if(isValidFasFunction(message.sysex[5]) && !message.validateChecksum()){ - BMC_PRINTLN("!!! FAS Received Bad CRC !!!"); - return false; - } - switch(message.sysex[5]){ - case BMC_FAS_FUNC_ID_FIRMWARE: - receivedFirmware(message); - return true; - case BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL: - receivedMidiChannel(message); - return true; - case BMC_FAS_FUNC_ID_BLOCK_PARAM: - receivedBlockParameter(message); - return true; - case BMC_FAS_FUNC_ID_CPU: - receivedCPU(message); - return true; - case BMC_FAS_FUNC_ID_PRESET_NAME: - receivedPresetName(message); - return true; - case BMC_FAS_FUNC_ID_GET_PRESET_NUMBER: - receivedPresetNumber(message); - return true; - case BMC_FAS_FUNC_ID_SCENE_NUMBER: - receivedSceneNumber(message); - return true; - case BMC_FAS_FUNC_ID_RESYNC: - device.paramReset(); - BMC_WARN("FAS RE-SYNC RECEIVED"); - receivedReSync(true); - return true; - case BMC_FAS_FUNC_ID_BLOCK_XY: - receivedBlockXY(message); - return true; - case BMC_FAS_FUNC_ID_GENERAL_PURPOSE: - receivedGeneralPurpose(message); - return true; - } - return false; - } // received the resync message void receivedReSync(bool quick=false){ if(resyncTimer.active()){ @@ -495,260 +491,23 @@ class BMCFas { flags.on(BMC_FAS_FLAG_SYNC_EXPECTING_PRESET); flags.on(BMC_FAS_FLAG_SYNC_EXPECTING_PRESET_NAME); flags.on(BMC_FAS_FLAG_SYNC_EXPECTING_SCENE); - +/* if(device.id!=BMC_FAS_DEVICE_ID_AX8){ flags.off(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); } - +*/ + flags.on(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); flags.on(BMC_FAS_FLAG_SYNC_EXPECTING_PARAMETERS); // quick is only true when the actual message is reaceived from the FAS device // this function is also used when other messages are received in which case // they request updated data, in that case we wait a little longer before resyncing // this is in case we're changing scenes quickly etc. if(quick){ - resyncTimer.start(500); + resyncTimer.start(250); } else { - resyncTimer.start(1000); - } - } - // General Purpose - void receivedGeneralPurpose(BMCMidiMessage& message){ - if(!isFractMessage(message, 10)){ - return; - } - BMC_PRINTLN("--> FAS GENERAL PURPOSE FUNC:", message.get7Bits(6),"CODE:", message.get7Bits(7)); - } - // Looper - void receivedLooperInfo(BMCMidiMessage& message){ - BMC_PRINTLN("--> FAS Looper Info", message.sysex[6], message.sysex[7]); - device.looper.set(message.sysex[6], message.sysex[7]); - } - // Tuner - void receivedTunerInfo(BMCMidiMessage& message){ - if(connected() && isFractMessage(message, 10)){ - tunerData.note = message.sysex[6]; - tunerData.stringNumber = message.sysex[7]; - tunerData.pitch = map((message.sysex[8]&0x7F), 0, 127, -63, 64); - tunerNote(tunerData.note, tunerData.noteName); - tunerTimeout.start(250); - if(!flags.read(BMC_FAS_FLAG_TUNER_ACTIVE)){ - if(midi.callback.fasTunerStateChange){ - midi.callback.fasTunerStateChange(true); - } - flags.on(BMC_FAS_FLAG_TUNER_ACTIVE); - BMC_PRINTLN("--> FAS TUNER ON"); - } - if(midi.callback.fasTunerReceived){ - midi.callback.fasTunerReceived(tunerData); - } - } - } - // Tuner - void receivedTempoBeat(BMCMidiMessage& message){ - if(connected() && isFractMessage(message, 5)){ - flags.on(BMC_FAS_FLAG_TEMPO_RECEIVED); + resyncTimer.start(750); } } - - // used when firmware is received - void receivedFirmware(BMCMidiMessage& message){ - if(!isFractMessage(message, 10) || connected() || syncing()){ - return; - } - device.version = (message.get7Bits(6)<<8) | message.get7Bits(7); - flags.on(BMC_FAS_FLAG_SYNCING); - startSyncTimer.stop(); - requestMidiChannel(); - BMC_PRINTLN("--> FAS FIRMWARE RECEIVED:", device.version); - } - // MIDI Channel received - void receivedMidiChannel(BMCMidiMessage& message){ - //BMC_PRINTLN("receivedMidiChannel message.size()", message.size()); - if(!isFractMessage(message, 9) || connected() || !syncing()){ - return; - } - device.channel = message.get7Bits(6)+1; -#ifdef BMC_FAS_DEBUG - BMC_PRINTLN("--> FAS CHANNEL RECEIVED:", device.channel); -#endif - flags.on(BMC_FAS_FLAG_CONNECTED); - flags.on(BMC_FAS_FLAG_CONNECTION_CHANGED); - flags.off(BMC_FAS_FLAG_SYNCING); - if(midi.callback.fasConnection){ - midi.callback.fasConnection(true); - } - receivedReSync(); - flags.on(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); - } - void receivedBlocksStates(BMCMidiMessage& message){ - if(!isFractMessage(message, 5)){ - return; - } - // crc used to know if there's a change within the blocks - uint8_t crc = device.blocks.getCRC(); - device.blocks.reset(); - for(uint8_t i = 6; i < message.size()-2; i+=5){ - uint32_t block = message.get32BitsLSBFirst(i); - bool isEngaged = bitRead(block, 0); - bool isX = bitRead(block, 1); - uint8_t blockId = (block>>24) & 0xFF; - -#ifdef BMC_FAS_DEBUG - BMC_PRINTLN("--> Received Blocks"); - uint8_t bypassCC = (block>>8) & 0x7F; - uint8_t xyCC = (block>>16) & 0x7F; - if(blockId>=100 && blockId<=170){ - BMC_PRINTLN("--> Block", blocksGlobalData[blockId-100].name, blockId, bypassCC, xyCC, isEngaged?"ON":"OFF", isX?"X":"Y"); - } -#endif - device.blocks.set(blockId, isEngaged, !isX); - } - - if(midi.callback.fasBlocksChange){ - device.blocks.createCRC(); - if(crc!=device.blocks.getCRC()){ - midi.callback.fasBlocksChange(); - } - } - - if(flags.toggleIfTrue(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS)){ - resyncTimer.start(BMC_FAS_RESYNC_QUEUE_TIMEOUT); - } - } - // received the current scene number - void receivedSceneNumber(BMCMidiMessage& message){ - if(!isFractMessage(message, 9)){ - return; - } - uint8_t value = message.get7Bits(6); - if(device.scene!=value){ - flags.on(BMC_FAS_FLAG_SYNC_PARAMETER_SYNC_BEGIN); - requestSyncParameters(); - if(midi.callback.fasSceneChange){ - midi.callback.fasSceneChange(value); - } - } - - device.scene = value; -#ifdef BMC_FAS_DEBUG - BMC_PRINTLN("--> FAS SCENE NUMBER", device.scene); -#endif - - if(flags.toggleIfTrue(BMC_FAS_FLAG_SYNC_EXPECTING_SCENE)){ - resyncTimer.start(BMC_FAS_RESYNC_QUEUE_TIMEOUT); - if(device.id==BMC_FAS_DEVICE_ID_AX8){ - flags.off(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); - } - } else { - if(device.id!=BMC_FAS_DEVICE_ID_AX8){ - flags.off(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); - resyncTimer.start(BMC_FAS_RESYNC_QUEUE_TIMEOUT); - } - } - } - - // received CPU usage - void receivedCPU(BMCMidiMessage& message){ - if(!isFractMessage(message, 9)){ - return; - } - uint8_t value = message.get7Bits(6); -#ifdef BMC_FAS_DEBUG - BMC_PRINTLN("--> FAS CPU", value, "%"); -#endif - if(midi.callback.fasCpuReceived){ - midi.callback.fasCpuReceived(value); - } - } - // received a block parameter value - void receivedBlockParameter(BMCMidiMessage& message){ - if(!isFractMessage(message, 15)){ - return; - } - uint8_t blockId = message.get8BitsLSBFirst(6); - uint8_t paramId = message.get8BitsLSBFirst(8); - uint16_t value = message.get16BitsLSBFirst(10); - if(paramId==255){ - device.blocks.set(blockId, (value==0), device.blocks.isY(blockId)); - if(midi.callback.fasBlocksChange && device.blocks.isEngaged(blockId) != (value==0)){ - midi.callback.fasBlocksChange(); - } -#ifdef BMC_FAS_DEBUG - BMC_PRINTLN("--> FAS BLOCK Bypass Received", blockId, value==0?"engaged":"bypassed"); -#endif - } else { - device.paramReceived(blockId, paramId, value); - requestSyncParameters(); - - if(midi.callback.fasBlockParameterReceived){ - uint8_t strLen = message.size()-(18+2); - char str[strLen] = ""; - message.getStringFromSysEx(18, str, strLen); - midi.callback.fasBlockParameterReceived(blockId, paramId, value, str, strLen); -#ifdef BMC_FAS_DEBUG - BMC_PRINTLN("--> FAS Block Parameter Received: Block:", blockId, - "Param:", paramId, "Value:", value, "Str:", str, strLen); -#endif - } - } - } - // received the block XY state after it was changed by BMC - void receivedBlockXY(BMCMidiMessage& message){ - if(!isFractMessage(message, 11)){ - return; - } - uint8_t blockId = message.get8BitsLSBFirst(6); - uint8_t xy = message.get7Bits(8); - device.blocks.set(blockId, device.blocks.isEngaged(blockId), xy); - if(midi.callback.fasBlocksChange && device.blocks.isY(blockId)!=xy){ - midi.callback.fasBlocksChange(); - } -#ifdef BMC_FAS_DEBUG - BMC_PRINTLN("--> FAS BLOCK XY Received", blockId, xy?"Y":"X"); -#endif - } - // received the current preset number - void receivedPresetNumber(BMCMidiMessage& message){ - if(!isFractMessage(message, 10)){ - return; - } - uint16_t value = message.get14Bits(6); - - if(device.preset!=value){ - device.paramReset(); - if(midi.callback.fasPresetChange){ - midi.callback.fasPresetChange(value); - } - } - device.preset = value; - BMC_PRINTLN("--> FAS PRESET NUMBER", device.preset); - if(flags.toggleIfTrue(BMC_FAS_FLAG_SYNC_EXPECTING_PRESET)){ - resyncTimer.start(1000); - } else { - receivedReSync(); - flags.off(BMC_FAS_FLAG_SYNC_EXPECTING_PRESET); - flags.on(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS); - resyncTimer.start(1000); - } - } - // received the current preset name - void receivedPresetName(BMCMidiMessage& message){ - if(!isFractMessage(message, 30)){ - return; - } - strcpy(device.presetName, ""); - message.getStringFromSysEx(6, device.presetName, 32); -#ifdef BMC_FAS_DEBUG - BMC_PRINTLN("--> FAS PRESET NAME", device.presetName,"size:", message.size()); -#endif - if(flags.toggleIfTrue(BMC_FAS_FLAG_SYNC_EXPECTING_PRESET_NAME)){ - resyncTimer.start(BMC_FAS_RESYNC_QUEUE_TIMEOUT); - } - if(midi.callback.fasPresetName){ - midi.callback.fasPresetName(device.presetName); - } - } - bool requestSyncParameters(){ if(!flags.read(BMC_FAS_FLAG_SYNC_PARAMETER_SYNC_BEGIN)){ return false; @@ -770,45 +529,17 @@ class BMCFas { flags.off(BMC_FAS_FLAG_SYNC_PARAMETER_SYNC_BEGIN); return false; } - - - - - - - - - - // GET THE FIRMWARE, also activates the 0x21 message sent by the device when - // there's a change - void requestFirmware(){ - sendSimpleSysEx(BMC_FAS_FUNC_ID_FIRMWARE); - } - // Disconnect BMC from fractal device, this should only be sent if BMC_FAS_FUNC_ID_FIRMWARE - // was sent first - void sendDisconnect(){ - sendSimpleSysEx(BMC_FAS_FUNC_ID_DISCONNECT); - } - // MIDI CHANNEL OF THE DEVICE - void requestMidiChannel(){ - sendSimpleSysEx(BMC_FAS_FUNC_ID_GET_MIDI_CHANNEL); - } - // PARSE THE BLOCKS DATA, these are the blocks loaded into the preset - // and the bypass and X/Y of the block - void requestBlocksData(){ - sendSimpleSysEx(BMC_FAS_FUNC_ID_BLOCKS_DATA); - } // Request the current Scene Number bool requestScene(){ return controlScene(0x7F); } // PRESET NUMBER void requestPresetNumber(){ - sendSimpleSysEx(BMC_FAS_FUNC_ID_GET_PRESET_NUMBER); + sendBasicSysEx(BMC_FAS_FUNC_ID_GET_PRESET_NUMBER); } // PRESET NAME void requestPresetName(){ - sendSimpleSysEx(BMC_FAS_FUNC_ID_PRESET_NAME); + sendBasicSysEx(BMC_FAS_FUNC_ID_PRESET_NAME); } @@ -822,7 +553,7 @@ class BMCFas { } else if(flags.read(BMC_FAS_FLAG_SYNC_EXPECTING_PRESET_NAME)){ requestPresetName(); } else if(flags.read(BMC_FAS_FLAG_SYNC_EXPECTING_BLOCKS)){ - requestBlocksData(); + sendBasicSysEx(BMC_FAS_FUNC_ID_BLOCKS_DATA); } else if(flags.read(BMC_FAS_FLAG_SYNC_EXPECTING_SCENE)){ requestScene(); } else if(flags.read(BMC_FAS_FLAG_SYNC_EXPECTING_PARAMETERS)){ @@ -845,6 +576,19 @@ class BMCFas { } } + void sendCustomFasSysEx(uint8_t funcId, uint8_t * bytes, uint8_t len, bool crc=true){ + if(!connected()){ + return; + } + BMCMidiMessage message; + prepSysEx(message, funcId); + + for(uint8_t i=0;i7 && value!=0x7F)){ return false; @@ -919,23 +666,6 @@ class BMCFas { } return 0; } - - void tunerNote(uint8_t note, char* str){ - switch(note){ - case 0: strcpy(str, "A "); break; - case 1: strcpy(str, "Bb"); break; - case 2: strcpy(str, "B "); break; - case 3: strcpy(str, "C "); break; - case 4: strcpy(str, "C#"); break; - case 5: strcpy(str, "D "); break; - case 6: strcpy(str, "Eb"); break; - case 7: strcpy(str, "E "); break; - case 8: strcpy(str, "F "); break; - case 9: strcpy(str, "F#"); break; - case 10: strcpy(str, "G "); break; - case 11: strcpy(str, "G#"); break; - } - } void findBlockData(uint8_t id, BMCFasBlocks& block){ if(!isValidBlockId(id)){ return; @@ -958,8 +688,6 @@ class BMCFas { } return blocksGlobalData[id-100].index; } - - // this is the data for blocks, this is used to determine if // block is available on the device loaded then to either bypass/XY the block // total: 71, totalUsable: 64 @@ -1029,7 +757,22 @@ class BMCFas { // 0x10 Axe-Fx III return (id==3 || (id>=6 && id<=8)); } - + void tunerNote(uint8_t note, char* str){ + switch(note){ + case 0: strcpy(str, "A "); break; + case 1: strcpy(str, "Bb"); break; + case 2: strcpy(str, "B "); break; + case 3: strcpy(str, "C "); break; + case 4: strcpy(str, "C#"); break; + case 5: strcpy(str, "D "); break; + case 6: strcpy(str, "Eb"); break; + case 7: strcpy(str, "E "); break; + case 8: strcpy(str, "F "); break; + case 9: strcpy(str, "F#"); break; + case 10: strcpy(str, "G "); break; + case 11: strcpy(str, "G#"); break; + } + } // this is the data for blocks, this is used to determine if // block is available on the device loaded then to either bypass/XY the block // total: 71, totalUsable: 64 diff --git a/src/utility/BMC-Helix.h b/src/sync/helix/BMC-Helix.h similarity index 100% rename from src/utility/BMC-Helix.h rename to src/sync/helix/BMC-Helix.h diff --git a/src/addon/BMC-Kemper.h b/src/sync/kemp/BMC-Kemp.h similarity index 98% rename from src/addon/BMC-Kemper.h rename to src/sync/kemp/BMC-Kemp.h index cb56ee2..93baef9 100644 --- a/src/addon/BMC-Kemper.h +++ b/src/sync/kemp/BMC-Kemp.h @@ -8,7 +8,7 @@ This class is not Tested, if you own a Kemper profiler please help development visit RoxXxtar.com/contact and message me. - Currently i'm trying to get my hands on a Kemper for testing once I do I will + Currently i'm trying to get my hands on a Kemper for testing once I do I will implement the whole thing ;-) 1: 0xF0 diff --git a/src/utility/BMC-Callbacks.h b/src/utility/BMC-Callbacks.h index 846b48c..0d2b159 100644 --- a/src/utility/BMC-Callbacks.h +++ b/src/utility/BMC-Callbacks.h @@ -96,6 +96,8 @@ class BMCCallbacks { fasBlocksChange = 0; fasTunerStateChange = 0; fasTunerReceived = 0; + fasLooperStateChange = 0; + fasLooperReceived = 0; fasCpuReceived = 0; fasBlockParameterReceived = 0; #endif @@ -200,6 +202,8 @@ class BMCCallbacks { void (*fasBlocksChange)(); void (*fasTunerStateChange)(bool state); void (*fasTunerReceived)(BMCTunerData& data); + void (*fasLooperStateChange)(bool state); + void (*fasLooperReceived)(uint8_t state, uint8_t position); void (*fasCpuReceived)(uint8_t n); void (*fasBlockParameterReceived)(uint8_t block, uint8_t param, uint16_t value, char* str, uint8_t strLen); diff --git a/src/utility/BMC-Def.h b/src/utility/BMC-Def.h index d468868..492fb37 100644 --- a/src/utility/BMC-Def.h +++ b/src/utility/BMC-Def.h @@ -196,6 +196,9 @@ #define digitalIsLow(pin) (digitalReadFast(pin)==LOW) #endif +const char bmcAlphabet[26] = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}; + + #define BMC_IS_EVEN(n) ((n % 2 == 0)) #define BMC_IS_ODD(n) ((n % 2 != 0)) #define BMC_GET_MIDI_STATUS(value) ( (uint8_t) ((value&0xFF)<0xF0) ? (value & 0xF0) : value ) @@ -836,6 +839,27 @@ #define BMC_BEATBUDDY_CMD_TRANS_PREV 226 #define BMC_BEATBUDDY_CMD_TRANS_NEXT 227 +#define BMC_FAS_LOOPER_STATE_RECORDING 0 +#define BMC_FAS_LOOPER_STATE_PLAYING 1 +#define BMC_FAS_LOOPER_STATE_ONCE 2 +#define BMC_FAS_LOOPER_STATE_OVERDUBBING 3 +#define BMC_FAS_LOOPER_STATE_REVERSED 4 +#define BMC_FAS_LOOPER_STATE_HALF 5 +#define BMC_FAS_LOOPER_STATE_UNDO 6 + +#define BMC_FAS_LOOPER_CONTROL_STOP 0 +#define BMC_FAS_LOOPER_CONTROL_RECORD 1 +#define BMC_FAS_LOOPER_CONTROL_PLAY 2 +#define BMC_FAS_LOOPER_CONTROL_ONCE 3 +#define BMC_FAS_LOOPER_CONTROL_OVERDUB 4 +#define BMC_FAS_LOOPER_CONTROL_REVERSE 5 +#define BMC_FAS_LOOPER_CONTROL_HALF 6 +#define BMC_FAS_LOOPER_CONTROL_UNDO 7 +#define BMC_FAS_LOOPER_CONTROL_REC_PLAY_DUB 8 +#define BMC_FAS_LOOPER_CONTROL_REC_DUB_PLAY 9 +#define BMC_FAS_LOOPER_CONTROL_CLEAR 10 + + // Line 6 Helix ID per Device #define BMC_HELIX_ID 0 #define BMC_HELIX_FX_ID 1 diff --git a/src/utility/BMC-Globals.h b/src/utility/BMC-Globals.h index 58b6df5..834e0c5 100644 --- a/src/utility/BMC-Globals.h +++ b/src/utility/BMC-Globals.h @@ -32,6 +32,7 @@ #define BMC_GLOBALS_DEBUG_FLAG_MIDI_OUT 5 #define BMC_GLOBALS_DEBUG_FLAG_MIDI_OUT_WITH_CLOCK 6 #define BMC_GLOBALS_DEBUG_FLAG_BUTTONS 7 +#define BMC_GLOBALS_DEBUG_FLAG_FAS 8 // https://github.com/mpflaga/Arduino-MemoryFree @@ -187,6 +188,12 @@ class BMCGlobals { bool getButtonsDebug(){ return debugFlags.read(BMC_GLOBALS_DEBUG_FLAG_BUTTONS); } + bool toggleFasDebug(){ + return debugFlags.toggle(BMC_GLOBALS_DEBUG_FLAG_FAS); + } + bool getFasDebug(){ + return debugFlags.read(BMC_GLOBALS_DEBUG_FLAG_FAS); + } #endif @@ -194,7 +201,7 @@ class BMCGlobals { private: BMCFlags flags; #ifdef BMC_DEBUG - BMCFlags debugFlags; + BMCFlags debugFlags; #endif uint32_t loopsPerSecond = 0; uint32_t lastLoopsPerSecond = 0; diff --git a/src/utility/BMC-Settings.h b/src/utility/BMC-Settings.h index 6abae0f..e2f3bf4 100644 --- a/src/utility/BMC-Settings.h +++ b/src/utility/BMC-Settings.h @@ -29,6 +29,7 @@ bit 6 getMidiRealTimeBlockInput bit 7 getMidiRealTimeBlockOutput bit 8 getTyperOffSet + bit 9 @@ -157,6 +158,8 @@ class BMCSettings { + + // DATA BYTES /* // moved to settins.data[2] to increase value