java - InputStream音频混合(MODE_STREAM)

标签 java android audio buffer inputstream

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

我正在AudioTrack中写入MODE_STREAM,以便可以使用所有InputStreams实现同步音频播放(可通过“ Activity ” InputStream列表获得,以下代码中为activeStreams)

音频始终为:PCM(WAV),16位立体声44100 Hz。

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

我已经开始缓冲播放,但是当涉及合并两个(或更多)InputStream的缓冲区时,互联网似乎正在讨论下一步该怎么做。 “”将byte []转换为short []!“,”不,即时进行位混合!“,”但是,如果您不使用shorts,字节的字节序将被忽略!“,”无论如何它都会被忽略!!” -我什至不知道。

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

我尝试了4种不同的StackOverflow解决方案,将byte []转换为short [],因此我可以将这些样本加在一起,但是这种转换总是会立即由于一些无法解释的错误消息使Java崩溃。所以现在我放弃了。这是我实现one such StackOverflow solution的代码...

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:ByteBuffer.wrap(bBuffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(sBuffer);

缓冲区不足怎么办?我只是将byte []转换为short []。

请帮忙!

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

(P.S.从byte []到short []的转换之后是一些脆弱的硬剪切,我什至还没有调试,但是也请多多指教)

最佳答案

您的解决方案似乎几乎不错,我看到了两个问题和一个潜在的问题:

  • 短数组的长度:它必须是字节数组的一半,否则您将获得下溢
  • 短裤的总和必须是短裤的平均值,而不仅是总和,否则您只会得到噪音
  • (潜在问题),InputStream读取的数组的长度不能完全空闲,因为每个InputStream的总和必须为2个字节(然后它必须是偶数数组),并且您应注意单声道或立体声音频文件(如果是立体声,则左声道有2个字节,右声道有2个字节(交错))

  • 在这里,您可以找到一个片段,用于将两个WAV数组(16bit,mono)相加
        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);
    

    关于java - InputStream音频混合(MODE_STREAM),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31704192/

    相关文章:

    java - 确保 Spring 组件是无状态的

    java - 使用 android studio 构建 Selenium 项目失败

    java - AndEngine 和@override 错误

    python - 预期 dense_3_input 具有形状 (None, 40) 但得到形状为 (40, 1) 的数组

    android - PCM Raw Bytes [] 到 Android 上的音频

    java - 如何使用 jackson 解析具有可变数量的第二个分数的 RFC3339 时间戳

    Java CASE 为什么我在使用和不使用 ' ' 的情况下得到完全不同的结果

    java - 日历月份和年份之前

    android - React Native 中不包含 NativeModules

    audio - 如何解码语音输入