Search code examples
flutterdartflutter-dependencies

How to play MPEG audio in flutter after requesting it from ElevenLabs API?


After pressing a floating action button, I am trying to play audio from the Eleven Labs voice API in flutter using the AudioPlayer library. I am getting a request back but I don't know how to save the audio temporarily and play it. Below is what I have but its not working. How do you create a temporary file that saves as an MPEG file type which can then be played as audio?

floatingActionButton: Align(
        alignment: Alignment.bottomRight,
        child: FloatingActionButton(
          onPressed: _playOutput ,
          child: const Icon(Icons.volume_up),
        ),

  void _playOutput() async {
    await playTextToSpeech(_outputController.text);
  }
//For the Text To Speech
  Future<void> playTextToSpeech(String text) async {
    // String apiKey = 'YOUR_API_KEY';
    String apiKey = 'MY-API-KEY';
    String url =
        'https://api.elevenlabs.io/v1/text-to-speech/21m00Tcm4TlvDq8ikWAM';

    final response = await http.post(
      Uri.parse(url),
      headers: {
        'accept': 'audio/mpeg',
        'xi-api-key': '$apiKey',
        'Content-Type': 'application/json',
        // 'Authorization': 'Bearer $apiKey',

      },
      body: json.encode({
        "text": text,
        "voice_settings": {
          "stability": 0,
          "similarity_boost": 1
        }

      }),
    );

    if (response.statusCode == 200) {
      print("////////// In tts");
      print(response.headers);
      print('headers >>> ${response.headers}');
      print('body bytes >>> ${response.bodyBytes}');
      print("//////// IN TTS");

      final bytes = response.bodyBytes;
      await player.play(bytes as Source);

    } else {
      throw Exception('Failed to load audio');
    }
  } /

The response has two values which I don't know what they mean. "headers" and "bodyBytes" - perhaps this can be used? Below is the output for headers and bodyBytes respectfully.

{access-control-allow-headers: *, alt-svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000, date: Sat, 25 Feb 2023 04:22:34 GMT, access-control-allow-origin: *, access-control-allow-methods: POST, OPTIONS, DELETE, GET, content-length: 12538, via: 1.1 google, content-type: audio/mpeg, server: uvicorn}
[255, 251, 80, 196, 0, 0, 5, 160, 1, 51, 128, 132, 96, 33, 90, 22, 166, 48, 193, 138, 32, 104, 184, 90, 110, 117, 80, 49, 69, 6, 18, 133, 13, 62, 228, 101, 192, 131, 121, 205, 78, 201, 249, 119, 242, 25, 126, 167, 95, 255, 137, 255, 47, 237, 168, 230, 183, 203, 255, 169, 202, 113, 242, 148, 35, 144, 83, 75, 101, 109, 116, 210, 43, 80, 26, 141, 64, 68, 200, 108, 120, 95, 186, 17, 88, 89, 9, 193, 40, 166, 168, 223, 28, 109, 28, 4, 142, 9, 231, 3, 192, 112, 187, 214, 50, 105, 79, 38, 137, 126, 173, 102, 187, 148, 207, 179, 33, 104, 219, 209, 235, 28, 56, 205, 152, 94, 105, 2, 204, 175, 245, 239, 74, 197, 8, 32, 42, 139, 28, 69, 136, 76, 232, 9, 196, 108, 120, 178, 198, 223, 21, 214, 245, 118, 79, 246, 142, 72, 218, 40, 2, 156, 178, 84, 30, 174, 65, 30, 12, 1, 185, 36, 252, 34, 33, 153, 14, 98, 181, 49, 38, 134, 10, 171, 146, 173, 145, 132, 37, 61, 171, 199, 64, 200, 145, 75, 93, 76, 170, 72, 33, 39, 86, 113, 64, 34, 40, 149, 255, 251, 82, 196, 27, 128, 11, 48, 113, 51, 166, 24, 110, 193, 117, 43,...]

Thanks!

UPDATE: I found the solution! Just_Audio package does the trick. Found on their website as well.

import 'package:just_audio/just_audio.dart';

...

class _ChatGPTPageState extends State<ChatGPTPage> {

....
  //For the Text To Speech
  Future<void> playTextToSpeech(String text) async {

    //display the loading icon while we wait for request
    setState(() {
      _isLoadingVoice = true; //progress indicator
    });

    // String apiKey = 'YOUR_API_KEY';
    String url =
        'https://api.elevenlabs.io/v1/text-to-speech/21m00Tcm4TlvDq8ikWAM';
    final response = await http.post(
      Uri.parse(url),
      headers: {
        'accept': 'audio/mpeg',
        'xi-api-key': apiKey,
        'Content-Type': 'application/json',
      },
      body: json.encode({
        "text": text,
        "voice_settings": {
          "stability": .2,
          "similarity_boost": .8
        }

      }),
    );

    if (response.statusCode == 200) {

      final bytes = response.bodyBytes; //get the bytes ElevenLabs sent back
      await player.setAudioSource(MyCustomSource(bytes)); //send the bytes to be read from the JustAudio library
      player.play(); //play the audio
    } else {
      throw Exception('Failed to load audio');
    }
  } 
}//_ChatGPTPageState class






// Feed your own stream of bytes into the player - Taken from JustAudio package
class MyCustomSource extends StreamAudioSource {
  final List<int> bytes;
  MyCustomSource(this.bytes);

  @override
  Future<StreamAudioResponse> request([int? start, int? end]) async {
    start ??= 0;
    end ??= bytes.length;
    return StreamAudioResponse(
      sourceLength: bytes.length,
      contentLength: end - start,
      offset: start,
      stream: Stream.value(bytes.sublist(start, end)),
      contentType: 'audio/mpeg',
    );
  }
}


Solution

  • Here's a full walkthrough on decoding the response and playing it using the just_audio package as was attempted in the above attempt.