-
Notifications
You must be signed in to change notification settings - Fork 573
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
293 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright 2018 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 OBOE_STABILIZEDCALLBACK_H | ||
#define OBOE_STABILIZEDCALLBACK_H | ||
|
||
#include <cstdint> | ||
#include "oboe/AudioStream.h" | ||
|
||
namespace oboe { | ||
|
||
class StabilizedCallback : public AudioStreamCallback { | ||
|
||
public: | ||
StabilizedCallback(AudioStreamCallback *callback); | ||
|
||
DataCallbackResult | ||
onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override; | ||
|
||
void onErrorBeforeClose(AudioStream *oboeStream, Result error) override { | ||
return mCallback->onErrorBeforeClose(oboeStream, error); | ||
} | ||
|
||
void onErrorAfterClose(AudioStream *oboeStream, Result error) override { | ||
return mCallback->onErrorAfterClose(oboeStream, error); | ||
} | ||
|
||
private: | ||
|
||
AudioStreamCallback *mCallback = nullptr; | ||
int64_t mFrameCount = 0; | ||
int64_t mEpochTimeNanos = 0; | ||
double mOpsPerNano = 1; | ||
|
||
void generateLoad(int64_t durationNanos); | ||
}; | ||
|
||
/** | ||
* cpu_relax is an architecture specific method of telling the CPU that you don't want it to | ||
* do much work. asm volatile keeps the compiler from optimising these instructions out. | ||
*/ | ||
#if defined(__i386__) || defined(__x86_64__) | ||
#define cpu_relax() asm volatile("rep; nop" ::: "memory"); | ||
|
||
#elif defined(__arm__) || defined(__mips__) | ||
#define cpu_relax() asm volatile("":::"memory") | ||
|
||
#elif defined(__aarch64__) | ||
#define cpu_relax() asm volatile("yield" ::: "memory") | ||
|
||
#else | ||
#error "cpu_relax is not defined for this architecture" | ||
#endif | ||
|
||
} | ||
|
||
#endif //OBOE_STABILIZEDCALLBACK_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/* | ||
* Copyright (C) 2018 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. | ||
*/ | ||
|
||
#include "oboe/StabilizedCallback.h" | ||
#include "common/AudioClock.h" | ||
#include "common/Trace.h" | ||
|
||
constexpr int32_t kLoadGenerationStepSizeNanos = 1000; | ||
constexpr float kPercentageOfCallbackToUse = 0.8; | ||
|
||
using namespace oboe; | ||
|
||
StabilizedCallback::StabilizedCallback(AudioStreamCallback *callback) : mCallback(callback){ | ||
Trace::initialize(); | ||
} | ||
|
||
/** | ||
* An audio callback which attempts to do work for a fixed amount of time. | ||
* | ||
* @param oboeStream | ||
* @param audioData | ||
* @param numFrames | ||
* @return | ||
*/ | ||
DataCallbackResult | ||
StabilizedCallback::onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) { | ||
|
||
int64_t startTimeNanos = AudioClock::getNanoseconds(); | ||
|
||
if (mFrameCount == 0){ | ||
mEpochTimeNanos = startTimeNanos; | ||
} | ||
|
||
int64_t durationSinceEpochNanos = startTimeNanos - mEpochTimeNanos; | ||
|
||
// In an ideal world the callback start time will be exactly the same as the duration of the | ||
// frames already read/written into the stream. In reality the callback can start early | ||
// or late. By finding the delta we can calculate the target duration for our stabilized | ||
// callback. | ||
int64_t idealStartTimeNanos = (mFrameCount * kNanosPerSecond) / oboeStream->getSampleRate(); | ||
int64_t lateStartNanos = durationSinceEpochNanos - idealStartTimeNanos; | ||
|
||
if (lateStartNanos < 0){ | ||
// This was an early start which indicates that our previous epoch was a late callback. | ||
// Update our epoch to this more accurate time. | ||
mEpochTimeNanos = startTimeNanos; | ||
mFrameCount = 0; | ||
} | ||
|
||
int64_t numFramesAsNanos = (numFrames * kNanosPerSecond) / oboeStream->getSampleRate(); | ||
int64_t targetDurationNanos = (int64_t) | ||
(numFramesAsNanos * kPercentageOfCallbackToUse) - lateStartNanos; | ||
|
||
Trace::beginSection("Actual load"); | ||
DataCallbackResult result = mCallback->onAudioReady(oboeStream, audioData, numFrames); | ||
Trace::endSection(); | ||
|
||
int64_t executionDurationNanos = AudioClock::getNanoseconds() - startTimeNanos; | ||
int64_t stabilizingLoadDurationNanos = targetDurationNanos - executionDurationNanos; | ||
|
||
Trace::beginSection("Stabilized load for %lldns", stabilizingLoadDurationNanos); | ||
generateLoad(stabilizingLoadDurationNanos); | ||
Trace::endSection(); | ||
|
||
// TODO: Could this be done with oboeStream->getFramesRead or getFramesWritten()? | ||
// Wraparound: At 48000 frames per second mFrameCount wraparound will occur after 6m years, | ||
// significantly longer than the average lifetime of an Android phone. | ||
mFrameCount += numFrames; | ||
return result; | ||
} | ||
|
||
void StabilizedCallback::generateLoad(int64_t durationNanos) { | ||
|
||
int64_t currentTimeNanos = AudioClock::getNanoseconds(); | ||
int64_t deadlineTimeNanos = currentTimeNanos + durationNanos; | ||
|
||
// opsPerStep gives us an estimated number of operations which need to be run to fully utilize | ||
// the CPU for a fixed amount of time (specified by kLoadGenerationStepSizeNanos). | ||
// After each step the opsPerStep value is re-calculated based on the actual time taken to | ||
// execute those operations. | ||
int opsPerStep = (int)(mOpsPerNano * kLoadGenerationStepSizeNanos); | ||
int64_t stepDurationNanos = 0; | ||
int64_t previousTimeNanos = 0; | ||
|
||
while (currentTimeNanos <= deadlineTimeNanos){ | ||
|
||
for (int i = 0; i < opsPerStep; i++) cpu_relax(); | ||
|
||
previousTimeNanos = currentTimeNanos; | ||
currentTimeNanos = AudioClock::getNanoseconds(); | ||
stepDurationNanos = currentTimeNanos - previousTimeNanos; | ||
mOpsPerNano = (int)(opsPerStep / stepDurationNanos); | ||
opsPerStep = (int)(mOpsPerNano * kLoadGenerationStepSizeNanos); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
* Copyright 2018 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. | ||
*/ | ||
|
||
#include <dlfcn.h> | ||
#include <cstdio> | ||
#include "Trace.h" | ||
#include "OboeDebug.h" | ||
|
||
static char buffer[256]; | ||
|
||
// Tracing functions | ||
static void *(*ATrace_beginSection)(const char *sectionName); | ||
|
||
static void *(*ATrace_endSection)(); | ||
|
||
typedef void *(*fp_ATrace_beginSection)(const char *sectionName); | ||
|
||
typedef void *(*fp_ATrace_endSection)(); | ||
|
||
bool Trace::mIsTracingSupported = false; | ||
|
||
void Trace::beginSection(const char *format, ...){ | ||
|
||
if (mIsTracingSupported) { | ||
va_list va; | ||
va_start(va, format); | ||
vsprintf(buffer, format, va); | ||
ATrace_beginSection(buffer); | ||
va_end(va); | ||
} else { | ||
LOGE("Tracing is either not initialized (call Trace::initialize()) " | ||
"or not supported on this device"); | ||
} | ||
} | ||
|
||
void Trace::endSection() { | ||
|
||
if (mIsTracingSupported) { | ||
ATrace_endSection(); | ||
} | ||
} | ||
|
||
void Trace::initialize() { | ||
|
||
// Using dlsym allows us to use tracing on API 21+ without needing android/trace.h which wasn't | ||
// published until API 23 | ||
void *lib = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL); | ||
if (lib == nullptr) { | ||
LOGE("Could not open libandroid.so to dynamically load tracing symbols"); | ||
} else { | ||
ATrace_beginSection = | ||
reinterpret_cast<fp_ATrace_beginSection >( | ||
dlsym(lib, "ATrace_beginSection")); | ||
ATrace_endSection = | ||
reinterpret_cast<fp_ATrace_endSection >( | ||
dlsym(lib, "ATrace_endSection")); | ||
|
||
if (ATrace_beginSection != nullptr && ATrace_endSection != nullptr){ | ||
mIsTracingSupported = true; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
* Copyright 2018 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 OBOE_TRACE_H | ||
#define OBOE_TRACE_H | ||
|
||
class Trace { | ||
|
||
public: | ||
static void beginSection(const char *format, ...); | ||
static void endSection(); | ||
static void initialize(); | ||
|
||
private: | ||
static bool mIsTracingSupported; | ||
}; | ||
|
||
#endif //OBOE_TRACE_H |