xref: /aosp_15_r20/external/oboe/apps/OboeTester/app/src/main/cpp/NativeAudioContext.h (revision 05767d913155b055644481607e6fa1e35e2fe72c)
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