Mencari Jarak Suara Menggunakan DTW#
Dynamic Time Warping (DTW)#
Dynamic Time Warping (DTW) merupakan teknik untuk mengukur tingkat kesamaan antara dua deret waktu yang kemungkinan memiliki durasi berbeda. Dalam konteks pengenalan suara, DTW berperan dalam mencocokkan pola akustik dari dua sinyal suara meskipun kecepatan atau panjang pengucapannya tidak sama.
DTW beroperasi dengan mencari lintasan penyesuaian (warping path) yang paling optimal di antara dua rangkaian fitur, contohnya fitur MFCC dari sampel audio dan audio input baru. Lintasan ini dipilih agar total jarak (biaya) antara kedua rangkaian menjadi minimum.
Inti Kerja DTW#
Perhitungan Jarak Lokal - Menghitung jarak antara setiap pasangan elemen fitur dari dua sinyal
Penyusunan Matriks Biaya Kumulatif - Membangun matriks menggunakan pemrograman dinamis
Pencarian Jalur Optimal - Menemukan lintasan melalui matriks yang meminimalkan total biaya
Pengambilan Nilai Akhir - Menggunakan nilai akhir sebagai jarak DTW
Semakin rendah nilai DTW yang diperoleh, semakin tinggi kemiripan antara kedua pola suara tersebut.
Suara Buka#
import os
import os
import librosa
import librosa.display
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import IPython.display as ipd
sns.set(style="whitegrid")
plt.rcParams['figure.figsize'] = (10, 4)
DATASET_PATH = "gabunganaudio"
train_path = os.path.join(DATASET_PATH, "train")
Visualisasi Suara Waveforms#
def plot_all_tutup_timeseries(base_path, max_files=None):
tutup_path = os.path.join(base_path, "buka")
tutup_files = os.listdir(tutup_path)
if max_files:
tutup_files = tutup_files[:max_files]
n_files = len(tutup_files)
cols = 2
rows = (n_files + cols - 1) // cols
fig, axes = plt.subplots(rows, cols, figsize=(15, 3*rows))
if rows == 1:
axes = axes.reshape(1, -1)
for i, filename in enumerate(tutup_files):
file_path = os.path.join(tutup_path, filename)
# Load audio
y, sr = librosa.load(file_path, sr=None)
# Hitung time axis
time = np.linspace(0, len(y)/sr, len(y))
# Plot
row = i // cols
col = i % cols
axes[row, col].plot(time, y)
axes[row, col].set_title(f'Buka - {filename}', fontsize=10)
axes[row, col].set_xlabel('Waktu (detik)')
axes[row, col].set_ylabel('Amplitude')
axes[row, col].grid(True, alpha=0.3)
for i in range(len(tutup_files), rows * cols):
row = i // cols
col = i % cols
fig.delaxes(axes[row, col])
plt.tight_layout()
plt.show()
print(f"Menampilkan {len(tutup_files)} file audio 'buka'")
plot_all_tutup_timeseries(train_path, max_files=10)
Menampilkan 10 file audio 'buka'
Perhitungan Dynamic Time Warping#
import numpy as np
from dtaidistance import dtw
import pandas as pd
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import seaborn as sns
# def extract_features_for_dtw(audio_path, feature_type='mfcc'):
# y, sr = librosa.load(audio_path, sr=22050)
# mel_spec = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128)
# mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)
# chroma = librosa.feature.chroma_stft(y=y, sr=sr)
# mel_6_mean = np.mean(mel_spec_db[6, :])
# mel_6_std = np.std(mel_spec_db[6, :])
# mel_7_mean = np.mean(mel_spec_db[7, :])
# mel_7_std = np.std(mel_spec_db[7, :])
# chroma_0_mean = np.mean(chroma[0, :])
# from scipy.stats import skew
# stat_skew = skew(y)
# model_features = np.array([
# mel_7_std,
# chroma_0_mean,
# mel_6_std,
# stat_skew,
# mel_7_mean
# ])
# return model_features
def extract_features_for_dtw(audio_path, n_mfcc=13):
y, sr = librosa.load(audio_path, sr=22050)
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=n_mfcc)
seq = mfcc[0, :]
return seq
def calculate_dtw_distances(new_audio_path, train_path, max_files=100):
try:
# new_features = extract_features_for_dtw(new_audio_path, 'mfcc')
new_features = extract_features_for_dtw(new_audio_path)
except Exception as e:
print(f"Errorrrrrrr")
return None
tutup_train_files = []
train_labels = []
tutup_path = os.path.join(train_path, "buka")
if os.path.exists(tutup_path):
for file in os.listdir(tutup_path):
if file.endswith(('.wav')):
tutup_train_files.append(os.path.join(tutup_path, file))
train_labels.append('buka')
if len(tutup_train_files) > max_files:
indices = np.random.choice(len(tutup_train_files), max_files, replace=False)
tutup_train_files = [tutup_train_files[i] for i in indices]
train_labels = [train_labels[i] for i in indices]
results = []
for i, (train_file, label) in enumerate(zip(tutup_train_files, train_labels)):
try:
# Ekstrak fitur dari file training
# train_features = extract_features_for_dtw(train_file, 'mfcc')
train_features = extract_features_for_dtw(train_file)
# Hitung jarak DTW
dtw_distance = dtw.distance(new_features, train_features)
results.append({
'rank': i + 1,
'filename': os.path.basename(train_file),
'label': label,
'dtw_distance': dtw_distance,
'file_path': train_file
})
except Exception as e:
print(f" Error processing {train_file}: {e}")
continue
df_results = pd.DataFrame(results)
df_results = df_results.sort_values('dtw_distance').reset_index(drop=True)
df_results['rank'] = range(1, len(df_results) + 1)
return df_results
# Path audio baru
new_audio_path = "gabunganaudio/train/bukanew.wav"
# Hitung jarak DTW
dtw_results = calculate_dtw_distances(new_audio_path, train_path, max_files=100)
if dtw_results is not None:
print(f"\n=== RANKING JARAK DTW (Top 20) ===")
print(dtw_results.head(20).to_string(index=False))
print(f"\n=== STATISTIK HASIL ===")
print(f"Total file dianalisis: {len(dtw_results)}")
print(f"Jarak minimum: {dtw_results['dtw_distance'].min():.4f}")
print(f"Jarak maksimum: {dtw_results['dtw_distance'].max():.4f}")
print(f"Jarak rata-rata: {dtw_results['dtw_distance'].mean():.4f}")
=== RANKING JARAK DTW (Top 20) ===
rank filename label dtw_distance file_path
1 buka_11_aug_shift_23.wav buka 382.517769 gabunganaudio/train/buka/buka_11_aug_shift_23.wav
2 buka_11_aug_noise_3.wav buka 392.582834 gabunganaudio/train/buka/buka_11_aug_noise_3.wav
3 buka_11.wav buka 447.051853 gabunganaudio/train/buka/buka_11.wav
4 buka_2_aug_noise_28.wav buka 466.331410 gabunganaudio/train/buka/buka_2_aug_noise_28.wav
5 Buka37.wav buka 483.874080 gabunganaudio/train/buka/Buka37.wav
6 buka_18.wav buka 499.659337 gabunganaudio/train/buka/buka_18.wav
7 buka_18_aug_noise_14.wav buka 499.782593 gabunganaudio/train/buka/buka_18_aug_noise_14.wav
8 buka_12.wav buka 510.688230 gabunganaudio/train/buka/buka_12.wav
9 buka_19.wav buka 519.059877 gabunganaudio/train/buka/buka_19.wav
10 Buka11.wav buka 522.181213 gabunganaudio/train/buka/Buka11.wav
11 buka_16.wav buka 525.251487 gabunganaudio/train/buka/buka_16.wav
12 buka_15_aug_noise_24.wav buka 535.851309 gabunganaudio/train/buka/buka_15_aug_noise_24.wav
13 buka_5_aug_noise_9.wav buka 541.601878 gabunganaudio/train/buka/buka_5_aug_noise_9.wav
14 buka_13.wav buka 545.225011 gabunganaudio/train/buka/buka_13.wav
15 buka_17.wav buka 548.849905 gabunganaudio/train/buka/buka_17.wav
16 buka_15.wav buka 549.044824 gabunganaudio/train/buka/buka_15.wav
17 buka_19_aug_speed_7.wav buka 565.323110 gabunganaudio/train/buka/buka_19_aug_speed_7.wav
18 buka_2.wav buka 570.292140 gabunganaudio/train/buka/buka_2.wav
19 buka_1.wav buka 571.017403 gabunganaudio/train/buka/buka_1.wav
20 buka_7.wav buka 571.789324 gabunganaudio/train/buka/buka_7.wav
=== STATISTIK HASIL ===
Total file dianalisis: 100
Jarak minimum: 382.5178
Jarak maksimum: 2314.7170
Jarak rata-rata: 949.0564
Perangkingan dan Perbandingan#
def visualize_dtw_results(dtw_results, new_audio_path):
fig, ax = plt.subplots(1, 1, figsize=(15, 8))
# Top 20 ranking
top_20 = dtw_results.head(20)
colors = ['red' if label == 'tutup' else 'blue' for label in top_20['label']]
ax.barh(range(len(top_20)), top_20['dtw_distance'], color=colors, alpha=0.7)
ax.set_yticks(range(len(top_20)))
ax.set_yticklabels([f"{row['rank']}-{row['filename'][:15]}..."
for _, row in top_20.iterrows()], fontsize=8)
ax.set_xlabel('DTW Distance')
ax.set_title('Top 20 Terdekat (Merah=Tutup, Biru=Buka)')
ax.invert_yaxis()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Waveform comparison
print("\n=== PERBANDINGAN WAVEFORM ===")
plot_waveform_comparison(new_audio_path, dtw_results.head(5))
def plot_waveform_comparison(new_audio_path, top_matches):
fig, axes = plt.subplots(3, 2, figsize=(15, 12))
# Audio baru
y_new, sr_new = librosa.load(new_audio_path, sr=22050)
time_new = np.linspace(0, len(y_new)/sr_new, len(y_new))
axes[0, 0].plot(time_new, y_new, 'g-', linewidth=1)
axes[0, 0].set_title(f'Audio Baru: {os.path.basename(new_audio_path)}')
axes[0, 0].set_xlabel('Time (s)')
axes[0, 0].set_ylabel('Amplitude')
axes[0, 0].grid(True, alpha=0.3)
for i, (_, row) in enumerate(top_matches.iterrows()):
if i >= 5:
break
try:
y_match, sr_match = librosa.load(row['file_path'], sr=22050)
time_match = np.linspace(0, len(y_match)/sr_match, len(y_match))
row_idx = (i + 1) // 2
col_idx = (i + 1) % 2
if row_idx < 3:
color = 'red' if row['label'] == 'tutup' else 'blue'
axes[row_idx, col_idx].plot(time_match, y_match, color=color, linewidth=1)
axes[row_idx, col_idx].set_title(f'Rank {row["rank"]}: {row["filename"][:20]}...\nLabel: {row["label"]}, Distance: {row["dtw_distance"]:.4f}')
axes[row_idx, col_idx].set_xlabel('Time (s)')
axes[row_idx, col_idx].set_ylabel('Amplitude')
axes[row_idx, col_idx].grid(True, alpha=0.3)
except Exception as e:
print(f"Error loading {row['filename']}: {e}")
continue
axes[0, 1].axis('off')
plt.tight_layout()
plt.show()
if dtw_results is not None:
visualize_dtw_results(dtw_results, new_audio_path)
print(f"\n=== SEMUA RANKING DTW (1-{len(dtw_results)}) ===")
print(dtw_results[['rank', 'filename', 'label', 'dtw_distance']].to_string(index=False))
=== PERBANDINGAN WAVEFORM ===
=== SEMUA RANKING DTW (1-100) ===
rank filename label dtw_distance
1 buka_11_aug_shift_23.wav buka 382.517769
2 buka_11_aug_noise_3.wav buka 392.582834
3 buka_11.wav buka 447.051853
4 buka_2_aug_noise_28.wav buka 466.331410
5 Buka37.wav buka 483.874080
6 buka_18.wav buka 499.659337
7 buka_18_aug_noise_14.wav buka 499.782593
8 buka_12.wav buka 510.688230
9 buka_19.wav buka 519.059877
10 Buka11.wav buka 522.181213
11 buka_16.wav buka 525.251487
12 buka_15_aug_noise_24.wav buka 535.851309
13 buka_5_aug_noise_9.wav buka 541.601878
14 buka_13.wav buka 545.225011
15 buka_17.wav buka 548.849905
16 buka_15.wav buka 549.044824
17 buka_19_aug_speed_7.wav buka 565.323110
18 buka_2.wav buka 570.292140
19 buka_1.wav buka 571.017403
20 buka_7.wav buka 571.789324
21 buka_17_aug_speed_30.wav buka 580.075830
22 buka_17_aug_speed_10.wav buka 586.556769
23 buka_12_aug_shift_11.wav buka 586.733915
24 buka_14.wav buka 589.899303
25 Buka10.wav buka 615.295331
26 buka_8.wav buka 619.306442
27 buka_4_aug_shift_1.wav buka 622.727368
28 buka_13_aug_pitch_25.wav buka 622.958089
29 buka_3.wav buka 626.962319
30 buka_2_aug_pitch_8.wav buka 629.954066
31 buka_19_aug_speed_27.wav buka 631.396567
32 Buka39.wav buka 634.502044
33 Buka38.wav buka 639.834207
34 buka_7_aug_pitch_15.wav buka 640.230720
35 Buka48.wav buka 646.568539
36 buka_14_aug_speed_6.wav buka 650.918150
37 buka_13_aug_speed_5.wav buka 652.066091
38 buka_16_aug_pitch_17.wav buka 652.706193
39 Buka46.wav buka 664.676002
40 buka_14_aug_speed_26.wav buka 666.042135
41 buka_6.wav buka 667.596412
42 buka_15_aug_speed_4.wav buka 695.282047
43 buka_5.wav buka 706.296923
44 buka_4.wav buka 717.224316
45 buka_8_aug_shift_13.wav buka 735.082648
46 Buka18.wav buka 755.280784
47 buka_5_aug_pitch_29.wav buka 758.338274
48 Buka3.wav buka 784.932362
49 buka_4_aug_pitch_21.wav buka 789.061468
50 buka_1_aug_shift_18.wav buka 790.622342
51 buka_3_aug_shift_19.wav buka 795.142165
52 buka_9.wav buka 797.292528
53 buka_9_aug_noise_16.wav buka 797.325765
54 Buka12.wav buka 808.111340
55 Buka28.wav buka 826.630019
56 Buka24.wav buka 829.714409
57 Buka5.wav buka 851.459592
58 buka_6_aug_speed_12.wav buka 852.683595
59 Buka20.wav buka 865.154936
60 Buka17.wav buka 891.680261
61 Buka47.wav buka 933.133364
62 Buka25.wav buka 936.005609
63 Buka29.wav buka 951.142077
64 Buka50.wav buka 957.367778
65 Buka40.wav buka 1006.572650
66 buka_20_aug_shift_2.wav buka 1068.996609
67 buka_20.wav buka 1100.263264
68 Buka2.wav buka 1131.363116
69 Buka13.wav buka 1140.365950
70 Buka8.wav buka 1150.754318
71 Buka23.wav buka 1176.041613
72 Buka45.wav buka 1230.536925
73 Buka35.wav buka 1253.777293
74 Buka14.wav buka 1264.903455
75 Buka31.wav buka 1274.017661
76 Buka6.wav buka 1276.810381
77 Buka1.wav buka 1326.218779
78 Buka1 copy.wav buka 1326.218779
79 Buka41.wav buka 1341.496739
80 Buka34.wav buka 1377.812397
81 Buka36.wav buka 1401.549410
82 Buka15.wav buka 1416.006797
83 Buka9.wav buka 1441.992545
84 Buka7.wav buka 1509.431847
85 Buka21.wav buka 1519.238707
86 Buka22.wav buka 1519.238707
87 buka_10_aug_speed_20.wav buka 1538.938758
88 Buka19.wav buka 1548.983053
89 Buka43.wav buka 1550.959703
90 buka_10.wav buka 1558.685183
91 Buka32.wav buka 1562.079063
92 Buka42.wav buka 1593.204554
93 Buka30.wav buka 1634.844182
94 Buka49.wav buka 1641.325760
95 Buka44.wav buka 1727.224942
96 Buka16.wav buka 1733.980320
97 Buka33.wav buka 1847.588090
98 Buka4.wav buka 1897.660731
99 Buka26.wav buka 2201.897137
100 Buka27.wav buka 2314.717045
Suara Tutup#
import os
import os
import librosa
import librosa.display
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import IPython.display as ipd
sns.set(style="whitegrid")
plt.rcParams['figure.figsize'] = (10, 4)
DATASET_PATH = "gabunganaudio"
train_path = os.path.join(DATASET_PATH, "train")
Visualisasi Suara Waveforms#
def plot_all_tutup_timeseries(base_path, max_files=None):
tutup_path = os.path.join(base_path, "tutup")
tutup_files = os.listdir(tutup_path)
if max_files:
tutup_files = tutup_files[:max_files]
n_files = len(tutup_files)
cols = 2
rows = (n_files + cols - 1) // cols
fig, axes = plt.subplots(rows, cols, figsize=(15, 3*rows))
if rows == 1:
axes = axes.reshape(1, -1)
for i, filename in enumerate(tutup_files):
file_path = os.path.join(tutup_path, filename)
# Load audio
y, sr = librosa.load(file_path, sr=None)
# Hitung time axis
time = np.linspace(0, len(y)/sr, len(y))
# Plot
row = i // cols
col = i % cols
axes[row, col].plot(time, y)
axes[row, col].set_title(f'Tutup - {filename}', fontsize=10)
axes[row, col].set_xlabel('Waktu (detik)')
axes[row, col].set_ylabel('Amplitude')
axes[row, col].grid(True, alpha=0.3)
for i in range(len(tutup_files), rows * cols):
row = i // cols
col = i % cols
fig.delaxes(axes[row, col])
plt.tight_layout()
plt.show()
print(f"Menampilkan {len(tutup_files)} file audio 'tutup'")
plot_all_tutup_timeseries(train_path, max_files=10)
Menampilkan 10 file audio 'tutup'
Perhitungan Dynamic Time Warping#
import numpy as np
from dtaidistance import dtw
import pandas as pd
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import seaborn as sns
# def extract_features_for_dtw(audio_path, feature_type='mfcc'):
# y, sr = librosa.load(audio_path, sr=22050)
# mel_spec = librosa.feature.melspectrogram(y=y, sr=sr, n_mels=128)
# mel_spec_db = librosa.power_to_db(mel_spec, ref=np.max)
# chroma = librosa.feature.chroma_stft(y=y, sr=sr)
# mel_6_mean = np.mean(mel_spec_db[6, :])
# mel_6_std = np.std(mel_spec_db[6, :])
# mel_7_mean = np.mean(mel_spec_db[7, :])
# mel_7_std = np.std(mel_spec_db[7, :])
# chroma_0_mean = np.mean(chroma[0, :])
# from scipy.stats import skew
# stat_skew = skew(y)
# model_features = np.array([
# mel_7_std,
# chroma_0_mean,
# mel_6_std,
# stat_skew,
# mel_7_mean
# ])
# return model_features
def extract_features_for_dtw(audio_path, n_mfcc=13):
y, sr = librosa.load(audio_path, sr=22050)
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=n_mfcc)
seq = mfcc[0, :]
return seq
def calculate_dtw_distances(new_audio_path, train_path, max_files=100):
try:
# new_features = extract_features_for_dtw(new_audio_path, 'mfcc')
new_features = extract_features_for_dtw(new_audio_path)
except Exception as e:
print(f"Errorrrrrrr")
return None
tutup_train_files = []
train_labels = []
tutup_path = os.path.join(train_path, "tutup")
if os.path.exists(tutup_path):
for file in os.listdir(tutup_path):
if file.endswith(('.wav')):
tutup_train_files.append(os.path.join(tutup_path, file))
train_labels.append('tutup')
if len(tutup_train_files) > max_files:
indices = np.random.choice(len(tutup_train_files), max_files, replace=False)
tutup_train_files = [tutup_train_files[i] for i in indices]
train_labels = [train_labels[i] for i in indices]
results = []
for i, (train_file, label) in enumerate(zip(tutup_train_files, train_labels)):
try:
# Ekstrak fitur dari file training
# train_features = extract_features_for_dtw(train_file, 'mfcc')
train_features = extract_features_for_dtw(train_file)
# Hitung jarak DTW
dtw_distance = dtw.distance(new_features, train_features)
results.append({
'rank': i + 1,
'filename': os.path.basename(train_file),
'label': label,
'dtw_distance': dtw_distance,
'file_path': train_file
})
except Exception as e:
print(f" Error processing {train_file}: {e}")
continue
df_results = pd.DataFrame(results)
df_results = df_results.sort_values('dtw_distance').reset_index(drop=True)
df_results['rank'] = range(1, len(df_results) + 1)
return df_results
# Path audio baru
new_audio_path = "gabunganaudio/train/tutupnew.wav"
# Hitung jarak DTW
dtw_results = calculate_dtw_distances(new_audio_path, train_path, max_files=100)
if dtw_results is not None:
print(f"\n=== RANKING JARAK DTW (Top 20) ===")
print(dtw_results.head(20).to_string(index=False))
print(f"\n=== STATISTIK HASIL ===")
print(f"Total file dianalisis: {len(dtw_results)}")
print(f"Jarak minimum: {dtw_results['dtw_distance'].min():.4f}")
print(f"Jarak maksimum: {dtw_results['dtw_distance'].max():.4f}")
print(f"Jarak rata-rata: {dtw_results['dtw_distance'].mean():.4f}")
=== RANKING JARAK DTW (Top 20) ===
rank filename label dtw_distance file_path
1 Tutup20.wav tutup 0.000000 gabunganaudio/train/tutup/Tutup20.wav
2 Tutup20_aug_shift_13.wav tutup 30.815539 gabunganaudio/train/tutup/Tutup20_aug_shift_13.wav
3 Tutup20_aug_shift_10.wav tutup 106.067127 gabunganaudio/train/tutup/Tutup20_aug_shift_10.wav
4 Tutup10.wav tutup 187.487968 gabunganaudio/train/tutup/Tutup10.wav
5 Tutup7_aug_noise_29.wav tutup 192.054720 gabunganaudio/train/tutup/Tutup7_aug_noise_29.wav
6 Tutup6.wav tutup 192.945325 gabunganaudio/train/tutup/Tutup6.wav
7 Tutup9_aug_noise_26.wav tutup 219.962346 gabunganaudio/train/tutup/Tutup9_aug_noise_26.wav
8 Tutup19.wav tutup 232.378244 gabunganaudio/train/tutup/Tutup19.wav
9 Tutup16.wav tutup 234.219749 gabunganaudio/train/tutup/Tutup16.wav
10 Tutup18_aug_speed_11.wav tutup 236.572515 gabunganaudio/train/tutup/Tutup18_aug_speed_11.wav
11 Tutup14.wav tutup 238.276576 gabunganaudio/train/tutup/Tutup14.wav
12 Tutup18_aug_pitch_1.wav tutup 244.708348 gabunganaudio/train/tutup/Tutup18_aug_pitch_1.wav
13 Tutup15.wav tutup 251.552345 gabunganaudio/train/tutup/Tutup15.wav
14 Tutup3.wav tutup 258.211003 gabunganaudio/train/tutup/Tutup3.wav
15 Tutup19_aug_speed_22.wav tutup 261.896002 gabunganaudio/train/tutup/Tutup19_aug_speed_22.wav
16 Tutup7.wav tutup 271.630839 gabunganaudio/train/tutup/Tutup7.wav
17 Tutup5.wav tutup 275.792622 gabunganaudio/train/tutup/Tutup5.wav
18 Tutup19_aug_pitch_4.wav tutup 283.493676 gabunganaudio/train/tutup/Tutup19_aug_pitch_4.wav
19 Tutup8.wav tutup 293.069411 gabunganaudio/train/tutup/Tutup8.wav
20 Tutup8_aug_noise_23.wav tutup 306.586397 gabunganaudio/train/tutup/Tutup8_aug_noise_23.wav
=== STATISTIK HASIL ===
Total file dianalisis: 50
Jarak minimum: 0.0000
Jarak maksimum: 1123.6811
Jarak rata-rata: 379.2039
Perangkingan dan Perbandingan#
def visualize_dtw_results(dtw_results, new_audio_path):
fig, ax = plt.subplots(1, 1, figsize=(15, 8))
# Top 20 ranking
top_20 = dtw_results.head(20)
colors = ['red' if label == 'tutup' else 'blue' for label in top_20['label']]
ax.barh(range(len(top_20)), top_20['dtw_distance'], color=colors, alpha=0.7)
ax.set_yticks(range(len(top_20)))
ax.set_yticklabels([f"{row['rank']}-{row['filename'][:15]}..."
for _, row in top_20.iterrows()], fontsize=8)
ax.set_xlabel('DTW Distance')
ax.set_title('Top 20 Terdekat (Merah=Tutup, Biru=Buka)')
ax.invert_yaxis()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print("\n=== PERBANDINGAN WAVEFORM ===")
plot_waveform_comparison(new_audio_path, dtw_results.head(5))
def plot_waveform_comparison(new_audio_path, top_matches):
fig, axes = plt.subplots(3, 2, figsize=(15, 12))
# Audio baru
y_new, sr_new = librosa.load(new_audio_path, sr=22050)
time_new = np.linspace(0, len(y_new)/sr_new, len(y_new))
axes[0, 0].plot(time_new, y_new, 'g-', linewidth=1)
axes[0, 0].set_title(f'Audio Baru: {os.path.basename(new_audio_path)}')
axes[0, 0].set_xlabel('Time (s)')
axes[0, 0].set_ylabel('Amplitude')
axes[0, 0].grid(True, alpha=0.3)
for i, (_, row) in enumerate(top_matches.iterrows()):
if i >= 5:
break
try:
y_match, sr_match = librosa.load(row['file_path'], sr=22050)
time_match = np.linspace(0, len(y_match)/sr_match, len(y_match))
row_idx = (i + 1) // 2
col_idx = (i + 1) % 2
if row_idx < 3:
color = 'red' if row['label'] == 'tutup' else 'blue'
axes[row_idx, col_idx].plot(time_match, y_match, color=color, linewidth=1)
axes[row_idx, col_idx].set_title(f'Rank {row["rank"]}: {row["filename"][:20]}...\nLabel: {row["label"]}, Distance: {row["dtw_distance"]:.4f}')
axes[row_idx, col_idx].set_xlabel('Time (s)')
axes[row_idx, col_idx].set_ylabel('Amplitude')
axes[row_idx, col_idx].grid(True, alpha=0.3)
except Exception as e:
print(f"Error loading {row['filename']}: {e}")
continue
axes[0, 1].axis('off')
plt.tight_layout()
plt.show()
if dtw_results is not None:
visualize_dtw_results(dtw_results, new_audio_path)
print(f"\n=== SEMUA RANKING DTW (1-{len(dtw_results)}) ===")
print(dtw_results[['rank', 'filename', 'label', 'dtw_distance']].to_string(index=False))
=== PERBANDINGAN WAVEFORM ===
=== SEMUA RANKING DTW (1-50) ===
rank filename label dtw_distance
1 Tutup20.wav tutup 0.000000
2 Tutup20_aug_shift_13.wav tutup 30.815539
3 Tutup20_aug_shift_10.wav tutup 106.067127
4 Tutup10.wav tutup 187.487968
5 Tutup7_aug_noise_29.wav tutup 192.054720
6 Tutup6.wav tutup 192.945325
7 Tutup9_aug_noise_26.wav tutup 219.962346
8 Tutup19.wav tutup 232.378244
9 Tutup16.wav tutup 234.219749
10 Tutup18_aug_speed_11.wav tutup 236.572515
11 Tutup14.wav tutup 238.276576
12 Tutup18_aug_pitch_1.wav tutup 244.708348
13 Tutup15.wav tutup 251.552345
14 Tutup3.wav tutup 258.211003
15 Tutup19_aug_speed_22.wav tutup 261.896002
16 Tutup7.wav tutup 271.630839
17 Tutup5.wav tutup 275.792622
18 Tutup19_aug_pitch_4.wav tutup 283.493676
19 Tutup8.wav tutup 293.069411
20 Tutup8_aug_noise_23.wav tutup 306.586397
21 Tutup4.wav tutup 311.507674
22 Tutup8_aug_shift_30.wav tutup 312.747102
23 Tutup9_aug_noise_3.wav tutup 320.994061
24 Tutup9.wav tutup 322.041195
25 Tutup13.wav tutup 325.722106
26 Tutup4_aug_shift_6.wav tutup 330.671098
27 Tutup11_aug_shift_2.wav tutup 337.190101
28 Tutup10_aug_pitch_15.wav tutup 347.444139
29 Tutup17_aug_pitch_9.wav tutup 351.177295
30 Tutup11.wav tutup 365.077903
31 Tutup18.wav tutup 368.382189
32 Tutup5_aug_speed_7.wav tutup 371.252146
33 Tutup13_aug_speed_27.wav tutup 382.481209
34 Tutup16_aug_pitch_21.wav tutup 440.627500
35 Tutup5_aug_pitch_18.wav tutup 442.417012
36 Tutup15_aug_speed_16.wav tutup 442.877805
37 Tutup1.wav tutup 445.813372
38 Tutup14_aug_speed_17.wav tutup 455.619479
39 Tutup17.wav tutup 474.272733
40 Tutup2_aug_shift_20.wav tutup 484.671957
41 Tutup15_aug_speed_19.wav tutup 501.015594
42 Tutup2.wav tutup 512.588529
43 Tutup12_aug_pitch_8.wav tutup 533.748156
44 Tutup12.wav tutup 535.914172
45 Tutup17_aug_shift_5.wav tutup 554.431776
46 Tutup2_aug_speed_14.wav tutup 588.016077
47 Tutup1_aug_noise_28.wav tutup 638.567122
48 Tutup4_aug_noise_25.wav tutup 927.430186
49 Tutup11_aug_noise_12.wav tutup 1094.094089
50 Tutup12_aug_noise_24.wav tutup 1123.681060