From 2f1ea3b369abaa2f85eee71c9c32d75d4eb3fdcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Sun, 8 Mar 2026 14:38:51 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20[sound-logo]=20Lyria=20WebSocket=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80=20=ED=82=A4=20=ED=98=95=EC=8B=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - client_content → clientContent (camelCase) - music_generation_config 중첩 제거 → musicGenerationConfig 최상위 - playback_control 중첩 제거 → playbackControl 최상위 - WAV 헤더 감지 시 decodeAudioData fallback 추가 --- resources/views/rd/sound-logo/index.blade.php | 49 +++++++++++++------ 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/resources/views/rd/sound-logo/index.blade.php b/resources/views/rd/sound-logo/index.blade.php index b02e9a48..77de33bb 100644 --- a/resources/views/rd/sound-logo/index.blade.php +++ b/resources/views/rd/sound-logo/index.blade.php @@ -1836,25 +1836,23 @@ function soundLogo() { this.bgmProgress = '프롬프트 설정 중...'; ws.send(JSON.stringify({ - client_content: { + clientContent: { weightedPrompts: [{ text: this.bgmPrompt, weight: 1.0 }] } })); ws.send(JSON.stringify({ - music_generation_config: { - musicGenerationConfig: { - bpm: this.bgmBpm, - density: this.bgmDensity / 100, - brightness: this.bgmBrightness / 100, - scale: this.bgmScale, - temperature: 1.0, - } + musicGenerationConfig: { + bpm: this.bgmBpm, + density: this.bgmDensity / 100, + brightness: this.bgmBrightness / 100, + scale: this.bgmScale, + temperature: 1.0, } })); ws.send(JSON.stringify({ - playback_control: { playbackControl: 'PLAY' } + playbackControl: 'PLAY' })); playStartTime = Date.now(); this.bgmProgress = '음악 생성 중... 0/' + duration + '초'; @@ -1920,7 +1918,7 @@ function soundLogo() { setTimeout(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ - playback_control: { playbackControl: 'STOP' } + playbackControl: 'STOP' })); setTimeout(() => { if (ws.readyState === WebSocket.OPEN) ws.close(); @@ -1937,7 +1935,8 @@ function soundLogo() { async decodeBgmChunks(chunks) { try { - // base64 청크들을 합쳐서 하나의 PCM 데이터로 변환 + console.log('[Lyria] 오디오 디코딩 시작: ' + chunks.length + '개 청크'); + // base64 청크들을 합쳐서 하나의 바이트 배열로 변환 const allBytes = []; for (const chunk of chunks) { const binaryStr = atob(chunk); @@ -1946,8 +1945,28 @@ function soundLogo() { } } 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 numChannels = 2; const bytesPerSample = 2; @@ -1959,7 +1978,6 @@ function soundLogo() { return; } - const ctx = this.getAudioCtx(); const buffer = ctx.createBuffer(numChannels, numSamples, sampleRate); const view = new DataView(pcmData.buffer); @@ -1968,7 +1986,7 @@ function soundLogo() { for (let i = 0; i < numSamples; i++) { const byteOffset = (i * numChannels + ch) * bytesPerSample; 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.toast('배경음악 생성 완료 (' + buffer.duration.toFixed(1) + '초)'); } catch (e) { + console.error('[Lyria] 오디오 디코딩 실패:', e); this.bgmError = '오디오 디코딩 실패: ' + e.message; this.toast('배경음악 오디오 처리에 실패했습니다.', 'error'); } finally {