• 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 <dlfcn.h>
21 #include <jni.h>
22 #include <sys/system_properties.h>
23 #include <thread>
24 #include <unordered_map>
25 #include <vector>
26 
27 #include "common/OboeDebug.h"
28 #include "oboe/Oboe.h"
29 
30 #include "AudioStreamGateway.h"
31 #include "flowunits/ImpulseOscillator.h"
32 #include "flowgraph/ManyToMultiConverter.h"
33 #include "flowgraph/MonoToMultiConverter.h"
34 #include "flowgraph/SinkFloat.h"
35 #include "flowgraph/SinkI16.h"
36 #include "flowunits/ExponentialShape.h"
37 #include "flowunits/LinearShape.h"
38 #include "flowunits/SineOscillator.h"
39 #include "flowunits/SawtoothOscillator.h"
40 
41 #include "FullDuplexEcho.h"
42 #include "FullDuplexGlitches.h"
43 #include "FullDuplexLatency.h"
44 #include "FullDuplexStream.h"
45 #include "InputStreamCallbackAnalyzer.h"
46 #include "MultiChannelRecording.h"
47 #include "OboeStreamCallbackProxy.h"
48 #include "PlayRecordingCallback.h"
49 #include "SawPingGenerator.h"
50 #include "flowunits/TriangleOscillator.h"
51 
52 // These must match order in strings.xml and in StreamConfiguration.java
53 #define NATIVE_MODE_UNSPECIFIED  0
54 #define NATIVE_MODE_OPENSLES     1
55 #define NATIVE_MODE_AAUDIO       2
56 
57 #define MAX_SINE_OSCILLATORS     8
58 #define AMPLITUDE_SINE           1.0
59 #define AMPLITUDE_SAWTOOTH       0.5
60 #define FREQUENCY_SAW_PING       800.0
61 #define AMPLITUDE_SAW_PING       0.8
62 #define AMPLITUDE_IMPULSE        0.7
63 
64 #define NANOS_PER_MICROSECOND    ((int64_t) 1000)
65 #define NANOS_PER_MILLISECOND    (1000 * NANOS_PER_MICROSECOND)
66 #define NANOS_PER_SECOND         (1000 * NANOS_PER_MILLISECOND)
67 
68 #define LIB_AAUDIO_NAME          "libaaudio.so"
69 #define FUNCTION_IS_MMAP         "AAudioStream_isMMapUsed"
70 #define FUNCTION_SET_MMAP_POLICY "AAudio_setMMapPolicy"
71 #define FUNCTION_GET_MMAP_POLICY "AAudio_getMMapPolicy"
72 
73 #define SECONDS_TO_RECORD        10
74 
75 typedef struct AAudioStreamStruct         AAudioStream;
76 
77 /**
78  * Call some AAudio test routines that are not part of the normal API.
79  */
80 class AAudioExtensions {
81 public:
AAudioExtensions()82     AAudioExtensions() {
83         int32_t policy = getIntegerProperty("aaudio.mmap_policy", 0);
84         mMMapSupported = isPolicyEnabled(policy);
85 
86         policy = getIntegerProperty("aaudio.mmap_exclusive_policy", 0);
87         mMMapExclusiveSupported = isPolicyEnabled(policy);
88     }
89 
isPolicyEnabled(int32_t policy)90     static bool isPolicyEnabled(int32_t policy) {
91         return (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS);
92     }
93 
getInstance()94     static AAudioExtensions &getInstance() {
95         static AAudioExtensions instance;
96         return instance;
97     }
98 
isMMapUsed(oboe::AudioStream * oboeStream)99     bool isMMapUsed(oboe::AudioStream *oboeStream) {
100         if (!loadLibrary()) return false;
101         if (mAAudioStream_isMMap == nullptr) return false;
102         AAudioStream *aaudioStream = (AAudioStream *) oboeStream->getUnderlyingStream();
103         return mAAudioStream_isMMap(aaudioStream);
104     }
105 
setMMapEnabled(bool enabled)106     bool setMMapEnabled(bool enabled) {
107         if (!loadLibrary()) return false;
108         if (mAAudio_setMMapPolicy == nullptr) return false;
109         return mAAudio_setMMapPolicy(enabled ? AAUDIO_POLICY_AUTO : AAUDIO_POLICY_NEVER);
110     }
111 
isMMapEnabled()112     bool isMMapEnabled() {
113         if (!loadLibrary()) return false;
114         if (mAAudio_getMMapPolicy == nullptr) return false;
115         int32_t policy = mAAudio_getMMapPolicy();
116         return isPolicyEnabled(policy);
117     }
118 
isMMapSupported()119     bool isMMapSupported() {
120         return mMMapSupported;
121     }
122 
isMMapExclusiveSupported()123     bool isMMapExclusiveSupported() {
124         return mMMapExclusiveSupported;
125     }
126 
127 private:
128 
129     enum {
130         AAUDIO_POLICY_NEVER = 1,
131         AAUDIO_POLICY_AUTO,
132         AAUDIO_POLICY_ALWAYS
133     };
134     typedef int32_t aaudio_policy_t;
135 
getIntegerProperty(const char * name,int defaultValue)136     int getIntegerProperty(const char *name, int defaultValue) {
137         int result = defaultValue;
138         char valueText[PROP_VALUE_MAX] = {0};
139         if (__system_property_get(name, valueText) != 0) {
140             result = atoi(valueText);
141         }
142         return result;
143     }
144 
145     // return true if it succeeds
loadLibrary()146     bool loadLibrary() {
147         if (mFirstTime) {
148             mFirstTime = false;
149             mLibHandle = dlopen(LIB_AAUDIO_NAME, 0);
150             if (mLibHandle == nullptr) {
151                 LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__);
152                 return false;
153             }
154 
155             mAAudioStream_isMMap = (bool (*)(AAudioStream *stream))
156                     dlsym(mLibHandle, FUNCTION_IS_MMAP);
157             if (mAAudioStream_isMMap == nullptr) {
158                 LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__);
159                 return false;
160             }
161 
162             mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy))
163                     dlsym(mLibHandle, FUNCTION_SET_MMAP_POLICY);
164             if (mAAudio_setMMapPolicy == nullptr) {
165                 LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__);
166                 return false;
167             }
168 
169             mAAudio_getMMapPolicy = (aaudio_policy_t (*)())
170                     dlsym(mLibHandle, FUNCTION_GET_MMAP_POLICY);
171             if (mAAudio_getMMapPolicy == nullptr) {
172                 LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__);
173                 return false;
174             }
175         }
176         return (mLibHandle != nullptr);
177     }
178 
179     bool      mFirstTime = true;
180     void     *mLibHandle = nullptr;
181     bool    (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr;
182     int32_t (*mAAudio_setMMapPolicy)(aaudio_policy_t policy) = nullptr;
183     aaudio_policy_t (*mAAudio_getMMapPolicy)() = nullptr;
184     bool      mMMapSupported = false;
185     bool      mMMapExclusiveSupported = false;
186 };
187 
188 /**
189  * Abstract base class that corresponds to a test at the Java level.
190  */
191 class ActivityContext {
192 public:
193 
ActivityContext()194     ActivityContext() {}
195     virtual ~ActivityContext() = default;
196 
getStream(int32_t streamIndex)197     oboe::AudioStream *getStream(int32_t streamIndex) {
198         auto it = mOboeStreams.find(streamIndex);
199         if (it != mOboeStreams.end()) {
200             return it->second.get();
201         } else {
202             return nullptr;
203         }
204     }
205 
206     virtual void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder);
207 
208     int open(jint nativeApi,
209              jint sampleRate,
210              jint channelCount,
211              jint format,
212              jint sharingMode,
213              jint performanceMode,
214              jint inputPreset,
215              jint deviceId,
216              jint sessionId,
217              jint framesPerBurst,
218              jboolean channelConversionAllowed,
219              jboolean formatConversionAllowed,
220              jint rateConversionQuality,
221              jboolean isMMap,
222              jboolean isInput);
223 
224 
225     virtual void close(int32_t streamIndex);
226 
configureForStart()227     virtual void configureForStart() {}
228 
229     oboe::Result start();
230 
231     oboe::Result pause();
232 
233     oboe::Result stopAllStreams();
234 
stop()235     virtual oboe::Result stop() {
236         return stopAllStreams();
237     }
238 
getCpuLoad()239     double getCpuLoad() {
240         return oboeCallbackProxy.getCpuLoad();
241     }
242 
setWorkload(double workload)243     void setWorkload(double workload) {
244         oboeCallbackProxy.setWorkload(workload);
245     }
246 
startPlayback()247     virtual oboe::Result startPlayback() {
248         return oboe::Result::OK;
249     }
250 
stopPlayback()251     virtual oboe::Result stopPlayback() {
252         return oboe::Result::OK;
253     }
254 
runBlockingIO()255     virtual void runBlockingIO() {};
256 
threadCallback(ActivityContext * context)257     static void threadCallback(ActivityContext *context) {
258         context->runBlockingIO();
259     }
260 
stopBlockingIOThread()261     void stopBlockingIOThread() {
262         if (dataThread != nullptr) {
263             // stop a thread that runs in place of the callback
264             threadEnabled.store(false); // ask thread to exit its loop
265             dataThread->join();
266             dataThread = nullptr;
267         }
268     }
269 
getPeakLevel(int index)270     virtual double getPeakLevel(int index) {
271         return 0.0;
272     }
273 
setEnabled(bool enabled)274     virtual void setEnabled(bool enabled) {
275     }
276 
277     bool isMMapUsed(int32_t streamIndex);
278 
getFramesPerBlock()279     int32_t getFramesPerBlock() {
280         return (callbackSize == 0) ? mFramesPerBurst : callbackSize;
281     }
282 
getCallbackCount()283     int64_t getCallbackCount() {
284         return oboeCallbackProxy.getCallbackCount();
285     }
286 
getFramesPerCallback()287     int32_t getFramesPerCallback() {
288         return oboeCallbackProxy.getFramesPerCallback();
289     }
290 
setChannelEnabled(int channelIndex,bool enabled)291     virtual void setChannelEnabled(int channelIndex, bool enabled) {}
292 
setSignalType(int signalType)293     virtual void setSignalType(int signalType) {}
294 
295     virtual int32_t saveWaveFile(const char *filename);
296 
setMinimumFramesBeforeRead(int32_t numFrames)297     virtual void setMinimumFramesBeforeRead(int32_t numFrames) {}
298 
299     static bool   mUseCallback;
300     static int    callbackSize;
301 
302 protected:
303     oboe::AudioStream *getInputStream();
304     oboe::AudioStream *getOutputStream();
305     int32_t allocateStreamIndex();
306     void freeStreamIndex(int32_t streamIndex);
307 
createRecording()308     virtual void createRecording() {
309         mRecording = std::make_unique<MultiChannelRecording>(mChannelCount,
310                                                              SECONDS_TO_RECORD * mSampleRate);
311     }
312 
finishOpen(bool isInput,oboe::AudioStream * oboeStream)313     virtual void finishOpen(bool isInput, oboe::AudioStream *oboeStream) {}
314 
315     virtual oboe::Result startStreams() = 0;
316 
317     std::unique_ptr<float []>    dataBuffer{};
318 
319     AudioStreamGateway           audioStreamGateway;
320     OboeStreamCallbackProxy      oboeCallbackProxy;
321 
322     std::unique_ptr<MultiChannelRecording>  mRecording{};
323 
324     int32_t                      mNextStreamHandle = 0;
325     std::unordered_map<int32_t, std::shared_ptr<oboe::AudioStream>>  mOboeStreams;
326     int32_t                      mFramesPerBurst = 0; // TODO per stream
327     int32_t                      mChannelCount = 0; // TODO per stream
328     int32_t                      mSampleRate = 0; // TODO per stream
329 
330     std::atomic<bool>            threadEnabled{false};
331     std::thread                 *dataThread = nullptr;
332 
333 private:
334 };
335 
336 /**
337  * Test a single input stream.
338  */
339 class ActivityTestInput : public ActivityContext {
340 public:
341 
ActivityTestInput()342     ActivityTestInput() {}
343     virtual ~ActivityTestInput() = default;
344 
345     void configureForStart() override;
346 
getPeakLevel(int index)347     double getPeakLevel(int index) override {
348         return mInputAnalyzer.getPeakLevel(index);
349     }
350 
351     void runBlockingIO() override;
352 
353     InputStreamCallbackAnalyzer  mInputAnalyzer;
354 
setMinimumFramesBeforeRead(int32_t numFrames)355     void setMinimumFramesBeforeRead(int32_t numFrames) override {
356         mInputAnalyzer.setMinimumFramesBeforeRead(numFrames);
357         mMinimumFramesBeforeRead = numFrames;
358     }
359 
getMinimumFramesBeforeRead()360     int32_t getMinimumFramesBeforeRead() const {
361         return mMinimumFramesBeforeRead;
362     }
363 
364 protected:
365 
startStreams()366     oboe::Result startStreams() override {
367         mInputAnalyzer.reset();
368         return getInputStream()->requestStart();
369     }
370 
371     int32_t mMinimumFramesBeforeRead = 0;
372 };
373 
374 /**
375  * Record a configured input stream and play it back some simple way.
376  */
377 class ActivityRecording : public ActivityTestInput {
378 public:
379 
ActivityRecording()380     ActivityRecording() {}
381     virtual ~ActivityRecording() = default;
382 
stop()383     oboe::Result stop() override {
384 
385         oboe::Result resultStopPlayback = stopPlayback();
386         oboe::Result resultStopAudio = ActivityContext::stop();
387 
388         return (resultStopPlayback != oboe::Result::OK) ? resultStopPlayback : resultStopAudio;
389     }
390 
391     oboe::Result startPlayback() override;
392 
393     oboe::Result stopPlayback() override;
394 
395     PlayRecordingCallback        mPlayRecordingCallback;
396     oboe::AudioStream           *playbackStream = nullptr;
397 
398 };
399 
400 /**
401  * Test a single output stream.
402  */
403 class ActivityTestOutput : public ActivityContext {
404 public:
ActivityTestOutput()405     ActivityTestOutput()
406             : sineOscillators(MAX_SINE_OSCILLATORS)
407             , sawtoothOscillators(MAX_SINE_OSCILLATORS) {}
408 
409     virtual ~ActivityTestOutput() = default;
410 
411     void close(int32_t streamIndex) override;
412 
startStreams()413     oboe::Result startStreams() override {
414         return getOutputStream()->start();
415     }
416 
417     void configureForStart() override;
418 
419     virtual void configureStreamGateway();
420 
421     void runBlockingIO() override;
422 
423     void setChannelEnabled(int channelIndex, bool enabled) override;
424 
425     // WARNING - must match order in strings.xml and OboeAudioOutputStream.java
426     enum SignalType {
427         Sine = 0,
428         Sawtooth = 1,
429         FreqSweep = 2,
430         PitchSweep = 3,
431         WhiteNoise = 4
432     };
433 
setSignalType(int signalType)434     void setSignalType(int signalType) override {
435         mSignalType = (SignalType) signalType;
436     }
437 
438 protected:
439     SignalType                       mSignalType = SignalType::Sine;
440 
441     std::vector<SineOscillator>      sineOscillators;
442     std::vector<SawtoothOscillator>  sawtoothOscillators;
443     static constexpr float           kSweepPeriod = 10.0; // for triangle up and down
444 
445     // A triangle LFO is shaped into either a linear or an exponential range.
446     TriangleOscillator               mTriangleOscillator;
447     LinearShape                      mLinearShape;
448     ExponentialShape                 mExponentialShape;
449 
450     std::unique_ptr<ManyToMultiConverter>   manyToMulti;
451     std::unique_ptr<MonoToMultiConverter>   monoToMulti;
452     std::shared_ptr<oboe::flowgraph::SinkFloat>   mSinkFloat;
453     std::shared_ptr<oboe::flowgraph::SinkI16>     mSinkI16;
454 };
455 
456 /**
457  * Generate a short beep with a very short attack.
458  * This is used by Java to measure output latency.
459  */
460 class ActivityTapToTone : public ActivityTestOutput {
461 public:
ActivityTapToTone()462     ActivityTapToTone() {}
463     virtual ~ActivityTapToTone() = default;
464 
465     void configureForStart() override;
466 
setEnabled(bool enabled)467     virtual void setEnabled(bool enabled) override {
468         sawPingGenerator.setEnabled(enabled);
469     }
470 
471     SawPingGenerator             sawPingGenerator;
472 };
473 
474 /**
475  * Activity that uses synchronized input/output streams.
476  */
477 class ActivityFullDuplex : public ActivityContext {
478 public:
479 
480     void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override;
481 
getState()482     virtual int32_t getState() { return -1; }
getResult()483     virtual int32_t getResult() { return -1; }
isAnalyzerDone()484     virtual bool isAnalyzerDone() { return false; }
485 
setMinimumFramesBeforeRead(int32_t numFrames)486     void setMinimumFramesBeforeRead(int32_t numFrames) override {
487         getFullDuplexAnalyzer()->setMinimumFramesBeforeRead(numFrames);
488     }
489 
490     virtual FullDuplexAnalyzer *getFullDuplexAnalyzer() = 0;
491 
getResetCount()492     int32_t getResetCount() {
493         return getFullDuplexAnalyzer()->getLoopbackProcessor()->getResetCount();
494     }
495 
496 protected:
createRecording()497     void createRecording() override {
498         mRecording = std::make_unique<MultiChannelRecording>(2, // output and input
499                                                              SECONDS_TO_RECORD * mSampleRate);
500     }
501 };
502 
503 /**
504  * Echo input to output through a delay line.
505  */
506 class ActivityEcho : public ActivityFullDuplex {
507 public:
508 
startStreams()509     oboe::Result startStreams() override {
510         return mFullDuplexEcho->start();
511     }
512 
513     void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override;
514 
setDelayTime(double delayTimeSeconds)515     void setDelayTime(double delayTimeSeconds) {
516         if (mFullDuplexEcho) {
517             mFullDuplexEcho->setDelayTime(delayTimeSeconds);
518         }
519     }
520 
getFullDuplexAnalyzer()521     FullDuplexAnalyzer *getFullDuplexAnalyzer() override {
522         return (FullDuplexAnalyzer *) mFullDuplexEcho.get();
523     }
524 
525 protected:
526     void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override;
527 
528 private:
529     std::unique_ptr<FullDuplexEcho>   mFullDuplexEcho{};
530 };
531 
532 /**
533  * Measure Round Trip Latency
534  */
535 class ActivityRoundTripLatency : public ActivityFullDuplex {
536 public:
537 
startStreams()538     oboe::Result startStreams() override {
539         return mFullDuplexLatency->start();
540     }
541 
542     void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override;
543 
getLatencyAnalyzer()544     LatencyAnalyzer *getLatencyAnalyzer() {
545         return mFullDuplexLatency->getLatencyAnalyzer();
546     }
547 
getState()548     int32_t getState() override {
549         return getLatencyAnalyzer()->getState();
550     }
getResult()551     int32_t getResult() override {
552         return getLatencyAnalyzer()->getState();
553     }
isAnalyzerDone()554     bool isAnalyzerDone() override {
555         return mFullDuplexLatency->isDone();
556     }
557 
getFullDuplexAnalyzer()558     FullDuplexAnalyzer *getFullDuplexAnalyzer() override {
559         return (FullDuplexAnalyzer *) mFullDuplexLatency.get();
560     }
561 
562 protected:
563     void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override;
564 
565 private:
566     std::unique_ptr<FullDuplexLatency>   mFullDuplexLatency{};
567 };
568 
569 /**
570  * Measure Glitches
571  */
572 class ActivityGlitches : public ActivityFullDuplex {
573 public:
574 
startStreams()575     oboe::Result startStreams() override {
576         return mFullDuplexGlitches->start();
577     }
578 
579     void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override;
580 
getGlitchAnalyzer()581     GlitchAnalyzer *getGlitchAnalyzer() {
582         if (!mFullDuplexGlitches) return nullptr;
583         return mFullDuplexGlitches->getGlitchAnalyzer();
584     }
585 
getState()586     int32_t getState() override {
587         return getGlitchAnalyzer()->getState();
588     }
getResult()589     int32_t getResult() override {
590         return getGlitchAnalyzer()->getResult();
591     }
isAnalyzerDone()592     bool isAnalyzerDone() override {
593         return mFullDuplexGlitches->isDone();
594     }
595 
getFullDuplexAnalyzer()596     FullDuplexAnalyzer *getFullDuplexAnalyzer() override {
597         return (FullDuplexAnalyzer *) mFullDuplexGlitches.get();
598     }
599 
600 protected:
601     void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override;
602 
603 private:
604     std::unique_ptr<FullDuplexGlitches>   mFullDuplexGlitches{};
605 };
606 
607 /**
608  * Test a single output stream.
609  */
610 class ActivityTestDisconnect : public ActivityContext {
611 public:
ActivityTestDisconnect()612     ActivityTestDisconnect() {}
613 
614     virtual ~ActivityTestDisconnect() = default;
615 
616     void close(int32_t streamIndex) override;
617 
startStreams()618     oboe::Result startStreams() override {
619         oboe::AudioStream *outputStream = getOutputStream();
620         if (outputStream) {
621             return outputStream->start();
622         }
623 
624         oboe::AudioStream *inputStream = getInputStream();
625         if (inputStream) {
626             return inputStream->start();
627         }
628         return oboe::Result::ErrorNull;
629     }
630 
631     void configureForStart() override;
632 
633 private:
634     std::unique_ptr<SineOscillator>         sineOscillator;
635     std::unique_ptr<MonoToMultiConverter>   monoToMulti;
636     std::shared_ptr<oboe::flowgraph::SinkFloat>   mSinkFloat;
637 };
638 
639 /**
640  * Switch between various
641  */
642 class NativeAudioContext {
643 public:
644 
getCurrentActivity()645     ActivityContext *getCurrentActivity() {
646         return currentActivity;
647     };
648 
setActivityType(int activityType)649     void setActivityType(int activityType) {
650         mActivityType = (ActivityType) activityType;
651         switch(mActivityType) {
652             default:
653             case ActivityType::Undefined:
654             case ActivityType::TestOutput:
655                 currentActivity = &mActivityTestOutput;
656                 break;
657             case ActivityType::TestInput:
658                 currentActivity = &mActivityTestInput;
659                 break;
660             case ActivityType::TapToTone:
661                 currentActivity = &mActivityTapToTone;
662                 break;
663             case ActivityType::RecordPlay:
664                 currentActivity = &mActivityRecording;
665                 break;
666             case ActivityType::Echo:
667                 currentActivity = &mActivityEcho;
668                 break;
669             case ActivityType::RoundTripLatency:
670                 currentActivity = &mActivityRoundTripLatency;
671                 break;
672             case ActivityType::Glitches:
673                 currentActivity = &mActivityGlitches;
674                 break;
675             case ActivityType::TestDisconnect:
676                 currentActivity = &mActivityTestDisconnect;
677                 break;
678         }
679     }
680 
setDelayTime(double delayTimeMillis)681     void setDelayTime(double delayTimeMillis) {
682         mActivityEcho.setDelayTime(delayTimeMillis);
683     }
684 
685     ActivityTestOutput           mActivityTestOutput;
686     ActivityTestInput            mActivityTestInput;
687     ActivityTapToTone            mActivityTapToTone;
688     ActivityRecording            mActivityRecording;
689     ActivityEcho                 mActivityEcho;
690     ActivityRoundTripLatency     mActivityRoundTripLatency;
691     ActivityGlitches             mActivityGlitches;
692     ActivityTestDisconnect       mActivityTestDisconnect;
693 
694 private:
695 
696     // WARNING - must match definitions in TestAudioActivity.java
697     enum ActivityType {
698         Undefined = -1,
699         TestOutput = 0,
700         TestInput = 1,
701         TapToTone = 2,
702         RecordPlay = 3,
703         Echo = 4,
704         RoundTripLatency = 5,
705         Glitches = 6,
706         TestDisconnect = 7,
707     };
708 
709     ActivityType                 mActivityType = ActivityType::Undefined;
710     ActivityContext             *currentActivity = &mActivityTestOutput;
711 
712 };
713 
714 #endif //NATIVEOBOE_NATIVEAUDIOCONTEXT_H
715