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(""" """, unsafe_allow_html=True) # ส่วนหัวแอป st.markdown('
🎧 Speech Separation Demo
', 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('ข้อมูลไฟล์เสียง
', 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('เสียงต้นฉบับ
', 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('ตัวเลือกการเพิ่มเสียงรบกวน
', 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('เสียงที่มีเสียงรบกวน
', 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('ปรับแต่งตัวกรองความถี่
', 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('กำลังแยกเสียง...
', 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('ผลลัพธ์การแยกเสียง
', unsafe_allow_html=True) st.markdown('แยกเสียงสำเร็จ! 🎉
', 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('อัปโหลดไฟล์เสียง
', unsafe_allow_html=True) uploaded_file = st.file_uploader("เลือกไฟล์เสียงที่มีเสียงรบกวน", type=["wav", "mp3", "ogg", "flac"]) if uploaded_file is not None: st.markdown('อัปโหลดไฟล์สำเร็จ! กำลังเริ่มประมวลผล...
', unsafe_allow_html=True) process_audio(uploaded_file) # รันแอป if __name__ == "__main__": main()