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