Voice Recognition: Buka Tutup Classification#

Data Understanding#

1. Import Libraries#

Import library dasar untuk analisis data dan audio processing.

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)

Mempersiapkan lingkungan analisis dengan mengimpor berbagai library yang diperlukan, dimana librosa dijadikan sebagai library utama untuk menangani pemrosesan sinyal audio, sementara tools lainnya seperti pandas disiapkan untuk manipulasi data, numpy untuk komputasi numerik, serta matplotlib dan seaborn untuk keperluan visualisasi.

2. Setup Path Dataset#

Setup path dataset dan cek struktur folder datasetaudio.

import os

DATASET_PATH = "datasetaudio"
train_path = os.path.join(DATASET_PATH, "train")

if os.path.exists(DATASET_PATH):
    print("Dataset structure:")
    
    if os.path.exists(train_path):
        print("Train folder:", os.listdir(train_path))

        # Loop speaker
        for speaker in os.listdir(train_path):
            speaker_path = os.path.join(train_path, speaker)
            if os.path.isdir(speaker_path):
                print(f"  └── {speaker}/  -> subfolders:", os.listdir(speaker_path))

                # Loop action (buka/tutup)
                for action in os.listdir(speaker_path):
                    action_path = os.path.join(speaker_path, action)
                    if os.path.isdir(action_path):
                        files = [f for f in os.listdir(action_path) if f.lower().endswith((".wav", ".mp3", ".flac"))]
                        print(f"        └── {action}/  -> {len(files)} files")
    else:
        print("Train folder tidak ditemukan!")

else:
    print(f"Dataset path '{DATASET_PATH}' tidak ditemukan!")
    print("Current directory:", os.getcwd())
    print("Files in current directory:", os.listdir('.'))
Dataset structure:
Train folder: ['asep', 'yotan']
  └── asep/  -> subfolders: ['buka', 'tutup']
        └── buka/  -> 50 files
        └── tutup/  -> 50 files
  └── yotan/  -> subfolders: ['buka', 'tutup']
        └── buka/  -> 50 files
        └── tutup/  -> 50 files

3. Cek Distribusi Data#

Analisis distribusi file audio per kelas dan split data train/validation.

def count_files_new(base_path):
    counts = {}

    for speaker in os.listdir(base_path):
        speaker_path = os.path.join(base_path, speaker)
        if not os.path.isdir(speaker_path):
            continue

        for action in os.listdir(speaker_path):  # buka/tutup
            action_path = os.path.join(speaker_path, action)
            if not os.path.isdir(action_path):
                continue

            # hitung file audio
            num_files = len([
                f for f in os.listdir(action_path)
                if f.lower().endswith((".wav", ".mp3", ".flac"))
            ])

            key = f"{speaker}_{action}"  
            counts[key] = num_files

    return counts


train_counts = count_files_new(train_path)

df_counts = pd.DataFrame({
    'Label': list(train_counts.keys()),
    'Jumlah': list(train_counts.values())
})

plt.figure(figsize=(10, 6))
sns.barplot(data=df_counts, x='Label', y='Jumlah')
plt.title("Distribusi Data per Speaker & Kelas (buka/tutup)")
plt.ylabel("Jumlah File")
plt.xticks(rotation=45)
plt.show()

# Hitung total untuk buka dan tutup secara global
total_buka = sum([v for k, v in train_counts.items() if "buka" in k])
total_tutup = sum([v for k, v in train_counts.items() if "tutup" in k])

print(f"Total data - Buka : {total_buka}")
print(f"Total data - Tutup: {total_tutup}")
print(f"Total keseluruhan: {sum(train_counts.values())} file")
_images/f9d540634b55926a7875305c68a5e3db5ad1b63b4c4036676c5c4d514a4b3271.png
Total data - Buka : 100
Total data - Tutup: 100
Total keseluruhan: 200 file

4. Contoh Audio + Metadata#

Play sample audio dan visualisasi waveform + spectrogram untuk setiap kelas.

import os
from IPython.display import Audio, display

DATASET_PATH = "datasetaudio"
train_path = os.path.join(DATASET_PATH, "train")

# urutan manusia: ke-2 -> index 1 (Python zero-based)
TARGET_INDEX = 1

VALID_EXTS = (".wav", ".mp3", ".flac", ".ogg", ".m4a")

def get_nth_audio_file(speaker, action, n=TARGET_INDEX):
    folder = os.path.join(train_path, speaker, action)
    if not os.path.isdir(folder):
        return None, f"Folder tidak ditemukan: {folder}"
    files = sorted([f for f in os.listdir(folder) if f.lower().endswith(VALID_EXTS)])
    if len(files) == 0:
        return None, f"Tidak ada file di: {folder}"
    # jika kurang dari n+1 file, ambil file terakhir
    idx = n if n < len(files) else (len(files) - 1)
    return os.path.join(folder, files[idx]), None

speakers = ["asep", "yotan"]
actions = ["buka", "tutup"]

for sp in speakers:
    for act in actions:
        path, err = get_nth_audio_file(sp, act)
        print(f"\nContoh: {sp} - {act}")
        if err:
            print("  Error:", err)
            continue
        print("  File:", path)
        display(Audio(path))
Contoh: asep - buka
  File: datasetaudio/train/asep/buka/Buka10.wav
Contoh: asep - tutup
  File: datasetaudio/train/asep/tutup/Tutup10.wav
Contoh: yotan - buka
  File: datasetaudio/train/yotan/buka/buka_10.wav
Contoh: yotan - tutup
  File: datasetaudio/train/yotan/tutup/tutup_10.wav

5. Analisis Durasi dan Sample Rate#

Analisis statistik durasi audio dan konsistensi sample rate dataset.

import os
import librosa
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

DATASET_PATH = "datasetaudio"
train_path = os.path.join(DATASET_PATH, "train")

# -----------------------------------------
# 1. ANALISIS AUDIO (Struktur speaker/action)
# -----------------------------------------
def analyze_audio_stats_new(base_path):
    data = []

    for speaker in os.listdir(base_path):
        speaker_path = os.path.join(base_path, speaker)
        if not os.path.isdir(speaker_path):
            continue

        for action in os.listdir(speaker_path):  # buka/tutup
            action_path = os.path.join(speaker_path, action)
            if not os.path.isdir(action_path):
                continue

            for fn in os.listdir(action_path):
                fp = os.path.join(action_path, fn)

                if not fn.lower().endswith((".wav", ".mp3", ".flac", ".ogg", ".m4a")):
                    continue

                try:
                    y, sr = librosa.load(fp, sr=None)
                    duration = len(y) / sr
                    data.append({
                        'speaker': speaker,
                        'action': action,
                        'label': f"{speaker}_{action}",
                        'sr': sr,
                        'duration': duration,
                        'filename': fn
                    })
                except Exception as e:
                    print("Gagal load:", fp, "| Error:", e)

    return pd.DataFrame(data)

audio_stats = analyze_audio_stats_new(train_path)

# -----------------------------------------
# 2. STATISTIK PER LABEL
# -----------------------------------------
print("Statistik durasi per speaker-action:")
print(audio_stats.groupby('label')['duration'].describe())

# -----------------------------------------
# 3. PLOTTING
# -----------------------------------------
fig, axes = plt.subplots(1, 2, figsize=(16, 5))

# Histogram durasi berdasarkan label (speaker_action)
sns.histplot(data=audio_stats, x='duration', hue='label', bins=30, kde=True, ax=axes[0])
axes[0].set_title("Distribusi Durasi Audio per Speaker-Action")
axes[0].set_xlabel("Durasi (detik)")

# Jumlah file per label
sns.countplot(data=audio_stats, x='label', ax=axes[1])
axes[1].set_title("Jumlah File per Speaker-Action")
axes[1].set_xlabel("Label (speaker_action)")
axes[1].tick_params(axis='x', rotation=45)

plt.tight_layout()
plt.show()

# -----------------------------------------
# 4. Informasi tambahan
# -----------------------------------------
print(f"\nSample-rate unik   : {audio_stats['sr'].unique()}")
print(f"Total file audio   : {len(audio_stats)}")
print("\nJumlah per speaker-action:")
print(audio_stats['label'].value_counts())
Statistik durasi per speaker-action:
             count      mean       std       min    25%       50%       75%  \
label                                                                         
asep_buka     50.0  2.247140  0.515910  1.387312  1.704  2.304000  2.593969   
asep_tutup    50.0  2.307255  0.358984  1.365333  2.016  2.364000  2.487156   
yotan_buka    50.0  1.436443  0.467269  0.885000  1.020  1.400000  1.513000   
yotan_tutup   50.0  1.254245  0.350424  0.776458  0.928  1.291156  1.537500   

                  max  
label                  
asep_buka    3.280229  
asep_tutup   3.218292  
yotan_buka   2.802188  
yotan_tutup  1.889000  
_images/a49f33d720603e9d0a111c75c101d86bacb59e7efb6c1b5be7aebff546191808.png
Sample-rate unik   : [48000]
Total file audio   : 200

Jumlah per speaker-action:
label
asep_buka      50
asep_tutup     50
yotan_buka     50
yotan_tutup    50
Name: count, dtype: int64