You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
303 lines
9.8 KiB
303 lines
9.8 KiB
/*
|
|
* Copyright (C) 2011 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 <inttypes.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <vector>
|
|
|
|
#include <audio_effects/effect_downmix.h>
|
|
#include <audio_utils/channels.h>
|
|
#include <audio_utils/primitives.h>
|
|
#include <log/log.h>
|
|
#include <system/audio.h>
|
|
|
|
#include "EffectDownmix.h"
|
|
#define FRAME_LENGTH 256
|
|
#define MAX_NUM_CHANNELS 8
|
|
|
|
struct downmix_cntxt_s {
|
|
effect_handle_t handle;
|
|
effect_config_t config;
|
|
|
|
int numFileChannels;
|
|
int numProcessChannels;
|
|
};
|
|
|
|
extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
|
|
|
|
void printUsage() {
|
|
printf("\nUsage:");
|
|
printf("\n downmixtest <input_file> <out_file> [options]\n");
|
|
printf("\nwhere,");
|
|
printf("\n <input_file> is the input file name");
|
|
printf("\n on which LVM effects are applied");
|
|
printf("\n <output_file> processed output file");
|
|
printf("\n and options are mentioned below");
|
|
printf("\n");
|
|
printf("\n -h");
|
|
printf("\n Prints this usage information");
|
|
printf("\n");
|
|
printf("\n -ch_fmt:<format_of_input_audio>");
|
|
printf("\n 0:AUDIO_CHANNEL_OUT_7POINT1(default)");
|
|
printf("\n 1:AUDIO_CHANNEL_OUT_5POINT1_SIDE");
|
|
printf("\n 2:AUDIO_CHANNEL_OUT_5POINT1_BACK");
|
|
printf("\n 3:AUDIO_CHANNEL_OUT_QUAD_SIDE");
|
|
printf("\n 4:AUDIO_CHANNEL_OUT_QUAD_BACK");
|
|
printf("\n");
|
|
printf("\n -fch:<file_channels> (1 through 8)");
|
|
printf("\n");
|
|
}
|
|
|
|
int32_t DownmixDefaultConfig(effect_config_t *pConfig) {
|
|
pConfig->inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
|
|
pConfig->inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
|
|
pConfig->inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
|
|
pConfig->inputCfg.bufferProvider.getBuffer = nullptr;
|
|
pConfig->inputCfg.bufferProvider.releaseBuffer = nullptr;
|
|
pConfig->inputCfg.bufferProvider.cookie = nullptr;
|
|
pConfig->inputCfg.mask = EFFECT_CONFIG_ALL;
|
|
|
|
pConfig->inputCfg.samplingRate = 44100;
|
|
pConfig->outputCfg.samplingRate = pConfig->inputCfg.samplingRate;
|
|
|
|
// set a default value for the access mode, but should be overwritten by caller
|
|
pConfig->outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
|
|
pConfig->outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
|
|
pConfig->outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
|
|
pConfig->outputCfg.bufferProvider.getBuffer = nullptr;
|
|
pConfig->outputCfg.bufferProvider.releaseBuffer = nullptr;
|
|
pConfig->outputCfg.bufferProvider.cookie = nullptr;
|
|
pConfig->outputCfg.mask = EFFECT_CONFIG_ALL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t DownmixConfiureAndEnable(downmix_cntxt_s *pDescriptor) {
|
|
effect_handle_t effectHandle = pDescriptor->handle;
|
|
const struct effect_interface_s *Downmix_api = *effectHandle;
|
|
int32_t err = 0;
|
|
uint32_t replySize = (uint32_t)sizeof(err);
|
|
|
|
err = (Downmix_api->command)(effectHandle, EFFECT_CMD_SET_CONFIG,
|
|
sizeof(effect_config_t), &(pDescriptor->config),
|
|
&replySize, &err);
|
|
if (err != 0) {
|
|
ALOGE("Downmix command to configure returned an error %d", err);
|
|
return err;
|
|
}
|
|
|
|
err = ((Downmix_api->command))(effectHandle, EFFECT_CMD_ENABLE, 0, nullptr,
|
|
&replySize, &err);
|
|
if (err != 0) {
|
|
ALOGE("Downmix command to enable effect returned an error %d", err);
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int32_t DownmixExecute(downmix_cntxt_s *pDescriptor, FILE *finp,
|
|
FILE *fout) {
|
|
effect_handle_t effectHandle = pDescriptor->handle;
|
|
const struct effect_interface_s *Downmix_api = *effectHandle;
|
|
|
|
const int numFileChannels = pDescriptor->numFileChannels;
|
|
const int numProcessChannels = pDescriptor->numProcessChannels;
|
|
const int fileFrameSize = numFileChannels * sizeof(short);
|
|
const unsigned int outputChannels =
|
|
audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_STEREO);
|
|
|
|
std::vector<float> outFloat(FRAME_LENGTH * MAX_NUM_CHANNELS);
|
|
std::vector<float> inFloat(FRAME_LENGTH * MAX_NUM_CHANNELS);
|
|
|
|
audio_buffer_t inbuffer, outbuffer;
|
|
inbuffer.f32 = inFloat.data();
|
|
outbuffer.f32 = outFloat.data();
|
|
inbuffer.frameCount = FRAME_LENGTH;
|
|
outbuffer.frameCount = FRAME_LENGTH;
|
|
|
|
audio_buffer_t *pinbuf, *poutbuf;
|
|
pinbuf = &inbuffer;
|
|
poutbuf = &outbuffer;
|
|
|
|
int frameCounter = 0;
|
|
std::vector<short> inS16(FRAME_LENGTH * MAX_NUM_CHANNELS);
|
|
std::vector<short> outS16(FRAME_LENGTH * MAX_NUM_CHANNELS);
|
|
|
|
while (fread(inS16.data(), fileFrameSize, FRAME_LENGTH, finp) ==
|
|
FRAME_LENGTH) {
|
|
if (numFileChannels != numProcessChannels) {
|
|
adjust_channels(inS16.data(), numFileChannels, inS16.data(),
|
|
numProcessChannels, sizeof(short),
|
|
FRAME_LENGTH * fileFrameSize);
|
|
}
|
|
|
|
memcpy_to_float_from_i16(inFloat.data(), inS16.data(),
|
|
FRAME_LENGTH * numProcessChannels);
|
|
|
|
const int32_t err = (Downmix_api->process)(effectHandle, pinbuf, poutbuf);
|
|
if (err != 0) {
|
|
ALOGE("DownmixProcess returned an error %d", err);
|
|
return -1;
|
|
}
|
|
|
|
memcpy_to_i16_from_float(outS16.data(), outFloat.data(),
|
|
FRAME_LENGTH * outputChannels);
|
|
fwrite(outS16.data(), sizeof(short), (FRAME_LENGTH * outputChannels),
|
|
fout);
|
|
frameCounter++;
|
|
}
|
|
printf("frameCounter: [%d]\n", frameCounter);
|
|
return 0;
|
|
}
|
|
|
|
int32_t DowmixMainProcess(downmix_cntxt_s *pDescriptor, FILE *finp,
|
|
FILE *fout) {
|
|
effect_handle_t *effectHandle = &pDescriptor->handle;
|
|
int32_t sessionId = 0, ioId = 0;
|
|
const effect_uuid_t downmix_uuid = {
|
|
0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}};
|
|
|
|
int32_t err = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(
|
|
&downmix_uuid, sessionId, ioId,
|
|
effectHandle);
|
|
if (err != 0) {
|
|
ALOGE("DownmixLib_Create returned an error %d", err);
|
|
return -1;
|
|
}
|
|
|
|
// Passing the init config for time being.
|
|
err = DownmixConfiureAndEnable(pDescriptor);
|
|
if (err != 0) {
|
|
ALOGE("DownmixConfigureAndEnable returned an error %d", err);
|
|
return -1;
|
|
}
|
|
// execute call for downmix.
|
|
err = DownmixExecute(pDescriptor, finp, fout);
|
|
if (err != 0) {
|
|
ALOGE("DownmixExecute returned an error %d", err);
|
|
return -1;
|
|
}
|
|
// Release the library function.
|
|
err = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(*effectHandle);
|
|
if (err != 0) {
|
|
ALOGE("DownmixRelease returned an error %d", err);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, const char *argv[]) {
|
|
int numFileChannels = 1, numProcessChannels = 8;
|
|
downmix_cntxt_s descriptor = {};
|
|
DownmixDefaultConfig(&(descriptor.config));
|
|
|
|
const char *infile = nullptr;
|
|
const char *outfile = nullptr;
|
|
for (int i = 1; i < argc; i++) {
|
|
printf("%s ", argv[i]);
|
|
if (argv[i][0] != '-') {
|
|
if (infile == nullptr) {
|
|
infile = argv[i];
|
|
} else if (outfile == nullptr) {
|
|
outfile = argv[i];
|
|
} else {
|
|
printUsage();
|
|
return -1;
|
|
}
|
|
} else if (!strncmp(argv[i], "-fs:", 4)) {
|
|
// Add a check for all the supported streams.
|
|
const int samplingFreq = atoi(argv[i] + 4);
|
|
if (samplingFreq != 8000 && samplingFreq != 11025 &&
|
|
samplingFreq != 12000 && samplingFreq != 16000 &&
|
|
samplingFreq != 22050 && samplingFreq != 24000 &&
|
|
samplingFreq != 32000 && samplingFreq != 44100 &&
|
|
samplingFreq != 48000 && samplingFreq != 88200 &&
|
|
samplingFreq != 96000 && samplingFreq != 176400 &&
|
|
samplingFreq != 192000) {
|
|
printf("Unsupported Sampling Frequency : %d", samplingFreq);
|
|
printUsage();
|
|
return -1;
|
|
}
|
|
|
|
descriptor.config.inputCfg.samplingRate = samplingFreq;
|
|
descriptor.config.outputCfg.samplingRate = samplingFreq;
|
|
} else if (!strncmp(argv[i], "-ch_fmt:", 8)) {
|
|
const int format = atoi(argv[i] + 8);
|
|
uint32_t *audioType = &descriptor.config.inputCfg.channels;
|
|
switch (format) {
|
|
case 0:
|
|
*audioType = AUDIO_CHANNEL_OUT_7POINT1;
|
|
break;
|
|
case 1:
|
|
*audioType = AUDIO_CHANNEL_OUT_5POINT1_SIDE;
|
|
break;
|
|
case 2:
|
|
*audioType = AUDIO_CHANNEL_OUT_5POINT1_BACK;
|
|
break;
|
|
case 3:
|
|
*audioType = AUDIO_CHANNEL_OUT_QUAD_SIDE;
|
|
break;
|
|
case 4:
|
|
*audioType = AUDIO_CHANNEL_OUT_QUAD_BACK;
|
|
break;
|
|
default:
|
|
*audioType = AUDIO_CHANNEL_OUT_7POINT1;
|
|
break;
|
|
}
|
|
descriptor.numProcessChannels =
|
|
audio_channel_count_from_out_mask(*audioType);
|
|
} else if (!strncmp(argv[i], "-fch:", 5)) {
|
|
const int fChannels = atoi(argv[i] + 5);
|
|
if (fChannels > 8 || fChannels < 1) {
|
|
printf("Unsupported number of file channels : %d", fChannels);
|
|
printUsage();
|
|
return -1;
|
|
}
|
|
descriptor.numFileChannels = fChannels;
|
|
|
|
} else if (!strncmp(argv[i], "-h", 2)) {
|
|
printUsage();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (/*infile == nullptr || */ outfile == nullptr) {
|
|
printUsage();
|
|
return -1;
|
|
}
|
|
|
|
FILE *finp = fopen(infile, "rb");
|
|
if (finp == nullptr) {
|
|
printf("Cannot open input file %s", infile);
|
|
return -1;
|
|
}
|
|
FILE *fout = fopen(outfile, "wb");
|
|
if (fout == nullptr) {
|
|
printf("Cannot open output file %s", outfile);
|
|
fclose(finp);
|
|
return -1;
|
|
}
|
|
|
|
const int err = DowmixMainProcess(&descriptor, finp, fout);
|
|
// close input and output files.
|
|
fclose(finp);
|
|
fclose(fout);
|
|
if (err != 0) {
|
|
printf("Error: %d\n", err);
|
|
}
|
|
return err;
|
|
}
|