File size: 5,179 Bytes
9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 1134cac 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 1134cac 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 9fcaeae 1a2f7a1 09dbf8a 1a2f7a1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | import streamlit as st
import numpy as np
import librosa
import os
from sklearn.metrics.pairwise import cosine_similarity
# Tidak perlu import soundfile, librosa akan menggunakannya secara otomatis
# --- PATH KONFIGURASI ---
# Menggunakan path absolut untuk menemukan folder 'enroll'
APP_DIR = os.path.dirname(os.path.abspath(__file__))
ENROLL_DIR = os.path.join(APP_DIR, "enroll")
# ------------------------------
# Ekstraksi fitur suara (MFCC)
# ------------------------------
def extract_features(audio_source):
"""
Kombinasi: Fungsi ini dari skrip Anda,
tapi dimodifikasi untuk menerima 'audio_source' (file-like object)
agar kita tidak perlu file temporer.
"""
try:
# librosa.load bisa membaca file-like object dari st.uploader
y, sr = librosa.load(audio_source, sr=None)
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=20)
return np.mean(mfcc.T, axis=0)
except Exception as e:
st.error(f"Gagal ekstrak fitur: {e}")
return None
# ------------------------------
# Verifikasi identitas pengguna
# ------------------------------
def verify_user(audio_source, enroll_dir):
"""
Logika dari skrip Anda, dikombinasikan dengan path yang lebih
kuat (ENROLL_DIR) dan fungsi 'extract_features' yang
sudah dimodifikasi.
"""
test_feat = extract_features(audio_source)
if test_feat is None:
return None, 0.0
similarities = {}
if not os.path.isdir(enroll_dir):
st.error(f"Folder 'enroll' tidak ditemukan di: {enroll_dir}")
return None, 0.0
for user in os.listdir(enroll_dir):
user_dir = os.path.join(enroll_dir, user)
if not os.path.isdir(user_dir):
continue
scores = []
for file in os.listdir(user_dir):
if file.endswith(".wav"):
# Untuk file pendaftaran, kita gunakan file path (str)
file_path = os.path.join(user_dir, file)
feat = extract_features(file_path)
if feat is not None:
# Logika 'cosine_similarity' Anda
sim = cosine_similarity([test_feat], [feat])[0][0]
scores.append(sim)
if scores:
similarities[user] = np.mean(scores)
if not similarities:
st.error("Tidak ada data pendaftaran (enroll) yang ditemukan/diproses.")
return None, 0.0
best_user = max(similarities, key=similarities.get)
best_score = similarities[best_user]
return best_user, best_score
# ------------------------------
# Deteksi kata kunci (buka/tutup) - Logika Anda
# ------------------------------
def detect_command(audio_source):
"""
Ini adalah fungsi 'dummy' dari skrip Anda.
Logikanya (berdasarkan durasi) dipertahankan.
"""
try:
y, sr = librosa.load(audio_source, sr=None)
duration = librosa.get_duration(y=y, sr=sr)
# Logika placeholder dari kode Anda:
if duration < 1.0:
text = "buka"
else:
text = "tutup"
return text
except Exception as e:
# Kita sertakan nama file dalam error untuk debug
st.error(f"Gagal deteksi perintah: {e}")
return None
# ------------------------------
# UI Streamlit
# ------------------------------
st.title("π Sistem Verifikasi Suara - Perintah Buka/Tutup")
st.caption("Hanya pengguna terdaftar yang dapat memberikan perintah suara 'buka' atau 'tutup'.")
st.warning(
"""PERHATIAN: Deteksi perintah ('buka'/'tutup') saat ini
hanyalah **placeholder** berdasarkan durasi audio (audio pendek = 'buka',
audio panjang = 'tutup') dan **tidak akurat**.""",
icon="β οΈ"
)
uploaded_file = st.file_uploader("ποΈ Unggah suara (.wav)", type=["wav"])
if uploaded_file is not None:
# Kombinasi: Kita HAPUS 'tempfile' dan 'os.remove'.
# Kita gunakan 'uploaded_file' secara langsung.
st.audio(uploaded_file, format="audio/wav")
if st.button("Mulai Verifikasi"):
with st.spinner("Menganalisis suara..."):
# PANGGILAN PERTAMA (memindahkan cursor ke akhir)
user, score = verify_user(uploaded_file, ENROLL_DIR)
# Logika UI Anda
if user and score > 0.85:
st.success(f"β
Pengguna terdeteksi: **{user}** (skor {score:.2f})")
# === PERBAIKAN ===
# Kembalikan cursor file ke awal sebelum membacanya lagi
uploaded_file.seek(0)
# =================
# PANGGILAN KEDUA (sekarang berhasil)
cmd = detect_command(uploaded_file)
if cmd == "buka":
st.success("π’ Perintah terdeteksi: **BUKA** β Sistem terbuka.")
elif cmd == "tutup":
st.warning("π΄ Perintah terdeteksi: **TUTUP** β Sistem tertutup.")
else:
st.info("β οΈ Tidak dapat mengenali perintah.")
else:
st.error("π« Akses ditolak! Suara tidak dikenali.")
# Tidak ada 'os.remove()' lagi karena tidak ada file temporer |