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