blob: ec0d3e9bdc282e4dc0fda4899c96eeeaf0b91886 [file] [log] [blame]
/*
* 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 <dlfcn.h>
#include <jni.h>
#include <thread>
#include <vector>
#include "common/OboeDebug.h"
#include "oboe/Oboe.h"
#include "AudioStreamGateway.h"
#include "flowgraph/ImpulseOscillator.h"
#include "flowgraph/ManyToMultiConverter.h"
#include "flowgraph/MonoToMultiConverter.h"
#include "flowgraph/SinkFloat.h"
#include "flowgraph/SinkI16.h"
#include "flowgraph/SineOscillator.h"
#include "flowgraph/SawtoothOscillator.h"
#include "FullDuplexEcho.h"
#include "FullDuplexStream.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 LIB_AAUDIO_NAME "libaaudio.so"
#define FUNCTION_IS_MMAP "AAudioStream_isMMapUsed"
/**
* Implement the native API for the Oboe Tester.
* Manage a stream.
* Generate signals, etc.
*/
class NativeAudioContext {
public:
NativeAudioContext();
void close(int32_t streamIndex);
bool isMMapUsed(int32_t streamIndex);
oboe::AudioStream *getStream(int32_t streamIndex) {
return mOboeStreams[streamIndex]; // TODO range check
}
int open(jint nativeApi,
jint sampleRate,
jint channelCount,
jint format,
jint sharingMode,
jint performanceMode,
jint deviceId,
jint sessionId,
jint framesPerBurst, jboolean isInput);
void setToneType(int toneType) {
LOGI("%s(%d)", __func__, toneType);
mToneType = (ToneType) toneType;
// connectTone();
}
int32_t getFramesPerBlock() {
return (callbackSize == 0) ? mFramesPerBurst : callbackSize;
}
void printScheduler() {
int scheduler = audioStreamGateway.getScheduler();
LOGI("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n", scheduler, SCHED_FIFO);
}
oboe::Result pause() {
LOGD("NativeAudioContext::%s() called", __func__);
oboe::Result result = oboe::Result::OK;
stopBlockingIOThread();
for (int32_t i = 0; i < kMaxStreams; i++) {
oboe::AudioStream *oboeStream = mOboeStreams[i];
if (oboeStream != nullptr) {
result = oboeStream->requestPause();
printScheduler();
}
}
return result;
}
oboe::Result stopAudio() {
LOGD("NativeAudioContext::%s() called", __func__);
oboe::Result result = oboe::Result::OK;
stopBlockingIOThread();
for (int32_t i = 0; i < kMaxStreams; i++) {
oboe::AudioStream *oboeStream = mOboeStreams[i];
if (oboeStream != nullptr) {
result = oboeStream->requestStop();
printScheduler();
}
}
LOGD("NativeAudioContext::%s() returns %d", __func__, result);
return result;
}
oboe::Result stopPlayback() {
LOGD("NativeAudioContext::%s() called", __func__);
oboe::Result result = oboe::Result::OK;
if (playbackStream != nullptr) {
result = playbackStream->requestStop();
playbackStream->close();
mPlayRecordingCallback.setRecording(nullptr);
delete playbackStream;
playbackStream = nullptr;
}
return result;
}
oboe::Result stop() {
oboe::Result resultStopPlayback = stopPlayback();
oboe::Result resultStopAudio = stopAudio();
LOGD("NativeAudioContext::%s() stop modules", __func__);
for (int i = 0; i < mChannelCount; i++) {
sineOscillators[i].stop();
sawtoothOscillators[i].stop();
}
impulseGenerator.stop();
sawPingGenerator.stop();
if (mSinkFloat) {
mSinkFloat->stop();
}
if (mSinkI16) {
mSinkI16->stop();
}
oboe::Result result = (resultStopPlayback != oboe::Result::OK)
? resultStopPlayback
: resultStopAudio;
LOGD("NativeAudioContext::%s() returns %d", __func__, result);
return result;
}
oboe::Result startPlayback() {
stop();
LOGD("NativeAudioContext::%s() called", __func__);
oboe::AudioStreamBuilder builder;
builder.setChannelCount(mChannelCount)
->setSampleRate(mSampleRate)
->setFormat(oboe::AudioFormat::Float)
->setCallback(&mPlayRecordingCallback)
->setAudioApi(oboe::AudioApi::OpenSLES);
oboe::Result result = builder.openStream(&playbackStream);
LOGD("NativeAudioContext::startPlayback() openStream() returned %d", result);
if (result != oboe::Result::OK) {
delete playbackStream;
playbackStream = nullptr;
} else if (playbackStream != nullptr) {
if (mRecording != nullptr) {
mRecording->rewind();
mPlayRecordingCallback.setRecording(mRecording.get());
result = playbackStream->requestStart();
}
}
return result;
}
static void threadCallback(NativeAudioContext *context) {
LOGD("%s: called", __func__);
context->runBlockingIO();
LOGD("%s: exiting", __func__);
}
oboe::Result start();
void setToneEnabled(bool enabled) {
LOGD("%s(%d)", __func__, enabled ? 1 : 0);
sawPingGenerator.setEnabled(enabled);
}
void setAmplitude(double amplitude) {
LOGD("%s(%f)", __func__, amplitude);
for (int i = 0; i < mChannelCount; i++) {
sineOscillators[i].amplitude.setValue(amplitude);
sawtoothOscillators[i].amplitude.setValue(amplitude);
}
sawPingGenerator.amplitude.setValue(amplitude);
impulseGenerator.amplitude.setValue(amplitude);
}
void setCallbackReturnStop(bool b) {
oboeCallbackProxy.setCallbackReturnStop(b);
}
int64_t getCallbackCount() {
return oboeCallbackProxy.getCallbackCount();
}
void setChannelEnabled(int channelIndex, bool enabled);
void setActivityType(int activityType) {
LOGD("%s(%d)", __func__, activityType);
mActivityType = (ActivityType) activityType;
}
double getPeakLevel(int index) {
return mInputAnalyzer.getPeakLevel(index);
}
bool useCallback = true;
bool callbackReturnStop = false;
int callbackSize = 0;
private:
// WARNING - must match order in strings.xml and OboeAudioOutputStream.java
enum ToneType {
SawPing = 0,
Sine = 1,
Impulse = 2,
Sawtooth = 3
};
// WARNING - must match definitions in TestAudioActivity.java
enum ActivityType {
Undefined = -1,
TestOutput = 0,
TestInput = 1,
TapToTone = 2,
RecordPlay = 3,
Echo = 4
};
oboe::AudioStream * getInputStream();
oboe::AudioStream * getOutputStream();
int32_t allocateStreamIndex();
void configureForActivityType();
void freeStreamIndex(int32_t streamIndex);
void connectTone();
void runBlockingIO();
void stopBlockingIOThread() {
if (!useCallback) {
// stop a thread that runs in place of the callback
threadEnabled.store(false); // ask thread to exit its loop
if (dataThread != nullptr) {
dataThread->join();
dataThread = nullptr;
}
}
}
static constexpr int kMaxStreams = 8;
oboe::AudioStream *mOboeStreams[kMaxStreams]{};
int32_t mFramesPerBurst = 0; // TODO per stream
int32_t mChannelCount = 0; // TODO per stream
int32_t mSampleRate = 0; // TODO per stream
ToneType mToneType = ToneType::Sine;
ActivityType mActivityType = ActivityType::Undefined;
std::atomic<bool> threadEnabled{false};
std::thread *dataThread = nullptr;
OboeStreamCallbackProxy oboeCallbackProxy;
AudioStreamGateway audioStreamGateway;
InputStreamCallbackAnalyzer mInputAnalyzer;
std::vector<SineOscillator> sineOscillators;
std::vector<SawtoothOscillator> sawtoothOscillators;
ImpulseOscillator impulseGenerator;
SawPingGenerator sawPingGenerator;
oboe::AudioStream *playbackStream = nullptr;
std::unique_ptr<float []> dataBuffer{};
std::unique_ptr<ManyToMultiConverter> manyToMulti;
std::unique_ptr<MonoToMultiConverter> monoToMulti;
std::shared_ptr<flowgraph::SinkFloat> mSinkFloat;
std::shared_ptr<flowgraph::SinkI16> mSinkI16;
std::unique_ptr<MultiChannelRecording> mRecording{};
std::unique_ptr<FullDuplexEcho> mFullDuplexEcho{};
PlayRecordingCallback mPlayRecordingCallback;
bool (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr;
void *mLibHandle = nullptr;
};
#endif //NATIVEOBOE_NATIVEAUDIOCONTEXT_H