• 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/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