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 <jni.h> 21 #include <sys/system_properties.h> 22 #include <thread> 23 #include <unordered_map> 24 #include <vector> 25 26 #include "common/OboeDebug.h" 27 #include "oboe/Oboe.h" 28 29 #include "aaudio/AAudioExtensions.h" 30 #include "AudioStreamGateway.h" 31 32 #include "flowunits/ImpulseOscillator.h" 33 #include "flowgraph/ManyToMultiConverter.h" 34 #include "flowgraph/MonoToMultiConverter.h" 35 #include "flowgraph/SinkFloat.h" 36 #include "flowgraph/SinkI16.h" 37 #include "flowgraph/SinkI24.h" 38 #include "flowgraph/SinkI32.h" 39 #include "flowunits/ExponentialShape.h" 40 #include "flowunits/LinearShape.h" 41 #include "flowunits/SineOscillator.h" 42 #include "flowunits/SawtoothOscillator.h" 43 #include "flowunits/TriangleOscillator.h" 44 #include "flowunits/WhiteNoise.h" 45 46 #include "FullDuplexAnalyzer.h" 47 #include "FullDuplexEcho.h" 48 #include "FullDuplexStream.h" 49 #include "analyzer/GlitchAnalyzer.h" 50 #include "analyzer/DataPathAnalyzer.h" 51 #include "InputStreamCallbackAnalyzer.h" 52 #include "MultiChannelRecording.h" 53 #include "OboeStreamCallbackProxy.h" 54 #include "PlayRecordingCallback.h" 55 #include "SawPingGenerator.h" 56 57 // These must match order in strings.xml and in StreamConfiguration.java 58 #define NATIVE_MODE_UNSPECIFIED 0 59 #define NATIVE_MODE_OPENSLES 1 60 #define NATIVE_MODE_AAUDIO 2 61 62 #define MAX_SINE_OSCILLATORS 16 63 #define AMPLITUDE_SINE 1.0 64 #define AMPLITUDE_SAWTOOTH 0.5 65 #define FREQUENCY_SAW_PING 800.0 66 #define AMPLITUDE_SAW_PING 0.8 67 #define AMPLITUDE_IMPULSE 0.7 68 69 #define NANOS_PER_MICROSECOND ((int64_t) 1000) 70 #define NANOS_PER_MILLISECOND (1000 * NANOS_PER_MICROSECOND) 71 #define NANOS_PER_SECOND (1000 * NANOS_PER_MILLISECOND) 72 73 #define SECONDS_TO_RECORD 10 74 75 /** 76 * Abstract base class that corresponds to a test at the Java level. 77 */ 78 class ActivityContext { 79 public: 80 ActivityContext()81 ActivityContext() {} 82 83 virtual ~ActivityContext() = default; 84 getStream(int32_t streamIndex)85 std::shared_ptr<oboe::AudioStream> getStream(int32_t streamIndex) { 86 auto it = mOboeStreams.find(streamIndex); 87 if (it != mOboeStreams.end()) { 88 return it->second; 89 } else { 90 return nullptr; 91 } 92 } 93 94 virtual void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder); 95 96 /** 97 * Open a stream with the given parameters. 98 * @param nativeApi 99 * @param sampleRate 100 * @param channelCount 101 * @param channelMask 102 * @param format 103 * @param sharingMode 104 * @param performanceMode 105 * @param inputPreset 106 * @param deviceId 107 * @param sessionId 108 * @param framesPerBurst 109 * @param channelConversionAllowed 110 * @param formatConversionAllowed 111 * @param rateConversionQuality 112 * @param isMMap 113 * @param isInput 114 * @return stream ID 115 */ 116 int open(jint nativeApi, 117 jint sampleRate, 118 jint channelCount, 119 jint channelMask, 120 jint format, 121 jint sharingMode, 122 jint performanceMode, 123 jint inputPreset, 124 jint usage, 125 jint contentType, 126 jint deviceId, 127 jint sessionId, 128 jboolean channelConversionAllowed, 129 jboolean formatConversionAllowed, 130 jint rateConversionQuality, 131 jboolean isMMap, 132 jboolean isInput); 133 134 virtual void close(int32_t streamIndex); 135 configureForStart()136 virtual void configureForStart() {} 137 138 oboe::Result start(); 139 140 oboe::Result pause(); 141 142 oboe::Result stopAllStreams(); 143 stop()144 virtual oboe::Result stop() { 145 return stopAllStreams(); 146 } 147 getCpuLoad()148 double getCpuLoad() { 149 return oboeCallbackProxy.getCpuLoad(); 150 } 151 getCallbackTimeString()152 std::string getCallbackTimeString() { 153 return oboeCallbackProxy.getCallbackTimeString(); 154 } 155 setWorkload(double workload)156 void setWorkload(double workload) { 157 oboeCallbackProxy.setWorkload(workload); 158 } 159 startPlayback()160 virtual oboe::Result startPlayback() { 161 return oboe::Result::OK; 162 } 163 stopPlayback()164 virtual oboe::Result stopPlayback() { 165 return oboe::Result::OK; 166 } 167 runBlockingIO()168 virtual void runBlockingIO() {}; 169 threadCallback(ActivityContext * context)170 static void threadCallback(ActivityContext *context) { 171 context->runBlockingIO(); 172 } 173 stopBlockingIOThread()174 void stopBlockingIOThread() { 175 if (dataThread != nullptr) { 176 // stop a thread that runs in place of the callback 177 threadEnabled.store(false); // ask thread to exit its loop 178 dataThread->join(); 179 dataThread = nullptr; 180 } 181 } 182 getPeakLevel(int index)183 virtual double getPeakLevel(int index) { 184 return 0.0; 185 } 186 187 static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) { 188 struct timespec time; 189 int result = clock_gettime(clockId, &time); 190 if (result < 0) { 191 return result; 192 } 193 return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec; 194 } 195 196 // Calculate time between beginning and when frame[0] occurred. calculateColdStartLatencyMillis(int32_t sampleRate,int64_t beginTimeNanos,int64_t timeStampPosition,int64_t timestampNanos)197 int32_t calculateColdStartLatencyMillis(int32_t sampleRate, 198 int64_t beginTimeNanos, 199 int64_t timeStampPosition, 200 int64_t timestampNanos) const { 201 int64_t elapsedNanos = NANOS_PER_SECOND * (timeStampPosition / (double) sampleRate); 202 int64_t timeOfFrameZero = timestampNanos - elapsedNanos; 203 int64_t coldStartLatencyNanos = timeOfFrameZero - beginTimeNanos; 204 return coldStartLatencyNanos / NANOS_PER_MILLISECOND; 205 } 206 getColdStartInputMillis()207 int32_t getColdStartInputMillis() { 208 std::shared_ptr<oboe::AudioStream> oboeStream = getInputStream(); 209 if (oboeStream != nullptr) { 210 int64_t framesRead = oboeStream->getFramesRead(); 211 if (framesRead > 0) { 212 // Base latency on the time that frame[0] would have been received by the app. 213 int64_t nowNanos = getNanoseconds(); 214 return calculateColdStartLatencyMillis(oboeStream->getSampleRate(), 215 mInputOpenedAt, 216 framesRead, 217 nowNanos); 218 } 219 } 220 return -1; 221 } 222 getColdStartOutputMillis()223 int32_t getColdStartOutputMillis() { 224 std::shared_ptr<oboe::AudioStream> oboeStream = getOutputStream(); 225 if (oboeStream != nullptr) { 226 auto result = oboeStream->getTimestamp(CLOCK_MONOTONIC); 227 if (result) { 228 auto frameTimestamp = result.value(); 229 // Calculate the time that frame[0] would have been played by the speaker. 230 int64_t position = frameTimestamp.position; 231 int64_t timestampNanos = frameTimestamp.timestamp; 232 return calculateColdStartLatencyMillis(oboeStream->getSampleRate(), 233 mOutputOpenedAt, 234 position, 235 timestampNanos); 236 } 237 } 238 return -1; 239 } 240 241 /** 242 * Trigger a sound or impulse. 243 * @param enabled 244 */ trigger()245 virtual void trigger() {} 246 247 bool isMMapUsed(int32_t streamIndex); 248 getFramesPerBlock()249 int32_t getFramesPerBlock() { 250 return (callbackSize == 0) ? mFramesPerBurst : callbackSize; 251 } 252 getCallbackCount()253 int64_t getCallbackCount() { 254 return oboeCallbackProxy.getCallbackCount(); 255 } 256 getLastErrorCallbackResult()257 oboe::Result getLastErrorCallbackResult() { 258 std::shared_ptr<oboe::AudioStream> stream = getOutputStream(); 259 if (stream == nullptr) { 260 stream = getInputStream(); 261 } 262 return stream ? oboe::Result::ErrorNull : stream->getLastErrorCallbackResult(); 263 } 264 getFramesPerCallback()265 int32_t getFramesPerCallback() { 266 return oboeCallbackProxy.getFramesPerCallback(); 267 } 268 setChannelEnabled(int channelIndex,bool enabled)269 virtual void setChannelEnabled(int channelIndex, bool enabled) {} 270 setSignalType(int signalType)271 virtual void setSignalType(int signalType) {} 272 273 virtual int32_t saveWaveFile(const char *filename); 274 setMinimumFramesBeforeRead(int32_t numFrames)275 virtual void setMinimumFramesBeforeRead(int32_t numFrames) {} 276 277 static bool mUseCallback; 278 static int callbackSize; 279 280 double getTimestampLatency(int32_t streamIndex); 281 282 protected: 283 std::shared_ptr<oboe::AudioStream> getInputStream(); 284 std::shared_ptr<oboe::AudioStream> getOutputStream(); 285 int32_t allocateStreamIndex(); 286 void freeStreamIndex(int32_t streamIndex); 287 createRecording()288 virtual void createRecording() { 289 mRecording = std::make_unique<MultiChannelRecording>(mChannelCount, 290 SECONDS_TO_RECORD * mSampleRate); 291 } 292 finishOpen(bool isInput,oboe::AudioStream * oboeStream)293 virtual void finishOpen(bool isInput, oboe::AudioStream *oboeStream) {} 294 295 virtual oboe::Result startStreams() = 0; 296 297 std::unique_ptr<float []> dataBuffer{}; 298 299 AudioStreamGateway audioStreamGateway; 300 OboeStreamCallbackProxy oboeCallbackProxy; 301 302 std::unique_ptr<MultiChannelRecording> mRecording{}; 303 304 int32_t mNextStreamHandle = 0; 305 std::unordered_map<int32_t, std::shared_ptr<oboe::AudioStream>> mOboeStreams; 306 int32_t mFramesPerBurst = 0; // TODO per stream 307 int32_t mChannelCount = 0; // TODO per stream 308 int32_t mSampleRate = 0; // TODO per stream 309 310 std::atomic<bool> threadEnabled{false}; 311 std::thread *dataThread = nullptr; // FIXME never gets deleted 312 313 private: 314 int64_t mInputOpenedAt = 0; 315 int64_t mOutputOpenedAt = 0; 316 }; 317 318 /** 319 * Test a single input stream. 320 */ 321 class ActivityTestInput : public ActivityContext { 322 public: 323 ActivityTestInput()324 ActivityTestInput() {} 325 virtual ~ActivityTestInput() = default; 326 327 void configureForStart() override; 328 getPeakLevel(int index)329 double getPeakLevel(int index) override { 330 return mInputAnalyzer.getPeakLevel(index); 331 } 332 333 void runBlockingIO() override; 334 setMinimumFramesBeforeRead(int32_t numFrames)335 void setMinimumFramesBeforeRead(int32_t numFrames) override { 336 mInputAnalyzer.setMinimumFramesBeforeRead(numFrames); 337 mMinimumFramesBeforeRead = numFrames; 338 } 339 getMinimumFramesBeforeRead()340 int32_t getMinimumFramesBeforeRead() const { 341 return mMinimumFramesBeforeRead; 342 } 343 344 protected: 345 startStreams()346 oboe::Result startStreams() override { 347 mInputAnalyzer.reset(); 348 mInputAnalyzer.setup(getInputStream()->getFramesPerBurst(), 349 getInputStream()->getChannelCount(), 350 getInputStream()->getFormat()); 351 return getInputStream()->requestStart(); 352 } 353 354 InputStreamCallbackAnalyzer mInputAnalyzer; 355 int32_t mMinimumFramesBeforeRead = 0; 356 }; 357 358 /** 359 * Record a configured input stream and play it back some simple way. 360 */ 361 class ActivityRecording : public ActivityTestInput { 362 public: 363 ActivityRecording()364 ActivityRecording() {} 365 virtual ~ActivityRecording() = default; 366 stop()367 oboe::Result stop() override { 368 369 oboe::Result resultStopPlayback = stopPlayback(); 370 oboe::Result resultStopAudio = ActivityContext::stop(); 371 372 return (resultStopPlayback != oboe::Result::OK) ? resultStopPlayback : resultStopAudio; 373 } 374 375 oboe::Result startPlayback() override; 376 377 oboe::Result stopPlayback() override; 378 379 PlayRecordingCallback mPlayRecordingCallback; 380 oboe::AudioStream *playbackStream = nullptr; 381 382 }; 383 384 /** 385 * Test a single output stream. 386 */ 387 class ActivityTestOutput : public ActivityContext { 388 public: ActivityTestOutput()389 ActivityTestOutput() 390 : sineOscillators(MAX_SINE_OSCILLATORS) 391 , sawtoothOscillators(MAX_SINE_OSCILLATORS) {} 392 393 virtual ~ActivityTestOutput() = default; 394 395 void close(int32_t streamIndex) override; 396 startStreams()397 oboe::Result startStreams() override { 398 return getOutputStream()->start(); 399 } 400 401 void configureForStart() override; 402 403 virtual void configureStreamGateway(); 404 405 void runBlockingIO() override; 406 407 void setChannelEnabled(int channelIndex, bool enabled) override; 408 409 // WARNING - must match order in strings.xml and OboeAudioOutputStream.java 410 enum SignalType { 411 Sine = 0, 412 Sawtooth = 1, 413 FreqSweep = 2, 414 PitchSweep = 3, 415 WhiteNoise = 4 416 }; 417 setSignalType(int signalType)418 void setSignalType(int signalType) override { 419 mSignalType = (SignalType) signalType; 420 } 421 422 protected: 423 SignalType mSignalType = SignalType::Sine; 424 425 std::vector<SineOscillator> sineOscillators; 426 std::vector<SawtoothOscillator> sawtoothOscillators; 427 static constexpr float kSweepPeriod = 10.0; // for triangle up and down 428 429 // A triangle LFO is shaped into either a linear or an exponential range for sweep. 430 TriangleOscillator mTriangleOscillator; 431 LinearShape mLinearShape; 432 ExponentialShape mExponentialShape; 433 class WhiteNoise mWhiteNoise; 434 435 std::unique_ptr<ManyToMultiConverter> manyToMulti; 436 std::unique_ptr<MonoToMultiConverter> monoToMulti; 437 std::shared_ptr<oboe::flowgraph::SinkFloat> mSinkFloat; 438 std::shared_ptr<oboe::flowgraph::SinkI16> mSinkI16; 439 std::shared_ptr<oboe::flowgraph::SinkI24> mSinkI24; 440 std::shared_ptr<oboe::flowgraph::SinkI32> mSinkI32; 441 }; 442 443 /** 444 * Generate a short beep with a very short attack. 445 * This is used by Java to measure output latency. 446 */ 447 class ActivityTapToTone : public ActivityTestOutput { 448 public: ActivityTapToTone()449 ActivityTapToTone() {} 450 virtual ~ActivityTapToTone() = default; 451 452 void configureForStart() override; 453 trigger()454 virtual void trigger() override { 455 sawPingGenerator.trigger(); 456 } 457 458 SawPingGenerator sawPingGenerator; 459 }; 460 461 /** 462 * Activity that uses synchronized input/output streams. 463 */ 464 class ActivityFullDuplex : public ActivityContext { 465 public: 466 467 void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; 468 getState()469 virtual int32_t getState() { return -1; } getResult()470 virtual int32_t getResult() { return -1; } isAnalyzerDone()471 virtual bool isAnalyzerDone() { return false; } 472 setMinimumFramesBeforeRead(int32_t numFrames)473 void setMinimumFramesBeforeRead(int32_t numFrames) override { 474 getFullDuplexAnalyzer()->setMinimumFramesBeforeRead(numFrames); 475 } 476 477 virtual FullDuplexAnalyzer *getFullDuplexAnalyzer() = 0; 478 getResetCount()479 int32_t getResetCount() { 480 return getFullDuplexAnalyzer()->getLoopbackProcessor()->getResetCount(); 481 } 482 483 protected: createRecording()484 void createRecording() override { 485 mRecording = std::make_unique<MultiChannelRecording>(2, // output and input 486 SECONDS_TO_RECORD * mSampleRate); 487 } 488 }; 489 490 /** 491 * Echo input to output through a delay line. 492 */ 493 class ActivityEcho : public ActivityFullDuplex { 494 public: 495 startStreams()496 oboe::Result startStreams() override { 497 return mFullDuplexEcho->start(); 498 } 499 500 void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; 501 setDelayTime(double delayTimeSeconds)502 void setDelayTime(double delayTimeSeconds) { 503 if (mFullDuplexEcho) { 504 mFullDuplexEcho->setDelayTime(delayTimeSeconds); 505 } 506 } 507 getFullDuplexAnalyzer()508 FullDuplexAnalyzer *getFullDuplexAnalyzer() override { 509 return (FullDuplexAnalyzer *) mFullDuplexEcho.get(); 510 } 511 512 protected: 513 void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; 514 515 private: 516 std::unique_ptr<FullDuplexEcho> mFullDuplexEcho{}; 517 }; 518 519 /** 520 * Measure Round Trip Latency 521 */ 522 class ActivityRoundTripLatency : public ActivityFullDuplex { 523 public: ActivityRoundTripLatency()524 ActivityRoundTripLatency() { 525 #define USE_WHITE_NOISE_ANALYZER 1 526 #if USE_WHITE_NOISE_ANALYZER 527 // New analyzer that uses a short pattern of white noise bursts. 528 mLatencyAnalyzer = std::make_unique<WhiteNoiseLatencyAnalyzer>(); 529 #else 530 // Old analyzer based on encoded random bits. 531 mLatencyAnalyzer = std::make_unique<EncodedRandomLatencyAnalyzer>(); 532 #endif 533 mLatencyAnalyzer->setup(); 534 } 535 virtual ~ActivityRoundTripLatency() = default; 536 startStreams()537 oboe::Result startStreams() override { 538 mAnalyzerLaunched = false; 539 return mFullDuplexLatency->start(); 540 } 541 542 void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; 543 getLatencyAnalyzer()544 LatencyAnalyzer *getLatencyAnalyzer() { 545 return mLatencyAnalyzer.get(); 546 } 547 getState()548 int32_t getState() override { 549 return getLatencyAnalyzer()->getState(); 550 } 551 getResult()552 int32_t getResult() override { 553 return getLatencyAnalyzer()->getState(); // TODO This does not look right. 554 } 555 isAnalyzerDone()556 bool isAnalyzerDone() override { 557 if (!mAnalyzerLaunched) { 558 mAnalyzerLaunched = launchAnalysisIfReady(); 559 } 560 return mLatencyAnalyzer->isDone(); 561 } 562 getFullDuplexAnalyzer()563 FullDuplexAnalyzer *getFullDuplexAnalyzer() override { 564 return (FullDuplexAnalyzer *) mFullDuplexLatency.get(); 565 } 566 analyzeData(LatencyAnalyzer * analyzer)567 static void analyzeData(LatencyAnalyzer *analyzer) { 568 analyzer->analyze(); 569 } 570 launchAnalysisIfReady()571 bool launchAnalysisIfReady() { 572 // Are we ready to do the analysis? 573 if (mLatencyAnalyzer->hasEnoughData()) { 574 // Crunch the numbers on a separate thread. 575 std::thread t(analyzeData, mLatencyAnalyzer.get()); 576 t.detach(); 577 return true; 578 } 579 return false; 580 } 581 582 jdouble measureTimestampLatency(); 583 584 protected: 585 void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; 586 587 private: 588 std::unique_ptr<FullDuplexAnalyzer> mFullDuplexLatency{}; 589 590 std::unique_ptr<LatencyAnalyzer> mLatencyAnalyzer; 591 bool mAnalyzerLaunched = false; 592 }; 593 594 /** 595 * Measure Glitches 596 */ 597 class ActivityGlitches : public ActivityFullDuplex { 598 public: 599 startStreams()600 oboe::Result startStreams() override { 601 return mFullDuplexGlitches->start(); 602 } 603 604 void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; 605 getGlitchAnalyzer()606 GlitchAnalyzer *getGlitchAnalyzer() { 607 return &mGlitchAnalyzer; 608 } 609 getState()610 int32_t getState() override { 611 return getGlitchAnalyzer()->getState(); 612 } 613 getResult()614 int32_t getResult() override { 615 return getGlitchAnalyzer()->getResult(); 616 } 617 isAnalyzerDone()618 bool isAnalyzerDone() override { 619 return mGlitchAnalyzer.isDone(); 620 } 621 getFullDuplexAnalyzer()622 FullDuplexAnalyzer *getFullDuplexAnalyzer() override { 623 return (FullDuplexAnalyzer *) mFullDuplexGlitches.get(); 624 } 625 626 protected: 627 void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; 628 629 private: 630 std::unique_ptr<FullDuplexAnalyzer> mFullDuplexGlitches{}; 631 GlitchAnalyzer mGlitchAnalyzer; 632 }; 633 634 /** 635 * Measure Data Path 636 */ 637 class ActivityDataPath : public ActivityFullDuplex { 638 public: 639 startStreams()640 oboe::Result startStreams() override { 641 return mFullDuplexDataPath->start(); 642 } 643 644 void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; 645 configureForStart()646 void configureForStart() override { 647 std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream(); 648 int32_t capacityInFrames = outputStream->getBufferCapacityInFrames(); 649 int32_t burstInFrames = outputStream->getFramesPerBurst(); 650 int32_t capacityInBursts = capacityInFrames / burstInFrames; 651 int32_t sizeInBursts = std::max(2, capacityInBursts / 2); 652 // Set size of buffer to minimize underruns. 653 auto result = outputStream->setBufferSizeInFrames(sizeInBursts * burstInFrames); 654 static_cast<void>(result); // Avoid unused variable. 655 LOGD("ActivityDataPath: %s() capacity = %d, burst = %d, size = %d", 656 __func__, capacityInFrames, burstInFrames, result.value()); 657 } 658 getDataPathAnalyzer()659 DataPathAnalyzer *getDataPathAnalyzer() { 660 return &mDataPathAnalyzer; 661 } 662 getFullDuplexAnalyzer()663 FullDuplexAnalyzer *getFullDuplexAnalyzer() override { 664 return (FullDuplexAnalyzer *) mFullDuplexDataPath.get(); 665 } 666 667 protected: 668 void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; 669 670 private: 671 std::unique_ptr<FullDuplexAnalyzer> mFullDuplexDataPath{}; 672 673 DataPathAnalyzer mDataPathAnalyzer; 674 }; 675 676 /** 677 * Test a single output stream. 678 */ 679 class ActivityTestDisconnect : public ActivityContext { 680 public: ActivityTestDisconnect()681 ActivityTestDisconnect() {} 682 683 virtual ~ActivityTestDisconnect() = default; 684 685 void close(int32_t streamIndex) override; 686 startStreams()687 oboe::Result startStreams() override { 688 std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream(); 689 if (outputStream) { 690 return outputStream->start(); 691 } 692 693 std::shared_ptr<oboe::AudioStream> inputStream = getInputStream(); 694 if (inputStream) { 695 return inputStream->start(); 696 } 697 return oboe::Result::ErrorNull; 698 } 699 700 void configureForStart() override; 701 702 private: 703 std::unique_ptr<SineOscillator> sineOscillator; 704 std::unique_ptr<MonoToMultiConverter> monoToMulti; 705 std::shared_ptr<oboe::flowgraph::SinkFloat> mSinkFloat; 706 }; 707 708 /** 709 * Switch between various 710 */ 711 class NativeAudioContext { 712 public: 713 getCurrentActivity()714 ActivityContext *getCurrentActivity() { 715 return currentActivity; 716 }; 717 setActivityType(int activityType)718 void setActivityType(int activityType) { 719 mActivityType = (ActivityType) activityType; 720 switch(mActivityType) { 721 default: 722 case ActivityType::Undefined: 723 case ActivityType::TestOutput: 724 currentActivity = &mActivityTestOutput; 725 break; 726 case ActivityType::TestInput: 727 currentActivity = &mActivityTestInput; 728 break; 729 case ActivityType::TapToTone: 730 currentActivity = &mActivityTapToTone; 731 break; 732 case ActivityType::RecordPlay: 733 currentActivity = &mActivityRecording; 734 break; 735 case ActivityType::Echo: 736 currentActivity = &mActivityEcho; 737 break; 738 case ActivityType::RoundTripLatency: 739 currentActivity = &mActivityRoundTripLatency; 740 break; 741 case ActivityType::Glitches: 742 currentActivity = &mActivityGlitches; 743 break; 744 case ActivityType::TestDisconnect: 745 currentActivity = &mActivityTestDisconnect; 746 break; 747 case ActivityType::DataPath: 748 currentActivity = &mActivityDataPath; 749 break; 750 } 751 } 752 setDelayTime(double delayTimeMillis)753 void setDelayTime(double delayTimeMillis) { 754 mActivityEcho.setDelayTime(delayTimeMillis); 755 } 756 757 ActivityTestOutput mActivityTestOutput; 758 ActivityTestInput mActivityTestInput; 759 ActivityTapToTone mActivityTapToTone; 760 ActivityRecording mActivityRecording; 761 ActivityEcho mActivityEcho; 762 ActivityRoundTripLatency mActivityRoundTripLatency; 763 ActivityGlitches mActivityGlitches; 764 ActivityDataPath mActivityDataPath; 765 ActivityTestDisconnect mActivityTestDisconnect; 766 767 768 private: 769 770 // WARNING - must match definitions in TestAudioActivity.java 771 enum ActivityType { 772 Undefined = -1, 773 TestOutput = 0, 774 TestInput = 1, 775 TapToTone = 2, 776 RecordPlay = 3, 777 Echo = 4, 778 RoundTripLatency = 5, 779 Glitches = 6, 780 TestDisconnect = 7, 781 DataPath = 8, 782 }; 783 784 ActivityType mActivityType = ActivityType::Undefined; 785 ActivityContext *currentActivity = &mActivityTestOutput; 786 787 }; 788 789 #endif //NATIVEOBOE_NATIVEAUDIOCONTEXT_H 790