InputStream音频混合(MODE_STREAM)

时间:2015-07-29 15:01:23

标签: java android audio buffer inputstream

我正在Android中制作鼓音序器......

我正在写AudioTrack中的MODE_STREAM,以便我可以使用所有InputStreams实现同步音频播放(可通过'有效'输入流列表{{1在下面的代码中)

音频始终为:PCM(WAV),16bit Stereo 44100 Hz。

显然,我无法在UI线程上实时合成音频,因此我使用activeStreams排队所有音频缓冲。

我有缓冲回放工作,但是当合并两个(或更多)AsyncTask的缓冲区时,互联网似乎在某种关于下一步该做什么的争论中。 “将byte []转换为short []!”,“不,在运行中进行混合!”,“但是如果你不使用short,字节Endianness会被忽略!”,“它无论如何都会被忽略!” - 我甚至都不知道。

如何混合两个或更多InputStream的缓冲区?我不明白为什么我当前的实现失败

我试过,4种不同的StackOverflow解决方案将byte []转换为short [],所以我可以将样本一起添加,但是转换总是立即崩溃Java带有一些我无法得到的神秘错误消息到处走走。所以现在我放弃了。这是我的代码实现one such StackOverflow solution ...

InputStream

我目前在这一行上收到protected Long doInBackground(Object ... Object) { int bytesWritten = 0; InputStream inputStream; int si = 0, i = 0; //The combined buffers. The 'composition' short[] cBuffer = new short[Synth.AUDIO_BUFFER_SIZE]; //The 'current buffer', the segment of inputStream audio. byte[] bBuffer = new byte[Synth.AUDIO_BUFFER_SIZE]; //The 'current buffer', converted to short? short[] sBuffer = new short[Synth.AUDIO_BUFFER_SIZE]; int curStreamNum; int numStreams = activeStreams.size(); short mix; //Start with an empty 'composition' cBuffer = new short[Synth.AUDIO_BUFFER_SIZE]; boolean bufferEmpty = false; try { while(true) { // keep going forever, until stopped or paused. for(curStreamNum = 0;curStreamNum < numStreams;curStreamNum++){ inputStream = activeStreams.get(curStreamNum); i = inputStream.read(bBuffer); bufferEmpty = i<=-1; if(bufferEmpty){ //Input stream buffer was empty. It's out of audio. Close and remove the stream. inputStream.close(); activeStreams.remove(curStreamNum); curStreamNum--; numStreams--; continue; // hard continue. }else{ //Take the now-read buffer, and convert to shorts. ByteBuffer.wrap(bBuffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(sBuffer); //Take the short buffer, merge into composition buffer. //TODO: Optimize by making the 'first layer' of the composition the first buffer, on its own. for(si=0;si<Synth.AUDIO_BUFFER_SIZE;si++){ mix = (short) (sBuffer[si] + cBuffer[si]); //This part is probably completely wrong too. I'm not up to here yet to evaluate whats needed... if(mix >= 32767){ mix = 32767; }else if (mix <= -32768){ mix = -32768; } cBuffer[si] = mix; } } } track.write(sBuffer, 0, i); //It's always full; full buffer of silence, or of composited audio. totalBytesWritten += Synth.AUDIO_BUFFER_SIZE; //.. queueNewInputStreams .. publishProgress(totalBytesWritten); if (isCancelled()) break; } } catch (IOException e) {e.printStackTrace();} return Long.valueOf(totalBytesWritten); } BufferUnderflowException

缓冲区欠载怎么可能?我只是将byte []转换为short []。

请帮忙!

我发布了我的整个功能,希望这个更完整的代码示例和相当适应性的用法可以帮助其他人。

(P.S。字节[]到短[]转换之后是一些脆弱的硬剪辑,我甚至还没有调试,但是建议也会受到赞赏)

1 个答案:

答案 0 :(得分:0)

你的解决方案似乎差不多好,我看到两个问题和一个潜在问题:

  1. 短数组的长度:它必须是字节数组的一半,否则你得到下溢
  2. 短片的总和必须是短裤的平均值而不仅仅是总和,否则你只会得到噪音
  3. (潜在问题)您通过InputStream读取的数组的长度不能完全免费,因为您必须为每个InputStream求和2个字节(然后它必须是偶数数组)并且您应该处理单声道与立体声音频文件(如果立体声,左声道有2字节,右声道有2字节交错)
  4. 在这里你可以找到一个片段,我将使用两个WAV阵列(16位,单声道)的总和

        Random random = new Random();
    
        int bufferLength = 20;
    
        byte[] is1 = new byte[bufferLength];
        byte[] is2 = new byte[bufferLength];
        byte[] average = new byte[bufferLength];
    
        random.nextBytes(is1);
        random.nextBytes(is2);
    
        short[] shorts1 = new short[bufferLength/2];
        ByteBuffer.wrap(is1).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts1);
    
        short[] shorts2 = new short[bufferLength/2];
        ByteBuffer.wrap(is2).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(shorts2);
    
        short[] result = new short[bufferLength/2];
    
        for (int i=0; i<result.length; i++) {
            result[i] = (short) ((shorts1[i] + shorts2[i])/2);
        }
    
        ByteBuffer.wrap(average).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().put(result);
    

    对于32位立体声,解决方案可能是

        Random random = new Random();
    
        int bufferLength = 8 * 50;
    
        byte[] is1 = new byte[bufferLength];
        byte[] is2 = new byte[bufferLength];
        byte[] average = new byte[bufferLength];
    
        random.nextBytes(is1);
        random.nextBytes(is2);
    
        System.out.println(bytesToHex(is1));
        System.out.println(bytesToHex(is2));
    
        int[] ints1 = new int[bufferLength/4];
        ByteBuffer.wrap(is1).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get(ints1);
    
        int[] ints2 = new int[bufferLength/4];
        ByteBuffer.wrap(is2).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().get(ints2);
    
        int[] result = new int[bufferLength/4];
    
        for (int i=0; i<result.length; i++) {
            result[i] = ((ints1[i] + ints2[i])/2);
        }
    
        ByteBuffer.wrap(average).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer().put(result);