Melody strategy
This commit is contained in:
parent
89f9821f5a
commit
1e475eeaa5
47
ArpStrategy.h
Normal file
47
ArpStrategy.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef ARP_STRATEGY_H
|
||||||
|
#define ARP_STRATEGY_H
|
||||||
|
|
||||||
|
#include "MelodyStrategy.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class ArpStrategy : public MelodyStrategy {
|
||||||
|
public:
|
||||||
|
void generate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes, int seed) override {
|
||||||
|
randomSeed(seed);
|
||||||
|
if (numScaleNotes == 0) return;
|
||||||
|
|
||||||
|
int currentNoteIndex = 0;
|
||||||
|
int octave = 4;
|
||||||
|
|
||||||
|
for (int i = 0; i < numSteps; i++) {
|
||||||
|
sequence[i].note = 12 * octave + scaleNotes[currentNoteIndex];
|
||||||
|
sequence[i].accent = (i % 4 == 0); // Accent on beat
|
||||||
|
sequence[i].tie = false;
|
||||||
|
|
||||||
|
currentNoteIndex++;
|
||||||
|
if (currentNoteIndex >= numScaleNotes) {
|
||||||
|
currentNoteIndex = 0;
|
||||||
|
octave++;
|
||||||
|
if (octave > 5) octave = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
randomSeed(micros());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mutate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes) override {
|
||||||
|
// Swap two notes
|
||||||
|
int s1 = random(numSteps);
|
||||||
|
int s2 = random(numSteps);
|
||||||
|
if (sequence[s1].note != -1 && sequence[s2].note != -1) {
|
||||||
|
int8_t temp = sequence[s1].note;
|
||||||
|
sequence[s1].note = sequence[s2].note;
|
||||||
|
sequence[s2].note = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getName() override {
|
||||||
|
return "Arp";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
42
LuckyStrategy.h
Normal file
42
LuckyStrategy.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef LUCKY_STRATEGY_H
|
||||||
|
#define LUCKY_STRATEGY_H
|
||||||
|
|
||||||
|
#include "MelodyStrategy.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
class LuckyStrategy : public MelodyStrategy {
|
||||||
|
public:
|
||||||
|
void generate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes, int seed) override {
|
||||||
|
randomSeed(seed);
|
||||||
|
if (numScaleNotes == 0) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < numSteps; i++) {
|
||||||
|
int octave = random(3) + 3; // 3, 4, 5 (Base is 4)
|
||||||
|
sequence[i].note = (random(100) < 50) ? (12 * octave + scaleNotes[random(numScaleNotes)]) : -1;
|
||||||
|
sequence[i].accent = (random(100) < 30);
|
||||||
|
sequence[i].tie = (random(100) < 20);
|
||||||
|
}
|
||||||
|
randomSeed(micros());
|
||||||
|
}
|
||||||
|
|
||||||
|
void mutate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes) override {
|
||||||
|
// Mutate 1 or 2 steps
|
||||||
|
int count = random(1, 3);
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int s = random(numSteps);
|
||||||
|
if (sequence[s].note != -1) {
|
||||||
|
int r = random(100);
|
||||||
|
if (r < 30) sequence[s].accent = !sequence[s].accent;
|
||||||
|
else if (r < 60) sequence[s].tie = !sequence[s].tie;
|
||||||
|
else if (r < 80) sequence[s].note += 12; // Up octave
|
||||||
|
else sequence[s].note -= 12; // Down octave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* getName() override {
|
||||||
|
return "Lucky";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
14
MelodyStrategy.h
Normal file
14
MelodyStrategy.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifndef MELODY_STRATEGY_H
|
||||||
|
#define MELODY_STRATEGY_H
|
||||||
|
|
||||||
|
#include "TrackerTypes.h"
|
||||||
|
|
||||||
|
class MelodyStrategy {
|
||||||
|
public:
|
||||||
|
virtual void generate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes, int seed) = 0;
|
||||||
|
virtual void mutate(Step* sequence, int numSteps, int* scaleNotes, int numScaleNotes) = 0;
|
||||||
|
virtual const char* getName() = 0;
|
||||||
|
virtual ~MelodyStrategy() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -5,6 +5,10 @@
|
|||||||
#include <Adafruit_NeoPixel.h>
|
#include <Adafruit_NeoPixel.h>
|
||||||
#include <EEPROM.h>
|
#include <EEPROM.h>
|
||||||
#include <pico/mutex.h>
|
#include <pico/mutex.h>
|
||||||
|
#include "TrackerTypes.h"
|
||||||
|
#include "MelodyStrategy.h"
|
||||||
|
#include "LuckyStrategy.h"
|
||||||
|
#include "ArpStrategy.h"
|
||||||
|
|
||||||
// --- HARDWARE CONFIGURATION ---
|
// --- HARDWARE CONFIGURATION ---
|
||||||
#define SCREEN_WIDTH 128
|
#define SCREEN_WIDTH 128
|
||||||
@ -30,12 +34,6 @@
|
|||||||
// --- TRACKER DATA ---
|
// --- TRACKER DATA ---
|
||||||
#define NUM_STEPS 16
|
#define NUM_STEPS 16
|
||||||
|
|
||||||
struct Step {
|
|
||||||
int8_t note; // MIDI Note (0-127), -1 for OFF
|
|
||||||
bool accent;
|
|
||||||
bool tie;
|
|
||||||
};
|
|
||||||
|
|
||||||
Step sequence[NUM_STEPS];
|
Step sequence[NUM_STEPS];
|
||||||
Step nextSequence[NUM_STEPS];
|
Step nextSequence[NUM_STEPS];
|
||||||
volatile bool sequenceChangeScheduled = false;
|
volatile bool sequenceChangeScheduled = false;
|
||||||
@ -53,17 +51,18 @@ enum UIState {
|
|||||||
UI_MENU_RANDOMIZE,
|
UI_MENU_RANDOMIZE,
|
||||||
UI_MENU_SETUP,
|
UI_MENU_SETUP,
|
||||||
UI_SETUP_CHANNEL_EDIT,
|
UI_SETUP_CHANNEL_EDIT,
|
||||||
UI_SETUP_TEMPO_EDIT
|
UI_EDIT_TEMPO,
|
||||||
|
UI_EDIT_FLAVOUR
|
||||||
};
|
};
|
||||||
|
|
||||||
UIState currentState = UI_MENU_MAIN;
|
UIState currentState = UI_MENU_MAIN;
|
||||||
|
|
||||||
const char* mainMenu[] = { "Tracker", "Randomize", "Setup" };
|
const char* mainMenu[] = { "Tracker", "Randomize", "Setup" };
|
||||||
const int mainMenuCount = sizeof(mainMenu) / sizeof(char*);
|
const int mainMenuCount = sizeof(mainMenu) / sizeof(char*);
|
||||||
const char* randomizeMenu[] = { "Back", "Scale", "Melody", "Mutation", "Song Mode", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" };
|
const char* randomizeMenu[] = { "Back", "Scale", "Melody", "Flavour", "Tempo", "Mutation", "Song Mode", "Theme 1", "Theme 2", "Theme 3", "Theme 4", "Theme 5", "Theme 6", "Theme 7" };
|
||||||
const int THEME_1_INDEX = 5;
|
const int THEME_1_INDEX = 7;
|
||||||
const int randomizeMenuCount = sizeof(randomizeMenu) / sizeof(char*);
|
const int randomizeMenuCount = sizeof(randomizeMenu) / sizeof(char*);
|
||||||
const char* setupMenu[] = { "Back", "Channel", "Tempo", "Save", "Load" };
|
const char* setupMenu[] = { "Back", "Channel", "Save", "Load" };
|
||||||
const int setupMenuCount = sizeof(setupMenu) / sizeof(char*);
|
const int setupMenuCount = sizeof(setupMenu) / sizeof(char*);
|
||||||
|
|
||||||
int menuSelection = 0;
|
int menuSelection = 0;
|
||||||
@ -76,7 +75,11 @@ int numScaleNotes = 0;
|
|||||||
int melodySeed = 0;
|
int melodySeed = 0;
|
||||||
volatile int queuedTheme = -1;
|
volatile int queuedTheme = -1;
|
||||||
volatile int currentThemeIndex = 1;
|
volatile int currentThemeIndex = 1;
|
||||||
const uint32_t EEPROM_MAGIC = 0x42424244;
|
const uint32_t EEPROM_MAGIC = 0x42424246;
|
||||||
|
|
||||||
|
MelodyStrategy* strategies[] = { new LuckyStrategy(), new ArpStrategy() };
|
||||||
|
const int numStrategies = 2;
|
||||||
|
int currentStrategyIndex = 0;
|
||||||
|
|
||||||
volatile bool mutationEnabled = false;
|
volatile bool mutationEnabled = false;
|
||||||
volatile bool songModeEnabled = false;
|
volatile bool songModeEnabled = false;
|
||||||
@ -145,11 +148,13 @@ void showMessage(const char* msg) {
|
|||||||
display.setTextSize(1);
|
display.setTextSize(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void saveSequence() {
|
void saveSequence(bool quiet = false) {
|
||||||
int addr = 0;
|
int addr = 0;
|
||||||
EEPROM.put(addr, EEPROM_MAGIC); addr += sizeof(EEPROM_MAGIC);
|
EEPROM.put(addr, EEPROM_MAGIC); addr += sizeof(EEPROM_MAGIC);
|
||||||
EEPROM.put(addr, midiChannel); addr += sizeof(midiChannel);
|
EEPROM.put(addr, midiChannel); addr += sizeof(midiChannel);
|
||||||
EEPROM.put(addr, melodySeed); addr += sizeof(melodySeed);
|
EEPROM.put(addr, melodySeed); addr += sizeof(melodySeed);
|
||||||
|
EEPROM.put(addr, currentStrategyIndex); addr += sizeof(currentStrategyIndex);
|
||||||
|
EEPROM.put(addr, (int)tempo); addr += sizeof(int);
|
||||||
|
|
||||||
EEPROM.put(addr, numScaleNotes); addr += sizeof(numScaleNotes);
|
EEPROM.put(addr, numScaleNotes); addr += sizeof(numScaleNotes);
|
||||||
for (int i=0; i<12; i++) {
|
for (int i=0; i<12; i++) {
|
||||||
@ -162,7 +167,7 @@ void saveSequence() {
|
|||||||
}
|
}
|
||||||
mutex_exit(&midiMutex);
|
mutex_exit(&midiMutex);
|
||||||
EEPROM.commit();
|
EEPROM.commit();
|
||||||
showMessage("SAVED!");
|
if (!quiet) showMessage("SAVED!");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool loadSequence() {
|
bool loadSequence() {
|
||||||
@ -174,6 +179,10 @@ bool loadSequence() {
|
|||||||
EEPROM.get(addr, midiChannel); addr += sizeof(midiChannel);
|
EEPROM.get(addr, midiChannel); addr += sizeof(midiChannel);
|
||||||
shMidiChannel = midiChannel;
|
shMidiChannel = midiChannel;
|
||||||
EEPROM.get(addr, melodySeed); addr += sizeof(melodySeed);
|
EEPROM.get(addr, melodySeed); addr += sizeof(melodySeed);
|
||||||
|
EEPROM.get(addr, currentStrategyIndex); addr += sizeof(currentStrategyIndex);
|
||||||
|
int t;
|
||||||
|
EEPROM.get(addr, t); addr += sizeof(int);
|
||||||
|
tempo = t;
|
||||||
|
|
||||||
EEPROM.get(addr, numScaleNotes); addr += sizeof(numScaleNotes);
|
EEPROM.get(addr, numScaleNotes); addr += sizeof(numScaleNotes);
|
||||||
for (int i=0; i<12; i++) {
|
for (int i=0; i<12; i++) {
|
||||||
@ -272,15 +281,7 @@ void generateRandomScale() {
|
|||||||
|
|
||||||
void generateSequenceData(int themeType, Step* target) {
|
void generateSequenceData(int themeType, Step* target) {
|
||||||
randomSeed(melodySeed + themeType * 12345); // Deterministic seed for this theme
|
randomSeed(melodySeed + themeType * 12345); // Deterministic seed for this theme
|
||||||
if (numScaleNotes == 0) generateRandomScale();
|
strategies[currentStrategyIndex]->generate(target, NUM_STEPS, scaleNotes, numScaleNotes, melodySeed + themeType * 12345);
|
||||||
|
|
||||||
for (int i = 0; i < NUM_STEPS; i++) {
|
|
||||||
int octave = random(3) + 3; // 3, 4, 5 (Base is 4)
|
|
||||||
target[i].note = (random(100) < 50) ? (12 * octave + scaleNotes[random(numScaleNotes)]) : -1;
|
|
||||||
target[i].accent = (random(100) < 30);
|
|
||||||
target[i].tie = (random(100) < 20);
|
|
||||||
}
|
|
||||||
randomSeed(micros()); // Restore randomness
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void generateTheme(int themeType) {
|
void generateTheme(int themeType) {
|
||||||
@ -298,18 +299,7 @@ void generateTheme(int themeType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void mutateSequence(Step* target) {
|
void mutateSequence(Step* target) {
|
||||||
// Mutate 1 or 2 steps
|
strategies[currentStrategyIndex]->mutate(target, NUM_STEPS, scaleNotes, numScaleNotes);
|
||||||
int count = random(1, 3);
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
int s = random(NUM_STEPS);
|
|
||||||
if (target[s].note != -1) {
|
|
||||||
int r = random(100);
|
|
||||||
if (r < 30) target[s].accent = !target[s].accent;
|
|
||||||
else if (r < 60) target[s].tie = !target[s].tie;
|
|
||||||
else if (r < 80) target[s].note += 12; // Up octave
|
|
||||||
else target[s].note -= 12; // Down octave
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleInput() {
|
void handleInput() {
|
||||||
@ -364,11 +354,16 @@ void handleInput() {
|
|||||||
if (midiChannel > 16) midiChannel = 1;
|
if (midiChannel > 16) midiChannel = 1;
|
||||||
shMidiChannel = midiChannel;
|
shMidiChannel = midiChannel;
|
||||||
break;
|
break;
|
||||||
case UI_SETUP_TEMPO_EDIT:
|
case UI_EDIT_TEMPO:
|
||||||
tempo += delta;
|
tempo += delta;
|
||||||
if (tempo < 40) tempo = 40;
|
if (tempo < 40) tempo = 40;
|
||||||
if (tempo > 240) tempo = 240;
|
if (tempo > 240) tempo = 240;
|
||||||
break;
|
break;
|
||||||
|
case UI_EDIT_FLAVOUR:
|
||||||
|
currentStrategyIndex += (delta > 0 ? 1 : -1);
|
||||||
|
if (currentStrategyIndex < 0) currentStrategyIndex = numStrategies - 1;
|
||||||
|
if (currentStrategyIndex >= numStrategies) currentStrategyIndex = 0;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,6 +412,7 @@ void handleInput() {
|
|||||||
sequenceChangeScheduled = true;
|
sequenceChangeScheduled = true;
|
||||||
mutex_exit(&midiMutex);
|
mutex_exit(&midiMutex);
|
||||||
}
|
}
|
||||||
|
saveSequence(true);
|
||||||
}
|
}
|
||||||
if (menuSelection == 2) {
|
if (menuSelection == 2) {
|
||||||
melodySeed = random(10000); // Melody
|
melodySeed = random(10000); // Melody
|
||||||
@ -427,9 +423,12 @@ void handleInput() {
|
|||||||
sequenceChangeScheduled = true;
|
sequenceChangeScheduled = true;
|
||||||
mutex_exit(&midiMutex);
|
mutex_exit(&midiMutex);
|
||||||
}
|
}
|
||||||
|
saveSequence(true);
|
||||||
}
|
}
|
||||||
if (menuSelection == 3) mutationEnabled = !mutationEnabled;
|
if (menuSelection == 3) currentState = UI_EDIT_FLAVOUR;
|
||||||
if (menuSelection == 4) {
|
if (menuSelection == 4) currentState = UI_EDIT_TEMPO;
|
||||||
|
if (menuSelection == 5) mutationEnabled = !mutationEnabled;
|
||||||
|
if (menuSelection == 6) {
|
||||||
songModeEnabled = !songModeEnabled;
|
songModeEnabled = !songModeEnabled;
|
||||||
if (songModeEnabled) {
|
if (songModeEnabled) {
|
||||||
songModeNeedsNext = true;
|
songModeNeedsNext = true;
|
||||||
@ -451,21 +450,26 @@ void handleInput() {
|
|||||||
case UI_MENU_SETUP:
|
case UI_MENU_SETUP:
|
||||||
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 2; }
|
if (menuSelection == 0) { currentState = UI_MENU_MAIN; menuSelection = 2; }
|
||||||
if (menuSelection == 1) currentState = UI_SETUP_CHANNEL_EDIT;
|
if (menuSelection == 1) currentState = UI_SETUP_CHANNEL_EDIT;
|
||||||
if (menuSelection == 2) currentState = UI_SETUP_TEMPO_EDIT;
|
if (menuSelection == 2) {
|
||||||
if (menuSelection == 3) {
|
|
||||||
saveSequence();
|
saveSequence();
|
||||||
currentState = UI_MENU_MAIN;
|
currentState = UI_MENU_MAIN;
|
||||||
}
|
}
|
||||||
if (menuSelection == 4) {
|
if (menuSelection == 3) {
|
||||||
if (loadSequence()) showMessage("LOADED!");
|
if (loadSequence()) showMessage("LOADED!");
|
||||||
currentState = UI_MENU_MAIN;
|
currentState = UI_MENU_MAIN;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UI_SETUP_CHANNEL_EDIT:
|
case UI_SETUP_CHANNEL_EDIT:
|
||||||
currentState = UI_MENU_SETUP;
|
currentState = UI_MENU_SETUP;
|
||||||
|
saveSequence(true);
|
||||||
break;
|
break;
|
||||||
case UI_SETUP_TEMPO_EDIT:
|
case UI_EDIT_TEMPO:
|
||||||
currentState = UI_MENU_SETUP;
|
currentState = UI_MENU_RANDOMIZE;
|
||||||
|
saveSequence(true);
|
||||||
|
break;
|
||||||
|
case UI_EDIT_FLAVOUR:
|
||||||
|
currentState = UI_MENU_RANDOMIZE;
|
||||||
|
saveSequence(true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -612,11 +616,6 @@ void drawMenu(const char* title, const char* items[], int count, int selection)
|
|||||||
display.print(F(": "));
|
display.print(F(": "));
|
||||||
display.print(midiChannel);
|
display.print(midiChannel);
|
||||||
}
|
}
|
||||||
// Special case for tempo display
|
|
||||||
if (currentState == UI_MENU_SETUP && i == 2) {
|
|
||||||
display.print(F(": "));
|
|
||||||
display.print(tempo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special case for queued theme
|
// Special case for queued theme
|
||||||
if (currentState == UI_MENU_RANDOMIZE && i >= THEME_1_INDEX && queuedTheme == (i - THEME_1_INDEX + 1)) {
|
if (currentState == UI_MENU_RANDOMIZE && i >= THEME_1_INDEX && queuedTheme == (i - THEME_1_INDEX + 1)) {
|
||||||
@ -642,10 +641,16 @@ void drawMenu(const char* title, const char* items[], int count, int selection)
|
|||||||
} else if (i == 2) { // Melody
|
} else if (i == 2) { // Melody
|
||||||
display.print(F(": "));
|
display.print(F(": "));
|
||||||
display.print(melodySeed);
|
display.print(melodySeed);
|
||||||
} else if (i == 3) { // Mutation
|
} else if (i == 3) { // Flavour
|
||||||
|
display.print(F(": "));
|
||||||
|
display.print(strategies[currentStrategyIndex]->getName());
|
||||||
|
} else if (i == 4) { // Tempo
|
||||||
|
display.print(F(": "));
|
||||||
|
display.print(tempo);
|
||||||
|
} else if (i == 5) { // Mutation
|
||||||
display.print(F(": "));
|
display.print(F(": "));
|
||||||
display.print(mutationEnabled ? F("ON") : F("OFF"));
|
display.print(mutationEnabled ? F("ON") : F("OFF"));
|
||||||
} else if (i == 4) { // Song Mode
|
} else if (i == 6) { // Song Mode
|
||||||
display.print(F(": "));
|
display.print(F(": "));
|
||||||
display.print(songModeEnabled ? F("ON") : F("OFF"));
|
display.print(songModeEnabled ? F("ON") : F("OFF"));
|
||||||
}
|
}
|
||||||
@ -759,7 +764,7 @@ void drawUI() {
|
|||||||
display.setCursor(0, 50);
|
display.setCursor(0, 50);
|
||||||
display.println(F(" (Press to confirm)"));
|
display.println(F(" (Press to confirm)"));
|
||||||
break;
|
break;
|
||||||
case UI_SETUP_TEMPO_EDIT:
|
case UI_EDIT_TEMPO:
|
||||||
display.println(F("SET TEMPO"));
|
display.println(F("SET TEMPO"));
|
||||||
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
|
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
|
||||||
display.setCursor(20, 25);
|
display.setCursor(20, 25);
|
||||||
@ -770,6 +775,16 @@ void drawUI() {
|
|||||||
display.setCursor(0, 50);
|
display.setCursor(0, 50);
|
||||||
display.println(F(" (Press to confirm)"));
|
display.println(F(" (Press to confirm)"));
|
||||||
break;
|
break;
|
||||||
|
case UI_EDIT_FLAVOUR:
|
||||||
|
display.println(F("SET FLAVOUR"));
|
||||||
|
display.drawLine(0, 8, 128, 8, SSD1306_WHITE);
|
||||||
|
display.setCursor(20, 25);
|
||||||
|
display.setTextSize(2);
|
||||||
|
display.print(strategies[currentStrategyIndex]->getName());
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.setCursor(0, 50);
|
||||||
|
display.println(F(" (Press to confirm)"));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
display.display();
|
display.display();
|
||||||
|
|||||||
12
TrackerTypes.h
Normal file
12
TrackerTypes.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef TRACKER_TYPES_H
|
||||||
|
#define TRACKER_TYPES_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct Step {
|
||||||
|
int8_t note; // MIDI Note (0-127), -1 for OFF
|
||||||
|
bool accent;
|
||||||
|
bool tie;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Reference in New Issue
Block a user