| /* |
| * Copyright (C) 2015 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.content.pm.PackageManager; |
| import android.cts.util.CtsAndroidTestCase; |
| import android.util.Log; |
| |
| public class AudioNativeTest extends CtsAndroidTestCase { |
| public void testAppendixBBufferQueue() { |
| nativeAppendixBBufferQueue(); |
| } |
| |
| public void testAppendixBRecording() { |
| // better to detect presence of microphone here. |
| if (!hasMicrophone()) { |
| return; |
| } |
| nativeAppendixBRecording(); |
| } |
| |
| public void testStereo16Playback() { |
| assertTrue(AudioTrackNative.test( |
| 2 /* numChannels */, 48000 /* sampleRate */, false /* useFloat */, |
| 20 /* msecPerBuffer */, 8 /* numBuffers */)); |
| } |
| |
| public void testStereo16Record() { |
| if (!hasMicrophone()) { |
| return; |
| } |
| assertTrue(AudioRecordNative.test( |
| 2 /* numChannels */, 48000 /* sampleRate */, false /* useFloat */, |
| 20 /* msecPerBuffer */, 8 /* numBuffers */)); |
| } |
| |
| public void testPlayStreamData() throws Exception { |
| final String TEST_NAME = "testPlayStreamData"; |
| final boolean TEST_FLOAT_ARRAY[] = { |
| false, |
| true, |
| }; |
| // due to downmixer algorithmic latency, source channels greater than 2 may |
| // sound shorter in duration at 4kHz sampling rate. |
| final int TEST_SR_ARRAY[] = { |
| /* 4000, */ // below limit of OpenSL ES |
| 12345, // irregular sampling rate |
| 44100, |
| 48000, |
| 96000, |
| 192000, |
| }; |
| final int TEST_CHANNELS_ARRAY[] = { |
| 1, |
| 2, |
| 3, |
| 4, |
| 5, |
| 6, |
| 7, |
| // 8 // can fail due to memory issues |
| }; |
| final float TEST_SWEEP = 0; // sine wave only |
| final int TEST_TIME_IN_MSEC = 300; |
| final int TOLERANCE_MSEC = 20; |
| |
| for (boolean TEST_FLOAT : TEST_FLOAT_ARRAY) { |
| double frequency = 400; // frequency changes for each test |
| for (int TEST_SR : TEST_SR_ARRAY) { |
| for (int TEST_CHANNELS : TEST_CHANNELS_ARRAY) { |
| // OpenSL ES BUG: we run out of AudioTrack memory for this config on MNC |
| // Log.d(TEST_NAME, "open channels:" + TEST_CHANNELS + " sr:" + TEST_SR); |
| if (TEST_FLOAT == true && TEST_CHANNELS >= 6 && TEST_SR >= 192000) { |
| continue; |
| } |
| AudioTrackNative track = new AudioTrackNative(); |
| assertTrue(TEST_NAME, |
| track.open(TEST_CHANNELS, TEST_SR, TEST_FLOAT, 1 /* numBuffers */)); |
| assertTrue(TEST_NAME, track.start()); |
| |
| final int sourceSamples = |
| (int)((long)TEST_SR * TEST_TIME_IN_MSEC * TEST_CHANNELS / 1000); |
| final double testFrequency = frequency / TEST_CHANNELS; |
| if (TEST_FLOAT) { |
| float data[] = AudioHelper.createSoundDataInFloatArray( |
| sourceSamples, TEST_SR, |
| testFrequency, TEST_SWEEP); |
| assertEquals(sourceSamples, |
| track.write(data, 0 /* offset */, sourceSamples, |
| AudioTrackNative.WRITE_FLAG_BLOCKING)); |
| } else { |
| short data[] = AudioHelper.createSoundDataInShortArray( |
| sourceSamples, TEST_SR, |
| testFrequency, TEST_SWEEP); |
| assertEquals(sourceSamples, |
| track.write(data, 0 /* offset */, sourceSamples, |
| AudioTrackNative.WRITE_FLAG_BLOCKING)); |
| } |
| |
| while (true) { |
| // OpenSL ES BUG: getPositionInMsec returns 0 after a data underrun. |
| |
| long position = track.getPositionInMsec(); |
| //Log.d(TEST_NAME, "position: " + position[0]); |
| if (position >= (long)(TEST_TIME_IN_MSEC - TOLERANCE_MSEC)) { |
| break; |
| } |
| |
| // It is safer to use a buffer count of 0 to determine termination |
| if (track.getBuffersPending() == 0) { |
| break; |
| } |
| Thread.sleep(5 /* millis */); |
| } |
| track.stop(); |
| track.close(); |
| Thread.sleep(40 /* millis */); // put a gap in the tone sequence |
| frequency += 50; // increment test tone frequency |
| } |
| } |
| } |
| } |
| |
| public void testRecordStreamData() throws Exception { |
| if (!hasMicrophone()) { |
| return; |
| } |
| final String TEST_NAME = "testRecordStreamData"; |
| final boolean TEST_FLOAT_ARRAY[] = { |
| false, |
| true, |
| }; |
| final int TEST_SR_ARRAY[] = { |
| //4000, // below limit of OpenSL ES |
| 12345, // irregular sampling rate |
| 44100, |
| 48000, |
| 96000, |
| 192000, |
| }; |
| final int TEST_CHANNELS_ARRAY[] = { |
| 1, |
| 2, |
| 3, |
| 4, |
| 5, |
| 6, |
| 7, |
| 8, |
| }; |
| final int SEGMENT_DURATION_IN_MSEC = 20; |
| final int NUMBER_SEGMENTS = 10; |
| |
| for (boolean TEST_FLOAT : TEST_FLOAT_ARRAY) { |
| for (int TEST_SR : TEST_SR_ARRAY) { |
| for (int TEST_CHANNELS : TEST_CHANNELS_ARRAY) { |
| // OpenSL ES BUG: we run out of AudioTrack memory for this config on MNC |
| if (TEST_FLOAT == true && TEST_CHANNELS >= 8 && TEST_SR >= 192000) { |
| continue; |
| } |
| AudioRecordNative record = new AudioRecordNative(); |
| doRecordTest(record, TEST_CHANNELS, TEST_SR, TEST_FLOAT, |
| SEGMENT_DURATION_IN_MSEC, NUMBER_SEGMENTS); |
| } |
| } |
| } |
| } |
| |
| public void testRecordAudit() throws Exception { |
| if (!hasMicrophone()) { |
| return; |
| } |
| AudioRecordNative record = new AudioHelper.AudioRecordAuditNative(); |
| doRecordTest(record, 4 /* numChannels */, 44100 /* sampleRate */, false /* useFloat */, |
| 1000 /* segmentDurationMs */, 10 /* numSegments */); |
| } |
| |
| static { |
| System.loadLibrary("audio_jni"); |
| } |
| |
| private static final String TAG = "AudioNativeTest"; |
| |
| private void doRecordTest(AudioRecordNative record, |
| int numChannels, int sampleRate, boolean useFloat, |
| int segmentDurationMs, int numSegments) { |
| final String TEST_NAME = "doRecordTest"; |
| try { |
| // Log.d(TEST_NAME, "open numChannels:" + numChannels + " sampleRate:" + sampleRate); |
| assertTrue(TEST_NAME, record.open(numChannels, sampleRate, useFloat, |
| numSegments /* numBuffers */)); |
| assertTrue(TEST_NAME, record.start()); |
| |
| final int sourceSamples = |
| (int)((long)sampleRate * segmentDurationMs * numChannels / 1000); |
| |
| if (useFloat) { |
| float data[] = new float[sourceSamples]; |
| for (int i = 0; i < numSegments; ++i) { |
| assertEquals(sourceSamples, |
| record.read(data, 0 /* offset */, sourceSamples, |
| AudioRecordNative.READ_FLAG_BLOCKING)); |
| } |
| } else { |
| short data[] = new short[sourceSamples]; |
| for (int i = 0; i < numSegments; ++i) { |
| assertEquals(sourceSamples, |
| record.read(data, 0 /* offset */, sourceSamples, |
| AudioRecordNative.READ_FLAG_BLOCKING)); |
| } |
| } |
| assertTrue(TEST_NAME, record.stop()); |
| } finally { |
| record.close(); |
| } |
| } |
| |
| private boolean hasMicrophone() { |
| return getContext().getPackageManager().hasSystemFeature( |
| PackageManager.FEATURE_MICROPHONE); |
| } |
| |
| private static native void nativeAppendixBBufferQueue(); |
| private static native void nativeAppendixBRecording(); |
| } |