diff --git a/examples/synth/src/bw_example_synth.c b/examples/synth/src/bw_example_synth.c
index 8effc7a..3d0b99d 100644
--- a/examples/synth/src/bw_example_synth.c
+++ b/examples/synth/src/bw_example_synth.c
@@ -27,6 +27,7 @@
 
 #include <bw_math.h>
 #include <bw_phase_gen.h>
+#include <bw_osc_saw.h>
 #include <bw_osc_pulse.h>
 #include <bw_osc_filt.h>
 #include <bw_noise_gen.h>
@@ -61,7 +62,6 @@ struct _bw_example_synth {
 	bw_phase_gen_coeffs	phase_gen_coeffs;
 	bw_phase_gen_state	phase_gen_state;
 	bw_osc_pulse_coeffs	osc_pulse_coeffs;
-	bw_osc_pulse_state	osc_pulse_state;
 	bw_osc_filt_state	osc_filt_state;	
 	bw_noise_gen_coeffs	noise_gen_coeffs;
 	bw_svf_coeffs		svf_coeffs;
@@ -170,10 +170,8 @@ void bw_example_synth_process(bw_example_synth instance, const float** x, float*
 		//bw_env_gen_set_gate(&instance->env_gen, 0);
 	
 	if (instance->note != -1) {
-		if (instance->note_prev < 0) {
-			bw_osc_pulse_reset_state(&instance->osc_pulse_coeffs, &instance->osc_pulse_state);
+		if (instance->note_prev < 0)
 			bw_osc_filt_reset_state(&instance->osc_filt_state);
-		}
 		bw_phase_gen_update_coeffs_ctrl(&instance->phase_gen_coeffs);
 		bw_osc_pulse_update_coeffs_ctrl(&instance->osc_pulse_coeffs);
 		for (int i = 0; i < n_samples; i++) {
@@ -181,7 +179,7 @@ void bw_example_synth_process(bw_example_synth instance, const float** x, float*
 			bw_phase_gen_update_coeffs_audio(&instance->phase_gen_coeffs);
 			bw_phase_gen_process1(&instance->phase_gen_coeffs, &instance->phase_gen_state, &phase, &phase_inc);
 			bw_osc_pulse_update_coeffs_audio(&instance->osc_pulse_coeffs);
-			y[0][i] = bw_osc_pulse_process1_antialias(&instance->osc_pulse_coeffs, &instance->osc_pulse_state, phase, phase_inc);
+			y[0][i] = bw_osc_pulse_process1_antialias(&instance->osc_pulse_coeffs, phase, phase_inc);
 		}
 		bw_osc_filt_process(&instance->osc_filt_state, y[0], y[0], n_samples);
 	} else {
diff --git a/include/bw_osc_pulse.h b/include/bw_osc_pulse.h
index ef43b7a..68567e7 100644
--- a/include/bw_osc_pulse.h
+++ b/include/bw_osc_pulse.h
@@ -48,6 +48,8 @@
 extern "C" {
 #endif
 
+#include <bw_common.h>
+
 /*! api {{{
  *    #### bw_osc_pulse_coeffs
  *  ```>>> */
@@ -55,12 +57,6 @@ typedef struct _bw_osc_pulse_coeffs bw_osc_pulse_coeffs;
 /*! <<<```
  *    Coefficients.
  *
- *    ### bw_one_pole_state
- *  >>> */
-typedef struct _bw_osc_pulse_state bw_osc_pulse_state;
-/*! <<<```
- *    State.
- *
  *    #### bw_osc_pulse_init()
  *  ```>>> */
 static inline void bw_osc_pulse_init(bw_osc_pulse_coeffs *BW_RESTRICT coeffs);
@@ -72,12 +68,6 @@ static inline void bw_osc_pulse_init(bw_osc_pulse_coeffs *BW_RESTRICT coeffs);
 static inline void bw_osc_pulse_set_sample_rate(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float sample_rate);
 /*! <<<```
  *    Sets the `sample_rate` (Hz) value for the given `coeffs`.
- *
- *    #### bw_osc_pulse_reset_state()
- *  ```>>> */
-static inline void bw_osc_pulse_reset_state(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state);
-/*! <<<```
- *    Resets the given `instance` to its initial state.
  *  >>> */
 
 static inline void bw_osc_pulse_reset_coeffs(bw_osc_pulse_coeffs *BW_RESTRICT coeffs);
@@ -85,13 +75,13 @@ static inline void bw_osc_pulse_reset_coeffs(bw_osc_pulse_coeffs *BW_RESTRICT co
 static inline void bw_osc_pulse_update_coeffs_ctrl(bw_osc_pulse_coeffs *BW_RESTRICT coeffs);
 static inline void bw_osc_pulse_update_coeffs_audio(bw_osc_pulse_coeffs *BW_RESTRICT coeffs);
 
-static inline float bw_osc_pulse_process1(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, float x);
-static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, float x, float x_phase_inc);
+static inline float bw_osc_pulse_process1(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x);
+static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc);
 
 /*! ...
  *    #### bw_osc_pulse_process()
  *  ```>>> */
-static inline void bw_osc_pulse_process(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, const float *x, const float *x_phase_inc, float *y, int n_samples);
+static inline void bw_osc_pulse_process(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, const float *x, const float *x_phase_inc, float *y, int n_samples);
 /*! <<<```
  *    Lets the given `instance` process `n_samples` samples from the input
  *    buffer `x` containing the normalized phase signal and fills the
@@ -141,11 +131,6 @@ struct _bw_osc_pulse_coeffs {
 	float			pulse_width;
 };
 
-struct _bw_osc_pulse_state {
-	// empty, but we keep for potential forward API compatibility
-	char	unused;
-};
-
 static inline void bw_osc_pulse_init(bw_osc_pulse_coeffs *BW_RESTRICT coeffs) {
 	bw_one_pole_init(&coeffs->smooth_coeffs);
 	bw_one_pole_set_tau(&coeffs->smooth_coeffs, 0.005f);
@@ -162,9 +147,6 @@ static inline void bw_osc_pulse_reset_coeffs(bw_osc_pulse_coeffs *BW_RESTRICT co
 	bw_one_pole_reset_state(&coeffs->smooth_coeffs, &coeffs->smooth_state, coeffs->pulse_width);
 }
 
-static inline void bw_osc_pulse_reset_state(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state) {
-}
-
 static inline void bw_osc_pulse_update_coeffs_ctrl(bw_osc_pulse_coeffs *BW_RESTRICT coeffs) {
 }
 
@@ -172,7 +154,7 @@ static inline void bw_osc_pulse_update_coeffs_audio(bw_osc_pulse_coeffs *BW_REST
 	bw_one_pole_process1(&coeffs->smooth_coeffs, &coeffs->smooth_state, coeffs->pulse_width);
 }
 
-static inline float bw_osc_pulse_process1(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, float x) {
+static inline float bw_osc_pulse_process1(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x) {
 	const float pw = bw_one_pole_get_y_z1(&coeffs->smooth_state);
 	return bw_signf(pw - x);
 }
@@ -184,7 +166,7 @@ static inline float _bw_osc_pulse_blep_diff(float x) {
 		: x * (x * ((0.6666666666666666f - 0.08333333333333333f * x) * x - 2.f) + 2.666666666666667f) - 1.333333333333333f;
 }
 
-static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, float x, float x_phase_inc) {
+static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc) {
 	const float pw = bw_one_pole_get_y_z1(&coeffs->smooth_state);
 	const float pw_m_phase = pw - x;
 	float v = bw_copysignf(1.f, pw_m_phase); // pw = phase case should be properly compensated by the AA
@@ -206,16 +188,16 @@ static inline float bw_osc_pulse_process1_antialias(const bw_osc_pulse_coeffs *B
 	return v;
 }
 
-static inline void bw_osc_pulse_process(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, bw_osc_pulse_state *BW_RESTRICT state, const float *x, const float *x_phase_inc, float *y, int n_samples) {
+static inline void bw_osc_pulse_process(bw_osc_pulse_coeffs *BW_RESTRICT coeffs, const float *x, const float *x_phase_inc, float *y, int n_samples) {
 	if (coeffs->antialiasing)
 		for (int i = 0; i < n_samples; i++) {
 			bw_osc_pulse_update_coeffs_audio(coeffs);
-			y[i] = bw_osc_pulse_process1_antialias(coeffs, state, x[i], x_phase_inc[i]);
+			y[i] = bw_osc_pulse_process1_antialias(coeffs, x[i], x_phase_inc[i]);
 		}
 	else
 		for (int i = 0; i < n_samples; i++) {
 			bw_osc_pulse_update_coeffs_audio(coeffs);
-			y[i] = bw_osc_pulse_process1(coeffs, state, x[i]);
+			y[i] = bw_osc_pulse_process1(coeffs, x[i]);
 		}
 }
 
diff --git a/include/bw_osc_saw.h b/include/bw_osc_saw.h
index ebf6996..cb8c65c 100644
--- a/include/bw_osc_saw.h
+++ b/include/bw_osc_saw.h
@@ -28,7 +28,7 @@
  *    <ul>
  *      <li>Version <strong>0.2.0</strong>:
  *        <ul>
- *          <li>Refactored API to avoid dynamic memory allocation.</li>
+ *          <li>Refactored API.</li>
  *        </ul>
  *      </li>
  *      <li>Version <strong>0.1.0</strong>:
@@ -47,32 +47,29 @@
 extern "C" {
 #endif
 
-/*! api {{{
- *    #### bw_osc_saw
- *  ```>>> */
-typedef struct _bw_osc_saw bw_osc_saw;
-/*! <<<```
- *    Instance object.
- *  >>> */
+#include <bw_common.h>
 
-/*! ...
+/*! api {{{
+ *    #### bw_osc_saw_coeffs
+ *  ```>>> */
+typedef struct _bw_osc_saw_coeffs bw_osc_saw_coeffs;
+/*! <<<```
+ *    Coefficients.
+ *
  *    #### bw_osc_saw_init()
  *  ```>>> */
-void bw_osc_saw_init(bw_osc_saw *instance);
+static inline void bw_osc_saw_init(bw_osc_saw_coeffs *BW_RESTRICT coeffs);
 /*! <<<```
- *    Initializes the `instance` object.
+ *    Initializes the `coeffs`.
  *  >>> */
 
-/*! ...
- *    #### bw_osc_saw_set_sample_rate() and bw_osc_saw_reset()
- *
- *    These do not exist (not needed).
- *  >>> */
+static inline float bw_osc_saw_process1(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x);
+static inline float bw_osc_saw_process1_antialias(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc);
 
 /*! ...
  *    #### bw_osc_saw_process()
  *  ```>>> */
-void bw_osc_saw_process(bw_osc_saw *instance, const float *x, const float *x_phase_inc, float* y, int n_samples);
+static inline void bw_osc_saw_process(bw_osc_saw_coeffs *BW_RESTRICT coeffs, const float *x, const float *x_phase_inc, float *y, int n_samples);
 /*! <<<```
  *    Lets the given `instance` process `n_samples` samples from the input
  *    buffer `x` containing the normalized phase signal and fills the
@@ -82,7 +79,7 @@ void bw_osc_saw_process(bw_osc_saw *instance, const float *x, const float *x_pha
 /*! ...
  *    #### bw_osc_saw_set_antialiasing()
  *  ```>>> */
-void bw_osc_saw_set_antialiasing(bw_osc_saw *instance, char value);
+static inline void bw_osc_saw_set_antialiasing(bw_osc_saw_coeffs *BW_RESTRICT coeffs, char value);
 /*! <<<```
  *    Sets whether the antialiasing is on (`value` non-`0`) or off (`0`) for the
  *    given `instance`.
@@ -90,14 +87,60 @@ void bw_osc_saw_set_antialiasing(bw_osc_saw *instance, char value);
  *    Default value: `0`.
  *  }}} */
 
-/* WARNING: the internal definition of this struct is not part of the public
- * API. Its content may change at any time in future versions. Please, do not
- * access its members directly. */
-struct _bw_osc_saw {
+/*** Implementation ***/
+
+/* WARNING: This part of the file is not part of the public API. Its content may
+ * change at any time in future versions. Please, do not use it directly. */
+
+#include <bw_math.h>
+
+struct _bw_osc_saw_coeffs {
 	// Parameters
 	char	antialiasing;
 };
 
+static inline void bw_osc_saw_init(bw_osc_saw_coeffs *BW_RESTRICT coeffs) {
+	coeffs->antialiasing = 0;
+}
+
+static inline float bw_osc_saw_process1(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x) {
+	return x + x - 1.f;
+}
+
+// PolyBLEP residual based on Parzen window (4th-order B-spline), one-sided (x in [0, 2])
+static inline float _bw_osc_saw_blep_diff(float x) {
+	return x < 1.f
+		? x * ((0.25f * x - 0.6666666666666666f) * x * x + 1.333333333333333f) - 1.f
+		: x * (x * ((0.6666666666666666f - 0.08333333333333333f * x) * x - 2.f) + 2.666666666666667f) - 1.333333333333333f;
+}
+
+static inline float bw_osc_saw_process1_antialias(const bw_osc_saw_coeffs *BW_RESTRICT coeffs, float x, float x_phase_inc) {
+	const float s_1_m_phase = 1.f - x;
+	float v = x - s_1_m_phase;
+	if (x_phase_inc != 0.f) {
+		const float phase_inc_2 = x_phase_inc + x_phase_inc;
+		const float phase_inc_rcp = bw_rcpf_2(x_phase_inc);
+		if (s_1_m_phase < phase_inc_2)
+			v += _bw_osc_saw_blep_diff(s_1_m_phase * phase_inc_rcp);
+		if (x < phase_inc_2)
+			v -= _bw_osc_saw_blep_diff(x * phase_inc_rcp);
+	}
+	return v;
+}
+
+static inline void bw_osc_saw_process(bw_osc_saw_coeffs *BW_RESTRICT coeffs, const float *x, const float *x_phase_inc, float *y, int n_samples) {
+	if (coeffs->antialiasing)
+		for (int i = 0; i < n_samples; i++)
+			y[i] = bw_osc_saw_process1_antialias(coeffs, x[i], x_phase_inc[i]);
+	else
+		for (int i = 0; i < n_samples; i++)
+			y[i] = bw_osc_saw_process1(coeffs, x[i]);
+}
+
+static inline void bw_osc_saw_set_antialiasing(bw_osc_saw_coeffs *BW_RESTRICT coeffs, char value) {
+	coeffs->antialiasing = value;
+}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/bw_osc_saw.c b/src/bw_osc_saw.c
deleted file mode 100644
index 282a71d..0000000
--- a/src/bw_osc_saw.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Brickworks
- *
- * Copyright (C) 2022 Orastron Srl unipersonale
- *
- * Brickworks is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3 of the License.
- *
- * Brickworks is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- *
- * File author: Stefano D'Angelo
- */
-
-#include <bw_osc_saw.h>
-
-#include <bw_math.h>
-
-void bw_osc_saw_init(bw_osc_saw *instance) {
-	instance->antialiasing = 0;
-}
-
-// PolyBLEP residual based on Parzen window (4th-order B-spline), one-sided (x in [0, 2])
-static inline float blep_diff(float x) {
-	return x < 1.f
-		? x * ((0.25f * x - 0.6666666666666666f) * x * x + 1.333333333333333f) - 1.f
-		: x * (x * ((0.6666666666666666f - 0.08333333333333333f * x) * x - 2.0f) + 2.666666666666667f) - 1.333333333333333f;
-}
-
-void bw_osc_saw_process(bw_osc_saw *instance, const float *x, const float *x_phase_inc, float* y, int n_samples) {
-	if (instance->antialiasing) {
-		for (int i = 0; i < n_samples; i++) {
-			const float s_1_m_phase = 1.f - x[i];
-			float v = x[i] - s_1_m_phase;
-
-			if (x_phase_inc[i] != 0.f) {
-				const float phase_inc_2 = x_phase_inc[i] + x_phase_inc[i];
-				const float phase_inc_rcp = bw_rcpf_2(x_phase_inc[i]);
-				if (s_1_m_phase < phase_inc_2)
-					v += blep_diff(s_1_m_phase * phase_inc_rcp);
-				if (x[i] < phase_inc_2)
-					v -= blep_diff(x[i] * phase_inc_rcp);
-			}
-
-			y[i] = v;
-		}
-	} else {
-		for (int i = 0; i < n_samples; i++)
-			y[i] = x[i] + x[i] - 1.f;
-	}
-}
-
-void bw_osc_saw_set_antialiasing(bw_osc_saw *instance, char value) {
-	instance->antialiasing = value;
-}