| /* |
| * 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; |
| } |