240 lines
7.7 KiB
Python
240 lines
7.7 KiB
Python
#!/usr/bin/python2.7
|
|
"""
|
|
Copyright (C) 2019 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.
|
|
"""
|
|
|
|
# Run OboeTester with progressive timing changes
|
|
# to measure the DSP timing profile.
|
|
# Print a CSV table of offsets and glitch counts.
|
|
#
|
|
# Run Automated Test using an Intent
|
|
# https://github.com/google/oboe/blob/master/apps/OboeTester/docs/AutomatedTesting.md
|
|
|
|
import array
|
|
import collections
|
|
import os
|
|
import os.path
|
|
import sys
|
|
import subprocess
|
|
import time
|
|
|
|
from datetime import datetime
|
|
|
|
kPropertyOutputOffset = "aaudio.out_mmap_offset_usec"
|
|
kMinPeakAmplitude = 0.04
|
|
kOffsetMin = 0000
|
|
kOffsetMax = 4000
|
|
kOffsetIncrement = 100
|
|
kOutputFileBase = "/sdcard/dsp_timing_"
|
|
gOutputFile = kOutputFileBase + "now.txt"
|
|
|
|
def launchLatencyTest():
|
|
command = ["adb", "shell", "am", \
|
|
"start", "-n", "com.google.sample.oboe.manualtest/.MainActivity", \
|
|
"--es", "test", "latency", \
|
|
"--es", "file", gOutputFile, \
|
|
"--ei", "buffer_bursts", "1"]
|
|
return subprocess.check_output(command)
|
|
|
|
def launchGlitchTest():
|
|
command = ["adb", "shell", "am", \
|
|
"start", "-n", "com.google.sample.oboe.manualtest/.MainActivity", \
|
|
"--es", "test", "glitch", \
|
|
"--es", "file", gOutputFile, \
|
|
"--es", "in_perf", "lowlat", \
|
|
"--es", "out_perf", "lowlat", \
|
|
"--es", "in_sharing", "exclusive", \
|
|
"--es", "out_sharing", "exclusive", \
|
|
"--ei", "buffer_bursts", "1"]
|
|
return subprocess.check_output(command)
|
|
|
|
def setAndroidProperty(property, value):
|
|
return subprocess.check_output(["adb", "shell", "setprop", property, value])
|
|
|
|
def getAndroidProperty(property):
|
|
return subprocess.check_output(["adb", "shell", "getprop", property])
|
|
|
|
def setOutputOffset(offset):
|
|
setAndroidProperty(kPropertyOutputOffset, str(offset))
|
|
|
|
def getOutputOffset():
|
|
offsetText = getAndroidProperty(kPropertyOutputOffset).strip()
|
|
if len(offsetText) == 0:
|
|
return 0;
|
|
return int(offsetText)
|
|
|
|
def loadNameValuePairsFromFile(filename):
|
|
myvars = {}
|
|
with open(filename) as myfile:
|
|
for line in myfile:
|
|
name, var = line.partition("=")[::2]
|
|
myvars[name.strip()] = var
|
|
return myvars
|
|
|
|
def loadNameValuePairsFromString(text):
|
|
myvars = {}
|
|
listOutput = text.splitlines()
|
|
for line in listOutput:
|
|
name, var = line.partition("=")[::2]
|
|
myvars[name.strip()] = var
|
|
return myvars
|
|
|
|
def waitForTestResult():
|
|
testOutput = ""
|
|
for i in range(10):
|
|
if subprocess.call(["adb", "shell", "ls", gOutputFile, "2>/dev/null"]) == 0:
|
|
testOutput = subprocess.check_output(["adb", "shell", "cat", gOutputFile])
|
|
break
|
|
else:
|
|
print str(i) + ": waiting until test finishes..."
|
|
time.sleep(2)
|
|
# print testOutput
|
|
subprocess.call(["adb", "shell", "rm", gOutputFile])
|
|
return loadNameValuePairsFromString(testOutput)
|
|
|
|
# volume too low?
|
|
# return true if bad
|
|
def checkPeakAmplitude(pairs):
|
|
if not pairs.has_key('peak.amplitude'):
|
|
print "ERROR no peak.amplitude"
|
|
return True
|
|
peakAmplitude = float(pairs['peak.amplitude'])
|
|
if peakAmplitude < kMinPeakAmplitude:
|
|
print "ERROR peakAmplitude = " + str(peakAmplitude) \
|
|
+ " < " + str(kMinPeakAmplitude) \
|
|
+ ", turn up volume"
|
|
return True
|
|
return False
|
|
|
|
def startOneTest(offset):
|
|
print "=========================="
|
|
setOutputOffset(offset)
|
|
print "try offset = " + getAndroidProperty(kPropertyOutputOffset)
|
|
subprocess.call(["adb", "shell", "rm", gOutputFile, "2>/dev/null"])
|
|
|
|
def runOneGlitchTest(offset):
|
|
startOneTest(offset)
|
|
print launchGlitchTest()
|
|
pairs = waitForTestResult()
|
|
if checkPeakAmplitude(pairs):
|
|
return -1
|
|
if not pairs.has_key('glitch.count'):
|
|
print "ERROR no glitch.count"
|
|
return -1
|
|
return int(pairs['glitch.count'])
|
|
|
|
def runOneLatencyTest(offset):
|
|
startOneTest(offset)
|
|
print launchLatencyTest()
|
|
pairs = waitForTestResult()
|
|
if not pairs.has_key('latency.msec'):
|
|
print "ERROR no latency.msec"
|
|
return -1
|
|
return float(pairs['latency.msec'])
|
|
|
|
def runGlitchSeries():
|
|
offsets = array.array('i')
|
|
glitches = array.array('i')
|
|
for offset in range(kOffsetMin, kOffsetMax, kOffsetIncrement):
|
|
offsets.append(offset)
|
|
result = runOneGlitchTest(offset)
|
|
glitches.append(result)
|
|
if result < 0:
|
|
break
|
|
print "offset = " + str(offset) + ", glitches = " + str(result)
|
|
print "offsetUs, glitches"
|
|
for i in range(len(offsets)):
|
|
print " " + str(offsets[i]) + ", " + str(glitches[i])
|
|
|
|
# return true if bad
|
|
def checkLatencyValid():
|
|
startOneTest(0)
|
|
print launchLatencyTest()
|
|
pairs = waitForTestResult()
|
|
print "burst = " + pairs['out.burst.frames']
|
|
capacity = int(pairs['out.buffer.capacity.frames'])
|
|
if capacity < 0:
|
|
print "ERROR capacity = " + str(capacity)
|
|
return True
|
|
sampleRate = int(pairs['out.rate'])
|
|
capacityMillis = capacity * 1000.0 / sampleRate
|
|
print "capacityMillis = " + str(capacityMillis)
|
|
if pairs['in.mmap'].strip() != "yes":
|
|
print "ERROR Not using input MMAP"
|
|
return True
|
|
if pairs['out.mmap'].strip() != "yes":
|
|
print "ERROR Not using output MMAP"
|
|
return True
|
|
# Check whether we can change latency a moving the DSP pointer
|
|
# past the CPU pointer and wrapping the buffer.
|
|
latencyMin = runOneLatencyTest(kOffsetMin)
|
|
latencyMax = runOneLatencyTest(kOffsetMax + 1000)
|
|
print "latency = " + str(latencyMin) + " => " + str(latencyMax)
|
|
if latencyMax < (latencyMin + (capacityMillis / 2)):
|
|
print "ERROR Latency not affected by changing offset!"
|
|
return True
|
|
return False
|
|
|
|
def isRunningAsRoot():
|
|
userName = subprocess.check_output(["adb", "shell", "whoami"]).strip()
|
|
if userName != "root":
|
|
print "WARNING: changing to 'adb root'"
|
|
subprocess.call(["adb", "root"])
|
|
userName = subprocess.check_output(["adb", "shell", "whoami"]).strip()
|
|
if userName != "root":
|
|
print "ERROR: cannot set 'adb root'"
|
|
return False
|
|
return True
|
|
|
|
def isMMapSupported():
|
|
mmapPolicy = int(getAndroidProperty("aaudio.mmap_policy").strip())
|
|
if mmapPolicy < 2:
|
|
print "ERROR: AAudio MMAP not enabled, aaudio.mmap_policy = " + str(mmapPolicy)
|
|
return False
|
|
if checkLatencyValid():
|
|
return False;
|
|
return True
|
|
|
|
def isTimingSeriesSupported():
|
|
if not isRunningAsRoot():
|
|
return False
|
|
if not isMMapSupported():
|
|
return False
|
|
return True
|
|
|
|
def main():
|
|
global gOutputFile
|
|
print "gOutputFile = " + gOutputFile
|
|
now = datetime.now() # current date and time
|
|
gOutputFile = kOutputFileBase \
|
|
+ now.strftime("%Y%m%d_%H%M%S") \
|
|
+ ".txt"
|
|
print "gOutputFile = " + gOutputFile
|
|
|
|
initialOffset = getOutputOffset()
|
|
print "initial offset = " + str(initialOffset)
|
|
|
|
print "Android version = " + \
|
|
getAndroidProperty("ro.build.id").strip()
|
|
print " release " + \
|
|
getAndroidProperty("ro.build.version.release").strip()
|
|
if (isTimingSeriesSupported()):
|
|
runGlitchSeries()
|
|
setOutputOffset(initialOffset)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|