File size: 6,672 Bytes
43b61a2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
import os
import logging
from typing import List, Optional
from pydub import AudioSegment
from pydub.effects import normalize
import uuid
logger = logging.getLogger(__name__)
class AudioProcessor:
def __init__(self):
self.supported_formats = ['mp3', 'wav', 'm4a', 'aac', 'ogg']
async def combine_audios(
self,
audio_paths: List[str],
background_music_path: Optional[str] = None,
output_dir: str = "temp",
background_volume: float = 0.3,
fade_duration: int = 1000 # milliseconds
) -> str:
"""
Tổng hợp các file audio thành một file duy nhất
Args:
audio_paths: Danh sách đường dẫn audio các cảnh
background_music_path: Đường dẫn nhạc nền (optional)
output_dir: Thư mục output
background_volume: Âm lượng nhạc nền (0.0 - 1.0)
fade_duration: Thời gian fade in/out (ms)
Returns:
Đường dẫn file audio đã tổng hợp
"""
try:
logger.info(f"Bắt đầu tổng hợp {len(audio_paths)} file audio")
# Kiểm tra file input
valid_audio_paths = []
for path in audio_paths:
if os.path.exists(path):
valid_audio_paths.append(path)
logger.info(f"Audio hợp lệ: {path}")
else:
logger.warning(f"Audio không tồn tại: {path}")
if not valid_audio_paths:
raise ValueError("Không có file audio hợp lệ nào")
# Load và ghép các audio cảnh
combined_audio = None
total_duration = 0
for i, audio_path in enumerate(valid_audio_paths):
logger.info(f"Xử lý audio {i+1}/{len(valid_audio_paths)}: {audio_path}")
# Load audio với format tự động detect
audio_segment = AudioSegment.from_file(audio_path)
# Normalize audio
audio_segment = normalize(audio_segment)
# Thêm fade in/out cho audio đầu và cuối
if i == 0: # Audio đầu tiên
audio_segment = audio_segment.fade_in(fade_duration)
if i == len(valid_audio_paths) - 1: # Audio cuối cùng
audio_segment = audio_segment.fade_out(fade_duration)
# Ghép audio
if combined_audio is None:
combined_audio = audio_segment
else:
combined_audio = combined_audio + audio_segment
total_duration += len(audio_segment)
logger.info(f"Đã thêm audio {i+1}, tổng thời lượng: {total_duration/1000:.2f}s")
logger.info(f"Hoàn thành ghép audio cảnh, tổng thời lượng: {total_duration/1000:.2f}s")
# Thêm nhạc nền nếu có
if background_music_path and os.path.exists(background_music_path):
logger.info("Đang thêm nhạc nền...")
# Load nhạc nền
background_music = AudioSegment.from_file(background_music_path)
# Điều chỉnh âm lượng nhạc nền
background_music = background_music - (20 - int(background_volume * 20)) # Giảm dB
# Lặp lại nhạc nền nếu cần
if len(background_music) < len(combined_audio):
# Tính số lần lặp cần thiết
repeat_times = (len(combined_audio) // len(background_music)) + 1
background_music = background_music * repeat_times
# Cắt nhạc nền cho khớp với audio chính
background_music = background_music[:len(combined_audio)]
# Thêm fade in/out cho nhạc nền
background_music = background_music.fade_in(fade_duration * 2).fade_out(fade_duration * 2)
# Mix audio chính với nhạc nền
combined_audio = combined_audio.overlay(background_music)
logger.info("Đã thêm nhạc nền thành công")
# Tạo tên file output
output_filename = f"combined_audio_{uuid.uuid4().hex[:8]}.wav"
output_path = os.path.join(output_dir, output_filename)
# Export file kết quả
combined_audio.export(
output_path,
format="wav",
parameters=["-ac", "2", "-ar", "44100"] # Stereo, 44.1kHz
)
logger.info(f"Đã xuất file audio tổng hợp: {output_path}")
logger.info(f"Thời lượng cuối cùng: {len(combined_audio)/1000:.2f}s")
return output_path
except Exception as e:
logger.error(f"Lỗi khi tổng hợp audio: {str(e)}")
raise
def get_audio_info(self, audio_path: str) -> dict:
"""
Lấy thông tin về file audio
"""
try:
audio = AudioSegment.from_file(audio_path)
return {
"duration_seconds": len(audio) / 1000,
"channels": audio.channels,
"frame_rate": audio.frame_rate,
"sample_width": audio.sample_width,
"file_size_mb": os.path.getsize(audio_path) / (1024 * 1024)
}
except Exception as e:
logger.error(f"Lỗi khi lấy thông tin audio {audio_path}: {str(e)}")
return {}
def validate_audio_file(self, audio_path: str) -> bool:
"""
Kiểm tra tính hợp lệ của file audio
"""
try:
if not os.path.exists(audio_path):
return False
# Kiểm tra extension
ext = audio_path.lower().split('.')[-1]
if ext not in self.supported_formats:
return False
# Thử load file
AudioSegment.from_file(audio_path)
return True
except Exception:
return False |