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