sahakan's picture
Update app.py
07477a1 verified
import os
import numpy as np
import matplotlib.pyplot as plt
import streamlit as st
import tempfile
import soundfile as sf
from scipy.io import wavfile
from scipy import signal
# ตั้งค่าหน้าเพจ
st.set_page_config(
page_title="Speech Separation Demo App",
page_icon="🎧",
layout="wide",
initial_sidebar_state="expanded"
)
# สไตล์ CSS
st.markdown("""
<style>
.main-header {
font-size: 2.5rem;
color: #1E88E5;
text-align: center;
}
.sub-header {
font-size: 1.5rem;
color: #42A5F5;
}
.success-text {
color: #4CAF50;
font-weight: bold;
}
.info-text {
color: #2196F3;
}
</style>
""", unsafe_allow_html=True)
# ส่วนหัวแอป
st.markdown('<p class="main-header">🎧 Speech Separation Demo</p>', unsafe_allow_html=True)
st.markdown("""
แอปนี้แสดงการสาธิตการแยกเสียงพูดจากเสียงรบกวน โดยใช้การประมวลผลสัญญาณดิจิทัลพื้นฐาน
ตัวอย่างนี้ใช้ตัวกรองความถี่แบบช่องผ่าน (Bandpass filter) เพื่อลดเสียงรบกวน
""")
st.markdown("---")
# สร้างโฟลเดอร์สำหรับเก็บไฟล์ชั่วคราว
os.makedirs("./temp", exist_ok=True)
# ฟังก์ชันเพิ่มเสียงรบกวน
def add_noise(signal, noise_factor=0.01):
"""เพิ่มเสียงรบกวนให้กับสัญญาณเสียง"""
noise = np.random.normal(0, noise_factor, signal.shape)
return signal + noise
# ฟังก์ชันแยกเสียงพูดด้วยตัวกรองความถี่แบบช่องผ่าน
def filter_speech(audio_signal, sample_rate, lowcut=300, highcut=3000, order=5):
"""
แยกเสียงพูดด้วยตัวกรองความถี่แบบช่องผ่าน
เสียงพูดมนุษย์ส่วนใหญ่มีความถี่อยู่ระหว่าง 300-3000 Hz
"""
nyquist = 0.5 * sample_rate
low = lowcut / nyquist
high = highcut / nyquist
# ออกแบบตัวกรอง Butterworth
b, a = signal.butter(order, [low, high], btype='band')
# ใช้ตัวกรองกับสัญญาณ
filtered_signal = signal.filtfilt(b, a, audio_signal)
return filtered_signal
def process_audio(audio_file):
"""ประมวลผลไฟล์เสียงและแยกเสียงพูด"""
# บันทึกไฟล์อัปโหลดไปยังไฟล์ชั่วคราว
with tempfile.NamedTemporaryFile(delete=False, suffix='.wav') as tmp_file:
tmp_file.write(audio_file.getvalue())
tmp_file_path = tmp_file.name
try:
# โหลดไฟล์เสียง
sample_rate, waveform = wavfile.read(tmp_file_path)
# เปลี่ยนเป็น float และ normalize ถ้าจำเป็น
if waveform.dtype == np.int16:
waveform = waveform.astype(np.float32) / 32768.0
elif waveform.dtype == np.int32:
waveform = waveform.astype(np.float32) / 2147483648.0
# ถ้าเป็น stereo ให้แปลงเป็น mono
if len(waveform.shape) > 1 and waveform.shape[1] > 1:
waveform = np.mean(waveform, axis=1)
# แสดงข้อมูลไฟล์เสียง
st.markdown('<p class="sub-header">ข้อมูลไฟล์เสียง</p>', unsafe_allow_html=True)
col1, col2 = st.columns(2)
with col1:
st.write(f"🔊 Sample rate: {sample_rate} Hz")
with col2:
st.write(f"⏱️ ความยาว: {len(waveform)/sample_rate:.2f} วินาที")
# ตัดเสียงถ้ายาวเกินไป (สำหรับ demo)
max_length = 10 * sample_rate # สูงสุด 10 วินาที
if len(waveform) > max_length:
st.warning(f"ตัดเสียงให้เหลือ 10 วินาทีแรกเพื่อให้การประมวลผลเร็วขึ้น")
waveform = waveform[:max_length]
st.markdown('<p class="sub-header">เสียงต้นฉบับ</p>', unsafe_allow_html=True)
# แสดงกราฟเสียงต้นฉบับ
fig, ax = plt.subplots(figsize=(10, 2))
ax.plot(waveform)
ax.set_title("Original Waveform")
ax.set_xlabel("Samples")
ax.set_ylabel("Amplitude")
ax.grid(True, alpha=0.3)
st.pyplot(fig)
# ให้เล่นเสียงต้นฉบับ
st.audio(tmp_file_path, format="audio/wav")
# เพิ่มเสียงรบกวน (สำหรับการทดสอบถ้าต้องการ)
st.markdown('<p class="sub-header">ตัวเลือกการเพิ่มเสียงรบกวน</p>', unsafe_allow_html=True)
add_noise_option = st.checkbox("เพิ่มเสียงรบกวนเพื่อการทดสอบ", value=False)
if add_noise_option:
noise_factor = st.slider("ระดับเสียงรบกวน", min_value=0.001, max_value=0.1, value=0.01, step=0.001)
noisy_waveform = add_noise(waveform, noise_factor)
st.markdown('<p class="sub-header">เสียงที่มีเสียงรบกวน</p>', unsafe_allow_html=True)
# แสดงกราฟเสียงที่มีเสียงรบกวน
fig, ax = plt.subplots(figsize=(10, 2))
ax.plot(noisy_waveform)
ax.set_title("Noisy Waveform")
ax.set_xlabel("Samples")
ax.set_ylabel("Amplitude")
ax.grid(True, alpha=0.3)
st.pyplot(fig)
# บันทึกเสียงที่มีเสียงรบกวน
noisy_path = "./temp/noisy_audio.wav"
sf.write(noisy_path, noisy_waveform, sample_rate)
st.audio(noisy_path, format="audio/wav")
else:
noisy_waveform = waveform # ใช้เสียงต้นฉบับถ้าไม่เพิ่มเสียงรบกวน
# ตั้งค่าพารามิเตอร์ตัวกรอง
st.markdown('<p class="sub-header">ปรับแต่งตัวกรองความถี่</p>', unsafe_allow_html=True)
col1, col2, col3 = st.columns(3)
with col1:
lowcut = st.slider("ความถี่ต่ำสุด (Hz)", min_value=50, max_value=500, value=300, step=10)
with col2:
highcut = st.slider("ความถี่สูงสุด (Hz)", min_value=1000, max_value=8000, value=3000, step=100)
with col3:
filter_order = st.slider("ระดับความแรงของตัวกรอง", min_value=1, max_value=10, value=5, step=1)
# ทำนาย (แยกเสียง)
st.markdown('<p class="sub-header">กำลังแยกเสียง...</p>', unsafe_allow_html=True)
# แสดง spinner ขณะประมวลผล
with st.spinner('กำลังแยกเสียง...'):
# ใช้ตัวกรองความถี่แบบช่องผ่าน
separated_waveform = filter_speech(noisy_waveform, sample_rate, lowcut, highcut, filter_order)
# Normalize
separated_waveform = separated_waveform / np.max(np.abs(separated_waveform))
st.markdown('<p class="sub-header">ผลลัพธ์การแยกเสียง</p>', unsafe_allow_html=True)
st.markdown('<p class="success-text">แยกเสียงสำเร็จ! 🎉</p>', unsafe_allow_html=True)
# แสดงกราฟเสียงที่แยกแล้ว
fig, ax = plt.subplots(figsize=(10, 2))
ax.plot(separated_waveform, color='green')
ax.set_title("Separated Waveform")
ax.set_xlabel("Samples")
ax.set_ylabel("Amplitude")
ax.grid(True, alpha=0.3)
st.pyplot(fig)
# บันทึกเสียงที่แยกแล้ว
output_path = "./temp/separated_audio.wav"
sf.write(output_path, separated_waveform, sample_rate)
# เล่นเสียงที่แยกแล้ว
st.markdown("### เสียงที่แยกแล้ว")
st.audio(output_path, format="audio/wav")
# เปรียบเทียบเสียงต้นฉบับและเสียงที่แยกแล้ว
st.markdown("### เปรียบเทียบเสียงต้นฉบับและเสียงที่แยกแล้ว")
# แสดงกราฟเปรียบเทียบ
fig, ax = plt.subplots(2, 1, figsize=(10, 4), sharex=True)
ax[0].plot(noisy_waveform)
ax[0].set_title("Original/Noisy Waveform")
ax[0].set_ylabel("Amplitude")
ax[0].grid(True, alpha=0.3)
ax[1].plot(separated_waveform, color='green')
ax[1].set_title("Separated Waveform")
ax[1].set_xlabel("Samples")
ax[1].set_ylabel("Amplitude")
ax[1].grid(True, alpha=0.3)
plt.tight_layout()
st.pyplot(fig)
# แสดงการวิเคราะห์ความถี่ (สเปกโตรแกรม)
st.markdown("### การวิเคราะห์ความถี่")
fig, ax = plt.subplots(2, 1, figsize=(10, 6))
# สเปกโตรแกรมของเสียงที่มีเสียงรบกวน
ax[0].specgram(noisy_waveform, Fs=sample_rate, NFFT=1024, noverlap=512, cmap='viridis')
ax[0].set_title("Spectrogram of Noisy Audio")
ax[0].set_ylabel("Frequency (Hz)")
# สเปกโตรแกรมของเสียงที่แยกแล้ว
ax[1].specgram(separated_waveform, Fs=sample_rate, NFFT=1024, noverlap=512, cmap='viridis')
ax[1].set_title("Spectrogram of Separated Audio")
ax[1].set_xlabel("Time (s)")
ax[1].set_ylabel("Frequency (Hz)")
plt.tight_layout()
st.pyplot(fig)
# ให้ดาวน์โหลดไฟล์เสียงที่แยกแล้ว
with open(output_path, "rb") as file:
st.download_button(
label="⬇️ ดาวน์โหลดเสียงที่แยกแล้ว",
data=file,
file_name="separated_audio.wav",
mime="audio/wav"
)
except Exception as e:
st.error(f"เกิดข้อผิดพลาด: {e}")
finally:
# ลบไฟล์ชั่วคราว
if os.path.exists(tmp_file_path):
os.remove(tmp_file_path)
# ส่วนหลักของแอป
def main():
# แสดงข้อมูลแอป
st.sidebar.image("https://img.icons8.com/fluency/96/000000/audio-wave.png", width=100)
st.sidebar.title("เกี่ยวกับแอปนี้")
st.sidebar.info(
"""
แอปนี้เป็นเดโมอย่างง่ายของการแยกเสียงพูดออกจากเสียงรบกวน โดยใช้เทคนิคการประมวลผลสัญญาณดิจิทัลพื้นฐาน
**วิธีใช้:**
1. อัปโหลดไฟล์เสียงที่มีเสียงรบกวน
2. (ตัวเลือก) เพิ่มเสียงรบกวนสำหรับการทดสอบ
3. ปรับแต่งพารามิเตอร์ของตัวกรองความถี่
4. ดูผลลัพธ์และดาวน์โหลดเสียงที่แยกแล้ว
"""
)
# อัปโหลดไฟล์เสียง
st.markdown('<p class="sub-header">อัปโหลดไฟล์เสียง</p>', unsafe_allow_html=True)
uploaded_file = st.file_uploader("เลือกไฟล์เสียงที่มีเสียงรบกวน", type=["wav", "mp3", "ogg", "flac"])
if uploaded_file is not None:
st.markdown('<p class="success-text">อัปโหลดไฟล์สำเร็จ! กำลังเริ่มประมวลผล...</p>', unsafe_allow_html=True)
process_audio(uploaded_file)
# รันแอป
if __name__ == "__main__":
main()