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