voicerecord / app.py
picard47at's picture
Fix app.py for HF Spaces: remove server config, update gradio version
e43f6a6
Raw
History Blame Contribute Delete
5.84 kB
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
語音錄音應用程式
部署在 Hugging Face Spaces 上,從麥克風錄音並保存為 16kHz WAV 文件
Usage:
python app.py
"""
import gradio as gr
import numpy as np
import wave
import os
from datetime import datetime
from pathlib import Path
# 設定
SAMPLE_RATE = 16000 # 16kHz
OUTPUT_DIR = "recordings" # 錄音保存目錄
# 確保輸出目錄存在
os.makedirs(OUTPUT_DIR, exist_ok=True)
def save_audio(audio_data: np.ndarray, sr: int) -> str:
"""
將音頻數據保存為 WAV 文件
Args:
audio_data: 音頻數據 (numpy array)
sr: 採樣率
Returns:
保存的文件路徑
"""
# 生成文件名
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"recording_{timestamp}.wav"
filepath = os.path.join(OUTPUT_DIR, filename)
# 確保是單聲道
if len(audio_data.shape) > 1:
audio_data = audio_data.mean(axis=1)
# 歸一化到 16-bit 範圍
audio_int16 = (audio_data * 32767).astype(np.int16)
# 保存為 WAV
with wave.open(filepath, 'w') as wav_file:
wav_file.setnchannels(1) # 單聲道
wav_file.setsampwidth(2) # 2 bytes (16-bit)
wav_file.setframerate(sr)
wav_file.writeframes(audio_int16.tobytes())
return filepath
def process_recording(audio_tuple):
"""
處理錄音並保存
Args:
audio_tuple: (sampling_rate, audio_data) 元組
Returns:
保存的文件路徑和狀態訊息
"""
if audio_tuple is None:
return None, "❌ 沒有錄音數據"
sr, audio_data = audio_tuple
# 如果採樣率不是 16kHz,需要重採樣
if sr != SAMPLE_RATE:
# 使用 librosa 重採樣
try:
import librosa
audio_data = librosa.resample(audio_data, orig_sr=sr, target_sr=SAMPLE_RATE)
sr = SAMPLE_RATE
except ImportError:
# 如果沒有 librosa,使用簡單的降採樣
ratio = SAMPLE_RATE / sr
new_length = int(len(audio_data) * ratio)
audio_data = np.interp(
np.linspace(0, len(audio_data), new_length),
np.arange(len(audio_data)),
audio_data
)
sr = SAMPLE_RATE
# 保存文件
filepath = save_audio(audio_data, sr)
# 計算錄音長度
duration = len(audio_data) / sr
return filepath, f"✅ 已保存:{filepath}\n📊 長度:{duration:.2f}秒 | 採樣率:{sr}Hz"
def create_demo():
"""創建 Gradio 介面"""
with gr.Blocks(title="語音錄音器") as demo:
gr.Markdown("""
# 🎤 語音錄音器
按下「開始錄音」按鈕開始錄音,再次按下「停止錄音」按鈕停止並保存。
錄音將保存為 16kHz 採樣率的 WAV 文件。
""")
with gr.Row():
with gr.Column(scale=1):
gr.Markdown("### 📝 錄音說明")
gr.Markdown("""
1. 點擊 **🔴 開始錄音** 按鈕
2. 對著麥克風說話
3. 點擊 **⏹️ 停止錄音** 按鈕
4. 錄音將自動保存為 WAV 文件
""")
gr.Markdown("### ⚙️ 設定")
gr.Markdown(f"""
- 採樣率:**{SAMPLE_RATE} Hz**
- 格式:**WAV**
- 聲道:**單聲道**
- 位元深度:**16-bit**
""")
with gr.Column(scale=2):
# 錄音組件
audio_input = gr.Audio(
label="🎤 錄音",
sources=["microphone"],
type="numpy",
waveform_options=gr.WaveformOptions(
sample_rate=SAMPLE_RATE,
),
)
# 狀態顯示
status_text = gr.Textbox(
label="📋 狀態",
placeholder="等待錄音...",
interactive=False,
)
# 保存的文件顯示
audio_output = gr.Audio(
label="📁 已保存的錄音",
type="filepath",
interactive=False,
)
# 按鈕
with gr.Row():
save_btn = gr.Button(
"💾 保存錄音",
variant="primary",
size="lg",
)
clear_btn = gr.Button(
"🗑️ 清除",
variant="secondary",
size="lg",
)
# 錄音說明
gr.Markdown("""
---
### 💡 使用提示
- **錄音時**:請確保瀏覽器已授權麥克風權限
- **停止錄音**:錄音完成後點擊「保存錄音」按鈕
- **文件位置**:錄音保存在 `recordings/` 目錄下
- **下載**:點擊音頻播放器右側的下載按鈕可下載文件
""")
# 事件處理
save_btn.click(
fn=process_recording,
inputs=[audio_input],
outputs=[audio_output, status_text],
)
clear_btn.click(
fn=lambda: (None, "🗑️ 已清除"),
inputs=[],
outputs=[audio_input, status_text],
)
return demo
if __name__ == "__main__":
# 創建並啟動應用
demo = create_demo()
demo.launch()