如何播放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压缩格式音频。
问题分析与解决方法
核心问题
- 响应处理错误:使用
StringRequest接收二进制音频数据会导致编码错乱——String是字符容器,二进制转字符串再转回字节数组会丢失原始音频信息。 - 播放方式不匹配:
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
相关产品推荐
相关产品推荐

