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

started instrument editor, fixed frequency calculation, instrument preview from editor, instrument base note, fixed error with tracker_engine_advance_channel not being executed

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

+ 11 - 3
flizzer_tracker.c

@@ -46,6 +46,12 @@ static void draw_callback(Canvas *canvas, void *ctx)
     {
         case PATTERN_VIEW:
         {
+            if (tracker->tracker_engine.song == NULL)
+            {
+                stop();
+                tracker_engine_set_song(&tracker->tracker_engine, &tracker->song);
+            }
+
             draw_songinfo_view(canvas, tracker);
             draw_sequence_view(canvas, tracker);
             draw_pattern_view(canvas, tracker);
@@ -120,7 +126,7 @@ int32_t flizzer_tracker_app(void *p)
 {
     UNUSED(p);
 
-    FlizzerTrackerApp *tracker = init_tracker(44100, 50, false, 1024);
+    FlizzerTrackerApp *tracker = init_tracker(44100, 50, true, 1024);
 
     // Текущее событие типа кастомного типа FlizzerTrackerEvent
     FlizzerTrackerEvent event;
@@ -138,8 +144,6 @@ int32_t flizzer_tracker_app(void *p)
     with_view_model(
         tracker->tracker_view->view, TrackerViewModel * model, { model->tracker = tracker; }, true);
 
-    view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER);
-
     tracker->text_input = text_input_alloc();
     view_dispatcher_add_view(tracker->view_dispatcher, VIEW_KEYBOARD, text_input_get_view(tracker->text_input));
 
@@ -206,6 +210,7 @@ int32_t flizzer_tracker_app(void *p)
         set_instrument(&tracker->song.pattern[1].step[i], 1);
     }
 
+    tracker->song.instrument[0]->base_note = MIDDLE_C;
     tracker->song.instrument[0]->adsr.a = 0x2;
     tracker->song.instrument[0]->adsr.d = 0x9;
     tracker->song.instrument[0]->adsr.volume = 0x80;
@@ -216,6 +221,7 @@ int32_t flizzer_tracker_app(void *p)
     tracker->song.instrument[0]->filter_type = FIL_OUTPUT_LOWPASS;
     tracker->song.instrument[0]->filter_cutoff = 10;*/
 
+    tracker->song.instrument[1]->base_note = MIDDLE_C;
     tracker->song.instrument[1]->adsr.a = 0x0;
     tracker->song.instrument[1]->adsr.d = 0x3;
     tracker->song.instrument[1]->adsr.volume = 0x18;
@@ -225,6 +231,8 @@ int32_t flizzer_tracker_app(void *p)
     tracker->tracker_engine.playing = false;
     play();
 
+    view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER);
+
     // Бесконечный цикл обработки очереди событий
     while (!(tracker->quit))
     {

+ 59 - 5
flizzer_tracker.h

@@ -8,8 +8,8 @@
 #include <stdio.h>
 #include <u8g2_glue.h>
 
-#include <gui/view_dispatcher.h>
 #include <gui/modules/text_input.h>
+#include <gui/view_dispatcher.h>
 
 #include "flizzer_tracker_hal.h"
 #include "sound_engine/freqs.h"
@@ -17,9 +17,6 @@
 #include "sound_engine/sound_engine_filter.h"
 #include "tracker_engine/tracker_engine_defs.h"
 
-#define MIDDLE_C (12 * 4)
-#define MAX_NOTE (12 * 7 + 11)
-
 typedef enum
 {
     EventTypeInput,
@@ -63,6 +60,60 @@ typedef enum
     SI_PARAMS,
 } SongInfoParam;
 
+typedef enum
+{
+    INST_CURRENTINSTRUMENT,
+    INST_INSTRUMENTNAME,
+
+    INST_CURRENT_NOTE,
+    INST_FINETUNE,
+
+    INST_SLIDESPEED,
+    INST_SETPW,
+    INST_PW,
+    INST_SETCUTOFF,
+
+    INST_WAVE_NOISE,
+    INST_WAVE_PULSE,
+    INST_WAVE_TRIANGLE,
+    INST_WAVE_SAWTOOTH,
+    INST_WAVE_NOISE_METAL,
+    INST_WAVE_SINE,
+
+    INST_ATTACK,
+    INST_DECAY,
+    INST_SUSTAIN,
+    INST_RELEASE,
+    INST_VOLUME,
+
+    INST_ENABLEFILTER,
+    INST_FILTERCUTOFF,
+    INST_FILTERRESONANCE,
+
+    INST_FILTERTYPE,
+    INST_ENABLERINGMOD,
+    INST_RINGMODSRC,
+    INST_ENABLEHARDSYNC,
+    INST_HARDSYNCSRC,
+
+    INST_RETRIGGERONSLIDE,
+    INST_ENABLEKEYSYNC,
+
+    INST_ENABLEVIBRATO,
+    INST_VIBRATOSPEED,
+    INST_VIBRATODEPTH,
+    INST_VIBRATODELAY,
+
+    INST_ENABLEPWM,
+    INST_PWMSPEED,
+    INST_PWMDEPTH,
+    INST_PWMDELAY,
+
+    INST_PROGRAMEPERIOD,
+    /* ========  */
+    INST_PARAMS,
+} InstrumentParam;
+
 typedef struct
 {
     View *view;
@@ -84,7 +135,7 @@ typedef struct
     Gui *gui;
     TrackerView *tracker_view;
     ViewDispatcher *view_dispatcher;
-    TextInput* text_input;
+    TextInput *text_input;
     bool was_it_back_keypress;
     uint32_t current_time;
     uint32_t period;
@@ -98,6 +149,9 @@ typedef struct
 
     uint8_t mode, focus;
     uint8_t patternx, current_channel, current_digit, program_step, current_instrument, current_note;
+
+    uint8_t inst_editor_shift;
+
     bool editing;
     bool was_editing;
 

BIN
images/channel_off.png


BIN
images/channel_on.png


BIN
images/flizzer_tracker_instrument.png


+ 6 - 0
init_deinit.c

@@ -18,6 +18,12 @@ FlizzerTrackerApp *init_tracker(uint32_t sample_rate, uint8_t rate, bool externa
 void deinit_tracker(FlizzerTrackerApp *tracker)
 {
     sound_engine_deinit(&tracker->sound_engine);
+
+    if (tracker->tracker_engine.song == NULL)
+    {
+        tracker_engine_set_song(&tracker->tracker_engine, &tracker->song);
+    }
+
     tracker_engine_deinit(&tracker->tracker_engine, false);
 
     FURI_CRITICAL_ENTER();

+ 572 - 2
input/instrument.c

@@ -1,7 +1,577 @@
 #include "instrument.h"
+#include "songinfo.h"
+
+void edit_instrument_param(FlizzerTrackerApp *tracker, uint8_t selected_param, int8_t delta)
+{
+    if (!(tracker->current_digit))
+    {
+        delta *= 16;
+    }
+
+    Instrument *inst = tracker->song.instrument[tracker->current_instrument];
+
+    switch (selected_param)
+    {
+        case INST_CURRENTINSTRUMENT:
+        {
+            int16_t inst = tracker->current_instrument;
+
+            if (inst + delta >= MUS_NOTE_INSTRUMENT_NONE)
+            {
+                if (delta > 0)
+                {
+                    inst = 0;
+                }
+
+                else
+                {
+                    inst = MUS_NOTE_INSTRUMENT_NONE - 1;
+                }
+            }
+
+            clamp(inst, delta, 0, tracker->song.num_instruments - 1);
+
+            tracker->current_instrument = inst;
+
+            break;
+        }
+
+        case INST_INSTRUMENTNAME:
+        {
+            text_input_set_header_text(tracker->text_input, "Instrument name:");
+            text_input_set_result_callback(tracker->text_input, return_from_keyboard_callback, tracker, (char *)&inst->name, MUS_INST_NAME_LEN + 1, false);
+
+            view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD);
+            break;
+        }
+
+        case INST_CURRENT_NOTE:
+        {
+            int8_t note_delta = 0;
+
+            if (delta < 0)
+            {
+                if (tracker->current_digit)
+                {
+                    note_delta = -12;
+                }
+
+                else
+                {
+                    note_delta = -1;
+                }
+            }
+
+            if (delta > 0)
+            {
+                if (tracker->current_digit)
+                {
+                    note_delta = 12;
+                }
+
+                else
+                {
+                    note_delta = 1;
+                }
+            }
+
+            clamp(inst->base_note, note_delta, 0, MAX_NOTE);
+
+            break;
+        }
+
+        case INST_FINETUNE:
+        {
+            int8_t fine_delta = 0;
+
+            if (delta < 0)
+            {
+                if (tracker->current_digit)
+                {
+                    fine_delta = -1;
+                }
+
+                else
+                {
+                    fine_delta = -10;
+                }
+            }
+
+            if (delta > 0)
+            {
+                if (tracker->current_digit)
+                {
+                    fine_delta = 1;
+                }
+
+                else
+                {
+                    fine_delta = 10;
+                }
+            }
+
+            inst->finetune += fine_delta;
+
+            break;
+        }
+
+        case INST_SLIDESPEED:
+        {
+            if ((int16_t)inst->slide_speed + (int16_t)delta >= 0 && (int16_t)inst->slide_speed + (int16_t)delta <= 0xff)
+            {
+                inst->slide_speed += delta;
+            }
+
+            break;
+        }
+
+        case INST_SETPW:
+        {
+            flipbit(inst->flags, TE_SET_PW);
+            break;
+        }
+
+        case INST_PW:
+        {
+            if ((int16_t)inst->pw + (int16_t)delta >= 0 && (int16_t)inst->pw + (int16_t)delta <= 0xff)
+            {
+                inst->pw += delta;
+            }
+
+            break;
+        }
+
+        case INST_SETCUTOFF:
+        {
+            flipbit(inst->flags, TE_SET_CUTOFF);
+            break;
+        }
+
+        case INST_WAVE_NOISE:
+        {
+            flipbit(inst->waveform, SE_WAVEFORM_NOISE);
+            break;
+        }
+
+        case INST_WAVE_PULSE:
+        {
+            flipbit(inst->waveform, SE_WAVEFORM_PULSE);
+            break;
+        }
+
+        case INST_WAVE_TRIANGLE:
+        {
+            flipbit(inst->waveform, SE_WAVEFORM_TRIANGLE);
+            break;
+        }
+
+        case INST_WAVE_SAWTOOTH:
+        {
+            flipbit(inst->waveform, SE_WAVEFORM_SAW);
+            break;
+        }
+
+        case INST_WAVE_NOISE_METAL:
+        {
+            flipbit(inst->waveform, SE_WAVEFORM_NOISE_METAL);
+            break;
+        }
+
+        case INST_WAVE_SINE:
+        {
+            flipbit(inst->waveform, SE_WAVEFORM_SINE);
+            break;
+        }
+
+        case INST_ATTACK:
+        {
+            if ((int16_t)inst->adsr.a + (int16_t)delta >= 0 && (int16_t)inst->adsr.a + (int16_t)delta <= 0xff)
+            {
+                inst->adsr.a += delta;
+            }
+
+            break;
+        }
+
+        case INST_DECAY:
+        {
+            if ((int16_t)inst->adsr.d + (int16_t)delta >= 0 && (int16_t)inst->adsr.d + (int16_t)delta <= 0xff)
+            {
+                inst->adsr.d += delta;
+            }
+
+            break;
+        }
+
+        case INST_SUSTAIN:
+        {
+            if ((int16_t)inst->adsr.s + (int16_t)delta >= 0 && (int16_t)inst->adsr.s + (int16_t)delta <= 0xff)
+            {
+                inst->adsr.s += delta;
+            }
+
+            break;
+        }
+
+        case INST_RELEASE:
+        {
+            if ((int16_t)inst->adsr.r + (int16_t)delta >= 0 && (int16_t)inst->adsr.r + (int16_t)delta <= 0xff)
+            {
+                inst->adsr.r += delta;
+            }
+
+            break;
+        }
+
+        case INST_VOLUME:
+        {
+            if ((int16_t)inst->adsr.volume + (int16_t)delta >= 0 && (int16_t)inst->adsr.volume + (int16_t)delta <= 0xff)
+            {
+                inst->adsr.volume += delta;
+            }
+
+            break;
+        }
+
+        case INST_ENABLEFILTER:
+        {
+            flipbit(inst->sound_engine_flags, SE_ENABLE_FILTER);
+            break;
+        }
+
+        case INST_FILTERCUTOFF:
+        {
+            if ((int16_t)inst->filter_cutoff + (int16_t)delta >= 0 && (int16_t)inst->filter_cutoff + (int16_t)delta <= 0xff)
+            {
+                inst->filter_cutoff += delta;
+            }
+
+            break;
+        }
+
+        case INST_FILTERRESONANCE:
+        {
+            if ((int16_t)inst->filter_resonance + (int16_t)delta >= 0 && (int16_t)inst->filter_resonance + (int16_t)delta <= 0xff)
+            {
+                inst->filter_resonance += delta;
+            }
+
+            break;
+        }
+
+        case INST_FILTERTYPE:
+        {
+            int8_t flt_delta = (delta > 0 ? 1 : -1);
+
+            if ((int16_t)inst->filter_type + (int16_t)flt_delta >= 0 && (int16_t)inst->filter_type + (int16_t)flt_delta < FIL_MODES)
+            {
+                inst->filter_type += flt_delta;
+            }
+
+            break;
+        }
+
+        case INST_ENABLERINGMOD:
+        {
+            flipbit(inst->sound_engine_flags, SE_ENABLE_RING_MOD);
+            break;
+        }
+
+        case INST_RINGMODSRC:
+        {
+            if ((int16_t)inst->ring_mod + (int16_t)delta >= 0 && (int16_t)inst->ring_mod + (int16_t)delta < SONG_MAX_CHANNELS)
+            {
+                inst->ring_mod += delta;
+            }
+
+            if ((int16_t)inst->ring_mod + (int16_t)delta < 0)
+            {
+                inst->ring_mod = 0xff; // 0xff = self
+            }
+
+            break;
+        }
+
+        case INST_ENABLEHARDSYNC:
+        {
+            flipbit(inst->sound_engine_flags, SE_ENABLE_HARD_SYNC);
+            break;
+        }
+
+        case INST_HARDSYNCSRC:
+        {
+            if ((int16_t)inst->hard_sync + (int16_t)delta >= 0 && (int16_t)inst->hard_sync + (int16_t)delta < SONG_MAX_CHANNELS)
+            {
+                inst->hard_sync += delta;
+            }
+
+            if ((int16_t)inst->hard_sync + (int16_t)delta < 0)
+            {
+                inst->hard_sync = 0xff; // 0xff = self
+            }
+
+            break;
+        }
+
+        case INST_RETRIGGERONSLIDE:
+        {
+            flipbit(inst->flags, TE_RETRIGGER_ON_SLIDE);
+            break;
+        }
+
+        case INST_ENABLEKEYSYNC:
+        {
+            flipbit(inst->sound_engine_flags, SE_ENABLE_KEYDOWN_SYNC);
+            break;
+        }
+
+        case INST_ENABLEVIBRATO:
+        {
+            flipbit(inst->flags, TE_ENABLE_VIBRATO);
+            break;
+        }
+
+        case INST_VIBRATOSPEED:
+        {
+            if ((int16_t)inst->vibrato_speed + (int16_t)delta >= 0 && (int16_t)inst->vibrato_speed + (int16_t)delta <= 0xff)
+            {
+                inst->vibrato_speed += delta;
+            }
+
+            break;
+        }
+
+        case INST_VIBRATODEPTH:
+        {
+            if ((int16_t)inst->vibrato_depth + (int16_t)delta >= 0 && (int16_t)inst->vibrato_depth + (int16_t)delta <= 0xff)
+            {
+                inst->vibrato_depth += delta;
+            }
+
+            break;
+        }
+
+        case INST_VIBRATODELAY:
+        {
+            if ((int16_t)inst->vibrato_delay + (int16_t)delta >= 0 && (int16_t)inst->vibrato_delay + (int16_t)delta <= 0xff)
+            {
+                inst->vibrato_delay += delta;
+            }
+
+            break;
+        }
+
+        case INST_ENABLEPWM:
+        {
+            flipbit(inst->flags, TE_ENABLE_PWM);
+            break;
+        }
+
+        case INST_PWMSPEED:
+        {
+            if ((int16_t)inst->pwm_speed + (int16_t)delta >= 0 && (int16_t)inst->pwm_speed + (int16_t)delta <= 0xff)
+            {
+                inst->pwm_speed += delta;
+            }
+
+            break;
+        }
+
+        case INST_PWMDEPTH:
+        {
+            if ((int16_t)inst->pwm_depth + (int16_t)delta >= 0 && (int16_t)inst->pwm_depth + (int16_t)delta <= 0xff)
+            {
+                inst->pwm_depth += delta;
+            }
+
+            break;
+        }
+
+        case INST_PWMDELAY:
+        {
+            if ((int16_t)inst->pwm_delay + (int16_t)delta >= 0 && (int16_t)inst->pwm_delay + (int16_t)delta <= 0xff)
+            {
+                inst->pwm_delay += delta;
+            }
+
+            break;
+        }
+    }
+}
 
 void instrument_edit_event(FlizzerTrackerApp *tracker, FlizzerTrackerEvent *event)
 {
-    UNUSED(tracker);
-    UNUSED(event);
+    if (event->input.key == InputKeyOk && event->input.type == InputTypeShort && !tracker->tracker_engine.playing)
+    {
+        tracker->editing = !(tracker->editing);
+        return;
+    }
+
+    if (event->input.key == InputKeyOk && event->input.type == InputTypeLong && !tracker->editing)
+    {
+        // play_song(tracker, true);
+        tracker_engine_set_song(&tracker->tracker_engine, NULL);
+
+        Instrument *inst = tracker->song.instrument[tracker->current_instrument];
+        tracker_engine_trigger_instrument_internal(&tracker->tracker_engine, 0, inst, (MIDDLE_C << 8));
+        tracker->tracker_engine.playing = true;
+        play();
+        return;
+    }
+
+    if (event->input.key == InputKeyOk && event->input.type == InputTypeRelease && !tracker->editing)
+    {
+        SoundEngineChannel *se_channel = &tracker->sound_engine.channel[0];
+        sound_engine_enable_gate(&tracker->sound_engine, se_channel, false);
+        // tracker_engine_set_song(&tracker->tracker_engine, &tracker->song);
+        return;
+    }
+
+    if (event->input.key == InputKeyRight && event->input.type == InputTypeShort && tracker->editing)
+    {
+        switch (tracker->selected_param)
+        {
+            default:
+            {
+                tracker->current_digit++;
+
+                if (tracker->current_digit > 1)
+                {
+                    tracker->selected_param++;
+
+                    tracker->current_digit = 0;
+
+                    if (tracker->selected_param > INST_PARAMS - 1)
+                    {
+                        tracker->selected_param = 0;
+                    }
+                }
+
+                break;
+            }
+
+            case INST_CURRENTINSTRUMENT:
+            case INST_INSTRUMENTNAME:
+            case INST_SETPW:
+            case INST_SETCUTOFF:
+            case INST_WAVE_NOISE:
+            case INST_WAVE_PULSE:
+            case INST_WAVE_TRIANGLE:
+            case INST_WAVE_SAWTOOTH:
+            case INST_WAVE_NOISE_METAL:
+            case INST_WAVE_SINE:
+            case INST_ENABLEFILTER:
+            case INST_FILTERTYPE:
+            case INST_ENABLERINGMOD:
+            case INST_RINGMODSRC:
+            case INST_ENABLEHARDSYNC:
+            case INST_HARDSYNCSRC:
+            case INST_RETRIGGERONSLIDE:
+            case INST_ENABLEKEYSYNC:
+            case INST_ENABLEVIBRATO:
+            case INST_ENABLEPWM:
+            {
+                tracker->selected_param++;
+
+                tracker->current_digit = 1;
+
+                if (tracker->selected_param > INST_PARAMS - 1)
+                {
+                    tracker->selected_param = 0;
+                }
+
+                break;
+            }
+        }
+    }
+
+    if (event->input.key == InputKeyLeft && event->input.type == InputTypeShort && tracker->editing)
+    {
+        switch (tracker->selected_param)
+        {
+            default:
+            {
+                tracker->current_digit--;
+
+                if (tracker->current_digit > 1) // unsigned int overflow
+                {
+                    tracker->selected_param--;
+
+                    tracker->current_digit = 1;
+
+                    if (tracker->selected_param > INST_PARAMS - 1) // unsigned int overflow
+                    {
+                        tracker->selected_param = INST_PARAMS - 1;
+                    }
+                }
+
+                break;
+            }
+
+            case INST_CURRENTINSTRUMENT:
+            case INST_INSTRUMENTNAME:
+            case INST_SETPW:
+            case INST_SETCUTOFF:
+            case INST_WAVE_NOISE:
+            case INST_WAVE_PULSE:
+            case INST_WAVE_TRIANGLE:
+            case INST_WAVE_SAWTOOTH:
+            case INST_WAVE_NOISE_METAL:
+            case INST_WAVE_SINE:
+            case INST_ENABLEFILTER:
+            case INST_FILTERTYPE:
+            case INST_ENABLERINGMOD:
+            case INST_RINGMODSRC:
+            case INST_ENABLEHARDSYNC:
+            case INST_HARDSYNCSRC:
+            case INST_RETRIGGERONSLIDE:
+            case INST_ENABLEKEYSYNC:
+            case INST_ENABLEVIBRATO:
+            case INST_ENABLEPWM:
+            {
+                tracker->selected_param--;
+
+                tracker->current_digit = 1;
+
+                if (tracker->selected_param > INST_PARAMS - 1) // unsigned int overflow
+                {
+                    tracker->selected_param = INST_PARAMS - 1;
+                }
+
+                break;
+            }
+        }
+
+        return;
+    }
+
+    if (event->input.key == InputKeyDown && event->input.type == InputTypeShort)
+    {
+        if (tracker->editing)
+        {
+            edit_instrument_param(tracker, tracker->selected_param, -1);
+        }
+
+        return;
+    }
+
+    if (event->input.key == InputKeyUp && event->input.type == InputTypeShort)
+    {
+        if (tracker->editing)
+        {
+            edit_instrument_param(tracker, tracker->selected_param, 1);
+        }
+
+        return;
+    }
+
+    if (tracker->selected_param > INST_VIBRATODELAY)
+    {
+        tracker->inst_editor_shift = 6;
+    }
+
+    if (tracker->selected_param < INST_CURRENT_NOTE)
+    {
+        tracker->inst_editor_shift = 0;
+    }
 }

+ 3 - 3
input/pattern.c

@@ -53,7 +53,7 @@ void edit_note(FlizzerTrackerApp *tracker, TrackerSongPatternStep *step, int8_t
     tracker->current_note = (uint8_t)note;
 }
 
-void edit_instrument(TrackerSongPatternStep *step, int8_t delta)
+void edit_instrument(FlizzerTrackerApp *tracker, TrackerSongPatternStep *step, int8_t delta)
 {
     int16_t inst = tracker_engine_get_instrument(step);
 
@@ -71,7 +71,7 @@ void edit_instrument(TrackerSongPatternStep *step, int8_t delta)
     }
 
     clamp(inst, delta, 0, MUS_NOTE_INSTRUMENT_NONE - 1);
-
+    tracker->current_instrument = inst; // remember last instrument
     set_instrument(step, (uint8_t)inst);
 }
 
@@ -246,7 +246,7 @@ void edit_pattern_step(FlizzerTrackerApp *tracker, TrackerSongPatternStep *step,
 
         case 1: // instrument
         {
-            edit_instrument(step, delta);
+            edit_instrument(tracker, step, delta);
             break;
         }
 

+ 28 - 16
input/songinfo.c

@@ -1,41 +1,53 @@
 #include "songinfo.h"
 
-#include <ctype.h>
-
-void return_from_keyboard_callback(void* ctx)
+void return_from_keyboard_callback(void *ctx)
 {
-    FlizzerTrackerApp *tracker = (FlizzerTrackerApp*)ctx;
+    FlizzerTrackerApp *tracker = (FlizzerTrackerApp *)ctx;
 
     uint8_t string_length = 0;
-    char* string = NULL;
+    char *string = NULL;
 
-    if(tracker->focus == EDIT_SONGINFO && tracker->mode == PATTERN_VIEW)
+    if (tracker->focus == EDIT_SONGINFO && tracker->mode == PATTERN_VIEW)
     {
-        switch(tracker->selected_param)
+        switch (tracker->selected_param)
         {
             case SI_SONGNAME:
             {
                 string_length = MUS_SONG_NAME_LEN;
-                string = (char*)&tracker->song.song_name;
+                string = (char *)&tracker->song.song_name;
                 break;
             }
 
             case SI_INSTRUMENTNAME:
             {
                 string_length = MUS_INST_NAME_LEN;
-                string = (char*)&tracker->song.instrument[tracker->current_instrument]->name;
+                string = (char *)&tracker->song.instrument[tracker->current_instrument]->name;
                 break;
             }
         }
     }
 
-    if(string == NULL || string_length == 0) return;
+    if (tracker->focus == EDIT_INSTRUMENT && tracker->mode == INST_EDITOR_VIEW)
+    {
+        switch (tracker->selected_param)
+        {
+            case INST_INSTRUMENTNAME:
+            {
+                string_length = MUS_INST_NAME_LEN;
+                string = (char *)&tracker->song.instrument[tracker->current_instrument]->name;
+                break;
+            }
+        }
+    }
 
-    for(uint8_t i = 0; i < string_length; i++) //I tinyfied the font by deleting lowercase chars, and I don't like the lowercase chars of any 3x5 pixels font
+    if (string == NULL || string_length == 0)
+        return;
+
+    for (uint8_t i = 0; i < string_length; i++) // I tinyfied the font by deleting lowercase chars, and I don't like the lowercase chars of any 3x5 pixels font
     {
         string[i] = toupper(string[i]);
     }
-    
+
     view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER);
 }
 
@@ -101,7 +113,7 @@ void edit_songinfo_param(FlizzerTrackerApp *tracker, uint8_t selected_param, int
         case SI_SONGNAME:
         {
             text_input_set_header_text(tracker->text_input, "Song name:");
-            text_input_set_result_callback(tracker->text_input, return_from_keyboard_callback, tracker, (char*)&tracker->song.song_name, MUS_SONG_NAME_LEN + 1, false);
+            text_input_set_result_callback(tracker->text_input, return_from_keyboard_callback, tracker, (char *)&tracker->song.song_name, MUS_SONG_NAME_LEN + 1, false);
 
             view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD);
             break;
@@ -134,7 +146,7 @@ void edit_songinfo_param(FlizzerTrackerApp *tracker, uint8_t selected_param, int
         case SI_INSTRUMENTNAME:
         {
             text_input_set_header_text(tracker->text_input, "Instrument name:");
-            text_input_set_result_callback(tracker->text_input, return_from_keyboard_callback, tracker, (char*)&tracker->song.instrument[tracker->current_instrument]->name, MUS_INST_NAME_LEN + 1, false);
+            text_input_set_result_callback(tracker->text_input, return_from_keyboard_callback, tracker, (char *)&tracker->song.instrument[tracker->current_instrument]->name, MUS_INST_NAME_LEN + 1, false);
 
             view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_KEYBOARD);
             break;
@@ -154,7 +166,7 @@ void songinfo_edit_event(FlizzerTrackerApp *tracker, FlizzerTrackerEvent *event)
 
     if (event->input.key == InputKeyRight && event->input.type == InputTypeShort && tracker->editing)
     {
-        switch(tracker->selected_param)
+        switch (tracker->selected_param)
         {
             default:
             {
@@ -195,7 +207,7 @@ void songinfo_edit_event(FlizzerTrackerApp *tracker, FlizzerTrackerEvent *event)
 
     if (event->input.key == InputKeyLeft && event->input.type == InputTypeShort && tracker->editing)
     {
-        switch(tracker->selected_param)
+        switch (tracker->selected_param)
         {
             default:
             {

+ 3 - 1
input/songinfo.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include <ctype.h>
 #include <furi.h>
 #include <input/input.h>
 #include <stdio.h>
@@ -9,4 +10,5 @@
 #include "../tracker_engine/tracker_engine_defs.h"
 #include "../util.h"
 
-void songinfo_edit_event(FlizzerTrackerApp *tracker, FlizzerTrackerEvent *event);
+void songinfo_edit_event(FlizzerTrackerApp *tracker, FlizzerTrackerEvent *event);
+void return_from_keyboard_callback(void *ctx);

+ 6 - 0
input_event.c

@@ -40,6 +40,9 @@ void cycle_view(FlizzerTrackerApp *tracker)
         tracker->mode = INST_EDITOR_VIEW;
         tracker->focus = EDIT_INSTRUMENT;
 
+        tracker->selected_param = 0;
+        tracker->current_digit = 0;
+
         return;
     }
 
@@ -48,6 +51,9 @@ void cycle_view(FlizzerTrackerApp *tracker)
         tracker->mode = PATTERN_VIEW;
         tracker->focus = EDIT_PATTERN;
 
+        tracker->selected_param = 0;
+        tracker->current_digit = 0;
+
         return;
     }
 }

+ 4 - 4
sound_engine/freqs.c

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

+ 1 - 0
sound_engine/freqs.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include <furi.h>
 #include <stdio.h>
 
 #define FREQ_TAB_SIZE 12 /* one octave */

+ 1 - 1
sound_engine/sound_engine.c

@@ -39,7 +39,7 @@ void sound_engine_deinit(SoundEngine *sound_engine)
 
     else
     {
-        furi_hal_gpio_init_simple(&gpio_ext_pa6, GpioModeOutputPushPull);
+        furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
     }
 
     furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdDma1Ch1, 13, NULL, NULL);

+ 3 - 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 / 8)) * 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
 {
@@ -46,6 +46,8 @@ typedef enum
     FIL_OUTPUT_LOWPASS = 1,
     FIL_OUTPUT_HIGHPASS = 2,
     FIL_OUTPUT_BANDPASS = 3,
+    /* ============ */
+    FIL_MODES = 4,
 } SoundEngineFilterModes;
 
 typedef enum

+ 23 - 21
sound_engine/sound_engine_osc.c

@@ -31,20 +31,35 @@ inline static void shift_lfsr(uint32_t *v, uint32_t tap_0, uint32_t tap_1)
     *v = (*v >> 1) ^ ((zero - (*v & lsb)) & feedback);
 }
 
+static inline uint16_t sound_engine_noise(SoundEngineChannel *channel, uint32_t prev_acc)
+{
+    if ((prev_acc & (ACC_LENGTH / 32)) != (channel->accumulator & (ACC_LENGTH / 32)))
+    {
+        if (channel->waveform & SE_WAVEFORM_NOISE_METAL)
+        {
+            shift_lfsr(&channel->lfsr, 14, 8);
+            channel->lfsr &= (1 << (14 + 1)) - 1;
+        }
+
+        else
+        {
+            shift_lfsr(&channel->lfsr, 22, 17);
+            channel->lfsr &= (1 << (22 + 1)) - 1;
+        }
+    }
+
+    return (channel->lfsr) & (WAVE_AMP - 1);
+}
+
 uint16_t sound_engine_osc(SoundEngine *sound_engine, SoundEngineChannel *channel, uint32_t prev_acc)
 {
     switch (channel->waveform)
     {
         case SE_WAVEFORM_NOISE:
+        case SE_WAVEFORM_NOISE_METAL:
+        case (SE_WAVEFORM_NOISE | SE_WAVEFORM_NOISE_METAL):
         {
-            if ((prev_acc & (ACC_LENGTH / 32)) != (channel->accumulator & (ACC_LENGTH / 32)))
-            {
-                shift_lfsr(&channel->lfsr, 22, 17);
-                channel->lfsr &= (1 << (22 + 1)) - 1;
-            }
-
-            return (channel->lfsr) & (WAVE_AMP - 1);
-
+            return sound_engine_noise(channel, prev_acc);
             break;
         }
 
@@ -66,19 +81,6 @@ uint16_t sound_engine_osc(SoundEngine *sound_engine, SoundEngineChannel *channel
             break;
         }
 
-        case SE_WAVEFORM_NOISE_METAL:
-        {
-            if ((prev_acc & (ACC_LENGTH / 32)) != (channel->accumulator & (ACC_LENGTH / 32)))
-            {
-                shift_lfsr(&channel->lfsr, 14, 8);
-                channel->lfsr &= (1 << (14 + 1)) - 1;
-            }
-
-            return (channel->lfsr) & (WAVE_AMP - 1);
-
-            break;
-        }
-
         case SE_WAVEFORM_SINE:
         {
             return sound_engine_sine(channel->accumulator, sound_engine);

+ 25 - 11
tracker_engine/tracker_engine.c

@@ -73,16 +73,21 @@ void tracker_engine_set_note(TrackerEngine *tracker_engine, uint8_t chan, uint16
     sound_engine_set_channel_frequency(tracker_engine->sound_engine, &tracker_engine->sound_engine->channel[chan], note);
 }
 
+void tracker_engine_set_song(TrackerEngine *tracker_engine, TrackerSong *song)
+{
+    tracker_engine->song = song;
+}
+
 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);
+    te_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->flags |= TEC_PROGRAM_RUNNING;
 
         te_channel->program_counter = 0;
         te_channel->program_loop = 0;
@@ -95,11 +100,12 @@ void tracker_engine_trigger_instrument_internal(TrackerEngine *tracker_engine, u
     se_channel->waveform = pinst->waveform;
     se_channel->flags = pinst->sound_engine_flags;
 
-    te_channel->flags = pinst->flags;
+    te_channel->channel_flags = pinst->flags;
 
     te_channel->arpeggio_note = 0;
     te_channel->fixed_note = 0xffff;
 
+    note += (uint16_t)(((int16_t)pinst->base_note - MIDDLE_C) << 8);
     tracker_engine_set_note(tracker_engine, chan, note + (int16_t)pinst->finetune, true);
 
     te_channel->last_note = te_channel->target_note = note + (int16_t)pinst->finetune;
@@ -113,13 +119,13 @@ void tracker_engine_trigger_instrument_internal(TrackerEngine *tracker_engine, u
 
     if (pinst->flags & TE_SET_CUTOFF)
     {
-        te_channel->filter_cutoff = (pinst->filter_cutoff << 3);
-        te_channel->filter_resonance = pinst->filter_resonance;
+        te_channel->filter_cutoff = ((uint16_t)pinst->filter_cutoff << 3);
+        te_channel->filter_resonance = ((uint16_t)pinst->filter_resonance << 5);
 
         sound_engine_filter_set_coeff(&se_channel->filter, te_channel->filter_cutoff, te_channel->filter_resonance);
     }
 
-    if(pinst->sound_engine_flags & SE_ENABLE_FILTER)
+    if (pinst->sound_engine_flags & SE_ENABLE_FILTER)
     {
         te_channel->filter_type = pinst->filter_type;
         se_channel->filter_mode = te_channel->filter_type;
@@ -133,6 +139,9 @@ void tracker_engine_trigger_instrument_internal(TrackerEngine *tracker_engine, u
         se_channel->pw = (pinst->pw << 4);
     }
 
+    se_channel->ring_mod = pinst->ring_mod;
+    se_channel->hard_sync = pinst->hard_sync;
+
     te_channel->slide_speed = 0;
 
     se_channel->adsr.a = pinst->adsr.a;
@@ -188,13 +197,18 @@ void tracker_engine_advance_channel(TrackerEngine *tracker_engine, uint8_t chan)
         // TODO: add instrument program execution
 
         // TODO: add PWM and vibrato execution
-        uint8_t vib = 0;
+        uint16_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);
     }
@@ -215,7 +229,7 @@ void tracker_engine_advance_tick(TrackerEngine *tracker_engine)
         SoundEngineChannel *se_channel = &tracker_engine->sound_engine->channel[chan];
         TrackerEngineChannel *te_channel = &tracker_engine->channel[chan];
 
-        if(tracker_engine->song)
+        if (tracker_engine->song)
         {
             uint8_t sequence_position = tracker_engine->sequence_position;
             uint8_t current_pattern = song->sequence.sequence_step[sequence_position].pattern_indices[chan];
@@ -264,7 +278,7 @@ void tracker_engine_advance_tick(TrackerEngine *tracker_engine)
                     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->note = (note << 8) + pinst->finetune;
 
                     te_channel->target_note = (note << 8) + pinst->finetune;
 
@@ -278,10 +292,10 @@ void tracker_engine_advance_tick(TrackerEngine *tracker_engine)
             tracker_engine_execute_track_command(tracker_engine, chan, &pattern->step[pattern_step], tracker_engine->current_tick == note_delay);
         }
 
-        tracker_engine_advance_channel(tracker_engine, chan); //this will be executed even if the song pointer is NULL; handy for live instrument playback from inst editor ("jams")
+        tracker_engine_advance_channel(tracker_engine, chan); // this will be executed even if the song pointer is NULL; handy for live instrument playback from inst editor ("jams")
     }
 
-    if(tracker_engine->song)
+    if (tracker_engine->song)
     {
         tracker_engine->current_tick++;
 

+ 2 - 0
tracker_engine/tracker_engine.h

@@ -5,6 +5,8 @@
 void tracker_engine_init(TrackerEngine *tracker_engine, uint8_t rate, SoundEngine *sound_engine);
 void tracker_engine_deinit(TrackerEngine *tracker_engine, bool free_song);
 void tracker_engine_advance_tick(TrackerEngine *tracker_engine);
+void tracker_engine_set_song(TrackerEngine *tracker_engine, TrackerSong *song);
+void tracker_engine_trigger_instrument_internal(TrackerEngine *tracker_engine, uint8_t chan, Instrument *pinst, uint16_t note);
 
 uint8_t tracker_engine_get_note(TrackerSongPatternStep *step);
 uint8_t tracker_engine_get_instrument(TrackerSongPatternStep *step);

+ 6 - 2
tracker_engine/tracker_engine_defs.h

@@ -26,6 +26,9 @@
 
 #define TRACKER_ENGINE_VERSION 1
 
+#define MIDDLE_C (12 * 4)
+#define MAX_NOTE (12 * 7 + 11)
+
 typedef enum
 {
     TE_ENABLE_VIBRATO = 1,
@@ -73,6 +76,7 @@ typedef struct
 
     uint8_t filter_cutoff, filter_resonance, filter_type;
 
+    uint8_t base_note;
     int8_t finetune;
 } Instrument;
 
@@ -91,8 +95,8 @@ typedef struct
 
     uint8_t program_counter, program_tick, program_loop, program_period;
 
-    uint16_t filter_cutoff;
-    uint8_t filter_resonance, filter_type;
+    uint16_t filter_cutoff, filter_resonance;
+    uint8_t filter_type;
 
     uint8_t vibrato_speed, vibrato_depth, vibrato_delay;
     uint8_t pwm_speed, pwm_depth, pwm_delay;

+ 2 - 0
util.c

@@ -43,6 +43,8 @@ void play_song(FlizzerTrackerApp *tracker, bool from_cursor)
 
     tracker_engine_set_rate(tracker->song.rate);
 
+    tracker_engine_set_song(&tracker->tracker_engine, &tracker->song);
+
     play();
 }
 

+ 4 - 0
util.h

@@ -8,6 +8,10 @@
 #include "tracker_engine/tracker_engine_defs.h"
 
 #define clamp(val, add, _min, _max) val = fmin(_max, fmax(_min, (int32_t)val + add))
+#define flipbit(val, bit) \
+    {                     \
+        val ^= bit;       \
+    };
 
 void set_note(TrackerSongPatternStep *step, uint8_t note);
 void set_instrument(TrackerSongPatternStep *step, uint8_t inst);

+ 138 - 3
view/instrument_editor.c

@@ -1,10 +1,145 @@
 #include "instrument_editor.h"
+#include "pattern_editor.h"
+
+#include <flizzer_tracker_icons.h>
+
+void draw_inst_flag(FlizzerTrackerApp *tracker, Canvas *canvas, uint8_t focus, uint8_t param, const char *text, uint8_t x, uint8_t y, uint16_t flags, uint16_t mask)
+{
+    canvas_draw_icon(canvas, x, y - 5, ((flags & mask) ? &I_checkbox_checked : &I_checkbox_empty));
+    canvas_draw_str(canvas, x + 6, y, text);
+
+    if (tracker->focus == focus && tracker->selected_param == param && tracker->editing)
+    {
+        if (text[strlen(text) - 1] == ':')
+        {
+            canvas_draw_box(canvas, x + 5, y - 6, strlen(text) * 4 - 1, 7);
+        }
+
+        else
+        {
+            canvas_draw_box(canvas, x + 5, y - 6, strlen(text) * 4 + 1, 7);
+        }
+    }
+}
+
+void draw_inst_text_one_digit(FlizzerTrackerApp *tracker, Canvas *canvas, uint8_t focus, uint8_t param, const char *text, uint8_t x, uint8_t y, uint8_t value) // text MUST end with semicolon
+{
+    canvas_draw_str(canvas, x, y, text);
+    char buffer[4];
+    snprintf(buffer, sizeof(buffer), "%01X", value);
+    canvas_draw_str(canvas, x + strlen(text) * 4 - 2, y, buffer);
+
+    if (tracker->focus == focus && tracker->selected_param == param && tracker->editing)
+    {
+        canvas_draw_box(canvas, x + strlen(text) * 4 - 3, y - 6, 5, 7);
+    }
+}
+
+void draw_inst_text_two_digits(FlizzerTrackerApp *tracker, Canvas *canvas, uint8_t focus, uint8_t param, const char *text, uint8_t x, uint8_t y, uint8_t value) // text MUST end with semicolon
+{
+    canvas_draw_str(canvas, x, y, text);
+    char buffer[4];
+    snprintf(buffer, sizeof(buffer), "%02X", value);
+    canvas_draw_str(canvas, x + strlen(text) * 4 - 2, y, buffer);
+
+    if (tracker->focus == focus && tracker->selected_param == param && tracker->editing)
+    {
+        canvas_draw_box(canvas, x + strlen(text) * 4 + 4 * tracker->current_digit - 3, y - 6, 5, 7);
+    }
+}
+
+static const char *filter_types[] =
+    {
+        "NONE",
+        "LOW",
+        "HIGH",
+        "BAND",
+};
 
 void draw_instrument_view(Canvas *canvas, FlizzerTrackerApp *tracker)
 {
-    canvas_draw_line(canvas, 0, 0, 10, 10);
-    UNUSED(canvas);
-    UNUSED(tracker);
+    SoundEngineChannel *se_channel = &tracker->sound_engine.channel[0];
+    if (!(se_channel->flags & SE_ENABLE_GATE))
+    {
+        stop();
+        tracker->tracker_engine.playing = false;
+        tracker_engine_set_song(&tracker->tracker_engine, &tracker->song);
+    }
+
+    char buffer[20];
+    Instrument *inst = tracker->song.instrument[tracker->current_instrument];
+    uint8_t shift = tracker->inst_editor_shift;
+
+    if (shift < 6)
+    {
+        snprintf(buffer, sizeof(buffer), "INST:%c", to_char(tracker->current_instrument));
+        draw_generic_n_digit_field(tracker, canvas, EDIT_INSTRUMENT, INST_CURRENTINSTRUMENT, buffer, 0, 5 - shift, 1);
+        snprintf(buffer, sizeof(buffer), "%s", tracker->song.instrument[tracker->current_instrument]->name);
+        draw_generic_n_digit_field(tracker, canvas, EDIT_INSTRUMENT, INST_INSTRUMENTNAME, buffer, 4 * 7, 5 - shift, 1);
+    }
+
+    snprintf(buffer, sizeof(buffer), "NOTE:%s", notename(inst->base_note));
+    canvas_draw_str(canvas, 0, 11, buffer);
+
+    if (tracker->editing && tracker->focus == EDIT_INSTRUMENT && tracker->selected_param == INST_CURRENT_NOTE)
+    {
+        if (tracker->current_digit)
+        {
+            canvas_draw_box(canvas, 19 + 2 * 4, 5 - shift, 5, 7);
+        }
+
+        else
+        {
+            canvas_draw_box(canvas, 19, 5 - shift, 5 + 4, 7);
+        }
+    }
+
+    snprintf(buffer, sizeof(buffer), "FINE:%+02d", inst->finetune);
+    canvas_draw_str(canvas, 37, 11, buffer);
+
+    if (tracker->editing && tracker->focus == EDIT_INSTRUMENT && tracker->selected_param == INST_FINETUNE)
+    {
+        if (tracker->current_digit)
+        {
+            canvas_draw_box(canvas, 60 + 4, 5 - shift, 5, 7);
+        }
+
+        else
+        {
+            canvas_draw_box(canvas, 60, 5 - shift, 5, 7);
+        }
+    }
+
+    draw_inst_text_two_digits(tracker, canvas, EDIT_INSTRUMENT, INST_SLIDESPEED, "SL.SPD:", 0, 17 - shift, inst->slide_speed);
+
+    draw_inst_flag(tracker, canvas, EDIT_INSTRUMENT, INST_SETPW, "PW:", 36, 17 - shift, inst->flags, TE_SET_PW);
+    draw_inst_text_two_digits(tracker, canvas, EDIT_INSTRUMENT, INST_PW, "", 54, 17 - shift, inst->pw);
+    draw_inst_flag(tracker, canvas, EDIT_INSTRUMENT, INST_SETCUTOFF, "CUT", 62, 17 - shift, inst->flags, TE_SET_CUTOFF);
+
+    draw_inst_flag(tracker, canvas, EDIT_INSTRUMENT, INST_WAVE_NOISE, "N", 0, 23 - shift, inst->waveform, SE_WAVEFORM_NOISE);
+    draw_inst_flag(tracker, canvas, EDIT_INSTRUMENT, INST_WAVE_PULSE, "P", 10, 23 - shift, inst->waveform, SE_WAVEFORM_PULSE);
+    draw_inst_flag(tracker, canvas, EDIT_INSTRUMENT, INST_WAVE_TRIANGLE, "T", 20, 23 - shift, inst->waveform, SE_WAVEFORM_TRIANGLE);
+    draw_inst_flag(tracker, canvas, EDIT_INSTRUMENT, INST_WAVE_SAWTOOTH, "S", 30, 23 - shift, inst->waveform, SE_WAVEFORM_SAW);
+    draw_inst_flag(tracker, canvas, EDIT_INSTRUMENT, INST_WAVE_NOISE_METAL, "M", 40, 23 - shift, inst->waveform, SE_WAVEFORM_NOISE_METAL);
+    draw_inst_flag(tracker, canvas, EDIT_INSTRUMENT, INST_WAVE_SINE, "SINE", 50, 23 - shift, inst->waveform, SE_WAVEFORM_SINE);
+
+    draw_inst_text_two_digits(tracker, canvas, EDIT_INSTRUMENT, INST_ATTACK, "A:", 0, 29 - shift, inst->adsr.a);
+    draw_inst_text_two_digits(tracker, canvas, EDIT_INSTRUMENT, INST_DECAY, "D:", 16, 29 - shift, inst->adsr.d);
+    draw_inst_text_two_digits(tracker, canvas, EDIT_INSTRUMENT, INST_SUSTAIN, "S:", 32, 29 - shift, inst->adsr.s);
+    draw_inst_text_two_digits(tracker, canvas, EDIT_INSTRUMENT, INST_RELEASE, "R:", 48, 29 - shift, inst->adsr.r);
+    draw_inst_text_two_digits(tracker, canvas, EDIT_INSTRUMENT, INST_VOLUME, "V:", 64, 29 - shift, inst->adsr.volume);
+
+    draw_inst_flag(tracker, canvas, EDIT_INSTRUMENT, INST_ENABLEFILTER, "FIL", 0, 35 - shift, inst->sound_engine_flags, SE_ENABLE_FILTER);
+    draw_inst_text_two_digits(tracker, canvas, EDIT_INSTRUMENT, INST_FILTERCUTOFF, "CUT:", 20, 35 - shift, inst->filter_cutoff);
+    draw_inst_text_two_digits(tracker, canvas, EDIT_INSTRUMENT, INST_FILTERRESONANCE, "RES:", 44, 35 - shift, inst->filter_resonance);
+
+    snprintf(buffer, sizeof(buffer), "TYPE:%s", filter_types[inst->filter_type]);
+    canvas_draw_str(canvas, 0, 41 - shift, buffer);
+
+    if (tracker->editing && tracker->focus == EDIT_INSTRUMENT && tracker->selected_param == INST_FILTERTYPE)
+    {
+        canvas_draw_box(canvas, 19, 35 - shift, strlen(filter_types[inst->filter_type]) * 4 + 1, 7);
+    }
 }
 
 void draw_instrument_program_view(Canvas *canvas, FlizzerTrackerApp *tracker)

+ 29 - 3
view/pattern_editor.c

@@ -44,7 +44,7 @@ char *notename(uint8_t note)
     return buffer;
 }
 
-static const char to_char_array[] =
+const char to_char_array[] =
     {
         '0',
         '1',
@@ -250,9 +250,35 @@ void draw_generic_n_digit_field(FlizzerTrackerApp *tracker, Canvas *canvas, uint
 
     if (tracker->focus == focus && tracker->selected_param == param && tracker->editing)
     {
-        if (param != SI_SONGNAME && param != SI_INSTRUMENTNAME)
+        bool select_string = true;
+
+        if (tracker->focus == EDIT_SONGINFO)
+        {
+            if (param != SI_SONGNAME && param != SI_INSTRUMENTNAME)
+            {
+                select_string = false;
+            }
+        }
+
+        if (tracker->focus == EDIT_INSTRUMENT)
+        {
+            if (param != INST_INSTRUMENTNAME)
+            {
+                select_string = false;
+            }
+        }
+
+        if (!(select_string))
         {
-            canvas_draw_box(canvas, x + strlen(text) * 4 - digits * 4 + tracker->current_digit * 4 - 1, y - 6, 5, 7);
+            if (tracker->focus == EDIT_INSTRUMENT && param == INST_CURRENTINSTRUMENT)
+            {
+                canvas_draw_box(canvas, x + strlen(text) * 4 - digits * 4 - 1, y - 6, 5, 7);
+            }
+
+            else
+            {
+                canvas_draw_box(canvas, x + strlen(text) * 4 - digits * 4 + tracker->current_digit * 4 - 1, y - 6, 5, 7);
+            }
         }
 
         else

+ 5 - 1
view/pattern_editor.h

@@ -8,4 +8,8 @@
 
 void draw_pattern_view(Canvas *canvas, FlizzerTrackerApp *tracker);
 void draw_sequence_view(Canvas *canvas, FlizzerTrackerApp *tracker);
-void draw_songinfo_view(Canvas *canvas, FlizzerTrackerApp *tracker);
+void draw_songinfo_view(Canvas *canvas, FlizzerTrackerApp *tracker);
+
+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);
+char to_char(uint8_t number);
+char *notename(uint8_t note);