README / app.py
WIN821899's picture
Update app.py
f779bf7 verified
Raw
History Blame Contribute Delete
11.5 kB
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")