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