You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何播放ElevenLabs API返回的音频字节数组?

ElevenLabs API 音频播放失败问题解决

我正在使用ElevenLabs API实现文本转语音功能,API返回音频格式的byte[],但尝试用MediaPlayer和AudioTrack播放均失败,求解决方法。

提供的信息

打印的字节数组片段

[-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,

API请求代码

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);
}

AudioTrack播放代码

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);
}

Postman响应说明

Postman返回的响应头显示内容类型为audio/mpeg,即API返回的是MPEG压缩格式音频。


问题分析与解决方法

核心问题

  1. 响应处理错误:使用StringRequest接收二进制音频数据会导致编码错乱——String是字符容器,二进制转字符串再转回字节数组会丢失原始音频信息。
  2. 播放方式不匹配:AudioTrack仅支持原始PCM音频,无法直接解析ElevenLabs返回的MPEG压缩数据。

解决步骤

1. 改用ByteArrayRequest接收原始二进制响应

替换StringRequest为自定义的ByteArrayRequest,直接获取未经过编码转换的音频字节数组:

// 自定义ByteArrayRequest类
public class ByteArrayRequest extends Request<byte[]> {
    private final Response.Listener<byte[]> listener;

    public ByteArrayRequest(int method, String url, Response.Listener<byte[]> listener, Response.ErrorListener errorListener) {
        super(method, url, errorListener);
        this.listener = listener;
    }

    @Override
    protected Response<byte[]> parseNetworkResponse(NetworkResponse response) {
        return Response.success(response.data, HttpHeaderParser.parseCacheHeaders(response));
    }

    @Override
    protected void deliverResponse(byte[] response) {
        listener.onResponse(response);
    }
}

// 修改后的API请求方法
private void elevenLab() throws JSONException {
    ByteArrayRequest byteArrayRequest = new ByteArrayRequest(Request.Method.POST, LAB_URL, 
        new Response.Listener<byte[]>() {
            @Override
            public void onResponse(byte[] response) {
                try {
                    playMpegAudio(response);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Log.d("VALUE","Response length: " + response.length);
            }
        }, 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<>();
            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(StandardCharsets.UTF_8);
        }
    };
    requestQueue.add(byteArrayRequest);
}

2. 选择合适的播放方案

方案一:MediaPlayer播放临时文件(简单易用)

将音频字节数组写入临时文件,利用MediaPlayer的解码能力播放:

private void playMpegAudio(byte[] audioBytes) throws IOException {
    File tempFile = File.createTempFile("elevenlabs", ".mp3", getCacheDir());
    FileOutputStream fos = new FileOutputStream(tempFile);
    fos.write(audioBytes);
    fos.close();

    MediaPlayer mediaPlayer = new MediaPlayer();
    mediaPlayer.setDataSource(tempFile.getAbsolutePath());
    mediaPlayer.prepare();
    mediaPlayer.start();

    // 播放完成后释放资源并删除临时文件
    mediaPlayer.setOnCompletionListener(mp -> {
        mp.release();
        tempFile.delete();
    });
}
方案二:MediaCodec解码+AudioTrack播放(流式场景)

如果需要流式播放,可通过MediaCodec将MPEG解码为PCM后,再用AudioTrack播放:

private void playMpegAudio(byte[] audioBytes) throws IOException {
    MediaCodec codec = MediaCodec.createDecoderByType("audio/mpeg");
    MediaFormat format = MediaFormat.createAudioFormat("audio/mpeg", 44100, 1);
    codec.configure(format, null, null, 0);
    codec.start();

    ByteBuffer[] inputBuffers = codec.getInputBuffers();
    ByteBuffer[] outputBuffers = codec.getOutputBuffers();
    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();

    // 输入音频数据到解码器
    int inputIndex = codec.dequeueInputBuffer(-1);
    if (inputIndex >= 0) {
        ByteBuffer inputBuffer = inputBuffers[inputIndex];
        inputBuffer.clear();
        inputBuffer.put(audioBytes);
        codec.queueInputBuffer(inputIndex, 0, audioBytes.length, 0, 0);
    }

    AudioTrack audioTrack = null;
    boolean isPlaying = false;

    // 循环解码并播放
    while (true) {
        int outputIndex = codec.dequeueOutputBuffer(bufferInfo, 10000);
        if (outputIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
            outputBuffers = codec.getOutputBuffers();
        } else if (outputIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            // 获取解码后的音频格式,初始化AudioTrack
            MediaFormat outputFormat = codec.getOutputFormat();
            int sampleRate = outputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
            int channelCount = outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
            int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
            int bufferSize = AudioTrack.getMinBufferSize(sampleRate,
                    channelCount == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO,
                    audioFormat);
            audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
                    channelCount == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO,
                    audioFormat, bufferSize, AudioTrack.MODE_STREAM);
            audioTrack.play();
            isPlaying = true;
        } else if (outputIndex >= 0) {
            // 读取解码后的PCM数据并写入AudioTrack
            ByteBuffer outputBuffer = outputBuffers[outputIndex];
            byte[] pcmData = new byte[bufferInfo.size];
            outputBuffer.get(pcmData);
            outputBuffer.clear();
            if (isPlaying && audioTrack != null) {
                audioTrack.write(pcmData, 0, pcmData.length);
            }
            codec.releaseOutputBuffer(outputIndex, false);
            // 检测是否播放完成
            if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                break;
            }
        }
    }

    // 释放资源
    if (audioTrack != null) {
        audioTrack.stop();
        audioTrack.release();
    }
    codec.stop();
    codec.release();
}

内容的提问来源于stack exchange,提问作者Taki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 22:42:47