Skip to content

Commit 7b962a4

Browse files
authored
Add Exponential Moving Average Filter (#6075)
1 parent a676ebc commit 7b962a4

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.thealgorithms.audiofilters;
2+
3+
/**
4+
* Exponential Moving Average (EMA) Filter for smoothing audio signals.
5+
*
6+
* <p>This filter applies an exponential moving average to a sequence of audio
7+
* signal values, making it useful for smoothing out rapid fluctuations.
8+
* The smoothing factor (alpha) controls the degree of smoothing.
9+
*
10+
* <p>Based on the definition from
11+
* <a href="https://en.wikipedia.org/wiki/Moving_average">Wikipedia link</a>.
12+
*/
13+
public class EMAFilter {
14+
private final double alpha;
15+
private double emaValue;
16+
/**
17+
* Constructs an EMA filter with a given smoothing factor.
18+
*
19+
* @param alpha Smoothing factor (0 < alpha <= 1)
20+
* @throws IllegalArgumentException if alpha is not in (0, 1]
21+
*/
22+
public EMAFilter(double alpha) {
23+
if (alpha <= 0 || alpha > 1) {
24+
throw new IllegalArgumentException("Alpha must be between 0 and 1.");
25+
}
26+
this.alpha = alpha;
27+
this.emaValue = 0.0;
28+
}
29+
/**
30+
* Applies the EMA filter to an audio signal array.
31+
*
32+
* @param audioSignal Array of audio samples to process
33+
* @return Array of processed (smoothed) samples
34+
*/
35+
public double[] apply(double[] audioSignal) {
36+
if (audioSignal.length == 0) {
37+
return new double[0];
38+
}
39+
double[] emaSignal = new double[audioSignal.length];
40+
emaValue = audioSignal[0];
41+
emaSignal[0] = emaValue;
42+
for (int i = 1; i < audioSignal.length; i++) {
43+
emaValue = alpha * audioSignal[i] + (1 - alpha) * emaValue;
44+
emaSignal[i] = emaValue;
45+
}
46+
return emaSignal;
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.thealgorithms.audiofilters;
2+
3+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
4+
5+
import org.junit.jupiter.api.Test;
6+
7+
public class EMAFilterTest {
8+
9+
@Test
10+
public void testApplyBasicSignal() {
11+
EMAFilter emaFilter = new EMAFilter(0.2);
12+
double[] audioSignal = {0.1, 0.5, 0.8, 0.6, 0.3, 0.9, 0.4};
13+
double[] expectedOutput = {0.1, 0.18, 0.304, 0.3632, 0.35056, 0.460448, 0.4483584};
14+
double[] result = emaFilter.apply(audioSignal);
15+
assertArrayEquals(expectedOutput, result, 1e-5);
16+
}
17+
18+
@Test
19+
public void testApplyEmptySignal() {
20+
EMAFilter emaFilter = new EMAFilter(0.2);
21+
double[] audioSignal = {};
22+
double[] expectedOutput = {};
23+
double[] result = emaFilter.apply(audioSignal);
24+
assertArrayEquals(expectedOutput, result);
25+
}
26+
27+
@Test
28+
public void testAlphaBounds() {
29+
EMAFilter emaFilterMin = new EMAFilter(0.01);
30+
EMAFilter emaFilterMax = new EMAFilter(1.0);
31+
double[] audioSignal = {1.0, 1.0, 1.0, 1.0};
32+
33+
// Minimal smoothing (alpha close to 0)
34+
double[] resultMin = emaFilterMin.apply(audioSignal);
35+
assertArrayEquals(audioSignal, resultMin, 1e-5);
36+
37+
// Maximum smoothing (alpha = 1, output should match input)
38+
double[] resultMax = emaFilterMax.apply(audioSignal);
39+
assertArrayEquals(audioSignal, resultMax, 1e-5);
40+
}
41+
}

0 commit comments

Comments
 (0)