KLingo Project Documentation 1.0.0
Unreal Engine 5.6 C++ Project Documentation
로딩중...
검색중...
일치하는것 없음
UVoiceFunctionLibrary 클래스 참조

음성 파일 변환과 명령 문자열 파싱 유틸리티를 제공합니다. 더 자세히 ...

#include <UVoiceFunctionLibrary.h>

+ UVoiceFunctionLibrary에 대한 상속 다이어그램 :
+ UVoiceFunctionLibrary에 대한 협력 다이어그램:

정적 Public 멤버 함수

static TArray< uint8 > ConvertPCM2WAV (const TArray< uint8 > &InPCMData, int32 InSampleRate, int32 InChannel, int32 InBitsPerSample)
 PCM 데이터를 WAV 포맷으로 감쌉니다.
 
static TArray< uint8 > ConvertStereoToMono (const TArray< uint8 > &InStereoPCMData)
 스테레오 PCM 데이터를 모노로 변환합니다. (좌우 채널 평균)
 
static USoundWaveProcedural * CreateProceduralSoundWaveFromWavData (const TArray< uint8 > &AudioData)
 절차형 사운드 웨이브를 생성해 스트리밍 재생에 사용합니다.
 
static USoundWave * CreateSoundWaveFromWavData (const TArray< uint8 > &WavData)
 WAV 데이터를 기반으로 사운드 웨이브 객체를 생성합니다.
 
static TArray< uint8 > ResampleAudio (const TArray< uint8 > &InPCMData, int32 InSampleRate, int32 OutSampleRate, int32 InNumChannels)
 PCM 오디오 데이터를 다른 샘플레이트로 리샘플링합니다.
 
static FString SaveWavToFile (TArray< uint8 > &InWavData, const FString &InFileName=TEXT(""))
 WAV 데이터를 파일로 저장합니다.
 

상세한 설명

음성 파일 변환과 명령 문자열 파싱 유틸리티를 제공합니다.

UVoiceFunctionLibrary.h 파일의 14 번째 라인에서 정의되었습니다.

멤버 함수 문서화

◆ ConvertPCM2WAV()

TArray< uint8 > UVoiceFunctionLibrary::ConvertPCM2WAV ( const TArray< uint8 > &  InPCMData,
int32  InSampleRate,
int32  InChannel,
int32  InBitsPerSample 
)
static

PCM 데이터를 WAV 포맷으로 감쌉니다.

매개변수
InPCMData[in] 원본 PCM 바이트 배열입니다.
InSampleRate[in] 샘플레이트(Hz)입니다.
InChannel[in] 채널 수입니다.
InBitsPerSample[in] 샘플당 비트 수입니다.
반환값
WAV 헤더가 포함된 바이트 배열입니다.

UVoiceFunctionLibrary.cpp 파일의 26 번째 라인에서 정의되었습니다.

31{
32 TArray<uint8> WavData;
33
34 const int32 ByteRate = InSampleRate * InChannel * InBitsPerSample / 8;
35 const int32 BlockAlign = InChannel * InBitsPerSample / 8;
36 const int32 DataSize = InPCMData.Num();
37 const int32 ChunkSize = 36 + DataSize;
38
39 // RIFF 헤더
40 WavData.Append(reinterpret_cast<const uint8*>("RIFF"), 4);
41 WavData.Append(reinterpret_cast<const uint8*>(&ChunkSize), 4);
42 WavData.Append(reinterpret_cast<const uint8*>("WAVE"), 4);
43
44 // fmt chunk
45 WavData.Append(reinterpret_cast<const uint8*>("fmt "), 4);
46 int32 SubChunk1Size = 16;
47 int16 AudioFormat = 1;
48
49 WavData.Append(reinterpret_cast<const uint8*>(&SubChunk1Size), 4);
50 WavData.Append(reinterpret_cast<const uint8*>(&AudioFormat), 2);
51 WavData.Append(reinterpret_cast<const uint8*>(&InChannel), 2);
52 WavData.Append(reinterpret_cast<const uint8*>(&InSampleRate), 4);
53 WavData.Append(reinterpret_cast<const uint8*>(&ByteRate), 4);
54 WavData.Append(reinterpret_cast<const uint8*>(&BlockAlign), 2);
55 WavData.Append(reinterpret_cast<const uint8*>(&InBitsPerSample), 2);
56 WavData.Append(reinterpret_cast<const uint8*>("data"), 4);
57 WavData.Append(reinterpret_cast<const uint8*>(&DataSize), 4);
58
59 WavData.Append(InPCMData);
60 return WavData;
61}

◆ ConvertStereoToMono()

TArray< uint8 > UVoiceFunctionLibrary::ConvertStereoToMono ( const TArray< uint8 > &  InStereoPCMData)
static

스테레오 PCM 데이터를 모노로 변환합니다. (좌우 채널 평균)

매개변수
InStereoPCMData[in] 스테레오 PCM 데이터 (16bit, 2채널)
반환값
모노 PCM 데이터 (16bit, 1채널)

UVoiceFunctionLibrary.cpp 파일의 303 번째 라인에서 정의되었습니다.

304{
305 // PCM 데이터는 int16 형식 (16bit), 스테레오는 [L0, R0, L1, R1, ...] 형태
306 const int32 NumSamples = InStereoPCMData.Num() / sizeof(int16);
307
308 if (NumSamples % 2 != 0)
309 {
310 PRINTLOG(TEXT("[ConvertStereoToMono] Invalid stereo PCM data size (not even number of samples)"));
311 return InStereoPCMData;
312 }
313
314 const int16* InSamples = reinterpret_cast<const int16*>(InStereoPCMData.GetData());
315 const int32 NumMonoSamples = NumSamples / 2;
316
317 TArray<uint8> MonoPCMData;
318 MonoPCMData.Reserve(NumMonoSamples * sizeof(int16));
319
320 // 좌우 채널 평균으로 모노 생성
321 for (int32 i = 0; i < NumMonoSamples; ++i)
322 {
323 const int16 LeftSample = InSamples[i * 2]; // 왼쪽 채널
324 const int16 RightSample = InSamples[i * 2 + 1]; // 오른쪽 채널
325
326 // 평균값 계산 (오버플로우 방지)
327 const int32 AverageSample = (static_cast<int32>(LeftSample) + static_cast<int32>(RightSample)) / 2;
328 const int16 MonoSample = static_cast<int16>(FMath::Clamp(AverageSample, -32768, 32767));
329
330 // 결과 버퍼에 추가
331 const uint8* SampleBytes = reinterpret_cast<const uint8*>(&MonoSample);
332 MonoPCMData.Append(SampleBytes, sizeof(int16));
333 }
334
335 PRINTLOG(TEXT("[ConvertStereoToMono] Converted %d stereo samples to %d mono samples"), NumSamples, NumMonoSamples);
336 return MonoPCMData;
337}
#define PRINTLOG(fmt,...)
Definition GameLogging.h:30

다음을 참조함 : PRINTLOG.

◆ CreateProceduralSoundWaveFromWavData()

USoundWaveProcedural * UVoiceFunctionLibrary::CreateProceduralSoundWaveFromWavData ( const TArray< uint8 > &  AudioData)
static

절차형 사운드 웨이브를 생성해 스트리밍 재생에 사용합니다.

매개변수
AudioData[in] PCM 데이터입니다.
반환값
생성된 USoundWaveProcedural 객체입니다.

UVoiceFunctionLibrary.cpp 파일의 178 번째 라인에서 정의되었습니다.

179{
180 // VoiceLogs 폴더에 타임스탬프 형식으로 저장
181 const FDateTime Now = FDateTime::Now();
182 const FString FileName = FString::Printf(TEXT("TTS_Output_%04d%02d%02d_%02d%02d%02d.wav"),
183 Now.GetYear(), Now.GetMonth(), Now.GetDay(),
184 Now.GetHour(), Now.GetMinute(), Now.GetSecond()
185 );
186 const FString FolderPath = FPaths::ProjectSavedDir() / VOICE_LOG;
187 IFileManager::Get().MakeDirectory(*FolderPath, true);
188 const FString SavePath = FolderPath / FileName;
189
190 if (!FFileHelper::SaveArrayToFile(AudioData, *SavePath))
191 {
192 PRINTLOG(TEXT("TTS WAV 저장 실패"));
193 return nullptr;
194 }
195
196 PRINTLOG(TEXT("TTS WAV 저장 완료: %s"), *SavePath);
197
198 if (AudioData.Num() < 44)
199 {
200 PRINTLOG(TEXT("Invalid WAV data (too small)"));
201 return nullptr;
202 }
203
204 const uint8* RawData = AudioData.GetData();
205
206 // --- WAV Header Parsing ---
207 uint16 NumChannels = ReadUInt16(RawData, 22);
208 uint32 SampleRate = ReadUInt32(RawData, 24);
209 uint16 BitsPerSample = ReadUInt16(RawData, 34);
210
211 // Find the 'data' chunk, as it's not always at a fixed position.
212 int32 DataChunkOffset = 36; // Typically after the 'fmt ' chunk.
213 while (DataChunkOffset + 8 < AudioData.Num())
214 {
215 // Read ChunkID as a 4-character string
216 const char* ChunkIDStr = (const char*)(RawData + DataChunkOffset);
217
218 if (strncmp(ChunkIDStr, "data", 4) == 0)
219 {
220 break; // Found it
221 }
222
223 // If not 'data', skip to the next chunk
224 uint32 ChunkSize = ReadUInt32(RawData, DataChunkOffset + 4);
225 DataChunkOffset += (8 + ChunkSize);
226 }
227
228 if (DataChunkOffset + 8 >= AudioData.Num())
229 {
230 PRINTLOG(TEXT("WAV 'data' chunk not found"));
231 return nullptr;
232 }
233
234 const uint32 DataSize = ReadUInt32(RawData, DataChunkOffset + 4);
235 const int32 DataStart = DataChunkOffset + 8;
236
237 if (DataStart + (int32)DataSize > AudioData.Num())
238 {
239 PRINTLOG(TEXT("Invalid WAV data chunk size"));
240 return nullptr;
241 }
242 // --- End of WAV Header Parsing ---
243
244 USoundWaveProcedural* SoundWave = NewObject<USoundWaveProcedural>();
245 if (!SoundWave)
246 {
247 PRINTLOG(TEXT("Failed to create USoundWaveProcedural"));
248 return nullptr;
249 }
250
251 // Calculate actual duration for one-shot playback
252 const float BytesPerSample = BitsPerSample / 8.0f;
253 const float ActualDuration = (float)DataSize / (SampleRate * NumChannels * BytesPerSample);
254
255 SoundWave->SetSampleRate(SampleRate);
256 SoundWave->NumChannels = NumChannels;
257 SoundWave->Duration = ActualDuration;
258 SoundWave->SoundGroup = ESoundGroup::SOUNDGROUP_Voice;
259 SoundWave->bLooping = false;
260
261 // Queue the raw PCM data for playback
262 SoundWave->QueueAudio(RawData + DataStart, DataSize);
263 return SoundWave;
264}
#define VOICE_LOG
static uint32 ReadUInt32(const uint8 *Data, int32 Offset)
static uint16 ReadUInt16(const uint8 *Data, int32 Offset)

다음을 참조함 : PRINTLOG, ReadUInt16(), ReadUInt32(), VOICE_LOG.

+ 이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:

◆ CreateSoundWaveFromWavData()

USoundWave * UVoiceFunctionLibrary::CreateSoundWaveFromWavData ( const TArray< uint8 > &  WavData)
static

WAV 데이터를 기반으로 사운드 웨이브 객체를 생성합니다.

매개변수
WavData[in] WAV 형식의 바이트 배열입니다.
반환값
재생 가능한 USoundWave 객체입니다.

UVoiceFunctionLibrary.cpp 파일의 102 번째 라인에서 정의되었습니다.

103{
104 if (WavData.Num() < 44)
105 {
106 PRINTLOG( TEXT("Invalid WAV data (too small)"));
107 return nullptr;
108 }
109
110 const uint8* RawData = WavData.GetData();
111
112 // WAV Header Parsing
113 // ChunkID "RIFF" (0~3)
114 // Format "WAVE" (8~11)
115 // Subchunk1ID "fmt " (12~15)
116 // AudioFormat, NumChannels, SampleRate, ByteRate, BlockAlign, BitsPerSample
117 uint16 AudioFormat = ReadUInt16(RawData, 20);
118 uint16 NumChannels = ReadUInt16(RawData, 22);
119 uint32 SampleRate = ReadUInt32(RawData, 24);
120 uint16 BitsPerSample = ReadUInt16(RawData, 34);
121
122 // Subchunk2ID "data"는 고정 위치가 아니므로 탐색
123 int32 DataChunkOffset = 36;
124 while (DataChunkOffset + 8 < WavData.Num())
125 {
126 uint32 ChunkID =
127 RawData[DataChunkOffset] |
128 (RawData[DataChunkOffset + 1] << 8) |
129 (RawData[DataChunkOffset + 2] << 16) |
130 (RawData[DataChunkOffset + 3] << 24);
131
132 uint32 ChunkSize = ReadUInt32(RawData, DataChunkOffset + 4);
133
134 // 'data' 체크
135 if (ChunkID == 'atad') // 'data'를 리틀엔디언 'atad'로 읽음
136 {
137 break;
138 }
139
140 // 다음 Chunk로 이동
141 DataChunkOffset += (8 + ChunkSize);
142 }
143
144 if (DataChunkOffset + 8 >= WavData.Num())
145 {
146 PRINTLOG( TEXT("WAV data chunk not found"));
147 return nullptr;
148 }
149
150 const int32 DataStart = DataChunkOffset + 8;
151 const int32 DataSize = WavData.Num() - DataStart;
152
153 // SoundWave 생성
154 USoundWave* SoundWave = NewObject<USoundWave>();
155 if (!SoundWave)
156 {
157 PRINTLOG( TEXT("Failed to create USoundWave"));
158 return nullptr;
159 }
160
161 SoundWave->SoundGroup = ESoundGroup::SOUNDGROUP_Voice;
162 SoundWave->DecompressionType = EDecompressionType::DTYPE_Procedural;
163 SoundWave->bLooping = false;
164
165 SoundWave->NumChannels = NumChannels;
166 SoundWave->Duration = (float)DataSize / (SampleRate * NumChannels * (BitsPerSample / 8));
167 SoundWave->SetSampleRate(SampleRate);
168 SoundWave->RawPCMDataSize = DataSize;
169
170 // RawPCMData에 복사
171 uint8* PCMData = (uint8*)FMemory::Malloc(DataSize);
172 FMemory::Memcpy(PCMData, RawData + DataStart, DataSize);
173 SoundWave->RawPCMData = PCMData;
174
175 return SoundWave;
176}

다음을 참조함 : PRINTLOG, ReadUInt16(), ReadUInt32().

+ 이 함수 내부에서 호출하는 함수들에 대한 그래프입니다.:

◆ ResampleAudio()

TArray< uint8 > UVoiceFunctionLibrary::ResampleAudio ( const TArray< uint8 > &  InPCMData,
int32  InSampleRate,
int32  OutSampleRate,
int32  InNumChannels 
)
static

PCM 오디오 데이터를 다른 샘플레이트로 리샘플링합니다.

매개변수
InPCMData[in] 원본 PCM 데이터 (16bit)
InSampleRate[in] 원본 샘플레이트
OutSampleRate[in] 목표 샘플레이트
InNumChannels[in] 채널 수
반환값
리샘플링된 PCM 데이터

UVoiceFunctionLibrary.cpp 파일의 266 번째 라인에서 정의되었습니다.

267{
268 if (InSampleRate == OutSampleRate)
269 return InPCMData;
270
271 // PCM 데이터는 int16 형식
272 const int32 NumSamples = InPCMData.Num() / sizeof(int16);
273 const int16* InSamples = reinterpret_cast<const int16*>(InPCMData.GetData());
274
275 // 리샘플링 비율 계산
276 const double ResampleRatio = static_cast<double>(OutSampleRate) / static_cast<double>(InSampleRate);
277 const int32 OutNumSamples = FMath::CeilToInt(NumSamples * ResampleRatio);
278
279 TArray<uint8> OutPCMData;
280 OutPCMData.Reserve(OutNumSamples * sizeof(int16));
281
282 // Linear interpolation 리샘플링
283 for (int32 i = 0; i < OutNumSamples; ++i)
284 {
285 const double SourceIndex = i / ResampleRatio;
286 const int32 Index0 = FMath::FloorToInt(SourceIndex);
287 const int32 Index1 = FMath::Min(Index0 + 1, NumSamples - 1);
288 const double Fraction = SourceIndex - Index0;
289
290 // Linear interpolation
291 const int16 Sample0 = InSamples[Index0];
292 const int16 Sample1 = InSamples[Index1];
293 const int16 InterpolatedSample = FMath::RoundToInt(Sample0 + (Sample1 - Sample0) * Fraction);
294
295 // 결과 버퍼에 추가
296 const uint8* SampleBytes = reinterpret_cast<const uint8*>(&InterpolatedSample);
297 OutPCMData.Append(SampleBytes, sizeof(int16));
298 }
299
300 return OutPCMData;
301}

◆ SaveWavToFile()

FString UVoiceFunctionLibrary::SaveWavToFile ( TArray< uint8 > &  InWavData,
const FString &  InFileName = TEXT("") 
)
static

WAV 데이터를 파일로 저장합니다.

매개변수
InWavData[in,out] 저장할 WAV 데이터입니다.
InFileName[in] 저장할 파일 이름(비어있으면 임시 파일 생성)입니다.
반환값
실제 저장된 파일 경로입니다.

UVoiceFunctionLibrary.cpp 파일의 64 번째 라인에서 정의되었습니다.

65{
66 if (InWavData.Num() == 0)
67 {
68 PRINTLOG( TEXT("WavData is empty, nothing to save."));
69 return FString();
70 }
71
72 FString FileName = InFileName;
73 if (FileName.IsEmpty())
74 {
75 // 날짜 기반 파일명 생성
76 const FDateTime Now = FDateTime::Now();
77 FileName = FString::Printf(TEXT("Voice_%04d%02d%02d_%02d%02d%02d.wav"),
78 Now.GetYear(), Now.GetMonth(), Now.GetDay(),
79 Now.GetHour(), Now.GetMinute(), Now.GetSecond()
80 );
81 }
82
83 FString FolderPath = FPaths::ProjectSavedDir() / VOICE_LOG;
84 // 폴더 없으면 생성
85 IFileManager::Get().MakeDirectory(*FolderPath, true);
86
87 FString FullPath = FolderPath / FileName;
88 if (FFileHelper::SaveArrayToFile(InWavData, *FullPath))
89 {
90 // 절대 경로로 변환하여 반환
91 FString AbsolutePath = FPaths::ConvertRelativePathToFull(FullPath);
92 PRINTLOG( TEXT("Saved WAV file: %s"), *AbsolutePath);
93 return AbsolutePath;
94 }
95 else
96 {
97 PRINTLOG( TEXT("Failed to save WAV file: %s"), *FullPath);
98 return FString();
99 }
100}

다음을 참조함 : PRINTLOG, VOICE_LOG.


이 클래스에 대한 문서화 페이지는 다음의 파일들로부터 생성되었습니다.: