Write-up Programming Assignment #1
EENG 5610 - Digital Signal Processing
Saad Iqbal Ali
11701913
Contents
1.
Abstract ....................................................................................................................... 3
2.
Objective ..................................................................................................................... 3
3.
Procedure .................................................................................................................... 3
Phase 1 – Recording speech ............................................................................................ 3
Phase 2 – Preparing the clean signal s(n)........................................................................ 4
Phase 3 – Synthesizing echoes to create x(n) ................................................................. 5
Phase 4 – Autocorrelation of two-echo signal ................................................................ 6
Phase 5 – Parameter estimation from autocorrelation .................................................... 7
Phase 6 – Single echo case.............................................................................................. 7
4.
Results and Observations ............................................................................................ 8
5.
Analytical Discussion ................................................................................................. 9
6.
Conclusion ................................................................................................................ 10
7.
Appendix ................................................................................................................... 10
Phase 1 – Recording speech (SA_phase1_record.m) ................................................... 10
Phase 2 – Preparing the clean signal (SA_phase2_prepare.m) ..................................... 11
Phase 3 – Synthesizing echoes (SA_phase3_make_echoes.m) .................................... 12
Phase 4 – Autocorrelation of two-echo signal (SA_phase4_autocorr_two.m) ............. 13
Phase 5 – Parameter estimation (SA_phase5_estimate_two.m) ................................... 15
Phase 6 – Single echo case (SA_phase6_one_echo.m) ................................................ 17
1. Abstract
In this assignment I worked in MATLAB to record a 20-second clip of my own speech at
8 kHz, mono, 8-bit. I cleaned the signal by removing the DC offset and normalizing it so
it wouldn’t clip. Then I added two echoes: one after 5 seconds with strength 0.6 and
another after 12 seconds with strength 0.3. When I played it back, I could clearly hear the
first echo louder than the second. The autocorrelation plot showed strong peaks at 5s, 12
s, and also at 7s, which matches the difference between them. From those peaks I
estimated the echo delays and gains, and they came out very close to the values I used.
Finally, I tried a single-echo case at 5s, and the autocorrelation showed just one peak,
which made the estimation much easier.
2. Objective
The goal of this assignment was to practice working with real audio signals in MATLAB
and use autocorrelation to detect echoes. I started by recording my own 20-second speech
clip at 8 kHz, mono, 8-bit. From there, I cleaned the signal, added artificial echoes, and
then analyzed the result using autocorrelation. The main objectives were:
Record and visualize a short speech signal.
Create echoes with known delays and strengths.
Compute and plot the autocorrelation of the echoed signal.
Estimate the delays and strengths from the autocorrelation.
Repeat the analysis for a simpler one-echo case.
3. Procedure
Phase 1 – Recording speech
I recorded a 20-second sample of my own speech at 8 kHz, mono, 8-bit. After recording,
I played it back to make sure it sounded okay and saved it in both .mat and .wav formats.
I also plotted the full 20-second waveform and a zoomed-in section between 1.0 and 1.5
seconds to see the fine details of the signal.
Figure 1: Recorded speech waveform
3
Figure 2: Zoomed recorded speech
Phase 2 – Preparing the clean signal s(n)
I loaded the recorded file, removed the DC offset, and normalized the amplitude so it
stayed just under 1. This ensured that the signal wouldn’t clip when I added echoes. I
saved the processed version as s(n) and plotted both the full and zoomed views.
Figure 3: Cleaned speech signal
Figure 4: Zoomed cleaned speech signal
4
Phase 3 – Synthesizing echoes to create x(n)
I created an echoed signal with two reflections: one at 5 seconds with gain 0.6 and
another at 12 seconds with gain 0.3. To do this, I convolved the clean signal with an
impulse response that included delays at 5 and 12 seconds. The resulting signal was saved
and plotted in both full and zoomed form.
Figure 5: Echoed signal waveform
Figure 6: Zoomed echoed signal
5
Phase 4 – Autocorrelation of two-echo signal
I computed the autocorrelation of the echoed signal using MATLAB’s xcorr function.
The result was normalized so that the center peak equaled 1. When plotted, the
autocorrelation showed clear peaks at ±5 s, ±12 s, and ±7 s, exactly as expected from the
echo delays.
Figure 7: Autocorrelation of two-echo signal
Figure 8: Autocorrelation with detected peaks
6
Phase 5 – Parameter estimation from autocorrelation
I used peak detection to estimate the echo delays and strengths from the autocorrelation.
The largest peaks lined up with 5 s and 12 s, and there was another peak at 7 s (the
difference between them). From the peak heights, I estimated reflection coefficients close
to 0.6 and 0.3, which matched the values I used to build the echoes.
Figure 9: Autocorrelation with estimated parameters
Phase 6 – Single echo case
Finally, I tried the one-echo case by setting 𝑟2 = 0. This left only a single reflection at 5 s
with gain 0.6. The echoed signal sounded like one repeat, and the autocorrelation plot
showed just one pair of peaks at ±5 s. Estimation was easy in this case because only one
echo was present.
Figure 10: Single-echo signal waveform
7
Figure 11: Autocorrelation of single echo
Figure 12: Autocorrelation with estimated echo lag
4. Results and Observations
The 20-second speech recording at 8 kHz, mono, 8-bit played back clearly. There was
some background noise from my environment, but the waveform looked fine and showed
natural rises and falls where I spoke and paused. In the zoomed plot between 1.0–1.5
seconds, I could see the detailed wiggles of the speech signal and the zero crossings.
After removing the DC offset and normalizing, the cleaned signal was centered around
zero and its peaks stayed below 1. This step didn’t change the shape much but ensured
that later echo processing wouldn’t clip the signal.
8
When I added echoes to form 𝑥(𝑛), I could clearly hear two repeats of my speech. The
first repeat at 5 seconds (gain 0.6) was noticeably louder than the second at 12 seconds
(gain 0.3). The time-domain plot showed the signal stretching longer (about 32 seconds
total) and extra bumps where the echoes appeared.
The autocorrelation of the echoed signal had clear peaks at around 5 s, 12 s, and also at 7
s (which is the difference between them). These matched what I expected from theory.
By checking the heights of those peaks relative to the center, the estimated gains came
out close to 0.6 and 0.3.
When I tested the single-echo case ( 𝑟2 = 0 ), I heard only one repeat at 5 s. The
autocorrelation also showed just one set of peaks at ±5 s, which made the estimation very
straightforward.
Parameter Actual value Estimated value
𝑘1 (s)
5.0
~5.0
𝑘2 (s)
12.0
~12.0
𝑟1
0.6
~0.6
𝑟2
0.3
~0.3
Table 1: Actual vs Estimated Echo Parameters
5. Analytical Discussion
The echoed signal was built as
𝑥[𝑛] = 𝑠[𝑛] + 𝑟1 𝑠[𝑛 − 𝑘1 ] + 𝑟2 𝑠[𝑛 − 𝑘2 ].
We know that the autocorrelation of this type of signal doesn’t just have a big peak at
zero lag — it also shows extra peaks at the echo delays. In this case, we expect peaks at
±5s, ±12s, and ±7s (the difference between 12 and 5).
The positions of the peaks tell us the delays (𝑘1 , 𝑘2 ).
The relative heights of the peaks give us approximate values of 𝑟1 and 𝑟2 .
For perfectly “white” input signals the match would be exact, but for real speech
the estimates are close enough.
In the simplified case with only one echo (𝑟2 = 0), the autocorrelation only shows one
extra set of peaks at ±5s. This matched what I observed in the plots and made the
estimation much easier.
Overall, the theoretical expressions explain why my autocorrelation plots looked the way
they did and why the parameters could be recovered.
9
6. Conclusion
This assignment showed how MATLAB can be used to work on real audio. I successfully
recorded a 20-second sample, cleaned it, and added two echoes at 5s and 12s. Listening
confirmed the echoes, with the first one louder than the second. The autocorrelation plots
revealed peaks at the expected delays and also at their difference, which matched the
theory. Using those peaks I was able to estimate the delays and gains, and they were close
to the original values. When I tried the single-echo case, the analysis was much simpler,
with only one clear peak in the autocorrelation. Overall, the results connected well with
the theoretical formulas and confirmed that autocorrelation is effective for detecting
echoes.
7. Appendix
MATLAB Code
Phase 1 – Recording speech (SA_phase1_record.m)
% SA_phase1_record.m
% Phase 1: record 20 seconds at 8 kHz, mono, 8-bit. Save audio and plots.
clc; clear; close all;
fs = 8000;
nbits = 8;
nch = 1;
durSec = 20;
recObj = audiorecorder(fs, nbits, nch);
disp('This is Phase 1 recording for the assignment.');
disp('When you are ready, speak clearly and read your script.');
disp('Recording will start now for 20 seconds...');
recordblocking(recObj, durSec);
disp('Recording finished. Playing back now...');
play(recObj);
speech = getaudiodata(recObj);
N = numel(speech);
t = (0:N-1)/fs;
save('SA_speech8kHz8bitMono.mat','speech','fs');
audiowrite('SA_speech8kHz8bitMono.wav', speech, fs, 'BitsPerSample',
nbits);
figure;
plot(t, speech); grid on;
10
xlabel('Time (s)'); ylabel('Amplitude');
title('Recorded Speech (full 20 s)');
saveas(gcf, 'SA_speech_time.png');
tStart = 1.0; tEnd = 1.5;
idxZ = (t >= tStart) & (t <= tEnd);
figure;
plot(t(idxZ), speech(idxZ)); grid on;
xlabel('Time (s)'); ylabel('Amplitude');
title('Recorded Speech (zoom 1.0 to 1.5 s)');
saveas(gcf, 'SA_speech_zoom_1_1p5.png');
fprintf('\nSummary\n');
fprintf(' Sample rate: %d Hz\n', fs);
fprintf(' Bit depth : %d-bit\n', nbits);
fprintf(' Channels : %d (mono)\n', nch);
fprintf(' Samples : %d\n', N);
fprintf(' Duration : %.3f s\n', N/fs);
fprintf(' Mean
: %.4f\n', mean(speech));
fprintf(' Max |x| : %.4f\n', max(abs(speech)));
disp('Saved: SA_speech8kHz8bitMono.(mat|wav), SA_speech_time.png,
SA_speech_zoom_1_1p5.png');
Phase 2 – Preparing the clean signal (SA_phase2_prepare.m)
% SA_phase2_prepare.m
% Phase 2: load speech, remove DC, normalize, save s(n) and plots.
clc; clear; close all;
if exist('SA_speech8kHz8bitMono.mat','file')
load('SA_speech8kHz8bitMono.mat','speech','fs');
elseif exist('SA_speech8kHz8bitMono.wav','file')
[speech, fs] = audioread('SA_speech8kHz8bitMono.wav');
else
error('Place SA_speech8kHz8bitMono.(mat|wav) in this folder first.');
end
speech = speech(:);
N = numel(speech);
t = (0:N-1)/fs;
dc = mean(speech);
s_dc = speech - dc;
11
pk = max(abs(s_dc));
if pk > 0
s = 0.98 * (s_dc / pk);
else
s = s_dc;
end
save('SA_s_clean.mat','s','fs');
figure;
plot(t, s); grid on;
xlabel('Time (s)'); ylabel('Amplitude');
title('s(n) — full duration');
saveas(gcf, 'SA_s_clean_time.png');
tStart = 1.0; tEnd = 1.5;
idxZ = (t >= tStart) & (t <= tEnd);
figure;
plot(t(idxZ), s(idxZ)); grid on;
xlabel('Time (s)'); ylabel('Amplitude');
title('s(n) — zoom 1.0 to 1.5 s');
saveas(gcf, 'SA_s_clean_zoom_1_1p5.png');
fprintf('fs=%d Hz, duration=%.3f s, DC before=%.5f, DC after=%.5f,
peak=%.3f\n', ...
fs, N/fs, dc, mean(s), max(abs(s)));
disp('Saved: SA_s_clean.mat, SA_s_clean_time.png,
SA_s_clean_zoom_1_1p5.png');
Phase 3 – Synthesizing echoes (SA_phase3_make_echoes.m)
% SA_phase3_make_echoes.m
% Phase 3: x(n) = s(n) + 0.6 s(n-5s) + 0.3 s(n-12s)
clc; clear; close all;
load('SA_s_clean.mat','s','fs');
s = s(:);
r1 = 0.6; k1_sec = 5;
r2 = 0.3; k2_sec = 12;
k1 = round(k1_sec*fs);
k2 = round(k2_sec*fs);
12
h = zeros(k2+1,1);
h(1) = 1;
h(k1+1) = r1;
h(k2+1) = r2;
x = conv(s, h, 'full');
t_x = (0:numel(x)-1)/fs;
peak = max(abs(x));
scale = 1.0;
if peak > 0.99
scale = 0.98/peak;
x = x * scale;
end
save('SA_x_two_echoes.mat','x','fs','r1','r2','k1','k2','k1_sec','k2_sec','scale');
audiowrite('SA_x_two_echoes.wav', x, fs, 'BitsPerSample', 16);
figure;
plot(t_x, x); grid on;
xlabel('Time (s)'); ylabel('Amplitude');
title('x(n) with echoes at 5 s and 12 s');
xline(k1_sec,'--','k1');
xline(k2_sec,'--','k2');
saveas(gcf,'SA_x_time.png');
tStart = 1.0; tEnd = 1.5;
idxZ = (t_x >= tStart) & (t_x <= tEnd);
figure;
plot(t_x(idxZ), x(idxZ)); grid on;
xlabel('Time (s)'); ylabel('Amplitude');
title('x(n) — zoom 1.0 to 1.5 s');
saveas(gcf,'SA_x_zoom_1_1p5.png');
disp('Saved: SA_x_two_echoes.(mat|wav), SA_x_time.png,
SA_x_zoom_1_1p5.png');
Phase 4 – Autocorrelation of two-echo signal (SA_phase4_autocorr_two.m)
% SA_phase4_autocorr_two.m
13
% Phase 4: r_xx(l) for two-echo signal and mark peaks
clc; clear; close all;
load('SA_x_two_echoes.mat','x','fs','k1_sec','k2_sec');
x = x(:);
[rxx, lags] = xcorr(x, 'biased');
[~, i0] = min(abs(lags));
rxx = rxx / rxx(i0);
lags_sec = lags / fs;
lagWin = 15;
idx = abs(lags_sec) <= lagWin;
rplot = rxx;
if numel(rplot) > 2000
rplot = movmean(rplot, 25);
end
figure;
plot(lags_sec(idx), rplot(idx)); grid on;
xlabel('Lag (s)'); ylabel('Normalized r_{xx}');
title('Autocorrelation of x(n) — two echoes');
saveas(gcf,'SA_rxx_two_echoes.png');
pos = lags_sec >= 0 & lags_sec <= lagWin;
[pk, locs] = findpeaks(rplot(pos), lags_sec(pos), ...
'MinPeakDistance', 0.8, 'MinPeakHeight', 0.05);
nz = locs > 1e-3;
pk = pk(nz); locs = locs(nz);
[pkS, ord] = sort(pk, 'descend');
locS = locs(ord);
K = min(3, numel(locS));
estLags = sort(locS(1:K), 'ascend');
estHeights = pkS(1:K);
hold on;
stem(estLags, estHeights, 'filled');
legend('r_{xx}', 'Detected peaks', 'Location','best');
saveas(gcf,'SA_rxx_two_echoes_peaks.png');
fprintf('Detected positive-lag peaks (s): '); fprintf('%.2f ', estLags);
fprintf('\n');
14
fprintf('Expected near: %.2f s, %.2f s, |%.2f-%.2f|=%.2f s\n', ...
k1_sec, k2_sec, k2_sec, k1_sec, abs(k2_sec-k1_sec));
Phase 5 – Parameter estimation (SA_phase5_estimate_two.m)
% SA_phase5_estimate_two.m
% Phase 5: estimate delays and gains from r_xx(l)
clc; clear; close all;
S = load('SA_x_two_echoes.mat');
x = S.x(:);
fs = S.fs;
% x, fs, k1_sec, k2_sec
[rxx, lags] = xcorr(x, 'biased');
[~, i0] = min(abs(lags));
rxx = rxx / rxx(i0);
lags_sec = lags / fs;
lagWin = 15;
idxWin = abs(lags_sec) <= lagWin;
rplot = rxx;
if numel(rplot) > 2000
rplot = movmean(rplot, 25);
end
pos = lags_sec >= 0 & lags_sec <= lagWin;
[pkAll, locAll] = findpeaks(rplot(pos), lags_sec(pos), ...
'MinPeakDistance', 0.8, 'MinPeakHeight', 0.05);
nz = locAll > 1e-3;
pkAll = pkAll(nz);
locAll = locAll(nz);
[pkS, ord] = sort(pkAll, 'descend');
locS = locAll(ord);
K = min(3, numel(locS));
locS = locS(1:K);
pkS = pkS(1:K);
[loc3, ordT] = sort(locS, 'ascend');
pk3 = pkS(ordT);
if numel(loc3) == 1
error('Only one peak found. Re-run Phase 4 with lower MinPeakHeight or
increase lagWin.');
15
elseif numel(loc3) == 2
k1_hat_sec = loc3(1);
k2_hat_sec = loc3(2);
else
a = loc3(1); b = loc3(2); c = loc3(3);
if abs((c - a) - b) < abs((c - b) - a)
k1_hat_sec = a; diff_hat_sec = b; k2_hat_sec = c;
else
k1_hat_sec = b; diff_hat_sec = a; k2_hat_sec = c;
end
end
if exist('diff_hat_sec','var')==0
diff_hat_sec = abs(k2_hat_sec - k1_hat_sec);
end
if k1_hat_sec > k2_hat_sec
tmp = k1_hat_sec; k1_hat_sec = k2_hat_sec; k2_hat_sec = tmp;
end
[~, i1] = min(abs(lags_sec - k1_hat_sec));
[~, i2] = min(abs(lags_sec - k2_hat_sec));
r1_hat = rxx(i1);
r2_hat = rxx(i2);
figure;
plot(lags_sec(idxWin), rplot(idxWin)); grid on;
xlabel('Lag (s)'); ylabel('Normalized r_{xx}');
title('Autocorrelation with estimated echo lags');
hold on;
stem([k1_hat_sec k2_hat_sec], [rxx(i1) rxx(i2)], 'filled');
text(k1_hat_sec, rxx(i1), sprintf(' k1~ %.2f s, r1~ %.2f', k1_hat_sec, r1_hat),
'VerticalAlignment','bottom');
text(k2_hat_sec, rxx(i2), sprintf(' k2~ %.2f s, r2~ %.2f', k2_hat_sec, r2_hat),
'VerticalAlignment','bottom');
saveas(gcf, 'SA_rxx_two_echoes_estimates.png');
T = table(k1_hat_sec, k2_hat_sec, diff_hat_sec, r1_hat, r2_hat, ...
'VariableNames',
{'k1_sec','k2_sec','k2_minus_k1_sec','r1_hat','r2_hat'});
writetable(T, 'SA_estimates_two_echoes.csv');
fprintf('\nEstimates:\n');
fprintf(' k1_hat ≈ %.3f s\n', k1_hat_sec);
fprintf(' k2_hat ≈ %.3f s\n', k2_hat_sec);
fprintf(' |k2-k1|_hat ≈ %.3f s\n', diff_hat_sec);
fprintf(' r1_hat ≈ %.3f\n', r1_hat);
fprintf(' r2_hat ≈ %.3f\n', r2_hat);
16
disp('Saved: SA_rxx_two_echoes_estimates.png,
SA_estimates_two_echoes.csv');
Phase 6 – Single echo case (SA_phase6_one_echo.m)
% SA_phase6_one_echo.m
% Phase 6: x1(n) = s(n) + 0.6 s(n-5s); r2=0 case. Autocorr + estimates.
clc; clear; close all;
load('SA_s_clean.mat','s','fs');
s = s(:);
r1 = 0.6; k1_sec = 5;
k1 = round(k1_sec*fs);
h1 = zeros(k1+1,1);
h1(1) = 1;
h1(k1+1) = r1;
x1 = conv(s, h1, 'full');
t1 = (0:numel(x1)-1)/fs;
peak = max(abs(x1));
if peak > 0.99
x1 = 0.98 * x1 / peak;
end
save('SA_x_one_echo.mat','x1','fs','r1','k1','k1_sec');
audiowrite('SA_x_one_echo.wav', x1, fs, 'BitsPerSample', 16);
figure;
plot(t1, x1); grid on;
xlabel('Time (s)'); ylabel('Amplitude');
title('x1(n) with single echo at 5 s');
xline(k1_sec,'--','k1');
saveas(gcf,'SA_x1_time.png');
[rxx1, lags1] = xcorr(x1, 'biased');
[~, i0] = min(abs(lags1));
rxx1 = rxx1 / rxx1(i0);
lags1_sec = lags1 / fs;
lagWin = 10;
idx = abs(lags1_sec) <= lagWin;
17
figure;
plot(lags1_sec(idx), rxx1(idx)); grid on;
xlabel('Lag (s)'); ylabel('Normalized r_{x1x1}');
title('Autocorrelation of x1(n) — single echo');
saveas(gcf,'SA_rxx_one_echo.png');
pos = lags1_sec >= 0 & lags1_sec <= lagWin;
[pk1, loc1] = findpeaks(rxx1(pos), lags1_sec(pos), ...
'MinPeakDistance', 0.8, 'MinPeakHeight', 0.05);
nz = loc1 > 1e-3;
pk1 = pk1(nz); loc1 = loc1(nz);
[pkS, ord] = sort(pk1, 'descend');
locS = loc1(ord);
if ~isempty(locS)
k1_hat_sec = locS(1);
[~, i1] = min(abs(lags1_sec - k1_hat_sec));
r1_hat = rxx1(i1);
else
k1_hat_sec = NaN; r1_hat = NaN;
end
figure;
plot(lags1_sec(idx), rxx1(idx)); grid on; hold on;
if ~isnan(k1_hat_sec)
stem(k1_hat_sec, r1_hat, 'filled');
end
xlabel('Lag (s)'); ylabel('Normalized r_{x1x1}');
title('Autocorrelation with estimated single echo lag');
saveas(gcf,'SA_rxx_one_echo_estimate.png');
fprintf('\nSingle-echo estimates:\n');
fprintf(' k1_hat ≈ %.3f s\n', k1_hat_sec);
fprintf(' r1_hat ≈ %.3f\n', r1_hat);
disp('Saved: SA_x_one_echo.(mat|wav), SA_x1_time.png,
SA_rxx_one_echo.png, SA_rxx_one_echo_estimate.png');
18
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )