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

Commit 4a603cd0 authored by Andy Hung's avatar Andy Hung Committed by Automerger Merge Worker
Browse files

Merge "libeffects: Reverb Testbench" am: 246b31dc am: 49b8da0e am:...

Merge "libeffects: Reverb Testbench" am: 246b31dc am: 49b8da0e am: 693f515a am: 7dd78282 am: acba2b91

Original change: https://android-review.googlesource.com/c/platform/frameworks/av/+/1402347

Change-Id: Idb161a4beae6ae94267df234d15291a62826cd91
parents aa8b2aae acba2b91
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -43,6 +43,36 @@ cc_test {
    ],
}

cc_test {
    name: "reverb_test",
    host_supported: false,
    proprietary: true,

    include_dirs: [
        "frameworks/av/media/libeffects/lvm/wrapper/Reverb"
    ],

    header_libs: [
        "libaudioeffects",
    ],

    shared_libs: [
        "libaudioutils",
        "liblog",
        "libreverbwrapper",
    ],

    srcs: [
        "reverb_test.cpp",
    ],

    cflags: [
        "-Wall",
        "-Werror",
        "-Wextra",
    ],
}

cc_test {
    name: "snr",
    host_supported: false,
+87 −0
Original line number Diff line number Diff line
#!/bin/bash
#
# reverb test
#

if [ -z "$ANDROID_BUILD_TOP" ]; then
    echo "Android build environment not set"
    exit -1
fi

# ensure we have mm
. $ANDROID_BUILD_TOP/build/envsetup.sh

mm -j

echo "waiting for device"

adb root && adb wait-for-device remount

# location of test files
testdir="/data/local/tmp/revTest"

echo "========================================"
echo "testing reverb"
adb shell mkdir -p $testdir
adb push $ANDROID_BUILD_TOP/cts/tests/tests/media/res/raw/sinesweepraw.raw $testdir

E_VAL=1
cmds="adb push $OUT/testcases/reverb_test/arm/reverb_test $testdir"

fs_arr=(
    8000
    16000
    22050
    32000
    44100
    48000
    88200
    96000
    176400
    192000
)

# run reverb at different configs, saving only the stereo channel
# pair.
error_count=0
for cmd in "${cmds[@]}"
do
    $cmd
    for preset_val in {0..6}
    do
        for fs in ${fs_arr[*]}
        do
            for chMask in {1..22}
            do
                adb shell LD_LIBRARY_PATH=/system/vendor/lib/soundfx $testdir/reverb_test \
                    --input $testdir/sinesweepraw.raw \
                    --output $testdir/sinesweep_$((chMask))_$((fs)).raw \
                    --chMask $chMask --fs $fs --preset $preset_val

                shell_ret=$?
                if [ $shell_ret -ne 0 ]; then
                    echo "error: $shell_ret"
                    ((++error_count))
                fi

                # two channel files should be identical to higher channel
                # computation (first 2 channels).
                if [[ "$chMask" -gt 1 ]]
                then
                    adb shell cmp $testdir/sinesweep_1_$((fs)).raw \
                        $testdir/sinesweep_$((chMask))_$((fs)).raw
                fi
                # cmp returns EXIT_FAILURE on mismatch.
                shell_ret=$?
                if [ $shell_ret -ne 0 ]; then
                    echo "error: $shell_ret"
                    ((++error_count))
                fi
            done
        done
    done
done

adb shell rm -r $testdir
echo "$error_count errors"
exit $error_count
+395 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 <assert.h>
#include <getopt.h>
#include <inttypes.h>
#include <iterator>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <vector>

#include <audio_utils/channels.h>
#include <audio_utils/primitives.h>
#include <log/log.h>
#include <system/audio.h>

#include "EffectReverb.h"

// This is the only symbol that needs to be exported
extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;

// Global Variables
enum ReverbParams {
  ARG_HELP = 1,
  ARG_INPUT,
  ARG_OUTPUT,
  ARG_FS,
  ARG_CH_MASK,
  ARG_PRESET,
  ARG_AUX,
  ARG_MONO_MODE,
  ARG_FILE_CH,
};

const effect_uuid_t kReverbUuids[] = {
    {0x172cdf00,
     0xa3bc,
     0x11df,
     0xa72f,
     {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},  // preset-insert mode
    {0xf29a1400,
     0xa3bb,
     0x11df,
     0x8ddc,
     {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},  // preset-aux mode
};

// structures
struct reverbConfigParams_t {
  int fChannels = 2;
  int monoMode = false;
  int frameLength = 256;
  int preset = 0;
  int nrChannels = 2;
  int sampleRate = 48000;
  int auxiliary = 0;
  audio_channel_mask_t chMask = AUDIO_CHANNEL_OUT_STEREO;
};

constexpr audio_channel_mask_t kReverbConfigChMask[] = {
    AUDIO_CHANNEL_OUT_MONO,
    AUDIO_CHANNEL_OUT_STEREO,
    AUDIO_CHANNEL_OUT_2POINT1,
    AUDIO_CHANNEL_OUT_2POINT0POINT2,
    AUDIO_CHANNEL_OUT_QUAD,
    AUDIO_CHANNEL_OUT_QUAD_BACK,
    AUDIO_CHANNEL_OUT_QUAD_SIDE,
    AUDIO_CHANNEL_OUT_SURROUND,
    (1 << 4) - 1,
    AUDIO_CHANNEL_OUT_2POINT1POINT2,
    AUDIO_CHANNEL_OUT_3POINT0POINT2,
    AUDIO_CHANNEL_OUT_PENTA,
    (1 << 5) - 1,
    AUDIO_CHANNEL_OUT_3POINT1POINT2,
    AUDIO_CHANNEL_OUT_5POINT1,
    AUDIO_CHANNEL_OUT_5POINT1_BACK,
    AUDIO_CHANNEL_OUT_5POINT1_SIDE,
    (1 << 6) - 1,
    AUDIO_CHANNEL_OUT_6POINT1,
    (1 << 7) - 1,
    AUDIO_CHANNEL_OUT_5POINT1POINT2,
    AUDIO_CHANNEL_OUT_7POINT1,
    (1 << 8) - 1,
};

constexpr int kReverbConfigChMaskCount = std::size(kReverbConfigChMask);

int reverbCreateEffect(effect_handle_t *pEffectHandle, effect_config_t *pConfig, int sessionId,
                       int ioId, int auxFlag) {
  if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(&kReverbUuids[auxFlag], sessionId,
                                                               ioId, pEffectHandle);
      status != 0) {
    ALOGE("Reverb create returned an error = %d\n", status);
    return EXIT_FAILURE;
  }
  int reply = 0;
  uint32_t replySize = sizeof(reply);
  (**pEffectHandle)
      ->command(*pEffectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t), pConfig,
                &replySize, &reply);
  return reply;
}

int reverbSetConfigParam(uint32_t paramType, uint32_t paramValue, effect_handle_t effectHandle) {
  int reply = 0;
  uint32_t replySize = sizeof(reply);
  uint32_t paramData[2] = {paramType, paramValue};
  effect_param_t *effectParam =
      (effect_param_t *)malloc(sizeof(*effectParam) + sizeof(paramData));
  memcpy(&effectParam->data[0], &paramData[0], sizeof(paramData));
  effectParam->psize = sizeof(paramData[0]);
  effectParam->vsize = sizeof(paramData[1]);
  int status =
      (*effectHandle)
          ->command(effectHandle, EFFECT_CMD_SET_PARAM,
                    sizeof(effect_param_t) + sizeof(paramData), effectParam, &replySize, &reply);
  free(effectParam);
  if (status != 0) {
    ALOGE("Reverb set config returned an error = %d\n", status);
    return status;
  }
  return reply;
}

void printUsage() {
  printf("\nUsage: ");
  printf("\n     <executable> [options]\n");
  printf("\nwhere options are, ");
  printf("\n     --input <inputfile>");
  printf("\n           path to the input file");
  printf("\n     --output <outputfile>");
  printf("\n           path to the output file");
  printf("\n     --help");
  printf("\n           prints this usage information");
  printf("\n     --chMask <channel_mask>\n");
  printf("\n           0  - AUDIO_CHANNEL_OUT_MONO");
  printf("\n           1  - AUDIO_CHANNEL_OUT_STEREO");
  printf("\n           2  - AUDIO_CHANNEL_OUT_2POINT1");
  printf("\n           3  - AUDIO_CHANNEL_OUT_2POINT0POINT2");
  printf("\n           4  - AUDIO_CHANNEL_OUT_QUAD");
  printf("\n           5  - AUDIO_CHANNEL_OUT_QUAD_BACK");
  printf("\n           6  - AUDIO_CHANNEL_OUT_QUAD_SIDE");
  printf("\n           7  - AUDIO_CHANNEL_OUT_SURROUND");
  printf("\n           8  - canonical channel index mask for 4 ch: (1 << 4) - 1");
  printf("\n           9  - AUDIO_CHANNEL_OUT_2POINT1POINT2");
  printf("\n           10 - AUDIO_CHANNEL_OUT_3POINT0POINT2");
  printf("\n           11 - AUDIO_CHANNEL_OUT_PENTA");
  printf("\n           12 - canonical channel index mask for 5 ch: (1 << 5) - 1");
  printf("\n           13 - AUDIO_CHANNEL_OUT_3POINT1POINT2");
  printf("\n           14 - AUDIO_CHANNEL_OUT_5POINT1");
  printf("\n           15 - AUDIO_CHANNEL_OUT_5POINT1_BACK");
  printf("\n           16 - AUDIO_CHANNEL_OUT_5POINT1_SIDE");
  printf("\n           17 - canonical channel index mask for 6 ch: (1 << 6) - 1");
  printf("\n           18 - AUDIO_CHANNEL_OUT_6POINT1");
  printf("\n           19 - canonical channel index mask for 7 ch: (1 << 7) - 1");
  printf("\n           20 - AUDIO_CHANNEL_OUT_5POINT1POINT2");
  printf("\n           21 - AUDIO_CHANNEL_OUT_7POINT1");
  printf("\n           22 - canonical channel index mask for 8 ch: (1 << 8) - 1");
  printf("\n           default 0");
  printf("\n     --fs <sampling_freq>");
  printf("\n           Sampling frequency in Hz, default 48000.");
  printf("\n     --preset <preset_value>");
  printf("\n           0 - None");
  printf("\n           1 - Small Room");
  printf("\n           2 - Medium Room");
  printf("\n           3 - Large Room");
  printf("\n           4 - Medium Hall");
  printf("\n           5 - Large Hall");
  printf("\n           6 - Plate");
  printf("\n           default 0");
  printf("\n     --fch <file_channels>");
  printf("\n           number of channels in input file (1 through 8), default 1");
  printf("\n     --M");
  printf("\n           Mono mode (force all input audio channels to be identical)");
  printf("\n     --aux <auxiliary_flag> ");
  printf("\n           0 - Insert Mode on");
  printf("\n           1 - auxiliary Mode on");
  printf("\n           default 0");
  printf("\n");
}

int main(int argc, const char *argv[]) {
  if (argc == 1) {
    printUsage();
    return EXIT_FAILURE;
  }

  reverbConfigParams_t revConfigParams{};  // default initialize
  const char *inputFile = nullptr;
  const char *outputFile = nullptr;

  const option long_opts[] = {
      {"help", no_argument, nullptr, ARG_HELP},
      {"input", required_argument, nullptr, ARG_INPUT},
      {"output", required_argument, nullptr, ARG_OUTPUT},
      {"fs", required_argument, nullptr, ARG_FS},
      {"chMask", required_argument, nullptr, ARG_CH_MASK},
      {"preset", required_argument, nullptr, ARG_PRESET},
      {"aux", required_argument, nullptr, ARG_AUX},
      {"M", no_argument, &revConfigParams.monoMode, true},
      {"fch", required_argument, nullptr, ARG_FILE_CH},
      {nullptr, 0, nullptr, 0},
  };

  while (true) {
    const int opt = getopt_long(argc, (char *const *)argv, "i:o:", long_opts, nullptr);
    if (opt == -1) {
      break;
    }
    switch (opt) {
      case ARG_HELP:
        printUsage();
        return EXIT_SUCCESS;
      case ARG_INPUT: {
        inputFile = (char *)optarg;
        break;
      }
      case ARG_OUTPUT: {
        outputFile = (char *)optarg;
        break;
      }
      case ARG_FS: {
        revConfigParams.sampleRate = atoi(optarg);
        break;
      }
      case ARG_CH_MASK: {
        int chMaskIdx = atoi(optarg);
        if (chMaskIdx < 0 or chMaskIdx > kReverbConfigChMaskCount) {
          ALOGE("Channel Mask index not in correct range\n");
          printUsage();
          return EXIT_FAILURE;
        }
        revConfigParams.chMask = kReverbConfigChMask[chMaskIdx];
        break;
      }
      case ARG_PRESET: {
        revConfigParams.preset = atoi(optarg);
        break;
      }
      case ARG_AUX: {
        revConfigParams.auxiliary = atoi(optarg);
        break;
      }
      case ARG_MONO_MODE: {
        break;
      }
      case ARG_FILE_CH: {
        revConfigParams.fChannels = atoi(optarg);
        break;
      }
      default:
        break;
    }
  }

  if (inputFile == nullptr) {
    ALOGE("Error: missing input files\n");
    printUsage();
    return EXIT_FAILURE;
  }
  std::unique_ptr<FILE, decltype(&fclose)> inputFp(fopen(inputFile, "rb"), &fclose);

  if (inputFp == nullptr) {
    ALOGE("Cannot open input file %s\n", inputFile);
    return EXIT_FAILURE;
  }

  if (outputFile == nullptr) {
    ALOGE("Error: missing output files\n");
    printUsage();
    return EXIT_FAILURE;
  }
  std::unique_ptr<FILE, decltype(&fclose)> outputFp(fopen(outputFile, "wb"), &fclose);

  if (outputFp == nullptr) {
    ALOGE("Cannot open output file %s\n", outputFile);
    return EXIT_FAILURE;
  }

  int32_t sessionId = 1;
  int32_t ioId = 1;
  effect_handle_t effectHandle = nullptr;
  effect_config_t config;
  config.inputCfg.samplingRate = config.outputCfg.samplingRate = revConfigParams.sampleRate;
  config.inputCfg.channels = config.outputCfg.channels = revConfigParams.chMask;
  config.inputCfg.format = config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
  if (int status =
          reverbCreateEffect(&effectHandle, &config, sessionId, ioId, revConfigParams.auxiliary);
      status != 0) {
    ALOGE("Create effect call returned error %i", status);
    return EXIT_FAILURE;
  }

  int reply = 0;
  uint32_t replySize = sizeof(reply);
  (*effectHandle)->command(effectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
  if (reply != 0) {
    ALOGE("Command enable call returned error %d\n", reply);
    return EXIT_FAILURE;
  }

  if (int status = reverbSetConfigParam(REVERB_PARAM_PRESET, (uint32_t)revConfigParams.preset,
                                        effectHandle);
      status != 0) {
    ALOGE("Invalid reverb preset. Error %d\n", status);
    return EXIT_FAILURE;
  }

  revConfigParams.nrChannels = audio_channel_count_from_out_mask(revConfigParams.chMask);
  const int channelCount = revConfigParams.nrChannels;
  const int frameLength = revConfigParams.frameLength;
#ifdef BYPASS_EXEC
  const int frameSize = (int)channelCount * sizeof(float);
#endif
  const int ioChannelCount = revConfigParams.fChannels;
  const int ioFrameSize = ioChannelCount * sizeof(short);
  const int maxChannelCount = std::max(channelCount, ioChannelCount);
  /*
   * Mono input will be converted to 2 channels internally in the process call
   * by copying the same data into the second channel.
   * Hence when channelCount is 1, output buffer should be allocated for
   * 2 channels. The memAllocChCount takes care of allocation of sufficient
   * memory for the output buffer.
   */
  const int memAllocChCount = (channelCount == 1 ? 2 : channelCount);

  std::vector<short> in(frameLength * maxChannelCount);
  std::vector<short> out(frameLength * maxChannelCount);
  std::vector<float> floatIn(frameLength * channelCount);
  std::vector<float> floatOut(frameLength * memAllocChCount);

  int frameCounter = 0;

  while (fread(in.data(), ioFrameSize, frameLength, inputFp.get()) == (size_t)frameLength) {
    if (ioChannelCount != channelCount) {
      adjust_channels(in.data(), ioChannelCount, in.data(), channelCount, sizeof(short),
                      frameLength * ioFrameSize);
    }
    memcpy_to_float_from_i16(floatIn.data(), in.data(), frameLength * channelCount);

    // Mono mode will replicate the first channel to all other channels.
    // This ensures all audio channels are identical. This is useful for testing
    // Bass Boost, which extracts a mono signal for processing.
    if (revConfigParams.monoMode && channelCount > 1) {
      for (int i = 0; i < frameLength; ++i) {
        auto *fp = &floatIn[i * channelCount];
        std::fill(fp + 1, fp + channelCount, *fp);  // replicate ch 0
      }
    }

    audio_buffer_t inputBuffer, outputBuffer;
    inputBuffer.frameCount = outputBuffer.frameCount = frameLength;
    inputBuffer.f32 = floatIn.data();
    outputBuffer.f32 = floatOut.data();
#ifndef BYPASS_EXEC
    if (int status = (*effectHandle)->process(effectHandle, &inputBuffer, &outputBuffer);
        status != 0) {
      ALOGE("\nError: Process returned with error %d\n", status);
      return EXIT_FAILURE;
    }
#else
    memcpy(floatOut.data(), floatIn.data(), frameLength * frameSize);
#endif
    memcpy_to_i16_from_float(out.data(), floatOut.data(), frameLength * channelCount);

    if (ioChannelCount != channelCount) {
      adjust_channels(out.data(), channelCount, out.data(), ioChannelCount, sizeof(short),
                      frameLength * channelCount * sizeof(short));
    }
    (void)fwrite(out.data(), ioFrameSize, frameLength, outputFp.get());
    frameCounter += frameLength;
  }

  if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle);
      status != 0) {
    ALOGE("Audio Preprocessing release returned an error = %d\n", status);
    return EXIT_FAILURE;
  }
  printf("frameCounter: [%d]\n", frameCounter);

  return EXIT_SUCCESS;
}