Add SPP (Song Position Pointer) detection - uses SPP on START to set correct tick position. Add beats_per_bar (default 4) with button combo: hold 10 + press 1=4/4, 2=3/4, 3=6/4
This commit is contained in:
@@ -4,6 +4,8 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
extern volatile uint32_t midi_tick_count;
|
extern volatile uint32_t midi_tick_count;
|
||||||
|
extern volatile uint16_t last_spp_position;
|
||||||
|
extern volatile bool spp_valid;
|
||||||
|
|
||||||
struct MidiEvent {
|
struct MidiEvent {
|
||||||
enum Type {
|
enum Type {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "app_task.h"
|
#include "app_task.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
extern volatile uint8_t beats_per_bar;
|
||||||
|
|
||||||
AppTask::AppTask(LedStub* led, SwitchStub* sw, UsbMidiTransport* midi)
|
AppTask::AppTask(LedStub* led, SwitchStub* sw, UsbMidiTransport* midi)
|
||||||
: led_driver(led), switch_driver(sw), midi_transport(midi) {
|
: led_driver(led), switch_driver(sw), midi_transport(midi) {
|
||||||
|
|
||||||
@@ -124,6 +126,18 @@ void AppTask::process_midi_event(const MidiEvent& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AppTask::process_switch_event(uint8_t switch_id, bool pressed) {
|
void AppTask::process_switch_event(uint8_t switch_id, bool pressed) {
|
||||||
|
// Time signature combo: hold pad 10 (switch 9) + press pad 1/2/3
|
||||||
|
if (switch_id <= 2 && switch_driver->is_pressed(9)) {
|
||||||
|
if (pressed) {
|
||||||
|
switch (switch_id) {
|
||||||
|
case 0: beats_per_bar = 4; Serial.println("[APP] Time sig: 4/4"); break;
|
||||||
|
case 1: beats_per_bar = 3; Serial.println("[APP] Time sig: 3/4"); break;
|
||||||
|
case 2: beats_per_bar = 6; Serial.println("[APP] Time sig: 6/4"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return; // suppress CC in combo mode
|
||||||
|
}
|
||||||
|
|
||||||
for (uint8_t i = 0; i < NUM_PADS; i++) {
|
for (uint8_t i = 0; i < NUM_PADS; i++) {
|
||||||
if (pad_mapping[i].physical_switch == switch_id) {
|
if (pad_mapping[i].physical_switch == switch_id) {
|
||||||
uint8_t channel = pad_mapping[i].midi_channel;
|
uint8_t channel = pad_mapping[i].midi_channel;
|
||||||
|
|||||||
+4
-3
@@ -22,6 +22,7 @@ AppTask controller(&led_driver, &switch_driver, &midi_transport);
|
|||||||
ExpressionPedal exp_pedal(4);
|
ExpressionPedal exp_pedal(4);
|
||||||
|
|
||||||
static volatile uint32_t flash_latency = 55; // ms delay to compensate for Loopy Pro rendering
|
static volatile uint32_t flash_latency = 55; // ms delay to compensate for Loopy Pro rendering
|
||||||
|
static volatile uint8_t beats_per_bar = 4; // change to match your project's time signature
|
||||||
|
|
||||||
TaskHandle_t midi_task_handle = NULL;
|
TaskHandle_t midi_task_handle = NULL;
|
||||||
|
|
||||||
@@ -45,8 +46,8 @@ void midi_task(void* parameter) {
|
|||||||
if (current_beat != last_beat) {
|
if (current_beat != last_beat) {
|
||||||
last_beat = current_beat;
|
last_beat = current_beat;
|
||||||
flash_start = now + flash_latency;
|
flash_start = now + flash_latency;
|
||||||
// Beat 1 = red on current_beat % 4 == 0, others = white
|
// Beat 1 = red on current_beat % beats_per_bar == 0, others = white
|
||||||
if (current_beat % 4 == 0) {
|
if (current_beat % beats_per_bar == 0) {
|
||||||
mux.set_led_color(6, 255, 0, 0);
|
mux.set_led_color(6, 255, 0, 0);
|
||||||
} else {
|
} else {
|
||||||
mux.set_led_color(6, 255, 255, 255);
|
mux.set_led_color(6, 255, 255, 255);
|
||||||
@@ -55,7 +56,7 @@ void midi_task(void* parameter) {
|
|||||||
|
|
||||||
if (flash_start > 0 && now >= flash_start) {
|
if (flash_start > 0 && now >= flash_start) {
|
||||||
mux.show();
|
mux.show();
|
||||||
uint32_t hold = (last_beat % 4 == 0) ? 100 : 50;
|
uint32_t hold = (last_beat % beats_per_bar == 0) ? 100 : 50;
|
||||||
uint32_t elapsed = now - flash_start;
|
uint32_t elapsed = now - flash_start;
|
||||||
if (elapsed >= hold) {
|
if (elapsed >= hold) {
|
||||||
mux.set_led_color(6, 20, 20, 20);
|
mux.set_led_color(6, 20, 20, 20);
|
||||||
|
|||||||
+24
-2
@@ -3,6 +3,8 @@
|
|||||||
#include "Adafruit_TinyUSB.h"
|
#include "Adafruit_TinyUSB.h"
|
||||||
|
|
||||||
volatile uint32_t midi_tick_count = 0;
|
volatile uint32_t midi_tick_count = 0;
|
||||||
|
volatile uint16_t last_spp_position = 0;
|
||||||
|
volatile bool spp_valid = false;
|
||||||
|
|
||||||
static Adafruit_USBD_MIDI usb_midi;
|
static Adafruit_USBD_MIDI usb_midi;
|
||||||
|
|
||||||
@@ -58,9 +60,29 @@ void UsbMidiTransport::update() {
|
|||||||
if (packet[1] == 0xF8) {
|
if (packet[1] == 0xF8) {
|
||||||
midi_tick_count++;
|
midi_tick_count++;
|
||||||
} else if (packet[1] == 0xFA) {
|
} else if (packet[1] == 0xFA) {
|
||||||
midi_tick_count = 0xFFFFFFFF;
|
if (spp_valid) {
|
||||||
Serial.println("[CLK] START - next F8 = tick 0");
|
midi_tick_count = last_spp_position * 6;
|
||||||
|
spp_valid = false;
|
||||||
|
Serial.printf("[CLK] START at SPP=%d -> tick %d\n", last_spp_position, midi_tick_count);
|
||||||
|
} else {
|
||||||
|
midi_tick_count = 0xFFFFFFFF;
|
||||||
|
Serial.println("[CLK] START (no SPP) - next F8 = tick 0");
|
||||||
|
}
|
||||||
|
} else if (packet[1] == 0xFB) {
|
||||||
|
// CONTINUE - same as START for our purposes
|
||||||
|
if (spp_valid) {
|
||||||
|
midi_tick_count = last_spp_position * 6;
|
||||||
|
spp_valid = false;
|
||||||
|
Serial.printf("[CLK] CONTINUE at SPP=%d -> tick %d\n", last_spp_position, midi_tick_count);
|
||||||
|
} else {
|
||||||
|
Serial.println("[CLK] CONTINUE (no SPP)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else if (cin == 0x03 && packet[1] == 0xF2) {
|
||||||
|
// Song Position Pointer
|
||||||
|
last_spp_position = (packet[3] << 7) | packet[2];
|
||||||
|
spp_valid = true;
|
||||||
|
Serial.printf("[CLK] SPP=%d\n", last_spp_position);
|
||||||
} else {
|
} else {
|
||||||
MidiEvent event;
|
MidiEvent event;
|
||||||
parse_midi_packet(packet, 4, event);
|
parse_midi_packet(packet, 4, event);
|
||||||
|
|||||||
Reference in New Issue
Block a user