Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
themeEmacs
public async Task<string> GetChatCompletion(string message, List<string> assist)
{
    var completionResult = await _client.CompleteChatAsync(new ChatMessage[]
    {
        ChatMessage.CreateUserMessage(message),
        ChatMessage.CreateAssistantMessage(string.Join("\n", assist))
    });
    string aiResponse = completionResult.Value.Content.FirstOrDefault()?.Text ?? string.Empty;

    return aiResponse;
}


/// <summary>
/// voice alloy, ash, ballad, coral, echo, fable, onyx, nova, sage, , shimme
/// </summary>
/// <param name="text"></param>
/// <param name="voice"></param>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public async Task<float[]> ConvertTextToVoiceAsync(string text, string voice = "alloy")
{
    var requestBody = new
    {
        model = "gpt-4o-mini-tts",  // TTS 모델 이름
        input = text,               // 변환할 텍스트
        voice                       // 음성 스타일
    };
    
    var ttsTask = _httpClient.PostAsJsonAsync("audio/speech", requestBody);
    
    await Task.WhenAll(ttsTask); // 두 작업이 완료될작업을 동시실행하고 , 동시완료될 때까지 기다림

    var response = await ttsTask;

    if (!response.IsSuccessStatusCode)
    {
        throw new InvalidOperationException($"TTS API 호출 실패: {response.ReasonPhrase}");
    }

    // MP3 데이터를 byte 배열로 변환하여 반환
    var audioBytes = await response.Content.ReadAsByteArrayAsync();

    // MP3 데이터를 PCM 데이터로 변환
    return ConvertMp3ToFloatArray(audioBytes);
}

...

  • OpenAPI의 TTS는 mp3로 반환하며~ 순수웹에서 스트리밍 재생하려면 PCM형태의 스트림 데이터 변환이 필요합니다필요하며 서버에서 변환합니다.


웹에서의 재생

Code Block
themeEmacs
async function playAudioBytes(audioBytes, playbackRate, type, dotNetRef) {
    try {
        if (!audioContext || audioContext.state === 'closed') {
            audioContext = new AudioContext();
        }

        // Float32Array로 변환된 PCM 데이터 사용
        const float32Array = new Float32Array(audioBytes);

        // AudioBuffer 생성
        const audioBuffer = audioContext.createBuffer(1, float32Array.length, audioContext.sampleRate);
        audioBuffer.copyToChannel(float32Array, 0);

        // 재생
        const bufferSource = audioContext.createBufferSource();
        bufferSource.buffer = audioBuffer;
        bufferSource.playbackRate.value = playbackRate; // 재생 속도 설정
        bufferSource.connect(audioContext.destination);                

        // 재생 완료 이벤트 핸들러 추가
        bufferSource.onended = () => {            
            // 타입에 따라 추가 작업 수행
            console.log(`오디오 재생 완료 재생 타입: ${type}`);
            if (type === 1) {
                console.log("Type 1: 휴먼요청 재생완료~ LLM응답재생 요청");
                if (dotNetRef && typeof dotNetRef.invokeMethodAsync === "function") {
                    dotNetRef.invokeMethodAsync("OnAudioPlaybackCompleted", 1)
                        .catch(err => console.error("Blazor 메서드 호출 OnAudioPlaybackCompleted 중 오류 발생:", err));
                }                
            } else if (type === 2) {
                console.log("Type 2: AI재생완료");
            } else if (type === 3) {
                console.log("Type 3: 사용자 정의 작업");
            }
        };

        bufferSource.start();
    } catch (err) {
        console.error("오디오 재생 중 오류 발생:", err);
    }
}

...

  • 음성채팅 기능의 이벤트를 처리하며 OpenAI API를 이용하고~ blazor에게 완료를 수행해 UI업데이트및 최종 프론트에게 재생 스트림을 전달해 재생시킬수도 있습니다.
  • 비교적 간단한 LLM ChatCompletion이 이용되었으며 이 부분을 개선해, 더 스마트한 음성챗봇을 만들수도 있습니다AI음성봇을 만들수도 있습니다.
  • 사용자로부터 Input Text를 받는형태이지만 필요하면 음성 입력스트림을 바로 받아 처리할수도 있습니다.

...


데모샘플 전체코드

...