• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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