Воспроизведение аудио с использованием AudioTrack

У меня есть функция, с которой я хочу перейти на использование Androids AudioTrack вместо MediaPlayer, из-за нескольких известных ошибок с MediaPlayer, таких как небольшой пробел, который появляется между циклами.

Мне рекомендовали использовать AudioTrack, но не нашли много примеров его использования. Я нашел вопрос о SO относительно AudioTrack и использовал часть этого кода, чтобы что-то взломать:

public class TestActivity extends Activity implements Runnable {

    Button playButton;
    byte[] byteData = null;
    int bufSize;
    AudioTrack myAT = null;
    Thread playThread = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);

        playButton = (Button) findViewById(R.id.testButton);

        InputStream inputStream = getResources().openRawResource(R.raw.whitenoise_wav);
        try {
            byteData = new byte[ inputStream.available()];
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            inputStream.read(byteData);
            inputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        initialize();

        playButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

                playThread.start();
            }
        });
    }

    void initialize() {

        bufSize = android.media.AudioTrack.getMinBufferSize(44100,
                AudioFormat.CHANNEL_CONFIGURATION_STEREO,
                AudioFormat.ENCODING_PCM_16BIT);

        myAT = new AudioTrack(AudioManager.STREAM_MUSIC,
                44100, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
                AudioFormat.ENCODING_PCM_16BIT, bufSize,
                AudioTrack.MODE_STREAM);
        myAT.setVolume(.2f);

        playThread = new Thread(this);
    }

    public void run() {
        if (myAT != null) {
            myAT.play();
            myAT.setLoopPoints(0, byteData.length, 6);
            myAT.write(byteData, 0, byteData.length);
        }
    }
}

Таким образом, это, кажется, воспроизводит всю звуковую дорожку (~ 1: 00 мин), а затем останавливается. Теперь конечная цель состоит в том, что у двух одновременно есть 2 отдельных звуковых дорожки, которые воспроизводятся и зацикливаются. В настоящее время у меня есть звуковые дорожки в каталоге /res/raw/, но я могу переместить их в простую папку assets, если это будет лучше. Верна ли моя текущая реализация AudioTrack? Если да, то как мне получить его в цикле?

В суммировании: как вы можете воспроизводить петлю звука без пробела, используя AudioTrack?

Приветствуются предложения по альтернативным способам получения петлевого аудио, например, сторонних библиотек.

Ответы

Ответ 1

Вы не можете использовать петлю с помощью AudioTrack, настроенного с помощью AudioTrack.MODE_STREAM. Если вы используете MODE_STREAM, AudioTrack должен быть заполнен новыми образцами непрерывно.

Но вы можете настроить его с помощью AudioTrack.MODE_STATIC и передать весь буфер для воспроизведения (я имею в виду: если вам нужно смешать два образца, вам нужно передать смешанные образцы).

setLoopPoints: устанавливает точки цикла и количество циклов. Цикл может быть бесконечным. Аналогично setPlaybackHeadPosition, трек должен быть остановлен или приостановлен для изменения точек петли и должен использовать режим MODE_STATIC.

Обратите внимание, что AudioTrack воспроизводит сырые образцы PCM, там нет поддержки WAV, MP3 или других контейнеров.

Ответ 2

Посмотрите на этот пример, похоже, подобная проблема, решила непрерывно подавать команду AudioTrack.

class ToneGenerator {
    int sampleRate = 8000;
    double sample[] = null;
    byte generatedSnd[] = null;
    int m_ifreq = 400;
    Thread m_PlayThread = null;
    boolean m_bStop = false;
    AudioTrack m_audioTrack = null;
    int m_play_length = 1000;//in seconds

    static public void PlayTone(int freq, int play_length) {
        ToneGenerator player = new ToneGenerator();
        player.m_ifreq = freq;
        player.m_play_length = play_length;
        player.play();
    }

    synchronized void stop() {
        m_bStop = true;
        if (m_PlayThread != null) {
            try {
                m_PlayThread.interrupt();
                m_PlayThread.join();
                m_PlayThread = null;
            } catch (Exception e) {

            }
        }
        if (m_audioTrack != null) {
            m_audioTrack.stop();
            m_audioTrack.release();
            m_audioTrack = null;
        }
    }

    synchronized void play() {
        m_bStop = false;
        m_PlayThread = new Thread() {
            public void run() {
                try {
                    int iToneStep = 0;

                    m_audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
                            sampleRate, AudioFormat.CHANNEL_OUT_MONO,
                            AudioFormat.ENCODING_PCM_16BIT, 2 * sampleRate,
                            AudioTrack.MODE_STREAM);

                    while (!m_bStop && m_play_length-- > 0) {
                        genTone(iToneStep++);

                        m_audioTrack.write(generatedSnd, 0, generatedSnd.length);
                        if (iToneStep == 1) {
                            m_audioTrack.play();
                        }
                    }
                } catch (Exception e) {
                    Log.e("Tone", e.toString());
                } catch (OutOfMemoryError e) {
                    Log.e("Tone", e.toString());
                }

            }
        };
        m_PlayThread.start();
    }

    //Generate tone data for 1 seconds
    synchronized void genTone(int iStep) {
        sample = new double[sampleRate];

        for (int i = 0; i < sampleRate; ++i) {
            sample[i] = Math.sin(2 * Math.PI * (i + iStep * sampleRate) / (sampleRate / m_ifreq));
        }

        // convert to 16 bit pcm sound array
        // assumes the sample buffer is normalised.
        generatedSnd = new byte[2 * sampleRate];
        int idx = 0;
        for (final double dVal : sample) {
            // scale to maximum amplitude
            final short val = (short) ((dVal * 32767));
            // in 16 bit wav PCM, first byte is the low order byte
            generatedSnd[idx++] = (byte) (val & 0x00ff);
            generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
        }
    }

}

Ответ 3

Вы передаете байтовую длину потока. setLoopPoints() ожидает количество звуковых блоков в потоке. См. GetBufferSizeInFrames() в документации.

Однако. Я предупреждаю вас, что большинство конкретных аудиоформатов не основаны на блоках; они основаны на выборке. Единственным исключением является MP3; он выровнен по 1152 байтам. Если исходным контентом является MP3 и явно не авторизованный для выравнивания по блоку, бесшовный цикл невозможен. Кажется, вы используете "whitenoise.wav" в качестве источника звука. Если длина этого файла не будет явно выровнена с размером блока, у вас могут возникнуть артефакты звука при циклизации.

Обходной путь для этого заключается в том, чтобы перекрестно затухать начальный и конечный блоки кадров, когда вы выполняете цикл, и обрабатываете всю буферизацию самостоятельно, но вам придется писать код, чтобы сделать это самостоятельно.