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