Просмотр исходного кода

usable sequence and songinfo interfaces

LTVA1 3 лет назад
Родитель
Сommit
bf36e51c32

+ 12 - 2
flizzer_tracker.c

@@ -7,6 +7,7 @@
 #define FLIZZER_TRACKER_FOLDER "/ext/flizzer_tracker"
 #define FLIZZER_TRACKER_FOLDER "/ext/flizzer_tracker"
 
 
 #include <flizzer_tracker_icons.h>
 #include <flizzer_tracker_icons.h>
+
 /*
 /*
 Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1
 Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1
 Copyright: 
 Copyright: 
@@ -51,6 +52,7 @@ static void draw_callback(Canvas* canvas, void* ctx)
 		case PATTERN_VIEW:
 		case PATTERN_VIEW:
 		{
 		{
 			draw_songinfo_view(canvas, tracker);
 			draw_songinfo_view(canvas, tracker);
+			draw_sequence_view(canvas, tracker);
 			draw_pattern_view(canvas, tracker);
 			draw_pattern_view(canvas, tracker);
 			break;
 			break;
 		}
 		}
@@ -109,13 +111,19 @@ int32_t flizzer_tracker_app(void* p)
 	// Подключаем view port к GUI в полноэкранном режиме
 	// Подключаем view port к GUI в полноэкранном режиме
 	gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 	gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 
 
+	tracker->view_dispatcher = view_dispatcher_alloc();
+	view_dispatcher_attach_to_gui(tracker->view_dispatcher, gui, ViewDispatcherTypeFullscreen);
+
 	tracker->notification = furi_record_open(RECORD_NOTIFICATION);
 	tracker->notification = furi_record_open(RECORD_NOTIFICATION);
 	notification_message(tracker->notification, &sequence_display_backlight_enforce_on);
 	notification_message(tracker->notification, &sequence_display_backlight_enforce_on);
 
 
+	tracker->tracker_engine.master_volume = 0x80;
+
 	tracker->song.speed = 5;
 	tracker->song.speed = 5;
+	tracker->song.rate = tracker->tracker_engine.rate;
 	tracker->song.num_instruments = 2;
 	tracker->song.num_instruments = 2;
-	tracker->song.num_patterns = 2;
-	tracker->song.num_sequence_steps = 1;
+	tracker->song.num_patterns = 3;
+	tracker->song.num_sequence_steps = 4;
 	tracker->song.pattern_length = 64;
 	tracker->song.pattern_length = 64;
 
 
 	tracker->song.sequence.sequence_step[0].pattern_indices[0] = 0;
 	tracker->song.sequence.sequence_step[0].pattern_indices[0] = 0;
@@ -209,6 +217,8 @@ int32_t flizzer_tracker_app(void* p)
 	view_port_free(view_port);
 	view_port_free(view_port);
 	furi_record_close(RECORD_GUI);
 	furi_record_close(RECORD_GUI);
 
 
+	view_dispatcher_free(tracker->view_dispatcher);
+
 	deinit_tracker(tracker);
 	deinit_tracker(tracker);
 
 
 	return 0;
 	return 0;

+ 17 - 0
flizzer_tracker.h

@@ -7,6 +7,8 @@
 #include <input/input.h>
 #include <input/input.h>
 #include <u8g2_glue.h>
 #include <u8g2_glue.h>
 
 
+#include <gui/view_dispatcher.h>
+
 #include "flizzer_tracker_hal.h"
 #include "flizzer_tracker_hal.h"
 #include "sound_engine/freqs.h"
 #include "sound_engine/freqs.h"
 #include "sound_engine/sound_engine_filter.h"
 #include "sound_engine/sound_engine_filter.h"
@@ -44,10 +46,25 @@ typedef enum
 	EDIT_PROGRAM,
 	EDIT_PROGRAM,
 } TrackerFocus;
 } TrackerFocus;
 
 
+typedef enum
+{
+	SI_PATTERNPOS,
+	SI_SEQUENCEPOS,
+	SI_SONGSPEED,
+	SI_SONGRATE,
+	SI_MASTERVOL,
+
+	SI_SONGNAME,
+	SI_INSTRUMENTNAME,
+	/* ========  */
+	SI_PARAMS,
+} SongInfoParam;
+
 typedef struct 
 typedef struct 
 {
 {
 	NotificationApp* notification;
 	NotificationApp* notification;
 	FuriMessageQueue* event_queue;
 	FuriMessageQueue* event_queue;
+	ViewDispatcher* view_dispatcher;
 	bool was_it_back_keypress;
 	bool was_it_back_keypress;
 	uint32_t current_time;
 	uint32_t current_time;
 	uint32_t period;
 	uint32_t period;

BIN
images/flizzer_tracker.png


+ 1 - 1
init_deinit.c

@@ -18,7 +18,7 @@ FlizzerTrackerApp* init_tracker(uint32_t sample_rate, uint8_t rate, bool externa
 void deinit_tracker(FlizzerTrackerApp* tracker)
 void deinit_tracker(FlizzerTrackerApp* tracker)
 {
 {
 	sound_engine_deinit(&tracker->sound_engine);
 	sound_engine_deinit(&tracker->sound_engine);
-	tracker_engine_deinit(&tracker->tracker_engine);
+	tracker_engine_deinit(&tracker->tracker_engine, false);
 
 
 	FURI_CRITICAL_ENTER();
 	FURI_CRITICAL_ENTER();
 	LL_TIM_DeInit(TRACKER_ENGINE_TIMER);
 	LL_TIM_DeInit(TRACKER_ENGINE_TIMER);

+ 385 - 0
input/pattern.c

@@ -0,0 +1,385 @@
+#include "pattern.h"
+
+uint8_t get_field(uint8_t patternx)
+{
+	uint8_t field = 0;
+
+	if(patternx <= 1) field = 0;
+	if(patternx == 2) field = 1;
+	if(patternx == 3) field = 2;
+	if(patternx > 3) field = 3;
+
+	return field;
+}
+
+void edit_note(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta) //here we need data about last note if we place a new note
+{
+	int16_t note = tracker_engine_get_note(step);
+
+	if(note == MUS_NOTE_NONE)
+	{
+		note = tracker->current_note; //remember which note we entered earlier and use it as reference
+	}
+
+	clamp(note, delta, 0, MAX_NOTE);
+
+	set_note(step, (uint8_t)note);
+
+	tracker->current_note = (uint8_t)note;
+}
+
+void edit_instrument(TrackerSongPatternStep* step, int8_t delta)
+{
+	int16_t inst = tracker_engine_get_instrument(step);
+
+	if(inst == MUS_NOTE_INSTRUMENT_NONE)
+	{
+		if(delta > 0)
+		{
+			inst = 0;
+		}
+
+		else
+		{
+			inst = MUS_NOTE_INSTRUMENT_NONE - 1;
+		}
+	}
+
+	clamp(inst, delta, 0, MUS_NOTE_INSTRUMENT_NONE - 1);
+
+	set_instrument(step, (uint8_t)inst);
+}
+
+void edit_volume(TrackerSongPatternStep* step, int8_t delta)
+{
+	int16_t vol = tracker_engine_get_volume(step);
+
+	if(vol == MUS_NOTE_VOLUME_NONE)
+	{
+		if(delta > 0)
+		{
+			vol = 0;
+		}
+
+		else
+		{
+			vol = MUS_NOTE_VOLUME_NONE - 1;
+		}
+	}
+
+	clamp(vol, delta, 0, MUS_NOTE_VOLUME_NONE - 1);
+
+	set_volume(step, (uint8_t)vol);
+}
+
+void edit_command(TrackerSongPatternStep* step, uint8_t digit, int8_t delta)
+{
+	int32_t command = tracker_engine_get_command(step);
+
+	switch(digit)
+	{
+		case 0: //upper 7 bits
+		{
+			int16_t fx_name = ((command & 0x7f00) >> 8);
+
+			if(fx_name + delta > 35) //loop
+			{                        //0-9 and then A-Z
+				fx_name = 0;
+			}
+
+			else if(fx_name + delta < 0)
+			{
+				fx_name = 35;
+			}
+
+			else
+			{
+				fx_name += delta;
+			}
+
+			command &= 0x00ff;
+
+			command |= (fx_name << 8);
+
+			set_command(step, (uint16_t)command);
+
+			break;
+		}
+
+		case 1: //upper digit of command param
+		{
+			int8_t upper_digit = ((command & 0x00f0) >> 4);
+
+			if(upper_digit + delta > 0xf) //loop
+			{
+				upper_digit = 0;
+			}
+
+			else if(upper_digit + delta < 0)
+			{
+				upper_digit = 0xf;
+			}
+
+			else
+			{
+				upper_digit += delta;
+			}
+
+			command &= 0xff0f;
+
+			command |= (upper_digit << 4);
+
+			set_command(step, (uint16_t)command);
+
+			break;
+		}
+
+		case 2: //lower digit of command param
+		{
+			int8_t lower_digit = (command & 0x000f);
+
+			if(lower_digit + delta > 0xf) //loop
+			{
+				lower_digit = 0;
+			}
+
+			else if(lower_digit + delta < 0)
+			{
+				lower_digit = 0xf;
+			}
+
+			else
+			{
+				lower_digit += delta;
+			}
+
+			command &= 0xfff0;
+
+			command |= lower_digit;
+
+			set_command(step, (uint16_t)command);
+
+			break;
+		}
+
+		default: break;
+	}
+}
+
+void delete_field(TrackerSongPatternStep* step, uint8_t field)
+{
+	switch(field)
+	{
+		case 0: //note
+		{
+			set_note(step, MUS_NOTE_NONE);
+			break;
+		}
+
+		case 1: //instrument
+		{
+			set_instrument(step, MUS_NOTE_INSTRUMENT_NONE);
+			break;
+		}
+
+		case 2: //volume
+		{
+			set_volume(step, MUS_NOTE_VOLUME_NONE);
+			break;
+		}
+
+		case 3: //command
+		{
+			set_command(step, 0);
+			break;
+		}
+
+		default: break;
+	}
+}
+
+void edit_pattern_step(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta)
+{
+	switch(get_field(tracker->patternx))
+	{
+		case 0: //note
+		{
+			if(tracker->patternx) //editing octave
+			{
+				edit_note(tracker, step, 12 * delta);
+			}
+
+			else //editing note
+			{
+				edit_note(tracker, step, delta);
+			}
+
+			break;
+		}
+
+		case 1: //instrument
+		{
+			edit_instrument(step, delta);
+			break;
+		}
+
+		case 2: //volume
+		{
+			edit_volume(step, delta);
+			break;
+		}
+
+		case 3: //command
+		{
+			uint8_t digit = 0;
+			if(tracker->patternx == 4) digit = 0;
+			if(tracker->patternx == 5) digit = 1;
+			if(tracker->patternx == 6) digit = 2;
+			edit_command(step, digit, delta);
+			break;
+		}
+
+		default: break;
+	}
+}
+
+void pattern_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
+{
+	uint8_t sequence_position = tracker->tracker_engine.sequence_position;
+	uint8_t current_pattern = tracker->tracker_engine.song->sequence.sequence_step[sequence_position].pattern_indices[tracker->current_channel];
+	uint8_t pattern_step = tracker->tracker_engine.pattern_position;
+
+	uint8_t pattern_length = tracker->tracker_engine.song->pattern_length;
+
+	TrackerSongPattern* pattern = &tracker->tracker_engine.song->pattern[current_pattern];
+
+	TrackerSongPatternStep* step = NULL;
+
+	if(pattern_step < pattern_length)
+	{
+		step = &pattern->step[pattern_step];
+	}
+
+	if(!(step)) return;
+
+	if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && !tracker->tracker_engine.playing)
+	{
+		tracker->editing = !tracker->editing;
+	}
+
+	if(event->input.key == InputKeyOk && event->input.type == InputTypeLong)
+	{
+		if(!(tracker->editing))
+		{
+			if(tracker->tracker_engine.playing)
+			{
+				stop_song(tracker);
+			}
+
+			else
+			{
+				play_song(tracker, true);
+			}
+		}
+
+		else
+		{
+			if(get_field(tracker->patternx) == 0)
+			{
+				set_note(step, MUS_NOTE_RELEASE);
+			}
+		}
+	}
+
+	if(event->input.key == InputKeyRight && event->input.type == InputTypeShort && tracker->editing)
+	{
+		tracker->patternx++;
+
+		if(tracker->patternx > MAX_PATTERNX - 1)
+		{
+			tracker->current_channel++;
+
+			tracker->patternx = 0;
+
+			if(tracker->current_channel > SONG_MAX_CHANNELS - 1)
+			{
+				tracker->current_channel = 0;
+			}
+		}
+	}
+
+	if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort && tracker->editing)
+	{
+		tracker->patternx--;
+
+		if(tracker->patternx > MAX_PATTERNX - 1) //unsigned int overflow
+		{
+			tracker->current_channel--;
+
+			tracker->patternx = MAX_PATTERNX - 1;
+
+			if(tracker->current_channel > SONG_MAX_CHANNELS - 1) //unsigned int overflow
+			{
+				tracker->current_channel = SONG_MAX_CHANNELS - 1;
+			}
+		}
+	}
+
+	if(event->input.key == InputKeyDown && event->input.type == InputTypeShort)
+	{
+		if(!(tracker->editing))
+		{
+			tracker->tracker_engine.pattern_position++;
+
+			if(tracker->tracker_engine.pattern_position > tracker->tracker_engine.song->pattern_length - 1 && tracker->tracker_engine.sequence_position < tracker->tracker_engine.song->num_sequence_steps - 1)
+			{
+				tracker->tracker_engine.pattern_position = 0;
+				tracker->tracker_engine.sequence_position++;
+			}
+
+			else if(tracker->tracker_engine.pattern_position > tracker->tracker_engine.song->pattern_length - 1)
+			{
+				tracker->tracker_engine.pattern_position = tracker->tracker_engine.song->pattern_length - 1;
+			}
+		}
+
+		if(tracker->editing)
+		{
+			edit_pattern_step(tracker, step, -1);
+		}
+	}
+
+	if(event->input.key == InputKeyUp && event->input.type == InputTypeShort)
+	{
+		if(!(tracker->editing))
+		{
+			int16_t temp_pattern_position = tracker->tracker_engine.pattern_position - 1;
+
+			if(temp_pattern_position < 0)
+			{
+				if(tracker->tracker_engine.sequence_position > 0)
+				{
+					tracker->tracker_engine.sequence_position--;
+					tracker->tracker_engine.pattern_position = tracker->tracker_engine.song->pattern_length - 1;
+				}
+			}
+
+			else
+			{
+				tracker->tracker_engine.pattern_position--;
+			}
+		}
+
+		if(tracker->editing)
+		{
+			edit_pattern_step(tracker, step, 1);
+		}
+	}
+
+	if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && tracker->editing)
+	{
+		uint8_t field = get_field(tracker->patternx);
+
+		delete_field(step, field);
+	}
+}

+ 14 - 0
input/pattern.h

@@ -0,0 +1,14 @@
+#pragma once
+
+#include <stdio.h>
+#include <furi.h>
+#include <input/input.h>
+
+#include "../sound_engine/sound_engine_defs.h"
+#include "../tracker_engine/tracker_engine_defs.h"
+#include "../flizzer_tracker.h"
+#include "../util.h"
+
+#define MAX_PATTERNX (2 + 1 + 1 + 3)
+
+void pattern_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event);

+ 178 - 0
input/sequence.c

@@ -0,0 +1,178 @@
+#include "sequence.h"
+
+void delete_sequence_step(FlizzerTrackerApp* tracker)
+{
+	uint8_t sequence_position = tracker->tracker_engine.sequence_position;
+	uint8_t* pattern = &tracker->tracker_engine.song->sequence.sequence_step[sequence_position].pattern_indices[tracker->current_channel];
+	*pattern = 0;
+}
+
+void edit_sequence_step(FlizzerTrackerApp* tracker, int8_t delta)
+{
+	uint8_t digit = tracker->current_digit;
+
+	uint8_t sequence_position = tracker->tracker_engine.sequence_position;
+	uint8_t pattern_index = tracker->tracker_engine.song->sequence.sequence_step[sequence_position].pattern_indices[tracker->current_channel];
+
+	uint8_t* pattern = &tracker->tracker_engine.song->sequence.sequence_step[sequence_position].pattern_indices[tracker->current_channel];
+	uint8_t temp_pattern = *pattern;
+
+	switch(digit)
+	{
+		case 0: //upper nibble
+		{
+			int8_t nibble = ((pattern_index & 0xf0) >> 4);
+
+			if(nibble + delta < 0)
+			{
+				nibble = 0xf;
+			}
+
+			else if(nibble + delta > 0xf)
+			{
+				nibble = 0;
+			}
+
+			else
+			{
+				nibble += delta;
+			}
+
+			temp_pattern &= 0xf0;
+			temp_pattern |= (nibble << 4);
+
+			break;
+		}
+
+		case 1: //lower nibble
+		{
+			int8_t nibble = (pattern_index & 0x0f);
+
+			if(nibble + delta < 0)
+			{
+				nibble = 0xf;
+			}
+
+			else if(nibble + delta > 0xf)
+			{
+				nibble = 0;
+			}
+
+			else
+			{
+				nibble += delta;
+			}
+
+			temp_pattern &= 0xf0;
+			temp_pattern |= nibble;
+
+			break;
+		}
+	}
+
+	if(check_and_allocate_pattern(&tracker->song, temp_pattern))
+	{
+		*pattern = temp_pattern;
+	}
+}
+
+void sequence_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
+{
+	if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && !tracker->tracker_engine.playing)
+	{
+		tracker->editing = !tracker->editing;
+	}
+
+	/*if(event->input.key == InputKeyOk && event->input.type == InputTypeLong)
+	{
+		if(tracker->tracker_engine.playing)
+		{
+			stop_song(tracker);
+		}
+
+		else
+		{
+			play_song(tracker, true);
+		}
+	}*/
+
+	if(event->input.key == InputKeyRight && event->input.type == InputTypeShort && tracker->editing)
+	{
+		tracker->current_digit++;
+
+		if(tracker->current_digit > 1)
+		{
+			tracker->current_channel++;
+
+			tracker->current_digit = 0;
+
+			if(tracker->current_channel > SONG_MAX_CHANNELS - 1)
+			{
+				tracker->current_channel = 0;
+			}
+		}
+	}
+
+	if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort && tracker->editing)
+	{
+		tracker->current_digit--;
+
+		if(tracker->current_digit > 1) //unsigned int overflow
+		{
+			tracker->current_channel--;
+
+			tracker->current_digit = 1;
+
+			if(tracker->current_channel > SONG_MAX_CHANNELS - 1) //unsigned int overflow
+			{
+				tracker->current_channel = SONG_MAX_CHANNELS - 1;
+			}
+		}
+	}
+
+	if(event->input.key == InputKeyDown && event->input.type == InputTypeShort)
+	{
+		if(!(tracker->editing))
+		{
+			tracker->tracker_engine.sequence_position++;
+
+			if(tracker->tracker_engine.sequence_position > tracker->tracker_engine.song->num_sequence_steps)
+			{
+				tracker->tracker_engine.sequence_position = 0;
+			}
+		}
+
+		if(tracker->editing)
+		{
+			edit_sequence_step(tracker, -1);
+		}
+	}
+
+	if(event->input.key == InputKeyUp && event->input.type == InputTypeShort)
+	{
+		if(!(tracker->editing))
+		{
+			int16_t temp_sequence_position = tracker->tracker_engine.sequence_position - 1;
+
+			if(temp_sequence_position < 0)
+			{
+				tracker->tracker_engine.sequence_position = tracker->tracker_engine.song->num_sequence_steps;
+			}
+
+			else
+			{
+				tracker->tracker_engine.sequence_position--;
+			}
+		}
+
+		if(tracker->editing)
+		{
+			edit_sequence_step(tracker, 1);
+		}
+	}
+
+	if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && tracker->editing)
+	{
+		delete_sequence_step(tracker);
+	}
+}

+ 12 - 0
input/sequence.h

@@ -0,0 +1,12 @@
+#pragma once
+
+#include <stdio.h>
+#include <furi.h>
+#include <input/input.h>
+
+#include "../sound_engine/sound_engine_defs.h"
+#include "../tracker_engine/tracker_engine_defs.h"
+#include "../flizzer_tracker.h"
+#include "../util.h"
+
+void sequence_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event);

+ 129 - 0
input/songinfo.c

@@ -0,0 +1,129 @@
+#include "songinfo.h"
+
+#include <gui/modules/text_input.h>
+
+void edit_songinfo_param(FlizzerTrackerApp* tracker, uint8_t selected_param, int8_t delta)
+{
+	if(!(tracker->current_digit))
+	{
+		delta *= 16;
+	}
+
+	switch(selected_param)
+	{
+		case SI_PATTERNPOS:
+		{
+			if((int16_t)tracker->song.pattern_length + (int16_t)delta > 1 && (int16_t)tracker->song.pattern_length + (int16_t)delta <= 0xff)
+			{
+				tracker->song.pattern_length += delta;
+			}
+
+			break;
+		}
+
+		case SI_SEQUENCEPOS:
+		{
+			if((int16_t)tracker->song.num_sequence_steps + (int16_t)delta > 1 && (int16_t)tracker->song.num_sequence_steps + (int16_t)delta <= 0xff)
+			{
+				tracker->song.num_sequence_steps += delta;
+			}
+
+			break;
+		}
+
+		case SI_SONGSPEED:
+		{
+			if((int16_t)tracker->song.speed + (int16_t)delta > 1 && (int16_t)tracker->song.speed + (int16_t)delta <= 0xff)
+			{
+				tracker->song.speed += delta;
+			}
+
+			break;
+		}
+
+		case SI_SONGRATE:
+		{
+			if((int16_t)tracker->song.rate + (int16_t)delta > 1 && (int16_t)tracker->song.rate + (int16_t)delta <= 0xff)
+			{
+				tracker->song.rate += delta;
+			}
+
+			break;
+		}
+
+		case SI_MASTERVOL:
+		{
+			if((int16_t)tracker->tracker_engine.master_volume + (int16_t)delta > 0 && (int16_t)tracker->tracker_engine.master_volume + (int16_t)delta <= 0xff)
+			{
+				tracker->tracker_engine.master_volume += delta;
+			}
+
+			break;
+		}
+
+		case SI_SONGNAME:
+		{
+			break;
+		}
+
+		default: break;
+	}
+}
+
+void songinfo_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
+{
+	if(event->input.key == InputKeyOk && event->input.type == InputTypeShort && !tracker->tracker_engine.playing)
+	{
+		tracker->editing = !tracker->editing;
+	}
+
+	if(event->input.key == InputKeyRight && event->input.type == InputTypeShort && tracker->editing)
+	{
+		tracker->current_digit++;
+
+		if(tracker->current_digit > 1)
+		{
+			tracker->selected_param++;
+
+			tracker->current_digit = 0;
+
+			if(tracker->selected_param > SI_PARAMS - 1)
+			{
+				tracker->selected_param = 0;
+			}
+		}
+	}
+
+	if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort && tracker->editing)
+	{
+		tracker->current_digit--;
+
+		if(tracker->current_digit > 1) //unsigned int overflow
+		{
+			tracker->selected_param--;
+
+			tracker->current_digit = 1;
+
+			if(tracker->selected_param > SI_PARAMS - 1) //unsigned int overflow
+			{
+				tracker->selected_param = SI_PARAMS - 1;
+			}
+		}
+	}
+
+	if(event->input.key == InputKeyDown && event->input.type == InputTypeShort)
+	{
+		if(tracker->editing)
+		{
+			edit_songinfo_param(tracker, tracker->selected_param, -1);
+		}
+	}
+
+	if(event->input.key == InputKeyUp && event->input.type == InputTypeShort)
+	{
+		if(tracker->editing)
+		{
+			edit_songinfo_param(tracker, tracker->selected_param, 1);
+		}
+	}
+}

+ 12 - 0
input/songinfo.h

@@ -0,0 +1,12 @@
+#pragma once
+
+#include <stdio.h>
+#include <furi.h>
+#include <input/input.h>
+
+#include "../sound_engine/sound_engine_defs.h"
+#include "../tracker_engine/tracker_engine_defs.h"
+#include "../flizzer_tracker.h"
+#include "../util.h"
+
+void songinfo_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event);

+ 25 - 362
input_event.c

@@ -1,383 +1,34 @@
 #include "input_event.h"
 #include "input_event.h"
 
 
-uint8_t get_field(uint8_t patternx)
+void cycle_focus(FlizzerTrackerApp* tracker)
 {
 {
-	uint8_t field = 0;
-
-	if(patternx <= 1) field = 0;
-	if(patternx == 2) field = 1;
-	if(patternx == 3) field = 2;
-	if(patternx > 3) field = 3;
-
-	return field;
-}
-
-void edit_note(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta) //here we need data about last note if we place a new note
-{
-	int16_t note = tracker_engine_get_note(step);
-
-	if(note == MUS_NOTE_NONE)
-	{
-		note = tracker->current_note; //remember which note we entered earlier and use it as reference
-	}
-
-	clamp(note, delta, 0, MAX_NOTE);
-
-	set_note(step, (uint8_t)note);
-
-	tracker->current_note = (uint8_t)note;
-}
-
-void edit_instrument(TrackerSongPatternStep* step, int8_t delta)
-{
-	int16_t inst = tracker_engine_get_instrument(step);
-
-	if(inst == MUS_NOTE_INSTRUMENT_NONE)
+	switch(tracker->mode)
 	{
 	{
-		if(delta > 0)
-		{
-			inst = 0;
-		}
-
-		else
+		case PATTERN_VIEW:
 		{
 		{
-			inst = MUS_NOTE_INSTRUMENT_NONE - 1;
-		}
-	}
-
-	clamp(inst, delta, 0, MUS_NOTE_INSTRUMENT_NONE - 1);
+			tracker->focus++;
 
 
-	set_instrument(step, (uint8_t)inst);
-}
-
-void edit_volume(TrackerSongPatternStep* step, int8_t delta)
-{
-	int16_t vol = tracker_engine_get_volume(step);
-
-	if(vol == MUS_NOTE_VOLUME_NONE)
-	{
-		if(delta > 0)
-		{
-			vol = 0;
-		}
-
-		else
-		{
-			vol = MUS_NOTE_VOLUME_NONE - 1;
-		}
-	}
-
-	clamp(vol, delta, 0, MUS_NOTE_VOLUME_NONE - 1);
-
-	set_volume(step, (uint8_t)vol);
-}
-
-void edit_command(TrackerSongPatternStep* step, uint8_t digit, int8_t delta)
-{
-	int32_t command = tracker_engine_get_command(step);
-
-	switch(digit)
-	{
-		case 0: //upper 7 bits
-		{
-			int16_t fx_name = ((command & 0x7f00) >> 8);
-
-			if(fx_name + delta > 35) //loop
-			{                        //0-9 and then A-Z
-				fx_name = 0;
-			}
-
-			else if(fx_name + delta < 0)
-			{
-				fx_name = 35;
-			}
-
-			else
+			if(tracker->focus > EDIT_SONGINFO)
 			{
 			{
-				fx_name += delta;
+				tracker->focus = EDIT_PATTERN;
 			}
 			}
-
-			command &= 0x00ff;
-
-			command |= (fx_name << 8);
-
-			set_command(step, (uint16_t)command);
-
-			break;
-		}
-
-		case 1: //upper digit of command param
-		{
-			int8_t upper_digit = ((command & 0x00f0) >> 4);
-
-			if(upper_digit + delta > 0xf) //loop
-			{
-				upper_digit = 0;
-			}
-
-			else if(upper_digit + delta < 0)
-			{
-				upper_digit = 0xf;
-			}
-
-			else
-			{
-				upper_digit += delta;
-			}
-
-			command &= 0xff0f;
-
-			command |= (upper_digit << 4);
-
-			set_command(step, (uint16_t)command);
-
-			break;
-		}
-
-		case 2: //lower digit of command param
-		{
-			int8_t lower_digit = (command & 0x000f);
-
-			if(lower_digit + delta > 0xf) //loop
-			{
-				lower_digit = 0;
-			}
-
-			else if(lower_digit + delta < 0)
-			{
-				lower_digit = 0xf;
-			}
-
-			else
-			{
-				lower_digit += delta;
-			}
-
-			command &= 0xfff0;
-
-			command |= lower_digit;
-
-			set_command(step, (uint16_t)command);
-
-			break;
 		}
 		}
 
 
 		default: break;
 		default: break;
 	}
 	}
 }
 }
 
 
-void delete_field(TrackerSongPatternStep* step, uint8_t field)
-{
-	switch(field)
-	{
-		case 0: //note
-		{
-			set_note(step, MUS_NOTE_NONE);
-			break;
-		}
-
-		case 1: //instrument
-		{
-			set_instrument(step, MUS_NOTE_INSTRUMENT_NONE);
-			break;
-		}
-
-		case 2: //volume
-		{
-			set_volume(step, MUS_NOTE_VOLUME_NONE);
-			break;
-		}
-
-		case 3: //command
-		{
-			set_command(step, 0);
-			break;
-		}
-
-		default: break;
-	}
-}
-
-void edit_pattern_step(FlizzerTrackerApp* tracker, TrackerSongPatternStep* step, int8_t delta)
-{
-	switch(get_field(tracker->patternx))
-	{
-		case 0: //note
-		{
-			if(tracker->patternx) //editing octave
-			{
-				edit_note(tracker, step, 12 * delta);
-			}
-
-			else //editing note
-			{
-				edit_note(tracker, step, delta);
-			}
-
-			break;
-		}
-
-		case 1: //instrument
-		{
-			edit_instrument(step, delta);
-			break;
-		}
-
-		case 2: //volume
-		{
-			edit_volume(step, delta);
-			break;
-		}
-
-		case 3: //command
-		{
-			uint8_t digit = 0;
-			if(tracker->patternx == 4) digit = 0;
-			if(tracker->patternx == 5) digit = 1;
-			if(tracker->patternx == 6) digit = 2;
-			edit_command(step, digit, delta);
-			break;
-		}
-
-		default: break;
-	}
-}
-
-void pattern_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
+void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
 {
 {
-	uint8_t sequence_position = tracker->tracker_engine.sequence_position;
-	uint8_t current_pattern = tracker->tracker_engine.song->sequence.sequence_step[sequence_position].pattern_indices[tracker->current_channel];
-	uint8_t pattern_step = tracker->tracker_engine.pattern_position;
-
-	uint8_t pattern_length = tracker->tracker_engine.song->pattern_length;
-
-	TrackerSongPattern* pattern = &tracker->tracker_engine.song->pattern[current_pattern];
-
-	TrackerSongPatternStep* step = NULL;
-
-	if(pattern_step < pattern_length)
-	{
-		step = &pattern->step[pattern_step];
-	}
-
-	if(!(step)) return;
-
-	if(event->input.key == InputKeyOk && event->input.type == InputTypeShort)
-	{
-		tracker->editing = !tracker->editing;
-	}
-
-	if(event->input.key == InputKeyOk && event->input.type == InputTypeLong)
-	{
-		if(tracker->tracker_engine.playing)
-		{
-			stop_song(tracker);
-		}
-
-		else
-		{
-			play_song(tracker, true);
-		}
-	}
-
-	if(event->input.key == InputKeyRight && event->input.type == InputTypeShort && tracker->editing)
-	{
-		tracker->patternx++;
-
-		if(tracker->patternx > MAX_PATTERNX - 1)
-		{
-			tracker->current_channel++;
-
-			tracker->patternx = 0;
-
-			if(tracker->current_channel > SONG_MAX_CHANNELS - 1)
-			{
-				tracker->current_channel = 0;
-			}
-		}
-	}
-
-	if(event->input.key == InputKeyLeft && event->input.type == InputTypeShort && tracker->editing)
-	{
-		tracker->patternx--;
-
-		if(tracker->patternx > MAX_PATTERNX - 1) //unsigned int overflow
-		{
-			tracker->current_channel--;
-
-			tracker->patternx = MAX_PATTERNX - 1;
-
-			if(tracker->current_channel > SONG_MAX_CHANNELS - 1) //unsigned int overflow
-			{
-				tracker->current_channel = SONG_MAX_CHANNELS - 1;
-			}
-		}
-	}
-
-	if(event->input.key == InputKeyDown && event->input.type == InputTypeShort)
-	{
-		if(!(tracker->editing))
-		{
-			tracker->tracker_engine.pattern_position++;
-
-			if(tracker->tracker_engine.pattern_position > tracker->tracker_engine.song->pattern_length - 1 && tracker->tracker_engine.sequence_position < tracker->tracker_engine.song->num_sequence_steps - 1)
-			{
-				tracker->tracker_engine.pattern_position = 0;
-				tracker->tracker_engine.sequence_position++;
-			}
-
-			else if(tracker->tracker_engine.pattern_position > tracker->tracker_engine.song->pattern_length - 1)
-			{
-				tracker->tracker_engine.pattern_position = tracker->tracker_engine.song->pattern_length - 1;
-			}
-		}
-
-		if(tracker->editing)
-		{
-			edit_pattern_step(tracker, step, -1);
-		}
-	}
-
-	if(event->input.key == InputKeyUp && event->input.type == InputTypeShort)
-	{
-		if(!(tracker->editing))
-		{
-			int16_t temp_pattern_position = tracker->tracker_engine.pattern_position - 1;
-
-			if(temp_pattern_position < 0)
-			{
-				if(tracker->tracker_engine.sequence_position > 0)
-				{
-					tracker->tracker_engine.sequence_position--;
-					tracker->tracker_engine.pattern_position = tracker->tracker_engine.song->pattern_length - 1;
-				}
-			}
-
-			else
-			{
-				tracker->tracker_engine.pattern_position--;
-			}
-		}
-
-		if(tracker->editing)
-		{
-			edit_pattern_step(tracker, step, 1);
-		}
-	}
-
-	if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && tracker->editing)
+	/*if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && event->period > 0 && event->period < 200) 
 	{
 	{
-		uint8_t field = get_field(tracker->patternx);
+		cycle_focus(tracker);
+	}*/
 
 
-		delete_field(step, field);
-	}
-}
-
-void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
-{
-	if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && event->period > 0 && event->period < 200) 
+	if(event->input.key == InputKeyBack && event->input.type == InputTypeShort && !(tracker->editing)) 
 	{
 	{
-		//TODO: add cycle focus
+		cycle_focus(tracker);
+		return;
 	}
 	}
 
 
 	// Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения
 	// Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения
@@ -395,6 +46,18 @@ void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
 			break;
 			break;
 		}
 		}
 
 
+		case EDIT_SEQUENCE:
+		{
+			sequence_edit_event(tracker, event);
+			break;
+		}
+
+		case EDIT_SONGINFO:
+		{
+			songinfo_edit_event(tracker, event);
+			break;
+		}
+
 		default: break;
 		default: break;
 	}
 	}
 }
 }

+ 3 - 4
input_event.h

@@ -2,16 +2,15 @@
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <furi.h>
 #include <furi.h>
-#include <gui/gui.h>
 #include <input/input.h>
 #include <input/input.h>
 
 
-#include "sound_engine/freqs.h"
-#include "sound_engine/sound_engine_filter.h"
 #include "sound_engine/sound_engine_defs.h"
 #include "sound_engine/sound_engine_defs.h"
 #include "tracker_engine/tracker_engine_defs.h"
 #include "tracker_engine/tracker_engine_defs.h"
 #include "flizzer_tracker.h"
 #include "flizzer_tracker.h"
 #include "util.h"
 #include "util.h"
 
 
-#define MAX_PATTERNX (2 + 1 + 1 + 3)
+#include "input/pattern.h"
+#include "input/sequence.h"
+#include "input/songinfo.h"
 
 
 void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event);
 void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event);

+ 1 - 1
sound_engine/sound_engine.c

@@ -153,7 +153,7 @@ void sound_engine_fill_buffer(SoundEngine* sound_engine, uint16_t* audio_buffer,
 					}
 					}
 				}
 				}
 
 
-				output += ((channel_output_final[chan]) / (64 * 4)); //2 more bits so all channels fit
+				output += ((channel_output_final[chan]) / (int32_t)(64 * 4)); //2 more bits so all channels fit
 			}
 			}
 		}
 		}
 
 

+ 1 - 1
sound_engine/sound_engine_adsr.c

@@ -61,5 +61,5 @@ int32_t sound_engine_cycle_and_output_adsr(int32_t input, SoundEngine* eng, Soun
 		}
 		}
 	}
 	}
 
 
-	return (int32_t)((int64_t)input * adsr->envelope / MAX_ADSR * (int64_t)adsr->volume / MAX_ADSR_VOLUME);
+	return (int32_t)((int64_t)input * (int64_t)adsr->envelope / (int64_t)MAX_ADSR * (int64_t)adsr->volume / (int64_t)MAX_ADSR_VOLUME);
 }
 }

+ 14 - 8
tracker_engine/tracker_engine.c

@@ -10,32 +10,36 @@ void tracker_engine_init(TrackerEngine* tracker_engine, uint8_t rate, SoundEngin
 	tracker_engine_init_hardware(rate);
 	tracker_engine_init_hardware(rate);
 
 
 	tracker_engine->sound_engine = sound_engine;
 	tracker_engine->sound_engine = sound_engine;
+	tracker_engine->rate = rate;
 }
 }
 
 
-void tracker_engine_deinit_song(TrackerSong* song)
+void tracker_engine_deinit_song(TrackerSong* song, bool free_song)
 {
 {
-	for(int i = 0; i < song->num_patterns; ++i)
+	for(int i = 0; i < MAX_PATTERNS; i++)
 	{
 	{
-		if(song->pattern[i].step)
+		if(song->pattern[i].step != NULL)
 		{
 		{
 			free(song->pattern[i].step);
 			free(song->pattern[i].step);
 		}
 		}
 	}
 	}
 
 
-	for(int i = 0; i < song->num_instruments; ++i)
+	for(int i = 0; i < MAX_INSTRUMENTS; i++)
 	{
 	{
-		if(song->instrument[i])
+		if(song->instrument[i] != NULL)
 		{
 		{
 			free(song->instrument[i]);
 			free(song->instrument[i]);
 		}
 		}
 	}
 	}
 
 
-	free(song);
+	if(free_song)
+	{
+		free(song);
+	}
 }
 }
 
 
-void tracker_engine_deinit(TrackerEngine* tracker_engine)
+void tracker_engine_deinit(TrackerEngine* tracker_engine, bool free_song)
 {
 {
-	tracker_engine_deinit_song(tracker_engine->song);
+	tracker_engine_deinit_song(tracker_engine->song, free_song);
 
 
 	furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdTim1UpTim16, 13, NULL, NULL);
 	furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdTim1UpTim16, 13, NULL, NULL);
 	tracker_engine_stop();
 	tracker_engine_stop();
@@ -204,6 +208,8 @@ void tracker_engine_advance_tick(TrackerEngine* tracker_engine)
 
 
 				//TODO: add setting slide speed if slide command is there
 				//TODO: add setting slide speed if slide command is there
 
 
+				//te_channel->slide_speed = pinst->slide_speed;
+
 				uint8_t prev_adsr_volume = se_channel->adsr.volume;
 				uint8_t prev_adsr_volume = se_channel->adsr.volume;
 
 
 				tracker_engine_trigger_instrument_internal(tracker_engine, chan, pinst, note << 8);
 				tracker_engine_trigger_instrument_internal(tracker_engine, chan, pinst, note << 8);

+ 1 - 1
tracker_engine/tracker_engine.h

@@ -3,7 +3,7 @@
 #include "tracker_engine_defs.h"
 #include "tracker_engine_defs.h"
 
 
 void tracker_engine_init(TrackerEngine* tracker_engine, uint8_t rate, SoundEngine* sound_engine);
 void tracker_engine_init(TrackerEngine* tracker_engine, uint8_t rate, SoundEngine* sound_engine);
-void tracker_engine_deinit(TrackerEngine* tracker_engine);
+void tracker_engine_deinit(TrackerEngine* tracker_engine, bool free_song);
 void tracker_engine_advance_tick(TrackerEngine* tracker_engine);
 void tracker_engine_advance_tick(TrackerEngine* tracker_engine);
 
 
 uint8_t tracker_engine_get_note(TrackerSongPatternStep* step);
 uint8_t tracker_engine_get_note(TrackerSongPatternStep* step);

+ 1 - 0
tracker_engine/tracker_engine_defs.h

@@ -156,6 +156,7 @@ typedef struct
 	uint8_t pattern_length;
 	uint8_t pattern_length;
 
 
 	uint8_t speed, rate;
 	uint8_t speed, rate;
+	uint8_t master_volume;
 
 
 	bool playing; //if we reach the end of the song and song does not loop we just stop there
 	bool playing; //if we reach the end of the song and song does not loop we just stop there
 } TrackerEngine;
 } TrackerEngine;

+ 61 - 0
util.c

@@ -49,4 +49,65 @@ void stop_song(FlizzerTrackerApp* tracker)
 	tracker->tracker_engine.playing = false;
 	tracker->tracker_engine.playing = false;
 	tracker->editing = tracker->was_editing;
 	tracker->editing = tracker->was_editing;
 	stop();
 	stop();
+}
+
+bool is_pattern_empty(TrackerSong* song, uint8_t pattern)
+{
+	TrackerSongPattern song_pattern = song->pattern[pattern];
+
+	for(int i = 0; i < song->pattern_length; i++)
+	{
+		TrackerSongPatternStep* step = &song_pattern.step[i];
+
+		if(tracker_engine_get_note(step) != MUS_NOTE_NONE ||
+		tracker_engine_get_instrument(step) != MUS_NOTE_INSTRUMENT_NONE ||
+		tracker_engine_get_volume(step) != MUS_NOTE_VOLUME_NONE ||
+		tracker_engine_get_command(step) != 0)
+		{
+			return false;
+		}
+	}
+
+	return true;
+}
+
+void set_empty_pattern(TrackerSong* song, uint8_t pattern)
+{
+	TrackerSongPattern song_pattern = song->pattern[pattern];
+
+	for(int i = 0; i < song->pattern_length; i++)
+	{
+		TrackerSongPatternStep* step = &song_pattern.step[i];
+
+		set_note(step, MUS_NOTE_NONE);
+		set_instrument(step, MUS_NOTE_INSTRUMENT_NONE);
+		set_volume(step, MUS_NOTE_VOLUME_NONE);
+		set_command(step, 0);
+	}
+}
+
+bool check_and_allocate_pattern(TrackerSong* song, uint8_t pattern)
+{
+	if(pattern < song->num_patterns) //we can set this pattern since it already exists
+	{
+		return true;
+	}
+
+	else
+	{
+		if(song->pattern[pattern - 1].step == NULL) return false; //if we hop through several patterns (e.g. editing upper digit)
+
+		if(!(is_pattern_empty(song, pattern - 1))) //don't let the user flood the song with empty patterns
+		{
+			song->pattern[pattern].step = malloc(sizeof(TrackerSongPatternStep) * song->pattern_length);
+			set_empty_pattern(song, pattern);
+			song->num_patterns++;
+			return true;
+		}
+
+		else
+		{
+			return false;
+		}
+	}
 }
 }

+ 5 - 1
util.h

@@ -15,4 +15,8 @@ void set_volume(TrackerSongPatternStep* step, uint8_t vol);
 void set_command(TrackerSongPatternStep* step, uint16_t command);
 void set_command(TrackerSongPatternStep* step, uint16_t command);
 
 
 void play_song(FlizzerTrackerApp* tracker, bool from_cursor);
 void play_song(FlizzerTrackerApp* tracker, bool from_cursor);
-void stop_song(FlizzerTrackerApp* tracker);
+void stop_song(FlizzerTrackerApp* tracker);
+
+void set_empty_pattern(TrackerSong* song, uint8_t pattern);
+bool is_pattern_empty(TrackerSong* song, uint8_t pattern);
+bool check_and_allocate_pattern(TrackerSong* song, uint8_t pattern);

+ 105 - 3
view/pattern_editor.c

@@ -1,5 +1,7 @@
 #include "pattern_editor.h"
 #include "pattern_editor.h"
 
 
+#include <flizzer_tracker_icons.h>
+
 #define PATTERN_EDITOR_Y (64 - (6 * 5) - 1)
 #define PATTERN_EDITOR_Y (64 - (6 * 5) - 1)
 
 
 static const char* notenames[] =
 static const char* notenames[] =
@@ -11,7 +13,20 @@ char* notename(uint8_t note)
 {
 {
 	static char buffer[4];
 	static char buffer[4];
 
 
-	snprintf(buffer, sizeof(buffer), "%s%d", notenames[note % 12], note / 12);
+	if(note == MUS_NOTE_CUT)
+	{
+		snprintf(buffer, sizeof(buffer), "%s", "OFF");
+	}
+
+	if(note == MUS_NOTE_RELEASE)
+	{
+		snprintf(buffer, sizeof(buffer), "   ");
+	}
+
+	else
+	{
+		snprintf(buffer, sizeof(buffer), "%s%d", notenames[note % 12], note / 12);
+	}
 
 
 	return buffer;
 	return buffer;
 }
 }
@@ -100,6 +115,11 @@ void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker)
 				snprintf(buffer, sizeof(buffer), "%s%c%c%s", (note == MUS_NOTE_NONE ? "---" : notename(note)), inst_ch, vol_ch, command_buffer);
 				snprintf(buffer, sizeof(buffer), "%s%c%c%s", (note == MUS_NOTE_NONE ? "---" : notename(note)), inst_ch, vol_ch, command_buffer);
 
 
 				canvas_draw_str(canvas, string_x, string_y, buffer);
 				canvas_draw_str(canvas, string_x, string_y, buffer);
+
+				if(note == MUS_NOTE_RELEASE)
+				{
+					canvas_draw_icon(canvas, string_x, string_y - 5, &I_note_release);
+				}
 			}
 			}
 		}
 		}
 	}
 	}
@@ -113,10 +133,56 @@ void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker)
 	}
 	}
 }
 }
 
 
+#define SEQ_SLIDER_X (4 * (4 * 2 + 1) + 2)
+#define SEQ_SLIDER_Y (32)
+
 void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker)
 void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker)
 {
 {
-	UNUSED(canvas);
-	UNUSED(tracker);
+	char buffer[4];
+
+	uint8_t sequence_position = tracker->tracker_engine.sequence_position;
+
+	for(int pos = sequence_position - 2; pos < sequence_position + 3; pos++)
+	{
+		if(pos >= 0 && pos < tracker->song.num_sequence_steps)
+		{
+			for(int i = 0; i < SONG_MAX_CHANNELS; ++i)
+			{
+				uint8_t current_pattern = tracker->tracker_engine.song->sequence.sequence_step[pos].pattern_indices[i];
+
+				uint8_t x = i * (4 * 2 + 1) + 3;
+				uint8_t y = (pos - (sequence_position - 2)) * 6 + 5;
+
+				snprintf(buffer, sizeof(buffer), "%02X", current_pattern);
+				canvas_draw_str(canvas, x, y, buffer);
+			}
+		}
+	}
+
+	//TODO: add song loop indication
+
+	canvas_set_color(canvas, ColorBlack);
+
+	canvas_draw_line(canvas, SEQ_SLIDER_X, 0, SEQ_SLIDER_X + 2, 0);
+	canvas_draw_line(canvas, SEQ_SLIDER_X, SEQ_SLIDER_Y, SEQ_SLIDER_X + 2, SEQ_SLIDER_Y);
+
+	canvas_draw_line(canvas, SEQ_SLIDER_X, 0, SEQ_SLIDER_X, SEQ_SLIDER_Y);
+	canvas_draw_line(canvas, SEQ_SLIDER_X + 2, 0, SEQ_SLIDER_X + 2, SEQ_SLIDER_Y);
+
+	uint8_t start_pos = sequence_position * (SEQ_SLIDER_Y - 2) / tracker->song.num_sequence_steps + 1;
+	uint8_t slider_length = (SEQ_SLIDER_Y - 2) / tracker->song.num_sequence_steps + 1;
+
+	canvas_draw_line(canvas, SEQ_SLIDER_X + 1, start_pos, SEQ_SLIDER_X + 1, (start_pos + slider_length));
+
+	canvas_set_color(canvas, ColorXOR);
+
+	if(tracker->editing && tracker->focus == EDIT_SEQUENCE)
+	{
+		uint8_t x = tracker->current_channel * (4 + 4 + 1) + (tracker->current_digit ? 4 : 0) + 2;
+		uint8_t y = 11;
+
+		canvas_draw_box(canvas, x, y, 5, 7);
+	}
 }
 }
 
 
 #define member_size(type, member) sizeof(((type *)0)->member)
 #define member_size(type, member) sizeof(((type *)0)->member)
@@ -131,8 +197,44 @@ uint32_t calculate_song_size(TrackerSong* song)
 	return song_size;
 	return song_size;
 }
 }
 
 
+void draw_generic_n_digit_field(FlizzerTrackerApp* tracker, Canvas* canvas, uint8_t focus, uint8_t param, const char* text, uint8_t x, uint8_t y, uint8_t digits) //last 1-2 symbols are digits we are editing
+{
+	canvas_draw_str(canvas, x, y, text);
+
+	if(tracker->focus == focus && tracker->selected_param == param && tracker->editing)
+	{
+		if(param != SI_SONGNAME && param != SI_INSTRUMENTNAME)
+		{
+			canvas_draw_box(canvas, x + strlen(text) * 4 - digits * 4 + tracker->current_digit * 4 - 1, y - 6, 5, 7);
+		}
+
+		else
+		{
+			canvas_draw_box(canvas, x - 1, y - 6, strlen(text) * 4 + 2, 7);
+		}
+	}
+}
+
 void draw_songinfo_view(Canvas* canvas, FlizzerTrackerApp* tracker)
 void draw_songinfo_view(Canvas* canvas, FlizzerTrackerApp* tracker)
 {
 {
+	char buffer[30];
+
+	snprintf(buffer, sizeof(buffer), "PAT.P.%02X/%02X", tracker->tracker_engine.pattern_position, tracker->song.pattern_length - 1);
+	draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_PATTERNPOS, buffer, 42, 5, 2);
+	snprintf(buffer, sizeof(buffer), "SEQ.P.%02X/%02X", tracker->tracker_engine.sequence_position, tracker->song.num_sequence_steps);
+	draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_SEQUENCEPOS, buffer, 42, 11, 2);
+	snprintf(buffer, sizeof(buffer), "SPD.%02X", tracker->song.speed);
+	draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_SONGSPEED, buffer, 42, 17, 2);
+	snprintf(buffer, sizeof(buffer), "RATE %02X", tracker->song.rate);
+	draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_SONGRATE, buffer, 42 + 4 * 7, 17, 2);
+	snprintf(buffer, sizeof(buffer), "VOL %02X", tracker->tracker_engine.master_volume);
+	draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_MASTERVOL, buffer, 42 + 4 * 7 + 4 * 8, 17, 2);
+
+	snprintf(buffer, sizeof(buffer), "SONG:%s", tracker->song.song_name);
+	draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_SONGNAME, buffer, 42, 23, 1);
+	snprintf(buffer, sizeof(buffer), "INST:%s", tracker->song.instrument[tracker->current_instrument]->name);
+	draw_generic_n_digit_field(tracker, canvas, EDIT_SONGINFO, SI_INSTRUMENTNAME, buffer, 42, 29, 1);
+
 	uint32_t song_size = calculate_song_size(&tracker->song);
 	uint32_t song_size = calculate_song_size(&tracker->song);
 	uint32_t free_bytes = memmgr_get_free_heap();
 	uint32_t free_bytes = memmgr_get_free_heap();
 	canvas_draw_line(canvas, 128 - 4 * 10 - 2, 0, 128 - 4 * 10 - 2, 10);
 	canvas_draw_line(canvas, 128 - 4 * 10 - 2, 0, 128 - 4 * 10 - 2, 10);