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

lowpass filter, tidying the code

LTVA1 3 лет назад
Родитель
Сommit
d184dc9213
9 измененных файлов с 478 добавлено и 153 удалено
  1. 237 138
      flizzer_tracker.c
  2. 32 0
      flizzer_tracker.h
  3. 26 0
      flizzer_tracker_hal.c
  4. 1 0
      flizzer_tracker_hal.h
  5. 17 0
      init_deinit.c
  6. 7 0
      init_deinit.h
  7. 9 9
      sound_engine/freqs.c
  8. 111 5
      sound_engine/sound_engine.c
  9. 38 1
      sound_engine/sound_engine.h

+ 237 - 138
flizzer_tracker.c

@@ -1,13 +1,5 @@
-#include <stdio.h>
-#include <furi.h>
-#include <gui/gui.h>
-#include <notification/notification_messages.h>
-#include <input/input.h>
-#include <furi_hal.h>
-#include <u8g2_glue.h>
-#include <stm32wbxx_ll_tim.h>
-
-#include "flizzer_tracker_hal.h"
+#include "flizzer_tracker.h"
+#include "init_deinit.h"
 
 /*
 Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1
@@ -40,93 +32,49 @@ const uint8_t u8g2_font_tom_thumb_4x6_tr[725] U8G2_FONT_SECTION("u8g2_font_tom_t
 	"y\11\227\307$\225dJ\0z\7\223\310\254\221\6{\10\227\310\251\32D\1|\6\265\310(\1}\11"
 	"\227\310\310\14RR\0~\6\213\313\215\4\0\0\0\4\377\377\0";
 
-typedef enum {
-	EventTypeTick,
+typedef enum
+{
 	EventTypeInput,
 } EventType;
 
-typedef struct {
+typedef struct
+{
 	EventType type;
 	InputEvent input;
 } FlizzerTrackerEvent;
 
-typedef struct 
-{
-	bool stop;
-	uint32_t counter;
-	uint32_t counter_2;
-	NotificationApp* notification;
-
-	SoundEngine sound_engine;
-
-	uint32_t frequency;
-	uint8_t current_waveform_index;
-	uint16_t pw;
-} FlizzerTrackerApp;
-
-#define FURI_HAL_SPEAKER_TIMER TIM2
-#define FURI_HAL_SPEAKER_CHANNEL LL_TIM_CHANNEL_CH1
-
-void timer_draw_callback(void* ctx)
-{
-	if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) 
-	{
-		LL_TIM_ClearFlag_UPDATE(TIM2);
-	}
-
-	FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx;
-
-	tracker->counter++;
-	//return;
-}
-
-void timer_draw_callback_2(void* ctx) 
+typedef enum
 {
-	if(LL_TIM_IsActiveFlag_UPDATE(TIM1)) 
-	{
-		LL_TIM_ClearFlag_UPDATE(TIM1);
-	}
-
-	FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx;
-
-	tracker->counter_2++;
-	//return;
-}
-
-static void sound_engine_dma_isr(void* ctx)
-{
-    FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx;
-
-    // half of transfer
-    if(LL_DMA_IsActiveFlag_HT1(DMA1))
-	{
-        LL_DMA_ClearFlag_HT1(DMA1);
-        // fill first half of buffer
-        sound_engine_fill_buffer(&tracker->sound_engine, tracker->sound_engine.audio_buffer, tracker->sound_engine.audio_buffer_size / 2);
-
-		tracker->counter++;
-    }
-
-    // transfer complete
-    if(LL_DMA_IsActiveFlag_TC1(DMA1))
-	{
-        LL_DMA_ClearFlag_TC1(DMA1);
-        // fill second half of buffer
-        sound_engine_fill_buffer(&tracker->sound_engine, &tracker->sound_engine.audio_buffer[tracker->sound_engine.audio_buffer_size / 2], tracker->sound_engine.audio_buffer_size / 2); //&app->sample_buffer[index]
-
-		tracker->counter++;
-    }
-}
-
-const char* wave_names[5] = 
+	PARAM_FREQUENCY,
+	PARAM_WAVEFORM,
+	PARAM_PW,
+	PARAM_ENABLE_FILTER,
+	PARAM_FILTER_CUTOFF,
+	PARAM_FILTER_RESONANCE,
+	PARAM_FILTER_TYPE,
+} SelectedParam;
+
+const char* wave_names[] = 
 {
 	"NONE",
 	"NOISE",
 	"PULSE",
 	"TRIANGLE",
 	"SAWTOOTH",
+	"METAL NOISE",
+	"SINE",
+};
+
+const char* filter_names[] = 
+{
+	"NONE",
+	"LOWPASS",
+	"HIGHPASS",
+	"BANDPASS",
 };
 
+#define NUM_PARAMS 10
+
 static void draw_callback(Canvas* canvas, void* ctx) 
 {
 	FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx;
@@ -139,15 +87,38 @@ static void draw_callback(Canvas* canvas, void* ctx)
 
 	snprintf(buffer, 20, "FREQUENCY: %ld Hz", tracker->frequency);
 	
-	canvas_draw_str(canvas, 0, 10, buffer);
+	canvas_draw_str(canvas, 0, 6, buffer);
 
 	snprintf(buffer, 20, "WAVEFORM: %s", wave_names[tracker->current_waveform_index]);
 	
-	canvas_draw_str(canvas, 0, 20, buffer);
+	canvas_draw_str(canvas, 0, 12, buffer);
 
 	snprintf(buffer, 20, "PULSE WIDTH: $%03X", tracker->pw);
 	
+	canvas_draw_str(canvas, 0, 18, buffer);
+
+	snprintf(buffer, 20, "FILTER: %s", (tracker->flags & SE_ENABLE_FILTER) ? "ENABLED" : "DISABLED");
+
+	canvas_draw_str(canvas, 0, 24, buffer);
+
+	snprintf(buffer, 20, "CUTOFF: %d", tracker->cutoff);
+	
 	canvas_draw_str(canvas, 0, 30, buffer);
+
+	snprintf(buffer, 20, "RESONANCE: %d", tracker->resonance);
+	
+	canvas_draw_str(canvas, 0, 36, buffer);
+
+	snprintf(buffer, 20, "TYPE: %s", filter_names[tracker->filter_type]);
+	
+	canvas_draw_str(canvas, 0, 42, buffer);
+
+	canvas_draw_str(canvas, 70, tracker->selected_param * 6 + 6, "<");
+
+    uint32_t bytes = memmgr_get_free_heap();
+	snprintf(buffer, 20, "BYTES FREE: %ld", bytes);
+
+	canvas_draw_str(canvas, 0, 64, buffer);
 }
 
 static void input_callback(InputEvent* input_event, void* ctx) 
@@ -160,13 +131,15 @@ static void input_callback(InputEvent* input_event, void* ctx)
 	furi_message_queue_put(event_queue, &event, FuriWaitForever);
 }
 
-const uint8_t waveforms[5] = 
+const uint8_t waveforms[] = 
 {
 	SE_WAVEFORM_NONE,
 	SE_WAVEFORM_NOISE,
 	SE_WAVEFORM_PULSE,
 	SE_WAVEFORM_TRIANGLE,
 	SE_WAVEFORM_SAW,
+	SE_WAVEFORM_NOISE_METAL,
+	SE_WAVEFORM_SINE,
 };
 
 int32_t flizzer_tracker_app(void* p) 
@@ -180,8 +153,6 @@ int32_t flizzer_tracker_app(void* p)
 
 	FlizzerTrackerApp* tracker = malloc(sizeof(FlizzerTrackerApp));
 
-	//direct_draw_run(tracker);
-
 	// Создаем новый view port
 	ViewPort* view_port = view_port_alloc();
 	// Создаем callback отрисовки, без контекста
@@ -196,15 +167,11 @@ int32_t flizzer_tracker_app(void* p)
 	gui_add_view_port(gui, view_port, GuiLayerFullscreen);
 
 	tracker->notification = furi_record_open(RECORD_NOTIFICATION);
-    notification_message(tracker->notification, &sequence_display_backlight_enforce_on);
+	notification_message(tracker->notification, &sequence_display_backlight_enforce_on);
 
-	furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdDma1Ch1, 13, sound_engine_dma_isr, tracker);
-
-	//sound_engine_init(&tracker->sound_engine, 44100, false, 4096);
-	sound_engine_init(&tracker->sound_engine, 44100, true, 4096);
+	init_tracker(tracker, 44100, true, 1024);
 
 	tracker->sound_engine.channel[0].waveform = SE_WAVEFORM_NOISE;
-	tracker->sound_engine.channel[0].pw = 0x200;
 
 	tracker->frequency = 440;
 	tracker->current_waveform_index = 1;
@@ -231,76 +198,208 @@ int32_t flizzer_tracker_app(void* p)
 
 			if(event.input.key == InputKeyUp && event.input.type == InputTypeShort) 
 			{
-				tracker->frequency += 50;
-				sound_engine_set_channel_frequency(&tracker->sound_engine, &tracker->sound_engine.channel[0], tracker->frequency * 1024);
-				//break;
+				if(tracker->selected_param > 0)
+				{
+					tracker->selected_param--;
+				}
 			}
 
 			if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) 
 			{
-				if(tracker->frequency > 50)
+				if(tracker->selected_param < NUM_PARAMS - 1)
 				{
-					tracker->frequency -= 50;
+					tracker->selected_param++;
 				}
-				sound_engine_set_channel_frequency(&tracker->sound_engine, &tracker->sound_engine.channel[0], tracker->frequency * 1024);
-				//break;
 			}
 
 			if(event.input.key == InputKeyRight && event.input.type == InputTypeShort) 
 			{
-				if(tracker->current_waveform_index < 4)
+				switch(tracker->selected_param)
 				{
-					tracker->current_waveform_index++;
+					case PARAM_FREQUENCY:
+					{
+						tracker->frequency += 25;
+
+						sound_engine_set_channel_frequency(&tracker->sound_engine, &tracker->sound_engine.channel[0], tracker->frequency * 1024);
+
+						break;
+					}
+
+					case PARAM_WAVEFORM:
+					{
+						if(tracker->current_waveform_index < 6)
+						{
+							tracker->current_waveform_index++;
+						}
+						
+						tracker->sound_engine.channel[0].waveform = waveforms[tracker->current_waveform_index];
+
+						break;
+					}
+
+					case PARAM_PW:
+					{
+						if(tracker->pw + 0x80 < 0xFFF)
+						{
+							tracker->pw += 0x80;
+						}
+
+						else
+						{
+							tracker->pw = 0x80;
+						}
+
+						tracker->sound_engine.channel[0].pw = tracker->pw;
+
+						break;
+					}
+
+					case PARAM_ENABLE_FILTER:
+					{
+						tracker->flags ^= SE_ENABLE_FILTER;
+
+						tracker->sound_engine.channel[0].filter_mode = FIL_OUTPUT_LOWPASS;
+						
+						tracker->sound_engine.channel[0].flags = tracker->flags;
+
+						break;
+					}
+
+					case PARAM_FILTER_CUTOFF:
+					{
+						tracker->cutoff += 10;
+
+						if(tracker->cutoff > 0xFFF)
+						{
+							tracker->cutoff = 0xFFF;
+						}
+						
+						tracker->sound_engine.channel[0].filter_cutoff = tracker->cutoff;
+
+						sound_engine_filter_set_coeff(&tracker->sound_engine.channel[0].filter, tracker->cutoff, tracker->resonance);
+
+						break;
+					}
+
+					case PARAM_FILTER_RESONANCE:
+					{
+						tracker->resonance++;
+						sound_engine_filter_set_coeff(&tracker->sound_engine.channel[0].filter, tracker->cutoff, tracker->resonance * 50);
+						break;
+					}
+
+					case PARAM_FILTER_TYPE:
+					{
+						if(tracker->filter_type < 3)
+						{
+							tracker->filter_type++;
+						}
+
+						tracker->sound_engine.channel[0].filter_mode = tracker->filter_type;
+						break;
+					}
 				}
-				
-				tracker->sound_engine.channel[0].waveform = waveforms[tracker->current_waveform_index];
-				//break;
 			}
 
 			if(event.input.key == InputKeyLeft && event.input.type == InputTypeShort) 
 			{
-				if(tracker->current_waveform_index > 0)
+				switch(tracker->selected_param)
 				{
-					tracker->current_waveform_index--;
+					case PARAM_FREQUENCY:
+					{
+						if(tracker->frequency > 25)
+						{
+							tracker->frequency -= 25;
+						}
+
+						sound_engine_set_channel_frequency(&tracker->sound_engine, &tracker->sound_engine.channel[0], tracker->frequency * 1024);
+
+						break;
+					}
+
+					case PARAM_WAVEFORM:
+					{
+						if(tracker->current_waveform_index > 0)
+						{
+							tracker->current_waveform_index--;
+						}
+						
+						tracker->sound_engine.channel[0].waveform = waveforms[tracker->current_waveform_index];
+
+						break;
+					}
+
+					case PARAM_PW:
+					{
+						if(tracker->pw - 0x80 > 0)
+						{
+							tracker->pw -= 0x80;
+						}
+
+						else
+						{
+							tracker->pw = 0xF80;
+						}
+
+						tracker->sound_engine.channel[0].pw = tracker->pw;
+
+						break;
+					}
+
+					case PARAM_ENABLE_FILTER:
+					{
+						tracker->flags ^= SE_ENABLE_FILTER;
+
+						tracker->sound_engine.channel[0].filter_mode = FIL_OUTPUT_LOWPASS;
+						
+						tracker->sound_engine.channel[0].flags = tracker->flags;
+
+						break;
+					}
+
+					case PARAM_FILTER_CUTOFF:
+					{
+						if(tracker->cutoff > 10)
+						{
+							tracker->cutoff -= 10;
+						}
+						
+						tracker->sound_engine.channel[0].filter_cutoff = tracker->cutoff;
+						sound_engine_filter_set_coeff(&tracker->sound_engine.channel[0].filter, tracker->cutoff, tracker->resonance);
+
+						break;
+					}
+
+					case PARAM_FILTER_RESONANCE:
+					{
+						if(tracker->resonance > 0)
+						{
+							tracker->resonance--;
+							sound_engine_filter_set_coeff(&tracker->sound_engine.channel[0].filter, tracker->cutoff, tracker->resonance * 50);
+						}
+
+						break;
+					}
+
+					case PARAM_FILTER_TYPE:
+					{
+						if(tracker->filter_type > 0)
+						{
+							tracker->filter_type--;
+						}
+
+						tracker->sound_engine.channel[0].filter_mode = tracker->filter_type;
+						break;
+					}
 				}
-				
-				tracker->sound_engine.channel[0].waveform = waveforms[tracker->current_waveform_index];
-				//break;
-			}
-
-			if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) 
-			{
-				if(tracker->pw + 0x100 < 0xFFF)
-				{
-					tracker->pw += 0x100;
-				}
-
-				else
-				{
-					tracker->pw = 0x100;
-				}
-				
-				tracker->sound_engine.channel[0].waveform = waveforms[tracker->current_waveform_index];
-				tracker->sound_engine.channel[0].pw = tracker->pw;
-				//break;
 			}
 		}
 	}
 
-	//furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdTIM2, 14, NULL, NULL);
-	//furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdTim1UpTim16, 15, NULL, NULL);
-
-	sound_engine_stop();
-	sound_engine_deinit(&tracker->sound_engine);
-
-	FURI_CRITICAL_ENTER();
-	LL_TIM_DeInit(TIM1);
-	LL_TIM_DeInit(TIM2);
-	LL_TIM_DeInit(TIM16);
-	FURI_CRITICAL_EXIT();
+	deinit_tracker(tracker);
 
 	notification_message(tracker->notification, &sequence_display_backlight_enforce_auto);
-    furi_record_close(RECORD_NOTIFICATION);
+	furi_record_close(RECORD_NOTIFICATION);
 
 	// Специальная очистка памяти, занимаемой очередью
 	furi_message_queue_free(event_queue);

+ 32 - 0
flizzer_tracker.h

@@ -0,0 +1,32 @@
+#pragma once
+
+#include <notification/notification_messages.h>
+#include <stdio.h>
+#include <furi.h>
+#include <gui/gui.h>
+#include <input/input.h>
+#include <u8g2_glue.h>
+
+#include "flizzer_tracker_hal.h"
+#include "sound_engine/freqs.h"
+
+typedef struct 
+{
+	bool stop;
+	uint32_t counter;
+	uint32_t counter_2;
+	NotificationApp* notification;
+
+	SoundEngine sound_engine;
+
+	uint32_t frequency;
+	uint8_t current_waveform_index;
+	uint16_t pw;
+
+	uint8_t selected_param;
+
+	uint16_t flags;
+	uint16_t cutoff;
+	uint8_t resonance;
+	uint8_t filter_type;
+} FlizzerTrackerApp;

+ 26 - 0
flizzer_tracker_hal.c

@@ -1,4 +1,5 @@
 #include "flizzer_tracker_hal.h"
+#include "flizzer_tracker.h"
 
 #define SPEAKER_PWM_TIMER TIM16
 #define SAMPLE_RATE_TIMER TIM2
@@ -10,6 +11,31 @@
 
 #define DMA_INSTANCE DMA1, LL_DMA_CHANNEL_1
 
+void sound_engine_dma_isr(void* ctx)
+{
+	SoundEngine* sound_engine = (SoundEngine*)ctx;
+
+	// half of transfer
+	if(LL_DMA_IsActiveFlag_HT1(DMA1))
+	{
+		LL_DMA_ClearFlag_HT1(DMA1);
+		// fill first half of buffer
+		uint16_t* audio_buffer = sound_engine->audio_buffer;
+		uint32_t audio_buffer_length = sound_engine->audio_buffer_size / 2;
+		sound_engine_fill_buffer(sound_engine, audio_buffer, audio_buffer_length);
+	}
+
+	// transfer complete
+	if(LL_DMA_IsActiveFlag_TC1(DMA1))
+	{
+		LL_DMA_ClearFlag_TC1(DMA1);
+		// fill second half of buffer
+		uint32_t audio_buffer_length = sound_engine->audio_buffer_size / 2;
+		uint16_t* audio_buffer = &sound_engine->audio_buffer[audio_buffer_length];
+		sound_engine_fill_buffer(sound_engine, audio_buffer, audio_buffer_length);
+	}
+}
+
 void sound_engine_PWM_timer_init(bool external_audio_output) //external audio on pin PA6
 {
 	LL_TIM_InitTypeDef TIM_InitStruct = {0};

+ 1 - 0
flizzer_tracker_hal.h

@@ -10,6 +10,7 @@
 #include <furi_hal_gpio.h>
 #include <furi_hal_resources.h>
 
+void sound_engine_dma_isr(void* ctx);
 void sound_engine_PWM_timer_init(bool external_audio_output);
 void sound_engine_timer_init(uint32_t sample_rate);
 void sound_engine_dma_init(uint32_t address, uint32_t size);

+ 17 - 0
init_deinit.c

@@ -0,0 +1,17 @@
+#include "init_deinit.h"
+
+void init_tracker(FlizzerTrackerApp* tracker, uint32_t sample_rate, bool external_audio_output, uint32_t audio_buffer_size)
+{
+	sound_engine_init(&tracker->sound_engine, sample_rate, external_audio_output, audio_buffer_size);
+}
+
+void deinit_tracker(FlizzerTrackerApp* tracker)
+{
+	sound_engine_deinit(&tracker->sound_engine);
+
+	FURI_CRITICAL_ENTER();
+	LL_TIM_DeInit(TIM1);
+	LL_TIM_DeInit(TIM2);
+	LL_TIM_DeInit(TIM16);
+	FURI_CRITICAL_EXIT();
+}

+ 7 - 0
init_deinit.h

@@ -0,0 +1,7 @@
+#pragma once
+
+#include "flizzer_tracker_hal.h"
+#include "flizzer_tracker.h"
+
+void init_tracker(FlizzerTrackerApp* tracker, uint32_t sample_rate, bool external_audio_output, uint32_t audio_buffer_size);
+void deinit_tracker(FlizzerTrackerApp* tracker);

+ 9 - 9
sound_engine/freqs.c

@@ -2,7 +2,7 @@
 
 const uint32_t frequency_table[FREQ_TAB_SIZE] = 
 {
-    (uint32_t)(2093.00 * 1024), //7th octave, the highest in this tracker
+	(uint32_t)(2093.00 * 1024), //7th octave, the highest in this tracker
 	(uint32_t)(2217.46 * 1024), //frequency precision is 1 / 1024th of Hz
 	(uint32_t)(2349.32 * 1024),
 	(uint32_t)(2489.02 * 1024),
@@ -18,21 +18,21 @@ const uint32_t frequency_table[FREQ_TAB_SIZE] =
 
 uint32_t get_freq(uint16_t note)
 {
-    if (note >= ((FREQ_TAB_SIZE * 8) << 8))
+	if (note >= ((FREQ_TAB_SIZE * 8) << 8))
 	{
 		return frequency_table[FREQ_TAB_SIZE - 1];
 	}
 
-    if ((note & 0xff) == 0)
+	if ((note & 0xff) == 0)
 	{
 		return frequency_table[((note >> 8) % 12)] / ((NUM_OCTAVES - 1) - ((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));
+	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));
 
-        return f1 + (uint64_t)((f2 - f1) * (note & 0xff)) / 256;
-    }
+		return f1 + (uint64_t)((f2 - f1) * (note & 0xff)) / 256;
+	}
 }

+ 111 - 5
sound_engine/sound_engine.c

@@ -1,8 +1,11 @@
 #include "sound_engine.h"
+#include "../flizzer_tracker.h"
 #include "../flizzer_tracker_hal.h"
 
 #include <furi_hal.h>
 
+#define PI 3.1415
+
 void sound_engine_init(SoundEngine* sound_engine, uint32_t sample_rate, bool external_audio_output, uint32_t audio_buffer_size)
 {
 	sound_engine->audio_buffer = malloc(audio_buffer_size * sizeof(sound_engine->audio_buffer[0]));
@@ -15,6 +18,13 @@ void sound_engine_init(SoundEngine* sound_engine, uint32_t sample_rate, bool ext
 		sound_engine->channel[i].lfsr = RANDOM_SEED;
 	}
 
+	for(int i = 0; i < SINE_LUT_SIZE; ++i)
+	{
+		sound_engine->sine_lut[i] = (uint8_t)((sinf(i / 64.0 * PI) + 1.0) * 127.0);
+	}
+
+	furi_hal_interrupt_set_isr_ex(FuriHalInterruptIdDma1Ch1, 15, sound_engine_dma_isr, sound_engine);
+
 	sound_engine_init_hardware(sample_rate, external_audio_output, sound_engine->audio_buffer, audio_buffer_size);
 }
 
@@ -26,6 +36,9 @@ void sound_engine_deinit(SoundEngine* sound_engine)
 	{
 		furi_hal_speaker_release();
 	}
+
+	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)
@@ -58,6 +71,11 @@ static inline uint16_t sound_engine_triangle(uint32_t acc)
 	return ((((acc & (ACC_LENGTH / 2)) ? ~acc : acc) >> (ACC_BITS - OUTPUT_BITS - 2)) & (WAVE_AMP * 2 - 1));
 }
 
+static inline uint16_t sound_engine_sine(uint32_t acc, SoundEngine* sound_engine) 
+{
+	return (sound_engine->sine_lut[(acc >> (ACC_BITS - SINE_LUT_BITDEPTH))] << (OUTPUT_BITS - SINE_LUT_BITDEPTH));
+}
+
 inline static void shift_lfsr(uint32_t* v, uint32_t tap_0, uint32_t tap_1)
 {
 	typedef uint32_t T;
@@ -71,13 +89,12 @@ inline static void shift_lfsr(uint32_t* v, uint32_t tap_0, uint32_t tap_1)
 	*v = (*v >> 1) ^ ((zero - (*v & lsb)) & feedback);
 }
 
-uint16_t sound_engine_osc(SoundEngineChannel* channel, uint32_t prev_acc)
+uint16_t sound_engine_osc(SoundEngine* sound_engine, SoundEngineChannel* channel, uint32_t prev_acc)
 {
 	switch(channel->waveform)
 	{
 		case SE_WAVEFORM_NOISE:
 		{
-			//return sound_engine_noise(channel->accumulator, prev_acc, &channel->lfsr);
 			if((prev_acc & (ACC_LENGTH / 32)) != (channel->accumulator & (ACC_LENGTH / 32)))
 			{
 				shift_lfsr(&channel->lfsr, 22, 17);
@@ -106,21 +123,82 @@ uint16_t sound_engine_osc(SoundEngineChannel* channel, uint32_t prev_acc)
 			return sound_engine_saw(channel->accumulator);
 			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);
+			break;
+		}
 	}
 
 	return 0;
 }
 
+void sound_engine_filter_set_coeff(SoundEngineFilter *flt, uint32_t frequency, uint16_t resonance)
+{
+	flt->q = 2048 - frequency;
+	flt->p = frequency + ((int32_t)(0.8f * 2048.0f) * frequency / 2048 * flt->q) / 2048;
+	flt->f = flt->p + flt->p - 2048;
+	flt->q = resonance;
+}
+
+void sound_engine_filter_cycle(SoundEngineFilter* flt, int32_t input)
+{
+	input -= flt->q * flt->b4 / 2048; //feedback
+	int32_t t1 = flt->b1;  
+	flt->b1 = (input + flt->b0) * flt->p / 2048 - flt->b1 * flt->f / 2048;
+	int32_t t2 = flt->b2;  
+	flt->b2 = (flt->b1 + t1) * flt->p / 2048 - flt->b2 * flt->f / 2048;
+	t1 = flt->b3;
+	flt->b3 = (flt->b2 + t2) * flt->p / 2048 - flt->b3 * flt->f / 2048;
+	flt->b4 = (flt->b3 + t1) * flt->p / 2048 - flt->b4 * flt->f / 2048;
+	
+	//flt->b4 = my_min(32767, my_max(-32768, flt->b4));    //clipping
+	
+	flt->b0 = input;
+}
+
+int32_t sound_engine_output_lowpass(SoundEngineFilter* flt)
+{
+	return flt->b4;
+}
+
+int32_t sound_engine_output_highpass(SoundEngineFilter* flt)
+{
+	return flt->b0 - flt->b4;
+}
+
+
+int32_t sound_engine_output_bandpass(SoundEngineFilter* flt)
+{
+	return 3 * (flt->b3 - flt->b4);
+}
+
 void sound_engine_fill_buffer(SoundEngine* sound_engine, uint16_t* audio_buffer, uint32_t audio_buffer_size)
 {
 	for(uint32_t i = 0; i < audio_buffer_size; ++i)
 	{
-		uint16_t output = 0;
+		int32_t output = 0;
 
 		for(uint32_t chan = 0; chan < NUM_CHANNELS; ++chan)
 		{
 			SoundEngineChannel* channel = &sound_engine->channel[chan];
 
+			int32_t channel_output = 0;
+
 			if(channel->frequency > 0)
 			{
 				uint32_t prev_acc = channel->accumulator;
@@ -128,10 +206,38 @@ void sound_engine_fill_buffer(SoundEngine* sound_engine, uint16_t* audio_buffer,
 				channel->accumulator += channel->frequency;
 				channel->accumulator &= ACC_LENGTH - 1;
 
-				output += (sound_engine_osc(channel, prev_acc) >> (6 + 2));
+				channel_output += sound_engine_osc(sound_engine, channel, prev_acc) - WAVE_AMP / 2;
+
+				if(channel->flags & SE_ENABLE_FILTER)
+				{
+					sound_engine_filter_cycle(&channel->filter, channel_output);
+
+					switch(channel->filter_mode)
+					{
+						case FIL_OUTPUT_LOWPASS:
+						{
+							channel_output = sound_engine_output_lowpass(&channel->filter);
+							break;
+						}
+
+						case FIL_OUTPUT_HIGHPASS:
+						{
+							channel_output = sound_engine_output_highpass(&channel->filter);
+							break;
+						}
+
+						case FIL_OUTPUT_BANDPASS:
+						{
+							channel_output = sound_engine_output_bandpass(&channel->filter);
+							break;
+						}
+					}
+				}
+
+				output += ((channel_output + WAVE_AMP / 2) >> (6 + 2)); //2 more bits so all channels fit
 			}
 		}
 
-		audio_buffer[i] = output; //2 more bits so all channels fit
+		audio_buffer[i] = output;
 	}
 }

+ 38 - 1
sound_engine/sound_engine.h

@@ -13,6 +13,9 @@
 #define OUTPUT_BITS 16
 #define WAVE_AMP (1 << OUTPUT_BITS)
 
+#define SINE_LUT_SIZE 256
+#define SINE_LUT_BITDEPTH 8
+
 typedef enum
 {
 	SE_WAVEFORM_NONE = 0,
@@ -20,14 +23,38 @@ typedef enum
 	SE_WAVEFORM_PULSE = 2,
 	SE_WAVEFORM_TRIANGLE = 4,
 	SE_WAVEFORM_SAW = 8,
+	SE_WAVEFORM_NOISE_METAL = 16,
+	SE_WAVEFORM_SINE = 32,
 } SoudEngineWaveformType;
 
+typedef enum
+{
+	SE_ENABLE_FILTER = 1,
+	SE_ENABLE_GATE = 2,
+	SE_ENABLE_RING_MOD = 4,
+	SE_ENABLE_HARD_SYNC = 8,
+	SE_ENABLE_KEYDOWN_SYNC = 16, //sync oscillators on keydown
+} SoundEngineFlags;
+
+typedef enum
+{
+	FIL_OUTPUT_LOWPASS = 1,
+	FIL_OUTPUT_HIGHPASS = 2,
+	FIL_OUTPUT_BANDPASS = 3,
+} SoundEngineFilterModes;
+
 typedef struct
 {
 	uint8_t a, d, s, r, volume;
 	uint32_t envelope, envelope_speed;
 } SoundEngineADSR;
 
+typedef struct
+{
+	int32_t f, q, p;
+	int32_t b0, b1, b2, b3, b4; //filter coefficients
+} SoundEngineFilter;
+
 typedef struct
 {
 	uint32_t accumulator;
@@ -36,6 +63,14 @@ typedef struct
 	uint16_t pw;
 	uint32_t lfsr;
 	SoundEngineADSR adsr;
+
+	uint16_t flags;
+
+	uint8_t filter_mode;
+	uint16_t filter_cutoff;
+	uint16_t filter_resonace;
+
+	SoundEngineFilter filter;
 } SoundEngineChannel;
 
 typedef struct
@@ -45,9 +80,11 @@ typedef struct
 	uint16_t* audio_buffer;
 	uint32_t audio_buffer_size;
 	bool external_audio_output;
+	uint8_t sine_lut[SINE_LUT_SIZE];
 } SoundEngine;
 
 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_fill_buffer(SoundEngine* sound_engine, uint16_t* audio_buffer, uint32_t audio_buffer_size);
+void sound_engine_filter_set_coeff(SoundEngineFilter *flt, uint32_t frequency, uint16_t resonance);