New ui, global settings.
This commit is contained in:
parent
b949063b6a
commit
d4749d2fe9
15 changed files with 253 additions and 82 deletions
2
Makefile
2
Makefile
|
@ -5,7 +5,7 @@ VERSION?=1
|
|||
|
||||
OBJS:=startup.o main.o system.o mem.o led.o \
|
||||
i2c.o display.o font_twisted.o button.o systick.o newlib.o uart.o ui.o timer.o\
|
||||
mutex.o ui_drone.o adc.o spi.o pot.o dac.o sound.o drone.o
|
||||
mutex.o ui_drone.o adc.o spi.o pot.o dac.o sound.o drone.o settings.o
|
||||
|
||||
|
||||
UMX:=lib/unicore-mx/lib/libucmx_stm32f4.a
|
||||
|
|
2
button.c
2
button.c
|
@ -39,7 +39,7 @@ enum button_state {
|
|||
#endif
|
||||
|
||||
#define BUTTON_DEBOUNCE_TIME 50
|
||||
#define BUTTON_HOLD_TIME 1500
|
||||
#define BUTTON_HOLD_TIME 200
|
||||
|
||||
|
||||
|
||||
|
|
6
drone.c
6
drone.c
|
@ -19,6 +19,12 @@ static struct drone_slot drone_pattern[MAX_PATTERN_LEN] = {
|
|||
{ 40, 10 }, { 40, 10}, {36, 10}, {32,10}
|
||||
};
|
||||
|
||||
void drone_mute(void)
|
||||
{
|
||||
pot_set(0, 0xFF);
|
||||
pot_set(1, 0x0);
|
||||
}
|
||||
|
||||
void drone_beat(int pattern_pos)
|
||||
{
|
||||
struct drone_slot *key = &drone_pattern[pattern_pos];
|
||||
|
|
2
drone.h
2
drone.h
|
@ -3,6 +3,8 @@
|
|||
#define DRONE_H_INCLUDED
|
||||
#include <stdint.h>
|
||||
|
||||
void drone_mute(void);
|
||||
|
||||
struct __attribute__((packed)) drone_slot {
|
||||
uint8_t pitch;
|
||||
uint8_t env;
|
||||
|
|
5
led.c
5
led.c
|
@ -84,10 +84,11 @@ void led_toggle(int led)
|
|||
|
||||
void led_beat(int b)
|
||||
{
|
||||
if (b < 1 || b > 8)
|
||||
if (b < 0 || b > 8)
|
||||
return;
|
||||
gpio_clear(GPIOB, BANK_B_LD);
|
||||
gpio_clear(GPIOE, BANK_E_LD);
|
||||
led_on(b);
|
||||
if (b != 0)
|
||||
led_on(b);
|
||||
}
|
||||
|
||||
|
|
12
main.c
12
main.c
|
@ -15,6 +15,12 @@
|
|||
#include "ui.h"
|
||||
#include "button.h"
|
||||
#include "adc.h"
|
||||
#include "pot.h"
|
||||
#include "settings.h"
|
||||
#include "drums.h"
|
||||
#include "drone.h"
|
||||
#include "uart.h"
|
||||
#include "timer.h"
|
||||
extern uint32_t cpu_freq;
|
||||
|
||||
#define BLINK_INTERVAL 500
|
||||
|
@ -39,9 +45,6 @@ static void bootlevel_0(void)
|
|||
printf("Buttons initialized.\r\n");
|
||||
}
|
||||
|
||||
extern int timer_init(uint32_t bpm);
|
||||
extern int timer_start();
|
||||
extern int timer_stop();
|
||||
|
||||
static void bootlevel_1(void)
|
||||
{
|
||||
|
@ -66,7 +69,7 @@ static void bootlevel_1(void)
|
|||
printf("Displaying splash screen...\r\n");
|
||||
ui_init();
|
||||
printf("UI initialized.\r\n");
|
||||
timer_init(140);
|
||||
timer_init();
|
||||
drums_init();
|
||||
drums_start();
|
||||
|
||||
|
@ -106,6 +109,7 @@ void process_button(uint8_t press, int hold)
|
|||
process_input_callback(press, hold);
|
||||
else
|
||||
ui_button_press(press, hold);
|
||||
|
||||
}
|
||||
|
||||
extern void gettime(uint32_t *s, uint32_t *us);
|
||||
|
|
40
pot.c
40
pot.c
|
@ -4,10 +4,11 @@
|
|||
#include "system.h"
|
||||
#include "led.h"
|
||||
#include "pinout.h"
|
||||
#include "settings.h"
|
||||
#include "pot.h"
|
||||
|
||||
extern volatile uint32_t jiffies;
|
||||
|
||||
uint32_t p1_lvl, p0_lvl, master_lvl;
|
||||
|
||||
|
||||
static int pot_cs[3] = { 8, 7, 6 }; // PC6, PC7, PC8
|
||||
|
@ -18,13 +19,11 @@ static int pot_cs[3] = { 8, 7, 6 }; // PC6, PC7, PC8
|
|||
#define POTINC GPIO14 // D14
|
||||
#define POTUD GPIO15 // D15
|
||||
|
||||
#define NUM_POTS 3
|
||||
#define POT_CS_PINS (POT0 | POT1 | POTM)
|
||||
#define POT_CTRL_PINS (POTINC | POTUD)
|
||||
|
||||
|
||||
static int HWLevel[3] = {0, 0, 0};
|
||||
static uint32_t Pots[NUM_POTS] = { POT0, POT1, POTM };
|
||||
static int Levels[NUM_POTS] = { 0, 0, 0 };
|
||||
|
||||
|
||||
static void p_offset(int p, int offset)
|
||||
|
@ -64,21 +63,21 @@ static void p_offset(int p, int offset)
|
|||
;;
|
||||
}
|
||||
|
||||
Levels[p] += offset;
|
||||
if (Levels[p] > 100)
|
||||
Levels[p] = 100;
|
||||
if (Levels[p] < 0)
|
||||
Levels[p] = 0;
|
||||
HWLevel[p] += offset;
|
||||
if (HWLevel[p] > 100)
|
||||
HWLevel[p] = 100;
|
||||
if (HWLevel[p] < 0)
|
||||
HWLevel[p] = 0;
|
||||
}
|
||||
|
||||
void pot_offset(int p, int offset)
|
||||
{
|
||||
if ((Levels[p] + offset) > 100) {
|
||||
offset = 100 - Levels[p];
|
||||
if ((HWLevel[p] + offset) > 100) {
|
||||
offset = 100 - HWLevel[p];
|
||||
}
|
||||
|
||||
if ((Levels[p] + offset) < 0) {
|
||||
offset = 0 - Levels[p];
|
||||
if ((HWLevel[p] + offset) < 0) {
|
||||
offset = 0 - HWLevel[p];
|
||||
}
|
||||
|
||||
if (offset == 0)
|
||||
|
@ -108,20 +107,31 @@ void pot_init(void)
|
|||
|
||||
}
|
||||
|
||||
void pot_sync(int p)
|
||||
{
|
||||
pot_set(p, Settings.levels[p]);
|
||||
}
|
||||
|
||||
|
||||
int pot_get(int p)
|
||||
{
|
||||
return Levels[p];
|
||||
return HWLevel[p];
|
||||
}
|
||||
|
||||
void pot_set(int p, int val)
|
||||
{
|
||||
int old = Levels[p];
|
||||
int old = HWLevel[p];
|
||||
int new = val;
|
||||
int off;
|
||||
|
||||
|
||||
if (val < 0)
|
||||
val = 0;
|
||||
if (val > 100)
|
||||
val = 100;
|
||||
|
||||
Settings.levels[p] = val;
|
||||
|
||||
off = val - old;
|
||||
if (off == 0)
|
||||
return;
|
||||
|
|
1
pot.h
1
pot.h
|
@ -11,5 +11,6 @@ void pot_offset(int p, int offset);
|
|||
|
||||
#define pot_get_master() pot_get(2)
|
||||
#define pot_set_master(x) pot_set(2,x)
|
||||
#define NUM_POTS 3
|
||||
|
||||
#endif
|
||||
|
|
6
settings.c
Normal file
6
settings.c
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include "settings.h"
|
||||
|
||||
struct settings Settings = {
|
||||
.bpm = 125,
|
||||
|
||||
};
|
14
settings.h
Normal file
14
settings.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
#ifndef SETTINGS_H_INCLUDED
|
||||
#define SETTINGS_H_INCLUDED
|
||||
#include <stdint.h>
|
||||
#include "pot.h"
|
||||
|
||||
struct settings {
|
||||
int levels[NUM_POTS];
|
||||
#define master_vol levels[2]
|
||||
int bpm;
|
||||
int sequencer_on;
|
||||
};
|
||||
|
||||
extern struct settings Settings;
|
||||
#endif
|
22
timer.c
22
timer.c
|
@ -3,11 +3,11 @@
|
|||
#include "led.h"
|
||||
#include "timer.h"
|
||||
#include <stdlib.h>
|
||||
#include "settings.h"
|
||||
|
||||
#define S_PER_MINUTE (60)
|
||||
|
||||
extern uint32_t cpu_freq;
|
||||
static uint32_t sys_bpm = 75;
|
||||
|
||||
void (*beat_callback)(uint32_t b) = NULL;
|
||||
|
||||
|
@ -21,19 +21,13 @@ void timer_clear_beat_callback(void)
|
|||
timer_set_beat_callback(NULL);
|
||||
}
|
||||
|
||||
|
||||
uint32_t timer_get_bpm(void)
|
||||
{
|
||||
return sys_bpm;
|
||||
}
|
||||
|
||||
int timer_set_bpm(uint32_t bpm)
|
||||
int timer_set_bpm(void)
|
||||
{
|
||||
uint32_t val = 0;
|
||||
uint32_t psc = 1;
|
||||
uint32_t err = 0;
|
||||
uint32_t reg = 0;
|
||||
uint32_t clock = (cpu_freq / (4 * bpm)) * (S_PER_MINUTE);;
|
||||
uint32_t clock = (cpu_freq / (4 * Settings.bpm)) * (S_PER_MINUTE);;
|
||||
|
||||
while (psc < 65535) {
|
||||
val = clock / psc;
|
||||
|
@ -52,7 +46,6 @@ int timer_set_bpm(uint32_t bpm)
|
|||
TIM4_PSC = psc;
|
||||
TIM4_ARR = val;
|
||||
TIM4_CNT = val - 1;
|
||||
sys_bpm = bpm;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -64,8 +57,8 @@ int timer_start(void)
|
|||
|
||||
int timer_stop(void)
|
||||
{
|
||||
TIM4_CR1 |= TIM_CR1_CLOCK_ENABLE;
|
||||
TIM4_DIER |= TIM_DIER_UIE;
|
||||
TIM4_CR1 &= ~TIM_CR1_CLOCK_ENABLE;
|
||||
TIM4_DIER &= ~TIM_DIER_UIE;
|
||||
}
|
||||
|
||||
static void timer_irq_setup(void)
|
||||
|
@ -79,12 +72,11 @@ static void timer_irq_setup(void)
|
|||
}
|
||||
|
||||
|
||||
int timer_init(uint32_t bpm)
|
||||
int timer_init(void)
|
||||
{
|
||||
timer_stop();
|
||||
timer_irq_setup();
|
||||
timer_set_bpm(bpm);
|
||||
timer_start();
|
||||
timer_set_bpm();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
5
timer.h
5
timer.h
|
@ -1,11 +1,10 @@
|
|||
#ifndef TIMER_H_INCLUDED
|
||||
#define TIMER_H_INCLUDED
|
||||
|
||||
uint32_t timer_get_bpm(void);
|
||||
int timer_set_bpm(uint32_t bpm);
|
||||
int timer_set_bpm(void);
|
||||
int timer_start(void);
|
||||
int timer_stop(void);
|
||||
int timer_init(uint32_t bpm);
|
||||
int timer_init(void);
|
||||
void timer_set_beat_callback(void (*b_cb)(uint32_t));
|
||||
void timer_clear_beat_callback(void);
|
||||
|
||||
|
|
151
ui.c
151
ui.c
|
@ -22,8 +22,9 @@
|
|||
//#define DEVICE_INITIALIZATION
|
||||
|
||||
static const char welcome_message0[]= " Waveblender ";
|
||||
static const char welcome_message1[]= " (c) 2020 ";
|
||||
static const char welcome_message2[]= " danielinux ";
|
||||
static const char welcome_message1[]= "(c) danielinux ";
|
||||
static const char welcome_message2[]= " GPL v.2 ";
|
||||
static const char welcome_message3[]= "src: wb.vado.li";
|
||||
|
||||
static uint32_t last_action = 0;
|
||||
static uint32_t offset = 0;
|
||||
|
@ -88,6 +89,7 @@ static void ui_display_menu_refresh(void)
|
|||
const struct display_menu *menu = CurrentMenu;
|
||||
uint8_t vline;
|
||||
int i;
|
||||
char txt[16];
|
||||
display_scroll(NULL, display_cur_v);
|
||||
display_clear(NULL);
|
||||
|
||||
|
@ -95,10 +97,74 @@ static void ui_display_menu_refresh(void)
|
|||
vline = i + 4;
|
||||
if (vline > 7)
|
||||
vline -= 8;
|
||||
switch (menu->entry[i].type) {
|
||||
case ENTRY_TYPE_TEXT:
|
||||
strncpy(txt, menu->entry[i].label, 15);
|
||||
break;
|
||||
case ENTRY_TYPE_HEX:
|
||||
{
|
||||
int x = *menu->entry[i].var ;
|
||||
int j;
|
||||
strncpy(txt, menu->entry[i].label, 11);
|
||||
for (j = strlen(menu->entry[i].label); j < 12; j++)
|
||||
txt[j] = ' ';
|
||||
if (x > 0xFF) {
|
||||
if (((x >> 8) & 0x0F) > 9)
|
||||
txt[12] = (((x >> 8) & 0x0F) - 10) + 'A';
|
||||
else
|
||||
txt[12] = ((x >> 8) & 0x0F) + '0';
|
||||
} else
|
||||
txt[12] = ' ';
|
||||
if (x > 0x0F) {
|
||||
if (((x >> 4) & 0x0F) > 9)
|
||||
txt[13] = (((x >> 4) & 0x0F) - 10) + 'A';
|
||||
else
|
||||
txt[13] = ((x >> 4) & 0x0F) + '0';
|
||||
} else
|
||||
txt[13] = ' ';
|
||||
if ((x & 0x0F) > 9)
|
||||
txt[14] = ((x & 0x0F) - 10) + 'A';
|
||||
else
|
||||
txt[14] = (x & 0x0F) + '0';
|
||||
}
|
||||
|
||||
break;
|
||||
case ENTRY_TYPE_BOOL:
|
||||
{
|
||||
int x = *menu->entry[i].var ;
|
||||
int j;
|
||||
strncpy(txt, menu->entry[i].label, 10);
|
||||
for (j = strlen(menu->entry[i].label); j < 12; j++)
|
||||
txt[j] = ' ';
|
||||
if (x)
|
||||
strcpy(txt + 10, "[ ON]");
|
||||
else
|
||||
strcpy(txt + 10, "[OFF]");
|
||||
}
|
||||
break;
|
||||
case ENTRY_TYPE_DEC:
|
||||
{
|
||||
int x = *menu->entry[i].var ;
|
||||
int j;
|
||||
strncpy(txt, menu->entry[i].label, 11);
|
||||
for (j = strlen(menu->entry[i].label); j < 12; j++)
|
||||
txt[j] = ' ';
|
||||
if (x > 99) {
|
||||
txt[12] = (x / 100) + '0';
|
||||
} else
|
||||
txt[12] = ' ';
|
||||
if (x > 9) {
|
||||
txt[13] = ((x % 100)/10) + '0';
|
||||
} else
|
||||
txt[13] = ' ';
|
||||
txt[14] = (x % 10) + '0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (i == menu_selection)
|
||||
display_text_inverse(vline, menu->entry[i].title);
|
||||
display_text_inverse(vline, txt);
|
||||
else
|
||||
display_text(vline, menu->entry[i].title);
|
||||
display_text(vline, txt);
|
||||
}
|
||||
WFI();
|
||||
}
|
||||
|
@ -115,32 +181,52 @@ void ui_display_menu(const struct display_menu *menu)
|
|||
|
||||
void ui_button_press(uint8_t b, int hold)
|
||||
{
|
||||
if (b == '+') {
|
||||
int n = menu_selection;
|
||||
menu_selection = 0;
|
||||
if (CurrentMenu->entry[n].action) {
|
||||
display_cur_v = 0;
|
||||
display_clear(NULL);
|
||||
display_scroll(NULL, display_cur_v);
|
||||
CurrentMenu->entry[n].action(
|
||||
CurrentMenu->entry[n].arg);
|
||||
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (b == 'U') {
|
||||
if (menu_selection > 0) {
|
||||
menu_selection--;
|
||||
ui_menu_autoscroll();
|
||||
ui_display_menu_refresh();
|
||||
}
|
||||
}
|
||||
if (b == 'D') {
|
||||
if (menu_selection < CurrentMenu->entry_n - 1) {
|
||||
menu_selection++;
|
||||
ui_menu_autoscroll();
|
||||
ui_display_menu_refresh();
|
||||
}
|
||||
int n = menu_selection;
|
||||
struct display_menu_entry e;
|
||||
switch (b) {
|
||||
case 'U':
|
||||
if (menu_selection > 0) {
|
||||
menu_selection--;
|
||||
ui_menu_autoscroll();
|
||||
ui_display_menu_refresh();
|
||||
}
|
||||
break;
|
||||
case 'D':
|
||||
if (menu_selection < CurrentMenu->entry_n - 1) {
|
||||
menu_selection++;
|
||||
ui_menu_autoscroll();
|
||||
ui_display_menu_refresh();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
e = CurrentMenu->entry[n];
|
||||
int changed = 0;
|
||||
/* Default actions for '-', '+', '*' */
|
||||
if (e.var && (e.type == ENTRY_TYPE_BOOL)) {
|
||||
if ((*e.var) && ((b == '-') || (b == '*'))) {
|
||||
*e.var = 0;
|
||||
changed = 1;
|
||||
} else if ((!(*e.var)) && ((b == '+') || (b == '*'))) {
|
||||
*e.var = 1;
|
||||
changed = 1;
|
||||
}
|
||||
} else if ((e.min != e.max) && e.var && ((e.type == ENTRY_TYPE_DEC) || (e.type == ENTRY_TYPE_DEC))) {
|
||||
if ((*e.var > e.min) && (b == '-')) {
|
||||
*e.var -= e.step;
|
||||
changed = 1;
|
||||
} else if ((*e.var < e.max) && (b == '+')) {
|
||||
*e.var += e.step;
|
||||
changed = 1;
|
||||
}
|
||||
}
|
||||
/* Custom actions for '-', '+', '*' (after changes are applied) */
|
||||
if (b != 0 && e.action) {
|
||||
e.action(&e, b);
|
||||
changed = 1;
|
||||
}
|
||||
if (changed) {
|
||||
ui_display_menu_refresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,16 +238,17 @@ void ui_init(void)
|
|||
display_text(0, welcome_message0);
|
||||
display_text(1, welcome_message1);
|
||||
display_text(2, welcome_message2);
|
||||
display_text(3, welcome_message3);
|
||||
now = jiffies;
|
||||
for (i = 0x3f; i >= 0x20; i--) {
|
||||
display_scroll(NULL, i);
|
||||
while ((jiffies - now) < 30)
|
||||
while ((jiffies - now) < 20)
|
||||
WFI();
|
||||
now = jiffies;
|
||||
}
|
||||
for (i = display_getcontrast(NULL); i >= 0; i--) {
|
||||
display_setcontrast(NULL, i);
|
||||
while ((jiffies - now) < 10)
|
||||
while ((jiffies - now) < 16)
|
||||
WFI();
|
||||
now = jiffies;
|
||||
}
|
||||
|
|
18
ui.h
18
ui.h
|
@ -4,14 +4,26 @@
|
|||
|
||||
void ui_init(void);
|
||||
|
||||
#define ENTRY_TYPE_TEXT 0
|
||||
#define ENTRY_TYPE_BOOL 1
|
||||
#define ENTRY_TYPE_DEC 2
|
||||
#define ENTRY_TYPE_HEX 3
|
||||
|
||||
#define MAX_ENTRY 8
|
||||
|
||||
|
||||
|
||||
struct display_menu {
|
||||
struct display_menu *next;
|
||||
int entry_n;
|
||||
struct display_menu_entry {
|
||||
char title[16];
|
||||
void (*action)(const void *arg);
|
||||
int type;
|
||||
char label[16];
|
||||
void (*action)(struct display_menu_entry *item, uint8_t button);
|
||||
int min, max, step;
|
||||
int *var;
|
||||
const void *arg;
|
||||
} entry[8];
|
||||
} entry[MAX_ENTRY];
|
||||
|
||||
};
|
||||
|
||||
|
|
49
ui_drone.c
49
ui_drone.c
|
@ -7,6 +7,9 @@
|
|||
#include "unicore-mx/stm32/gpio.h"
|
||||
#include "unicore-mx/stm32/rcc.h"
|
||||
#include "pot.h"
|
||||
#include "dac.h"
|
||||
#include "settings.h"
|
||||
#include "timer.h"
|
||||
|
||||
#define CENTER_X ((int)(52))
|
||||
#define CENTER_Y ((int)(50))
|
||||
|
@ -16,7 +19,7 @@ struct display_menu *CurrentMenu = &MainMenu;
|
|||
|
||||
extern volatile uint32_t jiffies;
|
||||
static uint32_t drone_xy_gain = 1;
|
||||
|
||||
#if 0
|
||||
static void ui_return(uint8_t press, int hold)
|
||||
{
|
||||
display_clear(NULL);
|
||||
|
@ -192,7 +195,7 @@ static void ui_bpm_input(uint8_t press, int hold)
|
|||
if (press == 'U') {
|
||||
if (sys_bpm < 300) {
|
||||
timer_stop();
|
||||
timer_set_bpm(sys_bpm + 1);
|
||||
timer_set_bpm();
|
||||
led_beat(1);
|
||||
timer_set_beat(1);
|
||||
timer_start();
|
||||
|
@ -208,7 +211,7 @@ static void ui_bpm_input(uint8_t press, int hold)
|
|||
timer_start();
|
||||
}
|
||||
}
|
||||
if (press == '+') {
|
||||
if (press == '*') {
|
||||
display_clear(NULL);
|
||||
clear_input_callback();
|
||||
ui_display_menu(&MainMenu);
|
||||
|
@ -294,15 +297,49 @@ const struct display_menu SettingsMenu = {
|
|||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static void ui_sequencer_action(struct display_menu_entry *e, uint8_t b)
|
||||
{
|
||||
if (Settings.sequencer_on) {
|
||||
timer_set_beat(1);
|
||||
timer_start();
|
||||
} else {
|
||||
led_beat(0);
|
||||
drone_mute();
|
||||
timer_stop();
|
||||
}
|
||||
}
|
||||
|
||||
static void ui_master_action(struct display_menu_entry *e, uint8_t b)
|
||||
{
|
||||
pot_sync(2);
|
||||
}
|
||||
|
||||
static void ui_bpm_action(struct display_menu_entry *e, uint8_t b)
|
||||
{
|
||||
timer_stop();
|
||||
timer_set_bpm();
|
||||
led_beat(1);
|
||||
timer_set_beat(1);
|
||||
timer_start();
|
||||
}
|
||||
|
||||
|
||||
const struct display_menu MainMenu = {
|
||||
.entry_n = 5,
|
||||
.entry_n = 3,
|
||||
.entry = {
|
||||
{ "Master Volume ", ui_mastervol, NULL },
|
||||
/* TYPE, label, action, min, max, var, arg */
|
||||
{ ENTRY_TYPE_BOOL, "Sequencer", ui_sequencer_action, 0, 0, 0, &Settings.sequencer_on, NULL },
|
||||
{ ENTRY_TYPE_DEC, "Volume: ", ui_master_action, 0, 100, 2, &Settings.levels[2], NULL },
|
||||
{ ENTRY_TYPE_DEC, "BPM: ", ui_bpm_action, 30, 300, 1, &Settings.bpm, NULL },
|
||||
/*
|
||||
{ "BPM" , ui_bpm, NULL},
|
||||
{ "Pattern ", ui_submenu, &PatternMenu },
|
||||
{ "Drone ", ui_submenu, &DroneMenu},
|
||||
{ "Bytebeat ", ui_bytebeat },
|
||||
{ "", NULL, NULL}
|
||||
*/
|
||||
{0, "", NULL, 0,0,0,NULL,NULL}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue