Add Basic Audio Data Tests To CHQTS
This CL adds a test to request audio data from the default handle,
buffer 4 seconds worth of audio, and perform basic validation
(non zero and unique adjacency) on the audio data.
Bug: 174601727
Test: run gts -m GtsGmscoreHostTestCases on multiple devices.
Change-Id: I672d30e69837ee9999348d0c5fe8d2fb355882ef
diff --git a/apps/test/chqts/src/general_test/basic_audio_test.cc b/apps/test/chqts/src/general_test/basic_audio_test.cc
index 8820ec0..bd76f4d 100644
--- a/apps/test/chqts/src/general_test/basic_audio_test.cc
+++ b/apps/test/chqts/src/general_test/basic_audio_test.cc
@@ -56,6 +56,11 @@
//! not practical for always-on, low-power use-cases.
constexpr uint64_t kMaxBufferDuration = kOneSecondInNanoseconds * 120;
+//! While a variety of sample rates are supported, for the purposes of basic
+//! audio data validation, we buffer about 4 seconds worth of PCM audio data
+//! sampled at 16KHz.
+constexpr uint32_t kRequiredSampleRate = 16000;
+
/**
* @return true if the character is ASCII printable.
*/
@@ -119,7 +124,6 @@
bool validateMinimumAudioSource(const struct chreAudioSource &source) {
// CHQTS requires a 16kHz, PCM-format, 2 second buffer.
- constexpr uint32_t kRequiredSampleRate = 16000;
constexpr uint64_t kRequiredBufferDuration = 2 * kOneSecondInNanoseconds;
// Ensure that the minimum buffer size is less than or equal to the required
@@ -169,6 +173,105 @@
}
}
+/**
+ * Attempts to request audio data from the default microphone handle (0),
+ * posts a failure if the data request failed
+ */
+void requestAudioData() {
+ constexpr uint32_t kAudioHandle = 0;
+ struct chreAudioSource audioSource;
+
+ if (!chreAudioGetSource(kAudioHandle, &audioSource)) {
+ sendFatalFailureToHost("Failed to query source for handle 0");
+ } else if (!chreAudioConfigureSource(kAudioHandle, true /* enable */,
+ audioSource.minBufferDuration,
+ audioSource.minBufferDuration)) {
+ sendFatalFailureToHost("Failed to request audio data for handle 0");
+ }
+}
+
+/**
+ * Check if the audio samples are all zeros
+ *
+ * @return true on check passing
+ */
+bool checkSamplesAllZeros(int16_t *data, size_t dataLen) {
+ for (size_t i = 0; i < dataLen; ++i) {
+ if (data[i] != 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Check if adjacent audio samples are unique
+ *
+ * @return true on check pass
+ */
+bool checkSamplesAllSame(int16_t *data, size_t dataLen) {
+ if (dataLen > 0) {
+ const int16_t controlValue = data[0];
+ for (size_t i = 1; i < dataLen; ++i) {
+ if (data[i] != controlValue) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void handleAudioDataEvent(const chreAudioDataEvent *dataEvent) {
+ constexpr uint32_t kAudioHandle = 0;
+
+ // A count of the number of data events we've received - we stop
+ // the test after receiving 2 data events.
+ static uint8_t numDataEventsSoFar = 1;
+
+ if (dataEvent == nullptr) {
+ sendFatalFailureToHost("Null event data");
+ } else if (dataEvent->samplesS16 == nullptr) {
+ sendFatalFailureToHost("Null audio data frame");
+ } else if (dataEvent->sampleCount == 0) {
+ sendFatalFailureToHost("0 samples in audio data frame");
+ } else {
+ struct chreAudioSource audioSource;
+ if (!chreAudioGetSource(kAudioHandle, &audioSource)) {
+ sendFatalFailureToHost("Failed to get audio source for handle 0");
+ } else {
+ // Per the CHRE Audio API requirements, it is expected that we exactly
+ // the number of samples that we ask for - we verify that here.
+ const size_t kNumSamplesExpected =
+ audioSource.minBufferDuration * kRequiredSampleRate;
+ if (dataEvent->sampleCount != kNumSamplesExpected) {
+ sendFatalFailureToHost(
+ "Failed to receive the expected number of samples in audio data "
+ "event");
+ }
+ }
+ }
+
+ if (numDataEventsSoFar == 2) {
+ if (!chreAudioConfigureSource(kAudioHandle, false /* enable */,
+ 0 /* bufferDuration */,
+ 0 /* deliveryInterval */)) {
+ sendFatalFailureToHost("Failed to disable audio source for handle 0");
+ }
+ } else {
+ ++numDataEventsSoFar;
+ }
+
+ if (!checkSamplesAllZeros(dataEvent->samplesS16, dataEvent->sampleCount)) {
+ sendFatalFailureToHost("All audio samples were zeros");
+ } else if (!checkSamplesAllSame(dataEvent->samplesS16,
+ dataEvent->sampleCount)) {
+ sendFatalFailureToHost("All audio samples were identical");
+ } else {
+ sendSuccessToHost();
+ }
+}
+} // namespace
+
} // anonymous namespace
BasicAudioTest::BasicAudioTest()
@@ -181,7 +284,10 @@
}
validateAudioSources();
- sendSuccessToHost();
+
+ mState = State::kExpectingAudioData;
+
+ requestAudioData();
}
void BasicAudioTest::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
@@ -195,8 +301,21 @@
if (mState == State::kPreStart) {
unexpectedEvent(eventType);
} else {
- // TODO: Handle audio data from sources, perform basic consistency checks on
- // it.
+ switch (eventType) {
+ case CHRE_EVENT_AUDIO_SAMPLING_CHANGE:
+ /* This event is received, but not relevant to this test since we're
+ * mostly interested in the audio data, so we ignore it. */
+ break;
+
+ case CHRE_EVENT_AUDIO_DATA:
+ handleAudioDataEvent(
+ static_cast<const chreAudioDataEvent *>(eventData));
+ break;
+
+ default:
+ unexpectedEvent(eventType);
+ break;
+ }
}
mInMethod = false;