Spaces:
Runtime error
Runtime error
| import streamlit as st | |
| import librosa | |
| import soundfile as sf | |
| import pyrubberband as pyrb | |
| from pydub import AudioSegment, effects | |
| import os | |
| import tempfile | |
| import shutil | |
| # ====================================================================== | |
| # CONFIGURATION AND HEADER | |
| # ====================================================================== | |
| # ตั้งค่าหน้า Streamlit | |
| st.set_page_config(page_title="AutoTune Karaoke Enhancer", page_icon="🎤", layout="centered") | |
| # แสดงหัวข้อและคำอธิบายแอปด้วย Markdown | |
| st.markdown(""" | |
| <h1 style='text-align:center; color:#FF4B4B;'>🎤 AutoTune Karaoke Enhancer</h1> | |
| <p style='text-align:center; font-size:18px;'> | |
| แปลงเสียงร้องคาราโอเกะของคุณให้เพราะขึ้นแบบมืออาชีพ | |
| </p> | |
| """, unsafe_allow_html=True) | |
| # แจ้งเตือนผู้ใช้เกี่ยวกับโปรแกรมที่ต้องติดตั้งเพิ่มเติม | |
| st.info(""" | |
| 💡 **ข้อควรรู้:** แอปนี้ต้องการโปรแกรมภายนอกอย่าง **spleeter** และ **sox** | |
| เพื่อให้ทำงานได้สมบูรณ์ โปรดตรวจสอบให้แน่ใจว่าได้ติดตั้งแล้วในระบบ | |
| """) | |
| # ====================================================================== | |
| # CORE PROCESSING FUNCTIONS | |
| # ====================================================================== | |
| def separate_vocals(input_file, output_dir): | |
| """ | |
| แยกเสียงร้อง (vocals) ออกจากดนตรี (accompaniment) โดยใช้ Spleeter | |
| :param input_file: เส้นทางไฟล์เสียงขาเข้า | |
| :param output_dir: โฟลเดอร์สำหรับเก็บไฟล์ขาออก | |
| :return: เส้นทางของไฟล์เสียงร้องและดนตรีที่แยกแล้ว | |
| """ | |
| try: | |
| # ใช้ os.system เรียกใช้ Spleeter เพื่อแยกเสียง | |
| st.info(f"🔍 กำลังแยกเสียงร้องจากไฟล์: {input_file}") | |
| os.system(f"spleeter separate -p spleeter:2stems -o {output_dir} {input_file}") | |
| # ค้นหาเส้นทางของไฟล์ที่ถูกสร้างขึ้น | |
| base = os.path.splitext(os.path.basename(input_file))[0] | |
| vocal_path = os.path.join(output_dir, base, "vocals.wav") | |
| accompaniment_path = os.path.join(output_dir, base, "accompaniment.wav") | |
| # ตรวจสอบว่าไฟล์ถูกสร้างขึ้นจริงหรือไม่ | |
| if not os.path.exists(vocal_path) or not os.path.exists(accompaniment_path): | |
| st.error("❌ การแยกเสียงล้มเหลว โปรดตรวจสอบว่า spleeter ได้รับการติดตั้งอย่างถูกต้องและไฟล์เสียงไม่มีปัญหา") | |
| return None, None | |
| return vocal_path, accompaniment_path | |
| except Exception as e: | |
| st.error(f"❌ เกิดข้อผิดพลาดในการแยกเสียง: {e}") | |
| return None, None | |
| def autotune(vocal_file, target_key="C", pitch_shift_strength=1.0): | |
| """ | |
| ปรับคีย์เสียงร้อง (Autotune) โดยหาคีย์เฉลี่ยและเลื่อนไปหาคีย์เป้าหมาย | |
| :param vocal_file: เส้นทางไฟล์เสียงร้อง | |
| :param target_key: คีย์เป้าหมาย เช่น "C", "D" | |
| :param pitch_shift_strength: ความเข้มข้นของการปรับคีย์ (0.0 ถึง 2.0) | |
| :return: เส้นทางของไฟล์เสียงร้องที่ปรับคีย์แล้ว | |
| """ | |
| st.info("🎯 กำลังปรับคีย์...") | |
| y, sr = librosa.load(vocal_file) | |
| # คำนวณ pitch ของเสียงร้อง | |
| pitches, magnitudes = librosa.piptrack(y=y, sr=sr) | |
| pitch_series = [pitches[magnitudes[:, i].argmax(), i] for i in range(pitches.shape[1]) if pitches[magnitudes[:, i].argmax(), i] > 0] | |
| if not pitch_series: | |
| st.warning("⚠️ ไม่พบข้อมูล pitch ในไฟล์เสียง ไม่สามารถทำการ Autotune ได้") | |
| return vocal_file | |
| semitones = librosa.hz_to_midi(pitch_series) | |
| # คำนวณระยะการเลื่อนคีย์ที่จำเป็น | |
| shift = (librosa.note_to_midi(target_key) - round(semitones.mean())) * pitch_shift_strength | |
| # ใช้ pyrubberband เพื่อเลื่อนคีย์ | |
| y_shifted = pyrb.pitch_shift(y, sr, shift) | |
| output_file = "autotuned_vocals.wav" | |
| sf.write(output_file, y_shifted, sr) | |
| return output_file | |
| def enhance_vocals(vocal_file, reverb_wetness=50): | |
| """ | |
| ปรับปรุงคุณภาพเสียงร้องโดยการใส่ฟิลเตอร์และ Reverb | |
| :param vocal_file: เส้นทางไฟล์เสียงร้อง | |
| :param reverb_wetness: ความเข้มข้นของ Reverb (0 ถึง 100) | |
| :return: เส้นทางของไฟล์เสียงร้องที่ปรับปรุงแล้ว | |
| """ | |
| st.info("✨ กำลังปรับโทนเสียง...") | |
| try: | |
| vocal = AudioSegment.from_file(vocal_file) | |
| # ใช้ low-pass และ high-pass filter เพื่อลดเสียงรบกวน | |
| vocal = vocal.low_pass_filter(8000).high_pass_filter(120) | |
| with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as temp_file: | |
| temp_path = temp_file.name | |
| vocal.export(temp_path, format="wav") | |
| # ใช้ SoX เพื่อเพิ่ม Reverb | |
| output_file = "enhanced_vocal.wav" | |
| os.system(f"sox {temp_path} {output_file} reverb {reverb_wetness} 50 100") | |
| os.remove(temp_path) | |
| return output_file | |
| except Exception as e: | |
| st.error(f"❌ เกิดข้อผิดพลาดในการปรับปรุงเสียง: {e}") | |
| return vocal_file | |
| def mix_tracks(vocal_file, music_file, output_file="final_mix.wav"): | |
| """ | |
| ผสมเสียงร้องที่ปรับปรุงแล้วกับดนตรี | |
| :param vocal_file: เส้นทางไฟล์เสียงร้อง | |
| :param music_file: เส้นทางไฟล์เสียงดนตรี | |
| :return: เส้นทางของไฟล์เสียงที่ผสมเสร็จแล้ว | |
| """ | |
| st.info("🎚 กำลังมิกซ์เสียง...") | |
| vocal = AudioSegment.from_file(vocal_file) | |
| music = AudioSegment.from_file(music_file) | |
| # ผสมเสียงและปรับความดังให้สมดุล | |
| mixed = effects.normalize(music.overlay(vocal)) | |
| mixed.export(output_file, format="wav") | |
| return output_file | |
| # ====================================================================== | |
| # STREAMLIT UI AND EXECUTION FLOW | |
| # ====================================================================== | |
| # ตัวเลือกให้ผู้ใช้อัปโหลดไฟล์ | |
| uploaded_file = st.file_uploader("📂 เลือกไฟล์เสียง (MP3/WAV)", type=["mp3", "wav"]) | |
| # ตัวเลือกการปรับแต่งเสียง | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| target_key = st.selectbox("🎯 เลือกคีย์เป้าหมาย", ["C", "D", "E", "F", "G", "A", "B"], help="เลือกคีย์ที่ต้องการให้เสียงร้องถูกปรับ") | |
| with col2: | |
| pitch_shift_strength = st.slider("🎶 ความเข้ม Autotune", 0.0, 2.0, 1.0, 0.1, help="ปรับความเข้มข้นของการปรับคีย์ (1.0 คือค่าปกติ)") | |
| reverb_wetness = st.slider("✨ ความเข้ม Reverb", 0, 100, 50, help="ปรับความก้องของเสียง (Reverb)") | |
| # ปุ่มเริ่มประมวลผล | |
| if uploaded_file is not None: | |
| if st.button("🚀 เริ่มประมวลผล"): | |
| progress_bar = st.progress(0) | |
| temp_file_path = None | |
| output_dir = "output" | |
| try: | |
| # สร้างไฟล์ชั่วคราวเพื่อเก็บไฟล์เสียงที่อัปโหลด | |
| with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as temp_file: | |
| temp_file.write(uploaded_file.read()) | |
| temp_file_path = temp_file.name | |
| # 1. แยกเสียงร้องและดนตรี | |
| vocals_path, music_path = separate_vocals(temp_file_path, output_dir) | |
| if not vocals_path: | |
| st.stop() | |
| progress_bar.progress(25) | |
| # 2. ปรับคีย์เสียงร้อง | |
| tuned_vocals_path = autotune(vocals_path, target_key=target_key, pitch_shift_strength=pitch_shift_strength) | |
| progress_bar.progress(50) | |
| # 3. ปรับปรุงโทนเสียง | |
| enhanced_vocals_path = enhance_vocals(tuned_vocals_path, reverb_wetness=reverb_wetness) | |
| progress_bar.progress(75) | |
| # 4. ผสมเสียงสุดท้าย | |
| final_song_path = mix_tracks(enhanced_vocals_path, music_path) | |
| progress_bar.progress(100) | |
| st.success("✅ เสร็จแล้ว! ดาวน์โหลดไฟล์ด้านล่าง") | |
| st.audio(final_song_path, format="audio/wav") | |
| with open(final_song_path, "rb") as f: | |
| st.download_button("📥 ดาวน์โหลดไฟล์", f, file_name="karaoke_enhanced.wav") | |
| except Exception as e: | |
| st.error(f"❌ เกิดข้อผิดพลาดที่ไม่คาดคิด: {e}") | |
| finally: | |
| # ลบไฟล์และโฟลเดอร์ชั่วคราวทั้งหมดที่สร้างขึ้น | |
| if temp_file_path and os.path.exists(temp_file_path): | |
| os.remove(temp_file_path) | |
| if os.path.exists(output_dir): | |
| shutil.rmtree(output_dir) | |
| if os.path.exists("autotuned_vocals.wav"): | |
| os.remove("autotuned_vocals.wav") | |
| if os.path.exists("enhanced_vocal.wav"): | |
| os.remove("enhanced_vocal.wav") | |
| if os.path.exists("final_mix.wav"): | |
| os.remove("final_mix.wav") | |