음성인식 프로젝트에서 sound 파일이 들어올 경우
해당 데이터를 학습된 모델을 통하여 Text 데이터로 변하는 것을 Return해주는 API를 만들어야 한다.
이를 위해서
이렇게 두 가지를 해야했다.
이 중 Whisper 파인튜닝을 통한 모델 학습을 먼저 하려 했으나 살짝 난항을 겪고 있어서 일단 구조를 먼저 만들기로 결심했다.
동작은 간단하다.
# main.py
from fastapi import FastAPI
import uvicorn
import os
from app.connectAPI.routers.wav_to_pcm import router as wav_to_pcm_router
from app.connectAPI.routers.pcm_to_text import router as pcm_to_text_router
app = FastAPI(title="WAV to Text API")
TEMP_DIRECTORY = "temp"
def start_server():
os.makedirs(TEMP_DIRECTORY, exist_ok=True)
import app.connectAPI.routers.wav_to_pcm as wav_to_pcm_module
wav_to_pcm_module.TEMP_DIR = TEMP_DIRECTORY
app.include_router(wav_to_pcm_router)
app.include_router(pcm_to_text_router)
return app
if __name__ == "__main__":
app = start_server() # 앱 초기화
uvicorn.run(app, host="0.0.0.0", port=8000)
main.py는 다음과 같다.
전역 변수로 다루는 것은 TEMP_DIRECTORY 하나이다. 아직 저 부분에 대해서 어떻게 할 지 고민이 되어 아무 의미 없는 값으로 채워 넣었다. (메모리에 직접 접근하여 바로 처리할까 생각을 했지만, 기능 구현에 사용할 데이터가 어느 정도의 크기까지 받을 지에 대한 구체적 논의가 아직 따로 없었어서 보류 상태로 두었다)
그것을 제외하곤 FastAPI의 기능을 router로 등록하여 사용하는 구조만을 이용했다.
# pcm_to_text.py
# 아직 모델 개발이 전부 진행되지 않음 / 임시 코드로 대체할 생각
from fastapi import APIRouter
router = APIRouter()
async def convert_pcm_to_text(pcm_array):
return "example data"
아직 모델 개발이 진행되지 않아서 임시 Return 값 만을 받을 수 있게 해놨다.
# struct.py
from pydantic import BaseModel
class TextResponse(BaseModel):
text: str
job_id: str
processing_time: float
# wav_to_pcm.py
from fastapi import APIRouter, File, UploadFile, HTTPException
from app.connectAPI.routers.struct import TextResponse
import time
import os
import uuid
import aiofiles
import numpy as np
import wave
router = APIRouter()
TEMP_DIR = None
@router.post("/api/wav-to-text", response_model=TextResponse)
async def wav_to_text(file: UploadFile = File(...)):
"""
WAV 파일을 받아서 텍스트로 변환하는 API
"""
start_time = time.time()
# 1. WAV 파일 받기
if not file.filename.endswith('.wav'):
raise HTTPException(status_code=400, detail="WAV 파일만 지원합니다.")
# 고유 작업 ID 생성
job_id = str(uuid.uuid4())
# WAV 파일 임시 저장
wav_path = os.path.join(TEMP_DIR, f"{job_id}.wav")
async with aiofiles.open(wav_path, 'wb') as out_file:
content = await file.read()
await out_file.write(content)
# WAV -> PCM 변환
pcm_data = await convert_wav_to_pcm(wav_path)
# 이 부분에 나중에 pcm_to_text 파일로 연결해야함
text = "example data"
os.remove(wav_path)
processing_time = time.time() - start_time
return TextResponse(
text=text,
job_id=job_id,
processing_time=processing_time
)
async def convert_wav_to_pcm(wav_path: str) -> np.ndarray:
"""
WAV 파일을 PCM 데이터로 변환
"""
try:
with wave.open(wav_path, 'rb') as wav_file:
# WAV 파일 정보 읽기
n_channels = wav_file.getnchannels()
sample_width = wav_file.getsampwidth()
framerate = wav_file.getframerate()
n_frames = wav_file.getnframes()
# PCM 데이터 읽기
pcm_data = wav_file.readframes(n_frames)
# NumPy 배열로 변환
if sample_width == 2: # 16-bit
pcm_array = np.frombuffer(pcm_data, dtype=np.int16)
elif sample_width == 4: # 32-bit
pcm_array = np.frombuffer(pcm_data, dtype=np.int32)
else:
pcm_array = np.frombuffer(pcm_data, dtype=np.uint8)
return pcm_array
except Exception as e:
raise HTTPException(status_code=500, detail=f"WAV -> PCM 변환 오류: {str(e)}")
먼저 위의 struct.py의 경우 API 함수의 반환값을 해당 구조를 따르도록 했음. (추후 이 부분의 조작을 통하여 반환 값을 바꿀 예정)
UUID 부분은 동시에 여러 요청 들어와도 각 요청 고유하게 구분 가능하게하고, 비동기 처리가 가능하게 한다.
밑의 코드의 경우 wav_to_text로 일단 wav 확장자 형태의 파일만을 받게 하였다.
또한 일단 따로 저장 장소를 갖지 않는 형태로 구현을 진행하였으며, conert_wav_to_pcm 함수를 이용하여
wav 파일을 pcm 데이터로 변환하는 구체적인 코드를 분리하였다. 해당 방식으로 pcm 데이터를 wav_to_text 함수로 전달하는 역할을 한다.
각각의 역할이 살짝 달랐다.
여기까지가 router에 대한 구체적인 설명이다.
[FastAPI] FastAPI 내부 코드 확인하기 (FastAPI 클래스 생성자 메소드 debug 인자) (0) | 2025.04.17 |
---|---|
[FastAPI] CRUD 구현 (models.py 중심) #4 (0) | 2025.04.11 |
[FastAPI] CRUD 구현 (schemas.py 부분) #3 (0) | 2025.04.11 |
[FastAPI] CRUD 구현 (database.py부분 중점) #2 (0) | 2025.04.09 |
[FastAPI] CRUD 구현 (Create 하는 법, routers/ 부분 중점) #1 (0) | 2025.04.09 |