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