简体   繁体   English

InputStream音频混合(MODE_STREAM)

[英]InputStream audio mixing (MODE_STREAM)

I'm making a drum sequencer in Android... 我正在用Android制作鼓音序器...

I'm writing to an AudioTrack in MODE_STREAM , so that I can achieve synchronized audio playback with all InputStreams (availible via a list of 'active' InputStreams, activeStreams in the code below) 我正在AudioTrack中写入MODE_STREAM ,以便可以使用所有InputStreams实现同步音频回放(可通过“活动” InputStream列表,在下面的代码中使用activeStreams

The audio is always: PCM (WAV), 16bit Stereo 44100 Hz. 音频始终为:PCM(WAV),16位立体声44100 Hz。

Obviously, I can't composite audio in real time on the UI thread, so I'm using an AsyncTask to queue up all the audio buffering. 显然,我无法在UI线程上实时合成音频,因此我正在使用AsyncTask来排队所有音频缓冲。

I got buffered playback working, but when it comes to merging the buffers of two (or more) InputStream 's, the internet seems to be in some kind of debate of what to do next. 我可以缓冲回放,但是当涉及合并两个(或多个) InputStream的缓冲区时,互联网似乎正在讨论下一步该怎么做。 "Convert the byte[] to short[]!", "No, do the bit mixing on-the-fly!", "But if you don't use shorts the byte Endianness is ignored!", "It gets ignored anyway!" “”将byte []转换为short []!“,”不,即时进行位混合!“,”但是,如果您不使用short,字节的字节序将被忽略!“,”无论如何它都会被忽略!” - I don't even know any more. -我什至不知道。

How do I mix the buffer of two or more InputStreams? 如何混合两个或多个InputStreams的缓冲区? I don't understand why my current implementation is failing 我不明白为什么我目前的执行失败

I've tried like, 4 different StackOverflow solutions to convert the byte[] to short[] so I can add the samples together, but the conversion always instantly crashes Java with some cryptic error message that I can't get my head around. 我尝试了4种不同的StackOverflow解决方案,将byte []转换为short [],因此我可以将这些样本加在一起,但是这种转换总是会立即因某些无法解释的错误消息使Java崩溃。 So now I give up. 所以现在我放弃了。 Here's my code implementing one such StackOverflow solution ... 这是我的代码,实现了这样的StackOverflow解决方案 ...

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

I'm currently getting a BufferUnderflowException on this line: ByteBuffer.wrap(bBuffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(sBuffer); 我当前在此行上获得BufferUnderflowExceptionByteBuffer.wrap(bBuffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(sBuffer); .

How is it possible to have buffer underrun? 缓冲区不足怎么办? I'm only converting a byte[] to a short[]. 我只是将byte []转换为short []。

Please help! 请帮忙!

I've posted my whole function in the hopes that this more complete code sample and fairly adaptable usage can help other people out there. 我已经发布了我的整个函数,希望这个更完整的代码示例和相当适应的用法可以帮助其他人。

(PS the byte[] to short[] conversion is followed by some flimsy hard clipping which I'm not even up to debugging yet, but advice there would also be appreciated) (PS从byte []到short []的转换是紧随其后的一些脆弱的硬剪切,我什至还没有调试过,但是也请多多指教)

Your solution seems almost good, I see two issues and a potential one: 您的解决方案似乎几乎不错,我看到了两个问题以及一个潜在的问题:

  1. the length of the short array: it MUST be the half of the byte array, otherwise you get the underflow 短数组的长度:必须为字节数组的一半,否则将产生下溢
  2. the sum of the short must be the average of the shorts and not just the sum, or you'll get just noise 空头的总和必须是空头的平均值,而不仅是总和,否则您只会得到噪音
  3. (potential issue) the length of the array you read by InputStream cannot be totally free, since you have to sum 2bytes for every InputStream (then it must be an even array) and you should take care of mono vs. stereo audio files (if stereo you have 2bytes for the left channel and 2bytes for the right channel interleaved) (潜在的问题)InputStream读取的数组的长度不能完全空闲,因为每个InputStream的总和必须为2bytes(然后它必须是偶数数组),并且应注意单声道与立体声音频文件(如果立体声,左声道有2个字节,右声道有2个字节)

Here you can find a snippet that I would use to sum of two WAV array (16bit, mono) 在这里,您可以找到一个片段,用于将两个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);

For a 32bit stereo, the solution could be 对于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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM