javascriptaudiosveltesveltekitamazon-polly

How to play an audio file through http response with sveltekit?


I'm using a text-to-speech API and I'm trying to serve the audio response to the client and have it play from there, but I'm constantly being met with this error:

GET blob:http://localhost:5173/8788f478-32ef-4e76-80a1-93c4f1a6a3a8 net::ERR_REQUEST_RANGE_NOT_SATISFIABLE

localhost/:1 Uncaught (in promise) DOMException: Failed to load because no supported source was found.

My client code looks like:

// +page.svelte

<script lang="ts">
  let audio: any;

  const handleClick = async () => {
    const what = await fetch('/api/speech', {
      method: 'POST',
      headers: {
        'Content-Type': 'audio/mpeg'
      }
    });

    /**
     * Play audio from blob
     */
    const blob = await what.blob();
    const url = URL.createObjectURL(blob);

    audio.src = url;
    audio.play();
  };
</script>

<button on:click={handleClick}> Clicky </button>

<audio bind:this={audio}>
  <source class="track" src="" type="audio/mpeg" />
</audio>

And my server code looks like:

// routes/api/speech/+server.ts

import AWS from 'aws-sdk';

export const POST = async () => {
  const awsConfig = new AWS.Config(...);

  const polly = new AWS.Polly(awsConfig);

  const input = {
    Engine: 'standard',
    LanguageCode: 'en-US',
    OutputFormat: 'mp3',
    TextType: 'text',
    VoiceId: 'Ivy',
    Text: `hello hello`
  };

  const speech = (await polly.synthesizeSpeech(input, (err, data: any) => {

    if (err) {
      new Response(String('err'));
    }

    /**
     * Return data in a way that's consumable by the browser
     */
    if (data) {
      if (data.AudioStream instanceof Buffer) {
        // fs.writeFile('speech.mp3', data.AudioStream, function (err) {
        //   if (err) {
        //     return console.log(err);
        //   }
        //   console.log('The file was saved!');
        // });
        return data;
      }
    }
  })) as any;

  return new Response(speech.AudioStream, {
    headers: {
      'Content-Type': 'audio/mpeg'
    }
  });
};

If I write the generated file to disk it works and plays properly, but if I use the same data from the generated response and serve it as a Response it doesn't work. What am I doing wrong or is there something I'm missing?


Solution

  • Switching over to AWS SDK v3 made it much simpler. Just had to adjust the server code to:

    import { PollyClient, SynthesizeSpeechCommand } from '@aws-sdk/client-polly';
    
    const pollyClient = new PollyClient(...);
    
      const input = {
        Engine: 'standard',
        LanguageCode: 'en-US',
        OutputFormat: 'mp3',
        TextType: 'text',
        VoiceId: 'Ivy',
        Text: `hi, I'm beepbooper`
      };
    
      const pollyCommand = new SynthesizeSpeechCommand(input);
    
      const response = (await pollyClient.send(pollyCommand)) as any;
    
      return new Response(response.AudioStream, {
        headers: {
          'Content-Type': 'audio/mpeg'
        }
      });