250 lines
6.4 KiB
C
250 lines
6.4 KiB
C
/*
|
|
* Copyright (C) 2018 Amlogic Corporation.
|
|
*
|
|
* 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.
|
|
*
|
|
* DESCRIPTION:
|
|
* This program is the factory of PCM data.
|
|
*
|
|
*/
|
|
|
|
#define LOG_TAG "AML_Reverb"
|
|
//#define LOG_NDEBUG 0
|
|
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
|
|
#include "aml_audio_log.h"
|
|
|
|
#include "aml_reverb.h"
|
|
#include "aml_malloc_debug.h"
|
|
|
|
#define MAXRATE 48000 //max sample rate in Hz
|
|
#define MAXTIME 1 //max delay time in second
|
|
#define DELAY_BUFSIZ (MAXTIME * MAXRATE)
|
|
#define MAXREVERBS 8
|
|
#define CHANNEL_NUM 2
|
|
|
|
struct reverb_t{
|
|
int counter;
|
|
int numdelays;
|
|
float *reverbbuf;
|
|
float in_gain;
|
|
float out_gain;
|
|
float time;
|
|
float delay[MAXREVERBS];
|
|
float decay[MAXREVERBS];
|
|
int samples[MAXREVERBS];
|
|
int maxsamples;
|
|
};
|
|
|
|
struct aml_reverb {
|
|
struct reverb_t AML_Reverb_L;
|
|
struct reverb_t AML_Reverb_R;
|
|
unsigned int mode;
|
|
float reverb_in_gain;
|
|
float reverb_out_gain;
|
|
};
|
|
|
|
static inline int clip16(int x) {
|
|
if (x < -32768) {
|
|
return -32768;
|
|
} else if (x > 32767) {
|
|
return 32767;
|
|
} else {
|
|
return x;
|
|
}
|
|
}
|
|
|
|
static int reverb_start(struct reverb_t *aml_Reverb) {
|
|
struct reverb_t *reverb = aml_Reverb;
|
|
int i;
|
|
|
|
reverb->counter = 0;
|
|
reverb->numdelays = 5;
|
|
reverb->in_gain = 0.75;
|
|
reverb->out_gain = 1.0;
|
|
reverb->maxsamples = 0;
|
|
reverb->counter = 0;
|
|
reverb->reverbbuf = NULL;
|
|
|
|
for (i = 0; i < reverb->numdelays; i++) {
|
|
reverb->delay[i] = 20 * (i +1);
|
|
}
|
|
|
|
for (i = 0; i < reverb->numdelays; i++) {
|
|
reverb->samples[i] = reverb->delay[i] * MAXRATE / 1000.0;
|
|
if ( reverb->samples[i] > DELAY_BUFSIZ ) {
|
|
ALOGE("reverb: delay must be less than %d seconds!\n", MAXTIME);
|
|
return-1;
|
|
}
|
|
/* Compute a realistic decay */
|
|
reverb->decay[i] = (float) pow(10.0, (-3.0 * reverb->delay[i] / reverb->time));
|
|
ALOGE("reverb: reverb->decay[%d] = %f\n", i, reverb->decay[i]);
|
|
if ( reverb->samples[i] > reverb->maxsamples )
|
|
reverb->maxsamples = reverb->samples[i];
|
|
}
|
|
|
|
if (! (reverb->reverbbuf = (float *) aml_audio_malloc(sizeof (float) * reverb->maxsamples))) {
|
|
ALOGE("reverb: Cannot malloc %d bytes!\n", (int)(sizeof(float) * reverb->maxsamples));
|
|
return-1;
|
|
}
|
|
|
|
for (i = 0; i < reverb->maxsamples; ++i)
|
|
reverb->reverbbuf[i] = 0.0;
|
|
|
|
/* Compute the input volume carefully */
|
|
for (i = 0; i < reverb->numdelays; i++)
|
|
reverb->in_gain *= ( 1.0 - ( reverb->decay[i] * reverb->decay[i] ));
|
|
|
|
ALOGI("reverb: reverb->in_gain = %f\n", reverb->in_gain);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Processed signed long samples from ibuf to obuf.
|
|
* Return number of samples processed.
|
|
*/
|
|
static int reverb_process(struct reverb_t *aml_Reverb, int16_t *ibuf, int16_t *obuf, int frame_count)
|
|
{
|
|
struct reverb_t *reverb = aml_Reverb;
|
|
int len, done;
|
|
int i, j, k;
|
|
|
|
float d_in, d_out;
|
|
int out;
|
|
|
|
i = reverb->counter;
|
|
len = frame_count;
|
|
for (done = 0; done < len; done++) {
|
|
d_in = (float) *ibuf;
|
|
ibuf += CHANNEL_NUM;
|
|
d_in = d_in * reverb->in_gain;
|
|
/* Mix decay of delay and input as output */
|
|
for (j = 0; j < reverb->numdelays; j++) {
|
|
k = (i + reverb->maxsamples - reverb->samples[j]) % reverb->maxsamples;
|
|
d_in += reverb->reverbbuf[k] * reverb->decay[j];
|
|
}
|
|
d_out = d_in * reverb->out_gain;
|
|
out = clip16((int) d_out);
|
|
*obuf = out;
|
|
obuf += CHANNEL_NUM;
|
|
reverb->reverbbuf[i] = d_in;
|
|
i++;
|
|
i %= reverb->maxsamples;
|
|
}
|
|
reverb->counter = i;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Clean up reverb effect.
|
|
*/
|
|
static int reverb_stop(struct reverb_t *aml_Reverb)
|
|
{
|
|
struct reverb_t *reverb = aml_Reverb;
|
|
|
|
if (reverb && reverb->reverbbuf)
|
|
aml_audio_free((char *) reverb->reverbbuf);
|
|
reverb->reverbbuf = NULL;
|
|
return 0;
|
|
}
|
|
|
|
int AML_Reverb_Init(void **reverb_handle)
|
|
{
|
|
struct aml_reverb *reverb;
|
|
|
|
reverb = aml_audio_calloc(1, sizeof(struct aml_reverb));
|
|
if (!reverb) {
|
|
ALOGE("%s, malloc error\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
reverb_start(&reverb->AML_Reverb_L);
|
|
reverb_start(&reverb->AML_Reverb_R);
|
|
|
|
*reverb_handle = (void *)reverb;
|
|
|
|
ALOGI("init. aml reverb!");
|
|
return 0;
|
|
}
|
|
|
|
int AML_Reverb_Process(void *reverb_handle, int16_t *inBuffer, int16_t *outBuffer, int frameCount)
|
|
{
|
|
struct aml_reverb *reverb = (struct aml_reverb *)reverb_handle;
|
|
struct reverb_t *AML_Reverb_L;
|
|
struct reverb_t *AML_Reverb_R;
|
|
float reverb_time;
|
|
|
|
if (!reverb)
|
|
return 0;
|
|
|
|
AML_Reverb_L = &reverb->AML_Reverb_L;
|
|
AML_Reverb_R = &reverb->AML_Reverb_R;
|
|
|
|
reverb_time = (float)(reverb->mode * 30);
|
|
if (reverb_time != AML_Reverb_L->time || reverb_time != AML_Reverb_R->time) {
|
|
reverb_stop(AML_Reverb_L);
|
|
reverb_stop(AML_Reverb_R);
|
|
AML_Reverb_L->time = reverb_time;
|
|
AML_Reverb_R->time = reverb_time;
|
|
reverb_start(AML_Reverb_L);
|
|
reverb_start(AML_Reverb_R);
|
|
}
|
|
|
|
reverb_process(AML_Reverb_L, inBuffer, outBuffer, frameCount);
|
|
reverb_process(AML_Reverb_R, (inBuffer + 1), (outBuffer + 1), frameCount);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int AML_Reverb_Release(void *reverb_handle)
|
|
{
|
|
struct aml_reverb *reverb = (struct aml_reverb *)reverb_handle;
|
|
|
|
if (!reverb)
|
|
return 0;
|
|
|
|
ALOGI("%s, remove aml reverb\n", __func__);
|
|
|
|
if (reverb->AML_Reverb_L.reverbbuf)
|
|
aml_audio_free(reverb->AML_Reverb_L.reverbbuf);
|
|
if (reverb->AML_Reverb_R.reverbbuf)
|
|
aml_audio_free(reverb->AML_Reverb_R.reverbbuf);
|
|
|
|
aml_audio_free(reverb);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void AML_Reverb_Set_Mode(void *reverb_handle, unsigned int mode)
|
|
{
|
|
struct aml_reverb *reverb = (struct aml_reverb *)reverb_handle;
|
|
|
|
if (!reverb)
|
|
return;
|
|
|
|
if (reverb->mode != mode) {
|
|
ALOGV("%s, set aml reverb mode: %d\n", __func__, mode);
|
|
reverb->mode = mode;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|