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

pattern view, tracker engine, test pattern

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

+ 1 - 1
application.fam

@@ -1,6 +1,6 @@
 App(
     appid="flizzer_tracker",
-    name="FlizzerTracker",
+    name="Flizzer Tracker",
     apptype=FlipperAppType.EXTERNAL,
     entry_point="flizzer_tracker_app",
     cdefines=["APP_FLIZZER_TRACKER"],



+ 31 - 0
event.c

@@ -0,0 +1,31 @@
+#include "event.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);
+}

+ 12 - 0
event.h

@@ -0,0 +1,12 @@
+#pragma once
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "sound_engine/sound_engine_defs.h"
+#include "tracker_engine/tracker_engine_defs.h"
+
+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);

+ 87 - 17
flizzer_tracker.c

@@ -1,5 +1,11 @@
 #include "flizzer_tracker.h"
 #include "init_deinit.h"
+#include "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
@@ -81,9 +87,13 @@ static void draw_callback(Canvas* canvas, void* ctx)
 
 	canvas_clear(canvas);
 
+	canvas_set_color(canvas, ColorXOR);
+
 	canvas_set_custom_font(canvas, u8g2_font_tom_thumb_4x6_tr);
 
-	char buffer[30] = {0};
+	draw_pattern_view(canvas, tracker);
+
+	/*char buffer[30] = {0};
 
 	snprintf(buffer, 20, "FREQUENCY:%ld Hz", tracker->frequency);
 	
@@ -113,7 +123,7 @@ static void draw_callback(Canvas* canvas, void* ctx)
 	
 	canvas_draw_str(canvas, 0, 42, buffer);
 
-	snprintf(buffer, 20, "TR.ENG.CALLS: %d", tracker->tracker_engine.absolute_position);
+	snprintf(buffer, 20, "TR.ENG.CALLS: %d", tracker->sound_engine.channel[0].adsr.volume);
 	
 	canvas_draw_str(canvas, 0, 48, buffer);
 
@@ -122,7 +132,9 @@ static void draw_callback(Canvas* canvas, void* ctx)
     uint32_t bytes = memmgr_get_free_heap();
 	snprintf(buffer, 20, "BYTES FREE:%ld", bytes);
 
-	canvas_draw_str(canvas, 0, 64, buffer);
+	canvas_draw_str(canvas, 0, 64, buffer);*/
+
+	//canvas_draw_icon(canvas, 0, 0, &I_test);
 }
 
 static void input_callback(InputEvent* input_event, void* ctx) 
@@ -173,12 +185,12 @@ 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->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], 440 * 1024);
+	/*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;
@@ -188,8 +200,76 @@ int32_t flizzer_tracker_app(void* p)
 
 	SoundEngine* eng = &(tracker->sound_engine);
 
-	tracker->sound_engine.channel[0].adsr.envelope_speed = envspd(eng, tracker->sound_engine.channel[0].adsr.a);
+	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;
+	tracker->song.num_sequence_steps = 1;
+	tracker->song.pattern_length = 64;
+
+	tracker->song.sequence.sequence_step[0].pattern_indices[0] = 0;
+	tracker->song.sequence.sequence_step[0].pattern_indices[1] = 1;
+	tracker->song.sequence.sequence_step[0].pattern_indices[2] = 2;
+	tracker->song.sequence.sequence_step[0].pattern_indices[3] = 2;
+
+	tracker->song.pattern[0].step = malloc(64 * sizeof(TrackerSongPatternStep));
+	tracker->song.pattern[1].step = malloc(64 * sizeof(TrackerSongPatternStep));
+	tracker->song.pattern[2].step = malloc(64 * sizeof(TrackerSongPatternStep));
+
+	memset(tracker->song.pattern[0].step, 0, 64 * sizeof(TrackerSongPatternStep));
+	memset(tracker->song.pattern[1].step, 0, 64 * sizeof(TrackerSongPatternStep));
+	memset(tracker->song.pattern[2].step, 0, 64 * sizeof(TrackerSongPatternStep));
+
+	tracker->song.instrument[0] = malloc(sizeof(Instrument));
+	tracker->song.instrument[1] = malloc(sizeof(Instrument));
+
+	for(int i = 0; i < 64; ++i)
+	{
+		set_note(&tracker->song.pattern[0].step[i], MUS_NOTE_NONE);
+		set_note(&tracker->song.pattern[1].step[i], MUS_NOTE_NONE);
+		set_note(&tracker->song.pattern[2].step[i], MUS_NOTE_NONE);
+
+		set_instrument(&tracker->song.pattern[0].step[i], MUS_NOTE_INSTRUMENT_NONE);
+		set_instrument(&tracker->song.pattern[1].step[i], MUS_NOTE_INSTRUMENT_NONE);
+		set_instrument(&tracker->song.pattern[2].step[i], MUS_NOTE_INSTRUMENT_NONE);
+
+		set_volume(&tracker->song.pattern[0].step[i], MUS_NOTE_VOLUME_NONE);
+		set_volume(&tracker->song.pattern[1].step[i], MUS_NOTE_VOLUME_NONE);
+		set_volume(&tracker->song.pattern[2].step[i], MUS_NOTE_VOLUME_NONE);
+	}
+
+	for(int i = 0; i < 64; i += 8)
+	{
+		set_note(&tracker->song.pattern[0].step[0 + i], 12 * 5);
+		set_note(&tracker->song.pattern[0].step[2 + i], 12 * 5 + 2);
+		set_note(&tracker->song.pattern[0].step[4 + i], 12 * 5 - 2);
+		set_note(&tracker->song.pattern[0].step[6 + i], 12 * 5 + 4);
+
+		set_instrument(&tracker->song.pattern[0].step[0 + i], 0);
+		set_instrument(&tracker->song.pattern[0].step[2 + i], 0);
+		set_instrument(&tracker->song.pattern[0].step[4 + i], 0);
+		set_instrument(&tracker->song.pattern[0].step[6 + i], 0);
+	}
+
+	for(int i = 0; i < 64; i++)
+	{
+		set_note(&tracker->song.pattern[1].step[i], 12 * 7 + 11);
+
+		set_instrument(&tracker->song.pattern[1].step[i], 1);
+	}
+
+	tracker->song.instrument[0]->adsr.a = 0x2;
+	tracker->song.instrument[0]->adsr.d = 0x9;
+	tracker->song.instrument[0]->adsr.volume = 0x80;
+	tracker->song.instrument[0]->waveform = SE_WAVEFORM_TRIANGLE;
 
+	tracker->song.instrument[1]->adsr.a = 0x0;
+	tracker->song.instrument[1]->adsr.d = 0x3;
+	tracker->song.instrument[1]->adsr.volume = 0x18;
+	tracker->song.instrument[1]->waveform = SE_WAVEFORM_NOISE;
+
+	tracker->tracker_engine.playing = false;
 	play();
 
 	// Бесконечный цикл обработки очереди событий
@@ -210,17 +290,7 @@ int32_t flizzer_tracker_app(void* p)
 
 			if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) 
 			{
-				tracker->playing = !(tracker->playing);
-
-				if(tracker->playing)
-				{
-					play();
-				}
-
-				else
-				{
-					stop();
-				}
+				tracker->tracker_engine.playing = !(tracker->tracker_engine.playing);
 			}
 
 			if(event.input.key == InputKeyUp && event.input.type == InputTypeShort) 

BIN
flizzer_tracker.png


BIN
images/flizzer_tracker.png


BIN
images/flizzer_tracker_module.png


BIN
images/note_release.png


+ 3 - 3
sound_engine/freqs.c

@@ -25,13 +25,13 @@ uint32_t get_freq(uint16_t note)
 
 	if ((note & 0xff) == 0)
 	{
-		return frequency_table[((note >> 8) % 12)] / ((NUM_OCTAVES - 1) - ((note >> 8) / 12)); //wrap to one octave
+		return frequency_table[((note >> 8) % 12)] / ((NUM_OCTAVES) - ((note >> 8) / 12)); //wrap to one octave
 	}
 
 	else
 	{
-		uint64_t f1 = frequency_table[((note >> 8) % 12)] / ((NUM_OCTAVES - 1) - ((note >> 8) / 12));
-		uint64_t f2 = frequency_table[(((note >> 8) + 1) % 12)] / ((NUM_OCTAVES - 1) - (((note >> 8) + 1) / 12));
+		uint64_t f1 = frequency_table[((note >> 8) % 12)] / ((NUM_OCTAVES) - ((note >> 8) / 12));
+		uint64_t f2 = frequency_table[(((note >> 8) + 1) % 12)] / ((NUM_OCTAVES) - (((note >> 8) + 1) / 12));
 
 		return f1 + (uint64_t)((f2 - f1) * (note & 0xff)) / 256;
 	}

+ 31 - 1
sound_engine/sound_engine.c

@@ -37,12 +37,19 @@ void sound_engine_deinit(SoundEngine* sound_engine)
 		furi_hal_speaker_release();
 	}
 
+	else
+	{
+		furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull);
+	}
+
 	furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdDma1Ch1, 13, NULL, NULL);
 	sound_engine_stop();
 }
 
-void sound_engine_set_channel_frequency(SoundEngine* sound_engine, SoundEngineChannel* channel, uint32_t frequency)
+void sound_engine_set_channel_frequency(SoundEngine* sound_engine, SoundEngineChannel* channel, uint16_t note)
 {
+	uint32_t frequency = get_freq(note);
+
 	if(frequency != 0)
 	{
 		channel->frequency = (uint64_t)(ACC_LENGTH) / (uint64_t)1024 * (uint64_t)(frequency) / (uint64_t)sound_engine->sample_rate;
@@ -54,6 +61,29 @@ void sound_engine_set_channel_frequency(SoundEngine* sound_engine, SoundEngineCh
 	}
 }
 
+void sound_engine_enable_gate(SoundEngine* sound_engine, SoundEngineChannel* channel, bool enable)
+{
+	if(enable)
+	{
+		channel->adsr.envelope = 0;
+		channel->adsr.envelope_speed = envspd(sound_engine, channel->adsr.a);
+		channel->adsr.envelope_state = ATTACK;
+
+		channel->flags |= SE_ENABLE_GATE;
+
+		if(channel->flags & SE_ENABLE_KEYDOWN_SYNC)
+		{
+			channel->accumulator = 0;
+		}
+	}
+
+	else
+	{
+		channel->adsr.envelope_state = RELEASE;
+		channel->adsr.envelope_speed = envspd(sound_engine, channel->adsr.r);
+	}
+}
+
 void sound_engine_fill_buffer(SoundEngine* sound_engine, uint16_t* audio_buffer, uint32_t audio_buffer_size)
 {
 	int32_t channel_output[NUM_CHANNELS];

+ 4 - 2
sound_engine/sound_engine.h

@@ -2,10 +2,12 @@
 
 #include "sound_engine_osc.h"
 #include "sound_engine_filter.h"
+#include "freqs.h"
 #include "sound_engine_defs.h"
 #include "sound_engine_adsr.h"
 
 void sound_engine_init(SoundEngine* sound_engine, uint32_t sample_rate, bool external_audio_output, uint32_t audio_buffer_size);
 void sound_engine_deinit(SoundEngine* sound_engine);
-void sound_engine_set_channel_frequency(SoundEngine* sound_engine, SoundEngineChannel* channel, uint32_t frequency);
-void sound_engine_fill_buffer(SoundEngine* sound_engine, uint16_t* audio_buffer, uint32_t audio_buffer_size);
+void sound_engine_set_channel_frequency(SoundEngine* sound_engine, SoundEngineChannel* channel, uint16_t note);
+void sound_engine_fill_buffer(SoundEngine* sound_engine, uint16_t* audio_buffer, uint32_t audio_buffer_size);
+void sound_engine_enable_gate(SoundEngine* sound_engine, SoundEngineChannel* channel, bool enable);

+ 1 - 1
sound_engine/sound_engine_defs.h

@@ -19,7 +19,7 @@
 #define MAX_ADSR (0xff << 17)
 #define MAX_ADSR_VOLUME 0x80
 #define BASE_FREQ 22050
-#define envspd(eng, slope) ((slope) != 0 ? (((uint64_t)MAX_ADSR / ((slope) * (slope) * 256)) * BASE_FREQ / eng->sample_rate) : ((uint64_t)MAX_ADSR * BASE_FREQ / eng->sample_rate))
+#define envspd(eng, slope) ((slope) != 0 ? (((uint64_t)MAX_ADSR / ((slope) * (slope) * 256 / 8)) * BASE_FREQ / eng->sample_rate) : ((uint64_t)MAX_ADSR * BASE_FREQ / eng->sample_rate))
 
 typedef enum
 {

+ 0 - 0
tracker_engine/diskop.c


+ 0 - 0
tracker_engine/diskop.h


+ 0 - 0
tracker_engine/do_effects.c


+ 0 - 0
tracker_engine/do_effects.h


+ 256 - 2
tracker_engine/tracker_engine.c

@@ -12,14 +12,268 @@ void tracker_engine_init(TrackerEngine* tracker_engine, uint8_t rate, SoundEngin
 	tracker_engine->sound_engine = sound_engine;
 }
 
+void tracker_engine_deinit_song(TrackerSong* song)
+{
+	for(int i = 0; i < song->num_patterns; ++i)
+	{
+		if(song->pattern[i].step)
+		{
+			free(song->pattern[i].step);
+		}
+	}
+
+	for(int i = 0; i < song->num_instruments; ++i)
+	{
+		if(song->instrument[i])
+		{
+			free(song->instrument[i]);
+		}
+	}
+
+	free(song);
+}
+
 void tracker_engine_deinit(TrackerEngine* tracker_engine)
 {
-	UNUSED(tracker_engine);
+	tracker_engine_deinit_song(tracker_engine->song);
+
 	furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdTim1UpTim16, 13, NULL, NULL);
 	tracker_engine_stop();
 }
 
+uint8_t tracker_engine_get_note(TrackerSongPatternStep* step)
+{
+	return (step->note & 0x7f);
+}
+
+uint8_t tracker_engine_get_instrument(TrackerSongPatternStep* step)
+{
+	return ((step->note & 0x80) >> 3) | ((step->inst_vol & 0xf0) >> 4);
+}
+
+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)
+{
+	return (step->command & 0x7fff);
+}
+
+void tracker_engine_set_note(TrackerEngine* tracker_engine, uint8_t chan, uint16_t note, bool update_note)
+{
+	if(update_note) tracker_engine->channel[chan].note = note;
+
+	sound_engine_set_channel_frequency(tracker_engine->sound_engine, &tracker_engine->sound_engine->channel[chan], note);
+}
+
+void tracker_engine_trigger_instrument_internal(TrackerEngine* tracker_engine, uint8_t chan, Instrument* pinst, uint16_t note)
+{
+	SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[chan];
+	TrackerEngineChannel* te_channel = &tracker_engine->channel[chan];
+
+	te_channel->channel_flags = TEC_PLAYING | (te_channel->channel_flags & TEC_DISABLED);
+
+	if(!(pinst->flags & TE_PROG_NO_RESTART) && pinst->program_period > 0)
+	{
+		te_channel->channel_flags |= TEC_PROGRAM_RUNNING;
+
+		te_channel->program_counter = 0;
+		te_channel->program_loop = 0;
+		te_channel->program_period = 0;
+		te_channel->program_tick = 0;
+	}
+
+	te_channel->program_period = pinst->program_period;
+
+	te_channel->instrument = pinst;
+
+	se_channel->waveform = pinst->waveform;
+	se_channel->flags = pinst->sound_engine_flags;
+
+	te_channel->flags = pinst->flags;
+
+	te_channel->arpeggio_note = 0;
+	te_channel->fixed_note = 0xffff;
+
+	tracker_engine_set_note(tracker_engine, chan, note + (uint16_t)pinst->finetune, true);
+
+	te_channel->last_note = te_channel->target_note = note + (uint16_t)pinst->finetune;
+
+	if(pinst->sound_engine_flags & SE_ENABLE_KEYDOWN_SYNC)
+	{
+		te_channel->vibrato_position = te_channel->pwm_position = 0;
+	}
+
+	if(pinst->flags & TE_SET_CUTOFF)
+	{
+		te_channel->filter_cutoff = pinst->filter_cutoff;
+		te_channel->filter_resonance = pinst->filter_resonance;
+
+		sound_engine_filter_set_coeff(&se_channel->filter, te_channel->filter_cutoff, (te_channel->filter_resonance << 5));
+	}
+
+	if(pinst->flags & TE_SET_PW)
+	{
+		te_channel->pw = (pinst->pw << 4);
+		se_channel->pw = (pinst->pw << 4);
+	}
+
+	te_channel->slide_speed = 0;
+
+	se_channel->adsr.a = pinst->adsr.a;
+	se_channel->adsr.d = pinst->adsr.d;
+	se_channel->adsr.s = pinst->adsr.s;
+	se_channel->adsr.r = pinst->adsr.r;
+	se_channel->adsr.volume = pinst->adsr.volume;
+
+	sound_engine_enable_gate(tracker_engine->sound_engine, &tracker_engine->sound_engine->channel[chan], true);
+}
+
+void tracker_engine_execute_track_command(TrackerEngine* tracker_engine, uint8_t chan, TrackerSongPatternStep* step, bool first_tick)
+{
+	UNUSED(first_tick);
+	UNUSED(tracker_engine);
+	UNUSED(chan);
+
+	uint8_t vol = tracker_engine_get_volume(step);
+
+	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);
+	}
+
+	//TODO: add actual big ass function that executes commands; add arpeggio commands there
+}
+
 void tracker_engine_advance_tick(TrackerEngine* tracker_engine)
 {
-	tracker_engine->absolute_position++;
+	if(!(tracker_engine->playing)) return;
+
+	if(!(tracker_engine->song)) return;
+
+	if(!(tracker_engine->sound_engine)) return;
+
+	TrackerSong* song = tracker_engine->song;
+
+	for(int chan = 0; chan < SONG_MAX_CHANNELS; ++chan)
+	{
+		SoundEngineChannel* se_channel = &tracker_engine->sound_engine->channel[chan];
+		TrackerEngineChannel* te_channel = &tracker_engine->channel[chan];
+
+		uint8_t sequence_position = tracker_engine->sequence_position;
+		uint8_t current_pattern = song->sequence.sequence_step[sequence_position].pattern_indices[chan];
+		uint8_t pattern_step = tracker_engine->pattern_position;
+
+		TrackerSongPattern* pattern = &song->pattern[current_pattern];
+
+		uint8_t note_delay = 0; //TODO: add note delay command
+
+		if(tracker_engine->current_tick == note_delay)
+		{
+			uint8_t note = tracker_engine_get_note(&pattern->step[pattern_step]);
+			uint8_t inst = tracker_engine_get_instrument(&pattern->step[pattern_step]);
+
+			Instrument* pinst = NULL;
+
+			if(inst == MUS_NOTE_INSTRUMENT_NONE)
+			{
+				pinst = te_channel->instrument;
+			}
+
+			else
+			{
+				if(inst < song->num_instruments)
+				{
+					pinst = song->instrument[inst];
+					te_channel->instrument = pinst;
+				}
+			}
+
+			//TODO: add note cut command
+
+			if(note == MUS_NOTE_RELEASE)
+			{
+				sound_engine_enable_gate(tracker_engine->sound_engine, se_channel, 0);
+			}
+
+			else if(pinst && note != MUS_NOTE_RELEASE && note != MUS_NOTE_CUT && note != MUS_NOTE_NONE)
+			{
+				te_channel->slide_speed = 0;
+
+				//TODO: add setting slide speed if slide command is there
+
+				uint8_t prev_adsr_volume = se_channel->adsr.volume;
+
+				tracker_engine_trigger_instrument_internal(tracker_engine, chan, pinst, note << 8);
+				te_channel->note = (note << 8);
+
+				te_channel->target_note = (note << 8) + pinst->finetune;
+
+				if(inst == MUS_NOTE_INSTRUMENT_NONE)
+				{
+					se_channel->adsr.volume = prev_adsr_volume;
+				}
+			}
+		}
+
+		tracker_engine_execute_track_command(tracker_engine, chan, &pattern->step[pattern_step], tracker_engine->current_tick == note_delay);
+
+		if(te_channel->flags & TEC_PLAYING)
+		{
+			if(!(se_channel->flags & SE_ENABLE_GATE))
+			{
+				te_channel->flags &= ~(TEC_PLAYING);
+			}
+
+			if(te_channel->slide_speed != 0)
+			{
+				if(te_channel->target_note > te_channel->note)
+				{
+					te_channel->target_note += fmin(te_channel->slide_speed, te_channel->target_note - te_channel->note);
+				}
+
+				else if(te_channel->target_note < te_channel->note)
+				{
+					te_channel->target_note -= fmin(te_channel->slide_speed, te_channel->note - te_channel->target_note);
+				}
+			}
+
+			//TODO: add instrument program execution
+
+			//TODO: add PWM and vibrato execution
+			uint8_t vib = 0;
+			int32_t chn_note = (te_channel->fixed_note != 0xffff ? te_channel->fixed_note : te_channel->note) + vib + ((int16_t)te_channel->arpeggio_note << 8);
+
+			if(chn_note < 0) chn_note = 0;
+			if(chn_note > ((12 * 7 + 11) << 8)) chn_note = ((12 * 7 + 11) << 8); //highest note is B-7
+
+			tracker_engine_set_note(tracker_engine, chan, (uint16_t)chn_note, false);
+		}
+	}
+
+	tracker_engine->current_tick++;
+
+	if(tracker_engine->current_tick >= song->speed)
+	{
+		//TODO: add pattern loop and pattern skip commands
+
+		tracker_engine->pattern_position++;
+
+		tracker_engine->current_tick = 0;
+
+		if(tracker_engine->pattern_position >= song->pattern_length)
+		{
+			tracker_engine->pattern_position = 0;
+			tracker_engine->sequence_position++;
+
+			if(tracker_engine->sequence_position >= song->num_sequence_steps)
+			{
+				tracker_engine->playing = false; //TODO: add song loop handling
+				tracker_engine->sequence_position--;
+				tracker_engine->pattern_position = song->pattern_length - 1;
+			}
+		}
+	}
 }

+ 6 - 1
tracker_engine/tracker_engine.h

@@ -4,4 +4,9 @@
 
 void tracker_engine_init(TrackerEngine* tracker_engine, uint8_t rate, SoundEngine* sound_engine);
 void tracker_engine_deinit(TrackerEngine* tracker_engine);
-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_instrument(TrackerSongPatternStep* step);
+uint8_t tracker_engine_get_volume(TrackerSongPatternStep* step);
+uint8_t tracker_engine_get_command(TrackerSongPatternStep* step);

+ 28 - 12
tracker_engine/tracker_engine_defs.h

@@ -12,15 +12,17 @@
 #define SONG_MAX_CHANNELS NUM_CHANNELS
 #define MAX_INSTRUMENTS 31
 #define MAX_PATTERN_LENGTH 256
+#define MAX_PATTERNS 64
 #define MAX_SEQUENCE_LENGTH 256
 
 #define MUS_NOTE_NONE 127
 #define MUS_NOTE_RELEASE 126
+#define MUS_NOTE_CUT 125
 
 #define MUS_NOTE_INSTRUMENT_NONE 31
 #define MUS_NOTE_VOLUME_NONE 31
 
-#define SONG_FILE_SIG "FLZ!SONG"
+#define SONG_FILE_SIG "FZT!SONG"
 
 #define TRACKER_ENGINE_VERSION 1
 
@@ -51,16 +53,19 @@ typedef struct
 {
 	char name[MUS_INST_NAME_LEN];
 
+	uint8_t waveform;
 	uint16_t flags;
 	uint16_t sound_engine_flags;
 
+	uint8_t slide_speed;
+
 	InstrumentAdsr adsr;
 
 	uint8_t ring_mod, hard_sync; //0xff = self
 
-	uint8_t pw, cutoff; //store only one byte since we don't have the luxury of virtually unlimited memory!
+	uint8_t pw; //store only one byte since we don't have the luxury of virtually unlimited memory!
 
-	uint16_t program[INST_PROG_LEN]; //MSB is unite bit (indicates this and mext command must be executed at once)
+	uint16_t program[INST_PROG_LEN]; //MSB is unite bit (indicates this and next command must be executed at once)
 	uint8_t program_period;
 
 	uint8_t vibrato_speed, vibrato_depth, vibrato_delay;
@@ -73,16 +78,18 @@ typedef struct
 
 typedef struct
 {
-	Instrument* inst;
+	Instrument* instrument;
 
 	uint16_t flags;
 
 	uint8_t channel_flags;
 
-	uint16_t note, target_note, last_note;
+	uint16_t note, target_note, last_note, fixed_note;
+	int16_t arpeggio_note;
+
 	uint8_t volume;
 
-	uint8_t program_counter, program_tick, program_loop, prog_period;
+	uint8_t program_counter, program_tick, program_loop, program_period;
 
 	uint16_t filter_cutoff;
 	uint8_t filter_resonance, filter_type;
@@ -95,6 +102,8 @@ typedef struct
 	uint8_t extarp1, extarp2;
 
 	uint16_t pw;
+
+	uint8_t slide_speed;
 } TrackerEngineChannel;
 
 typedef struct
@@ -116,19 +125,22 @@ typedef struct
 
 typedef struct
 {
-	TrackerSongSequenceStep* sequence[MAX_SEQUENCE_LENGTH];
+	TrackerSongSequenceStep sequence_step[MAX_SEQUENCE_LENGTH];
 } TrackerSongSequence;
 
 typedef struct
 {
-	Instrument* instrument;
-	TrackerSongPattern* pattern;
-	TrackerSongSequence* sequence;
+	Instrument* instrument[MAX_INSTRUMENTS];
+	TrackerSongPattern pattern[MAX_PATTERNS];
+	TrackerSongSequence sequence;
 
 	uint8_t num_patterns, num_sequence_steps, num_instruments;
+	uint8_t pattern_length;
 
 	char song_name[MUS_SONG_NAME_LEN];
 	uint8_t speed, rate;
+
+	uint8_t loop_start, loop_end;
 } TrackerSong;
 
 typedef struct
@@ -138,8 +150,12 @@ typedef struct
 	TrackerSong* song;
 	SoundEngine* sound_engine;
 
-	uint8_t pattern_position, sequence_position, current_tick, song_speed;
-	uint16_t absolute_position; //sequence_position * sequence_length + pattern_position
+	uint8_t pattern_position, sequence_position, current_tick;
+	uint16_t absolute_position; //sequence_position * pattern_length + pattern_position
+
+	uint8_t pattern_length;
 
 	uint8_t speed, rate;
+
+	bool playing; //if we reach the end of the song and song does not loop we just stop there
 } TrackerEngine;

+ 0 - 0
view/instrument_editor.c


+ 0 - 0
view/instrument_editor.h


+ 120 - 0
view/pattern_editor.c

@@ -0,0 +1,120 @@
+#include "pattern_editor.h"
+
+#define PATTERN_EDITOR_Y (64 - (6 * 5) - 1)
+
+static const char* notenames[] =
+{
+	"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-",
+};
+
+char* notename(uint8_t note)
+{
+	static char buffer[4];
+
+	snprintf(buffer, sizeof(buffer), "%s%d", notenames[note % 12], note / 12);
+
+	return buffer;
+}
+
+static const char to_char_array[] = 
+{
+	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+	'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+	'U', 'V', 'W', 'X', 'Y', 'Z',
+};
+
+char to_char(uint8_t number)
+{
+	return to_char_array[number];
+}
+
+void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker)
+{
+	char command_buffer[6] = {0};
+	char buffer[11] = {0};
+
+	canvas_draw_line(canvas, 0, PATTERN_EDITOR_Y, 127, PATTERN_EDITOR_Y);
+
+	for(int i = 1; i < SONG_MAX_CHANNELS; ++i)
+	{
+		for(int y = PATTERN_EDITOR_Y + 1; y < 64; y += 2)
+		{
+			canvas_draw_dot(canvas, i * 32 - 1, y);
+		}
+	}
+
+	for(int i = 0; i < SONG_MAX_CHANNELS; ++i)
+	{
+		uint8_t sequence_position = tracker->tracker_engine.sequence_position;
+		uint8_t current_pattern = tracker->tracker_engine.song->sequence.sequence_step[sequence_position].pattern_indices[i];
+		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];
+
+		for(uint8_t pos = 0; pos < 5; ++pos)
+		{
+			TrackerSongPatternStep* step = NULL;
+
+			if(pattern_step - 2 + pos >= 0 && pattern_step - 2 + pos < pattern_length)
+			{
+				step = &pattern->step[pattern_step + pos - 2];
+			}
+
+			uint8_t string_x = i * 32;
+			uint8_t string_y = PATTERN_EDITOR_Y + 6 * pos + 6 + 1;
+			
+			if(step)
+			{
+				uint8_t note = tracker_engine_get_note(step);
+				uint8_t inst = tracker_engine_get_instrument(step);
+				uint8_t vol = tracker_engine_get_volume(step);
+				uint16_t command = tracker_engine_get_command(step);
+
+				char inst_ch = to_char(inst);
+				char vol_ch = to_char(vol);
+				char command_ch = to_char(command >> 8);
+				
+				if(inst == MUS_NOTE_INSTRUMENT_NONE)
+				{
+					inst_ch = '-';
+				}
+
+				if(vol == MUS_NOTE_VOLUME_NONE)
+				{
+					vol_ch = '-';
+				}
+
+				if(command == 0)
+				{
+					snprintf(command_buffer, sizeof(command_buffer), "---");
+				}
+
+				else
+				{
+					snprintf(command_buffer, sizeof(command_buffer), "%c%02X", command_ch, (command & 0xff));
+				}
+
+				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_box(canvas, 0, PATTERN_EDITOR_Y + 6 * 2 + 1, 127, 7);
+}
+
+void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker)
+{
+	UNUSED(canvas);
+	UNUSED(tracker);
+}
+
+void draw_songinfo_view(Canvas* canvas, FlizzerTrackerApp* tracker)
+{
+	UNUSED(canvas);
+	UNUSED(tracker);
+}

+ 11 - 0
view/pattern_editor.h

@@ -0,0 +1,11 @@
+#pragma once
+
+#include "../tracker_engine/tracker_engine_defs.h"
+#include "../flizzer_tracker.h"
+
+#include <furi.h>
+#include <gui/gui.h>
+
+void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker);
+void draw_sequence_view(Canvas* canvas, FlizzerTrackerApp* tracker);
+void draw_songinfo_view(Canvas* canvas, FlizzerTrackerApp* tracker);