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