diff --git a/applications/plugins/musictracker/tracker_engine/tracker.c b/applications/plugins/musictracker/tracker_engine/tracker.c index ce2563f8c..e5efcea17 100644 --- a/applications/plugins/musictracker/tracker_engine/tracker.c +++ b/applications/plugins/musictracker/tracker_engine/tracker.c @@ -40,7 +40,7 @@ typedef struct { } UnpackedRow; struct Tracker { - Song* song; + const Song* song; bool playing; TrackerMessageCallback callback; void* context; @@ -126,9 +126,9 @@ static float frequency_get_seventh_of_a_semitone(float frequency) { return frequency * ((1.0f / 12.0f) / 7.0f); } -static UnpackedRow get_current_row(Song* song, SongState* song_state, uint8_t channel) { - Pattern* pattern = &song->patterns[song_state->pattern_index]; - Row row = pattern->channels[channel].rows[song_state->row_index]; +static UnpackedRow get_current_row(const Song* song, SongState* song_state, uint8_t channel) { + const Pattern* pattern = &song->patterns[song_state->pattern_index]; + const Row row = pattern->channels[channel].rows[song_state->row_index]; return (UnpackedRow){ .note = record_get_note(row), .effect = record_get_effect(row), @@ -136,7 +136,7 @@ static UnpackedRow get_current_row(Song* song, SongState* song_state, uint8_t ch }; } -static int16_t advance_order_and_get_next_pattern_index(Song* song, SongState* song_state) { +static int16_t advance_order_and_get_next_pattern_index(const Song* song, SongState* song_state) { song_state->order_list_index++; if(song_state->order_list_index >= song->order_list_size) { return -1; @@ -205,7 +205,7 @@ static void tracker_interrupt_body(Tracker* tracker) { const uint8_t channel_index = 0; SongState* song_state = &tracker->song_state; ChannelState* channel_state = &song_state->channels[channel_index]; - Song* song = tracker->song; + const Song* song = tracker->song; UnpackedRow row = get_current_row(song, song_state, channel_index); // load frequency from note at tick 0 @@ -403,7 +403,7 @@ void tracker_set_message_callback(Tracker* tracker, TrackerMessageCallback callb tracker->context = context; } -void tracker_set_song(Tracker* tracker, Song* song) { +void tracker_set_song(Tracker* tracker, const Song* song) { furi_check(tracker->playing == false); tracker->song = song; tracker_song_state_init(tracker); diff --git a/applications/plugins/musictracker/tracker_engine/tracker.h b/applications/plugins/musictracker/tracker_engine/tracker.h index 87845d5ca..70bf4bd6b 100644 --- a/applications/plugins/musictracker/tracker_engine/tracker.h +++ b/applications/plugins/musictracker/tracker_engine/tracker.h @@ -27,7 +27,7 @@ void tracker_free(Tracker* tracker); void tracker_set_message_callback(Tracker* tracker, TrackerMessageCallback callback, void* context); -void tracker_set_song(Tracker* tracker, Song* song); +void tracker_set_song(Tracker* tracker, const Song* song); void tracker_set_order_index(Tracker* tracker, uint8_t order_index); diff --git a/applications/plugins/musictracker/view/tracker_view.c b/applications/plugins/musictracker/view/tracker_view.c new file mode 100644 index 000000000..87e6b0fcf --- /dev/null +++ b/applications/plugins/musictracker/view/tracker_view.c @@ -0,0 +1,182 @@ +#include "tracker_view.h" +#include +#include + +typedef struct { + const Song* song; + uint8_t order_list_index; + uint8_t row; +} TrackerViewModel; + +struct TrackerView { + View* view; + void* back_context; + TrackerViewCallback back_callback; +}; + +static Channel* get_current_channel(TrackerViewModel* model) { + uint8_t channel_id = 0; + uint8_t pattern_id = model->song->order_list[model->order_list_index]; + Pattern* pattern = &model->song->patterns[pattern_id]; + return &pattern->channels[channel_id]; +} + +static const char* get_note_from_id(uint8_t note) { +#define NOTE_COUNT 12 + const char* notes[NOTE_COUNT] = { + "C ", + "C#", + "D ", + "D#", + "E ", + "F ", + "F#", + "G ", + "G#", + "A ", + "A#", + "B ", + }; + return notes[(note) % NOTE_COUNT]; +#undef NOTE_COUNT +} + +static uint8_t get_octave_from_id(uint8_t note) { + return ((note) / 12) + 2; +} + +static uint8_t get_first_row_id(uint8_t row) { + return (row / 10) * 10; +} + +static void + draw_row(Canvas* canvas, uint8_t i, Channel* channel, uint8_t row, FuriString* buffer) { + uint8_t x = 12 * (i + 1); + uint8_t first_row_id = get_first_row_id(row); + uint8_t current_row_id = first_row_id + i; + + if((current_row_id) >= 64) { + return; + } + + Row current_row = channel->rows[current_row_id]; + uint8_t note = current_row & ROW_NOTE_MASK; + uint8_t effect = (current_row >> 6) & ROW_EFFECT_MASK; + uint8_t data = (current_row >> 10) & ROW_EFFECT_DATA_MASK; + + if(current_row_id == row) { + canvas_set_color(canvas, ColorBlack); + canvas_draw_line(canvas, x - 9, 1, x - 9, 62); + canvas_draw_box(canvas, x - 8, 0, 9, 64); + canvas_draw_line(canvas, x + 1, 1, x + 1, 62); + canvas_set_color(canvas, ColorWhite); + } + + furi_string_printf(buffer, "%02X", current_row_id); + canvas_draw_str(canvas, x, 61, furi_string_get_cstr(buffer)); + + if(note > 0 && note < NOTE_OFF) { + furi_string_printf( + buffer, "%s%d", get_note_from_id(note - 1), get_octave_from_id(note - 1)); + canvas_draw_str(canvas, x, 44, furi_string_get_cstr(buffer)); + } else if(note == NOTE_OFF) { + canvas_draw_str(canvas, x, 44, "OFF"); + } else { + canvas_draw_str(canvas, x, 44, "---"); + } + + if(effect == 0 && data == 0) { + canvas_draw_str(canvas, x, 21, "-"); + canvas_draw_str(canvas, x, 12, "--"); + } else { + furi_string_printf(buffer, "%X", effect); + canvas_draw_str(canvas, x, 21, furi_string_get_cstr(buffer)); + + if(effect == EffectArpeggio || effect == EffectVibrato) { + uint8_t data_x = EFFECT_DATA_GET_X(data); + uint8_t data_y = EFFECT_DATA_GET_Y(data); + furi_string_printf(buffer, "%d%d", data_x, data_y); + canvas_draw_str(canvas, x, 12, furi_string_get_cstr(buffer)); + } else { + furi_string_printf(buffer, "%02X", data); + canvas_draw_str(canvas, x, 12, furi_string_get_cstr(buffer)); + } + } + + if(current_row_id == row) { + canvas_set_color(canvas, ColorBlack); + } +} + +static void tracker_view_draw_callback(Canvas* canvas, void* _model) { + TrackerViewModel* model = _model; + if(model->song == NULL) { + return; + } + + canvas_set_font_direction(canvas, CanvasDirectionBottomToTop); + canvas_set_font(canvas, FontKeyboard); + + Channel* channel = get_current_channel(model); + FuriString* buffer = furi_string_alloc(); + + for(uint8_t i = 0; i < 10; i++) { + draw_row(canvas, i, channel, model->row, buffer); + } + furi_string_free(buffer); +} + +static bool tracker_view_input_callback(InputEvent* event, void* context) { + TrackerView* tracker_view = context; + + if(tracker_view->back_callback) { + if(event->type == InputTypeShort && event->key == InputKeyBack) { + tracker_view->back_callback(tracker_view->back_context); + return true; + } + } + return false; +} + +TrackerView* tracker_view_alloc() { + TrackerView* tracker_view = malloc(sizeof(TrackerView)); + tracker_view->view = view_alloc(); + view_allocate_model(tracker_view->view, ViewModelTypeLocking, sizeof(TrackerViewModel)); + view_set_context(tracker_view->view, tracker_view); + view_set_draw_callback(tracker_view->view, (ViewDrawCallback)tracker_view_draw_callback); + view_set_input_callback(tracker_view->view, (ViewInputCallback)tracker_view_input_callback); + return tracker_view; +} + +void tracker_view_free(TrackerView* tracker_view) { + view_free(tracker_view->view); + free(tracker_view); +} + +View* tracker_view_get_view(TrackerView* tracker_view) { + return tracker_view->view; +} + +void tracker_view_set_back_callback( + TrackerView* tracker_view, + TrackerViewCallback callback, + void* context) { + tracker_view->back_callback = callback; + tracker_view->back_context = context; +} + +void tracker_view_set_song(TrackerView* tracker_view, const Song* song) { + with_view_model( + tracker_view->view, TrackerViewModel * model, { model->song = song; }, true); +} + +void tracker_view_set_position(TrackerView* tracker_view, uint8_t order_list_index, uint8_t row) { + with_view_model( + tracker_view->view, + TrackerViewModel * model, + { + model->order_list_index = order_list_index; + model->row = row; + }, + true); +} \ No newline at end of file diff --git a/applications/plugins/musictracker/view/tracker_view.h b/applications/plugins/musictracker/view/tracker_view.h new file mode 100644 index 000000000..6c2e69ba4 --- /dev/null +++ b/applications/plugins/musictracker/view/tracker_view.h @@ -0,0 +1,29 @@ +#include +#include "../tracker_engine/tracker.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct TrackerView TrackerView; + +TrackerView* tracker_view_alloc(); + +void tracker_view_free(TrackerView* tracker_view); + +View* tracker_view_get_view(TrackerView* tracker_view); + +typedef void (*TrackerViewCallback)(void* context); + +void tracker_view_set_back_callback( + TrackerView* tracker_view, + TrackerViewCallback callback, + void* context); + +void tracker_view_set_song(TrackerView* tracker_view, const Song* song); + +void tracker_view_set_position(TrackerView* tracker_view, uint8_t order_list_index, uint8_t row); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/plugins/musictracker/zero_tracker.c b/applications/plugins/musictracker/zero_tracker.c index b906c0ce7..2ce219268 100644 --- a/applications/plugins/musictracker/zero_tracker.c +++ b/applications/plugins/musictracker/zero_tracker.c @@ -1,6 +1,10 @@ #include +#include +#include +#include #include "zero_tracker.h" #include "tracker_engine/tracker.h" +#include "view/tracker_view.h" // Channel p_0_channels[] = { // { @@ -479,6 +483,17 @@ void tracker_message(TrackerMessage message, void* context) { int32_t zero_tracker_app(void* p) { UNUSED(p); + NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); + notification_message(notification, &sequence_display_backlight_enforce_on); + + Gui* gui = furi_record_open(RECORD_GUI); + ViewDispatcher* view_dispatcher = view_dispatcher_alloc(); + TrackerView* tracker_view = tracker_view_alloc(); + tracker_view_set_song(tracker_view, &song); + view_dispatcher_add_view(view_dispatcher, 0, tracker_view_get_view(tracker_view)); + view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); + view_dispatcher_switch_to_view(view_dispatcher, 0); + FuriMessageQueue* queue = furi_message_queue_alloc(8, sizeof(TrackerMessage)); Tracker* tracker = tracker_alloc(); tracker_set_message_callback(tracker, tracker_message, queue); @@ -493,6 +508,7 @@ int32_t zero_tracker_app(void* p) { uint8_t order_list_index = message.data.position.order_list_index; uint8_t row = message.data.position.row; uint8_t pattern = song.order_list[order_list_index]; + tracker_view_set_position(tracker_view, order_list_index, row); FURI_LOG_I("Tracker", "O:%d P:%d R:%d", order_list_index, pattern, row); } else if(message.type == TrackerEndOfSong) { FURI_LOG_I("Tracker", "End of song"); @@ -505,5 +521,14 @@ int32_t zero_tracker_app(void* p) { tracker_free(tracker); furi_message_queue_free(queue); + view_dispatcher_remove_view(view_dispatcher, 0); + tracker_view_free(tracker_view); + view_dispatcher_free(view_dispatcher); + + notification_message(notification, &sequence_display_backlight_enforce_auto); + + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + return 0; } \ No newline at end of file