using Android's AudioTrack to combine bytes of sound samples produces noise

前端 未结 1 1868
伪装坚强ぢ
伪装坚强ぢ 2021-01-03 04:35

I\'m building a fairly simple Android app (sdk revision 14: ICS) which allows users to pick two audio clips at a time (all are RIFF/WAV format, little-endian, signed PCM-16

相关标签:
1条回答
  • 2021-01-03 05:32

    I am not familiar with android audio, so I can't answer all your questions, but I can tell you what the fundamental problem is: adding audio data byte-by-byte won't work. Since it sort-of works, and from looking at your code, and the fact that it's most common, I'm going to assume you have 16-bit PCM data. Yet everywhere, you are dealing with bytes. Bytes are not appropriate for processing audio (unless the audio happens to be 8-bit)

    Bytes are aprox +/- 128. You say "I would expect to see values ranging from −32768 to 32767 in logcat from the Log.d(...) invocation above, but instead the results tend to be within the range of -100 to 100 (with some outliers beyond that)" Well, how could you possibly go to that range when you are printing values from a byte array? The correct datatype for 16 bit signed data is short, not byte. If you were printing short values, you'd see the range you expected.

    You must convert your bytes to shorts and sum the shorts. This will take care of much of the misc noise you are hearing. Since you are reading right off the file, though, why bother converting? why not read it off the file as a short using something like this http://docs.oracle.com/javase/1.4.2/docs/api/java/io/DataInputStream.html#readShort()

    The next issue is that you must deal with out-of-range values, rather than letting them "wrap around". The simplest solution is simply to do the summing as integers, "clip" into the short range, and then store the clipped output. This will get rid of your clicks and pops.

    In psuedo-code, the entire process will look something like this:

    file1 = Open file 1
    file2 = Open file 2
    output = Open output for writing
    
    numSampleFrames1 = file1.readHeader()
    numSampleFrames2 = file2.readHeader()
    numSampleFrames = min( numSampleFrames1, numSampleFrames2 )
    output.createHeader( numSampleFrames )
    
    for( int i=0; i<numSampleFrames * channels; ++i ) {
        //read data from file 1
        int a = file1.readShort();
        //read data from file 2, and add it to data we read from file 1
        a += file2.readShort();
        //clip into range
        if( a > Short.MAX_VALUE )
           a = Short.MAX_VALUE;
        if( a < Short.MIN_VALUE )
           a = Short.MIN_VALUE;
        //write it to the output
        output.writeShort( (Short) a );
    }
    

    You will get a little distortion from the "clipping" step, but there's no simple way around that, and clipping is MUCH better than wrap-around. (that said, unless your tracks are extremely "hot", and heavy in the low frequencies, the distortion shouldn't be too noticeable. If it is a problem, you can do other things: multiply a by .5 for example and skip the clipping, but then your output will be much quieter, which, on a phone, is probably not what you want).

    0 讨论(0)
提交回复
热议问题