Skip to content

Commit a1d4213

Browse files
antiimaging filter
1 parent 6f155b4 commit a1d4213

File tree

5 files changed

+37
-21
lines changed

5 files changed

+37
-21
lines changed

README.md

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ to only that which is essential for understanding the core of the simulation.
1818

1919
# The Digital Audio Workbench
2020

21-
https://idmil.gitlab.io/course-materials/mumt203/interactive-demos
21+
https://idmil.gitlab.io/course-materials/mumt203/interactive-demos
2222

2323
## Introduction
2424

@@ -44,7 +44,7 @@ unfortunately cannot use real continuous time analog inputs and outputs.
4444
Instead, we simulate the ADC-DAC processes in the discrete time domain. The
4545
analog input and output are represented as discrete time signals with a high
4646
sampling rate; at the time of writing, the maximum sampling rate supported
47-
by WebAudio is 96 kHz.
47+
by WebAudio is 96 kHz.
4848

4949
The ADC process consists of several steps, including antialiasing, sampling,
5050
and quantization. All of these are simulated in our model: antialiasing is
@@ -55,7 +55,7 @@ signal (which ranges from -1.0 to 1.0) by the maximum integer value possible
5555
given the requested bit depth (e.g. 255 for a bit depth of 8 bits), and then
5656
rounding every sample to the nearest integer. The DAC process is simulated
5757
in turn by zero stuffing and lowpass filtering the sampled and quantized
58-
output of the ADC simultion.
58+
output of the ADC simultion.
5959

6060
In summary, the continuous time input is simulated by a 96 kHz discrete time
6161
signal, the sampled output of the ADC process is simulated by a downsampled
@@ -85,7 +85,7 @@ const fadeTimeSeconds = 0.125;
8585
function renderWavesImpl(settings, fft, p) { return (playback = false) => {
8686

8787
// if we are not rendering for playback, we are rendering for simulation
88-
let simulation = !playback;
88+
let simulation = !playback;
8989

9090
// select the buffer to render to; playback buffer, or simulation buffer
9191
var original = playback ? settings.original_pb : settings.original;
@@ -104,34 +104,34 @@ function renderWavesImpl(settings, fft, p) { return (playback = false) => {
104104
// already have been calculated earlier when rendering for playback
105105

106106
if (simulation) {
107-
let harmonic_number = 1;
108-
let harmonic_amplitude = 1;
107+
let harmonic_number = 1;
108+
let harmonic_amplitude = 1;
109109
let invert = 1;
110110
let harmInc = (settings.harmType =="Odd" || settings.harmType == "Even") ? 2 : 1;
111-
111+
112112
for (let i = 0; simulation && i < settings.numHarm; i++) {
113-
113+
114114
// the amplitude of each harmonic depends on the harmonic slope setting
115115
if (settings.harmSlope == "lin") harmonic_amplitude = 1 - i/settings.numHarm;
116116
else if (settings.harmSlope == "1/x") harmonic_amplitude = 1/harmonic_number;
117117
else if (settings.harmSlope == "1/x2") harmonic_amplitude = 1/harmonic_number/harmonic_number;
118118
else if (settings.harmSlope == "flat") harmonic_amplitude = 1;
119-
119+
120120
// In case the harmonic slope is 1/x^2 and the harmonic type is "odd",
121121
// by inverting every other harmonic we generate a nice triangle wave.
122122
if (settings.harmSlope =="1/x2" && settings.harmType == "Odd") {
123123
harmonic_amplitude = harmonic_amplitude * invert;
124124
invert *= -1;
125125
}
126-
126+
127127
// the frequency of each partial is a multiple of the fundamental frequency
128128
settings.harmonicFreqs[i] = harmonic_number*settings.fundFreq;
129-
129+
130130
// The harmonic amplitude is calculated above according to the harmonic
131131
// slope setting, taking into account the special case for generating a
132132
// triangle.
133133
settings.harmonicAmps[i] = harmonic_amplitude;
134-
134+
135135
// With harmonic type set to "even" we want the fundamental and even
136136
// harmonics. To achieve this, we increment the harmonic number by 1 after
137137
// the fundamental and by 2 after every other partial.
@@ -187,7 +187,7 @@ function renderWavesImpl(settings, fft, p) { return (playback = false) => {
187187
// simulation are not ideal brick wall filters, but approximations.
188188

189189
// apply antialiasing only if the filter order is set
190-
if (settings.antialiasing > 1) {
190+
if (settings.antialiasing > 1) {
191191

192192
// specify the filter parameters; Fs = sampling rate, Fc = cutoff frequency
193193

@@ -196,10 +196,10 @@ function renderWavesImpl(settings, fft, p) { return (playback = false) => {
196196
// signal is WEBAUDIO_MAX_SAMPLERATE / the downsampling factor. This is
197197
// divided by 2 to get the Nyquist frequency.
198198
var filterCoeffs = firCalculator.lowpass(
199-
{ order: settings.antialiasing
199+
{ order: settings.antialiasing
200200
, Fs: WEBAUDIO_MAX_SAMPLERATE
201201
, Fc: (WEBAUDIO_MAX_SAMPLERATE / settings.downsamplingFactor) / 2
202-
});
202+
});
203203

204204
// generate the filter
205205
var filter = new Fili.FirFilter(filterCoeffs);
@@ -282,7 +282,7 @@ function renderWavesImpl(settings, fft, p) { return (playback = false) => {
282282

283283
// sparsely fill the reconstruction buffer to avoid having to zero-stuff
284284
reconstructed[n * settings.downsamplingFactor] = quantized;
285-
stuffed[n * settings.downsamplingFactor] = quantized * settings.downsamplingFactor;
285+
stuffed[n * settings.downsamplingFactor] = quantized * settings.downsamplingFactor;
286286

287287
// record the quantization error
288288
quantNoise[n] = quantized - y;
@@ -293,10 +293,10 @@ function renderWavesImpl(settings, fft, p) { return (playback = false) => {
293293

294294
// specify filter parameters; as before, the cutoff is set to the Nyquist
295295
var filterCoeffs = firCalculator.lowpass(
296-
{ order: 200
296+
{ order: settings.antiimaging
297297
, Fs: WEBAUDIO_MAX_SAMPLERATE
298298
, Fc: (WEBAUDIO_MAX_SAMPLERATE / settings.downsamplingFactor) / 2
299-
});
299+
});
300300

301301
// generate the filter
302302
var filter = new Fili.FirFilter(filterCoeffs);
@@ -334,7 +334,7 @@ function renderWavesImpl(settings, fft, p) { return (playback = false) => {
334334
fft.completeSpectrum(settings.reconstructedFreq);
335335

336336
fft.realTransform(settings.quantNoiseFreq, quantNoiseStuffed)
337-
fft.completeSpectrum(settings.quantNoiseFreq);
337+
fft.completeSpectrum(settings.quantNoiseFreq);
338338
}
339339

340340
// fade in and out and suppress clipping distortions ------------------------
@@ -353,9 +353,9 @@ function renderWavesImpl(settings, fft, p) { return (playback = false) => {
353353
let fade = (_, n, arr) => {
354354
let fadeTimeSamps = Math.min(fadeTimeSeconds * WEBAUDIO_MAX_SAMPLERATE, arr.length / 2);
355355
// The conditional ensures there is a fade even if the fade time is longer than the signal
356-
if (n < fadeTimeSamps)
356+
if (n < fadeTimeSamps)
357357
arr[n] = (n / fadeTimeSamps) * arr[n] / normalize;
358-
else if (n > arr.length - fadeTimeSamps)
358+
else if (n > arr.length - fadeTimeSamps)
359359
arr[n] = ((arr.length - n) / fadeTimeSamps) * arr[n] / normalize;
360360
else arr[n] = arr[n] / normalize;
361361
};

all-panels/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
, new numHarmSlider()
3737
, new sampleRateSlider()
3838
, new antialiasingSlider()
39+
, new antiimagingSlider()
3940
, new ditherSlider()
4041
, new bitDepthSlider()
4142
, new amplitudeSlider()

sampling/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
, new numHarmSlider()
3333
, new sampleRateSlider()
3434
, new antialiasingSlider()
35+
, new antiimagingSlider()
3536
, new phaseSlider()
3637
, new freqZoomSlider()
3738
, new timeZoomSlider()

slider.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,19 @@ class antialiasingSlider extends slider {
217217
}
218218
}
219219

220+
class antiimagingSlider extends slider {
221+
setup(p, settings) {
222+
this.settings = settings;
223+
this.propName ="antiimaging";
224+
this.name = "Anti-imaging filter order";
225+
this.min = 0.0;
226+
this.max = 200;
227+
this.initial = 200;
228+
this.step = 10;
229+
this.makeSlider(p);
230+
}
231+
}
232+
220233
class phaseSlider extends slider{
221234
setup(p,settings){
222235
this.settings = settings;

widget.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ var settings =
2929
, quantType : "midRise" // type of quantization
3030
, dither : 0.0 // amplitude of white noise added to signal before quantization
3131
, antialiasing : 0 // antialiasing filter order
32+
, antiimaging : 200 // anti-imaging filter order
3233
, original: new Float32Array(displaySignalSize)
3334
, downsampled: new Float32Array(1) // this gets re-inited when rendering waves
3435
, reconstructed: new Float32Array(displaySignalSize)

0 commit comments

Comments
 (0)