0

There is a website, it is connected to the OpenAI Speech API on the server, the server accepts a POST request from the frontend, which contains the text that needs to be voiced, the API voices it and the server returns to the frontend a link to an audio file in mp3 format. The Speech API has a function for streaming audio in real time, the voiceover is transmitted in parts - part voiced, transmitted, part voiced, transmitted.

Everything works for me in the request part, the POST request is sent, I take the link back, but the link does not want to be voiced. If you output it to the console and click on it, you can see that it is partially loaded and fully ready to listen about three seconds after returning from the server.

I've written this code in which the getPromiseFromEvent function wraps the EventListener in a promise and waits for it to be executed. This code works, for example, with a click, if I hang a click on the same button in this way, then the code patiently waits for the click and then continues working on the section below, outputs console.log('canplay') to the console. But if we are talking about an Audio object event listener, then the "canplaythrough" listener does not work at all.

speech.addEventListener('click', async (e) => {
        e.preventDefault();
        let speechText = currentAnswer.replace(/[^\p{L}\d\s.,\-+!?:;]/gu, '');
        
        if (paidVoice === 2) {
            
            function getPromiseFromEvent(item, event) {
              return new Promise((resolve) => {
                const listener = () => {
                  item.removeEventListener(event, listener);
                  resolve();
                }
                item.addEventListener(event, listener);
              })
            }
            
            async function waitForAudioLoad() {
              const audio = new Audio();
              audioURL = await getOpenaiVoice(voiceAPI, voiceID, speechText);

              audio.src = audioURL;  
              
              audio.addEventListener("progress", () => {
                  if (audio.readyState > 0) {
                      console.log('Audio is loading');
                  }
              })
              
              if (audio.readyState > 0) {
                      console.log('Audio is loading-2');
                }
              
              console.log(audio.readyState);
              
              await getPromiseFromEvent(audio, "canplaythrough");
              audio.play();
              
              
              console.log('canplay')
            }
            waitForAudioLoad();
        }

I tried to 'Blob' the audio, tried to add event listener without functions, nothing works, when I used Blob I got an error "Failed to load because no supported source was found."

Only thinks that works for me is wrapping the audio.src = audio URL in setTimeout for 3 seconds, then the code works as it should and the voiceover starts and audio.readyState changes its state to full load, like that

 async function waitForAudioLoad() {
              const audio = new Audio();
              audioURL = await getOpenaiVoice(voiceAPI, voiceID, speechText);
              
              setTimeout(() => {
                audio.src = audioURL;   
              }, 3000)

              
              audio.addEventListener("progress", () => {
                  if (audio.readyState > 0) {
                      console.log('Audio is loading');
                  }
              })
              await getPromiseFromEvent(audio, "canplaythrough");
              audio.play();

If I do this, audio wait 3 seconds after click and perfectly working. I think if I replace setTimeout with a competent download completion listener, everything will work. But I don't know how to do it, so hope for your help.

This is a getOpenaiVoice function, just in case. I don't think problem is here.

async function getOpenaiVoice(voiceAPI, id, answer) {
          
          const requestOptions = {
              method: "POST",
              headers: {
                  "Content-Type": "application/json",
                  "Accept": "application/json"
              },
              body: JSON.stringify({
                  "id_answer": id.toString(),
                  "answer": answer,
              }),
          };

          const response = await fetch(voiceAPI, requestOptions);
          if (!response.ok) {
            const error = await response.json();
            return error;
          }
          const responseJson = await response.json();
          const audioURL = responseJson.audio_url;
          
          return audioURL;
      }
5
  • Most likely, your code is fine, but the server blocks Cross-Origin Requests. Test if CORS is available. Commented Oct 8, 2024 at 17:27
  • await getPromiseFromEvent(audio, "canplaythrough"); does this resolve? Does the line audio.play() ever get called? Put a breakpoint using devtools debugger and check, or throw in a console.log before audio.play. Commented Oct 8, 2024 at 18:40
  • @James No, it never called, only if I add "setTimeout". Commented Oct 8, 2024 at 20:00
  • some things to try Commented Oct 8, 2024 at 20:28
  • @Yogi Have you used a local voiceover saved on your computer or a voiceover that is generated through OpenAI? Commented Oct 9, 2024 at 8:51

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.