import gradio as gr
import speech_recognition as sr
import whisper
import torch
import tempfile
import os
from pydub import AudioSegment
import numpy as np
# 방법 1: Google Speech Recognition API 사용 (인터넷 필요)
def transcribe_with_google(audio_file):
"""Google Speech Recognition을 사용한 음성 인식"""
if audio_file is None:
return "음성 파일을 업로드해주세요."
try:
recognizer = sr.Recognizer()
# 오디오 파일을 WAV로 변환
audio = AudioSegment.from_file(audio_file)
audio = audio.set_frame_rate(16000).set_channels(1)
# 임시 WAV 파일 생성
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp_file:
audio.export(tmp_file.name, format="wav")
# 음성 인식 수행
with sr.AudioFile(tmp_file.name) as source:
audio_data = recognizer.record(source)
# Google API로 텍스트 변환 (한국어)
text_korean = recognizer.recognize_google(audio_data, language='ko-KR')
# 영어도 시도
text_english = recognizer.recognize_google(audio_data, language='en-US')
os.unlink(tmp_file.name) # 임시 파일 삭제
return f"🇰🇷 한국어: {text_korean}\n\n🇺🇸 English: {text_english}"
except sr.UnknownValueError:
return "음성을 인식할 수 없습니다. 더 명확하게 말해주세요."
except sr.RequestError as e:
return f"Google Speech Recognition 서비스 오류: {e}"
except Exception as e:
return f"오류가 발생했습니다: {str(e)}"
# 방법 2: OpenAI Whisper 모델 사용 (오프라인 가능)
class WhisperTranscriber:
def __init__(self):
self.model = None
self.model_size = "base" # tiny, base, small, medium, large
def load_model(self, model_size):
"""Whisper 모델 로드"""
try:
self.model_size = model_size
self.model = whisper.load_model(model_size)
return f"✅ Whisper {model_size} 모델이 로드되었습니다."
except Exception as e:
return f"❌ 모델 로드 실패: {str(e)}"
def transcribe(self, audio_file):
"""Whisper로 음성 인식"""
if audio_file is None:
return "음성 파일을 업로드해주세요."
if self.model is None:
return "먼저 모델을 로드해주세요."
try:
# Whisper로 전사
result = self.model.transcribe(audio_file, language='ko') # 한국어 우선
# 결과 포맷팅
text = result["text"]
language = result.get("language", "unknown")
# 세그먼트별 상세 정보
segments_info = ""
if "segments" in result:
segments_info = "\n\n📋 상세 분석:\n"
for i, segment in enumerate(result["segments"][:5]): # 처음 5개만
start = segment["start"]
end = segment["end"]
text_segment = segment["text"]
segments_info += f"[{start:.1f}s - {end:.1f}s] {text_segment}\n"
return f"""
🎯 인식된 텍스트:
{text}
🌍 감지된 언어: {language}
🎚️ 모델: Whisper {self.model_size}
{segments_info}
""".strip()
except Exception as e:
return f"음성 인식 중 오류 발생: {str(e)}"
# Whisper 인스턴스 생성
whisper_transcriber = WhisperTranscriber()
# 방법 3: 실시간 음성 인식 (마이크 입력)
def transcribe_microphone(audio_data):
"""마이크 입력을 실시간으로 인식"""
if audio_data is None:
return "마이크로 음성을 녹음해주세요."
try:
recognizer = sr.Recognizer()
# 오디오 데이터 처리
sample_rate, audio_array = audio_data
# numpy 배열을 AudioSegment로 변환
audio_segment = AudioSegment(
audio_array.tobytes(),
frame_rate=sample_rate,
sample_width=audio_array.dtype.itemsize,
channels=1
)
# 임시 파일로 저장
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp_file:
audio_segment.export(tmp_file.name, format="wav")
with sr.AudioFile(tmp_file.name) as source:
audio = recognizer.record(source)
# 한국어와 영어로 인식 시도
try:
text_ko = recognizer.recognize_google(audio, language='ko-KR')
result = f"🇰🇷 {text_ko}"
except:
try:
text_en = recognizer.recognize_google(audio, language='en-US')
result = f"🇺🇸 {text_en}"
except:
result = "음성을 인식할 수 없습니다."
os.unlink(tmp_file.name)
return result
except Exception as e:
return f"오류: {str(e)}"
# Gradio 인터페이스 생성
with gr.Blocks(
theme=gr.themes.Soft(),
title="🎤 AI 음성 인식 스튜디오",
css="""
.gradio-container {
max-width: 1200px;
margin: 0 auto;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.main-header {
text-align: center;
color: white;
padding: 2rem 0;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.method-card {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 2rem;
margin: 1rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.upload-area {
border: 2px dashed #cbd5e0;
border-radius: 12px;
padding: 2rem;
text-align: center;
transition: all 0.3s ease;
}
.upload-area:hover {
border-color: #4299e1;
background-color: #f7fafc;
}
.btn-primary {
background: linear-gradient(45deg, #4299e1, #3182ce) !important;
border: none !important;
border-radius: 25px !important;
padding: 12px 24px !important;
font-weight: 600 !important;
}
.status-success {
color: #38a169;
font-weight: 600;
}
.status-error {
color: #e53e3e;
font-weight: 600;
}
"""
) as demo:
gr.HTML("""
<h1>🎤 AI 음성 인식 스튜디오</h1>
<p>다양한 방법으로 음성을 텍스트로 변환하세요</p>
""")
with gr.Tabs():
# 탭 1: 파일 업로드 (Google API)
with gr.TabItem("📁 파일 업로드 (Google)", elem_classes=["method-card"]):
gr.Markdown("""
### 🌐 Google Speech Recognition
음성 파일을 업로드하여 텍스트로 변환합니다. (인터넷 연결 필요)
지원 형식: WAV, MP3, M4A, FLAC
""")
with gr.Row():
with gr.Column():
audio_upload = gr.Audio(
label="🎵 음성 파일 업로드",
type="filepath",
elem_classes=["upload-area"]
)
transcribe_btn = gr.Button(
"🚀 텍스트 변환",
variant="primary",
size="lg"
)
with gr.Column():
google_output = gr.Textbox(
label="📝 인식 결과",
lines=8,
placeholder="여기에 변환된 텍스트가 표시됩니다..."
)
transcribe_btn.click(
transcribe_with_google,
inputs=audio_upload,
outputs=google_output
)
# 탭 2: Whisper 모델
with gr.TabItem("🧠 Whisper AI", elem_classes=["method-card"]):
gr.Markdown("""
### 🤖 OpenAI Whisper
고성능 AI 모델로 정확한 음성 인식 (오프라인 가능)
""")
with gr.Row():
with gr.Column():
# 모델 선택 및 로드
model_selector = gr.Dropdown(
choices=["tiny", "base", "small", "medium", "large"],
value="base",
label="🎯 Whisper 모델 크기",
info="large가 가장 정확하지만 느림"
)
load_model_btn = gr.Button("📥 모델 로드", variant="secondary")
model_status = gr.Textbox(
label="모델 상태",
value="모델을 선택하고 로드 버튼을 눌러주세요.",
interactive=False,
lines=2
)
whisper_audio = gr.Audio(
label="🎵 음성 파일",
type="filepath"
)
whisper_transcribe_btn = gr.Button(
"🎯 Whisper 인식",
variant="primary",
size="lg"
)
with gr.Column():
whisper_output = gr.Textbox(
label="🤖 Whisper 결과",
lines=12,
placeholder="Whisper AI가 분석한 결과가 여기에 표시됩니다..."
)
# 이벤트 핸들러
load_model_btn.click(
whisper_transcriber.load_model,
inputs=model_selector,
outputs=model_status
)
whisper_transcribe_btn.click(
whisper_transcriber.transcribe,
inputs=whisper_audio,
outputs=whisper_output
)
# 탭 3: 실시간 마이크 인식
with gr.TabItem("🎙️ 실시간 마이크", elem_classes=["method-card"]):
gr.Markdown("""
### 🎙️ 실시간 음성 인식
마이크로 직접 녹음하여 즉시 텍스트로 변환
""")
with gr.Row():
with gr.Column():
microphone = gr.Audio(
label="🎤 마이크 녹음",
sources=["microphone"],
type="numpy",
streaming=False
)
mic_transcribe_btn = gr.Button(
"🎯 음성 인식",
variant="primary",
size="lg"
)
# 녹음 팁
gr.Markdown("""
💡 녹음 팁:
- 조용한 환경에서 녹음하세요
- 마이크에 가까이서 명확하게 말하세요
- 3-10초 정도의 짧은 문장이 가장 좋습니다
""")
with gr.Column():
microphone_output = gr.Textbox(
label="🎙️ 실시간 인식 결과",
lines=8,
placeholder="마이크로 녹음하고 인식 버튼을 눌러주세요..."
)
# 음성 품질 정보
audio_info = gr.Textbox(
label="📊 오디오 정보",
lines=3,
interactive=False
)
def analyze_microphone_audio(audio_data):
if audio_data is None:
return "녹음된 데이터가 없습니다.", "정보 없음"
sample_rate, audio_array = audio_data
duration = len(audio_array) / sample_rate
max_amplitude = np.max(np.abs(audio_array))
# 음성 인식 수행
recognition_result = transcribe_microphone(audio_data)
# 오디오 정보
info = f"""
샘플링 레이트: {sample_rate} Hz
녹음 길이: {duration:.2f}초
최대 진폭: {max_amplitude:.3f}
품질: {'좋음' if max_amplitude > 0.1 else '낮음'}
""".strip()
return recognition_result, info
mic_transcribe_btn.click(
analyze_microphone_audio,
inputs=microphone,
outputs=[microphone_output, audio_info]
)
# 탭 4: 배치 처리
with gr.TabItem("📦 배치 처리", elem_classes=["method-card"]):
gr.Markdown("""
### 📦 다중 파일 음성 인식
여러 음성 파일을 한 번에 처리합니다.
""")
with gr.Row():
with gr.Column():
file_list = gr.File(
label="🗂️ 음성 파일들",
file_count="multiple",
file_types=["audio"]
)
batch_method = gr.Radio(
choices=["Google API", "Whisper"],
label="인식 방법",
value="Google API"
)
batch_process_btn = gr.Button(
"🔄 배치 처리 시작",
variant="primary",
size="lg"
)
with gr.Column():
batch_output = gr.Textbox(
label="📊 배치 처리 결과",
lines=15,
placeholder="파일들을 선택하고 배치 처리를 시작하세요..."
)
# 진행률 표시
progress_bar = gr.HTML("""
""")
def process_batch_files(files, method):
if not files:
return "처리할 파일을 선택해주세요."
results = []
total_files = len(files)
for i, file in enumerate(files):
try:
filename = os.path.basename(file.name)
if method == "Google API":
result = transcribe_with_google(file.name)
else: # Whisper
if whisper_transcriber.model is None:
whisper_transcriber.load_model("base")
result = whisper_transcriber.transcribe(file.name)
results.append(f"""
📁 파일: {filename}
{result}
{'='*50}
""")
except Exception as e:
results.append(f"❌ {filename}: 처리 실패 - {str(e)}\n{'='*50}\n")
return "\n".join(results)
batch_process_btn.click(
process_batch_files,
inputs=[file_list, batch_method],
outputs=batch_output
)
# 하단 정보
gr.Markdown("""
---
### 📚 사용 가이드
🌐 Google API: 빠르고 정확하지만 인터넷 연결 필요
🧠 Whisper: 오프라인 가능, 매우 정확하지만 초기 모델 로드 시간 필요
🎙️ 실시간: 마이크로 직접 녹음하여 즉시 변환
📦 배치: 여러 파일을 한 번에 처리
💡 팁: 명확한 발음과 조용한 환경에서 최상의 결과를 얻을 수 있습니다.
""")
# Colab에서 실행
if __name__ == "__main__":
demo.launch(
share=True, # 공유 링크 생성
debug=True, # 디버그 모드
server_name="0.0.0.0", # Colab용 설정
server_port=7860
)