Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion lib/ectocore.h
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,8 @@ void __not_in_flash_func(input_handling)() {
uint8_t knob_selector = 0;
EctoLoopstartTrigState loopstart_trig_state;
ecto_loopstart_trig_state_init(&loopstart_trig_state);
uint32_t loopstart_trig_seen_transport_start_generation =
ecto_loopstart_transport_start_generation;

while (1) {
#ifdef INCLUDE_MIDI
Expand Down Expand Up @@ -1793,11 +1795,19 @@ void __not_in_flash_func(input_handling)() {
uint8_t current_slice =
sample_info != NULL ? sample_info->slice_current : 0;
uint8_t slice_num = sample_info != NULL ? sample_info->slice_num : 0;
uint32_t transport_start_generation =
ecto_loopstart_transport_start_generation;
bool transport_started =
transport_start_generation !=
loopstart_trig_seen_transport_start_generation;
loopstart_trig_seen_transport_start_generation =
transport_start_generation;
EctoLoopstartTrigEvent loopstart_event = ecto_loopstart_trig_step(
&loopstart_trig_state, (uintptr_t)sample_info, playback_stopped,
current_slice, slice_num,
ecto_selected_mode_has_loop_start_transient(ectocore_trigger_mode,
sample_info));
sample_info),
transport_started);

if (loopstart_event != ECTO_LOOPSTART_TRIG_NONE) {
if (ecto_trig_out_last == 0 ||
Expand Down
4 changes: 2 additions & 2 deletions lib/ectocore_loopstart_trig.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ static inline bool ecto_loopstart_trig_transient_pos_is_start(
static inline EctoLoopstartTrigEvent ecto_loopstart_trig_step(
EctoLoopstartTrigState *state, uintptr_t sample_info_id,
bool playback_stopped, uint8_t current_slice, uint8_t slice_num,
bool selected_mode_has_loop_start_transient) {
bool selected_mode_has_loop_start_transient, bool transport_started) {
if (state == NULL) {
return ECTO_LOOPSTART_TRIG_NONE;
}
Expand All @@ -64,7 +64,7 @@ static inline EctoLoopstartTrigEvent ecto_loopstart_trig_step(
bool playback_started_now = state->prev_playback_stopped && !playback_stopped;
bool strict_loop_wrap = false;

if (playback_started_now) {
if (!playback_stopped && (playback_started_now || transport_started)) {
state->pending = true;
}

Expand Down
3 changes: 3 additions & 0 deletions lib/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ uint8_t grimoire_rune = 0;
bool clock_out_do = false;
bool clock_out_ready = false;
uint32_t ecto_trig_out_last = 0;
#ifdef INCLUDE_ECTOCORE
volatile uint32_t ecto_loopstart_transport_start_generation = 0;
#endif
volatile bool clock_in_do = false;
bool clock_input_absent_zeptocore = false;
bool clock_in_ready = false;
Expand Down
68 changes: 53 additions & 15 deletions lib/test/ectocore_loopstart_trig/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ static void test_playback_start_waits_for_slice_zero(void) {
EctoLoopstartTrigState state;
ecto_loopstart_trig_state_init(&state);

assert(ecto_loopstart_trig_step(&state, 1, false, 3, 8, true) ==
assert(ecto_loopstart_trig_step(&state, 1, false, 3, 8, true, false) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(state.pending);

EctoLoopstartTrigEvent event =
ecto_loopstart_trig_step(&state, 1, false, 0, 8, true);
ecto_loopstart_trig_step(&state, 1, false, 0, 8, true, false);
assert(event == ECTO_LOOPSTART_TRIG_PLAYBACK_START);
ecto_loopstart_trig_mark_emitted(&state, event);
assert(!state.pending);

assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, true) ==
assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, true, false) ==
ECTO_LOOPSTART_TRIG_NONE);
}

Expand All @@ -27,18 +27,54 @@ static void test_wrap_from_last_slice_to_zero(void) {
ecto_loopstart_trig_state_init(&state);

EctoLoopstartTrigEvent event =
ecto_loopstart_trig_step(&state, 1, false, 0, 8, true);
ecto_loopstart_trig_step(&state, 1, false, 0, 8, true, false);
assert(event == ECTO_LOOPSTART_TRIG_PLAYBACK_START);
ecto_loopstart_trig_mark_emitted(&state, event);

assert(ecto_loopstart_trig_step(&state, 1, false, 7, 8, true) ==
assert(ecto_loopstart_trig_step(&state, 1, false, 7, 8, true, false) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, true) ==
assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, true, false) ==
ECTO_LOOPSTART_TRIG_WRAP);
assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, true) ==
assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, true, false) ==
ECTO_LOOPSTART_TRIG_NONE);
}

static void test_transport_restart_emits_while_already_running(void) {
EctoLoopstartTrigState state;
ecto_loopstart_trig_state_init(&state);

EctoLoopstartTrigEvent event =
ecto_loopstart_trig_step(&state, 1, false, 0, 8, true, false);
assert(event == ECTO_LOOPSTART_TRIG_PLAYBACK_START);
ecto_loopstart_trig_mark_emitted(&state, event);
assert(!state.pending);

event = ecto_loopstart_trig_step(&state, 1, false, 0, 8, true, true);
assert(event == ECTO_LOOPSTART_TRIG_PLAYBACK_START);
ecto_loopstart_trig_mark_emitted(&state, event);
assert(!state.pending);
}

static void test_transport_restart_waits_for_slice_zero(void) {
EctoLoopstartTrigState state;
ecto_loopstart_trig_state_init(&state);

EctoLoopstartTrigEvent event =
ecto_loopstart_trig_step(&state, 1, false, 0, 8, true, false);
assert(event == ECTO_LOOPSTART_TRIG_PLAYBACK_START);
ecto_loopstart_trig_mark_emitted(&state, event);
assert(!state.pending);

assert(ecto_loopstart_trig_step(&state, 1, false, 3, 8, true, true) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(state.pending);

event = ecto_loopstart_trig_step(&state, 1, false, 0, 8, true, false);
assert(event == ECTO_LOOPSTART_TRIG_PLAYBACK_START);
ecto_loopstart_trig_mark_emitted(&state, event);
assert(!state.pending);
}

static void test_transient_start_window(void) {
assert(ecto_loopstart_trig_transient_pos_is_start(0));
assert(ecto_loopstart_trig_transient_pos_is_start(1));
Expand All @@ -52,40 +88,42 @@ static void test_random_mode_does_not_emit(void) {
EctoLoopstartTrigState state;
ecto_loopstart_trig_state_init(&state);

assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, false) ==
assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, false, false) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(ecto_loopstart_trig_step(&state, 1, false, 7, 8, false) ==
assert(ecto_loopstart_trig_step(&state, 1, false, 7, 8, false, false) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, false) ==
assert(ecto_loopstart_trig_step(&state, 1, false, 0, 8, false, false) ==
ECTO_LOOPSTART_TRIG_NONE);
}

static void test_sample_change_and_invalid_state_clear_pending(void) {
EctoLoopstartTrigState state;
ecto_loopstart_trig_state_init(&state);

assert(ecto_loopstart_trig_step(&state, 1, false, 3, 8, true) ==
assert(ecto_loopstart_trig_step(&state, 1, false, 3, 8, true, false) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(state.pending);

assert(ecto_loopstart_trig_step(&state, 2, false, 0, 8, true) ==
assert(ecto_loopstart_trig_step(&state, 2, false, 0, 8, true, false) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(!state.pending);

assert(ecto_loopstart_trig_step(&state, 2, true, 0, 8, true) ==
assert(ecto_loopstart_trig_step(&state, 2, true, 0, 8, true, false) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(ecto_loopstart_trig_step(&state, 2, false, 3, 8, true) ==
assert(ecto_loopstart_trig_step(&state, 2, false, 3, 8, true, false) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(state.pending);

assert(ecto_loopstart_trig_step(&state, 0, false, 0, 0, true) ==
assert(ecto_loopstart_trig_step(&state, 0, false, 0, 0, true, false) ==
ECTO_LOOPSTART_TRIG_NONE);
assert(!state.pending);
}

int main(void) {
test_playback_start_waits_for_slice_zero();
test_wrap_from_last_slice_to_zero();
test_transport_restart_emits_while_already_running();
test_transport_restart_waits_for_slice_zero();
test_transient_start_window();
test_random_mode_does_not_emit();
test_sample_change_and_invalid_state_clear_pending();
Expand Down
8 changes: 7 additions & 1 deletion main.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ bool __not_in_flash_func(timer_step)() {
cancel_repeating_timer(&timer);
update_repeating_timer_to_bpm(sf->bpm_tempo);
}
bool transport_restarted_this_step = false;
if (do_restart_playback) {
do_restart_playback = false;
#ifdef INCLUDE_ECTOCORE
ecto_loopstart_transport_start_generation++;
#endif
transport_restarted_this_step = true;
playback_restarted = true;
bpm_timer_counter = -1;
bpm_timer_counter_last = bpm_timer_counter;
Expand Down Expand Up @@ -515,7 +520,8 @@ bool __not_in_flash_func(timer_step)() {

// Only update if beat_current actually changed
static int last_beat_current = -1;
if (new_beat_current != last_beat_current) {
if (new_beat_current != last_beat_current ||
transport_restarted_this_step) {
beat_current = new_beat_current;
last_beat_current = beat_current;
beat_did_activate = true; // Set flag for LED display update
Expand Down
Loading