1 /* 2 * Copyright (C) 2015 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 org.drrickorang.loopback; 18 19 package com.android.cts.verifier.audio; 20 21 import android.media.AudioFormat; 22 import android.media.AudioManager; 23 import android.media.AudioTrack; 24 //import android.media.MediaPlayer; 25 import android.media.AudioRecord; 26 import android.media.MediaRecorder; 27 import android.util.Log; 28 29 import android.os.Handler; 30 import android.os.Message; 31 32 /** 33 * A thread/audio track based audio synth. 34 */ 35 public class NativeAudioThread extends Thread { 36 37 public boolean isRunning = false; 38 double twoPi = 6.28318530718; 39 40 public int mSessionId; 41 42 public double[] mvSamples; //captured samples 43 int mSamplesIndex; 44 45 private final int mSecondsToRun = 2; 46 public int mSamplingRate = 48000; 47 private int mChannelConfigIn = AudioFormat.CHANNEL_IN_MONO; 48 private int mAudioFormat = AudioFormat.ENCODING_PCM_16BIT; 49 50 int mMinPlayBufferSizeInBytes = 0; 51 int mMinRecordBuffSizeInBytes = 0; 52 private int mChannelConfigOut = AudioFormat.CHANNEL_OUT_MONO; 53 54 int mMicSource = 0; 55 56 // private double [] samples = new double[50000]; 57 58 boolean isPlaying = false; 59 private Handler mMessageHandler; 60 boolean isDestroying = false; 61 boolean hasDestroyingErrors = false; 62 63 static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED = 892; 64 static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR = 893; 65 static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE = 894; 66 static final int NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS = 895; 67 setParams(int samplingRate, int playBufferInBytes, int recBufferInBytes, int micSource)68 public void setParams(int samplingRate, int playBufferInBytes, int recBufferInBytes, 69 int micSource) { 70 mSamplingRate = samplingRate; 71 mMinPlayBufferSizeInBytes = playBufferInBytes; 72 mMinRecordBuffSizeInBytes = recBufferInBytes; 73 mMicSource = micSource; 74 } 75 76 //JNI load 77 static { 78 try { 79 System.loadLibrary("audioloopback_jni"); 80 } catch (UnsatisfiedLinkError e) { 81 log("Error loading loopback JNI library"); 82 e.printStackTrace(); 83 } 84 85 /* TODO: gracefully fail/notify if the library can't be loaded */ 86 } 87 88 //jni calls slesInit(int samplingRate, int frameCount, int micSource)89 public native long slesInit(int samplingRate, int frameCount, int micSource); slesProcessNext(long sles_data, double[] samples, long offset)90 public native int slesProcessNext(long sles_data, double[] samples, long offset); slesDestroy(long sles_data)91 public native int slesDestroy(long sles_data); 92 run()93 public void run() { 94 95 setPriority(Thread.MAX_PRIORITY); 96 isRunning = true; 97 98 //erase output buffer 99 if (mvSamples != null) 100 mvSamples = null; 101 102 //resize 103 int nNewSize = (int)(1.1* mSamplingRate * mSecondsToRun ); //10% more just in case 104 mvSamples = new double[nNewSize]; 105 mSamplesIndex = 0; //reset index 106 107 //clear samples 108 for (int i=0; i<nNewSize; i++) { 109 mvSamples[i] = 0; 110 } 111 112 //start playing 113 isPlaying = true; 114 115 116 log(" Started capture test"); 117 if (mMessageHandler != null) { 118 Message msg = Message.obtain(); 119 msg.what = NATIVE_AUDIO_THREAD_MESSAGE_REC_STARTED; 120 mMessageHandler.sendMessage(msg); 121 } 122 123 124 125 log(String.format("about to init, sampling rate: %d, buffer:%d", mSamplingRate, 126 mMinPlayBufferSizeInBytes/2 )); 127 long sles_data = slesInit(mSamplingRate, mMinPlayBufferSizeInBytes/2, mMicSource); 128 log(String.format("sles_data = 0x%X",sles_data)); 129 130 if (sles_data == 0 ) { 131 log(" ERROR at JNI initialization"); 132 if (mMessageHandler != null) { 133 Message msg = Message.obtain(); 134 msg.what = NATIVE_AUDIO_THREAD_MESSAGE_REC_ERROR; 135 mMessageHandler.sendMessage(msg); 136 } 137 } else { 138 139 //wait a little bit... 140 try { 141 sleep(10); //just to let it start properly? 142 } catch (InterruptedException e) { 143 e.printStackTrace(); 144 } 145 146 147 148 mSamplesIndex = 0; 149 int totalSamplesRead = 0; 150 long offset = 0; 151 for (int ii = 0; ii < mSecondsToRun; ii++) { 152 log(String.format("block %d...", ii)); 153 int samplesRead = slesProcessNext(sles_data, mvSamples,offset); 154 totalSamplesRead += samplesRead; 155 156 offset += samplesRead; 157 log(" [" + ii + "] jni samples read:" + samplesRead + " currentOffset:" + offset); 158 } 159 160 log(String.format(" samplesRead: %d, sampleOffset:%d", totalSamplesRead, offset)); 161 log(String.format("about to destroy...")); 162 163 runDestroy(sles_data); 164 165 int maxTry = 20; 166 int tryCount = 0; 167 //isDestroying = true; 168 while (isDestroying) { 169 170 try { 171 sleep(40); 172 } catch (InterruptedException e) { 173 e.printStackTrace(); 174 } 175 176 tryCount++; 177 178 log("destroy try: " + tryCount); 179 180 if (tryCount >= maxTry) { 181 hasDestroyingErrors = true; 182 log("WARNING: waited for max time to properly destroy JNI."); 183 break; 184 } 185 } 186 log(String.format("after destroying. TotalSamplesRead = %d", totalSamplesRead)); 187 188 if (totalSamplesRead==0) 189 { 190 hasDestroyingErrors = true; 191 } 192 193 endTest(); 194 } 195 } 196 setMessageHandler(Handler messageHandler)197 public void setMessageHandler(Handler messageHandler) { 198 mMessageHandler = messageHandler; 199 } 200 runDestroy(final long sles_data )201 private void runDestroy(final long sles_data ) { 202 isDestroying = true; 203 204 //start thread 205 206 final long local_sles_data = sles_data; 207 //// 208 Thread thread = new Thread(new Runnable() { 209 public void run() { 210 isDestroying = true; 211 log("**Start runnable destroy"); 212 213 int status = slesDestroy(local_sles_data); 214 log(String.format("**End runnable destroy sles delete status: %d", status)); 215 isDestroying = false; 216 } 217 }); 218 219 thread.start(); 220 221 222 223 log("end of runDestroy()"); 224 225 226 } 227 togglePlay()228 public void togglePlay() { 229 230 } 231 runTest()232 public void runTest() { 233 234 235 } 236 endTest()237 public void endTest() { 238 log("--Ending capture test--"); 239 isPlaying = false; 240 241 242 if (mMessageHandler != null) { 243 Message msg = Message.obtain(); 244 if (hasDestroyingErrors) 245 msg.what = NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE_ERRORS; 246 else 247 msg.what = NATIVE_AUDIO_THREAD_MESSAGE_REC_COMPLETE; 248 mMessageHandler.sendMessage(msg); 249 } 250 251 } 252 finish()253 public void finish() { 254 255 if (isRunning) { 256 isRunning = false; 257 try { 258 sleep(20); 259 } catch (InterruptedException e) { 260 e.printStackTrace(); 261 } 262 } 263 } 264 log(String msg)265 private static void log(String msg) { 266 Log.v("Loopback", msg); 267 } 268 getWaveData()269 double [] getWaveData () { 270 return mvSamples; 271 } 272 273 } //end thread. 274