1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media.audio.cts;
18 
19 import android.annotation.RawRes;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.content.res.AssetFileDescriptor;
23 import android.content.res.Resources;
24 import android.media.AudioAttributes;
25 import android.media.AudioFormat;
26 import android.media.AudioManager;
27 import android.media.AudioProfile;
28 import android.media.AudioTimestamp;
29 import android.media.AudioTrack;
30 import android.media.audio.cts.R;
31 import android.platform.test.annotations.AppModeSdkSandbox;
32 import android.util.Log;
33 
34 import com.android.compatibility.common.util.CtsAndroidTestCase;
35 import com.android.compatibility.common.util.NonMainlineTest;
36 
37 import java.io.BufferedInputStream;
38 import java.io.ByteArrayOutputStream;
39 import java.io.InputStream;
40 import java.nio.ByteBuffer;
41 import java.nio.ByteOrder;
42 import java.nio.ShortBuffer;
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.Random;
46 
47 // Test the Java AudioTrack surround sound and HDMI passthrough.
48 // Most tests involve creating a track with a given format and then playing
49 // a few seconds of audio. The playback is verified by measuring the output
50 // sample rate based on the AudioTimestamps.
51 
52 @NonMainlineTest
53 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
54 public class AudioTrackSurroundTest extends CtsAndroidTestCase {
55     private static final String TAG = "AudioTrackSurroundTest";
56 
57     private static final double MAX_RATE_TOLERANCE_FRACTION = 0.01;
58     private static final boolean LOG_TIMESTAMPS = false; // set true for debugging
59     // just long enough to measure the rate
60     private static final long SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS = 5000;
61     // AC3 and IEC61937 tracks require more time
62     private static final long SAMPLE_RATE_LONG_TEST_DURATION_MILLIS = 12000;
63 
64     // Should we fail if there is no PCM16 profile reported?
65     // This can happen if, for example, an ATV set top box does not have its HDMI cable plugged in.
66     private static final boolean REQUIRE_PCM_PROFILE = false;
67 
68     private final static long NANOS_PER_MILLISECOND = 1000000L;
69     private final static int MILLIS_PER_SECOND = 1000;
70     private final static long NANOS_PER_SECOND = NANOS_PER_MILLISECOND * MILLIS_PER_SECOND;
71 
72     private final static int RES_AC3_SPDIF_VOICE_32000 = R.raw.voice12_32k_128kbps_15s_ac3_spdif;
73     private final static int RES_AC3_SPDIF_VOICE_44100 = R.raw.voice12_44k_128kbps_15s_ac3_spdif;
74     private final static int RES_AC3_SPDIF_VOICE_48000 = R.raw.voice12_48k_128kbps_15s_ac3_spdif;
75     private final static int RES_AC3_VOICE_48000 = R.raw.voice12_48k_128kbps_15s_ac3;
76 
77     private static int mLastPlayedEncoding = AudioFormat.ENCODING_INVALID;
78 
79     // Profiles that support various encodings.
80     private static AudioProfile mProfilePCM16 = null;
81     private static AudioProfile mProfileAC3 = null;
82     private static AudioProfile mProfileE_AC3 = null;
83     private static AudioProfile mProfileDTS = null;
84     private static AudioProfile mProfileDTS_HD = null;
85     private static AudioProfile mProfileIEC61937 = null;
86 
87     private static AudioAttributes mAudioAttributes = null;
88 
log(String testName, String message)89     private static void log(String testName, String message) {
90         Log.i(TAG, "[" + testName + "] " + message);
91     }
92 
logw(String testName, String message)93     private static void logw(String testName, String message) {
94         Log.w(TAG, "[" + testName + "] " + message);
95     }
96 
loge(String testName, String message)97     private static void loge(String testName, String message) {
98         Log.e(TAG, "[" + testName + "] " + message);
99     }
100 
101     // This is a special method that is called automatically before each test.
102     @Override
setUp()103     protected void setUp() throws Exception {
104         // Note that I tried to only scan for encodings once but the static
105         // data did not persist properly. That may be a bug.
106         // For now, just scan before every test.
107         scanProfilesForEncodings();
108     }
109 
scanProfilesForEncodings()110     private void scanProfilesForEncodings() throws Exception {
111         final String MTAG = "scanProfilesForEncodings";
112         // Scan profiles to see which encodings are supported.
113         AudioManager audioManager = (AudioManager) getContext()
114                 .getSystemService(Context.AUDIO_SERVICE);
115         mAudioAttributes = new AudioAttributes.Builder()
116                                 .setUsage(AudioAttributes.USAGE_MEDIA)
117                                 .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
118                                 .build();
119         List<AudioProfile> profiles = audioManager.getDirectProfilesForAttributes(mAudioAttributes);
120         if (profiles.size() == 0) {
121             log(MTAG, "no direct profiles for media + music found");
122         }
123         for (AudioProfile profile : profiles) {
124             log(MTAG, "scanning profiles, profile = " + profile.toString());
125             if (profile.getEncapsulationType() == AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937) {
126                 mProfileIEC61937 = profile;
127                 log(MTAG, "mProfileIEC61937 set to " + profile);
128                 break;
129             } else { // AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE
130                 switch (profile.getFormat()) {
131                     case AudioFormat.ENCODING_PCM_16BIT:
132                         mProfilePCM16 = profile;
133                         log(MTAG, "mProfilePCM16 set to " + profile);
134                         break;
135                     case AudioFormat.ENCODING_AC3:
136                         mProfileAC3 = profile;
137                         log(MTAG, "mProfileAC3 set to " + profile);
138                         break;
139                     case AudioFormat.ENCODING_E_AC3:
140                         mProfileE_AC3 = profile;
141                         log(MTAG, "mProfileE_AC3 set to " + profile);
142                         break;
143                     case AudioFormat.ENCODING_DTS:
144                         mProfileDTS = profile;
145                         log(MTAG, "mProfileDTS set to " + profile);
146                         break;
147                     case AudioFormat.ENCODING_DTS_HD:
148                         mProfileDTS_HD = profile;
149                         log(MTAG, "mProfileDTS_HD set to " + profile);
150                         break;
151                     default:
152                         // This is OK. It is just an encoding that we don't care about.
153                         break;
154                 }
155             }
156 
157         }
158     }
159 
160     // Load a resource into a byte[]
loadRawResourceBytes(@awRes int id)161     private byte[] loadRawResourceBytes(@RawRes int id) throws Exception {
162         InputStream is = getContext().getResources().openRawResource(id);
163         ByteArrayOutputStream bos = new ByteArrayOutputStream();
164         try (BufferedInputStream bis = new BufferedInputStream(is)) {
165             for (int b = bis.read(); b != -1; b = bis.read()) {
166                 bos.write(b);
167             }
168         }
169         return bos.toByteArray();
170     }
171 
172     // Load a resource into a short[]
loadRawResourceShorts(@awRes int id)173     private short[] loadRawResourceShorts(@RawRes int id) throws Exception {
174         byte[] byteBuffer = loadRawResourceBytes(id);
175         ShortBuffer shortBuffer =
176                 ByteBuffer.wrap(byteBuffer).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
177         // Unfortunately, ShortBuffer.array() works with allocated buffers only.
178         short[] mainBuffer = new short[byteBuffer.length / 2];
179         for (int i = 0; i < mainBuffer.length; i++) {
180             mainBuffer[i] = shortBuffer.get();
181         }
182         return mainBuffer;
183     }
184 
testLoadSineSweep()185     public void testLoadSineSweep() throws Exception {
186         final String TEST_NAME = "testLoadSineSweep";
187         short[] shortData = loadRawResourceShorts(R.raw.sinesweepraw);
188         assertTrue(TEST_NAME + ": load sinesweepraw as shorts", shortData.length > 100);
189         byte[] byteData = loadRawResourceBytes(R.raw.sinesweepraw);
190         assertTrue(TEST_NAME + ": load sinesweepraw as bytes", byteData.length > shortData.length);
191     }
192 
createAudioTrack(int sampleRate, int encoding, int channelConfig)193     private static AudioTrack createAudioTrack(int sampleRate, int encoding, int channelConfig) {
194         final String TEST_NAME = "createAudioTrack";
195         int minBufferSize = AudioTrack.getMinBufferSize(
196                 sampleRate, channelConfig,
197                 encoding);
198         assertTrue(TEST_NAME + ": getMinBufferSize", minBufferSize > 0);
199         int bufferSize = minBufferSize * 3; // plenty big
200         AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC,
201                 sampleRate, channelConfig,
202                 encoding, bufferSize,
203                 AudioTrack.MODE_STREAM);
204         return track;
205     }
206 
207     static class TimestampAnalyzer {
208         ArrayList<AudioTimestamp> mTimestamps = new ArrayList<AudioTimestamp>();
209         AudioTimestamp mPreviousTimestamp = null;
210 
timestampToString(AudioTimestamp timestamp)211         static String timestampToString(AudioTimestamp timestamp) {
212             if (timestamp == null)
213                 return "null";
214             return "(pos = " + timestamp.framePosition + ", nanos = " + timestamp.nanoTime + ")";
215         }
216 
217         // Add timestamp if unique and valid.
addTimestamp(AudioTrack track)218         void addTimestamp(AudioTrack track) {
219             AudioTimestamp timestamp = new AudioTimestamp();
220             boolean gotTimestamp = track.getTimestamp(timestamp);
221             if (gotTimestamp) {
222                 // Only save timestamps after the data is flowing.
223                 boolean accepted = mPreviousTimestamp != null
224                         && timestamp.framePosition > 0
225                         && timestamp.nanoTime != mPreviousTimestamp.nanoTime
226                         && timestamp.framePosition != mPreviousTimestamp.framePosition;
227                 if (accepted) {
228                     mTimestamps.add(timestamp);
229                 }
230                 Log.d(TAG, (accepted ? "" : "NOT ") + "added ts " + timestampToString(timestamp));
231                 mPreviousTimestamp = timestamp;
232             }
233         }
234 
checkIndividualTimestamps(int sampleRate)235         void checkIndividualTimestamps(int sampleRate) {
236             AudioTimestamp previous = null;
237             double sumDeltaSquared = 0.0;
238             int populationSize = 0;
239             double maxDeltaMillis = 0.0;
240             // Make sure the timestamps are smooth and don't go retrograde.
241             for (AudioTimestamp timestamp : mTimestamps) {
242                 if (previous != null) {
243 
244                     assertTrue("framePosition must be monotonic",
245                             timestamp.framePosition >= previous.framePosition);
246                     assertTrue("nanoTime must be monotonic",
247                             timestamp.nanoTime >= previous.nanoTime);
248 
249                     if (timestamp.framePosition > previous.framePosition) {
250                         // Measure timing jitter.
251                         // Calculate predicted duration based on measured rate and compare
252                         // it with actual duration.
253                         final double TOLERANCE_MILLIS = 2.0;
254                         long elapsedFrames = timestamp.framePosition - previous.framePosition;
255                         long elapsedNanos = timestamp.nanoTime - previous.nanoTime;
256                         double measuredMillis = elapsedNanos / (double) NANOS_PER_MILLISECOND;
257                         double expectedMillis = elapsedFrames * (double) MILLIS_PER_SECOND
258                             / sampleRate;
259                         double deltaMillis = measuredMillis - expectedMillis;
260                         sumDeltaSquared += deltaMillis * deltaMillis;
261                         populationSize++;
262                         // We only issue a warning here because the CDD does not mandate a
263                         // specific tolerance.
264                         double absDeltaMillis = Math.abs(deltaMillis);
265                         if (absDeltaMillis > TOLERANCE_MILLIS) {
266                             Log.w(TAG, "measured time exceeds expected"
267                                 + ", srate = " + sampleRate
268                                 + ", frame = " + timestamp.framePosition
269                                 + ", expected = " + expectedMillis
270                                 + ", measured = " + measuredMillis + " (msec)"
271                                 );
272                         }
273                         if (absDeltaMillis > maxDeltaMillis) {
274                             maxDeltaMillis = absDeltaMillis;
275                         }
276                     }
277                 }
278                 previous = timestamp;
279             }
280             Log.d(TAG, "max abs(delta) from expected duration = " + maxDeltaMillis + " msec");
281             if (populationSize > 0) {
282                 double deviation = Math.sqrt(sumDeltaSquared / populationSize);
283                 Log.d(TAG, "standard deviation from expected duration = " + deviation + " msec");
284             }
285         }
286 
287         // Use collected timestamps to estimate a sample rate.
estimateSampleRate()288         double estimateSampleRate() {
289             Log.w(TAG, "timestamps collected: " + mTimestamps.size());
290             assertTrue("expect many timestamps, got " + mTimestamps.size(),
291                     mTimestamps.size() > 10);
292             // Use first and last timestamp to get the most accurate rate.
293             AudioTimestamp first = mTimestamps.get(0);
294             AudioTimestamp last = mTimestamps.get(mTimestamps.size() - 1);
295             return calculateSampleRate(first, last);
296         }
297 
298         /**
299          * @param timestamp1
300          * @param timestamp2
301          */
calculateSampleRate(AudioTimestamp timestamp1, AudioTimestamp timestamp2)302         private double calculateSampleRate(AudioTimestamp timestamp1, AudioTimestamp timestamp2) {
303             long elapsedFrames = timestamp2.framePosition - timestamp1.framePosition;
304             long elapsedNanos = timestamp2.nanoTime - timestamp1.nanoTime;
305             double measuredRate = elapsedFrames * (double) NANOS_PER_SECOND / elapsedNanos;
306             if (LOG_TIMESTAMPS) {
307                 Log.i(TAG, "calculateSampleRate(), elapsedFrames =, " + elapsedFrames
308                         + ", measuredRate =, "
309                         + (int) measuredRate);
310             }
311             return measuredRate;
312         }
313     }
314 
315     // Class for looping a recording for several seconds and measuring the sample rate.
316     // This is not static because it needs to call getContext().
317     abstract class SamplePlayerBase {
318         private final int mSampleRate;
319         private final int mEncoding;
320         private final int mChannelConfig;
321         private int mBlockSize = 512;
322         protected int mOffset = 0;
323         protected AudioTrack mTrack;
324         private final TimestampAnalyzer mTimestampAnalyzer = new TimestampAnalyzer();
325 
SamplePlayerBase(int sampleRate, int encoding, int channelConfig)326         SamplePlayerBase(int sampleRate, int encoding, int channelConfig) {
327             mSampleRate = sampleRate;
328             mEncoding = encoding;
329             mChannelConfig = channelConfig;
330         }
331 
332         // Use abstract write to handle byte[] or short[] data.
writeBlock(int numSamples)333         protected abstract int writeBlock(int numSamples);
334 
primeBuffer()335         private int primeBuffer() {
336             // Will not block when track is stopped.
337             return writeBlock(Integer.MAX_VALUE);
338         }
339 
340         // Add a warning to the assert message that might help folks figure out why their
341         // PCM test is failing.
getPcmWarning()342         private String getPcmWarning() {
343             return (mProfilePCM16 == null && AudioFormat.isEncodingLinearPcm(mEncoding))
344                 ? " (No PCM profile!)" : "";
345         }
346 
playAndMeasureRate(long testDurationMillis)347         public void playAndMeasureRate(long testDurationMillis) throws Exception {
348             final String TEST_NAME = "playAndMeasureRate";
349 
350             if (mLastPlayedEncoding == AudioFormat.ENCODING_INVALID ||
351                     !AudioFormat.isEncodingLinearPcm(mEncoding) ||
352                     !AudioFormat.isEncodingLinearPcm(mLastPlayedEncoding)) {
353                 Log.d(TAG, "switching from format: " + mLastPlayedEncoding
354                         + " to: " + mEncoding
355                         + " requires sleep");
356                 // Switching between compressed formats may require
357                 // some time for the HAL to adjust and give proper timing.
358                 // One second should be ok, but we use 2 just in case.
359                 Thread.sleep(2000 /* millis */);
360             }
361             mLastPlayedEncoding = mEncoding;
362 
363             log(TEST_NAME, String.format("test using rate = %d, encoding = 0x%08x",
364                     mSampleRate, mEncoding));
365             // Create a track and prime it.
366             mTrack = createAudioTrack(mSampleRate, mEncoding, mChannelConfig);
367             try {
368                 assertEquals(TEST_NAME + ": track created " + getPcmWarning(),
369                         AudioTrack.STATE_INITIALIZED,
370                         mTrack.getState());
371 
372                 int bytesWritten = 0;
373                 mOffset = primeBuffer(); // prime the buffer
374                 assertTrue(TEST_NAME + ": priming offset = " + mOffset + ", " + getPcmWarning(),
375                     mOffset > 0);
376                 bytesWritten += mOffset;
377 
378                 // Play for a while.
379                 mTrack.play();
380 
381                 log(TEST_NAME, "native rate = "
382                         + mTrack.getNativeOutputSampleRate(mTrack.getStreamType()));
383                 long elapsedMillis = 0;
384                 long startTime = System.currentTimeMillis();
385                 while (elapsedMillis < testDurationMillis) {
386                     writeBlock(mBlockSize);
387                     elapsedMillis = System.currentTimeMillis() - startTime;
388                     mTimestampAnalyzer.addTimestamp(mTrack);
389                 }
390 
391                 // Did we underrun? Allow 0 or 1 because there is sometimes
392                 // an underrun on startup.
393                 int underrunCount1 = mTrack.getUnderrunCount();
394                 assertTrue(TEST_NAME + ": too many underruns, got " + underrunCount1
395                         + ", " + getPcmWarning(),
396                         underrunCount1 < 2);
397 
398                 // Estimate the sample rate and compare it with expected.
399                 double estimatedRate = mTimestampAnalyzer.estimateSampleRate();
400                 Log.d(TAG, "measured sample rate = " + estimatedRate);
401                 assertEquals(TEST_NAME + ": measured sample rate " + getPcmWarning(),
402                         mSampleRate, estimatedRate, mSampleRate * MAX_RATE_TOLERANCE_FRACTION);
403 
404                 // Check for jitter or retrograde motion in each timestamp.
405                 mTimestampAnalyzer.checkIndividualTimestamps(mSampleRate);
406 
407             } finally {
408                 mTrack.release();
409             }
410         }
411     }
412 
413     // Create player for short[]
414     class SamplePlayerShorts extends SamplePlayerBase {
415         private final short[] mData;
416 
SamplePlayerShorts(int sampleRate, int encoding, int channelConfig)417         SamplePlayerShorts(int sampleRate, int encoding, int channelConfig) {
418             super(sampleRate, encoding, channelConfig);
419             mData = new short[64 * 1024];
420             // Fill with noise. We should not hear the noise for IEC61937.
421             int amplitude = 8000;
422             Random random = new Random();
423             for (int i = 0; i < mData.length; i++) {
424                 mData[i] = (short)(random.nextInt(amplitude) - (amplitude / 2));
425             }
426         }
427 
SamplePlayerShorts(int sampleRate, int encoding, int channelConfig, @RawRes int resourceId)428         SamplePlayerShorts(int sampleRate, int encoding, int channelConfig, @RawRes int resourceId)
429                 throws Exception {
430             super(sampleRate, encoding, channelConfig);
431             mData = loadRawResourceShorts(resourceId);
432             assertTrue("SamplePlayerShorts: load resource file as shorts", mData.length > 0);
433         }
434 
435         @Override
writeBlock(int numShorts)436         protected int writeBlock(int numShorts) {
437             int result = 0;
438             int shortsToWrite = numShorts;
439             int shortsLeft = mData.length - mOffset;
440             if (shortsToWrite > shortsLeft) {
441                 shortsToWrite = shortsLeft;
442             }
443             if (shortsToWrite > 0) {
444                 result = mTrack.write(mData, mOffset, shortsToWrite);
445                 mOffset += result;
446             } else {
447                 mOffset = 0; // rewind
448             }
449             return result;
450         }
451     }
452 
453     // Create player for byte[]
454     class SamplePlayerBytes extends SamplePlayerBase {
455         private final byte[] mData;
456 
SamplePlayerBytes(int sampleRate, int encoding, int channelConfig)457         SamplePlayerBytes(int sampleRate, int encoding, int channelConfig) {
458             super(sampleRate, encoding, channelConfig);
459             mData = new byte[128 * 1024];
460         }
461 
SamplePlayerBytes(int sampleRate, int encoding, int channelConfig, @RawRes int resourceId)462         SamplePlayerBytes(int sampleRate, int encoding, int channelConfig, @RawRes int resourceId)
463                 throws Exception {
464             super(sampleRate, encoding, channelConfig);
465             mData = loadRawResourceBytes(resourceId);
466             assertTrue("SamplePlayerBytes: load resource file as bytes", mData.length > 0);
467         }
468 
469         @Override
writeBlock(int numBytes)470         protected int writeBlock(int numBytes) {
471             int result = 0;
472             int bytesToWrite = numBytes;
473             int bytesLeft = mData.length - mOffset;
474             if (bytesToWrite > bytesLeft) {
475                 bytesToWrite = bytesLeft;
476             }
477             if (bytesToWrite > 0) {
478                 result = mTrack.write(mData, mOffset, bytesToWrite);
479                 mOffset += result;
480             } else {
481                 mOffset = 0; // rewind
482             }
483             return result;
484         }
485     }
486 
testPlayAC3Bytes()487     public void testPlayAC3Bytes() throws Exception {
488         if (mProfileAC3 != null) {
489             SamplePlayerBytes player = new SamplePlayerBytes(
490                     48000, AudioFormat.ENCODING_AC3, AudioFormat.CHANNEL_OUT_STEREO,
491                     RES_AC3_VOICE_48000);
492             player.playAndMeasureRate(SAMPLE_RATE_LONG_TEST_DURATION_MILLIS);
493         }
494     }
495 
testPlayAC3Shorts()496     public void testPlayAC3Shorts() throws Exception {
497         if (mProfileAC3 != null) {
498             SamplePlayerShorts player = new SamplePlayerShorts(
499                     48000, AudioFormat.ENCODING_AC3, AudioFormat.CHANNEL_OUT_STEREO,
500                     RES_AC3_VOICE_48000);
501             player.playAndMeasureRate(SAMPLE_RATE_LONG_TEST_DURATION_MILLIS);
502         }
503     }
504 
testPlayIEC61937_32000()505     public void testPlayIEC61937_32000() throws Exception {
506         if (mProfileIEC61937 != null) {
507             SamplePlayerShorts player = new SamplePlayerShorts(
508                     32000, AudioFormat.ENCODING_IEC61937, AudioFormat.CHANNEL_OUT_STEREO,
509                     RES_AC3_SPDIF_VOICE_32000);
510             player.playAndMeasureRate(SAMPLE_RATE_LONG_TEST_DURATION_MILLIS);
511         }
512     }
513 
testPlayIEC61937_44100()514     public void testPlayIEC61937_44100() throws Exception {
515         if (mProfileIEC61937 != null) {
516             SamplePlayerShorts player = new SamplePlayerShorts(
517                     44100, AudioFormat.ENCODING_IEC61937, AudioFormat.CHANNEL_OUT_STEREO,
518                     RES_AC3_SPDIF_VOICE_44100);
519             player.playAndMeasureRate(SAMPLE_RATE_LONG_TEST_DURATION_MILLIS);
520         }
521     }
522 
testPlayIEC61937_48000()523     public void testPlayIEC61937_48000() throws Exception {
524         if (mProfileIEC61937 != null) {
525             SamplePlayerShorts player = new SamplePlayerShorts(
526                     48000, AudioFormat.ENCODING_IEC61937, AudioFormat.CHANNEL_OUT_STEREO,
527                     RES_AC3_SPDIF_VOICE_48000);
528             player.playAndMeasureRate(SAMPLE_RATE_LONG_TEST_DURATION_MILLIS);
529         }
530     }
531 
testPcmSupport()532     public void testPcmSupport() throws Exception {
533         if (REQUIRE_PCM_PROFILE) {
534             // There should always be a fake PCM profile available.
535             assertTrue("testPcmSupport: PCM should be supported."
536                     + " On ATV device please check HDMI connection.",
537                     mProfilePCM16 != null);
538         }
539     }
540 
isPcmTestingEnabled()541     private boolean isPcmTestingEnabled() {
542         return (mProfilePCM16 != null || !REQUIRE_PCM_PROFILE);
543     }
544 
testPlaySineSweepShorts()545     public void testPlaySineSweepShorts() throws Exception {
546         if (isPcmTestingEnabled()) {
547             SamplePlayerShorts player = new SamplePlayerShorts(
548                     44100, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_OUT_STEREO,
549                     R.raw.sinesweepraw);
550             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
551         }
552     }
553 
testPlaySineSweepBytes()554     public void testPlaySineSweepBytes() throws Exception {
555         if (isPcmTestingEnabled()) {
556             SamplePlayerBytes player = new SamplePlayerBytes(
557                     44100, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_OUT_STEREO,
558                     R.raw.sinesweepraw);
559             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
560         }
561     }
562 
testPlaySineSweepBytes48000()563     public void testPlaySineSweepBytes48000() throws Exception {
564         if (isPcmTestingEnabled()) {
565             SamplePlayerBytes player = new SamplePlayerBytes(
566                     48000, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_OUT_STEREO,
567                     R.raw.sinesweepraw);
568             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
569         }
570     }
571 
testPlaySineSweepShortsMono()572     public void testPlaySineSweepShortsMono() throws Exception {
573         if (isPcmTestingEnabled()) {
574             SamplePlayerShorts player = new SamplePlayerShorts(44100, AudioFormat.ENCODING_PCM_16BIT,
575                     AudioFormat.CHANNEL_OUT_MONO,
576                     R.raw.sinesweepraw);
577             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
578         }
579     }
580 
testPlaySineSweepBytesMono()581     public void testPlaySineSweepBytesMono()
582             throws Exception {
583         if (isPcmTestingEnabled()) {
584             SamplePlayerBytes player = new SamplePlayerBytes(44100, AudioFormat.ENCODING_PCM_16BIT,
585                     AudioFormat.CHANNEL_OUT_MONO, R.raw.sinesweepraw);
586             player.playAndMeasureRate(SAMPLE_RATE_SHORT_TEST_DURATION_MILLIS);
587         }
588     }
589 
590 }
591