404 lines
15 KiB
C++
404 lines
15 KiB
C++
/*
|
|
* 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,
|
|
AUDIO_CHANNEL_INDEX_MASK_4,
|
|
AUDIO_CHANNEL_OUT_2POINT1POINT2,
|
|
AUDIO_CHANNEL_OUT_3POINT0POINT2,
|
|
AUDIO_CHANNEL_OUT_PENTA,
|
|
AUDIO_CHANNEL_INDEX_MASK_5,
|
|
AUDIO_CHANNEL_OUT_3POINT1POINT2,
|
|
AUDIO_CHANNEL_OUT_5POINT1,
|
|
AUDIO_CHANNEL_OUT_5POINT1_BACK,
|
|
AUDIO_CHANNEL_OUT_5POINT1_SIDE,
|
|
AUDIO_CHANNEL_INDEX_MASK_6,
|
|
AUDIO_CHANNEL_OUT_6POINT1,
|
|
AUDIO_CHANNEL_INDEX_MASK_7,
|
|
AUDIO_CHANNEL_OUT_5POINT1POINT2,
|
|
AUDIO_CHANNEL_OUT_7POINT1,
|
|
AUDIO_CHANNEL_INDEX_MASK_8,
|
|
AUDIO_CHANNEL_INDEX_MASK_9,
|
|
AUDIO_CHANNEL_INDEX_MASK_10,
|
|
AUDIO_CHANNEL_INDEX_MASK_11,
|
|
AUDIO_CHANNEL_INDEX_MASK_12,
|
|
AUDIO_CHANNEL_INDEX_MASK_13,
|
|
AUDIO_CHANNEL_INDEX_MASK_14,
|
|
AUDIO_CHANNEL_INDEX_MASK_15,
|
|
AUDIO_CHANNEL_INDEX_MASK_16,
|
|
AUDIO_CHANNEL_INDEX_MASK_17,
|
|
AUDIO_CHANNEL_INDEX_MASK_18,
|
|
AUDIO_CHANNEL_INDEX_MASK_19,
|
|
AUDIO_CHANNEL_INDEX_MASK_20,
|
|
AUDIO_CHANNEL_INDEX_MASK_21,
|
|
AUDIO_CHANNEL_INDEX_MASK_22,
|
|
AUDIO_CHANNEL_INDEX_MASK_23,
|
|
AUDIO_CHANNEL_INDEX_MASK_24,
|
|
};
|
|
|
|
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], ¶mData[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;
|
|
}
|
|
for (int i = 1; i < argc; i++) {
|
|
printf("%s ", argv[i]);
|
|
}
|
|
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);
|
|
|
|
std::vector<short> in(frameLength * maxChannelCount);
|
|
std::vector<short> out(frameLength * maxChannelCount);
|
|
std::vector<float> floatIn(frameLength * channelCount);
|
|
std::vector<float> floatOut(frameLength * channelCount);
|
|
|
|
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;
|
|
}
|