/*
* sequence.c
*
* Copyright (c) 2024 Thomas Buck (thomas@xythobuz.de)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* See .
*/
#include
#include
#include "pico/stdlib.h"
#include "led.h"
#include "pulse.h"
#include "ui.h"
#include "sequence.h"
static const uint32_t channel_times[NUM_CHANNELS] = CH_GPIO_TIMINGS;
static uint32_t ms_per_beat = 0;
static uint32_t beats = MAX_BEATS;
static uint32_t last_t = 0;
static uint32_t last_i = 0;
static uint32_t bank = 0;
static uint32_t beattimer_start = 0;
static enum channels sequence[MAX_BEATS] = {0};
void sequence_init(void) {
ms_per_beat = 0;
beats = MAX_BEATS;
last_t = to_ms_since_boot(get_absolute_time());
last_i = 0;
for (uint i = 0; i < MAX_BEATS; i++) {
sequence[i] = 0;
}
}
void sequence_set_bpm(uint32_t new_bpm) {
ms_per_beat = 60000 / new_bpm / beats;
printf("bpm now %"PRIu32" = %"PRIu32"ms\n", new_bpm, ms_per_beat);
}
uint32_t sequence_get_bpm(void) {
return 60000 / (ms_per_beat * beats);
}
uint32_t sequence_get_ms(void) {
return ms_per_beat;
}
void sequence_set_beats(uint32_t new_beats) {
beats = (new_beats <= MAX_BEATS) ? new_beats : MAX_BEATS;
uint32_t max_banks_currently = (beats + (NUM_BTNS - 1)) / NUM_BTNS;
bank = (bank < max_banks_currently) ? bank : max_banks_currently;
}
uint32_t sequence_get_beats(void) {
return beats;
}
void sequence_set_bank(uint32_t new_bank) {
uint32_t b = (new_bank < MAX_BANKS) ? new_bank : MAX_BANKS;
uint32_t max_banks_currently = (beats + (NUM_BTNS - 1)) / NUM_BTNS;
bank = (b < max_banks_currently) ? b : max_banks_currently;
}
uint32_t sequence_get_bank(void) {
return bank;
}
static void sequence_set(uint32_t beat, enum channels ch, bool value) {
if (beat < MAX_BEATS) {
if (value) {
sequence[beat] |= ch;
} else {
sequence[beat] &= ~ch;
}
}
}
static bool sequence_get(uint32_t beat, enum channels ch) {
if (beat < MAX_BEATS) {
return (sequence[beat] & ch) != 0;
}
return false;
}
void sequence_handle_button_loopstation(enum buttons btn, bool rec) {
switch (btn) {
case BTN_A:
case BTN_E: {
pulse_trigger_out(0, channel_times[0]);
pulse_trigger_led(0, channel_times[0]);
pulse_trigger_led(4, channel_times[0]);
break;
}
case BTN_B:
case BTN_F: {
pulse_trigger_out(1, channel_times[1]);
pulse_trigger_led(1, channel_times[1]);
pulse_trigger_led(5, channel_times[1]);
break;
}
case BTN_C:
case BTN_G: {
pulse_trigger_out(2, channel_times[2]);
pulse_trigger_led(2, channel_times[2]);
pulse_trigger_led(6, channel_times[2]);
break;
}
default: {
break;
}
}
if (rec) {
switch (btn) {
case BTN_A:
case BTN_E: {
sequence_set(last_i, CH_KICK, true);
break;
}
case BTN_B:
case BTN_F: {
sequence_set(last_i, CH_SNARE, true);
break;
}
case BTN_C:
case BTN_G: {
sequence_set(last_i, CH_HIHAT, true);
break;
}
default: {
break;
}
}
}
}
void sequence_handle_button_drummachine(enum buttons btn) {
uint32_t beat = 42; // TODO!!
switch (btn) {
case BTN_A: {
// TODO kick is wrong here
bool val = !sequence_get(beat, CH_KICK);
sequence_set(beat, CH_KICK, val);
break;
}
case BTN_B: {
// TODO snare is wrong here
bool val = !sequence_get(beat, CH_SNARE);
sequence_set(beat, CH_SNARE, val);
break;
}
case BTN_C: {
// TODO hihat is wrong here
bool val = !sequence_get(beat, CH_HIHAT);
sequence_set(beat, CH_HIHAT, val);
break;
}
default: {
break;
}
}
}
void sequence_looptime(bool fin) {
if (!fin) {
beattimer_start = to_ms_since_boot(get_absolute_time());
} else {
if (ms_per_beat == 0) {
uint32_t now = to_ms_since_boot(get_absolute_time());
uint32_t diff = now - beattimer_start;
ms_per_beat = diff / beats;
printf("looptime: diff=%"PRIu32"ms per_beat=%"PRIu32"ms\n", diff, ms_per_beat);
}
}
}
void sequence_run(void) {
uint32_t now = to_ms_since_boot(get_absolute_time());
if ((ms_per_beat > 0) && (now >= (last_t + ms_per_beat))) {
//printf("trigger\n");
uint32_t i = last_i + 1;
if (i >= beats) i = 0;
if (ui_get_machinemode() == MODE_DRUMMACHINE) {
led_set(last_i, false);
led_set(i, true);
}
for (uint ch = 0; ch < NUM_CHANNELS; ch++) {
if (sequence[i] & (1 << ch)) {
pulse_trigger_out(ch, channel_times[ch]);
if (ui_get_machinemode() == MODE_LOOPSTATION) {
pulse_trigger_led(ch, channel_times[ch]);
}
}
}
last_t = now;
last_i = i;
}
}