androidkotlinandroid-volley

Play audio file from api request


I'm using ElevenLabs Api to convert text to speech , this api mainly returns byte[] as an audio , i tried to play it using MediaPlayer and AudioTrack but failed to do so, does anyone how to do that , Thank you in advance

 [-61, -65, -61, -69, 80, -61, -124, 0, 0, 7, 40, 1, 103, -62, -76, 17, -62, -128, 1, -61, -113, -62, -99, -61, -83, 115, 24, -62, -80, 0, 0, 57, 38, 104, 0, -61, -108, 47, -61,
  private  void elevenLab() throws JSONException {

        // Request a string response from the provided URL.
        StringRequest stringRequest = new StringRequest(Request.Method.POST, LAB_URL, new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        try {
                            // getting response as byte
                            playFile(response.getBytes());
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        Log.d("VALUE","Response " + Arrays.toString(response.getBytes()));
                    }
                     }, new Response.ErrorListener() {
                   @Override
                  public void onErrorResponse(VolleyError error) {
                       Log.d("TAG","Error is " + error.getMessage());
                  }
             }) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> headers = new HashMap<String, String>();
                headers.put("accept", "audio/mpeg");
                headers.put("xi-api-key", LAB_KEY);
                headers.put("Content-Type", "application/json");
                return headers;
            }

            @Override
            public byte[] getBody() {
                JSONObject jsonBody = new JSONObject();
                try {
                    jsonBody.put("text", "Hello How are you man?");
                    JSONObject voiceSettings = new JSONObject();
                    voiceSettings.put("stability", 0);
                    voiceSettings.put("similarity_boost", 0);
                    jsonBody.put("voice_settings", voiceSettings);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                return jsonBody.toString().getBytes();
            }
        };

        requestQueue.add(stringRequest);
    }

 private void playFile(byte[] response) throws IOException {
        int sampleRate = 44100;
        int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
        int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
        int bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
        AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, bufferSize, AudioTrack.MODE_STREAM);
        audioTrack.play();
        audioTrack.write(response, 0, response.length);
    }

enter image description here


Solution

  • The main problem with this code is that you are requesting and operating on strings, while the returned object is byte[].

    Here's a suggested change to your code:

     Request<byte[]> request = new Request<byte[]>(Request.Method.POST, LAB_URL, new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        // Handle error
                    }})
           {
            @Override
            public byte[] getBody() throws AuthFailureError {
                JSONObject jsonBody = new JSONObject();
                try {
                    jsonBody.put("text", responseMsg);
                    JSONObject voiceSettings = new JSONObject();
                    voiceSettings.put("stability", 0);
                    voiceSettings.put("similarity_boost", 0);
                    jsonBody.put("voice_settings", voiceSettings);
                } catch (JSONException e) {
                    e.printStackTrace();
                }
                return jsonBody.toString().getBytes();
            }
    
            @Override
            public String getBodyContentType() {
                return "application/json";
            }
    
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> headers = new HashMap<>();
                headers.put("accept", "audio/mpeg");
                headers.put("xi-api-key", "apikey");
                return headers;
            }
    
            @Override
            protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {
                return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response));
            }
    
            @Override
            protected void deliverResponse(byte[] response) {
                try {
                    playFile(response);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        };
    
        requestQueue.add(request);
    

    Note that this does not check if the response is 200. According to their specs it will return application/json content if something is wrong with the request.