Files
2026-08-24 10:03:24 +08:00

1269 lines
44 KiB
C

/*
* Copyright (C) 2017 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.
*/
#define LOG_TAG "audio_hw_hal_kara"
//#define LOG_NDEBUG 0
#include "aml_audio_log.h"
#include <audio_utils/channels.h>
#include <unistd.h>
#include <limits.h>
#include "audio_data_process.h"
#include "audio_hw_utils.h"
#include "karaoke_manager.h"
#include "aml_volume_utils.h"
//#include "EffectReverb.h"
#include "aml_malloc_debug.h"
#include "aml_reverb.h"
#include "aml_dump_debug.h"
#include "audio_hw_ms12_v2.h"
#include "alsa_device_parser.h"
#define USB_DEFAULT_PERIOD_SIZE 512
#define USB_DEFAULT_PERIOD_COUNT 4
#define LINEIN_DEFAULT_SAMPLE_RATE 48000
#define LINEIN_DEFAULT_CHANNEL 2
#define LINEIN_DEFAULT_FORMAT PCM_FORMAT_S16_LE
#define LINEIN_DEFAULT_PERIOD_SIZE 1024
#define LINEIN_DEFAULT_PERIOD_COUNT 4
#define ALOOP_DEFAULT_PERIOD_SIZE 512
#define ALOOP_DEFAULT_PERIOD_COUNT 6
struct voice_in {
struct audioCfg cfg;
bool debug;
struct pcm *pcm_handle;
struct pcm_config pcm_in_config;
void *conversion_buffer;
size_t conversion_buffer_size;
};
struct kara_device {
pthread_mutex_t lock;
kara_input_type_t kara_input_type;
kara_output_type_t kara_output_type;
bool karaoke_on;
bool karaoke_enable;
bool karaoke_start;
bool kara_mic_record;
bool kara_mic_mute;
float kara_mic_gain;
struct voice_in in;
void *buf;
size_t buf_len;
ring_buffer_t mic_buffer;
struct audioCfg mixout_config;
void *echo_reference;
struct pcm *loopback_handle;
int (*open)(struct kara_device *in, struct audioCfg *cfg);
int (*close)(struct kara_device *in);
/* mixer audio data to output */
int (*mix)(struct kara_device *in, void *buffer, size_t bytes);
/* read data from ringbuffer */
ssize_t (*read)(struct kara_device *in, void *buffer, size_t bytes);
/* reverb for mic */
void *reverb_handle;
bool reverb_enable;
int reverb_mode;
audio_resample_config_t resample_config;
aml_audio_resample_t *resample_handle;
ring_buffer_t resample_buffer;
int resample_wait_count;
};
struct kara_manager {
struct kara_device in_device[KARA_INPUT_TYPE_MAX];
};
/* karaoke init*/
int karaoke_init(struct kara_device *kara,
kara_input_type_t kara_input_type,
kara_output_type_t kara_output_type);
/* karaoke check if should do capture and mix */
int karaoke_check_mix_output(struct kara_device *karaoke, void *buffer, size_t bytes);
/* karaoke get audio config from ms12 info */
int karaoke_get_audioCfg_from_ms12_info(struct audioCfg *cfg, struct aml_ms12_dec_info *ms12_info);
/* karaoke get audio config from pcm_config */
int karaoke_get_audioCfg_from_pcm_config(struct audioCfg *cfg, struct pcm_config *pcm_cfg);
/* karaoke close */
int karaoke_close(struct kara_device *kara);
/* karaoke key parameters get && set function*/
kara_input_type_t karaoke_get_input_type(struct kara_device *kara);
int karaoke_set_input_type(struct kara_device *kara, kara_input_type_t value);
kara_output_type_t karaoke_get_output_type(struct kara_device *kara);
int karaoke_set_output_type(struct kara_device *kara, kara_output_type_t value);
bool karaoke_get_on(struct kara_device *kara);
int karaoke_set_on(struct kara_device *kara, bool value);
bool karaoke_get_enable(struct kara_device *kara);
int karaoke_set_enable(struct kara_device *kara, bool value);
bool karaoke_get_start(struct kara_device *kara);
int karaoke_set_start(struct kara_device *kara, bool value);
bool karaoke_get_mic_record(struct kara_device *kara);
int karaoke_set_mic_record(struct kara_device *kara, bool value);
bool karaoke_get_mic_mute(struct kara_device *kara);
int karaoke_set_mic_mute(struct kara_device *kara, bool value);
float karaoke_get_mic_gain(struct kara_device *kara);
int karaoke_set_mic_gain(struct kara_device *kara, float value);
bool karaoke_get_debug(struct kara_device *kara);
int karaoke_set_debug(struct kara_device *kara, bool value);
/* get audio config for recording such as adev_open_input_stream */
int karaoke_get_config_by_record_type(struct kara_device *kara,
kara_record_type_t record_type,
struct audio_config *config);
/* privide data for recorder by ring_buffer/echo_reference/ALOOP and HW loopback(hw provide) */
static int provide_data_for_recorder(struct kara_device *kara,
kara_record_type_t record_type,
void* buffer,
struct audioCfg config,
size_t size)
{
if (!kara || !buffer || 0 == size) {
return -EINVAL;
}
/* Original mic data, recorder by ring buffer */
if (KARA_RECORD_TYPE_MIC_ORIGINAL == record_type) {
// write data only when record start
if (kara->kara_mic_record) {
ring_buffer_write(&kara->mic_buffer, (unsigned char *)buffer, size, UNCOVER_WRITE);
}
return size;
}
struct voice_in *in = &kara->in;
if (!in) {
return -EINVAL;
}
/* Mic data after volume process, recorder by echo_reference buffer */
if (KARA_RECORD_TYPE_MIC_AFTER_VOLUME == record_type) {
size_t original_size = size;
size_t after_process_size = original_size; //size after process
int channel_support = 2; //ench reference only support 2ch write currently
void* ench_source_buffer = NULL;
if (0 == config.channelCnt) {
return -EINVAL;
}
if (kara->echo_reference == NULL) {
// do nothing
} else {
//use conversion buffer for saving data after channel adjust
if (channel_support != config.channelCnt) {
after_process_size = (original_size * channel_support) / config.channelCnt;
if (after_process_size > in->conversion_buffer_size) {
in->conversion_buffer_size = after_process_size;
in->conversion_buffer = aml_audio_realloc(in->conversion_buffer, in->conversion_buffer_size);
if (!in->conversion_buffer) {
AM_LOGE("conversion_buffer malloc is fail");
return -1;
}
}
/* adjust to channel supported by echo_reference */
enum pcm_format format = convert_audio_format_2_alsa_format(config.format);
unsigned int sample_size_in_bytes = pcm_format_to_bits(format) / 8;
after_process_size = adjust_channels(buffer, config.channelCnt,
in->conversion_buffer, channel_support,
sample_size_in_bytes, original_size);
ench_source_buffer = in->conversion_buffer;
} else {
ench_source_buffer = buffer;
}
/* save data to echo reference*/
#if 0
struct echo_reference_buffer b;
b.raw = ench_source_buffer;
b.frame_count = original_size / config.frame_size;
clock_gettime(CLOCK_REALTIME, &b.time_stamp);
b.delay_ns = 0;
kara->echo_reference->write(kara->echo_reference, &b);
#endif
if (get_debug_value(AML_DUMP_AUDIOHAL_OUT) || in->debug) {
aml_dump_audio_bitstreams("/data/audio/kara_echo_reference.raw", ench_source_buffer, after_process_size);
}
}
return size;
}
/* Mic data after reverb process */
if (KARA_RECORD_TYPE_MIC_AFTER_REVERB == record_type) {
// implement when need record data after reverb
}
/* Data after mix, recorder by ALOOP */
if (KARA_RECORD_TYPE_MIC_AFTER_SW_MIX == record_type) {
if (kara->karaoke_on && kara->loopback_handle) {
pcm_write(kara->loopback_handle, (void *)buffer, size);
if (get_debug_value(AML_DUMP_AUDIOHAL_OUT) || in->debug) {
aml_dump_audio_bitstreams("/data/audio/kara_write_to_loopback.pcm", buffer, size);
}
}
return size;
}
return size;
}
/* Kara input data manager */
static ssize_t mic_data_input(struct kara_device *kara, size_t frames)
{
if (!kara || 0 == frames) {
return -EINVAL;
}
struct voice_in *in = &kara->in;
if (!in) {
return -EINVAL;
}
uint32_t in_framesize = in->cfg.frame_size;
if (0 == in_framesize) {
return -EINVAL;
}
int ret = -1;
/* real read size/frames from alsa or third party */
size_t num_read_buff_bytes = frames * in_framesize; // init read size
size_t real_frames = frames; // init real_frames as sample rate is same
/* read size need modify when in&mixout sample rate are different */
if (kara->resample_handle && kara->resample_config.output_sr) {
real_frames = (real_frames * kara->resample_config.input_sr) / kara->resample_config.output_sr;
int remainder = (real_frames * kara->resample_config.input_sr) % kara->resample_config.output_sr;
if (remainder) {
real_frames += 1; // read more 1 frame because data may overrun
}
num_read_buff_bytes = real_frames * in_framesize;
}
/* kara->buf is for saving the original mic data */
if (num_read_buff_bytes > kara->buf_len) {
kara->buf = aml_audio_realloc(kara->buf, num_read_buff_bytes);
if (!kara->buf) {
AM_LOGE("kara->buf aml_audio_realloc is fail");
return -1;
}
kara->buf_len = num_read_buff_bytes;
}
void *read_buff = kara->buf;
if (!read_buff) {
AM_LOGE("kara->buf is null");
return -1;
}
/* Read mic data via different way */
switch (kara->kara_input_type) {
case KARA_INPUT_TYPE_USB_MIC:
case KARA_INPUT_TYPE_LINEIN_MIC:
if (!in->pcm_handle) {
AM_LOGE("in->pcm_handle is null");
return -1;
}
ret = pcm_read(in->pcm_handle, read_buff, num_read_buff_bytes);
break;
default:
//add other read function here
break;
}
//AM_LOGD("ret = %d, num_read_buff_bytes = %zu", ret, num_read_buff_bytes);
if (0 == ret) {
if (get_debug_value(AML_DUMP_AUDIOHAL_OUT) || in->debug) {
if (KARA_INPUT_TYPE_USB_MIC == kara->kara_input_type) {
aml_dump_audio_bitstreams("/data/audio/kara_mic_input_original_usb.raw", read_buff, num_read_buff_bytes);
} else if (KARA_INPUT_TYPE_LINEIN_MIC == kara->kara_input_type) {
aml_dump_audio_bitstreams("/data/audio/kara_mic_input_original_linein.raw", read_buff, num_read_buff_bytes);
} else {
aml_dump_audio_bitstreams("/data/audio/kara_mic_input_original.raw", read_buff, num_read_buff_bytes);
}
}
/* Provide data for recording original data */
provide_data_for_recorder(kara, KARA_RECORD_TYPE_MIC_ORIGINAL, read_buff, in->cfg, num_read_buff_bytes);
} else {
real_frames = 0;
}
return real_frames;
}
static ssize_t mic_data_process(struct kara_device *kara, size_t in_frames, size_t out_frames)
{
if (!kara || !kara->buf || 0 == in_frames || 0 == out_frames) {
return -EINVAL;
}
struct voice_in *in = &kara->in;
if (!in) {
return -EINVAL;
}
size_t original_size = in_frames * in->cfg.frame_size; // size of original mic data
enum pcm_format format = in->pcm_in_config.format;
unsigned int sample_size_in_bytes = pcm_format_to_bits(format) / 8;
/* Volume Process Start*/
//AM_LOGD("original_size=%zu mute=%d gain=%f", original_size, kara->kara_mic_mute, kara->kara_mic_gain);
if (kara->kara_mic_mute) {
memset(kara->buf, 0, original_size); // mute mic data
} else {
apply_volume(kara->kara_mic_gain, kara->buf, sample_size_in_bytes, original_size);
}
if (get_debug_value(AML_DUMP_AUDIOHAL_OUT) || in->debug) {
if (KARA_INPUT_TYPE_USB_MIC == kara->kara_input_type) {
aml_dump_audio_bitstreams("/data/audio/kara_after_volume_process_usb.raw", kara->buf, original_size);
} else if (KARA_INPUT_TYPE_LINEIN_MIC == kara->kara_input_type) {
aml_dump_audio_bitstreams("/data/audio/kara_after_volume_process_linein.raw", kara->buf, original_size);
} else {
aml_dump_audio_bitstreams("/data/audio/kara_after_volume_process.raw", kara->buf, original_size);
}
}
/* Provide data for recording after volume process */
provide_data_for_recorder(kara, KARA_RECORD_TYPE_MIC_AFTER_VOLUME, kara->buf, in->cfg, original_size);
/* Volume Process End*/
/* Reverb Process Start*/
if (kara->reverb_enable) {
/* reverb only support 2 channel & 16 bit currently */
if ( 2 == in->pcm_in_config.channels && PCM_FORMAT_S16_LE == in->pcm_in_config.format) {
AML_Reverb_Set_Mode(kara->reverb_handle, kara->reverb_mode);
AML_Reverb_Process(kara->reverb_handle, kara->buf, kara->buf, original_size >> 2);
} else {
// todo after reverb support more input config
}
}
if (get_debug_value(AML_DUMP_AUDIOHAL_OUT) || in->debug) {
aml_dump_audio_bitstreams("/data/audio/kara_after_reverb_process.pcm", kara->buf, original_size);
}
/* Reverb Process End */
return in_frames;
}
static ssize_t mic_data_output(struct kara_device *kara, void *main_buffer, size_t in_frames, size_t out_frames)
{
if (!kara || !kara->buf || !main_buffer || 0 == out_frames) {
return -EINVAL;
}
struct voice_in *in = &kara->in;
if (!in) {
return -EINVAL;
}
size_t original_size = in_frames * in->cfg.frame_size; // size of original mic data
size_t after_process_size = original_size; // size after process, initial with no process
size_t mixout_bytes = out_frames * kara->mixout_config.frame_size;
void *read_buff = kara->buf;
void *out_buff = kara->buf;
unsigned int num_in_channels = in->cfg.channelCnt;
unsigned int num_mixout_channels = kara->mixout_config.channelCnt;
uint32_t in_sample_rate = in->cfg.sampleRate;
uint32_t out_sample_rate = kara->mixout_config.sampleRate;
if (0 == num_in_channels || 0 == num_mixout_channels
|| 0 == in_sample_rate || 0 == out_sample_rate) {
return -EINVAL;
}
enum pcm_format in_format = in->pcm_in_config.format;
unsigned int in_sample_size = pcm_format_to_bits(in_format) / 8;
/* SW Mix Process Start */
if (KARA_OUTPUT_TYPE_SW_MIX == kara->kara_output_type) {
/* 1.do channel adjust */
if (num_in_channels == num_mixout_channels) {
//do not need channel adjust
out_buff = kara->buf;
} else {
// need channel adjust
after_process_size = (original_size * num_mixout_channels) / num_in_channels;
if (after_process_size <= kara->buf_len) {
// use kara->buf for saving data after channel contract
read_buff = kara->buf;
out_buff = kara->buf;
} else {
// use conversion_buffer for saving data after channel expand
if (after_process_size > in->conversion_buffer_size) {
in->conversion_buffer = aml_audio_realloc(in->conversion_buffer, after_process_size);
if (!in->conversion_buffer) {
AM_LOGE("channel adjust conversion_buffer malloc error");
return -1;
}
in->conversion_buffer_size = after_process_size;
}
read_buff = kara->buf;
out_buff = in->conversion_buffer;
}
after_process_size = adjust_channels(read_buff, num_in_channels,
out_buff, num_mixout_channels,
in_sample_size, original_size);
}
/* 2.do resample */
if (kara->resample_handle) {
read_buff = out_buff; //the last out buffer is the next input buffer
if (!aml_audio_resample_process(kara->resample_handle, read_buff, after_process_size)) {
after_process_size = kara->resample_handle->resample_size;
if (after_process_size <= kara->buf_len) {
// use kara->buf for saving data after resample contract
out_buff = kara->buf;
} else {
// use conversion_buffer for saving data after resample expand
if (after_process_size > in->conversion_buffer_size) {
in->conversion_buffer = aml_audio_realloc(in->conversion_buffer, after_process_size);
if (!in->conversion_buffer) {
AM_LOGE("resample conversion_buffer malloc error");
return -1;
}
in->conversion_buffer_size = after_process_size;
}
out_buff = in->conversion_buffer;
}
// use ring buffer because resample handle different length each time
ring_buffer_write(&kara->resample_buffer, (unsigned char *)kara->resample_handle->resample_buffer,
kara->resample_handle->resample_size, UNCOVER_WRITE);
if (kara->resample_wait_count <= 3) {
kara->resample_wait_count += 1;
return 0;
}
// copy resample data to out buffer
enum pcm_format out_format = convert_audio_format_2_alsa_format(kara->mixout_config.format);
int out_sample_size = pcm_format_to_bits(out_format) / 8;
int single_bytes = mixout_bytes * in_sample_size / out_sample_size;
if (get_buffer_read_space(&kara->resample_buffer) >= single_bytes) {
ring_buffer_read(&kara->resample_buffer, out_buff, single_bytes);
//memcpy(out_buff, kara->resample_handle->resample_buffer, kara->resample_handle->resample_size);
} else {
AM_LOGE("resample ring buffer size(%d) not enough for single_bytes(%d)",
get_buffer_read_space(&kara->resample_buffer), single_bytes);
}
}
}
/* 3.mixer mic data with main buffer */
read_buff = out_buff; //the last out buffer is the next input buffer
do_mixing_specified_channel_cnt(main_buffer, read_buff, out_frames,
in->cfg.format, kara->mixout_config.format,
num_mixout_channels);
/* 4.dump data after mix process when debug */
if (get_debug_value(AML_DUMP_AUDIOHAL_OUT) || in->debug) {
if (KARA_INPUT_TYPE_USB_MIC == kara->kara_input_type) {
aml_dump_audio_bitstreams("/data/audio/kara_after_mix_usb.raw", main_buffer, mixout_bytes);
} else if (KARA_INPUT_TYPE_LINEIN_MIC == kara->kara_input_type) {
aml_dump_audio_bitstreams("/data/audio/kara_after_mix_linein.raw", main_buffer, mixout_bytes);
} else {
aml_dump_audio_bitstreams("/data/audio/kara_after_mix.raw", main_buffer, mixout_bytes);
}
}
/* 5.Provide data for recording data after mix process */
provide_data_for_recorder(kara, KARA_RECORD_TYPE_MIC_AFTER_SW_MIX, main_buffer, kara->mixout_config, mixout_bytes);
/* SW Mix Process End */
}else {
// add other output type here
}
return after_process_size;
}
static int kara_mix_micphone(struct kara_device *kara, void *buf, size_t bytes)
{
if (!kara || !buf || 0 == bytes) {
return -EINVAL;
}
if (0 == kara->mixout_config.frame_size) {
return -EINVAL;
}
size_t out_frames = bytes / kara->mixout_config.frame_size; // frames on main buffer
ssize_t frames_ret = 0;
pthread_mutex_lock(&kara->lock);
/* 1.mic data input management*/
frames_ret = mic_data_input(kara, out_frames); // return real read frames
if (frames_ret <= 0) {
AM_LOGE("mic_data_input fail frames_ret(%zu)", frames_ret);
goto exit;
}
/* 2.mic data process management */
frames_ret = mic_data_process(kara, frames_ret, out_frames);
if (frames_ret <= 0) {
AM_LOGE("mic_data_process fail frames_ret(%zu)", frames_ret);
goto exit;
}
/* 3.mic data output management */
frames_ret = mic_data_output(kara, buf, frames_ret, out_frames);
if (frames_ret <= 0) {
AM_LOGE("mic_data_output fail frames_ret(%zu)", frames_ret);
goto exit;
}
exit:
pthread_mutex_unlock(&kara->lock);
return frames_ret;
}
/* For userspace recording original mic buffer */
static ssize_t kara_read_mic_buffer(struct kara_device *kara, void *buffer, size_t bytes)
{
if (!kara || !buffer || kara->mic_buffer.size == 0 || !kara->karaoke_start) {
return -EINVAL;
}
uint32_t read_bytes = 0;
int nodata_count = 0;
int retry_max_count = 20;
int sleep_us = 5000;
size_t ret = 0;
while (read_bytes < bytes) {
ret = ring_buffer_read(&kara->mic_buffer, (uint8_t *)buffer + read_bytes, bytes - read_bytes);
read_bytes += ret;
if (read_bytes == bytes) {
return bytes;
}
nodata_count++;
if (nodata_count >= retry_max_count) {
AM_LOGW("read data timeout, need:%zu, read_bytes:%d", bytes, read_bytes);
memset(buffer, 0, bytes);
return bytes;
}
usleep(sleep_us);
}
return bytes;
}
/* init ALOOP handle*/
static int kara_open_aloop_handle(struct kara_device *kara, struct audioCfg *cfg)
{
if (!kara || !cfg) {
AM_LOGE("Input null pointer");
return -EINVAL;
}
int aloop_card = alsa_device_get_card_index_by_name("Loopback");
if (aloop_card < 0) {
kara->loopback_handle = NULL;
AM_LOGI("Aloop card device not found");
return -1;
} else {
unsigned int aloop_device = 0; // Aloop device 0 for pcm write
AM_LOGI("Aloop card device exist");
struct pcm_config pcm_mixout_config;
memset(&pcm_mixout_config, 0, sizeof(pcm_mixout_config));
pcm_mixout_config.channels = cfg->channelCnt;
pcm_mixout_config.rate = cfg->sampleRate;
pcm_mixout_config.format = convert_audio_format_2_alsa_format(cfg->format);
pcm_mixout_config.period_size = ALOOP_DEFAULT_PERIOD_SIZE;
pcm_mixout_config.period_count = ALOOP_DEFAULT_PERIOD_COUNT;
pcm_mixout_config.start_threshold = pcm_mixout_config.period_size *
pcm_mixout_config.period_count / 2;
pcm_mixout_config.silence_size = LONG_MAX;
kara->loopback_handle = pcm_open(aloop_card, aloop_device, PCM_OUT, &pcm_mixout_config);
if (!pcm_is_ready(kara->loopback_handle)) {
AM_LOGE("cannot open loopback: %s", pcm_get_error(kara->loopback_handle));
pcm_close (kara->loopback_handle);
kara->loopback_handle = NULL;
}
}
return 0;
}
static int kara_open_micphone(struct kara_device *kara, struct audioCfg *cfg)
{
/* open alsa capture handle */
int card = 0;
int device = 0;
AM_LOGI("Enter");
if (!kara || !cfg) {
AM_LOGE("Input null pointer");
return -EINVAL;
}
pthread_mutex_lock(&kara->lock);
if (true == kara->karaoke_start) {
AM_LOGI("%s() linein karaoke is already opened!", __func__);
pthread_mutex_unlock(&kara->lock);
return 0;
}
int init_ret = -EINVAL;
struct voice_in *in = NULL;
struct pcm *pcmIn = NULL;
struct pcm_config config;
memset(&config, 0, sizeof(config));
/*the record parameter depends on pcm in config */
config.channels = LINEIN_DEFAULT_CHANNEL;
config.rate = LINEIN_DEFAULT_SAMPLE_RATE;
config.format = LINEIN_DEFAULT_FORMAT;
if (KARA_INPUT_TYPE_USB_MIC == kara->kara_input_type) {
config.period_size = USB_DEFAULT_PERIOD_SIZE;
config.period_count = USB_DEFAULT_PERIOD_COUNT;
} else if (KARA_INPUT_TYPE_LINEIN_MIC == kara->kara_input_type) {
config.period_size = LINEIN_DEFAULT_PERIOD_SIZE;
config.period_count = LINEIN_DEFAULT_PERIOD_COUNT;
} else {
AM_LOGE("unsupport input type: %d", kara->kara_input_type);
goto err;
}
AM_LOGD("channels %d, format %d, rate %d", config.channels, config.format, config.rate);
in = &kara->in;
/* in config for channel adjust and mix */
in->cfg.channelCnt = config.channels;
in->cfg.channelMask = AUDIO_CHANNEL_IN_STEREO;
in->cfg.sampleRate = config.rate;
in->cfg.format = AUDIO_FORMAT_PCM_16_BIT;
in->cfg.frame_size = in->cfg.channelCnt * pcm_format_to_bits(config.format) / 8;
in->debug = 0;
in->pcm_in_config = config;
kara->mixout_config = *cfg;
kara->mixout_config.channelMask = AUDIO_CHANNEL_IN_STEREO;
if (KARA_INPUT_TYPE_USB_MIC == kara->kara_input_type) {
card = alsa_device_get_card_index_by_name("USB Audio");
device = alsa_device_get_pcm_index_by_name("USB Audio");
} else if (KARA_INPUT_TYPE_LINEIN_MIC == kara->kara_input_type) {
card = alsa_device_get_card_index();
device = alsa_device_update_pcm_index(PORT_I2S, CAPTURE);
}
pcmIn = pcm_open(card, device, PCM_IN, &config);
if (!pcm_is_ready(pcmIn)) {
AM_LOGE("pcm_is_ready error!");
if (pcmIn != NULL) {
pcm_close(pcmIn);
pcmIn = NULL;
}
init_ret = -1;
goto err;
}
in->pcm_handle = pcmIn;
in->conversion_buffer = NULL;
in->conversion_buffer_size = 0;
kara->buf = NULL;
kara->buf_len = 0;
/* init resample */
if (in->cfg.sampleRate != kara->mixout_config.sampleRate) {
kara->resample_config.aformat = in->cfg.format;
kara->resample_config.channels = kara->mixout_config.channelCnt; // do channel adjust before resample
kara->resample_config.input_sr = in->cfg.sampleRate;
kara->resample_config.output_sr = kara->mixout_config.sampleRate;
ALOGI("init resampler from in-samplerate(%d) to out-samplerate(%d) channels(%d) format(%d)\n",
kara->resample_config.input_sr, kara->resample_config.output_sr,
kara->resample_config.channels, kara->resample_config.aformat);
init_ret = aml_audio_resample_init((aml_audio_resample_t **)&kara->resample_handle, AML_AUDIO_SIMPLE_RESAMPLE, &kara->resample_config);
if (init_ret < 0) {
ALOGE("karaoke resample init error\n");
kara->resample_handle = NULL;
}
/* init resample buffer for saving data after resample */
init_ret = ring_buffer_init(&kara->resample_buffer, LINEIN_DEFAULT_PERIOD_SIZE * 32);
if (init_ret < 0) {
AM_LOGE("malloc resample_buffer error");
}
} else {
kara->resample_handle = NULL;
}
kara->resample_wait_count = 0;
init_ret = ring_buffer_init(&kara->mic_buffer, LINEIN_DEFAULT_PERIOD_SIZE * 32);
if (init_ret < 0) {
AM_LOGE("malloc mic buffer error");
}
AM_LOGI("in_configs: channels = %d, format = %d, rate = %d, frame_size = %d",
in->cfg.channelCnt, in->cfg.format, in->cfg.sampleRate, in->cfg.frame_size);
AM_LOGI("mixout_configs: channels = %d, format = %d, rate = %d, frame_size = %d",
cfg->channelCnt, cfg->format, cfg->sampleRate, cfg->frame_size);
/* init ALOOP handle for recording */
kara_open_aloop_handle(kara, cfg);
kara->karaoke_start = true;
pthread_mutex_unlock(&kara->lock);
AM_LOGI("success and exit");
return 0;
err:
AM_LOGI("fail and exit");
kara->karaoke_start = false;
pthread_mutex_unlock(&kara->lock);
return init_ret;
}
static int kara_close_micphone(struct kara_device *kara)
{
if (!kara) {
AM_LOGE("Input null pointer");
return -EINVAL;
}
struct voice_in *in = &kara->in;
AM_LOGI("enter");
pthread_mutex_lock(&kara->lock);
if (kara->karaoke_start == false) {
AM_LOGI("karaoke already closed");
pthread_mutex_unlock(&kara->lock);
return 0;
}
/* close mic handle*/
if (in->pcm_handle != NULL) {
pcm_close(in->pcm_handle);
in->pcm_handle = NULL;
} else {
// add close function for other input here
}
aml_audio_free(in->conversion_buffer);
in->conversion_buffer = NULL;
in->conversion_buffer_size = 0;
aml_audio_free(kara->buf);
kara->buf = NULL;
kara->buf_len = 0;
ring_buffer_release(&kara->mic_buffer);
kara->karaoke_start = false;
/* close resample handle */
if (kara->resample_handle) {
aml_audio_resample_close(kara->resample_handle);
kara->resample_handle = NULL;
}
kara->resample_wait_count = 0;
/* close loopback handle */
if (kara->loopback_handle) {
pcm_close(kara->loopback_handle);
kara->loopback_handle = NULL;
ring_buffer_release(&kara->resample_buffer);
kara->resample_wait_count = 0;
}
pthread_mutex_unlock(&kara->lock);
AM_LOGI("exit");
return 0;
}
int karaoke_check_mix_output(struct kara_device *kara, void *buffer, size_t bytes)
{
if (!kara || !buffer || 0 == bytes) {
AM_LOGE("parameter invalid");
return -EINVAL;
}
int ret = 0;
// AM_LOGI("karaoke_input_type=%d, karaoke_on=%d, karaoke_enable=%d, karaoke_start=%d",
// kara->kara_input_type, kara->karaoke_on, kara->karaoke_enable, kara->karaoke_start);
if (kara->karaoke_on && kara->karaoke_enable) {
if (!kara->karaoke_start && kara->open) {
ret = kara->open(kara, &kara->mixout_config);
if (ret < 0) {
AM_LOGE("karaoke open failed: %d", ret);
}
} else if (kara->karaoke_start && kara->mix) {
kara->mix(kara, buffer, bytes);
}
} else if (kara->karaoke_start && kara->close) {
kara->close(kara);
}
return 0;
}
int karaoke_get_audioCfg_from_ms12_info(struct audioCfg *cfg, struct aml_ms12_dec_info *ms12_info)
{
int ret = 0;
if (!cfg || !ms12_info) {
AM_LOGE("parameter invalid");
ret = -EINVAL;
return ret;
}
cfg->format = ms12_info->data_type;
cfg->channelCnt = ms12_info->output_ch;
cfg->sampleRate = ms12_info->output_sr;
cfg->frame_size = cfg->channelCnt *
pcm_format_to_bits(convert_audio_format_2_alsa_format(cfg->format)) / 8;
AM_LOGI("format=%d, channel=%d, sampleRate=%d", cfg->format, cfg->channelCnt, cfg->sampleRate);
return ret;
}
int karaoke_get_audioCfg_from_pcm_config(struct audioCfg *cfg, struct pcm_config *pcm_cfg)
{
int ret = 0;
if (!cfg || !pcm_cfg) {
AM_LOGE("parameter invalid");
ret = -EINVAL;
return ret;
}
cfg->channelCnt = pcm_cfg->channels;
cfg->sampleRate = pcm_cfg->rate;
cfg->format = AUDIO_FORMAT_PCM_16_BIT;
cfg->frame_size = cfg->channelCnt * pcm_format_to_bits(pcm_cfg->format) / 8;
AM_LOGI("format=%d, channel=%d, sampleRate=%d, frame_size=%d",
cfg->format, cfg->channelCnt, cfg->sampleRate, cfg->frame_size);
return ret;
}
/* karaoke init*/
int karaoke_init(struct kara_device *kara,
kara_input_type_t kara_input_type,
kara_output_type_t kara_output_type)
{
AM_LOGI("enter, kara_input_type=%d, kara_output_type=%d", kara_input_type, kara_output_type);
if (!kara) {
return -EINVAL;
}
int ret = -1;
pthread_mutex_init(&kara->lock, NULL);
kara->kara_output_type = kara_output_type;
/* distinguish different input open by kara_input_type*/
kara->kara_input_type = kara_input_type;
kara->open = kara_open_micphone;
kara->read = kara_read_mic_buffer;
kara->mix = kara_mix_micphone;
kara->close = kara_close_micphone;
kara->kara_mic_gain = 1.0f;
kara->mixout_config.channelCnt = 2;
kara->mixout_config.sampleRate = 48000;
kara->mixout_config.channelMask = AUDIO_CHANNEL_IN_STEREO;
kara->mixout_config.format = AUDIO_FORMAT_PCM_16_BIT;
kara->mixout_config.frame_size = kara->mixout_config.channelCnt *
pcm_format_to_bits(convert_audio_format_2_alsa_format(AUDIO_FORMAT_PCM_16_BIT)) / 8;;
/* init reverb handle */
if (!kara->reverb_handle) {
ret = AML_Reverb_Init(&kara->reverb_handle);
if (ret < 0) {
AM_LOGE("%s() int Reverb Error!", __func__);
return -EINVAL;
}
}
AM_LOGI("exit");
return 0;
}
/* karaoke close mic and free resource */
int karaoke_close(struct kara_device *kara)
{
AM_LOGI("enter");
int ret = 0;
if (!kara || !kara->close) {
ret = -ENOSYS;
return ret;
}
if (kara->karaoke_start) {
kara->close(kara);
}
AM_LOGI("exit");
return ret;
}
/* query karaoke input type */
kara_input_type_t karaoke_get_input_type(struct kara_device *kara)
{
if (!kara) {
return KARA_INPUT_TYPE_NONE;
}
AM_LOGI("type = %d", kara->kara_input_type);
return kara->kara_input_type;
}
/* set karaoke input type */
int karaoke_set_input_type(struct kara_device *kara, kara_input_type_t value)
{
AM_LOGI("value = %d", value);
if (!kara) {
return -EINVAL;
}
kara->kara_input_type = value;
return 0;
}
/* query karaoke output type */
kara_output_type_t karaoke_get_output_type(struct kara_device *kara)
{
if (!kara) {
return KARA_OUTPUT_TYPE_NONE;
}
AM_LOGI("type = %d", kara->kara_output_type);
return kara->kara_output_type;
}
/* set karaoke output type */
int karaoke_set_output_type(struct kara_device *kara, kara_output_type_t value)
{
AM_LOGI("value = %d", value);
if (!kara) {
return -EINVAL;
}
kara->kara_output_type = value;
return 0;
}
/* query karaoke is on or not*/
bool karaoke_get_on(struct kara_device *kara)
{
if (!kara) {
return false;
}
//AM_LOGI("value = %d", kara->karaoke_on);
return kara->karaoke_on;
}
/* set karaoke on ture or false*/
int karaoke_set_on(struct kara_device *kara, bool value)
{
AM_LOGI("value = %d", value);
if (!kara) {
return -EINVAL;
}
kara->karaoke_on = value;
return 0;
}
/* query karaoke is enabled or not*/
bool karaoke_get_enable(struct kara_device *kara)
{
if (!kara) {
return false;
}
//AM_LOGI("value = %d", kara->karaoke_enable);
return kara->karaoke_enable;
}
/* set karaoke enable ture or false*/
int karaoke_set_enable(struct kara_device *kara, bool value)
{
AM_LOGI("value = %d", value);
if (!kara) {
return -EINVAL;
}
kara->karaoke_enable = value;
return 0;
}
/* query karaoke is started or not*/
bool karaoke_get_start(struct kara_device *kara)
{
if (!kara) {
return false;
}
//AM_LOGI("value = %d", kara->karaoke_start);
return kara->karaoke_start;
}
/* set karaoke start ture or false*/
int karaoke_set_start(struct kara_device *kara, bool value)
{
AM_LOGI("value = %d", value);
if (!kara) {
return -EINVAL;
}
kara->karaoke_start = value;
return 0;
}
/* query karaoke record start or not*/
bool karaoke_get_mic_record(struct kara_device *kara)
{
if (!kara) {
return false;
}
AM_LOGI("value = %d", kara->kara_mic_record);
return kara->kara_mic_record;
}
/* set karaoke mic record start ture or false*/
int karaoke_set_mic_record(struct kara_device *kara, bool value)
{
AM_LOGI("value = %d", value);
if (!kara) {
return -EINVAL;
}
kara->kara_mic_record = value;
return 0;
}
/* query karaoke mic mute ture or false*/
bool karaoke_get_mic_mute(struct kara_device *kara)
{
if (!kara) {
return false;
}
AM_LOGI("value = %d", kara->kara_mic_mute);
return kara->kara_mic_mute;
}
/* set karaoke mic mute ture or false*/
int karaoke_set_mic_mute(struct kara_device *kara, bool value)
{
AM_LOGI("value = %d", value);
if (!kara) {
return -EINVAL;
}
kara->kara_mic_mute = value;
return 0;
}
/* query karaoke mic volume gain*/
float karaoke_get_mic_gain(struct kara_device *kara)
{
if (!kara) {
return false;
}
AM_LOGI("value = %f", kara->kara_mic_gain);
return kara->kara_mic_gain;
}
/* set karaoke mic gain*/
int karaoke_set_mic_gain(struct kara_device *kara, float value)
{
AM_LOGI("value = %f", value);
if (!kara) {
return -EINVAL;
}
kara->kara_mic_gain = value;
return 0;
}
/* query karaoke debug value */
bool karaoke_get_debug(struct kara_device *kara)
{
if (!kara) {
return false;
}
AM_LOGI("value = %d", kara->in.debug);
return kara->in.debug;
}
/* set karaoke debug value */
int karaoke_set_debug(struct kara_device *kara, bool value)
{
AM_LOGI("value = %d", value);
if (!kara) {
return -EINVAL;
}
kara->in.debug = value;
return 0;
}
/* get audio config for recording such as adev_open_input_stream */
int karaoke_get_config_by_record_type(struct kara_device *kara,
kara_record_type_t record_type,
struct audio_config *config)
{
AM_LOGI("record_type = %d", record_type);
if (!kara) {
return -EINVAL;
}
switch (record_type) {
case KARA_RECORD_TYPE_MIC_ORIGINAL:
config->sample_rate = kara->in.cfg.sampleRate;
config->format = kara->in.cfg.format;
config->channel_mask = kara->in.cfg.channelMask;
break;
case KARA_RECORD_TYPE_MIC_AFTER_VOLUME:
case KARA_RECORD_TYPE_MIC_AFTER_REVERB:
config->sample_rate = kara->in.cfg.sampleRate;
config->format = kara->in.cfg.format;
//default 2 when using echo_reference
config->channel_mask = AUDIO_CHANNEL_IN_STEREO;
break;
case KARA_RECORD_TYPE_MIC_AFTER_SW_MIX:
config->sample_rate = kara->mixout_config.sampleRate;
config->format = kara->mixout_config.format;
config->channel_mask = kara->mixout_config.channelMask;
break;
default:
//add other config here
AM_LOGW("record_type do not support");
return -1;
}
AM_LOGI("config->sample_rate(%d), config->format(%d), config->channel_mask(%x)",
config->sample_rate, config->format, config->channel_mask);
return 0;
}
int karaoke_manager_init(void ** manager)
{
struct kara_manager * pmanager = NULL;
int index = 0;
if (!manager) {
return -1;
}
pmanager = aml_audio_calloc(1, sizeof(struct kara_manager));
if (!pmanager) {
AM_LOGE("malloc fail!!!");
return -1;
}
//only support USB mic and Linein mic, so initialize both.
for (index = 0; index <= KARA_INPUT_TYPE_LINEIN_MIC; index ++) {
karaoke_init(&pmanager->in_device[index], index, KARA_OUTPUT_TYPE_SW_MIX);
}
*manager = pmanager;
return 0;
}
int karaoke_manager_release(void * manager)
{
struct kara_manager * pmanager = (struct kara_manager *)manager;
int index = 0;
if (!manager) {
return -1;
}
for (index = 0; index <= KARA_INPUT_TYPE_LINEIN_MIC; index ++) {
karaoke_close(&pmanager->in_device[index]);
}
aml_audio_free(manager);
return 0;
}
int karaoke_manager_process(void * manager, const void *buffer, size_t bytes, audio_format_t format)
{
struct kara_manager * pmanager = (struct kara_manager *)manager;
int index = 0;
if (!manager) {
return -1;
}
if (format != pmanager->in_device[KARA_INPUT_TYPE_USB_MIC].mixout_config.format ||
format != pmanager->in_device[KARA_INPUT_TYPE_LINEIN_MIC].mixout_config.format) {
int index = 0;
for (index = 0; index <= KARA_INPUT_TYPE_LINEIN_MIC; index ++) {
pmanager->in_device[index].mixout_config.channelCnt = 2;
pmanager->in_device[index].mixout_config.sampleRate = 48000;
pmanager->in_device[index].mixout_config.channelMask = AUDIO_CHANNEL_IN_STEREO;
pmanager->in_device[index].mixout_config.format = format;
pmanager->in_device[index].mixout_config.frame_size = pmanager->in_device[index].mixout_config.channelCnt *
pcm_format_to_bits(convert_audio_format_2_alsa_format(format)) / 8;
}
}
for (index = 0; index <= KARA_INPUT_TYPE_LINEIN_MIC; index ++) {
karaoke_check_mix_output(&pmanager->in_device[index], buffer, bytes);
}
return 0;
}
int karaoke_manager_command(void * manager, char *cmd, int length)
{
struct kara_manager * pmanager = (struct kara_manager *)manager;
int index = 0;
char* sub_cmd = NULL;
int val = 0;
char *pt = NULL, *tmp = NULL;
int i = 0;
if (!manager || !cmd) {
return -1;
}
pt = strtok_r (cmd, ", ", &tmp);
while (pt != NULL) {
switch (i) {
case 0:
sub_cmd = pt;
break;
case 1:
index = atoi(pt);
break;
case 2:
val = atoi(pt);
break;
default:
break;
}
pt = strtok_r (NULL, ", ", &tmp);
i++;
}
sscanf(cmd, "%s,%d,%d", sub_cmd, &index, &val);
AM_LOGI("cmd:%s, index:%d, val:%d", sub_cmd, index, val);
if (KARA_INPUT_TYPE_USB_MIC != index && KARA_INPUT_TYPE_LINEIN_MIC != index) {
AM_LOGE("unsupport type:%d", index);
return -1;
}
if (!strncmp(sub_cmd, "switch", 6)) {
bool karaoke_on = !!val;
karaoke_set_on(&pmanager->in_device[index], karaoke_on);
karaoke_set_enable(&pmanager->in_device[index], karaoke_on);
} else if(!strncmp(sub_cmd, "debug", 5)) {
bool kara_debug = !!val;
karaoke_set_debug(&pmanager->in_device[index], kara_debug);
} else if(!strncmp(sub_cmd, "mic_mute", 8)) {
bool mic_mute = !!val;
karaoke_set_mic_mute(&pmanager->in_device[index], mic_mute);
} else if(!strncmp(sub_cmd, "mic_volume", 10)) {
float volume = (float)val/100;
karaoke_set_mic_gain(&pmanager->in_device[index], volume);
} else if(!strncmp(sub_cmd, "reverb_enable", 13)) {
bool reverb_enable = !!val;
pmanager->in_device[index].reverb_enable = reverb_enable;
} else if(!strncmp(sub_cmd, "reverb_mode", 11)) {
pmanager->in_device[index].reverb_mode = val;
} else {
AM_LOGE("unsupport command!");
}
return 0;
}
int karaoke_manager_dump(void * manager, int fd)
{
struct kara_manager * pmanager = (struct kara_manager *)manager;
struct kara_device * pdevice = NULL;
int i = 0;
if (!manager) {
return -1;
}
dprintf(fd, "\n-------------[AML_HAL] karaoke manager info------------------\n");
for (i = 0; i <= KARA_INPUT_TYPE_LINEIN_MIC; i ++) {
pdevice = &pmanager->in_device[i];
dprintf(fd, " [AML_HAL] ==== input type: %d ====\n", i);
dprintf(fd, " [AML_HAL] output type: %d\n", pdevice->kara_output_type);
dprintf(fd, " [AML_HAL] on(%d) enable(%d) start(%d) record(%d)\n",
pdevice->karaoke_on, pdevice->karaoke_enable, pdevice->karaoke_start, pdevice->kara_mic_record);
dprintf(fd, " [AML_HAL] mute(%d) gain(%5f)\n", pdevice->kara_mic_mute, pdevice->kara_mic_gain);
if (pdevice->in.pcm_handle)
aml_alsa_pcm_info_dump(pdevice->in.pcm_handle, fd);
dprintf(fd, " [AML_HAL] reverb enable(%d) mode(%d)\n\n", pdevice->reverb_enable, pdevice->reverb_mode);
}
return 0;
}