fix: [sound-logo] Lyria WebSocket 메시지 키 형식 수정

- client_content → clientContent (camelCase)
- music_generation_config 중첩 제거 → musicGenerationConfig 최상위
- playback_control 중첩 제거 → playbackControl 최상위
- WAV 헤더 감지 시 decodeAudioData fallback 추가
This commit is contained in:
김보곤
2026-03-08 14:38:51 +09:00
parent 697560b2de
commit 2f1ea3b369

View File

@@ -1836,25 +1836,23 @@ function soundLogo() {
this.bgmProgress = '프롬프트 설정 중...'; this.bgmProgress = '프롬프트 설정 중...';
ws.send(JSON.stringify({ ws.send(JSON.stringify({
client_content: { clientContent: {
weightedPrompts: [{ text: this.bgmPrompt, weight: 1.0 }] weightedPrompts: [{ text: this.bgmPrompt, weight: 1.0 }]
} }
})); }));
ws.send(JSON.stringify({ ws.send(JSON.stringify({
music_generation_config: { musicGenerationConfig: {
musicGenerationConfig: { bpm: this.bgmBpm,
bpm: this.bgmBpm, density: this.bgmDensity / 100,
density: this.bgmDensity / 100, brightness: this.bgmBrightness / 100,
brightness: this.bgmBrightness / 100, scale: this.bgmScale,
scale: this.bgmScale, temperature: 1.0,
temperature: 1.0,
}
} }
})); }));
ws.send(JSON.stringify({ ws.send(JSON.stringify({
playback_control: { playbackControl: 'PLAY' } playbackControl: 'PLAY'
})); }));
playStartTime = Date.now(); playStartTime = Date.now();
this.bgmProgress = '음악 생성 중... 0/' + duration + '초'; this.bgmProgress = '음악 생성 중... 0/' + duration + '초';
@@ -1920,7 +1918,7 @@ function soundLogo() {
setTimeout(() => { setTimeout(() => {
if (ws.readyState === WebSocket.OPEN) { if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ ws.send(JSON.stringify({
playback_control: { playbackControl: 'STOP' } playbackControl: 'STOP'
})); }));
setTimeout(() => { setTimeout(() => {
if (ws.readyState === WebSocket.OPEN) ws.close(); if (ws.readyState === WebSocket.OPEN) ws.close();
@@ -1937,7 +1935,8 @@ function soundLogo() {
async decodeBgmChunks(chunks) { async decodeBgmChunks(chunks) {
try { try {
// base64 청크들을 합쳐서 하나의 PCM 데이터로 변환 console.log('[Lyria] 오디오 디코딩 시작: ' + chunks.length + '개 청크');
// base64 청크들을 합쳐서 하나의 바이트 배열로 변환
const allBytes = []; const allBytes = [];
for (const chunk of chunks) { for (const chunk of chunks) {
const binaryStr = atob(chunk); const binaryStr = atob(chunk);
@@ -1946,8 +1945,28 @@ function soundLogo() {
} }
} }
const pcmData = new Uint8Array(allBytes); const pcmData = new Uint8Array(allBytes);
console.log('[Lyria] 전체 PCM 크기:', pcmData.length, 'bytes');
// Lyria RealTime: 16-bit PCM, stereo, 48kHz (little-endian) if (pcmData.length < 4) {
this.bgmError = '유효한 오디오 데이터가 없습니다.';
this.bgmLoading = false;
return;
}
const ctx = this.getAudioCtx();
// WAV 헤더 감지 (RIFF 매직넘버) → decodeAudioData 사용
const hasWavHeader = pcmData[0] === 0x52 && pcmData[1] === 0x49 && pcmData[2] === 0x46 && pcmData[3] === 0x46;
if (hasWavHeader) {
console.log('[Lyria] WAV 헤더 감지, decodeAudioData 사용');
const buffer = await ctx.decodeAudioData(pcmData.buffer.slice(0));
this.bgmBuffer = buffer;
this.toast('배경음악 생성 완료 (' + buffer.duration.toFixed(1) + '초)');
return;
}
// Raw PCM: Lyria RealTime — 16-bit PCM, stereo, 48kHz (little-endian)
console.log('[Lyria] Raw PCM 디코딩: 48kHz, stereo, 16-bit LE');
const sampleRate = 48000; const sampleRate = 48000;
const numChannels = 2; const numChannels = 2;
const bytesPerSample = 2; const bytesPerSample = 2;
@@ -1959,7 +1978,6 @@ function soundLogo() {
return; return;
} }
const ctx = this.getAudioCtx();
const buffer = ctx.createBuffer(numChannels, numSamples, sampleRate); const buffer = ctx.createBuffer(numChannels, numSamples, sampleRate);
const view = new DataView(pcmData.buffer); const view = new DataView(pcmData.buffer);
@@ -1968,7 +1986,7 @@ function soundLogo() {
for (let i = 0; i < numSamples; i++) { for (let i = 0; i < numSamples; i++) {
const byteOffset = (i * numChannels + ch) * bytesPerSample; const byteOffset = (i * numChannels + ch) * bytesPerSample;
if (byteOffset + 1 < pcmData.length) { if (byteOffset + 1 < pcmData.length) {
channelData[i] = view.getInt16(byteOffset, true) / 32768; // little-endian channelData[i] = view.getInt16(byteOffset, true) / 32768;
} }
} }
} }
@@ -1976,6 +1994,7 @@ function soundLogo() {
this.bgmBuffer = buffer; this.bgmBuffer = buffer;
this.toast('배경음악 생성 완료 (' + buffer.duration.toFixed(1) + '초)'); this.toast('배경음악 생성 완료 (' + buffer.duration.toFixed(1) + '초)');
} catch (e) { } catch (e) {
console.error('[Lyria] 오디오 디코딩 실패:', e);
this.bgmError = '오디오 디코딩 실패: ' + e.message; this.bgmError = '오디오 디코딩 실패: ' + e.message;
this.toast('배경음악 오디오 처리에 실패했습니다.', 'error'); this.toast('배경음악 오디오 처리에 실패했습니다.', 'error');
} finally { } finally {