Prechádzať zdrojové kódy

pattern editing functionality, double button press correct reading

LTVA1 3 rokov pred
rodič
commit
20c4e0db0e

+ 1 - 0
diskop.c

@@ -0,0 +1 @@
+#include "diskop.h"

+ 6 - 0
diskop.h

@@ -0,0 +1,6 @@
+#pragma once
+
+#include "sound_engine/sound_engine_defs.h"
+#include "tracker_engine/tracker_engine_defs.h"
+
+#define INST_FILE_SIG "FZT!INST"

+ 39 - 332
flizzer_tracker.c

@@ -1,12 +1,12 @@
 #include "flizzer_tracker.h"
 #include "init_deinit.h"
-#include "event.h"
+#include "util.h"
+#include "input_event.h"
 #include "view/pattern_editor.h"
 
 #define FLIZZER_TRACKER_FOLDER "/ext/flizzer_tracker"
 
 #include <flizzer_tracker_icons.h>
-
 /*
 Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1
 Copyright: 
@@ -38,136 +38,63 @@ const uint8_t u8g2_font_tom_thumb_4x6_tr[725] U8G2_FONT_SECTION("u8g2_font_tom_t
 	"y\11\227\307$\225dJ\0z\7\223\310\254\221\6{\10\227\310\251\32D\1|\6\265\310(\1}\11"
 	"\227\310\310\14RR\0~\6\213\313\215\4\0\0\0\4\377\377\0";
 
-typedef enum
-{
-	EventTypeInput,
-} EventType;
-
-typedef struct
-{
-	EventType type;
-	InputEvent input;
-} FlizzerTrackerEvent;
-
-typedef enum
-{
-	PARAM_FREQUENCY,
-	PARAM_WAVEFORM,
-	PARAM_PW,
-	PARAM_ENABLE_FILTER,
-	PARAM_FILTER_CUTOFF,
-	PARAM_FILTER_RESONANCE,
-	PARAM_FILTER_TYPE,
-} SelectedParam;
-
-const char* wave_names[] = 
-{
-	"NONE",
-	"NOISE",
-	"PULSE",
-	"TRIANGLE",
-	"SAWTOOTH",
-	"METAL NOISE",
-	"SINE",
-};
-
-const char* filter_names[] = 
-{
-	"NONE",
-	"LOWPASS",
-	"HIGHPASS",
-	"BANDPASS",
-};
-
-#define NUM_PARAMS 10
-
 static void draw_callback(Canvas* canvas, void* ctx) 
 {
 	FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx;
 
-	canvas_clear(canvas);
-
 	canvas_set_color(canvas, ColorXOR);
 
 	canvas_set_custom_font(canvas, u8g2_font_tom_thumb_4x6_tr);
 
-	draw_pattern_view(canvas, tracker);
-
-	/*char buffer[30] = {0};
-
-	snprintf(buffer, 20, "FREQUENCY:%ld Hz", tracker->frequency);
-	
-	canvas_draw_str(canvas, 0, 6, buffer);
-
-	snprintf(buffer, 20, "WAVEFORM:%s", wave_names[tracker->current_waveform_index]);
-	
-	canvas_draw_str(canvas, 0, 12, buffer);
-
-	snprintf(buffer, 20, "PULSE WIDTH:$%03X", tracker->pw);
-	
-	canvas_draw_str(canvas, 0, 18, buffer);
-
-	snprintf(buffer, 20, "FILTER:%s", (tracker->flags & SE_ENABLE_FILTER) ? "ENABLED" : "DISABLED");
-
-	canvas_draw_str(canvas, 0, 24, buffer);
-
-	snprintf(buffer, 20, "CUTOFF:%d", tracker->cutoff);
-	
-	canvas_draw_str(canvas, 0, 30, buffer);
-
-	snprintf(buffer, 20, "RESONANCE:%d", tracker->resonance);
-	
-	canvas_draw_str(canvas, 0, 36, buffer);
-
-	snprintf(buffer, 20, "TYPE:%s", filter_names[tracker->filter_type]);
-	
-	canvas_draw_str(canvas, 0, 42, buffer);
-
-	snprintf(buffer, 20, "TR.ENG.CALLS: %d", tracker->sound_engine.channel[0].adsr.volume);
-	
-	canvas_draw_str(canvas, 0, 48, buffer);
-
-	canvas_draw_str(canvas, 70, tracker->selected_param * 6 + 6, "<");
-
-    uint32_t bytes = memmgr_get_free_heap();
-	snprintf(buffer, 20, "BYTES FREE:%ld", bytes);
-
-	canvas_draw_str(canvas, 0, 64, buffer);*/
+	switch(tracker->mode)
+	{
+		case PATTERN_VIEW:
+		{
+			draw_songinfo_view(canvas, tracker);
+			draw_pattern_view(canvas, tracker);
+			break;
+		}
 
-	//canvas_draw_icon(canvas, 0, 0, &I_test);
+		default: break;
+	}
 }
 
 static void input_callback(InputEvent* input_event, void* ctx) 
 {
 	// Проверяем, что контекст не нулевой
 	furi_assert(ctx);
-	FuriMessageQueue* event_queue = ctx;
+	FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx;
 
-	FlizzerTrackerEvent event = {.type = EventTypeInput, .input = *input_event};
-	furi_message_queue_put(event_queue, &event, FuriWaitForever);
-}
+	if(input_event->key == InputKeyBack && input_event->type == InputTypeShort)
+	{
+		tracker->period = furi_get_tick() - tracker->current_time;
+		tracker->current_time = furi_get_tick();
 
-const uint8_t waveforms[] = 
-{
-	SE_WAVEFORM_NONE,
-	SE_WAVEFORM_NOISE,
-	SE_WAVEFORM_PULSE,
-	SE_WAVEFORM_TRIANGLE,
-	SE_WAVEFORM_SAW,
-	SE_WAVEFORM_NOISE_METAL,
-	SE_WAVEFORM_SINE,
-};
+		tracker->was_it_back_keypress = true;
+	}
+
+	else if(input_event->type == InputTypeShort || input_event->type == InputTypeLong)
+	{
+		tracker->was_it_back_keypress = false;
+		tracker->period = 0;
+	}
+
+	uint32_t final_period = (tracker->was_it_back_keypress ? tracker->period : 0);
+
+	FlizzerTrackerEvent event = {.type = EventTypeInput, .input = *input_event, .period = final_period};
+	furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever);
+}
 
 int32_t flizzer_tracker_app(void* p) 
 {
 	UNUSED(p);
 
+	FlizzerTrackerApp* tracker = init_tracker(44100, 50, true, 1024);
+
 	// Текущее событие типа кастомного типа FlizzerTrackerEvent
 	FlizzerTrackerEvent event;
 	// Очередь событий на 8 элементов размера FlizzerTrackerEvent
-	FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(FlizzerTrackerEvent));
-
-	FlizzerTrackerApp* tracker = init_tracker(44100, 50, true, 1024);
+	tracker->event_queue = furi_message_queue_alloc(8, sizeof(FlizzerTrackerEvent));
 
 	// Создаем новый view port
 	ViewPort* view_port = view_port_alloc();
@@ -175,7 +102,7 @@ int32_t flizzer_tracker_app(void* p)
 	view_port_draw_callback_set(view_port, draw_callback, tracker);
 	// Создаем callback нажатий на клавиши, в качестве контекста передаем
 	// нашу очередь сообщений, чтоб запихивать в неё эти события
-	view_port_input_callback_set(view_port, input_callback, event_queue);
+	view_port_input_callback_set(view_port, input_callback, tracker);
 
 	// Создаем GUI приложения
 	Gui* gui = furi_record_open(RECORD_GUI);
@@ -185,23 +112,6 @@ int32_t flizzer_tracker_app(void* p)
 	tracker->notification = furi_record_open(RECORD_NOTIFICATION);
 	notification_message(tracker->notification, &sequence_display_backlight_enforce_on);
 
-	//tracker->sound_engine.channel[0].waveform = SE_WAVEFORM_NOISE;
-
-	tracker->frequency = 440;
-	tracker->current_waveform_index = 1;
-
-	/*sound_engine_set_channel_frequency(&tracker->sound_engine, &tracker->sound_engine.channel[0], ((12 * 4) << 8));
-
-	tracker->sound_engine.channel[0].adsr.a = 0x10;
-	tracker->sound_engine.channel[0].adsr.d = 0x10;
-	tracker->sound_engine.channel[0].adsr.s = 0xff;
-	tracker->sound_engine.channel[0].adsr.volume = 0x80;
-	tracker->sound_engine.channel[0].adsr.envelope_state = ATTACK;
-
-	SoundEngine* eng = &(tracker->sound_engine);
-
-	tracker->sound_engine.channel[0].adsr.envelope_speed = envspd(eng, tracker->sound_engine.channel[0].adsr.a);*/
-
 	tracker->song.speed = 5;
 	tracker->song.num_instruments = 2;
 	tracker->song.num_patterns = 2;
@@ -273,219 +183,16 @@ int32_t flizzer_tracker_app(void* p)
 	play();
 
 	// Бесконечный цикл обработки очереди событий
-	while(1) 
+	while(!(tracker->quit)) 
 	{
 		// Выбираем событие из очереди в переменную event (ждём бесконечно долго, если очередь пуста)
 		// и проверяем, что у нас получилось это сделать
-		furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk);
+		furi_check(furi_message_queue_get(tracker->event_queue, &event, FuriWaitForever) == FuriStatusOk);
 
 		// Наше событие — это нажатие кнопки
 		if(event.type == EventTypeInput) 
 		{
-			// Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения
-			if(event.input.key == InputKeyBack && event.input.type == InputTypeShort) 
-			{
-				break;
-			}
-
-			if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) 
-			{
-				tracker->tracker_engine.playing = !(tracker->tracker_engine.playing);
-			}
-
-			if(event.input.key == InputKeyUp && event.input.type == InputTypeShort) 
-			{
-				if(tracker->selected_param > 0)
-				{
-					tracker->selected_param--;
-				}
-			}
-
-			if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) 
-			{
-				if(tracker->selected_param < NUM_PARAMS - 1)
-				{
-					tracker->selected_param++;
-				}
-			}
-
-			if(event.input.key == InputKeyRight && event.input.type == InputTypeShort) 
-			{
-				switch(tracker->selected_param)
-				{
-					case PARAM_FREQUENCY:
-					{
-						tracker->frequency += 25;
-
-						sound_engine_set_channel_frequency(&tracker->sound_engine, &tracker->sound_engine.channel[0], tracker->frequency * 1024);
-
-						break;
-					}
-
-					case PARAM_WAVEFORM:
-					{
-						if(tracker->current_waveform_index < 6)
-						{
-							tracker->current_waveform_index++;
-						}
-						
-						tracker->sound_engine.channel[0].waveform = waveforms[tracker->current_waveform_index];
-
-						break;
-					}
-
-					case PARAM_PW:
-					{
-						if(tracker->pw + 0x80 < 0xFFF)
-						{
-							tracker->pw += 0x80;
-						}
-
-						else
-						{
-							tracker->pw = 0x80;
-						}
-
-						tracker->sound_engine.channel[0].pw = tracker->pw;
-
-						break;
-					}
-
-					case PARAM_ENABLE_FILTER:
-					{
-						tracker->flags ^= SE_ENABLE_FILTER;
-						
-						tracker->sound_engine.channel[0].flags = tracker->flags;
-
-						break;
-					}
-
-					case PARAM_FILTER_CUTOFF:
-					{
-						tracker->cutoff += 10;
-
-						if(tracker->cutoff > 0xFFF)
-						{
-							tracker->cutoff = 0xFFF;
-						}
-						
-						tracker->sound_engine.channel[0].filter_cutoff = tracker->cutoff;
-
-						sound_engine_filter_set_coeff(&tracker->sound_engine.channel[0].filter, tracker->cutoff, tracker->resonance * 50);
-
-						break;
-					}
-
-					case PARAM_FILTER_RESONANCE:
-					{
-						tracker->resonance++;
-						sound_engine_filter_set_coeff(&tracker->sound_engine.channel[0].filter, tracker->cutoff, tracker->resonance * 50);
-						break;
-					}
-
-					case PARAM_FILTER_TYPE:
-					{
-						if(tracker->filter_type < 3)
-						{
-							tracker->filter_type++;
-						}
-
-						tracker->sound_engine.channel[0].filter_mode = tracker->filter_type;
-						break;
-					}
-				}
-			}
-
-			if(event.input.key == InputKeyLeft && event.input.type == InputTypeShort) 
-			{
-				switch(tracker->selected_param)
-				{
-					case PARAM_FREQUENCY:
-					{
-						if(tracker->frequency > 25)
-						{
-							tracker->frequency -= 25;
-						}
-
-						sound_engine_set_channel_frequency(&tracker->sound_engine, &tracker->sound_engine.channel[0], tracker->frequency * 1024);
-
-						break;
-					}
-
-					case PARAM_WAVEFORM:
-					{
-						if(tracker->current_waveform_index > 0)
-						{
-							tracker->current_waveform_index--;
-						}
-						
-						tracker->sound_engine.channel[0].waveform = waveforms[tracker->current_waveform_index];
-
-						break;
-					}
-
-					case PARAM_PW:
-					{
-						if(tracker->pw - 0x80 > 0)
-						{
-							tracker->pw -= 0x80;
-						}
-
-						else
-						{
-							tracker->pw = 0xF80;
-						}
-
-						tracker->sound_engine.channel[0].pw = tracker->pw;
-
-						break;
-					}
-
-					case PARAM_ENABLE_FILTER:
-					{
-						tracker->flags ^= SE_ENABLE_FILTER;
-						
-						tracker->sound_engine.channel[0].flags = tracker->flags;
-
-						break;
-					}
-
-					case PARAM_FILTER_CUTOFF:
-					{
-						if(tracker->cutoff > 10)
-						{
-							tracker->cutoff -= 10;
-						}
-						
-						tracker->sound_engine.channel[0].filter_cutoff = tracker->cutoff;
-						sound_engine_filter_set_coeff(&tracker->sound_engine.channel[0].filter, tracker->cutoff, tracker->resonance * 50);
-
-						break;
-					}
-
-					case PARAM_FILTER_RESONANCE:
-					{
-						if(tracker->resonance > 0)
-						{
-							tracker->resonance--;
-							sound_engine_filter_set_coeff(&tracker->sound_engine.channel[0].filter, tracker->cutoff, tracker->resonance * 50);
-						}
-
-						break;
-					}
-
-					case PARAM_FILTER_TYPE:
-					{
-						if(tracker->filter_type > 0)
-						{
-							tracker->filter_type--;
-						}
-
-						tracker->sound_engine.channel[0].filter_mode = tracker->filter_type;
-						break;
-					}
-				}
-			}
+			process_input_event(tracker, &event);
 		}
 	}
 
@@ -495,7 +202,7 @@ int32_t flizzer_tracker_app(void* p)
 	furi_record_close(RECORD_NOTIFICATION);
 
 	// Специальная очистка памяти, занимаемой очередью
-	furi_message_queue_free(event_queue);
+	furi_message_queue_free(tracker->event_queue);
 
 	// Чистим созданные объекты, связанные с интерфейсом
 	gui_remove_view_port(gui, view_port);

+ 40 - 12
flizzer_tracker.h

@@ -13,28 +13,56 @@
 #include "sound_engine/sound_engine_defs.h"
 #include "tracker_engine/tracker_engine_defs.h"
 
+#define MIDDLE_C (12 * 4)
+#define MAX_NOTE (12 * 7 + 11)
+
+typedef enum
+{
+	EventTypeInput,
+} EventType;
+
+typedef struct
+{
+	EventType type;
+	InputEvent input;
+	uint32_t period;
+} FlizzerTrackerEvent;
+
+typedef enum
+{
+	PATTERN_VIEW,
+	INST_EDITOR_VIEW,
+	EXPORT_WAV_VIEW,
+} TrackerMode;
+
+typedef enum
+{
+	EDIT_PATTERN,
+	EDIT_SEQUENCE,
+	EDIT_SONGINFO,
+	EDIT_INSTRUMENT,
+	EDIT_PROGRAM,
+} TrackerFocus;
+
 typedef struct 
 {
-	bool stop;
-	uint32_t counter;
-	uint32_t counter_2;
 	NotificationApp* notification;
+	FuriMessageQueue* event_queue;
+	bool was_it_back_keypress;
+	uint32_t current_time;
+	uint32_t period;
 
 	SoundEngine sound_engine;
 	TrackerEngine tracker_engine;
 
 	TrackerSong song;
 
-	uint32_t frequency;
-	uint8_t current_waveform_index;
-	uint16_t pw;
-
 	uint8_t selected_param;
 
-	uint16_t flags;
-	uint16_t cutoff;
-	uint8_t resonance;
-	uint8_t filter_type;
+	uint8_t mode, focus;
+	uint8_t patternx, current_channel, current_digit, program_step, current_instrument, current_note;
+	bool editing;
+	bool was_editing;
 
-	bool playing;
+	bool quit;
 } FlizzerTrackerApp;

BIN
images/unite_begin.png


BIN
images/unite_end.png


BIN
images/unite_mid.png


+ 2 - 0
init_deinit.c

@@ -10,6 +10,8 @@ FlizzerTrackerApp* init_tracker(uint32_t sample_rate, uint8_t rate, bool externa
 
 	tracker->tracker_engine.song = &tracker->song;
 
+	tracker->current_note = MIDDLE_C;
+
 	return tracker;
 }
 

+ 400 - 0
input_event.c

@@ -0,0 +1,400 @@
+#include "input_event.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->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)
+	{
+		uint8_t field = get_field(tracker->patternx);
+
+		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) 
+	{
+		//TODO: add cycle focus
+	}
+
+	// Если нажата кнопка "назад", то выходим из цикла, а следовательно и из приложения
+	if(event->input.key == InputKeyBack && event->input.type == InputTypeLong) 
+	{
+		tracker->quit = true;
+		return;
+	}
+
+	switch(tracker->focus)
+	{
+		case EDIT_PATTERN:
+		{
+			pattern_edit_event(tracker, event);
+			break;
+		}
+
+		default: break;
+	}
+}

+ 17 - 0
input_event.h

@@ -0,0 +1,17 @@
+#pragma once
+
+#include <stdio.h>
+#include <furi.h>
+#include <gui/gui.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 "tracker_engine/tracker_engine_defs.h"
+#include "flizzer_tracker.h"
+#include "util.h"
+
+#define MAX_PATTERNX (2 + 1 + 1 + 3)
+
+void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event);

+ 2 - 2
tracker_engine/tracker_engine.c

@@ -56,7 +56,7 @@ uint8_t tracker_engine_get_volume(TrackerSongPatternStep* step)
 	return (step->inst_vol & 0xf) | ((step->command & 0x8000) >> 11);
 }
 
-uint8_t tracker_engine_get_command(TrackerSongPatternStep* step)
+uint16_t tracker_engine_get_command(TrackerSongPatternStep* step)
 {
 	return (step->command & 0x7fff);
 }
@@ -141,7 +141,7 @@ void tracker_engine_execute_track_command(TrackerEngine* tracker_engine, uint8_t
 
 	if(vol != MUS_NOTE_VOLUME_NONE)
 	{
-		//tracker_engine->sound_engine->channel[chan].adsr.volume = (int32_t)tracker_engine->sound_engine->channel[chan].adsr.volume * (int32_t)vol / (MUS_NOTE_VOLUME_NONE - 1);
+		tracker_engine->sound_engine->channel[chan].adsr.volume = (int32_t)tracker_engine->sound_engine->channel[chan].adsr.volume * (int32_t)vol / (MUS_NOTE_VOLUME_NONE - 1);
 	}
 
 	//TODO: add actual big ass function that executes commands; add arpeggio commands there

+ 1 - 1
tracker_engine/tracker_engine.h

@@ -9,4 +9,4 @@ void tracker_engine_advance_tick(TrackerEngine* tracker_engine);
 uint8_t tracker_engine_get_note(TrackerSongPatternStep* step);
 uint8_t tracker_engine_get_instrument(TrackerSongPatternStep* step);
 uint8_t tracker_engine_get_volume(TrackerSongPatternStep* step);
-uint8_t tracker_engine_get_command(TrackerSongPatternStep* step);
+uint16_t tracker_engine_get_command(TrackerSongPatternStep* step);

+ 3 - 3
tracker_engine/tracker_engine_defs.h

@@ -6,7 +6,7 @@
 #include "../sound_engine/sound_engine_defs.h"
 
 #define INST_PROG_LEN 16
-#define MUS_SONG_NAME_LEN 17
+#define MUS_SONG_NAME_LEN 16
 #define MUS_INST_NAME_LEN (MUS_SONG_NAME_LEN - 3)
 
 #define SONG_MAX_CHANNELS NUM_CHANNELS
@@ -51,7 +51,7 @@ typedef struct
 
 typedef struct
 {
-	char name[MUS_INST_NAME_LEN];
+	char name[MUS_INST_NAME_LEN + 1];
 
 	uint8_t waveform;
 	uint16_t flags;
@@ -137,7 +137,7 @@ typedef struct
 	uint8_t num_patterns, num_sequence_steps, num_instruments;
 	uint8_t pattern_length;
 
-	char song_name[MUS_SONG_NAME_LEN];
+	char song_name[MUS_SONG_NAME_LEN + 1];
 	uint8_t speed, rate;
 
 	uint8_t loop_start, loop_end;

+ 52 - 0
util.c

@@ -0,0 +1,52 @@
+#include "util.h"
+
+void set_note(TrackerSongPatternStep* step, uint8_t note)
+{
+	step->note &= 0x80;
+	step->note |= (note & 0x7f);
+}
+
+void set_instrument(TrackerSongPatternStep* step, uint8_t inst)
+{
+	step->note &= 0x7f;
+	step->inst_vol &= 0x0f;
+
+	step->note |= ((inst & 0x10) << 3);
+	step->inst_vol |= ((inst & 0xf) << 4);
+}
+
+void set_volume(TrackerSongPatternStep* step, uint8_t vol)
+{
+	step->command &= 0x7fff;
+	step->inst_vol &= 0xf0;
+
+	step->command |= ((vol & 0x10) << 11);
+	step->inst_vol |= (vol & 0xf);
+}
+
+void set_command(TrackerSongPatternStep* step, uint16_t command)
+{
+	step->command &= 0x8000;
+	step->command |= command & (0x7fff);
+}
+
+void play_song(FlizzerTrackerApp* tracker, bool from_cursor)
+{
+	tracker->tracker_engine.playing = true;
+	tracker->was_editing = tracker->editing;
+	tracker->editing = false;
+
+	if(!(from_cursor))
+	{
+		tracker->tracker_engine.pattern_position = 0;
+	}
+
+	play();
+}
+
+void stop_song(FlizzerTrackerApp* tracker)
+{
+	tracker->tracker_engine.playing = false;
+	tracker->editing = tracker->was_editing;
+	stop();
+}

+ 18 - 0
util.h

@@ -0,0 +1,18 @@
+#pragma once
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "sound_engine/sound_engine_defs.h"
+#include "tracker_engine/tracker_engine_defs.h"
+#include "flizzer_tracker.h"
+
+#define clamp(val, add, _min, _max) val = fmin(_max, fmax(_min, (int32_t)val + add))
+
+void set_note(TrackerSongPatternStep* step, uint8_t note);
+void set_instrument(TrackerSongPatternStep* step, uint8_t inst);
+void set_volume(TrackerSongPatternStep* step, uint8_t vol);
+void set_command(TrackerSongPatternStep* step, uint16_t command);
+
+void play_song(FlizzerTrackerApp* tracker, bool from_cursor);
+void stop_song(FlizzerTrackerApp* tracker);

+ 48 - 3
view/pattern_editor.c

@@ -104,7 +104,13 @@ void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker)
 		}
 	}
 
-	canvas_draw_box(canvas, 0, PATTERN_EDITOR_Y + 6 * 2 + 1, 127, 7);
+	if(tracker->editing && tracker->focus == EDIT_PATTERN)
+	{
+		uint8_t x = tracker->current_channel * 32 + tracker->patternx * 4 + (tracker->patternx > 0 ? 4 : 0) - 1;
+		uint8_t y = PATTERN_EDITOR_Y + 6 * 2 + 1;
+
+		canvas_draw_box(canvas, x, y, (tracker->patternx > 0 ? 5 : 9), 7);
+	}
 }
 
 void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker)
@@ -113,8 +119,47 @@ void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker)
 	UNUSED(tracker);
 }
 
+#define member_size(type, member) sizeof(((type *)0)->member)
+
+#define SONG_HEADER_SIZE (member_size(TrackerSong, song_name) + member_size(TrackerSong, speed) + member_size(TrackerSong, rate) \
+ + member_size(TrackerSong, loop_start) + member_size(TrackerSong, loop_end) + member_size(TrackerSong, num_patterns) \
+ + member_size(TrackerSong, num_sequence_steps) + member_size(TrackerSong, num_instruments) + member_size(TrackerSong, pattern_length))
+
+uint32_t calculate_song_size(TrackerSong* song)
+{
+	uint32_t song_size = SONG_HEADER_SIZE + sizeof(Instrument) * song->num_instruments + sizeof(TrackerSongPatternStep) * song->num_patterns * song->pattern_length + sizeof(TrackerSongSequenceStep) * song->num_sequence_steps;
+	return song_size;
+}
+
 void draw_songinfo_view(Canvas* canvas, FlizzerTrackerApp* tracker)
 {
-	UNUSED(canvas);
-	UNUSED(tracker);
+	uint32_t song_size = calculate_song_size(&tracker->song);
+	uint32_t free_bytes = memmgr_get_free_heap();
+	canvas_draw_line(canvas, 128 - 4 * 10 - 2, 0, 128 - 4 * 10 - 2, 10);
+
+	char song_size_buffer[12];
+	char free_bytes_buffer[12];
+
+	if(song_size > 999)
+	{
+		snprintf(song_size_buffer, sizeof(song_size_buffer), "TUNE:%.1fK", (double)song_size / (double)1024.0);
+	}
+
+	else
+	{
+		snprintf(song_size_buffer, sizeof(song_size_buffer), "TUNE:%ld", song_size);
+	}
+
+	if(free_bytes > 999)
+	{
+		snprintf(free_bytes_buffer, sizeof(song_size_buffer), "FREE:%.1fK", (double)free_bytes / (double)1024.0);
+	}
+
+	else
+	{
+		snprintf(free_bytes_buffer, sizeof(song_size_buffer), "FREE:%ld", free_bytes);
+	}
+
+	canvas_draw_str(canvas, 128 - 4 * 10, 5, song_size_buffer);
+	canvas_draw_str(canvas, 128 - 4 * 10, 11, free_bytes_buffer);
 }