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

settings submenu, saving settings in config

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

+ 1 - 1
application.fam

@@ -4,7 +4,7 @@ App(
     apptype=FlipperAppType.EXTERNAL,
     entry_point="flizzer_tracker_app",
     cdefines=["APP_FLIZZER_TRACKER"],
-    stack_size=1024,
+    stack_size=2 * 1024,
     order=90,
 	fap_icon="flizzer_tracker.png",
 	fap_icon_assets="images",

+ 8 - 0
audio_modes.c

@@ -0,0 +1,8 @@
+#include <stdint.h>
+#include <stdbool.h>
+
+char* audio_modes_text[2] = {
+    "Internal",
+    "External",
+};
+bool audio_modes_values[2] = {false, true};

+ 57 - 1
diskop.c

@@ -1,5 +1,7 @@
 #include "diskop.h"
 
+#define CFG_FILENAME "settings.cfg"
+
 void save_instrument_inner(Stream *stream, Instrument *inst)
 {
     size_t rwops = stream_write(stream, (uint8_t *)inst->name, sizeof(inst->name));
@@ -103,7 +105,7 @@ bool save_song(FlizzerTrackerApp *tracker, FuriString *filepath)
 
     for (uint16_t i = 0; i < song->num_patterns; i++)
     {
-        rwops = stream_write(tracker->stream, (uint8_t *)song->pattern[i].step, sizeof(TrackerSongPatternStep) * (song->pattern_length - 1));
+        rwops = stream_write(tracker->stream, (uint8_t *)song->pattern[i].step, sizeof(TrackerSongPatternStep) * (song->pattern_length));
     }
 
     rwops = stream_write(tracker->stream, (uint8_t *)&song->num_instruments, sizeof(song->num_instruments));
@@ -134,4 +136,58 @@ bool load_song_util(FlizzerTrackerApp *tracker, FuriString *filepath)
     furi_string_free(filepath);
     UNUSED(open_file);
     return result;
+}
+
+void save_config(FlizzerTrackerApp* tracker)
+{
+    //stream_read_line
+    FuriString* filepath = furi_string_alloc();
+    FuriString* config_line = furi_string_alloc();
+    furi_string_cat_printf(filepath, "%s/%s", FLIZZER_TRACKER_FOLDER, CFG_FILENAME);
+
+    bool open_file = file_stream_open(tracker->stream, furi_string_get_cstr(filepath), FSAM_WRITE, FSOM_OPEN_ALWAYS);
+    UNUSED(open_file);
+
+    furi_string_cat_printf(config_line, "%s = %s\n", "external_audio", tracker->external_audio ? "true" : "false");
+    stream_write_string(tracker->stream, config_line);
+
+    file_stream_close(tracker->stream);
+    furi_string_free(filepath);
+    furi_string_free(config_line);
+}
+
+void load_config(FlizzerTrackerApp* tracker)
+{
+    FuriString* filepath = furi_string_alloc();
+    FuriString* config_line = furi_string_alloc();
+    furi_string_cat_printf(filepath, "%s/%s", FLIZZER_TRACKER_FOLDER, CFG_FILENAME);
+
+    bool open_file = file_stream_open(tracker->stream, furi_string_get_cstr(filepath), FSAM_READ, FSOM_OPEN_ALWAYS);
+    UNUSED(open_file);
+
+    stream_read_line(tracker->stream, config_line);
+
+    sscanf(furi_string_get_cstr(config_line), "%s%s%s", tracker->param, tracker->eq, tracker->value);
+
+    if(strcmp(tracker->param, "external_audio") == 0)
+    {
+        if(strcmp(tracker->value, "false") == 0)
+        {
+            tracker->external_audio = false;
+            //strcpy(tracker->value, "false_");
+        }
+
+        if(strcmp(tracker->value, "true") == 0)
+        {
+            tracker->external_audio = true;
+            //strcpy(tracker->value, "true_");
+        }
+
+        sound_engine_init(&tracker->sound_engine, tracker->sound_engine.sample_rate, tracker->external_audio, tracker->sound_engine.audio_buffer_size);
+        //sound_engine_set_audio_output(tracker->external_audio);
+    }
+
+    file_stream_close(tracker->stream);
+    furi_string_free(filepath);
+    furi_string_free(config_line);
 }

+ 4 - 1
diskop.h

@@ -7,4 +7,7 @@
 
 bool save_song(FlizzerTrackerApp *tracker, FuriString *filepath);
 
-bool load_song_util(FlizzerTrackerApp *tracker, FuriString *filepath);
+bool load_song_util(FlizzerTrackerApp *tracker, FuriString *filepath);
+
+void save_config(FlizzerTrackerApp* tracker);
+void load_config(FlizzerTrackerApp* tracker);

+ 11 - 1
flizzer_tracker.c

@@ -126,7 +126,7 @@ int32_t flizzer_tracker_app(void *p)
     UNUSED(st);
     furi_record_close(RECORD_STORAGE);
 
-    FlizzerTrackerApp *tracker = init_tracker(44100, 50, false, 1024);
+    FlizzerTrackerApp *tracker = init_tracker(44100, 50, true, 1024);
 
     // Текущее событие типа кастомного типа FlizzerTrackerEvent
     FlizzerTrackerEvent event;
@@ -260,10 +260,20 @@ int32_t flizzer_tracker_app(void *p)
                 furi_string_free(path);
             }
         }
+
+        if(event.type == EventTypeSetAudioMode)
+        {
+            sound_engine_set_audio_output(tracker->external_audio);
+            sound_engine_PWM_timer_init(tracker->external_audio);
+
+            tracker->sound_engine.external_audio_output = tracker->external_audio;
+        }
     }
 
     stop();
 
+    save_config(tracker);
+
     deinit_tracker(tracker);
 
     return 0;

+ 11 - 2
flizzer_tracker.h

@@ -13,6 +13,7 @@
 #include <toolbox/stream/file_stream.h>
 
 #include <gui/modules/text_input.h>
+#include <gui/modules/variable_item_list.h>
 #include <gui/view_dispatcher.h>
 
 #include "flizzer_tracker_hal.h"
@@ -29,6 +30,7 @@ typedef enum
     EventTypeInput,
     EventTypeSaveSong,
     EventTypeLoadSong,
+    EventTypeSetAudioMode,
 } EventType;
 
 typedef struct
@@ -137,19 +139,19 @@ typedef enum
     VIEW_SUBMENU_PATTERN,
     VIEW_SUBMENU_INSTRUMENT,
     VIEW_FILE_OVERWRITE,
+    VIEW_SETTINGS,
 } FlizzerTrackerViews;
 
 typedef enum
 {
-    SUBMENU_PATTERN_RETURN,
     SUBMENU_PATTERN_LOAD_SONG,
     SUBMENU_PATTERN_SAVE_SONG,
+    SUBMENU_PATTERN_SETTINGS,
     SUBMENU_PATTERN_EXIT,
 } PatternSubmenuParams;
 
 typedef enum
 {
-    SUBMENU_INSTRUMENT_RETURN,
     SUBMENU_INSTRUMENT_EXIT,
 } InstrumentSubmenuParams;
 
@@ -167,12 +169,15 @@ typedef struct
     DialogsApp *dialogs;
     Submenu *pattern_submenu;
     Submenu *instrument_submenu;
+    VariableItemList* settings_list;
     Widget *overwrite_file_widget;
     char filename[FILE_NAME_LEN + 1];
     bool was_it_back_keypress;
     uint32_t current_time;
     uint32_t period;
 
+    bool external_audio;
+
     SoundEngine sound_engine;
     TrackerEngine tracker_engine;
 
@@ -192,6 +197,10 @@ typedef struct
     bool is_saving;
 
     bool quit;
+
+    char eq[2];
+    char param[80];
+    char value[10];
 } FlizzerTrackerApp;
 
 typedef struct

+ 38 - 2
flizzer_tracker_hal.c

@@ -40,6 +40,9 @@ void tracker_engine_timer_isr(void *ctx)
 
 void sound_engine_PWM_timer_init(bool external_audio_output) // external audio on pin PA6
 {
+    LL_TIM_DisableAllOutputs(SPEAKER_PWM_TIMER);
+    LL_TIM_DisableCounter(SPEAKER_PWM_TIMER);
+
     LL_TIM_InitTypeDef TIM_InitStruct = {0};
     LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
 
@@ -56,12 +59,21 @@ void sound_engine_PWM_timer_init(bool external_audio_output) // external audio o
     if (external_audio_output)
     {
         furi_hal_gpio_init_ex(&gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16);
+
+        if(furi_hal_speaker_is_mine())
+        {
+            furi_hal_speaker_release();
+        }
     }
 
     else
     {
-        bool unu = furi_hal_speaker_acquire(1000);
-        UNUSED(unu);
+        if(!(furi_hal_speaker_is_mine()))
+        {
+            bool unu = furi_hal_speaker_acquire(1000);
+            UNUSED(unu);
+        }
+        
         furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
     }
 
@@ -69,6 +81,30 @@ void sound_engine_PWM_timer_init(bool external_audio_output) // external audio o
     LL_TIM_EnableCounter(SPEAKER_PWM_TIMER);
 }
 
+void sound_engine_set_audio_output(bool external_audio_output)
+{
+    if (external_audio_output)
+    {
+        furi_hal_gpio_init_ex(&gpio_ext_pa6, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn14TIM16);
+
+        if(furi_hal_speaker_is_mine())
+        {
+            furi_hal_speaker_release();
+        }
+    }
+
+    else
+    {
+        if(!(furi_hal_speaker_is_mine()))
+        {
+            bool unu = furi_hal_speaker_acquire(1000);
+            UNUSED(unu);
+        }
+
+        furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    }
+}
+
 void sound_engine_timer_init(uint32_t sample_rate) // external audio on pin PA6
 {
     LL_TIM_InitTypeDef TIM_InitStruct = {0};

+ 2 - 0
flizzer_tracker_hal.h

@@ -24,6 +24,8 @@
 void sound_engine_dma_isr(void *ctx);
 void tracker_engine_timer_isr(void *ctx);
 void sound_engine_init_hardware(uint32_t sample_rate, bool external_audio_output, uint16_t *audio_buffer, uint32_t audio_buffer_size);
+void sound_engine_PWM_timer_init(bool external_audio_output);
+void sound_engine_set_audio_output(bool external_audio_output);
 void tracker_engine_init_hardware(uint8_t rate);
 void tracker_engine_set_rate(uint8_t rate);
 void sound_engine_start();

+ 45 - 2
init_deinit.c

@@ -1,6 +1,10 @@
 #include "init_deinit.h"
 #include "input_event.h"
 
+#include "diskop.h"
+
+#define AUDIO_MODES_COUNT 2
+
 TrackerView *tracker_view_alloc(FlizzerTrackerApp *tracker)
 {
     TrackerView *tracker_view = malloc(sizeof(TrackerView));
@@ -21,11 +25,29 @@ void tracker_view_free(TrackerView *tracker_view)
     free(tracker_view);
 }
 
+uint8_t my_value_index_bool(const bool value, const bool values[], uint8_t values_count) //why the fuck it gives unresolved symbol if I include it from toolbox???!!!
+{
+    uint8_t index = 0;
+
+    for(uint8_t i = 0; i < values_count; i++)
+    {
+        if(value == values[i])
+        {
+            index = i;
+            break;
+        }
+    }
+
+    return index;
+}
+
 FlizzerTrackerApp *init_tracker(uint32_t sample_rate, uint8_t rate, bool external_audio_output, uint32_t audio_buffer_size)
 {
     FlizzerTrackerApp *tracker = malloc(sizeof(FlizzerTrackerApp));
     memset(tracker, 0, sizeof(FlizzerTrackerApp));
 
+    tracker->external_audio = external_audio_output;
+
     sound_engine_init(&tracker->sound_engine, sample_rate, external_audio_output, audio_buffer_size);
     tracker_engine_init(&tracker->tracker_engine, rate, &tracker->sound_engine);
 
@@ -56,17 +78,35 @@ FlizzerTrackerApp *init_tracker(uint32_t sample_rate, uint8_t rate, bool externa
     tracker->pattern_submenu = submenu_alloc();
     tracker->instrument_submenu = submenu_alloc();
 
-    submenu_add_item(tracker->pattern_submenu, "Return", SUBMENU_PATTERN_RETURN, submenu_callback, tracker);
+    view_set_previous_callback(submenu_get_view(tracker->pattern_submenu), submenu_exit_callback);
+    view_set_previous_callback(submenu_get_view(tracker->instrument_submenu), submenu_exit_callback);
+
     submenu_add_item(tracker->pattern_submenu, "Load song", SUBMENU_PATTERN_LOAD_SONG, submenu_callback, tracker);
     submenu_add_item(tracker->pattern_submenu, "Save song", SUBMENU_PATTERN_SAVE_SONG, submenu_callback, tracker);
+    submenu_add_item(tracker->pattern_submenu, "Settings", SUBMENU_PATTERN_SETTINGS, submenu_callback, tracker);
     submenu_add_item(tracker->pattern_submenu, "Exit", SUBMENU_PATTERN_EXIT, submenu_callback, tracker);
 
-    submenu_add_item(tracker->instrument_submenu, "Return", SUBMENU_INSTRUMENT_RETURN, submenu_callback, tracker);
     submenu_add_item(tracker->instrument_submenu, "Exit", SUBMENU_INSTRUMENT_EXIT, submenu_callback, tracker);
 
     view_dispatcher_add_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN, submenu_get_view(tracker->pattern_submenu));
     view_dispatcher_add_view(tracker->view_dispatcher, VIEW_SUBMENU_INSTRUMENT, submenu_get_view(tracker->instrument_submenu));
 
+    load_config(tracker);
+
+    tracker->settings_list = variable_item_list_alloc();
+    View* view = variable_item_list_get_view(tracker->settings_list);
+    view_set_previous_callback(view, submenu_settings_exit_callback);
+
+    VariableItem* item;
+    uint8_t value_index;
+
+    item = variable_item_list_add(tracker->settings_list, "Audio output", AUDIO_MODES_COUNT, audio_output_changed_callback, tracker);
+    value_index = my_value_index_bool(tracker->external_audio, audio_modes_values, AUDIO_MODES_COUNT);
+    variable_item_set_current_value_index(item, value_index);
+    variable_item_set_current_value_text(item, audio_modes_text[value_index]);
+
+    view_dispatcher_add_view(tracker->view_dispatcher, VIEW_SETTINGS, view);
+
     tracker->overwrite_file_widget = widget_alloc();
 
     widget_add_button_element(
@@ -100,6 +140,7 @@ void deinit_tracker(FlizzerTrackerApp *tracker)
     // Специальная очистка памяти, занимаемой очередью
     furi_message_queue_free(tracker->event_queue);
 
+    view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SETTINGS);
     view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_FILE_OVERWRITE);
     view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_INSTRUMENT);
     view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN);
@@ -108,6 +149,8 @@ void deinit_tracker(FlizzerTrackerApp *tracker)
 
     text_input_free(tracker->text_input);
 
+    variable_item_list_free(tracker->settings_list);
+
     submenu_free(tracker->pattern_submenu);
     submenu_free(tracker->instrument_submenu);
 

+ 3 - 0
init_deinit.h

@@ -3,5 +3,8 @@
 #include "flizzer_tracker.h"
 #include "flizzer_tracker_hal.h"
 
+extern bool audio_modes_values[];
+extern char* audio_modes_text[];
+
 FlizzerTrackerApp *init_tracker(uint32_t sample_rate, uint8_t rate, bool external_audio_output, uint32_t audio_buffer_size);
 void deinit_tracker(FlizzerTrackerApp *tracker);

+ 2 - 2
input/instrument_program.c

@@ -97,7 +97,7 @@ void instrument_program_edit_event(FlizzerTrackerApp *tracker, FlizzerTrackerEve
                 {
                     int8_t nibble = ((opcode & 0x00f0) >> 4);
 
-                    if (nibble + 1 < 0xf)
+                    if (nibble + 1 <= 0xf)
                     {
                         nibble++;
                     }
@@ -117,7 +117,7 @@ void instrument_program_edit_event(FlizzerTrackerApp *tracker, FlizzerTrackerEve
                 {
                     int8_t nibble = (opcode & 0x000f);
 
-                    if (nibble + 1 < 0xf)
+                    if (nibble + 1 <= 0xf)
                     {
                         nibble++;
                     }

+ 10 - 0
input/songinfo.c

@@ -19,6 +19,11 @@ void edit_songinfo_param(FlizzerTrackerApp *tracker, uint8_t selected_param, int
             {
                 new_length += delta;
                 change_pattern_length(&tracker->song, new_length);
+
+                if(tracker->tracker_engine.pattern_position >= new_length)
+                {
+                    tracker->tracker_engine.pattern_position = new_length - 1;
+                }
             }
 
             break;
@@ -29,6 +34,11 @@ void edit_songinfo_param(FlizzerTrackerApp *tracker, uint8_t selected_param, int
             if ((int16_t)tracker->song.num_sequence_steps + (int16_t)delta > 0 && (int16_t)tracker->song.num_sequence_steps + (int16_t)delta <= 0x100)
             {
                 tracker->song.num_sequence_steps += delta;
+
+                if(tracker->tracker_engine.sequence_position >= tracker->song.num_sequence_steps)
+                {
+                    tracker->tracker_engine.sequence_position = tracker->song.num_sequence_steps - 1;
+                }
             }
 
             break;

+ 44 - 14
input_event.c

@@ -2,6 +2,8 @@
 
 #include "diskop.h"
 
+#define AUDIO_MODES_COUNT 2
+
 void return_from_keyboard_callback(void *ctx)
 {
     FlizzerTrackerApp *tracker = (FlizzerTrackerApp *)ctx;
@@ -106,6 +108,18 @@ void overwrite_file_widget_no_input_callback(GuiButtonType result, InputType typ
     }
 }
 
+uint32_t submenu_settings_exit_callback(void* context)
+{
+    UNUSED(context);
+    return VIEW_SUBMENU_PATTERN;
+}
+
+uint32_t submenu_exit_callback(void* context)
+{
+    UNUSED(context);
+    return VIEW_TRACKER;
+}
+
 void submenu_callback(void *context, uint32_t index)
 {
     FlizzerTrackerApp *tracker = (FlizzerTrackerApp *)context;
@@ -116,12 +130,6 @@ void submenu_callback(void *context, uint32_t index)
         {
             switch (index)
             {
-                case SUBMENU_PATTERN_RETURN:
-                {
-                    view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER);
-                    break;
-                }
-
                 case SUBMENU_PATTERN_EXIT:
                 {
                     tracker->quit = true;
@@ -153,6 +161,12 @@ void submenu_callback(void *context, uint32_t index)
                     break;
                 }
 
+                case SUBMENU_PATTERN_SETTINGS:
+                {
+                    view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SETTINGS);
+                    break;
+                }
+
                 default:
                     break;
             }
@@ -164,12 +178,6 @@ void submenu_callback(void *context, uint32_t index)
         {
             switch (index)
             {
-                case SUBMENU_INSTRUMENT_RETURN:
-                {
-                    view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER);
-                    break;
-                }
-
                 case SUBMENU_INSTRUMENT_EXIT:
                 {
                     tracker->quit = true;
@@ -193,6 +201,28 @@ void submenu_callback(void *context, uint32_t index)
     }
 }
 
+void audio_output_changed_callback(VariableItem* item)
+{
+    FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)variable_item_get_context(item);
+    uint8_t index = variable_item_get_current_value_index(item);
+    variable_item_set_current_value_text(item, audio_modes_text[(index > 1 ? 1 : index)]);
+
+    if(tracker)
+    {
+        tracker->external_audio = (bool)index;
+
+        tracker->external_audio = audio_modes_values[(index > 1 ? 1 : index)];
+
+        //sound_engine_init(&tracker->sound_engine, tracker->sound_engine.sample_rate, tracker->external_audio, tracker->sound_engine.audio_buffer_size);
+        //sound_engine_init_hardware(tracker->sound_engine.sample_rate, tracker->external_audio, tracker->sound_engine.audio_buffer, tracker->sound_engine.audio_buffer_size);
+
+        FlizzerTrackerEvent event = {.type = EventTypeSetAudioMode, .input = {0}, .period = 0};
+        furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever);
+
+        UNUSED(event);
+    }
+}
+
 void cycle_focus(FlizzerTrackerApp *tracker)
 {
     switch (tracker->mode)
@@ -283,14 +313,14 @@ void process_input_event(FlizzerTrackerApp *tracker, FlizzerTrackerEvent *event)
         {
             case PATTERN_VIEW:
             {
-                submenu_set_selected_item(tracker->pattern_submenu, SUBMENU_PATTERN_RETURN);
+                submenu_set_selected_item(tracker->pattern_submenu, SUBMENU_PATTERN_LOAD_SONG);
                 view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN);
                 break;
             }
 
             case INST_EDITOR_VIEW:
             {
-                submenu_set_selected_item(tracker->instrument_submenu, SUBMENU_INSTRUMENT_RETURN);
+                submenu_set_selected_item(tracker->instrument_submenu, SUBMENU_INSTRUMENT_EXIT);
                 view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SUBMENU_INSTRUMENT);
                 break;
             }

+ 6 - 0
input_event.h

@@ -15,10 +15,16 @@
 #include "input/sequence.h"
 #include "input/songinfo.h"
 
+extern bool audio_modes_values[];
+extern char* audio_modes_text[];
+
 void return_from_keyboard_callback(void *ctx);
 
 void overwrite_file_widget_yes_input_callback(GuiButtonType result, InputType type, void *ctx);
 void overwrite_file_widget_no_input_callback(GuiButtonType result, InputType type, void *ctx);
 
+uint32_t submenu_exit_callback(void* context);
+uint32_t submenu_settings_exit_callback(void* context);
 void submenu_callback(void *context, uint32_t index);
+void audio_output_changed_callback(VariableItem* item);
 void process_input_event(FlizzerTrackerApp *tracker, FlizzerTrackerEvent *event);

+ 4 - 1
sound_engine/sound_engine.c

@@ -46,7 +46,10 @@ void sound_engine_deinit(SoundEngine *sound_engine)
 
     if (!(sound_engine->external_audio_output))
     {
-        furi_hal_speaker_release();
+        if(furi_hal_speaker_is_mine())
+        {
+            furi_hal_speaker_release();
+        }
     }
 
     else

+ 2 - 2
tracker_engine/diskop.c

@@ -92,9 +92,9 @@ bool load_song_inner(TrackerSong *song, Stream *stream)
 
     for (uint16_t i = 0; i < song->num_patterns; i++)
     {
-        song->pattern[i].step = (TrackerSongPatternStep *)malloc(sizeof(TrackerSongPatternStep) * (song->pattern_length - 1));
+        song->pattern[i].step = (TrackerSongPatternStep *)malloc(sizeof(TrackerSongPatternStep) * (song->pattern_length));
         set_empty_pattern(&song->pattern[i], song->pattern_length);
-        rwops = stream_read(stream, (uint8_t *)song->pattern[i].step, sizeof(TrackerSongPatternStep) * (song->pattern_length - 1));
+        rwops = stream_read(stream, (uint8_t *)song->pattern[i].step, sizeof(TrackerSongPatternStep) * (song->pattern_length));
     }
 
     rwops = stream_read(stream, (uint8_t *)&song->num_instruments, sizeof(song->num_instruments));

+ 10 - 13
tracker_engine/do_effects.c

@@ -149,20 +149,17 @@ void do_command(uint16_t opcode, TrackerEngine *tracker_engine, uint8_t channel,
 
         case TE_EFFECT_VOLUME_FADE:
         {
-            if (tick == 0)
+            if (!(te_channel->channel_flags & TEC_DISABLED))
             {
-                if (!(te_channel->channel_flags & TEC_DISABLED))
-                {
-                    te_channel->volume -= opcode & 0xf;
-                    if (te_channel->volume > MAX_ADSR_VOLUME)
-                        te_channel->volume = 0;
-                    te_channel->volume += (opcode >> 4) & 0xf;
-                    if (te_channel->volume > MAX_ADSR_VOLUME)
-                        te_channel->volume = MAX_ADSR_VOLUME;
-
-                    se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * (int32_t)te_channel->volume / MAX_ADSR_VOLUME * (int32_t)te_channel->instrument->adsr.volume / MAX_ADSR_VOLUME;
-                    se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME;
-                }
+                te_channel->volume -= opcode & 0xf;
+                if (te_channel->volume > MAX_ADSR_VOLUME)
+                    te_channel->volume = 0;
+                te_channel->volume += (opcode >> 4) & 0xf;
+                if (te_channel->volume > MAX_ADSR_VOLUME)
+                    te_channel->volume = MAX_ADSR_VOLUME;
+
+                se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * (int32_t)te_channel->volume / MAX_ADSR_VOLUME * (int32_t)te_channel->instrument->adsr.volume / MAX_ADSR_VOLUME;
+                se_channel->adsr.volume = (int32_t)se_channel->adsr.volume * (int32_t)tracker_engine->master_volume / MAX_ADSR_VOLUME;
             }
 
             break;