Android AudioTrack buffering problems

前端 未结 3 1786
梦如初夏
梦如初夏 2021-01-31 11:25

Ok so I have a frequency generator which uses AudioTrack to send PCM data to the hardware. Here\'s the code I\'m using for that:

private class playSoundTask ext         


        
相关标签:
3条回答
  • 2021-01-31 12:05

    In case anyone stumbles across this with the same problem (as I did), the solution actually has nothing to do with buffers, it has to do with the sine function. Try replacing angle += increment with angle += (increment % (2.0f * (float) Math.PI));. Also, for efficiency, try using FloatMath.sin() instead of Math.sin().

    0 讨论(0)
  • 2021-01-31 12:08

    I think I've managed to solve the problem.

    ...
    samples[i] = (short)((float)Math.sin( angle )*Short.MAX_VALUE);
    angle += increment;
    angle = angle % (2.0f * (float) Math.PI); //added statement
    ...
    

    As stated before, it's not about buffers. It is about the angle variable, it increases continuously. After a while, the variable gets too large and does not support little steps.

    As the sine repeats after 2*PI, we need to take the modulus of angle.

    Hope this helps.

    edited: angle += increment is enough for the job.

    0 讨论(0)
  • 2021-01-31 12:14

    I found the cause!

    The problem is the size of the AudioTrack's buffer. If you cannot generate samples fast enough, the samples run out, the playback pauses, and continues when there are enough samples again.

    The only solution is to make sure that you can generate samples fast enough (44100/s). As a fast fix, try lowering the sampling rate to 22000 (or increasing the size of the buffer).

    At least this is how it behaves for me - when I optimized my sample generation routine, the jumping went away.

    (Increasing the size of the buffer makes the sound start playing later, as there is some reserve being waited for - until the buffer fills. But still, if you are not generating the samples fast enough, eventually the samples run out).


    Oh, and you should put invariant variables outside of the loop! And maybe make the samples array larger, so that the loop runs longer.

    short samples[] = new short[4*1024];
    ...
    
    frequency = (float)Main.this.slider.getProgress();
    increment = (float)(2 * Math.PI) * frequency / 44100;
    for(int i = 0; i < samples.length; i++)
    {
       samples[i] = (short)((float)Math.sin(angle) * Short.MAX_VALUE);
       angle += increment;
    }
    

    You could also precompute the values of all sines for frequencies 200Hz-8000Hz, with a step of 10Hz. Or do this on demand: when the user select a frequency, generate samples using your method for a while and also save them to an array. When you generate enough samples to have a one full sine wave, you can just keep looping over the array (since sin is a periodic function). Actually there might be some small inconsistencies in the sound as you never hit a period exactly (because you are increasing the angle by 1 and the lenght of one full sine wave is sampleRate / (double)frequency samples). But taking a right multiple of the period makes the inconsistencies unnoticeable.

    Also, see http://developer.android.com/guide/practices/design/performance.html

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