Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 629afae6 authored by Eric Laurent's avatar Eric Laurent
Browse files

aaudio: add tests for device switch detection and refactor

Add detection for device switch during capture and playback tests:
 write_sine, write_sine_callback, input_monitor, input_monitor_callback

Refactor tests and move simple player, simple recorder and utility
methods in separate header files and folder.

Bug: 33355262
Test: run write_sine and input_monitor tests
Change-Id: Iced66fa4344aecd8a2952e22a98e6e8454f38a5e
parent fb00fc77
Loading
Loading
Loading
Loading
+15 −17
Original line number Diff line number Diff line
@@ -4,32 +4,30 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES := \
    $(call include-path-for, audio-utils) \
    frameworks/av/media/liboboe/include
    frameworks/av/media/libaaudio/include \
    frameworks/av/media/libaaudio/src \
    frameworks/av/media/libaaudio/examples/utils

LOCAL_SRC_FILES:= frameworks/av/media/liboboe/src/write_sine.cpp
LOCAL_SHARED_LIBRARIES := libaudioutils libmedia libtinyalsa \
        libbinder libcutils libutils
LOCAL_STATIC_LIBRARIES := libsndfile
LOCAL_MODULE := write_sine_ndk
LOCAL_SHARED_LIBRARIES += liboboe_prebuilt
# NDK recommends using this kind of relative path instead of an absolute path.
LOCAL_SRC_FILES:= ../src/input_monitor.cpp
LOCAL_SHARED_LIBRARIES := libaaudio
LOCAL_MODULE := input_monitor_ndk
include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES := \
    $(call include-path-for, audio-utils) \
    frameworks/av/media/liboboe/include
    frameworks/av/media/libaaudio/include \
    frameworks/av/media/libaaudio/examples/utils

LOCAL_SRC_FILES:= frameworks/av/media/liboboe/src/write_sine_threaded.cpp
LOCAL_SHARED_LIBRARIES := libaudioutils libmedia libtinyalsa \
        libbinder libcutils libutils
LOCAL_STATIC_LIBRARIES := libsndfile
LOCAL_MODULE := write_sine_threaded_ndk
LOCAL_SHARED_LIBRARIES += liboboe_prebuilt
LOCAL_SRC_FILES:= ../src/input_monitor_callback.cpp
LOCAL_SHARED_LIBRARIES := libaaudio
LOCAL_MODULE := input_monitor_callback_ndk
include $(BUILD_EXECUTABLE)

include $(CLEAR_VARS)
LOCAL_MODULE := liboboe_prebuilt
LOCAL_SRC_FILES := liboboe.so
LOCAL_MODULE := libaaudio_prebuilt
LOCAL_SRC_FILES := libaaudio.so
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
include $(PREBUILT_SHARED_LIBRARY)
 No newline at end of file
+22 −48
Original line number Diff line number Diff line
@@ -22,36 +22,21 @@
#include <stdlib.h>
#include <math.h>
#include <aaudio/AAudio.h>
#include "AAudioExampleUtils.h"
#include "AAudioSimpleRecorder.h"

#define SAMPLE_RATE        48000
#define NUM_SECONDS        5
#define NANOS_PER_MICROSECOND ((int64_t)1000)
#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
#define NANOS_PER_SECOND      (NANOS_PER_MILLISECOND * 1000)

#define MIN_FRAMES_TO_READ    48  /* arbitrary, 1 msec at 48000 Hz */
#define NUM_SECONDS        10

static const char *getSharingModeText(aaudio_sharing_mode_t mode) {
    const char *modeText = "unknown";
    switch (mode) {
    case AAUDIO_SHARING_MODE_EXCLUSIVE:
        modeText = "EXCLUSIVE";
        break;
    case AAUDIO_SHARING_MODE_SHARED:
        modeText = "SHARED";
        break;
    default:
        break;
    }
    return modeText;
}
#define MIN_FRAMES_TO_READ 48  /* arbitrary, 1 msec at 48000 Hz */

int main(int argc, char **argv)
{
    (void)argc; // unused

    aaudio_result_t result;

    AAudioSimpleRecorder recorder;
    int actualSamplesPerFrame;
    int actualSampleRate;
    const aaudio_audio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16;
@@ -66,7 +51,6 @@ int main(int argc, char **argv)
    //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
    aaudio_sharing_mode_t actualSharingMode;

    AAudioStreamBuilder *aaudioBuilder = nullptr;
    AAudioStream *aaudioStream = nullptr;
    aaudio_stream_state_t state;
    int32_t framesPerBurst = 0;
@@ -84,24 +68,16 @@ int main(int argc, char **argv)

    printf("%s - Monitor input level using AAudio\n", argv[0]);

    // Use an AAudioStreamBuilder to contain requested parameters.
    result = AAudio_createStreamBuilder(&aaudioBuilder);
    if (result != AAUDIO_OK) {
        goto finish;
    }

    // Request stream properties.
    AAudioStreamBuilder_setDirection(aaudioBuilder, AAUDIO_DIRECTION_INPUT);
    AAudioStreamBuilder_setFormat(aaudioBuilder, requestedDataFormat);
    AAudioStreamBuilder_setSharingMode(aaudioBuilder, requestedSharingMode);
    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, requestedPerformanceMode);
    AAudioStreamBuilder_setChannelCount(aaudioBuilder, requestedInputChannelCount);
    recorder.setPerformanceMode(requestedPerformanceMode);
    recorder.setSharingMode(requestedSharingMode);

    // Create an AAudioStream using the Builder.
    result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
    result = recorder.open(requestedInputChannelCount, 48000, requestedDataFormat,
                           nullptr, nullptr, nullptr);
    if (result != AAUDIO_OK) {
        fprintf(stderr, "ERROR -  recorder.open() returned %d\n", result);
        goto finish;
    }
    aaudioStream = recorder.getStream();

    actualSamplesPerFrame = AAudioStream_getSamplesPerFrame(aaudioStream);
    printf("SamplesPerFrame = %d\n", actualSamplesPerFrame);
@@ -143,10 +119,9 @@ int main(int argc, char **argv)
    }

    // Start the stream.
    printf("call AAudioStream_requestStart()\n");
    result = AAudioStream_requestStart(aaudioStream);
    result = recorder.start();
    if (result != AAUDIO_OK) {
        fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d\n", result);
        fprintf(stderr, "ERROR -  recorder.start() returned %d\n", result);
        goto finish;
    }

@@ -181,12 +156,7 @@ int main(int argc, char **argv)

        // Display level as stars, eg. "******".
        if ((loopCounter++ % 10) == 0) {
            printf("%5.3f ", peakLevel);
            int numStars = (int)(peakLevel * 50);
            for (int i = 0; i < numStars; i++) {
                printf("*");
            }
            printf("\n");
            displayPeakLevel(peakLevel);
            peakLevel = 0.0;
        }
    }
@@ -194,9 +164,13 @@ int main(int argc, char **argv)
    xRunCount = AAudioStream_getXRunCount(aaudioStream);
    printf("AAudioStream_getXRunCount %d\n", xRunCount);

    result = recorder.stop();
    if (result != AAUDIO_OK) {
        goto finish;
    }

finish:
    AAudioStream_close(aaudioStream);
    AAudioStreamBuilder_delete(aaudioBuilder);
    recorder.close();
    delete[] data;
    printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
    return (result != AAUDIO_OK) ? EXIT_FAILURE : EXIT_SUCCESS;
+25 −218
Original line number Diff line number Diff line
@@ -23,229 +23,18 @@
#include <math.h>
#include <time.h>
#include <aaudio/AAudio.h>
#include "AAudioExampleUtils.h"
#include "AAudioSimpleRecorder.h"

#define NUM_SECONDS           5
#define NANOS_PER_MICROSECOND ((int64_t)1000)
#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
#define NANOS_PER_SECOND      (NANOS_PER_MILLISECOND * 1000)

//#define SHARING_MODE  AAUDIO_SHARING_MODE_EXCLUSIVE
#define SHARING_MODE  AAUDIO_SHARING_MODE_SHARED

/**
 * Simple wrapper for AAudio that opens an input stream and then calls
 * a callback function to process the input data.
 */
class SimpleAAudioRecorder {
public:
    SimpleAAudioRecorder() {}
    ~SimpleAAudioRecorder() {
        close();
    };

    /**
     * Call this before calling open().
     * @param requestedSharingMode
     */
    void setSharingMode(aaudio_sharing_mode_t requestedSharingMode) {
        mRequestedSharingMode = requestedSharingMode;
    }

    /**
     * Also known as "sample rate"
     * Only call this after open() has been called.
     */
    int32_t getFramesPerSecond() {
        if (mStream == nullptr) {
            return AAUDIO_ERROR_INVALID_STATE;
        }
        return AAudioStream_getSampleRate(mStream);;
    }

    /**
     * Only call this after open() has been called.
     */
    int32_t getSamplesPerFrame() {
        if (mStream == nullptr) {
            return AAUDIO_ERROR_INVALID_STATE;
        }
        return AAudioStream_getSamplesPerFrame(mStream);;
    }
    /**
     * Only call this after open() has been called.
     */
    int64_t getFramesRead() {
        if (mStream == nullptr) {
            return AAUDIO_ERROR_INVALID_STATE;
        }
        return AAudioStream_getFramesRead(mStream);;
    }

    /**
     * Open a stream
     */
    aaudio_result_t open(AAudioStream_dataCallback proc, void *userContext) {
        aaudio_result_t result = AAUDIO_OK;

        // Use an AAudioStreamBuilder to contain requested parameters.
        result = AAudio_createStreamBuilder(&mBuilder);
        if (result != AAUDIO_OK) return result;

        AAudioStreamBuilder_setDirection(mBuilder, AAUDIO_DIRECTION_INPUT);
        AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
        AAudioStreamBuilder_setDataCallback(mBuilder, proc, userContext);
        AAudioStreamBuilder_setFormat(mBuilder, AAUDIO_FORMAT_PCM_I16);

        // Open an AAudioStream using the Builder.
        result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
        if (result != AAUDIO_OK) {
            fprintf(stderr, "ERROR - AAudioStreamBuilder_openStream() returned %d %s\n",
                    result, AAudio_convertResultToText(result));
            goto finish1;
        }

        printf("AAudioStream_getFramesPerBurst()         = %d\n",
               AAudioStream_getFramesPerBurst(mStream));
        printf("AAudioStream_getBufferSizeInFrames()     = %d\n",
               AAudioStream_getBufferSizeInFrames(mStream));
        printf("AAudioStream_getBufferCapacityInFrames() = %d\n",
               AAudioStream_getBufferCapacityInFrames(mStream));
        return result;

     finish1:
        AAudioStreamBuilder_delete(mBuilder);
        mBuilder = nullptr;
        return result;
    }

    aaudio_result_t close() {
        if (mStream != nullptr) {
            printf("call AAudioStream_close(%p)\n", mStream);  fflush(stdout);
            AAudioStream_close(mStream);
            mStream = nullptr;
            AAudioStreamBuilder_delete(mBuilder);
            mBuilder = nullptr;
        }
        return AAUDIO_OK;
    }

    // Write zero data to fill up the buffer and prevent underruns.
    aaudio_result_t prime() {
        int32_t samplesPerFrame = AAudioStream_getSamplesPerFrame(mStream);
        const int numFrames = 32; // arbitrary
        float zeros[numFrames * samplesPerFrame];
        memset(zeros, 0, sizeof(zeros));
        aaudio_result_t result = numFrames;
        while (result == numFrames) {
            result = AAudioStream_write(mStream, zeros, numFrames, 0);
        }
        return result;
    }

    // Start the stream. AAudio will start calling your callback function.
     aaudio_result_t start() {
        aaudio_result_t result = AAudioStream_requestStart(mStream);
        if (result != AAUDIO_OK) {
            fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n",
                    result, AAudio_convertResultToText(result));
        }
        return result;
    }

    // Stop the stream. AAudio will stop calling your callback function.
    aaudio_result_t stop() {
        aaudio_result_t result = AAudioStream_requestStop(mStream);
        if (result != AAUDIO_OK) {
            fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
                    result, AAudio_convertResultToText(result));
        }
        return result;
    }

    // Pause the stream. AAudio will stop calling your callback function.
    aaudio_result_t pause() {
        aaudio_result_t result = AAudioStream_requestPause(mStream);
        if (result != AAUDIO_OK) {
            fprintf(stderr, "ERROR - AAudioStream_requestPause() returned %d %s\n",
                    result, AAudio_convertResultToText(result));
        }
        return result;
    }

private:
    AAudioStreamBuilder    *mBuilder = nullptr;
    AAudioStream           *mStream = nullptr;
    aaudio_sharing_mode_t   mRequestedSharingMode = SHARING_MODE;
};

// Application data that gets passed to the callback.
typedef struct PeakTrackerData {
    float peakLevel;
} PeakTrackerData_t;

#define DECAY_FACTOR   0.999

// Callback function that fills the audio output buffer.
aaudio_data_callback_result_t MyDataCallbackProc(
        AAudioStream *stream,
        void *userData,
        void *audioData,
        int32_t numFrames
        ) {

    PeakTrackerData_t *data = (PeakTrackerData_t *) userData;
    // printf("MyCallbackProc(): frameCount = %d\n", numFrames);
    int32_t samplesPerFrame = AAudioStream_getSamplesPerFrame(stream);
    float sample;
    // This code assume mono or stereo.
    switch (AAudioStream_getFormat(stream)) {
        case AAUDIO_FORMAT_PCM_I16: {
            int16_t *audioBuffer = (int16_t *) audioData;
            // Peak follower
            for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
                sample = audioBuffer[frameIndex * samplesPerFrame] * (1.0/32768);
                data->peakLevel *= DECAY_FACTOR;
                if (sample > data->peakLevel) {
                    data->peakLevel = sample;
                }
            }
        }
        break;
        case AAUDIO_FORMAT_PCM_FLOAT: {
            float *audioBuffer = (float *) audioData;
            // Peak follower
            for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
                sample = audioBuffer[frameIndex * samplesPerFrame];
                data->peakLevel *= DECAY_FACTOR;
                if (sample > data->peakLevel) {
                    data->peakLevel = sample;
                }
            }
        }
        break;
        default:
            return AAUDIO_CALLBACK_RESULT_STOP;
    }

    return AAUDIO_CALLBACK_RESULT_CONTINUE;
}

void displayPeakLevel(float peakLevel) {
    printf("%5.3f ", peakLevel);
    const int maxStars = 50; // arbitrary, fits on one line
    int numStars = (int) (peakLevel * maxStars);
    for (int i = 0; i < numStars; i++) {
        printf("*");
    }
    printf("\n");
}

int main(int argc, char **argv)
{
    (void)argc; // unused
    SimpleAAudioRecorder recorder;
    AAudioSimpleRecorder recorder;
    PeakTrackerData_t myData = {0.0};
    aaudio_result_t result;
    aaudio_stream_state_t state;
    const int displayRateHz = 20; // arbitrary
    const int loopsNeeded = NUM_SECONDS * displayRateHz;

@@ -254,9 +43,8 @@ int main(int argc, char **argv)
    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
    printf("%s - Display audio input using an AAudio callback\n", argv[0]);

    recorder.setSharingMode(SHARING_MODE);

    result = recorder.open(MyDataCallbackProc, &myData);
    result = recorder.open(2, 48000, AAUDIO_FORMAT_PCM_I16,
                       SimpleRecorderDataCallbackProc, SimpleRecorderErrorCallbackProc, &myData);
    if (result != AAUDIO_OK) {
        fprintf(stderr, "ERROR -  recorder.open() returned %d\n", result);
        goto error;
@@ -278,6 +66,19 @@ int main(int argc, char **argv)
        (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
        printf("%08d: ", (int)recorder.getFramesRead());
        displayPeakLevel(myData.peakLevel);

        result = AAudioStream_waitForStateChange(recorder.getStream(),
                                                 AAUDIO_STREAM_STATE_CLOSED,
                                                 &state,
                                                 0);
        if (result != AAUDIO_OK) {
            fprintf(stderr, "ERROR - AAudioStream_waitForStateChange() returned %d\n", result);
            goto error;
        }
        if (state != AAUDIO_STREAM_STATE_STARTING && state != AAUDIO_STREAM_STATE_STARTED) {
            printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
            break;
        }
    }
    printf("Woke up. Stop for a moment.\n");

@@ -300,6 +101,12 @@ int main(int argc, char **argv)
        (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
        printf("%08d: ", (int)recorder.getFramesRead());
        displayPeakLevel(myData.peakLevel);

        state = AAudioStream_getState(recorder.getStream());
        if (state != AAUDIO_STREAM_STATE_STARTING && state != AAUDIO_STREAM_STATE_STARTED) {
            printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
            break;
        }
    }
    printf("Woke up now.\n");

+4 −2
Original line number Diff line number Diff line
@@ -4,7 +4,8 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := examples
LOCAL_C_INCLUDES := \
    $(call include-path-for, audio-utils) \
    frameworks/av/media/libaaudio/include
    frameworks/av/media/libaaudio/include \
    frameworks/av/media/libaaudio/examples/utils

# TODO reorganize folders to avoid using ../
LOCAL_SRC_FILES:= ../src/input_monitor.cpp
@@ -22,7 +23,8 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES := \
    $(call include-path-for, audio-utils) \
    frameworks/av/media/libaaudio/include
    frameworks/av/media/libaaudio/include \
    frameworks/av/media/libaaudio/examples/utils

LOCAL_SRC_FILES:= ../src/input_monitor_callback.cpp

+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 AAUDIO_EXAMPLE_UTILS_H
#define AAUDIO_EXAMPLE_UTILS_H

#include <unistd.h>
#include <sched.h>
#include <aaudio/AAudio.h>

#define NANOS_PER_MICROSECOND ((int64_t)1000)
#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
#define NANOS_PER_SECOND      (NANOS_PER_MILLISECOND * 1000)

static const char *getSharingModeText(aaudio_sharing_mode_t mode) {
    const char *modeText = "unknown";
    switch (mode) {
    case AAUDIO_SHARING_MODE_EXCLUSIVE:
        modeText = "EXCLUSIVE";
        break;
    case AAUDIO_SHARING_MODE_SHARED:
        modeText = "SHARED";
        break;
    default:
        break;
    }
    return modeText;
}

static int64_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
    struct timespec time;
    int result = clock_gettime(clockId, &time);
    if (result < 0) {
        return -errno;
    }
    return (time.tv_sec * NANOS_PER_SECOND) + time.tv_nsec;
}

void displayPeakLevel(float peakLevel) {
    printf("%5.3f ", peakLevel);
    const int maxStars = 50; // arbitrary, fits on one line
    int numStars = (int) (peakLevel * maxStars);
    for (int i = 0; i < numStars; i++) {
        printf("*");
    }
    printf("\n");
}

#endif // AAUDIO_EXAMPLE_UTILS_H
Loading