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 android.media.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 23 import com.android.compatibility.common.util.DeviceReportLog; 24 import com.android.compatibility.common.util.ResultType; 25 import com.android.compatibility.common.util.ResultUnit; 26 import java.nio.ByteBuffer; 27 28 import org.junit.Assert; 29 30 import android.annotation.IntRange; 31 import android.annotation.NonNull; 32 import android.annotation.Nullable; 33 import android.media.AudioAttributes; 34 import android.media.AudioFormat; 35 import android.media.AudioManager; 36 import android.media.AudioRecord; 37 import android.media.AudioTimestamp; 38 import android.media.AudioTrack; 39 import android.os.Looper; 40 import android.os.PersistableBundle; 41 import android.util.Log; 42 43 import androidx.test.InstrumentationRegistry; 44 45 // Used for statistics and loopers in listener tests. 46 // See AudioRecordTest.java and AudioTrack_ListenerTest.java. 47 public class AudioHelper { 48 49 // asserts key equals expected in the metrics bundle. 50 public static void assertMetricsKeyEquals( 51 PersistableBundle metrics, String key, Object expected) { 52 Object actual = metrics.get(key); 53 assertEquals("metric " + key + " actual " + actual + " != " + " expected " + expected, 54 expected, actual); 55 } 56 57 // asserts key exists in the metrics bundle. 58 public static void assertMetricsKey(PersistableBundle metrics, String key) { 59 Object actual = metrics.get(key); 60 assertNotNull("metric " + key + " does not exist", actual); 61 } 62 63 // create sine waves or chirps for data arrays 64 public static byte[] createSoundDataInByteArray(int bufferSamples, final int sampleRate, 65 final double frequency, double sweep) { 66 final double rad = 2 * Math.PI * frequency / sampleRate; 67 byte[] vai = new byte[bufferSamples]; 68 sweep = Math.PI * sweep / ((double)sampleRate * vai.length); 69 for (int j = 0; j < vai.length; j++) { 70 int unsigned = (int)(Math.sin(j * (rad + j * sweep)) * Byte.MAX_VALUE) 71 + Byte.MAX_VALUE & 0xFF; 72 vai[j] = (byte) unsigned; 73 } 74 return vai; 75 } 76 77 public static short[] createSoundDataInShortArray(int bufferSamples, final int sampleRate, 78 final double frequency, double sweep) { 79 final double rad = 2 * Math.PI * frequency / sampleRate; 80 short[] vai = new short[bufferSamples]; 81 sweep = Math.PI * sweep / ((double)sampleRate * vai.length); 82 for (int j = 0; j < vai.length; j++) { 83 vai[j] = (short)(Math.sin(j * (rad + j * sweep)) * Short.MAX_VALUE); 84 } 85 return vai; 86 } 87 88 public static float[] createSoundDataInFloatArray(int bufferSamples, final int sampleRate, 89 final double frequency, double sweep) { 90 final double rad = 2 * Math.PI * frequency / sampleRate; 91 float[] vaf = new float[bufferSamples]; 92 sweep = Math.PI * sweep / ((double)sampleRate * vaf.length); 93 for (int j = 0; j < vaf.length; j++) { 94 vaf[j] = (float)(Math.sin(j * (rad + j * sweep))); 95 } 96 return vaf; 97 } 98 99 /** 100 * Create and fill a short array with complete sine waves so we can 101 * hear buffer underruns more easily. 102 */ 103 public static short[] createSineWavesShort(int numFrames, int samplesPerFrame, 104 int numCycles, double amplitude) { 105 final short[] data = new short[numFrames * samplesPerFrame]; 106 final double rad = numCycles * 2.0 * Math.PI / numFrames; 107 for (int j = 0; j < data.length;) { 108 short sample = (short)(amplitude * Math.sin(j * rad) * Short.MAX_VALUE); 109 for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) { 110 data[j++] = sample; 111 } 112 } 113 return data; 114 } 115 116 public static int frameSizeFromFormat(AudioFormat format) { 117 return format.getChannelCount() 118 * format.getBytesPerSample(format.getEncoding()); 119 } 120 121 public static int frameCountFromMsec(int ms, AudioFormat format) { 122 return ms * format.getSampleRate() / 1000; 123 } 124 125 public static class Statistics { 126 public void add(double value) { 127 final double absValue = Math.abs(value); 128 mSum += value; 129 mSumAbs += absValue; 130 mMaxAbs = Math.max(mMaxAbs, absValue); 131 ++mCount; 132 } 133 134 public double getAvg() { 135 if (mCount == 0) { 136 return 0; 137 } 138 return mSum / mCount; 139 } 140 141 public double getAvgAbs() { 142 if (mCount == 0) { 143 return 0; 144 } 145 return mSumAbs / mCount; 146 } 147 148 public double getMaxAbs() { 149 return mMaxAbs; 150 } 151 152 private int mCount = 0; 153 private double mSum = 0; 154 private double mSumAbs = 0; 155 private double mMaxAbs = 0; 156 } 157 158 // for listener tests 159 // lightweight java.util.concurrent.Future* 160 public static class FutureLatch<T> 161 { 162 private T mValue; 163 private boolean mSet; 164 public void set(T value) 165 { 166 synchronized (this) { 167 assert !mSet; 168 mValue = value; 169 mSet = true; 170 notify(); 171 } 172 } 173 public T get() 174 { 175 T value; 176 synchronized (this) { 177 while (!mSet) { 178 try { 179 wait(); 180 } catch (InterruptedException e) { 181 ; 182 } 183 } 184 value = mValue; 185 } 186 return value; 187 } 188 } 189 190 // for listener tests 191 // represents a factory for T 192 public interface MakesSomething<T> 193 { 194 T makeSomething(); 195 } 196 197 // for listener tests 198 // used to construct an object in the context of an asynchronous thread with looper 199 public static class MakeSomethingAsynchronouslyAndLoop<T> 200 { 201 private Thread mThread; 202 volatile private Looper mLooper; 203 private final MakesSomething<T> mWhatToMake; 204 205 public MakeSomethingAsynchronouslyAndLoop(MakesSomething<T> whatToMake) 206 { 207 assert whatToMake != null; 208 mWhatToMake = whatToMake; 209 } 210 211 public T make() 212 { 213 final FutureLatch<T> futureLatch = new FutureLatch<T>(); 214 mThread = new Thread() 215 { 216 @Override 217 public void run() 218 { 219 Looper.prepare(); 220 mLooper = Looper.myLooper(); 221 T something = mWhatToMake.makeSomething(); 222 futureLatch.set(something); 223 Looper.loop(); 224 } 225 }; 226 mThread.start(); 227 return futureLatch.get(); 228 } 229 public void join() 230 { 231 mLooper.quit(); 232 try { 233 mThread.join(); 234 } catch (InterruptedException e) { 235 ; 236 } 237 // avoid dangling references 238 mLooper = null; 239 mThread = null; 240 } 241 } 242 243 public static int outChannelMaskFromInChannelMask(int channelMask) { 244 switch (channelMask) { 245 case AudioFormat.CHANNEL_IN_MONO: 246 return AudioFormat.CHANNEL_OUT_MONO; 247 case AudioFormat.CHANNEL_IN_STEREO: 248 return AudioFormat.CHANNEL_OUT_STEREO; 249 default: 250 return AudioFormat.CHANNEL_INVALID; 251 } 252 } 253 254 public static class TimestampVerifier { 255 256 // CDD 5.6 1ms timestamp accuracy 257 private static final double TEST_MAX_JITTER_MS_ALLOWED = 6.; // a sanity check 258 private static final double TEST_STD_JITTER_MS_ALLOWED = 3.; // flaky tolerance 3x 259 private static final double TEST_STD_JITTER_MS_WARN = 1.; // CDD requirement warning 260 261 // CDD 5.6 100ms track startup latency 262 private static final double TEST_STARTUP_TIME_MS_ALLOWED = 500.; // flaky tolerance 5x 263 private static final double TEST_STARTUP_TIME_MS_WARN = 100.; // CDD requirement warning 264 265 private static final int MILLIS_PER_SECOND = 1000; 266 private static final long NANOS_PER_MILLISECOND = 1000000; 267 private static final long NANOS_PER_SECOND = NANOS_PER_MILLISECOND * MILLIS_PER_SECOND; 268 private static final String REPORT_LOG_NAME = "CtsMediaTestCases"; 269 270 private final String mTag; 271 private final int mSampleRate; 272 273 // Running statistics 274 private int mCount = 0; 275 private long mLastFrames = 0; 276 private long mLastTimeNs = 0; 277 private int mJitterCount = 0; 278 private double mMeanJitterMs = 0.; 279 private double mSecondMomentJitterMs = 0.; 280 private double mMaxAbsJitterMs = 0.; 281 private int mWarmupCount = 0; 282 283 public TimestampVerifier(@Nullable String tag, @IntRange(from=4000) int sampleRate) { 284 mTag = tag; // Log accepts null 285 mSampleRate = sampleRate; 286 } 287 288 public int getJitterCount() { return mJitterCount; } 289 public double getMeanJitterMs() { return mMeanJitterMs; } 290 public double getStdJitterMs() { return Math.sqrt(mSecondMomentJitterMs / mJitterCount); } 291 public double getMaxAbsJitterMs() { return mMaxAbsJitterMs; } 292 public double getStartTimeNs() { 293 return mLastTimeNs - (mLastFrames * NANOS_PER_SECOND / mSampleRate); 294 } 295 296 public void add(@NonNull AudioTimestamp ts) { 297 final long frames = ts.framePosition; 298 final long timeNs = ts.nanoTime; 299 300 assertTrue("timestamps must have causal time", System.nanoTime() >= timeNs); 301 302 if (mCount > 0) { // need delta info from previous iteration (skipping first) 303 final long deltaFrames = frames - mLastFrames; 304 final long deltaTimeNs = timeNs - mLastTimeNs; 305 306 if (deltaFrames == 0 && deltaTimeNs == 0) return; 307 308 final double deltaFramesNs = (double)deltaFrames * NANOS_PER_SECOND / mSampleRate; 309 final double jitterMs = (deltaTimeNs - deltaFramesNs) // actual - expected 310 * (1. / NANOS_PER_MILLISECOND); 311 312 Log.d(mTag, "frames(" + frames 313 + ") timeNs(" + timeNs 314 + ") lastframes(" + mLastFrames 315 + ") lastTimeNs(" + mLastTimeNs 316 + ") deltaFrames(" + deltaFrames 317 + ") deltaTimeNs(" + deltaTimeNs 318 + ") jitterMs(" + jitterMs + ")"); 319 assertTrue("timestamp time should be increasing", deltaTimeNs >= 0); 320 assertTrue("timestamp frames should be increasing", deltaFrames >= 0); 321 322 if (mLastFrames != 0) { 323 if (mWarmupCount++ > 1) { // ensure device is warmed up 324 // Welford's algorithm 325 // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance 326 ++mJitterCount; 327 final double delta = jitterMs - mMeanJitterMs; 328 mMeanJitterMs += delta / mJitterCount; 329 final double delta2 = jitterMs - mMeanJitterMs; 330 mSecondMomentJitterMs += delta * delta2; 331 332 // jitterMs is signed, so max uses abs() here. 333 final double absJitterMs = Math.abs(jitterMs); 334 if (absJitterMs > mMaxAbsJitterMs) { 335 mMaxAbsJitterMs = absJitterMs; 336 } 337 } 338 } 339 } 340 ++mCount; 341 mLastFrames = frames; 342 mLastTimeNs = timeNs; 343 } 344 345 public void verifyAndLog(long trackStartTimeNs, @Nullable String logName) { 346 // enough timestamps? 347 assertTrue("need at least 2 jitter measurements", mJitterCount >= 2); 348 349 // Compute startup time and std jitter. 350 final int startupTimeMs = 351 (int) ((getStartTimeNs() - trackStartTimeNs) / NANOS_PER_MILLISECOND); 352 final double stdJitterMs = getStdJitterMs(); 353 354 // Check startup time 355 if (startupTimeMs > TEST_STARTUP_TIME_MS_WARN) { 356 Log.w(mTag, "CDD warning: startup time " + startupTimeMs 357 + " > " + TEST_STARTUP_TIME_MS_WARN); 358 } 359 assertTrue("expect startupTimeMs " + startupTimeMs 360 + " < " + TEST_STARTUP_TIME_MS_ALLOWED, 361 startupTimeMs < TEST_STARTUP_TIME_MS_ALLOWED); 362 363 // Check maximum jitter 364 assertTrue("expect maxAbsJitterMs(" + mMaxAbsJitterMs + ") < " 365 + TEST_MAX_JITTER_MS_ALLOWED, 366 mMaxAbsJitterMs < TEST_MAX_JITTER_MS_ALLOWED); 367 368 // Check std jitter 369 if (stdJitterMs > TEST_STD_JITTER_MS_WARN) { 370 Log.w(mTag, "CDD warning: std timestamp jitter " + stdJitterMs 371 + " > " + TEST_STD_JITTER_MS_WARN); 372 } 373 assertTrue("expect stdJitterMs " + stdJitterMs + " < " + TEST_STD_JITTER_MS_ALLOWED, 374 stdJitterMs < TEST_STD_JITTER_MS_ALLOWED); 375 376 Log.d(mTag, "startupTimeMs(" + startupTimeMs 377 + ") meanJitterMs(" + mMeanJitterMs 378 + ") maxAbsJitterMs(" + mMaxAbsJitterMs 379 + ") stdJitterMs(" + stdJitterMs 380 + ")"); 381 382 // Log results if logName is provided 383 if (logName != null) { 384 DeviceReportLog log = new DeviceReportLog(REPORT_LOG_NAME, logName); 385 // ReportLog needs at least one Value and Summary. 386 log.addValue("startup_time_ms", startupTimeMs, 387 ResultType.LOWER_BETTER, ResultUnit.MS); 388 log.addValue("maximum_abs_jitter_ms", mMaxAbsJitterMs, 389 ResultType.LOWER_BETTER, ResultUnit.MS); 390 log.addValue("mean_jitter_ms", mMeanJitterMs, 391 ResultType.LOWER_BETTER, ResultUnit.MS); 392 log.setSummary("std_jitter_ms", stdJitterMs, 393 ResultType.LOWER_BETTER, ResultUnit.MS); 394 log.submit(InstrumentationRegistry.getInstrumentation()); 395 } 396 } 397 } 398 399 /* AudioRecordAudit extends AudioRecord to allow concurrent playback 400 * of read content to an AudioTrack. This is for testing only. 401 * For general applications, it is NOT recommended to extend AudioRecord. 402 * This affects AudioRecord timing. 403 */ 404 public static class AudioRecordAudit extends AudioRecord { 405 public AudioRecordAudit(int audioSource, int sampleRate, int channelMask, 406 int format, int bufferSize, boolean isChannelIndex) { 407 this(audioSource, sampleRate, channelMask, format, bufferSize, isChannelIndex, 408 AudioManager.STREAM_MUSIC, 500 /*delayMs*/); 409 } 410 411 public AudioRecordAudit(int audioSource, int sampleRate, int channelMask, 412 int format, int bufferSize, 413 boolean isChannelIndex, int auditStreamType, int delayMs) { 414 // without channel index masks, one could call: 415 // super(audioSource, sampleRate, channelMask, format, bufferSize); 416 super(new AudioAttributes.Builder() 417 .setInternalCapturePreset(audioSource) 418 .build(), 419 (isChannelIndex 420 ? new AudioFormat.Builder().setChannelIndexMask(channelMask) 421 : new AudioFormat.Builder().setChannelMask(channelMask)) 422 .setEncoding(format) 423 .setSampleRate(sampleRate) 424 .build(), 425 bufferSize, 426 AudioManager.AUDIO_SESSION_ID_GENERATE); 427 428 if (delayMs >= 0) { // create an AudioTrack 429 final int channelOutMask = isChannelIndex ? channelMask : 430 outChannelMaskFromInChannelMask(channelMask); 431 final int bufferOutFrames = sampleRate * delayMs / 1000; 432 final int bufferOutSamples = bufferOutFrames 433 * AudioFormat.channelCountFromOutChannelMask(channelOutMask); 434 final int bufferOutSize = bufferOutSamples 435 * AudioFormat.getBytesPerSample(format); 436 437 // Caution: delayMs too large results in buffer sizes that cannot be created. 438 mTrack = new AudioTrack.Builder() 439 .setAudioAttributes(new AudioAttributes.Builder() 440 .setLegacyStreamType(auditStreamType) 441 .build()) 442 .setAudioFormat((isChannelIndex ? 443 new AudioFormat.Builder().setChannelIndexMask(channelOutMask) : 444 new AudioFormat.Builder().setChannelMask(channelOutMask)) 445 .setEncoding(format) 446 .setSampleRate(sampleRate) 447 .build()) 448 .setBufferSizeInBytes(bufferOutSize) 449 .build(); 450 Assert.assertEquals(AudioTrack.STATE_INITIALIZED, mTrack.getState()); 451 mPosition = 0; 452 mFinishAtMs = 0; 453 } 454 } 455 456 @Override 457 public int read(byte[] audioData, int offsetInBytes, int sizeInBytes) { 458 // for byte array access we verify format is 8 bit PCM (typical use) 459 Assert.assertEquals(TAG + ": format mismatch", 460 AudioFormat.ENCODING_PCM_8BIT, getAudioFormat()); 461 int samples = super.read(audioData, offsetInBytes, sizeInBytes); 462 if (mTrack != null) { 463 Assert.assertEquals(samples, mTrack.write(audioData, offsetInBytes, samples)); 464 mPosition += samples / mTrack.getChannelCount(); 465 } 466 return samples; 467 } 468 469 @Override 470 public int read(byte[] audioData, int offsetInBytes, int sizeInBytes, int readMode) { 471 // for byte array access we verify format is 8 bit PCM (typical use) 472 Assert.assertEquals(TAG + ": format mismatch", 473 AudioFormat.ENCODING_PCM_8BIT, getAudioFormat()); 474 int samples = super.read(audioData, offsetInBytes, sizeInBytes, readMode); 475 if (mTrack != null) { 476 Assert.assertEquals(samples, mTrack.write(audioData, offsetInBytes, samples, 477 AudioTrack.WRITE_BLOCKING)); 478 mPosition += samples / mTrack.getChannelCount(); 479 } 480 return samples; 481 } 482 483 @Override 484 public int read(short[] audioData, int offsetInShorts, int sizeInShorts) { 485 // for short array access we verify format is 16 bit PCM (typical use) 486 Assert.assertEquals(TAG + ": format mismatch", 487 AudioFormat.ENCODING_PCM_16BIT, getAudioFormat()); 488 int samples = super.read(audioData, offsetInShorts, sizeInShorts); 489 if (mTrack != null) { 490 Assert.assertEquals(samples, mTrack.write(audioData, offsetInShorts, samples)); 491 mPosition += samples / mTrack.getChannelCount(); 492 } 493 return samples; 494 } 495 496 @Override 497 public int read(short[] audioData, int offsetInShorts, int sizeInShorts, int readMode) { 498 // for short array access we verify format is 16 bit PCM (typical use) 499 Assert.assertEquals(TAG + ": format mismatch", 500 AudioFormat.ENCODING_PCM_16BIT, getAudioFormat()); 501 int samples = super.read(audioData, offsetInShorts, sizeInShorts, readMode); 502 if (mTrack != null) { 503 Assert.assertEquals(samples, mTrack.write(audioData, offsetInShorts, samples, 504 AudioTrack.WRITE_BLOCKING)); 505 mPosition += samples / mTrack.getChannelCount(); 506 } 507 return samples; 508 } 509 510 @Override 511 public int read(float[] audioData, int offsetInFloats, int sizeInFloats, int readMode) { 512 // for float array access we verify format is float PCM (typical use) 513 Assert.assertEquals(TAG + ": format mismatch", 514 AudioFormat.ENCODING_PCM_FLOAT, getAudioFormat()); 515 int samples = super.read(audioData, offsetInFloats, sizeInFloats, readMode); 516 if (mTrack != null) { 517 Assert.assertEquals(samples, mTrack.write(audioData, offsetInFloats, samples, 518 AudioTrack.WRITE_BLOCKING)); 519 mPosition += samples / mTrack.getChannelCount(); 520 } 521 return samples; 522 } 523 524 @Override 525 public int read(ByteBuffer audioBuffer, int sizeInBytes) { 526 int bytes = super.read(audioBuffer, sizeInBytes); 527 if (mTrack != null) { 528 // read does not affect position and limit of the audioBuffer. 529 // we make a duplicate to change that for writing to the output AudioTrack 530 // which does check position and limit. 531 ByteBuffer copy = audioBuffer.duplicate(); 532 copy.position(0).limit(bytes); // read places data at the start of the buffer. 533 Assert.assertEquals(bytes, mTrack.write(copy, bytes, AudioTrack.WRITE_BLOCKING)); 534 mPosition += bytes / 535 (mTrack.getChannelCount() 536 * AudioFormat.getBytesPerSample(mTrack.getAudioFormat())); 537 } 538 return bytes; 539 } 540 541 @Override 542 public int read(ByteBuffer audioBuffer, int sizeInBytes, int readMode) { 543 int bytes = super.read(audioBuffer, sizeInBytes, readMode); 544 if (mTrack != null) { 545 // read does not affect position and limit of the audioBuffer. 546 // we make a duplicate to change that for writing to the output AudioTrack 547 // which does check position and limit. 548 ByteBuffer copy = audioBuffer.duplicate(); 549 copy.position(0).limit(bytes); // read places data at the start of the buffer. 550 Assert.assertEquals(bytes, mTrack.write(copy, bytes, AudioTrack.WRITE_BLOCKING)); 551 mPosition += bytes / 552 (mTrack.getChannelCount() 553 * AudioFormat.getBytesPerSample(mTrack.getAudioFormat())); 554 } 555 return bytes; 556 } 557 558 @Override 559 public void startRecording() { 560 super.startRecording(); 561 if (mTrack != null) { 562 mTrack.play(); 563 } 564 } 565 566 @Override 567 public void stop() { 568 super.stop(); 569 if (mTrack != null) { 570 if (mPosition > 0) { // stop may be called multiple times. 571 final int remainingFrames = mPosition - mTrack.getPlaybackHeadPosition(); 572 mFinishAtMs = System.currentTimeMillis() 573 + remainingFrames * 1000 / mTrack.getSampleRate(); 574 mPosition = 0; 575 } 576 mTrack.stop(); // allows remaining data to play out 577 } 578 } 579 580 @Override 581 public void release() { 582 super.release(); 583 if (mTrack != null) { 584 final long remainingMs = mFinishAtMs - System.currentTimeMillis(); 585 if (remainingMs > 0) { 586 try { 587 Thread.sleep(remainingMs); 588 } catch (InterruptedException e) { 589 ; 590 } 591 } 592 mTrack.release(); 593 mTrack = null; 594 } 595 } 596 597 public AudioTrack mTrack; 598 private final static String TAG = "AudioRecordAudit"; 599 private int mPosition; 600 private long mFinishAtMs; 601 } 602 603 /* AudioRecordAudit extends AudioRecord to allow concurrent playback 604 * of read content to an AudioTrack. This is for testing only. 605 * For general applications, it is NOT recommended to extend AudioRecord. 606 * This affects AudioRecord timing. 607 */ 608 public static class AudioRecordAuditNative extends AudioRecordNative { 609 public AudioRecordAuditNative() { 610 super(); 611 // Caution: delayMs too large results in buffer sizes that cannot be created. 612 mTrack = new AudioTrackNative(); 613 } 614 615 @Override 616 public boolean open(int numChannels, int sampleRate, boolean useFloat, int numBuffers) { 617 if (super.open(numChannels, sampleRate, useFloat, numBuffers)) { 618 if (!mTrack.open(numChannels, sampleRate, useFloat, 2 /* numBuffers */)) { 619 mTrack = null; // remove track 620 } 621 return true; 622 } 623 return false; 624 } 625 626 @Override 627 public void close() { 628 super.close(); 629 if (mTrack != null) { 630 mTrack.close(); 631 } 632 } 633 634 @Override 635 public boolean start() { 636 if (super.start()) { 637 if (mTrack != null) { 638 mTrack.start(); 639 } 640 return true; 641 } 642 return false; 643 } 644 645 @Override 646 public boolean stop() { 647 if (super.stop()) { 648 if (mTrack != null) { 649 mTrack.stop(); // doesn't allow remaining data to play out 650 } 651 return true; 652 } 653 return false; 654 } 655 656 @Override 657 public int read(short[] audioData, int offsetInShorts, int sizeInShorts, int readFlags) { 658 int samples = super.read(audioData, offsetInShorts, sizeInShorts, readFlags); 659 if (mTrack != null) { 660 Assert.assertEquals(samples, mTrack.write(audioData, offsetInShorts, samples, 661 AudioTrackNative.WRITE_FLAG_BLOCKING)); 662 mPosition += samples / mTrack.getChannelCount(); 663 } 664 return samples; 665 } 666 667 @Override 668 public int read(float[] audioData, int offsetInFloats, int sizeInFloats, int readFlags) { 669 int samples = super.read(audioData, offsetInFloats, sizeInFloats, readFlags); 670 if (mTrack != null) { 671 Assert.assertEquals(samples, mTrack.write(audioData, offsetInFloats, samples, 672 AudioTrackNative.WRITE_FLAG_BLOCKING)); 673 mPosition += samples / mTrack.getChannelCount(); 674 } 675 return samples; 676 } 677 678 public AudioTrackNative mTrack; 679 private final static String TAG = "AudioRecordAuditNative"; 680 private int mPosition; 681 } 682 } 683