1 /* 2 * Copyright 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 #ifndef NATIVEOBOE_NATIVEAUDIOCONTEXT_H 18 #define NATIVEOBOE_NATIVEAUDIOCONTEXT_H 19 20 #include <dlfcn.h> 21 #include <jni.h> 22 #include <sys/system_properties.h> 23 #include <thread> 24 #include <unordered_map> 25 #include <vector> 26 27 #include "common/OboeDebug.h" 28 #include "oboe/Oboe.h" 29 30 #include "AudioStreamGateway.h" 31 #include "flowunits/ImpulseOscillator.h" 32 #include "flowgraph/ManyToMultiConverter.h" 33 #include "flowgraph/MonoToMultiConverter.h" 34 #include "flowgraph/SinkFloat.h" 35 #include "flowgraph/SinkI16.h" 36 #include "flowunits/ExponentialShape.h" 37 #include "flowunits/LinearShape.h" 38 #include "flowunits/SineOscillator.h" 39 #include "flowunits/SawtoothOscillator.h" 40 41 #include "FullDuplexEcho.h" 42 #include "FullDuplexGlitches.h" 43 #include "FullDuplexLatency.h" 44 #include "FullDuplexStream.h" 45 #include "InputStreamCallbackAnalyzer.h" 46 #include "MultiChannelRecording.h" 47 #include "OboeStreamCallbackProxy.h" 48 #include "PlayRecordingCallback.h" 49 #include "SawPingGenerator.h" 50 #include "flowunits/TriangleOscillator.h" 51 52 // These must match order in strings.xml and in StreamConfiguration.java 53 #define NATIVE_MODE_UNSPECIFIED 0 54 #define NATIVE_MODE_OPENSLES 1 55 #define NATIVE_MODE_AAUDIO 2 56 57 #define MAX_SINE_OSCILLATORS 8 58 #define AMPLITUDE_SINE 1.0 59 #define AMPLITUDE_SAWTOOTH 0.5 60 #define FREQUENCY_SAW_PING 800.0 61 #define AMPLITUDE_SAW_PING 0.8 62 #define AMPLITUDE_IMPULSE 0.7 63 64 #define NANOS_PER_MICROSECOND ((int64_t) 1000) 65 #define NANOS_PER_MILLISECOND (1000 * NANOS_PER_MICROSECOND) 66 #define NANOS_PER_SECOND (1000 * NANOS_PER_MILLISECOND) 67 68 #define LIB_AAUDIO_NAME "libaaudio.so" 69 #define FUNCTION_IS_MMAP "AAudioStream_isMMapUsed" 70 #define FUNCTION_SET_MMAP_POLICY "AAudio_setMMapPolicy" 71 #define FUNCTION_GET_MMAP_POLICY "AAudio_getMMapPolicy" 72 73 #define SECONDS_TO_RECORD 10 74 75 typedef struct AAudioStreamStruct AAudioStream; 76 77 /** 78 * Call some AAudio test routines that are not part of the normal API. 79 */ 80 class AAudioExtensions { 81 public: AAudioExtensions()82 AAudioExtensions() { 83 int32_t policy = getIntegerProperty("aaudio.mmap_policy", 0); 84 mMMapSupported = isPolicyEnabled(policy); 85 86 policy = getIntegerProperty("aaudio.mmap_exclusive_policy", 0); 87 mMMapExclusiveSupported = isPolicyEnabled(policy); 88 } 89 isPolicyEnabled(int32_t policy)90 static bool isPolicyEnabled(int32_t policy) { 91 return (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS); 92 } 93 getInstance()94 static AAudioExtensions &getInstance() { 95 static AAudioExtensions instance; 96 return instance; 97 } 98 isMMapUsed(oboe::AudioStream * oboeStream)99 bool isMMapUsed(oboe::AudioStream *oboeStream) { 100 if (!loadLibrary()) return false; 101 if (mAAudioStream_isMMap == nullptr) return false; 102 AAudioStream *aaudioStream = (AAudioStream *) oboeStream->getUnderlyingStream(); 103 return mAAudioStream_isMMap(aaudioStream); 104 } 105 setMMapEnabled(bool enabled)106 bool setMMapEnabled(bool enabled) { 107 if (!loadLibrary()) return false; 108 if (mAAudio_setMMapPolicy == nullptr) return false; 109 return mAAudio_setMMapPolicy(enabled ? AAUDIO_POLICY_AUTO : AAUDIO_POLICY_NEVER); 110 } 111 isMMapEnabled()112 bool isMMapEnabled() { 113 if (!loadLibrary()) return false; 114 if (mAAudio_getMMapPolicy == nullptr) return false; 115 int32_t policy = mAAudio_getMMapPolicy(); 116 return isPolicyEnabled(policy); 117 } 118 isMMapSupported()119 bool isMMapSupported() { 120 return mMMapSupported; 121 } 122 isMMapExclusiveSupported()123 bool isMMapExclusiveSupported() { 124 return mMMapExclusiveSupported; 125 } 126 127 private: 128 129 enum { 130 AAUDIO_POLICY_NEVER = 1, 131 AAUDIO_POLICY_AUTO, 132 AAUDIO_POLICY_ALWAYS 133 }; 134 typedef int32_t aaudio_policy_t; 135 getIntegerProperty(const char * name,int defaultValue)136 int getIntegerProperty(const char *name, int defaultValue) { 137 int result = defaultValue; 138 char valueText[PROP_VALUE_MAX] = {0}; 139 if (__system_property_get(name, valueText) != 0) { 140 result = atoi(valueText); 141 } 142 return result; 143 } 144 145 // return true if it succeeds loadLibrary()146 bool loadLibrary() { 147 if (mFirstTime) { 148 mFirstTime = false; 149 mLibHandle = dlopen(LIB_AAUDIO_NAME, 0); 150 if (mLibHandle == nullptr) { 151 LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__); 152 return false; 153 } 154 155 mAAudioStream_isMMap = (bool (*)(AAudioStream *stream)) 156 dlsym(mLibHandle, FUNCTION_IS_MMAP); 157 if (mAAudioStream_isMMap == nullptr) { 158 LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__); 159 return false; 160 } 161 162 mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy)) 163 dlsym(mLibHandle, FUNCTION_SET_MMAP_POLICY); 164 if (mAAudio_setMMapPolicy == nullptr) { 165 LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__); 166 return false; 167 } 168 169 mAAudio_getMMapPolicy = (aaudio_policy_t (*)()) 170 dlsym(mLibHandle, FUNCTION_GET_MMAP_POLICY); 171 if (mAAudio_getMMapPolicy == nullptr) { 172 LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__); 173 return false; 174 } 175 } 176 return (mLibHandle != nullptr); 177 } 178 179 bool mFirstTime = true; 180 void *mLibHandle = nullptr; 181 bool (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr; 182 int32_t (*mAAudio_setMMapPolicy)(aaudio_policy_t policy) = nullptr; 183 aaudio_policy_t (*mAAudio_getMMapPolicy)() = nullptr; 184 bool mMMapSupported = false; 185 bool mMMapExclusiveSupported = false; 186 }; 187 188 /** 189 * Abstract base class that corresponds to a test at the Java level. 190 */ 191 class ActivityContext { 192 public: 193 ActivityContext()194 ActivityContext() {} 195 virtual ~ActivityContext() = default; 196 getStream(int32_t streamIndex)197 oboe::AudioStream *getStream(int32_t streamIndex) { 198 auto it = mOboeStreams.find(streamIndex); 199 if (it != mOboeStreams.end()) { 200 return it->second.get(); 201 } else { 202 return nullptr; 203 } 204 } 205 206 virtual void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder); 207 208 int open(jint nativeApi, 209 jint sampleRate, 210 jint channelCount, 211 jint format, 212 jint sharingMode, 213 jint performanceMode, 214 jint inputPreset, 215 jint deviceId, 216 jint sessionId, 217 jint framesPerBurst, 218 jboolean channelConversionAllowed, 219 jboolean formatConversionAllowed, 220 jint rateConversionQuality, 221 jboolean isMMap, 222 jboolean isInput); 223 224 225 virtual void close(int32_t streamIndex); 226 configureForStart()227 virtual void configureForStart() {} 228 229 oboe::Result start(); 230 231 oboe::Result pause(); 232 233 oboe::Result stopAllStreams(); 234 stop()235 virtual oboe::Result stop() { 236 return stopAllStreams(); 237 } 238 getCpuLoad()239 double getCpuLoad() { 240 return oboeCallbackProxy.getCpuLoad(); 241 } 242 setWorkload(double workload)243 void setWorkload(double workload) { 244 oboeCallbackProxy.setWorkload(workload); 245 } 246 startPlayback()247 virtual oboe::Result startPlayback() { 248 return oboe::Result::OK; 249 } 250 stopPlayback()251 virtual oboe::Result stopPlayback() { 252 return oboe::Result::OK; 253 } 254 runBlockingIO()255 virtual void runBlockingIO() {}; 256 threadCallback(ActivityContext * context)257 static void threadCallback(ActivityContext *context) { 258 context->runBlockingIO(); 259 } 260 stopBlockingIOThread()261 void stopBlockingIOThread() { 262 if (dataThread != nullptr) { 263 // stop a thread that runs in place of the callback 264 threadEnabled.store(false); // ask thread to exit its loop 265 dataThread->join(); 266 dataThread = nullptr; 267 } 268 } 269 getPeakLevel(int index)270 virtual double getPeakLevel(int index) { 271 return 0.0; 272 } 273 setEnabled(bool enabled)274 virtual void setEnabled(bool enabled) { 275 } 276 277 bool isMMapUsed(int32_t streamIndex); 278 getFramesPerBlock()279 int32_t getFramesPerBlock() { 280 return (callbackSize == 0) ? mFramesPerBurst : callbackSize; 281 } 282 getCallbackCount()283 int64_t getCallbackCount() { 284 return oboeCallbackProxy.getCallbackCount(); 285 } 286 getFramesPerCallback()287 int32_t getFramesPerCallback() { 288 return oboeCallbackProxy.getFramesPerCallback(); 289 } 290 setChannelEnabled(int channelIndex,bool enabled)291 virtual void setChannelEnabled(int channelIndex, bool enabled) {} 292 setSignalType(int signalType)293 virtual void setSignalType(int signalType) {} 294 295 virtual int32_t saveWaveFile(const char *filename); 296 setMinimumFramesBeforeRead(int32_t numFrames)297 virtual void setMinimumFramesBeforeRead(int32_t numFrames) {} 298 299 static bool mUseCallback; 300 static int callbackSize; 301 302 protected: 303 oboe::AudioStream *getInputStream(); 304 oboe::AudioStream *getOutputStream(); 305 int32_t allocateStreamIndex(); 306 void freeStreamIndex(int32_t streamIndex); 307 createRecording()308 virtual void createRecording() { 309 mRecording = std::make_unique<MultiChannelRecording>(mChannelCount, 310 SECONDS_TO_RECORD * mSampleRate); 311 } 312 finishOpen(bool isInput,oboe::AudioStream * oboeStream)313 virtual void finishOpen(bool isInput, oboe::AudioStream *oboeStream) {} 314 315 virtual oboe::Result startStreams() = 0; 316 317 std::unique_ptr<float []> dataBuffer{}; 318 319 AudioStreamGateway audioStreamGateway; 320 OboeStreamCallbackProxy oboeCallbackProxy; 321 322 std::unique_ptr<MultiChannelRecording> mRecording{}; 323 324 int32_t mNextStreamHandle = 0; 325 std::unordered_map<int32_t, std::shared_ptr<oboe::AudioStream>> mOboeStreams; 326 int32_t mFramesPerBurst = 0; // TODO per stream 327 int32_t mChannelCount = 0; // TODO per stream 328 int32_t mSampleRate = 0; // TODO per stream 329 330 std::atomic<bool> threadEnabled{false}; 331 std::thread *dataThread = nullptr; 332 333 private: 334 }; 335 336 /** 337 * Test a single input stream. 338 */ 339 class ActivityTestInput : public ActivityContext { 340 public: 341 ActivityTestInput()342 ActivityTestInput() {} 343 virtual ~ActivityTestInput() = default; 344 345 void configureForStart() override; 346 getPeakLevel(int index)347 double getPeakLevel(int index) override { 348 return mInputAnalyzer.getPeakLevel(index); 349 } 350 351 void runBlockingIO() override; 352 353 InputStreamCallbackAnalyzer mInputAnalyzer; 354 setMinimumFramesBeforeRead(int32_t numFrames)355 void setMinimumFramesBeforeRead(int32_t numFrames) override { 356 mInputAnalyzer.setMinimumFramesBeforeRead(numFrames); 357 mMinimumFramesBeforeRead = numFrames; 358 } 359 getMinimumFramesBeforeRead()360 int32_t getMinimumFramesBeforeRead() const { 361 return mMinimumFramesBeforeRead; 362 } 363 364 protected: 365 startStreams()366 oboe::Result startStreams() override { 367 mInputAnalyzer.reset(); 368 return getInputStream()->requestStart(); 369 } 370 371 int32_t mMinimumFramesBeforeRead = 0; 372 }; 373 374 /** 375 * Record a configured input stream and play it back some simple way. 376 */ 377 class ActivityRecording : public ActivityTestInput { 378 public: 379 ActivityRecording()380 ActivityRecording() {} 381 virtual ~ActivityRecording() = default; 382 stop()383 oboe::Result stop() override { 384 385 oboe::Result resultStopPlayback = stopPlayback(); 386 oboe::Result resultStopAudio = ActivityContext::stop(); 387 388 return (resultStopPlayback != oboe::Result::OK) ? resultStopPlayback : resultStopAudio; 389 } 390 391 oboe::Result startPlayback() override; 392 393 oboe::Result stopPlayback() override; 394 395 PlayRecordingCallback mPlayRecordingCallback; 396 oboe::AudioStream *playbackStream = nullptr; 397 398 }; 399 400 /** 401 * Test a single output stream. 402 */ 403 class ActivityTestOutput : public ActivityContext { 404 public: ActivityTestOutput()405 ActivityTestOutput() 406 : sineOscillators(MAX_SINE_OSCILLATORS) 407 , sawtoothOscillators(MAX_SINE_OSCILLATORS) {} 408 409 virtual ~ActivityTestOutput() = default; 410 411 void close(int32_t streamIndex) override; 412 startStreams()413 oboe::Result startStreams() override { 414 return getOutputStream()->start(); 415 } 416 417 void configureForStart() override; 418 419 virtual void configureStreamGateway(); 420 421 void runBlockingIO() override; 422 423 void setChannelEnabled(int channelIndex, bool enabled) override; 424 425 // WARNING - must match order in strings.xml and OboeAudioOutputStream.java 426 enum SignalType { 427 Sine = 0, 428 Sawtooth = 1, 429 FreqSweep = 2, 430 PitchSweep = 3, 431 WhiteNoise = 4 432 }; 433 setSignalType(int signalType)434 void setSignalType(int signalType) override { 435 mSignalType = (SignalType) signalType; 436 } 437 438 protected: 439 SignalType mSignalType = SignalType::Sine; 440 441 std::vector<SineOscillator> sineOscillators; 442 std::vector<SawtoothOscillator> sawtoothOscillators; 443 static constexpr float kSweepPeriod = 10.0; // for triangle up and down 444 445 // A triangle LFO is shaped into either a linear or an exponential range. 446 TriangleOscillator mTriangleOscillator; 447 LinearShape mLinearShape; 448 ExponentialShape mExponentialShape; 449 450 std::unique_ptr<ManyToMultiConverter> manyToMulti; 451 std::unique_ptr<MonoToMultiConverter> monoToMulti; 452 std::shared_ptr<oboe::flowgraph::SinkFloat> mSinkFloat; 453 std::shared_ptr<oboe::flowgraph::SinkI16> mSinkI16; 454 }; 455 456 /** 457 * Generate a short beep with a very short attack. 458 * This is used by Java to measure output latency. 459 */ 460 class ActivityTapToTone : public ActivityTestOutput { 461 public: ActivityTapToTone()462 ActivityTapToTone() {} 463 virtual ~ActivityTapToTone() = default; 464 465 void configureForStart() override; 466 setEnabled(bool enabled)467 virtual void setEnabled(bool enabled) override { 468 sawPingGenerator.setEnabled(enabled); 469 } 470 471 SawPingGenerator sawPingGenerator; 472 }; 473 474 /** 475 * Activity that uses synchronized input/output streams. 476 */ 477 class ActivityFullDuplex : public ActivityContext { 478 public: 479 480 void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; 481 getState()482 virtual int32_t getState() { return -1; } getResult()483 virtual int32_t getResult() { return -1; } isAnalyzerDone()484 virtual bool isAnalyzerDone() { return false; } 485 setMinimumFramesBeforeRead(int32_t numFrames)486 void setMinimumFramesBeforeRead(int32_t numFrames) override { 487 getFullDuplexAnalyzer()->setMinimumFramesBeforeRead(numFrames); 488 } 489 490 virtual FullDuplexAnalyzer *getFullDuplexAnalyzer() = 0; 491 getResetCount()492 int32_t getResetCount() { 493 return getFullDuplexAnalyzer()->getLoopbackProcessor()->getResetCount(); 494 } 495 496 protected: createRecording()497 void createRecording() override { 498 mRecording = std::make_unique<MultiChannelRecording>(2, // output and input 499 SECONDS_TO_RECORD * mSampleRate); 500 } 501 }; 502 503 /** 504 * Echo input to output through a delay line. 505 */ 506 class ActivityEcho : public ActivityFullDuplex { 507 public: 508 startStreams()509 oboe::Result startStreams() override { 510 return mFullDuplexEcho->start(); 511 } 512 513 void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; 514 setDelayTime(double delayTimeSeconds)515 void setDelayTime(double delayTimeSeconds) { 516 if (mFullDuplexEcho) { 517 mFullDuplexEcho->setDelayTime(delayTimeSeconds); 518 } 519 } 520 getFullDuplexAnalyzer()521 FullDuplexAnalyzer *getFullDuplexAnalyzer() override { 522 return (FullDuplexAnalyzer *) mFullDuplexEcho.get(); 523 } 524 525 protected: 526 void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; 527 528 private: 529 std::unique_ptr<FullDuplexEcho> mFullDuplexEcho{}; 530 }; 531 532 /** 533 * Measure Round Trip Latency 534 */ 535 class ActivityRoundTripLatency : public ActivityFullDuplex { 536 public: 537 startStreams()538 oboe::Result startStreams() override { 539 return mFullDuplexLatency->start(); 540 } 541 542 void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; 543 getLatencyAnalyzer()544 LatencyAnalyzer *getLatencyAnalyzer() { 545 return mFullDuplexLatency->getLatencyAnalyzer(); 546 } 547 getState()548 int32_t getState() override { 549 return getLatencyAnalyzer()->getState(); 550 } getResult()551 int32_t getResult() override { 552 return getLatencyAnalyzer()->getState(); 553 } isAnalyzerDone()554 bool isAnalyzerDone() override { 555 return mFullDuplexLatency->isDone(); 556 } 557 getFullDuplexAnalyzer()558 FullDuplexAnalyzer *getFullDuplexAnalyzer() override { 559 return (FullDuplexAnalyzer *) mFullDuplexLatency.get(); 560 } 561 562 protected: 563 void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; 564 565 private: 566 std::unique_ptr<FullDuplexLatency> mFullDuplexLatency{}; 567 }; 568 569 /** 570 * Measure Glitches 571 */ 572 class ActivityGlitches : public ActivityFullDuplex { 573 public: 574 startStreams()575 oboe::Result startStreams() override { 576 return mFullDuplexGlitches->start(); 577 } 578 579 void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; 580 getGlitchAnalyzer()581 GlitchAnalyzer *getGlitchAnalyzer() { 582 if (!mFullDuplexGlitches) return nullptr; 583 return mFullDuplexGlitches->getGlitchAnalyzer(); 584 } 585 getState()586 int32_t getState() override { 587 return getGlitchAnalyzer()->getState(); 588 } getResult()589 int32_t getResult() override { 590 return getGlitchAnalyzer()->getResult(); 591 } isAnalyzerDone()592 bool isAnalyzerDone() override { 593 return mFullDuplexGlitches->isDone(); 594 } 595 getFullDuplexAnalyzer()596 FullDuplexAnalyzer *getFullDuplexAnalyzer() override { 597 return (FullDuplexAnalyzer *) mFullDuplexGlitches.get(); 598 } 599 600 protected: 601 void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; 602 603 private: 604 std::unique_ptr<FullDuplexGlitches> mFullDuplexGlitches{}; 605 }; 606 607 /** 608 * Test a single output stream. 609 */ 610 class ActivityTestDisconnect : public ActivityContext { 611 public: ActivityTestDisconnect()612 ActivityTestDisconnect() {} 613 614 virtual ~ActivityTestDisconnect() = default; 615 616 void close(int32_t streamIndex) override; 617 startStreams()618 oboe::Result startStreams() override { 619 oboe::AudioStream *outputStream = getOutputStream(); 620 if (outputStream) { 621 return outputStream->start(); 622 } 623 624 oboe::AudioStream *inputStream = getInputStream(); 625 if (inputStream) { 626 return inputStream->start(); 627 } 628 return oboe::Result::ErrorNull; 629 } 630 631 void configureForStart() override; 632 633 private: 634 std::unique_ptr<SineOscillator> sineOscillator; 635 std::unique_ptr<MonoToMultiConverter> monoToMulti; 636 std::shared_ptr<oboe::flowgraph::SinkFloat> mSinkFloat; 637 }; 638 639 /** 640 * Switch between various 641 */ 642 class NativeAudioContext { 643 public: 644 getCurrentActivity()645 ActivityContext *getCurrentActivity() { 646 return currentActivity; 647 }; 648 setActivityType(int activityType)649 void setActivityType(int activityType) { 650 mActivityType = (ActivityType) activityType; 651 switch(mActivityType) { 652 default: 653 case ActivityType::Undefined: 654 case ActivityType::TestOutput: 655 currentActivity = &mActivityTestOutput; 656 break; 657 case ActivityType::TestInput: 658 currentActivity = &mActivityTestInput; 659 break; 660 case ActivityType::TapToTone: 661 currentActivity = &mActivityTapToTone; 662 break; 663 case ActivityType::RecordPlay: 664 currentActivity = &mActivityRecording; 665 break; 666 case ActivityType::Echo: 667 currentActivity = &mActivityEcho; 668 break; 669 case ActivityType::RoundTripLatency: 670 currentActivity = &mActivityRoundTripLatency; 671 break; 672 case ActivityType::Glitches: 673 currentActivity = &mActivityGlitches; 674 break; 675 case ActivityType::TestDisconnect: 676 currentActivity = &mActivityTestDisconnect; 677 break; 678 } 679 } 680 setDelayTime(double delayTimeMillis)681 void setDelayTime(double delayTimeMillis) { 682 mActivityEcho.setDelayTime(delayTimeMillis); 683 } 684 685 ActivityTestOutput mActivityTestOutput; 686 ActivityTestInput mActivityTestInput; 687 ActivityTapToTone mActivityTapToTone; 688 ActivityRecording mActivityRecording; 689 ActivityEcho mActivityEcho; 690 ActivityRoundTripLatency mActivityRoundTripLatency; 691 ActivityGlitches mActivityGlitches; 692 ActivityTestDisconnect mActivityTestDisconnect; 693 694 private: 695 696 // WARNING - must match definitions in TestAudioActivity.java 697 enum ActivityType { 698 Undefined = -1, 699 TestOutput = 0, 700 TestInput = 1, 701 TapToTone = 2, 702 RecordPlay = 3, 703 Echo = 4, 704 RoundTripLatency = 5, 705 Glitches = 6, 706 TestDisconnect = 7, 707 }; 708 709 ActivityType mActivityType = ActivityType::Undefined; 710 ActivityContext *currentActivity = &mActivityTestOutput; 711 712 }; 713 714 #endif //NATIVEOBOE_NATIVEAUDIOCONTEXT_H 715