| /* |
| * Copyright 2015 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef NATIVEOBOE_NATIVEAUDIOCONTEXT_H |
| #define NATIVEOBOE_NATIVEAUDIOCONTEXT_H |
| |
| #include <jni.h> |
| #include <sys/system_properties.h> |
| #include <thread> |
| #include <unordered_map> |
| #include <vector> |
| |
| #include "common/OboeDebug.h" |
| #include "oboe/Oboe.h" |
| |
| #include "aaudio/AAudioExtensions.h" |
| #include "AudioStreamGateway.h" |
| |
| #include "flowunits/ImpulseOscillator.h" |
| #include "flowgraph/ManyToMultiConverter.h" |
| #include "flowgraph/MonoToMultiConverter.h" |
| #include "flowgraph/SinkFloat.h" |
| #include "flowgraph/SinkI16.h" |
| #include "flowunits/ExponentialShape.h" |
| #include "flowunits/LinearShape.h" |
| #include "flowunits/SineOscillator.h" |
| #include "flowunits/SawtoothOscillator.h" |
| #include "flowunits/TriangleOscillator.h" |
| |
| #include "FullDuplexAnalyzer.h" |
| #include "FullDuplexEcho.h" |
| #include "FullDuplexStream.h" |
| #include "analyzer/GlitchAnalyzer.h" |
| #include "analyzer/DataPathAnalyzer.h" |
| #include "InputStreamCallbackAnalyzer.h" |
| #include "MultiChannelRecording.h" |
| #include "OboeStreamCallbackProxy.h" |
| #include "PlayRecordingCallback.h" |
| #include "SawPingGenerator.h" |
| |
| // These must match order in strings.xml and in StreamConfiguration.java |
| #define NATIVE_MODE_UNSPECIFIED 0 |
| #define NATIVE_MODE_OPENSLES 1 |
| #define NATIVE_MODE_AAUDIO 2 |
| |
| #define MAX_SINE_OSCILLATORS 8 |
| #define AMPLITUDE_SINE 1.0 |
| #define AMPLITUDE_SAWTOOTH 0.5 |
| #define FREQUENCY_SAW_PING 800.0 |
| #define AMPLITUDE_SAW_PING 0.8 |
| #define AMPLITUDE_IMPULSE 0.7 |
| |
| #define NANOS_PER_MICROSECOND ((int64_t) 1000) |
| #define NANOS_PER_MILLISECOND (1000 * NANOS_PER_MICROSECOND) |
| #define NANOS_PER_SECOND (1000 * NANOS_PER_MILLISECOND) |
| |
| #define SECONDS_TO_RECORD 10 |
| |
| /** |
| * Abstract base class that corresponds to a test at the Java level. |
| */ |
| class ActivityContext { |
| public: |
| |
| ActivityContext() {} |
| |
| virtual ~ActivityContext() = default; |
| |
| std::shared_ptr<oboe::AudioStream> getStream(int32_t streamIndex) { |
| auto it = mOboeStreams.find(streamIndex); |
| if (it != mOboeStreams.end()) { |
| return it->second; |
| } else { |
| return nullptr; |
| } |
| } |
| |
| virtual void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder); |
| |
| /** |
| * Open a stream with the given parameters. |
| * @param nativeApi |
| * @param sampleRate |
| * @param channelCount |
| * @param format |
| * @param sharingMode |
| * @param performanceMode |
| * @param inputPreset |
| * @param deviceId |
| * @param sessionId |
| * @param framesPerBurst |
| * @param channelConversionAllowed |
| * @param formatConversionAllowed |
| * @param rateConversionQuality |
| * @param isMMap |
| * @param isInput |
| * @return stream ID |
| */ |
| int open(jint nativeApi, |
| jint sampleRate, |
| jint channelCount, |
| jint format, |
| jint sharingMode, |
| jint performanceMode, |
| jint inputPreset, |
| jint deviceId, |
| jint sessionId, |
| jint framesPerBurst, |
| jboolean channelConversionAllowed, |
| jboolean formatConversionAllowed, |
| jint rateConversionQuality, |
| jboolean isMMap, |
| jboolean isInput); |
| |
| virtual void close(int32_t streamIndex); |
| |
| virtual void configureForStart() {} |
| |
| oboe::Result start(); |
| |
| oboe::Result pause(); |
| |
| oboe::Result stopAllStreams(); |
| |
| virtual oboe::Result stop() { |
| return stopAllStreams(); |
| } |
| |
| double getCpuLoad() { |
| return oboeCallbackProxy.getCpuLoad(); |
| } |
| |
| void setWorkload(double workload) { |
| oboeCallbackProxy.setWorkload(workload); |
| } |
| |
| virtual oboe::Result startPlayback() { |
| return oboe::Result::OK; |
| } |
| |
| virtual oboe::Result stopPlayback() { |
| return oboe::Result::OK; |
| } |
| |
| virtual void runBlockingIO() {}; |
| |
| static void threadCallback(ActivityContext *context) { |
| context->runBlockingIO(); |
| } |
| |
| void stopBlockingIOThread() { |
| if (dataThread != nullptr) { |
| // stop a thread that runs in place of the callback |
| threadEnabled.store(false); // ask thread to exit its loop |
| dataThread->join(); |
| dataThread = nullptr; |
| } |
| } |
| |
| virtual double getPeakLevel(int index) { |
| return 0.0; |
| } |
| |
| static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) { |
| struct timespec time; |
| int result = clock_gettime(clockId, &time); |
| if (result < 0) { |
| return result; |
| } |
| return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec; |
| } |
| |
| // Calculate time between beginning and when frame[0] occurred. |
| int32_t calculateColdStartLatencyMillis(int32_t sampleRate, |
| int64_t beginTimeNanos, |
| int64_t timeStampPosition, |
| int64_t timestampNanos) const { |
| int64_t elapsedNanos = NANOS_PER_SECOND * (timeStampPosition / (double) sampleRate); |
| int64_t timeOfFrameZero = timestampNanos - elapsedNanos; |
| int64_t coldStartLatencyNanos = timeOfFrameZero - beginTimeNanos; |
| return coldStartLatencyNanos / NANOS_PER_MILLISECOND; |
| } |
| |
| int32_t getColdStartInputMillis() { |
| std::shared_ptr<oboe::AudioStream> oboeStream = getInputStream(); |
| if (oboeStream != nullptr) { |
| int64_t framesRead = oboeStream->getFramesRead(); |
| if (framesRead > 0) { |
| // Base latency on the time that frame[0] would have been received by the app. |
| int64_t nowNanos = getNanoseconds(); |
| return calculateColdStartLatencyMillis(oboeStream->getSampleRate(), |
| mInputOpenedAt, |
| framesRead, |
| nowNanos); |
| } |
| } |
| return -1; |
| } |
| |
| int32_t getColdStartOutputMillis() { |
| std::shared_ptr<oboe::AudioStream> oboeStream = getOutputStream(); |
| if (oboeStream != nullptr) { |
| auto result = oboeStream->getTimestamp(CLOCK_MONOTONIC); |
| if (result) { |
| auto frameTimestamp = result.value(); |
| // Calculate the time that frame[0] would have been played by the speaker. |
| int64_t position = frameTimestamp.position; |
| int64_t timestampNanos = frameTimestamp.timestamp; |
| return calculateColdStartLatencyMillis(oboeStream->getSampleRate(), |
| mOutputOpenedAt, |
| position, |
| timestampNanos); |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Trigger a sound or impulse. |
| * @param enabled |
| */ |
| virtual void trigger() {} |
| |
| bool isMMapUsed(int32_t streamIndex); |
| |
| int32_t getFramesPerBlock() { |
| return (callbackSize == 0) ? mFramesPerBurst : callbackSize; |
| } |
| |
| int64_t getCallbackCount() { |
| return oboeCallbackProxy.getCallbackCount(); |
| } |
| |
| oboe::Result getLastErrorCallbackResult() { |
| std::shared_ptr<oboe::AudioStream> stream = getOutputStream(); |
| if (stream == nullptr) { |
| stream = getInputStream(); |
| } |
| return stream ? oboe::Result::ErrorNull : stream->getLastErrorCallbackResult(); |
| } |
| |
| int32_t getFramesPerCallback() { |
| return oboeCallbackProxy.getFramesPerCallback(); |
| } |
| |
| virtual void setChannelEnabled(int channelIndex, bool enabled) {} |
| |
| virtual void setSignalType(int signalType) {} |
| |
| virtual int32_t saveWaveFile(const char *filename); |
| |
| virtual void setMinimumFramesBeforeRead(int32_t numFrames) {} |
| |
| static bool mUseCallback; |
| static int callbackSize; |
| |
| double getTimestampLatency(int32_t streamIndex); |
| |
| protected: |
| std::shared_ptr<oboe::AudioStream> getInputStream(); |
| std::shared_ptr<oboe::AudioStream> getOutputStream(); |
| int32_t allocateStreamIndex(); |
| void freeStreamIndex(int32_t streamIndex); |
| |
| virtual void createRecording() { |
| mRecording = std::make_unique<MultiChannelRecording>(mChannelCount, |
| SECONDS_TO_RECORD * mSampleRate); |
| } |
| |
| virtual void finishOpen(bool isInput, oboe::AudioStream *oboeStream) {} |
| |
| virtual oboe::Result startStreams() = 0; |
| |
| std::unique_ptr<float []> dataBuffer{}; |
| |
| AudioStreamGateway audioStreamGateway; |
| OboeStreamCallbackProxy oboeCallbackProxy; |
| |
| std::unique_ptr<MultiChannelRecording> mRecording{}; |
| |
| int32_t mNextStreamHandle = 0; |
| std::unordered_map<int32_t, std::shared_ptr<oboe::AudioStream>> mOboeStreams; |
| int32_t mFramesPerBurst = 0; // TODO per stream |
| int32_t mChannelCount = 0; // TODO per stream |
| int32_t mSampleRate = 0; // TODO per stream |
| |
| std::atomic<bool> threadEnabled{false}; |
| std::thread *dataThread = nullptr; |
| |
| private: |
| int64_t mInputOpenedAt = 0; |
| int64_t mOutputOpenedAt = 0; |
| }; |
| |
| /** |
| * Test a single input stream. |
| */ |
| class ActivityTestInput : public ActivityContext { |
| public: |
| |
| ActivityTestInput() {} |
| virtual ~ActivityTestInput() = default; |
| |
| void configureForStart() override; |
| |
| double getPeakLevel(int index) override { |
| return mInputAnalyzer.getPeakLevel(index); |
| } |
| |
| void runBlockingIO() override; |
| |
| InputStreamCallbackAnalyzer mInputAnalyzer; |
| |
| void setMinimumFramesBeforeRead(int32_t numFrames) override { |
| mInputAnalyzer.setMinimumFramesBeforeRead(numFrames); |
| mMinimumFramesBeforeRead = numFrames; |
| } |
| |
| int32_t getMinimumFramesBeforeRead() const { |
| return mMinimumFramesBeforeRead; |
| } |
| |
| protected: |
| |
| oboe::Result startStreams() override { |
| mInputAnalyzer.reset(); |
| return getInputStream()->requestStart(); |
| } |
| |
| int32_t mMinimumFramesBeforeRead = 0; |
| }; |
| |
| /** |
| * Record a configured input stream and play it back some simple way. |
| */ |
| class ActivityRecording : public ActivityTestInput { |
| public: |
| |
| ActivityRecording() {} |
| virtual ~ActivityRecording() = default; |
| |
| oboe::Result stop() override { |
| |
| oboe::Result resultStopPlayback = stopPlayback(); |
| oboe::Result resultStopAudio = ActivityContext::stop(); |
| |
| return (resultStopPlayback != oboe::Result::OK) ? resultStopPlayback : resultStopAudio; |
| } |
| |
| oboe::Result startPlayback() override; |
| |
| oboe::Result stopPlayback() override; |
| |
| PlayRecordingCallback mPlayRecordingCallback; |
| oboe::AudioStream *playbackStream = nullptr; |
| |
| }; |
| |
| /** |
| * Test a single output stream. |
| */ |
| class ActivityTestOutput : public ActivityContext { |
| public: |
| ActivityTestOutput() |
| : sineOscillators(MAX_SINE_OSCILLATORS) |
| , sawtoothOscillators(MAX_SINE_OSCILLATORS) {} |
| |
| virtual ~ActivityTestOutput() = default; |
| |
| void close(int32_t streamIndex) override; |
| |
| oboe::Result startStreams() override { |
| return getOutputStream()->start(); |
| } |
| |
| void configureForStart() override; |
| |
| virtual void configureStreamGateway(); |
| |
| void runBlockingIO() override; |
| |
| void setChannelEnabled(int channelIndex, bool enabled) override; |
| |
| // WARNING - must match order in strings.xml and OboeAudioOutputStream.java |
| enum SignalType { |
| Sine = 0, |
| Sawtooth = 1, |
| FreqSweep = 2, |
| PitchSweep = 3, |
| WhiteNoise = 4 |
| }; |
| |
| void setSignalType(int signalType) override { |
| mSignalType = (SignalType) signalType; |
| } |
| |
| protected: |
| SignalType mSignalType = SignalType::Sine; |
| |
| std::vector<SineOscillator> sineOscillators; |
| std::vector<SawtoothOscillator> sawtoothOscillators; |
| static constexpr float kSweepPeriod = 10.0; // for triangle up and down |
| |
| // A triangle LFO is shaped into either a linear or an exponential range. |
| TriangleOscillator mTriangleOscillator; |
| LinearShape mLinearShape; |
| ExponentialShape mExponentialShape; |
| |
| std::unique_ptr<ManyToMultiConverter> manyToMulti; |
| std::unique_ptr<MonoToMultiConverter> monoToMulti; |
| std::shared_ptr<oboe::flowgraph::SinkFloat> mSinkFloat; |
| std::shared_ptr<oboe::flowgraph::SinkI16> mSinkI16; |
| }; |
| |
| /** |
| * Generate a short beep with a very short attack. |
| * This is used by Java to measure output latency. |
| */ |
| class ActivityTapToTone : public ActivityTestOutput { |
| public: |
| ActivityTapToTone() {} |
| virtual ~ActivityTapToTone() = default; |
| |
| void configureForStart() override; |
| |
| virtual void trigger() override { |
| sawPingGenerator.trigger(); |
| } |
| |
| SawPingGenerator sawPingGenerator; |
| }; |
| |
| /** |
| * Activity that uses synchronized input/output streams. |
| */ |
| class ActivityFullDuplex : public ActivityContext { |
| public: |
| |
| void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; |
| |
| virtual int32_t getState() { return -1; } |
| virtual int32_t getResult() { return -1; } |
| virtual bool isAnalyzerDone() { return false; } |
| |
| void setMinimumFramesBeforeRead(int32_t numFrames) override { |
| getFullDuplexAnalyzer()->setMinimumFramesBeforeRead(numFrames); |
| } |
| |
| virtual FullDuplexAnalyzer *getFullDuplexAnalyzer() = 0; |
| |
| int32_t getResetCount() { |
| return getFullDuplexAnalyzer()->getLoopbackProcessor()->getResetCount(); |
| } |
| |
| protected: |
| void createRecording() override { |
| mRecording = std::make_unique<MultiChannelRecording>(2, // output and input |
| SECONDS_TO_RECORD * mSampleRate); |
| } |
| }; |
| |
| /** |
| * Echo input to output through a delay line. |
| */ |
| class ActivityEcho : public ActivityFullDuplex { |
| public: |
| |
| oboe::Result startStreams() override { |
| return mFullDuplexEcho->start(); |
| } |
| |
| void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; |
| |
| void setDelayTime(double delayTimeSeconds) { |
| if (mFullDuplexEcho) { |
| mFullDuplexEcho->setDelayTime(delayTimeSeconds); |
| } |
| } |
| |
| FullDuplexAnalyzer *getFullDuplexAnalyzer() override { |
| return (FullDuplexAnalyzer *) mFullDuplexEcho.get(); |
| } |
| |
| protected: |
| void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; |
| |
| private: |
| std::unique_ptr<FullDuplexEcho> mFullDuplexEcho{}; |
| }; |
| |
| /** |
| * Measure Round Trip Latency |
| */ |
| class ActivityRoundTripLatency : public ActivityFullDuplex { |
| public: |
| |
| oboe::Result startStreams() override { |
| mAnalyzerLaunched = false; |
| return mFullDuplexLatency->start(); |
| } |
| |
| void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; |
| |
| LatencyAnalyzer *getLatencyAnalyzer() { |
| return &mEchoAnalyzer; |
| } |
| |
| int32_t getState() override { |
| return getLatencyAnalyzer()->getState(); |
| } |
| |
| int32_t getResult() override { |
| return getLatencyAnalyzer()->getState(); // TODO This does not look right. |
| } |
| |
| bool isAnalyzerDone() override { |
| if (!mAnalyzerLaunched) { |
| mAnalyzerLaunched = launchAnalysisIfReady(); |
| } |
| return mEchoAnalyzer.isDone(); |
| } |
| |
| FullDuplexAnalyzer *getFullDuplexAnalyzer() override { |
| return (FullDuplexAnalyzer *) mFullDuplexLatency.get(); |
| } |
| |
| static void analyzeData(PulseLatencyAnalyzer *analyzer) { |
| analyzer->analyze(); |
| } |
| |
| bool launchAnalysisIfReady() { |
| // Are we ready to do the analysis? |
| if (mEchoAnalyzer.hasEnoughData()) { |
| // Crunch the numbers on a separate thread. |
| std::thread t(analyzeData, &mEchoAnalyzer); |
| t.detach(); |
| return true; |
| } |
| return false; |
| } |
| |
| protected: |
| void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; |
| |
| private: |
| std::unique_ptr<FullDuplexAnalyzer> mFullDuplexLatency{}; |
| |
| PulseLatencyAnalyzer mEchoAnalyzer; |
| bool mAnalyzerLaunched = false; |
| }; |
| |
| /** |
| * Measure Glitches |
| */ |
| class ActivityGlitches : public ActivityFullDuplex { |
| public: |
| |
| oboe::Result startStreams() override { |
| return mFullDuplexGlitches->start(); |
| } |
| |
| void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; |
| |
| GlitchAnalyzer *getGlitchAnalyzer() { |
| return &mGlitchAnalyzer; |
| } |
| |
| int32_t getState() override { |
| return getGlitchAnalyzer()->getState(); |
| } |
| |
| int32_t getResult() override { |
| return getGlitchAnalyzer()->getResult(); |
| } |
| |
| bool isAnalyzerDone() override { |
| return mGlitchAnalyzer.isDone(); |
| } |
| |
| FullDuplexAnalyzer *getFullDuplexAnalyzer() override { |
| return (FullDuplexAnalyzer *) mFullDuplexGlitches.get(); |
| } |
| |
| protected: |
| void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; |
| |
| private: |
| std::unique_ptr<FullDuplexAnalyzer> mFullDuplexGlitches{}; |
| GlitchAnalyzer mGlitchAnalyzer; |
| }; |
| |
| /** |
| * Measure Data Path |
| */ |
| class ActivityDataPath : public ActivityFullDuplex { |
| public: |
| |
| oboe::Result startStreams() override { |
| return mFullDuplexDataPath->start(); |
| } |
| |
| void configureBuilder(bool isInput, oboe::AudioStreamBuilder &builder) override; |
| |
| void configureForStart() override { |
| std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream(); |
| int32_t capacityInFrames = outputStream->getBufferCapacityInFrames(); |
| int32_t burstInFrames = outputStream->getFramesPerBurst(); |
| int32_t capacityInBursts = capacityInFrames / burstInFrames; |
| int32_t sizeInBursts = std::max(2, capacityInBursts / 2); |
| // Set size of buffer to minimize underruns. |
| auto result = outputStream->setBufferSizeInFrames(sizeInBursts * burstInFrames); |
| static_cast<void>(result); // Avoid unused variable. |
| LOGD("ActivityDataPath: %s() capacity = %d, burst = %d, size = %d", |
| __func__, capacityInFrames, burstInFrames, result.value()); |
| } |
| |
| DataPathAnalyzer *getDataPathAnalyzer() { |
| return &mDataPathAnalyzer; |
| } |
| |
| FullDuplexAnalyzer *getFullDuplexAnalyzer() override { |
| return (FullDuplexAnalyzer *) mFullDuplexDataPath.get(); |
| } |
| |
| protected: |
| void finishOpen(bool isInput, oboe::AudioStream *oboeStream) override; |
| |
| private: |
| std::unique_ptr<FullDuplexAnalyzer> mFullDuplexDataPath{}; |
| |
| DataPathAnalyzer mDataPathAnalyzer; |
| }; |
| |
| /** |
| * Test a single output stream. |
| */ |
| class ActivityTestDisconnect : public ActivityContext { |
| public: |
| ActivityTestDisconnect() {} |
| |
| virtual ~ActivityTestDisconnect() = default; |
| |
| void close(int32_t streamIndex) override; |
| |
| oboe::Result startStreams() override { |
| std::shared_ptr<oboe::AudioStream> outputStream = getOutputStream(); |
| if (outputStream) { |
| return outputStream->start(); |
| } |
| |
| std::shared_ptr<oboe::AudioStream> inputStream = getInputStream(); |
| if (inputStream) { |
| return inputStream->start(); |
| } |
| return oboe::Result::ErrorNull; |
| } |
| |
| void configureForStart() override; |
| |
| private: |
| std::unique_ptr<SineOscillator> sineOscillator; |
| std::unique_ptr<MonoToMultiConverter> monoToMulti; |
| std::shared_ptr<oboe::flowgraph::SinkFloat> mSinkFloat; |
| }; |
| |
| /** |
| * Switch between various |
| */ |
| class NativeAudioContext { |
| public: |
| |
| ActivityContext *getCurrentActivity() { |
| return currentActivity; |
| }; |
| |
| void setActivityType(int activityType) { |
| mActivityType = (ActivityType) activityType; |
| switch(mActivityType) { |
| default: |
| case ActivityType::Undefined: |
| case ActivityType::TestOutput: |
| currentActivity = &mActivityTestOutput; |
| break; |
| case ActivityType::TestInput: |
| currentActivity = &mActivityTestInput; |
| break; |
| case ActivityType::TapToTone: |
| currentActivity = &mActivityTapToTone; |
| break; |
| case ActivityType::RecordPlay: |
| currentActivity = &mActivityRecording; |
| break; |
| case ActivityType::Echo: |
| currentActivity = &mActivityEcho; |
| break; |
| case ActivityType::RoundTripLatency: |
| currentActivity = &mActivityRoundTripLatency; |
| break; |
| case ActivityType::Glitches: |
| currentActivity = &mActivityGlitches; |
| break; |
| case ActivityType::TestDisconnect: |
| currentActivity = &mActivityTestDisconnect; |
| break; |
| case ActivityType::DataPath: |
| currentActivity = &mActivityDataPath; |
| break; |
| } |
| } |
| |
| void setDelayTime(double delayTimeMillis) { |
| mActivityEcho.setDelayTime(delayTimeMillis); |
| } |
| |
| ActivityTestOutput mActivityTestOutput; |
| ActivityTestInput mActivityTestInput; |
| ActivityTapToTone mActivityTapToTone; |
| ActivityRecording mActivityRecording; |
| ActivityEcho mActivityEcho; |
| ActivityRoundTripLatency mActivityRoundTripLatency; |
| ActivityGlitches mActivityGlitches; |
| ActivityDataPath mActivityDataPath; |
| ActivityTestDisconnect mActivityTestDisconnect; |
| |
| |
| private: |
| |
| // WARNING - must match definitions in TestAudioActivity.java |
| enum ActivityType { |
| Undefined = -1, |
| TestOutput = 0, |
| TestInput = 1, |
| TapToTone = 2, |
| RecordPlay = 3, |
| Echo = 4, |
| RoundTripLatency = 5, |
| Glitches = 6, |
| TestDisconnect = 7, |
| DataPath = 8, |
| }; |
| |
| ActivityType mActivityType = ActivityType::Undefined; |
| ActivityContext *currentActivity = &mActivityTestOutput; |
| |
| }; |
| |
| #endif //NATIVEOBOE_NATIVEAUDIOCONTEXT_H |