blob: cf1a4064178c609cb84d4fb5cde1eaa158ca6bb3 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.media.cts;
import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.cts.util.CtsAndroidTestCase;
import android.media.AudioAttributes;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTimestamp;
import android.media.AudioTrack;
import android.media.PlaybackParams;
import android.util.Log;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
// Test the Java AudioTrack low latency related features:
//
// setBufferSizeInFrames()
// getBufferCapacityInFrames()
// ASSUME getMinBufferSize in frames is significantly lower than getBufferCapacityInFrames.
// This gives us room to adjust the sizes.
//
// getUnderrunCount()
// ASSUME normal track will underrun with setBufferSizeInFrames(0).
//
// AudioAttributes.FLAG_LOW_LATENCY
// ASSUME FLAG_LOW_LATENCY reduces output latency by more than 10 msec.
// Warns if not. This can happen if there is no Fast Mixer or if a FastTrack
// is not available.
public class AudioTrackLatencyTest extends CtsAndroidTestCase {
private String TAG = "AudioTrackLatencyTest";
private final static long NANOS_PER_MILLISECOND = 1000000L;
private final static int MILLIS_PER_SECOND = 1000;
private final static long NANOS_PER_SECOND = NANOS_PER_MILLISECOND * MILLIS_PER_SECOND;
private void log(String testName, String message) {
Log.i(TAG, "[" + testName + "] " + message);
}
private void logw(String testName, String message) {
Log.w(TAG, "[" + testName + "] " + message);
}
private void loge(String testName, String message) {
Log.e(TAG, "[" + testName + "] " + message);
}
public void testSetBufferSize() throws Exception {
// constants for test
final String TEST_NAME = "testSetBufferSize";
final int TEST_SR = 44100;
final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
// -------- initialization --------------
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
minBuffSize, TEST_MODE);
// -------- test --------------
// Initial values
int bufferCapacity = track.getBufferCapacityInFrames();
int initialBufferSize = track.getBufferSizeInFrames();
assertTrue(TEST_NAME, bufferCapacity > 0);
assertTrue(TEST_NAME, initialBufferSize > 0);
assertTrue(TEST_NAME, initialBufferSize <= bufferCapacity);
// set to various values
int resultNegative = track.setBufferSizeInFrames(-1);
assertEquals(TEST_NAME + ": negative size", AudioTrack.ERROR_BAD_VALUE, resultNegative);
assertEquals(TEST_NAME + ": should be unchanged",
initialBufferSize, track.getBufferSizeInFrames());
int resultZero = track.setBufferSizeInFrames(0);
assertTrue(TEST_NAME + ": should be >0, but got " + resultZero, resultZero > 0);
assertTrue(TEST_NAME + ": zero size < original, but got " + resultZero,
resultZero < initialBufferSize);
assertEquals(TEST_NAME + ": should match resultZero",
resultZero, track.getBufferSizeInFrames());
int resultMax = track.setBufferSizeInFrames(Integer.MAX_VALUE);
assertTrue(TEST_NAME + ": set MAX_VALUE, >", resultMax > resultZero);
assertTrue(TEST_NAME + ": set MAX_VALUE, <=", resultMax <= bufferCapacity);
assertEquals(TEST_NAME + ": should match resultMax",
resultMax, track.getBufferSizeInFrames());
int resultMiddle = track.setBufferSizeInFrames(bufferCapacity / 2);
assertTrue(TEST_NAME + ": set middle, >", resultMiddle > resultZero);
assertTrue(TEST_NAME + ": set middle, <=", resultMiddle < resultMax);
assertEquals(TEST_NAME + ": should match resultMiddle",
resultMiddle, track.getBufferSizeInFrames());
// -------- tear down --------------
track.release();
}
// Helper class for tests
private static class TestSetup {
public int sampleRate = 48000;
public int samplesPerFrame = 2;
public int bytesPerSample = 2;
public int config = AudioFormat.CHANNEL_OUT_STEREO;
public int format = AudioFormat.ENCODING_PCM_16BIT;
public int mode = AudioTrack.MODE_STREAM;
public int streamType = AudioManager.STREAM_MUSIC;
public int framesPerBuffer = 256;
public double amplitude = 0.5;
private AudioTrack mTrack;
private short[] mData;
private int mActualSizeInFrames;
AudioTrack createTrack() {
mData = AudioHelper.createSineWavesShort(framesPerBuffer,
samplesPerFrame, 1, amplitude);
int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, config, format);
// Create a buffer that is 3/2 times bigger than the minimum.
// This gives me room to cut it in half and play without glitching.
// This is an arbitrary scaling factor.
int bufferSize = (minBufferSize * 3) / 2;
mTrack = new AudioTrack(streamType, sampleRate, config, format,
bufferSize, mode);
// Calculate and use a smaller buffer size
int smallBufferSize = bufferSize / 2; // arbitrary, smaller might underflow
int smallBuffSizeInFrames = smallBufferSize / (samplesPerFrame * bytesPerSample);
mActualSizeInFrames = mTrack.setBufferSizeInFrames(smallBuffSizeInFrames);
return mTrack;
}
int primeAudioTrack(String testName) {
// Prime the buffer.
int samplesWrittenTotal = 0;
int samplesWritten;
do{
samplesWritten = mTrack.write(mData, 0, mData.length);
if (samplesWritten > 0) {
samplesWrittenTotal += samplesWritten;
}
} while (samplesWritten == mData.length);
int framesWrittenTotal = samplesWrittenTotal / samplesPerFrame;
assertTrue(testName + ": framesWrittenTotal = " + framesWrittenTotal
+ ", size = " + mActualSizeInFrames,
framesWrittenTotal >= mActualSizeInFrames);
return framesWrittenTotal;
}
/**
* @param seconds
*/
public void writeSeconds(double seconds) throws InterruptedException {
long msecEnd = System.currentTimeMillis() + (long)(seconds * 1000);
while (System.currentTimeMillis() < msecEnd) {
// Use non-blocking mode in case the track is hung.
int samplesWritten = mTrack.write(mData, 0, mData.length, AudioTrack.WRITE_NON_BLOCKING);
if (samplesWritten < mData.length) {
int samplesRemaining = mData.length - samplesWritten;
int framesRemaining = samplesRemaining / samplesPerFrame;
int millis = (framesRemaining * 1000) / sampleRate;
Thread.sleep(millis);
}
}
}
}
// Try to play an AudioTrack when the initial size is less than capacity.
// We want to make sure the track starts properly and is not stuck.
public void testPlaySmallBuffer() throws Exception {
final String TEST_NAME = "testPlaySmallBuffer";
TestSetup setup = new TestSetup();
AudioTrack track = setup.createTrack();
// Prime the buffer.
int framesWrittenTotal = setup.primeAudioTrack(TEST_NAME);
// Start playing and let it drain.
int position1 = track.getPlaybackHeadPosition();
assertEquals(TEST_NAME + ": initial position", 0, position1);
track.play();
// Make sure it starts within a reasonably short time.
final long MAX_TIME_TO_START_MSEC = 500; // arbitrary
long giveUpAt = System.currentTimeMillis() + MAX_TIME_TO_START_MSEC;
int position2 = track.getPlaybackHeadPosition();
while ((position1 == position2)
&& (System.currentTimeMillis() < giveUpAt)) {
Thread.sleep(20); // arbitrary interval
position2 = track.getPlaybackHeadPosition();
}
assertTrue(TEST_NAME + ": did it start?, position after start = " + position2,
position2 > position1);
// Make sure it finishes playing the data.
// Wait several times longer than it should take to play the data.
final int several = 3; // arbitrary
Thread.sleep(several * framesWrittenTotal * MILLIS_PER_SECOND / setup.sampleRate);
position2 = track.getPlaybackHeadPosition();
assertEquals(TEST_NAME + ": did it play all the data?",
framesWrittenTotal, position2);
track.release();
}
// Try to play and pause an AudioTrack when the initial size is less than capacity.
// We want to make sure the track starts properly and is not stuck.
public void testPlayPauseSmallBuffer() throws Exception {
final String TEST_NAME = "testPlayPauseSmallBuffer";
TestSetup setup = new TestSetup();
AudioTrack track = setup.createTrack();
// Prime the buffer.
setup.primeAudioTrack(TEST_NAME);
// Start playing then pause and play in a loop.
int position1 = track.getPlaybackHeadPosition();
assertEquals(TEST_NAME + ": initial position", 0, position1);
track.play();
// try pausing several times to see it if it fails
final int several = 4; // arbitrary
for (int i = 0; i < several; i++) {
// write data in non-blocking mode for a few seconds
setup.writeSeconds(2.0); // arbitrary, long enough for audio to get to the device
// Did position advance as we were playing? Or was the track stuck?
int position2 = track.getPlaybackHeadPosition();
int delta = position2 - position1; // safe from wrapping
assertTrue(TEST_NAME + ": [" + i + "] did it advance? p1 = " + position1
+ ", p2 = " + position2, delta > 0);
position1 = position2;
// pause for a second
track.pause();
Thread.sleep(MILLIS_PER_SECOND);
track.play();
}
track.release();
}
// Create a track with or without FLAG_LOW_LATENCY
private AudioTrack createCustomAudioTrack(boolean lowLatency) {
final String TEST_NAME = "createCustomAudioTrack";
final int TEST_SR = 48000;
final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_CONTENT_TYPE = AudioAttributes.CONTENT_TYPE_MUSIC;
// Start with buffer twice as large as needed.
int bufferSizeBytes = 2 * AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
AudioAttributes.Builder attributesBuilder = new AudioAttributes.Builder()
.setContentType(TEST_CONTENT_TYPE);
if (lowLatency) {
attributesBuilder.setFlags(AudioAttributes.FLAG_LOW_LATENCY);
}
AudioAttributes attributes = attributesBuilder.build();
// Do not specify the sample rate so we get the optimal rate.
AudioFormat format = new AudioFormat.Builder()
.setEncoding(TEST_FORMAT)
.setChannelMask(TEST_CONF)
.build();
AudioTrack track = new AudioTrack.Builder()
.setAudioAttributes(attributes)
.setAudioFormat(format)
.setBufferSizeInBytes(bufferSizeBytes)
.build();
assertTrue(track != null);
log(TEST_NAME, "Track sample rate = " + track.getSampleRate() + " Hz");
return track;
}
private int checkOutputLowLatency(boolean lowLatency) throws Exception {
// constants for test
final String TEST_NAME = "checkOutputLowLatency";
final int TEST_SAMPLES_PER_FRAME = 2;
final int TEST_BYTES_PER_SAMPLE = 2;
final int TEST_NUM_SECONDS = 4;
final int TEST_FRAMES_PER_BUFFER = 128;
final double TEST_AMPLITUDE = 0.5;
final short[] data = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BUFFER,
TEST_SAMPLES_PER_FRAME, 1, TEST_AMPLITUDE);
// -------- initialization --------------
AudioTrack track = createCustomAudioTrack(lowLatency);
assertTrue(TEST_NAME + " actual SR", track.getSampleRate() > 0);
// -------- test --------------
// Play some audio for a few seconds.
int numSeconds = TEST_NUM_SECONDS;
int numBuffers = numSeconds * track.getSampleRate() / TEST_FRAMES_PER_BUFFER;
long framesWritten = 0;
boolean isPlaying = false;
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
framesWritten += TEST_FRAMES_PER_BUFFER;
// prime the buffer a bit before playing
if (!isPlaying) {
track.play();
isPlaying = true;
}
}
// Estimate the latency from the timestamp.
long timeWritten = System.nanoTime();
AudioTimestamp timestamp = new AudioTimestamp();
boolean result = track.getTimestamp(timestamp);
// FIXME failing LOW_LATENCY case! b/26413951
assertTrue(TEST_NAME + " did not get a timestamp, lowLatency = "
+ lowLatency, result);
// Calculate when the last frame written is going to be rendered.
long framesPending = framesWritten - timestamp.framePosition;
long timeDelta = framesPending * NANOS_PER_SECOND / track.getSampleRate();
long timePresented = timestamp.nanoTime + timeDelta;
long latencyNanos = timePresented - timeWritten;
int latencyMillis = (int) (latencyNanos / NANOS_PER_MILLISECOND);
assertTrue(TEST_NAME + " got latencyMillis <= 0 == "
+ latencyMillis, latencyMillis > 0);
// -------- cleanup --------------
track.release();
return latencyMillis;
}
// Compare output latency with and without FLAG_LOW_LATENCY.
public void testOutputLowLatency() throws Exception {
final String TEST_NAME = "testOutputLowLatency";
int highLatencyMillis = checkOutputLowLatency(false);
log(TEST_NAME, "High latency = " + highLatencyMillis + " msec");
int lowLatencyMillis = checkOutputLowLatency(true);
log(TEST_NAME, "Low latency = " + lowLatencyMillis + " msec");
// We are not guaranteed to get a FAST track. Some platforms
// do not even have a FastMixer. So just warn and not fail.
if (highLatencyMillis <= (lowLatencyMillis + 10)) {
logw(TEST_NAME, "high latency should be much higher, "
+ highLatencyMillis
+ " vs " + lowLatencyMillis);
}
}
// Verify that no underruns when buffer is >= getMinBufferSize().
// Verify that we get underruns with buffer at smallest possible size.
public void testGetUnderrunCount() throws Exception {
// constants for test
final String TEST_NAME = "testGetUnderrunCount";
final int TEST_SR = 44100;
final int TEST_SAMPLES_PER_FRAME = 2;
final int TEST_BYTES_PER_SAMPLE = 2;
final int TEST_NUM_SECONDS = 2;
final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
final int TEST_FRAMES_PER_BUFFER = 256;
final int TEST_FRAMES_PER_BLIP = TEST_SR / 8;
final int TEST_CYCLES_PER_BLIP = 700 * TEST_FRAMES_PER_BLIP / TEST_SR;
final double TEST_AMPLITUDE = 0.5;
final short[] data = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BUFFER,
TEST_SAMPLES_PER_FRAME, 1, TEST_AMPLITUDE);
final short[] blip = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BLIP,
TEST_SAMPLES_PER_FRAME, TEST_CYCLES_PER_BLIP, TEST_AMPLITUDE);
// -------- initialization --------------
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
// Start with buffer twice as large as needed.
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
minBuffSize * 2, TEST_MODE);
// -------- test --------------
// Initial values
int bufferCapacity = track.getBufferCapacityInFrames();
int initialBufferSize = track.getBufferSizeInFrames();
int minBuffSizeInFrames = minBuffSize / (TEST_SAMPLES_PER_FRAME * TEST_BYTES_PER_SAMPLE);
assertTrue(TEST_NAME, bufferCapacity > 0);
assertTrue(TEST_NAME, initialBufferSize > 0);
assertTrue(TEST_NAME, initialBufferSize <= bufferCapacity);
// Play with initial size.
int underrunCount1 = track.getUnderrunCount();
assertEquals(TEST_NAME + ": initially no underruns", 0, underrunCount1);
// Prime the buffer.
while (track.write(data, 0, data.length) == data.length);
// Start playing
track.play();
int numBuffers = TEST_SR / TEST_FRAMES_PER_BUFFER;
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
}
int underrunCountBase = track.getUnderrunCount();
int numSeconds = TEST_NUM_SECONDS;
numBuffers = numSeconds * TEST_SR / TEST_FRAMES_PER_BUFFER;
track.write(blip, 0, blip.length);
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
}
underrunCount1 = track.getUnderrunCount();
assertEquals(TEST_NAME + ": no more underruns after initial",
underrunCountBase, underrunCount1);
// Play with getMinBufferSize() size.
int resultMin = track.setBufferSizeInFrames(minBuffSizeInFrames);
assertTrue(TEST_NAME + ": set minBuff, >", resultMin > 0);
assertTrue(TEST_NAME + ": set minBuff, <=", resultMin <= initialBufferSize);
track.write(blip, 0, blip.length);
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
}
track.write(blip, 0, blip.length);
underrunCount1 = track.getUnderrunCount();
assertEquals(TEST_NAME + ": no more underruns at min", underrunCountBase, underrunCount1);
// Play with ridiculously small size. We want to get underruns so we know that an app
// can get to the edge of underrunning.
int resultZero = track.setBufferSizeInFrames(0);
assertTrue(TEST_NAME + ": should return > 0, got " + resultZero, resultZero > 0);
assertTrue(TEST_NAME + ": zero size < original", resultZero < initialBufferSize);
numSeconds = TEST_NUM_SECONDS / 2; // cuz test takes longer when underflowing
numBuffers = numSeconds * TEST_SR / TEST_FRAMES_PER_BUFFER;
// Play for a few seconds or until we get some new underruns.
for (int i = 0; (i < numBuffers) && ((underrunCount1 - underrunCountBase) < 10); i++) {
track.write(data, 0, data.length);
underrunCount1 = track.getUnderrunCount();
}
assertTrue(TEST_NAME + ": underruns at zero", underrunCount1 > underrunCountBase);
int underrunCount2 = underrunCount1;
// Play for a few seconds or until we get some new underruns.
for (int i = 0; (i < numBuffers) && ((underrunCount2 - underrunCount1) < 10); i++) {
track.write(data, 0, data.length);
underrunCount2 = track.getUnderrunCount();
}
assertTrue(TEST_NAME + ": underruns still accumulating", underrunCount2 > underrunCount1);
// Restore buffer to good size
numSeconds = TEST_NUM_SECONDS;
numBuffers = numSeconds * TEST_SR / TEST_FRAMES_PER_BUFFER;
int resultMax = track.setBufferSizeInFrames(bufferCapacity);
track.write(blip, 0, blip.length);
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
}
// Should have stopped by now.
underrunCount1 = track.getUnderrunCount();
track.write(blip, 0, blip.length);
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
}
// Counts should match.
underrunCount2 = track.getUnderrunCount();
assertEquals(TEST_NAME + ": underruns should stop happening",
underrunCount1, underrunCount2);
// -------- tear down --------------
track.release();
}
// Verify that we get underruns if we stop writing to the buffer.
public void testGetUnderrunCountSleep() throws Exception {
// constants for test
final String TEST_NAME = "testGetUnderrunCountSleep";
final int TEST_SR = 48000;
final int TEST_SAMPLES_PER_FRAME = 2;
final int TEST_BYTES_PER_SAMPLE = 2;
final int TEST_NUM_SECONDS = 2;
final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
final int TEST_FRAMES_PER_BUFFER = 256;
final int TEST_FRAMES_PER_BLIP = TEST_SR / 8;
final int TEST_CYCLES_PER_BLIP = 700 * TEST_FRAMES_PER_BLIP / TEST_SR;
final double TEST_AMPLITUDE = 0.5;
final short[] data = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BUFFER,
TEST_SAMPLES_PER_FRAME, 1, TEST_AMPLITUDE);
final short[] blip = AudioHelper.createSineWavesShort(TEST_FRAMES_PER_BLIP,
TEST_SAMPLES_PER_FRAME, TEST_CYCLES_PER_BLIP, TEST_AMPLITUDE);
// -------- initialization --------------
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
// Start with buffer twice as large as needed.
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
minBuffSize * 2, TEST_MODE);
// -------- test --------------
// Initial values
int minBuffSizeInFrames = minBuffSize / (TEST_SAMPLES_PER_FRAME * TEST_BYTES_PER_SAMPLE);
int underrunCount1 = track.getUnderrunCount();
assertEquals(TEST_NAME + ": initially no underruns", 0, underrunCount1);
// Prime the buffer.
while (track.write(data, 0, data.length) == data.length);
// Start playing
track.play();
int numBuffers = TEST_SR / TEST_FRAMES_PER_BUFFER;
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
}
int underrunCountBase = track.getUnderrunCount();
int numSeconds = TEST_NUM_SECONDS;
numBuffers = numSeconds * TEST_SR / TEST_FRAMES_PER_BUFFER;
track.write(blip, 0, blip.length);
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
}
underrunCount1 = track.getUnderrunCount();
assertEquals(TEST_NAME + ": no more underruns after initial",
underrunCountBase, underrunCount1);
// Sleep and force underruns.
track.write(blip, 0, blip.length);
for (int i = 0; i < 10; i++) {
track.write(data, 0, data.length);
Thread.sleep(500); // ========================= SLEEP! ===========
}
track.write(blip, 0, blip.length);
underrunCount1 = track.getUnderrunCount();
assertTrue(TEST_NAME + ": expect underruns after sleep, #ur="
+ underrunCount1,
underrunCountBase < underrunCount1);
track.write(blip, 0, blip.length);
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
}
// Should have stopped by now.
underrunCount1 = track.getUnderrunCount();
track.write(blip, 0, blip.length);
for (int i = 0; i < numBuffers; i++) {
track.write(data, 0, data.length);
}
// Counts should match.
int underrunCount2 = track.getUnderrunCount();
assertEquals(TEST_NAME + ": underruns should stop happening",
underrunCount1, underrunCount2);
// -------- tear down --------------
track.release();
}
static class TrackBufferSizeChecker {
private final static String TEST_NAME = "testTrackBufferSize";
private final static int TEST_SR = 48000;
private final static int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
private final static int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
private final static int TEST_MODE = AudioTrack.MODE_STREAM;
private final static int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
private final static int FRAME_SIZE = 2 * 2; // stereo 16-bit PCM
public static int getFrameSize() {
return FRAME_SIZE;
}
public static int getMinBufferSize() {
return AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
}
public static AudioTrack createAudioTrack(int bufferSize) {
return new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
bufferSize, TEST_MODE);
}
public static void checkBadSize(int bufferSize) {
AudioTrack track = null;
try {
track = TrackBufferSizeChecker.createAudioTrack(bufferSize);
assertTrue(TEST_NAME + ": should not have survived size " + bufferSize, false);
} catch(IllegalArgumentException e) {
// expected
} finally {
if (track != null) {
track.release();
}
}
}
public static void checkSmallSize(int bufferSize) {
AudioTrack track = null;
try {
track = TrackBufferSizeChecker.createAudioTrack(bufferSize);
assertEquals(TEST_NAME + ": should still be initialized with small size " + bufferSize,
AudioTrack.STATE_INITIALIZED, track.getState());
} finally {
if (track != null) {
track.release();
}
}
}
}
/**
* Test various values for bufferSizeInBytes.
*
* According to the latest documentation, any positive bufferSize that is a multiple
* of the frameSize is legal. Small sizes will be rounded up to the minimum size.
*
* Negative sizes, zero, or any non-multiple of the frameSize is illegal.
*
* @throws Exception
*/
public void testTrackBufferSize() throws Exception {
TrackBufferSizeChecker.checkBadSize(0);
TrackBufferSizeChecker.checkBadSize(17);
TrackBufferSizeChecker.checkBadSize(18);
TrackBufferSizeChecker.checkBadSize(-9);
int frameSize = TrackBufferSizeChecker.getFrameSize();
TrackBufferSizeChecker.checkBadSize(-4 * frameSize);
for (int i = 1; i < 8; i++) {
TrackBufferSizeChecker.checkSmallSize(i * frameSize);
TrackBufferSizeChecker.checkBadSize(3 + (i * frameSize));
}
}
}