/* * Copyright (C) 2018 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. */ #define LOG_TAG "audio_hw_hal_submixing" //#define LOG_NDEBUG 0 #define DEBUG_DUMP 0 #define __USE_GNU #include "aml_audio_log.h" #include #include #include #include #include #include #include #include #include #ifndef NO_AUDIO_CAP #include #endif #include "amlAudioMixer.h" #include "audio_hw_utils.h" #include "hw_avsync.h" #include "audio_hwsync.h" #include "audio_data_process.h" #include "audio_virtual_buf.h" #include "audio_hw.h" #include "aml_audio_timer.h" #include "aml_malloc_debug.h" #include "audio_hw_resource_mgr.h" #include "aml_dump_debug.h" #include "aml_audio_output.h" #include "aml_audio_limiter.h" #define DUMP_AUDIOMIXER_INPORT_READ 0x1 #define DUMP_AUDIOMIXER_OUTDUMP 0x2 #define AML_LIMITER_RELEASE_TIME (100) static int mixer_update_limiter_gain(struct amlAudioMixer *audio_mixer); static int get_audiomixer_dump_enable(int dump_type) { int value = 0; value = get_debug_value(AML_DUMP_AUDIOHAL_SUBMIXING); return (value & dump_type); } enum { INPORT_NORMAL, // inport not underrun INPORT_UNDERRUN, //inport doesn't have data, underrun may happen later INPORT_FEED_SILENCE_DONE, //underrun will happen quickly, we feed some silence data to avoid noise }; //simple mixer support: 2 in , 1 out struct amlAudioMixer { input_port *in_ports[NR_INPORTS]; uint32_t inportsMasks; // records of inport IDs uint32_t inportsAvailMasks; // 1<< NR_INPORTS - 1 output_port *out_ports[MIXER_OUTPUT_PORT_NUM]; pthread_mutex_t outport_locks[MIXER_OUTPUT_PORT_NUM]; pthread_mutex_t inport_lock; pthread_t out_mixer_tid; pthread_mutex_t lock; int exit_thread; int mixing_enable; aml_mixer_state state; struct aml_audio_device *adev; bool continuous_output; int submix_standby; //current status bool enable_standby;//This variable represents whether it is possible to enter standby mode /*multich pcm output*/ bool mc_out_enable; port_state mc_out_status; aml_pcm_mixing_st multich_mixer; aml_pcm_downmix_st pcm_downmix; void *mixer_limiter; float pre_gain, last_pre_gain; /*end*/ }; int mixer_set_state(struct amlAudioMixer *audio_mixer, aml_mixer_state state) { audio_mixer->state = state; return 0; } int mixer_set_continuous_output(struct amlAudioMixer *audio_mixer, bool continuous_output) { audio_mixer->continuous_output = continuous_output; return 0; } bool mixer_is_continuous_enabled(struct amlAudioMixer *audio_mixer) { return audio_mixer->continuous_output; } aml_mixer_state mixer_get_state(struct amlAudioMixer *audio_mixer) { return audio_mixer->state; } /** * Returns the first initialized port according to pMasks and resets * the corresponding bit. Can be called repeatedly to iterate over * initialized ports. */ static inline input_port *mixer_get_inport_by_mask_right_first( struct amlAudioMixer *audio_mixer, uint32_t *pMasks) { uint8_t bit_position = get_bit_position_in_mask(NR_INPORTS - 1, pMasks); return audio_mixer->in_ports[bit_position]; } /** * Returns the index of the first available supported port. */ static unsigned int mixer_get_available_inport_index(struct amlAudioMixer *audio_mixer) { unsigned int index = 0; AM_LOGD("+inportsAvailMasks: %#x", audio_mixer->inportsAvailMasks); index = __builtin_ctz(audio_mixer->inportsAvailMasks); audio_mixer->inportsAvailMasks &= ~(1 << index); AM_LOGD("-inportsAvailMasks:%#x, index %d", audio_mixer->inportsAvailMasks, index); return index; } int init_mixer_input_port(struct amlAudioMixer *audio_mixer, struct audio_config *config, audio_output_flags_t flags, int (*on_notify_cbk)(void *data), void *notify_data, int (*on_input_avail_cbk)(void *data), void *input_avail_data, float volume) { R_CHECK_POINTER_LEGAL(-EINVAL, audio_mixer, ""); R_CHECK_POINTER_LEGAL(-EINVAL, config, ""); R_CHECK_POINTER_LEGAL(-EINVAL, notify_data, ""); input_port *in_port = NULL; int port_index = -1; struct aml_stream_out *aml_out = notify_data; bool direct_on = false; if (aml_out->inputPortID != -1) { AM_LOGW("stream input port id:%d exits delete it.", aml_out->inputPortID); delete_mixer_input_port(audio_mixer, aml_out->inputPortID); } port_index = mixer_get_available_inport_index(audio_mixer); R_CHECK_PARAM_LEGAL(-1, port_index, 0, NR_INPORTS - 1, ""); /* if direct on, ie. the ALSA buffer is full, no need padding data anymore */ direct_on = (audio_mixer->in_ports[AML_MIXER_INPUT_PORT_PCM_DIRECT] != NULL); in_port = new_input_port(MIXER_FRAME_COUNT, config, flags, volume, direct_on); if (audio_mixer->in_ports[port_index] != NULL) { AM_LOGW("inport index:[%d]%s already exists! recreate", port_index, mixerInputType2Str(port_index)); free_input_port(audio_mixer->in_ports[port_index]); } in_port->ID = port_index; AM_LOGI("input port:%s, size %d frames, frame_write_sum:%" PRId64 "", mixerInputType2Str(in_port->enInPortType), MIXER_FRAME_COUNT, aml_out->frame_write_sum); audio_mixer->in_ports[port_index] = in_port; audio_mixer->inportsMasks |= 1 << port_index; aml_out->inputPortID = port_index; set_port_notify_cbk(in_port, on_notify_cbk, notify_data); set_port_input_avail_cbk(in_port, on_input_avail_cbk, input_avail_data); in_port->initial_frames = aml_out->frame_write_sum; return 0; } int delete_mixer_input_port(struct amlAudioMixer *audio_mixer, int port_index) { R_CHECK_POINTER_LEGAL(-EINVAL, audio_mixer, ""); R_CHECK_PARAM_LEGAL(-EINVAL, port_index, 0, NR_INPORTS - 1, ""); input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); AM_LOGI("input port ID:%d, type:%s, cur mask:%#x", port_index, mixerInputType2Str(in_port->enInPortType), audio_mixer->inportsMasks); pthread_mutex_lock(&audio_mixer->lock); pthread_mutex_lock(&audio_mixer->inport_lock); free_input_port(in_port); audio_mixer->in_ports[port_index] = NULL; audio_mixer->inportsMasks &= ~(1 << port_index); audio_mixer->inportsAvailMasks |= 1 << port_index; pthread_mutex_unlock(&audio_mixer->inport_lock); pthread_mutex_unlock(&audio_mixer->lock); return 0; } int send_mixer_inport_message(struct amlAudioMixer *audio_mixer, uint8_t port_index, PORT_MSG msg) { input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return send_inport_message(in_port, msg); } int send_mixer_outport_message(struct amlAudioMixer *audio_mixer, uint8_t port_index, PORT_MSG msg, void *info, int info_len) { output_port *out_port = audio_mixer->out_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, out_port, "port_index:%d", port_index); return send_outport_message(out_port, msg, info, info_len); } uint32_t get_mixer_inport_consumed_frames(struct amlAudioMixer *audio_mixer, uint8_t port_index) { input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return get_inport_consumed_size(in_port) / in_port->cfg.frame_size; } int set_mixer_inport_volume(struct amlAudioMixer *audio_mixer, uint8_t port_index, float vol) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); R_CHECK_PARAM_LEGAL(-1, port_index, 0, NR_INPORTS - 1, ""); input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); if (vol < 0) { AM_LOGE("invalid vol %f", vol); return -EINVAL; } set_inport_volume(in_port, vol); return 0; } float get_mixer_inport_volume(struct amlAudioMixer *audio_mixer, uint8_t port_index) { input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return get_inport_volume(in_port); } int set_mixer_inport_mute(struct amlAudioMixer *audio_mixer, uint8_t port_index, bool mute) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); R_CHECK_PARAM_LEGAL(-1, port_index, 0, NR_INPORTS - 1, ""); input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); if (in_port->port_mute != mute) { set_inport_mute(in_port, mute); } return 0; } int mixer_write_inport(struct amlAudioMixer *audio_mixer, uint8_t port_index, const void *buffer, int bytes) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); R_CHECK_PARAM_LEGAL(-1, port_index, 0, NR_INPORTS - 1, ""); input_port *in_port = audio_mixer->in_ports[port_index]; int written = 0; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); written = in_port->write(in_port, buffer, bytes); if (get_inport_state(in_port) != ACTIVE) { AM_LOGI("input port:%s is active now", mixerInputType2Str(in_port->enInPortType)); set_inport_state(in_port, ACTIVE); } AM_LOGV("portIndex %d", port_index); return written; } int mixer_read_inport(struct amlAudioMixer *audio_mixer, uint8_t port_index, void *buffer, int bytes) { R_CHECK_POINTER_LEGAL(0, audio_mixer, ""); R_CHECK_PARAM_LEGAL(0, port_index, 0, NR_INPORTS - 1, ""); input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return in_port->read(in_port, buffer, bytes); } int mixer_set_inport_state(struct amlAudioMixer *audio_mixer, uint8_t port_index, port_state state) { input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return set_inport_state(in_port, state); } port_state mixer_get_inport_state(struct amlAudioMixer *audio_mixer, uint8_t port_index) { input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return get_inport_state(in_port); } static inline output_port *mixer_get_cur_outport_with_lock(struct amlAudioMixer *audio_mixer, MIXER_OUTPUT_PORT port_index) { output_port *out_port = NULL; R_CHECK_PARAM_LEGAL(NULL, port_index, MIXER_OUTPUT_PORT_STEREO_PCM, MIXER_OUTPUT_PORT_NUM - 1, ""); R_CHECK_POINTER_LEGAL(NULL, audio_mixer, ""); pthread_mutex_lock(&audio_mixer->outport_locks[port_index]); out_port = audio_mixer->out_ports[port_index]; if (out_port == NULL) { AM_LOGE("out_port is null"); pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); return NULL; } return out_port; } void release_cur_outport_lock(struct amlAudioMixer *audio_mixer, MIXER_OUTPUT_PORT port_index) { R_CHECK_POINTER_LEGAL((void)0, audio_mixer, ""); R_CHECK_PARAM_LEGAL((void)0, port_index, MIXER_OUTPUT_PORT_STEREO_PCM, MIXER_OUTPUT_PORT_NUM - 1, ""); pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); } size_t get_outport_data_avail(output_port *outport) { return outport->bytes_avail; } int set_outport_data_avail(output_port *outport, size_t avail) { R_CHECK_POINTER_LEGAL(0, outport, ""); if (avail > outport->data_buf_len) { AM_LOGE("invalid avail %zu", avail); return -EINVAL; } outport->bytes_avail = avail; return 0; } int init_mixer_output_port(struct amlAudioMixer *audio_mixer, MIXER_OUTPUT_PORT output_type, struct audioCfg *config, size_t buf_frames) { R_CHECK_PARAM_LEGAL(-1, output_type, MIXER_OUTPUT_PORT_STEREO_PCM, MIXER_OUTPUT_PORT_NUM - 1, ""); struct aml_audio_device *adev = audio_mixer->adev; pthread_mutex_lock(&audio_mixer->outport_locks[output_type]); AM_LOGI("output port:%s", mixerOutputType2Str(output_type)); output_port *out_port = new_output_port(output_type, config, buf_frames); if (out_port == NULL) { AM_LOGW("new_output_port fail"); pthread_mutex_unlock(&audio_mixer->outport_locks[output_type]); return -1; } audio_mixer->out_ports[output_type] = out_port; if (config->channelCnt > 2) { aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_I2S2HDMI_FORMAT, AML_MULTI_CH_LPCM); } else { aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_FORMAT, AML_STEREO_PCM); } pthread_mutex_unlock(&audio_mixer->outport_locks[output_type]); return 0; } int delete_mixer_output_port(struct amlAudioMixer *audio_mixer, MIXER_OUTPUT_PORT port_index) { R_CHECK_PARAM_LEGAL(-1, port_index, MIXER_OUTPUT_PORT_STEREO_PCM, MIXER_OUTPUT_PORT_NUM - 1, ""); struct aml_audio_device *adev = audio_mixer->adev; AM_LOGI("output port:%s", mixerOutputType2Str(port_index)); pthread_mutex_lock(&audio_mixer->outport_locks[port_index]); output_port *out_port = audio_mixer->out_ports[port_index]; if (out_port == NULL) { AM_LOGW("out_port is null"); pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); return -1; } free_output_port(out_port); audio_mixer->out_ports[port_index] = NULL; pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); aml_mixer_ctrl_set_int(&adev->alsa_mixer, AML_MIXER_ID_SPDIF_FORMAT, AML_STEREO_PCM); return 0; } static int mixer_output_startup(struct amlAudioMixer *audio_mixer) { output_port *out_port = NULL; MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; out_port = mixer_get_cur_outport_with_lock(audio_mixer, port_index); if (out_port) { AM_LOGI("output port:%s", mixerOutputType2Str(port_index)); out_port->start(out_port); release_cur_outport_lock(audio_mixer, port_index); } audio_mixer->submix_standby = 0; return 0; } int mixer_output_standby(struct amlAudioMixer *audio_mixer) { ALOGI("[%s:%d] request sleep thread", __func__, __LINE__); (void)audio_mixer; //audio_mixer->run_state = AML_AUDIO_MIXER_RUN_STATE_REQ_SLEEP; return 0; } int mixer_enable_standby(struct amlAudioMixer *audio_mixer, bool enable_standby) { if (audio_mixer) { audio_mixer->enable_standby = enable_standby; } return 0; } static int mixer_thread_sleep(struct amlAudioMixer *audio_mixer) { output_port *out_port = NULL; MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; out_port = mixer_get_cur_outport_with_lock(audio_mixer, port_index); if (out_port) { AM_LOGI("output port:%s", mixerOutputType2Str(port_index)); if (false == audio_mixer->submix_standby) { ALOGI("[%s:%d] start going to standby", __func__, __LINE__); out_port->standby(out_port); audio_mixer->submix_standby = true; } release_cur_outport_lock(audio_mixer, port_index); } return 0; } int mixer_output_dummy(struct amlAudioMixer *audio_mixer, bool en) { output_port *out_port = NULL; MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; out_port = mixer_get_cur_outport_with_lock(audio_mixer, port_index); if (out_port) { AM_LOGI("output port:%s, en:%d", mixerOutputType2Str(port_index), en); outport_set_dummy(out_port, en); release_cur_outport_lock(audio_mixer, port_index); } return 0; } static int mixer_output_write(struct amlAudioMixer *audio_mixer) { output_port *out_port = NULL; MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; out_port = mixer_get_cur_outport_with_lock(audio_mixer, port_index); /* stereo pcm output */ if (out_port) { while (out_port->bytes_avail > 0) { if (audio_mixer->submix_standby) { pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); mixer_output_startup(audio_mixer); pthread_mutex_lock(&audio_mixer->outport_locks[port_index]); } out_port->write(out_port, out_port->data_buf, out_port->bytes_avail); set_outport_data_avail(out_port, 0); }; release_cur_outport_lock(audio_mixer, port_index); } /* output multi-ch pcm */ port_index = MIXER_OUTPUT_PORT_MULTI_PCM; pthread_mutex_lock(&audio_mixer->outport_locks[port_index]); output_port * mc_out_port = audio_mixer->out_ports[port_index]; if (mc_out_port && mc_out_port->bytes_avail > 0) { mc_out_port->write(mc_out_port, mc_out_port->data_buf, mc_out_port->bytes_avail); mc_out_port->bytes_avail = 0; } pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); return 0; } #define DEFAULT_KERNEL_FRAMES (DEFAULT_PLAYBACK_PERIOD_SIZE*DEFAULT_PLAYBACK_PERIOD_CNT) static int mixer_update_tstamp(struct amlAudioMixer *audio_mixer) { output_port *out_port = NULL; input_port *in_port = NULL; unsigned int avail; uint32_t masks = audio_mixer->inportsMasks; while (masks) { in_port = mixer_get_inport_by_mask_right_first(audio_mixer, &masks); if (NULL != in_port && in_port->enInPortType == AML_MIXER_INPUT_PORT_PCM_SYSTEM) { break; } } /*only deal with system audio */ if (in_port == NULL) { AM_LOGV("in_port:%p is null", in_port); return 0; } struct aml_audio_device *adev = audio_mixer->adev; MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; out_port = mixer_get_cur_outport_with_lock(audio_mixer, port_index); if (out_port) { if (aml_audio_pcm_out_get_timestamp(adev, &avail, &in_port->timestamp) == 0) { size_t kernel_buf_size = DEFAULT_KERNEL_FRAMES; int64_t signed_frames = in_port->mix_consumed_frames - kernel_buf_size + avail; if (signed_frames < 0) { signed_frames = 0; } in_port->presentation_frames = in_port->initial_frames + signed_frames; AM_LOGV("present frames:%" PRId64 ", initial %" PRId64 ", consumed %" PRId64 ", sec:%ld, nanosec:%ld", in_port->presentation_frames, in_port->initial_frames, in_port->mix_consumed_frames, in_port->timestamp.tv_sec, in_port->timestamp.tv_nsec); } release_cur_outport_lock(audio_mixer, port_index); } return 0; } float get_fade_step_by_size(int fade_size, int frame_size) { return 1.0/(fade_size/frame_size); } int init_fade(struct fade_out *fade_out, int fade_size, int sample_size, int channel_cnt) { fade_out->vol = 1.0; fade_out->target_vol = 0; fade_out->fade_size = fade_size; fade_out->sample_size = sample_size; fade_out->channel_cnt = channel_cnt; fade_out->stride = get_fade_step_by_size(fade_size, sample_size * channel_cnt); AM_LOGI("size %d, stride %f", fade_size, fade_out->stride); return 0; } int process_fade_out(void *buf, int bytes, struct fade_out *fout) { int i = 0; int frame_cnt = bytes / fout->sample_size / fout->channel_cnt; int16_t *sample = (int16_t *)buf; if (fout->channel_cnt != 2 || fout->sample_size != 2) AM_LOGE("not support yet"); AM_LOGI("++++fade out vol %f, size %d", fout->vol, fout->fade_size); for (i = 0; i < frame_cnt; i++) { sample[i] = sample[i]*fout->vol; sample[i+1] = sample[i+1]*fout->vol; fout->vol -= fout->stride; if (fout->vol < 0) fout->vol = 0; } fout->fade_size -= bytes; AM_LOGI("----fade out vol %f, size %d", fout->vol, fout->fade_size); return 0; } static int update_inport_avail(input_port *in_port) { in_port->mix_consumed_frames += in_port->data_buf_frame_cnt; in_port->data_valid = 1; return 0; } static void process_port_msg(input_port *in_port) { port_message *msg = get_inport_message(in_port); if (msg) { AM_LOGI("msg: %s", port_msg_to_str(msg->msg_what)); switch (msg->msg_what) { case MSG_PAUSE: { struct aml_stream_out *out = (struct aml_stream_out *)in_port->notify_cbk_data; if ((out->avsync_ctx) && (out->avsync_ctx->mediasync_ctx)) { mediasync_wrap_setPause(out->avsync_ctx->mediasync_ctx->handle, true); } set_inport_state(in_port, PAUSING); break; } case MSG_FLUSH: set_inport_state(in_port, FLUSHING); break; case MSG_RESUME: { set_inport_state(in_port, RESUMING); break; } default: AM_LOGE("not support"); } remove_inport_message(in_port, msg); } } int mixer_pause_inport(struct amlAudioMixer *audio_mixer, uint8_t port_index) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); R_CHECK_PARAM_LEGAL(-1, port_index, 0, NR_INPORTS - 1, ""); input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return send_inport_message(in_port, MSG_PAUSE); } int mixer_resume_inport(struct amlAudioMixer *audio_mixer, uint8_t port_index) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); R_CHECK_PARAM_LEGAL(-1, port_index, 0, NR_INPORTS - 1, ""); input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return send_inport_message(in_port, MSG_RESUME); } int mixer_flush_inport(struct amlAudioMixer *audio_mixer, uint8_t port_index) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); R_CHECK_PARAM_LEGAL(-1, port_index, 0, NR_INPORTS - 1, ""); input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return send_inport_message(in_port, MSG_FLUSH); } int mixer_seek_inport(struct amlAudioMixer *audio_mixer, uint8_t port_index, int bytes) { input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); return seek_input_port(in_port, bytes); } static int mixer_inports_read(struct amlAudioMixer *audio_mixer) { unsigned int masks = audio_mixer->inportsMasks; AM_LOGV("+++"); mixer_update_limiter_gain(audio_mixer); while (masks) { input_port *in_port = NULL; in_port = mixer_get_inport_by_mask_right_first(audio_mixer, &masks); if (NULL == in_port) { continue; } int ret = 0, fade_out = 0, fade_in = 0; aml_mixer_input_port_type_e type = in_port->enInPortType; process_port_msg(in_port); port_state state = get_inport_state(in_port); if (type == AML_MIXER_INPUT_PORT_PCM_DIRECT) { //if in pausing states, don't retrieve data if (state == PAUSING) { fade_out = 1; } else if (state == RESUMING) { fade_in = 1; AM_LOGI("input port:%s resume", mixerInputType2Str(type)); set_inport_state(in_port, ACTIVE); } else if (state == FLUSHING) { reset_input_port(in_port); AM_LOGI("input port:%s flushing->flushed", mixerInputType2Str(type)); set_inport_state(in_port, FLUSHED); continue; } else if (state == STOPPED || state == PAUSED || state == FLUSHED) { AM_LOGV("input port:%s stopped, paused or flushed", mixerInputType2Str(type)); continue; } if (get_inport_state(in_port) == ACTIVE && in_port->data_valid) { AM_LOGI("input port:%s data already valid", mixerInputType2Str(type)); continue; } } else { if (in_port->data_valid) { AM_LOGI("input port ID:%d port:%s, data already valid", in_port->ID, mixerInputType2Str(type)); continue; } } int input_avail_size = in_port->rbuf_avail(in_port); AM_LOGV("input port:%s, portId:%d, avail:%d, masks:%#x, inportsMasks:%#x, data_len_bytes:%zu", mixerInputType2Str(type), in_port->ID, input_avail_size, masks, audio_mixer->inportsMasks, in_port->data_len_bytes); if (input_avail_size >= in_port->data_len_bytes) { if (in_port->first_read) { if (input_avail_size < in_port->inport_start_threshold) { continue; } else { AM_LOGI("input port:%s first start, portId:%d, avail:%d", mixerInputType2Str(type), in_port->ID, input_avail_size); in_port->first_read = false; } } ret = mixer_read_inport(audio_mixer, in_port->ID, in_port->data, in_port->data_len_bytes); if (ret == (int)in_port->data_len_bytes) { apply_volume_fade(in_port->last_volume, in_port->volume, in_port->data, audio_bytes_per_sample(in_port->cfg.format), in_port->cfg.channelCnt, in_port->data_len_bytes); if (get_audiomixer_dump_enable(DUMP_AUDIOMIXER_INPORT_READ)) { char file_name[128] = { 0 }; snprintf(file_name, 128, "%s_%d.pcm", "inport_read_before_mute_process", in_port->ID); aml_dump_audio_bitstreams(file_name, in_port->data, in_port->data_len_bytes); } aml_audio_mute_process(&in_port->mute_process_handle, in_port->data, in_port->data_len_bytes); in_port->last_volume = in_port->volume; if (fade_out) { AM_LOGI("output port:%s fade out, pausing->paused", mixerInputType2Str(type)); audio_fade_func(in_port->data, ret, 0); set_inport_state(in_port, PAUSED); } else if (fade_in) { AM_LOGI("input port:%s fade in", mixerInputType2Str(type)); audio_fade_func(in_port->data, ret, 1); set_inport_state(in_port, ACTIVE); } update_inport_avail(in_port); if (get_audiomixer_dump_enable(DUMP_AUDIOMIXER_INPORT_READ)) { char file_name[128] = { 0 }; snprintf(file_name, 128, "%s_%d.pcm", "inport_read", in_port->ID); aml_dump_audio_bitstreams(file_name, in_port->data, in_port->data_len_bytes); } } else { AM_LOGW("port:%s read fail, have read:%d Byte, need %zu Byte", mixerInputType2Str(type), ret, in_port->data_len_bytes); } } else { struct aml_audio_device *adev = audio_mixer->adev; if (adev->debug_flag) { AM_LOGD("port:%d ring buffer data is not enough", in_port->ID); } } } return 0; } int mixer_need_wait_forever(struct amlAudioMixer *audio_mixer) { return mixer_get_state(audio_mixer) != MIXER_INPORTS_READY; } static inline int16_t CLIP16(int r) { return (r > 0x7fff) ? 0x7fff : (r < -0x8000) ? 0x8000 : r; } static int mixer_add_mixing_data(struct amlAudioMixer *audio_mixer, void *input, input_port *in_port) { aml_pcm_mixing_st *p_multich_mixer = &audio_mixer->multich_mixer; if (in_port->data_buf_frame_cnt < MIXER_FRAME_COUNT) { AM_LOGE("input port type:%s buf frames:%zu too small", mixerInputType2Str(in_port->enInPortType), in_port->data_buf_frame_cnt); return -EINVAL; } do_mixing_multi_ch(p_multich_mixer, input, MIXER_FRAME_COUNT, &in_port->cfg); in_port->data_valid = 0; AM_LOGV("input port ID:%d channels:%d", in_port->ID, in_port->cfg.channelCnt); return 0; } static int init_multich_mixer(struct amlAudioMixer *audio_mixer, struct audioCfg *p_mixer_cfg) { int ret = 0; struct audioCfg mixer_cfg; R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); R_CHECK_POINTER_LEGAL(-1, p_mixer_cfg, ""); memcpy(&mixer_cfg, p_mixer_cfg, sizeof(*p_mixer_cfg)); mixer_cfg.format = p_mixer_cfg->format; mixer_cfg.channelCnt = 8; mixer_cfg.channelMask = AUDIO_CHANNEL_OUT_7POINT1; mixer_cfg.frame_size = mixer_cfg.channelCnt * audio_bytes_per_sample(mixer_cfg.format); AM_LOGI("[Submix]init_multich_mixer---format:%x ch:%d frame_size:%d", mixer_cfg.format, mixer_cfg.channelCnt, mixer_cfg.frame_size); ret = init_aml_pcm_mixer(&audio_mixer->multich_mixer, &mixer_cfg, MIXER_FRAME_COUNT); if (ret < 0) { deinit_aml_pcm_mixer(&audio_mixer->multich_mixer); } return ret; } static int deinit_multich_mixer(struct amlAudioMixer *audio_mixer) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); deinit_aml_pcm_mixer(&audio_mixer->multich_mixer); return 0; } void mixer_enable_multich_output(struct amlAudioMixer *audio_mixer, bool enable) { output_port *mc_out_port = NULL; const MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_MULTI_PCM; R_CHECK_POINTER_LEGAL((void)0, audio_mixer, ""); if (audio_mixer->mc_out_enable == enable) { return; } AM_LOGI("mc_out_enable %d", enable); pthread_mutex_lock(&audio_mixer->outport_locks[port_index]); audio_mixer->mc_out_enable = enable; mc_out_port = audio_mixer->out_ports[port_index]; if (!enable && mc_out_port) { /* close multich alsa handle, then npcm can use it. */ if (mc_out_port->port_status != STOPPED) { mc_out_port->standby(mc_out_port); } free_mc_output_port(mc_out_port); audio_mixer->out_ports[port_index] = NULL; } pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); } static int deinit_mixer_limiter(struct amlAudioMixer *audio_mixer) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); if (audio_mixer->mixer_limiter) { aml_audio_limiter_free(audio_mixer->mixer_limiter ); audio_mixer->mixer_limiter = NULL; } return 0; } static int init_mixer_limiter(struct amlAudioMixer *audio_mixer) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); struct aml_audio_device *adev = (struct aml_audio_device *)adev_get_handle(); int ret = 0; audio_mixer->last_pre_gain = audio_mixer->pre_gain = 1.0f; audio_format_t format = adev->audio_process_bitwidth == 32 ? AUDIO_FORMAT_PCM_32_BIT : AUDIO_FORMAT_PCM_16_BIT; audio_mixer->mixer_limiter = aml_audio_limiter_new(format); if (!audio_mixer->mixer_limiter) { goto error; } ret = aml_audio_limiter_set(audio_mixer->mixer_limiter, audio_mixer->pre_gain, 1, AML_LIMITER_RELEASE_TIME, MIXER_FRAME_COUNT / 2, AML_LIMITER); if (ret) { goto error; } aml_audio_limiter_enable(audio_mixer->mixer_limiter, 1); AM_LOGI("Enable mixer_limiter, limiter peak is %f, release time is %d", audio_mixer->pre_gain, AML_LIMITER_RELEASE_TIME); return 0; error: deinit_mixer_limiter(audio_mixer); return ret; } static int mixer_update_limiter_gain(struct amlAudioMixer *audio_mixer) { R_CHECK_POINTER_LEGAL(-1, audio_mixer, ""); int ret = 0; int inport_count = get_mixer_inport_count(audio_mixer); if (inport_count && fabsf(1.0f / inport_count - audio_mixer->pre_gain) > FLOAT_ZERO) { audio_mixer->pre_gain = 1.0f / inport_count; ret = aml_audio_limiter_set(audio_mixer->mixer_limiter, audio_mixer->pre_gain, 1, AML_LIMITER_RELEASE_TIME, MIXER_FRAME_COUNT / 2, AML_LIMITER); AM_LOGI("update limiter pre_gain: %f", audio_mixer->pre_gain); } return ret; } static int mixer_loudness_input_control(struct amlAudioMixer *audio_mixer, void *buffer, int bytes, struct audioCfg in_cfg) { if (bytes <= 0 || NULL == buffer) { AM_LOGE("Invalid param: bytes(%d) buffer(%p)", bytes, buffer); return -1; } int channels = in_cfg.channelCnt; int sample_size = audio_bytes_per_sample(in_cfg.format); apply_volume_fade(audio_mixer->last_pre_gain, audio_mixer->pre_gain, buffer, sample_size, channels, bytes); if (audio_mixer->pre_gain != audio_mixer->last_pre_gain) { AM_LOGI(" pre_gain=%f last_pre_gain=%f channels=%d bytes=%d", audio_mixer->pre_gain , audio_mixer->last_pre_gain, channels, bytes); audio_mixer->last_pre_gain = audio_mixer->pre_gain; } return 0; } static int mixer_loudness_output_control(struct amlAudioMixer *audio_mixer,void *buffer, int bytes, struct audioCfg in_cfg) { void *loudness_limiter_handle = audio_mixer->mixer_limiter; int sample_size = audio_bytes_per_sample(in_cfg.format); if (!audio_mixer || !buffer || bytes <= 0) { AM_LOGE("Invalid param: bytes(%d) buffer(%p)", bytes, buffer); return -1; } if (get_audiomixer_dump_enable(DUMP_AUDIOMIXER_OUTDUMP)) { char before_dump_name[64] = {0}; snprintf(before_dump_name, 64, "mixed_before_limiter_8ch.pcm"); aml_dump_audio_bitstreams(before_dump_name, buffer, bytes); } int process_frames =bytes / sample_size / in_cfg.channelCnt; //MIXER_FRAME_COUNT aml_audio_limiter_process(loudness_limiter_handle, buffer, in_cfg.format, buffer, in_cfg.format, in_cfg.channelCnt, process_frames); if (get_audiomixer_dump_enable(DUMP_AUDIOMIXER_OUTDUMP)) { char after_dump_name[64] = {0}; snprintf(after_dump_name, 64, "mixed_after_limiter_8ch.pcm"); aml_dump_audio_bitstreams(after_dump_name, buffer, bytes); } return 0; } /* * 2ch pcm output always exist, multi-ch pcm output is additional and dependent with sink device. */ static int mixer_config_multich_output(struct amlAudioMixer *audio_mixer) { int ret = 0; uint32_t mixer_max_channels = 8; struct audioCfg *p_mixer_cfg = &audio_mixer->multich_mixer.cfg; const MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_MULTI_PCM; pthread_mutex_lock(&audio_mixer->outport_locks[port_index]); output_port *mc_out_port = audio_mixer->out_ports[port_index]; if (audio_mixer->mc_out_enable && mixer_max_channels != 2) { if (mc_out_port == NULL || mc_out_port->cfg.channelCnt != mixer_max_channels) { AM_LOGI("mc output channel change to %d", mixer_max_channels); if (mc_out_port != NULL) { free_mc_output_port(mc_out_port); audio_mixer->out_ports[port_index] = NULL; } mc_out_port = new_mc_output_port(p_mixer_cfg, MIXER_FRAME_COUNT); if (mc_out_port == NULL) { AM_LOGE("new_mc_output_port failed !"); pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); return -1; } audio_mixer->out_ports[port_index] = mc_out_port; mc_out_port->start(mc_out_port); } else if (mc_out_port->port_status != ACTIVE) { mc_out_port->start(mc_out_port); } }else if (mc_out_port) { if (mc_out_port->port_status != STOPPED) { mc_out_port->standby(mc_out_port); } mc_out_port->port_status = STOPPED; } if (mc_out_port) { audio_mixer->mc_out_status = mc_out_port->port_status; } else { audio_mixer->mc_out_status = STOPPED; } pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); return ret; } static int mixer_do_mixing(struct amlAudioMixer *audio_mixer) { input_port *in_port = NULL; output_port *out_port = NULL; char acFilePathStr[ENUM_TYPE_STR_MAX_LEN] = {0}; uint32_t masks = 0; aml_pcm_mixing_st *p_multich_mixer = &audio_mixer->multich_mixer; aml_pcm_downmix_st *p_downmix = &audio_mixer->pcm_downmix; MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; struct aml_audio_device* adev = audio_mixer->adev; audio_channel_mask_t mixed_data_valid_channelMask = AUDIO_CHANNEL_OUT_STEREO; out_port = mixer_get_cur_outport_with_lock(audio_mixer, port_index); if (!out_port) { AM_LOGE("out_port is NULL, need check!!!"); return -1; } release_cur_outport_lock(audio_mixer, port_index); mixer_config_multich_output(audio_mixer); if (p_multich_mixer->mixed_buf) { memset(p_multich_mixer->mixed_buf, 0, p_multich_mixer->mixed_buf_size); } masks = audio_mixer->inportsMasks; while (masks) { in_port = mixer_get_inport_by_mask_right_first(audio_mixer, &masks); if (NULL == in_port || 0 == in_port->data_valid) { continue; } mixed_data_valid_channelMask |= in_port->cfg.channelMask; if (get_audiomixer_dump_enable(DUMP_AUDIOMIXER_INPORT_READ)) { snprintf(acFilePathStr, ENUM_TYPE_STR_MAX_LEN, "%s_%d.pcm", "SubMix_before_mixer_inport", in_port->ID); aml_dump_audio_bitstreams(acFilePathStr, in_port->data, in_port->data_len_bytes); } mixer_loudness_input_control(audio_mixer, (void *)in_port->data, in_port->data_len_bytes, in_port->cfg); mixer_add_mixing_data(audio_mixer, in_port->data, in_port); } void *mixed_data_ptr = p_multich_mixer->mixed_buf; int mixed_data_size = p_multich_mixer->mixed_buf_size; if (get_audiomixer_dump_enable(DUMP_AUDIOMIXER_OUTDUMP)) { snprintf(acFilePathStr, ENUM_TYPE_STR_MAX_LEN, "%s_8ch.pcm", "SubMix_after_mixed"); aml_dump_audio_bitstreams(acFilePathStr, mixed_data_ptr, mixed_data_size); } mixer_loudness_output_control(audio_mixer, mixed_data_ptr, mixed_data_size, p_multich_mixer->cfg); /*AI Audio process*/ if (adev->aml_ai_audio_type != eAmlAiAudioUnsupported) { int sample_size = audio_bytes_per_sample(p_multich_mixer->cfg.format); int process_frames = mixed_data_size / sample_size / p_multich_mixer->cfg.channelCnt; int ret = aml_ai_audio_process(process_frames, p_multich_mixer->cfg.channelCnt, p_multich_mixer->cfg.format, mixed_data_valid_channelMask, mixed_data_ptr); if (ret != 0) { AM_LOGE("aml_ai_audio_process failed, ret=%d", ret); } } /* multich_output data */ pthread_mutex_lock(&audio_mixer->outport_locks[MIXER_OUTPUT_PORT_MULTI_PCM]); output_port *mc_out_port = audio_mixer->out_ports[MIXER_OUTPUT_PORT_MULTI_PCM]; if (mixed_data_ptr && mc_out_port && mc_out_port->port_status != STOPPED) { if (mixed_data_size > mc_out_port->data_buf_len) { AM_LOGE("mixed_data_size too large(%d > %zu), truncate", mixed_data_size, mc_out_port->data_buf_len); mixed_data_size = mc_out_port->data_buf_len; } memcpy(mc_out_port->data_buf, mixed_data_ptr, mixed_data_size); mc_out_port->bytes_avail = mixed_data_size; if (get_audiomixer_dump_enable(DUMP_AUDIOMIXER_OUTDUMP)) { snprintf(acFilePathStr, ENUM_TYPE_STR_MAX_LEN, "%s_mch.pcm", "mc_outport"); aml_dump_audio_bitstreams(acFilePathStr, mc_out_port->data_buf, mc_out_port->bytes_avail); } } pthread_mutex_unlock(&audio_mixer->outport_locks[MIXER_OUTPUT_PORT_MULTI_PCM]); /* stereo pcm data */ pthread_mutex_lock(&audio_mixer->outport_locks[port_index]); do_downmix_to_2ch(p_downmix, mixed_data_ptr, MIXER_FRAME_COUNT, &p_multich_mixer->cfg); memcpy(out_port->data_buf, p_downmix->output_buf, p_downmix->output_buf_size); set_outport_data_avail(out_port, p_downmix->output_buf_size); if (get_audiomixer_dump_enable(DUMP_AUDIOMIXER_OUTDUMP)) { snprintf(acFilePathStr, ENUM_TYPE_STR_MAX_LEN, "%s_2ch.pcm", "stero_outport"); aml_dump_audio_bitstreams(acFilePathStr, out_port->data_buf, out_port->bytes_avail); } pthread_mutex_unlock(&audio_mixer->outport_locks[port_index]); return 0; } int notify_mixer_input_avail(struct amlAudioMixer *audio_mixer) { for (uint8_t port_index = 0; port_index < NR_INPORTS; port_index++) { input_port *in_port = audio_mixer->in_ports[port_index]; if (in_port && in_port->on_input_avail_cbk) in_port->on_input_avail_cbk(in_port->input_avail_cbk_data); } return 0; } int notify_mixer_exit(struct amlAudioMixer *audio_mixer) { for (uint8_t port_index = 0; port_index < NR_INPORTS; port_index++) { input_port *in_port = audio_mixer->in_ports[port_index]; if (in_port && in_port->on_notify_cbk) in_port->on_notify_cbk(in_port->notify_cbk_data); } return 0; } uint32_t get_mixer_inport_count(struct amlAudioMixer *audio_mixer) { return __builtin_popcount(audio_mixer->inportsMasks); } static bool is_submix_disable(struct amlAudioMixer *audio_mixer) { struct aml_audio_device *adev = audio_mixer->adev; if (is_dev_patch_exist(adev)) { return false; } else if (is_bypass_submix_active(adev)) { return true; } return false; } static void *mixer_threadloop(void *data) { struct amlAudioMixer *audio_mixer = data; struct audio_virtual_buf *pstVirtualBuffer = NULL; AM_LOGI("begin create thread"); if (audio_mixer->mixing_enable == 0) { pthread_exit(0); AM_LOGI("mixing_enable is 0 exit thread"); return NULL; } audio_mixer->exit_thread = 0; prctl(PR_SET_NAME, "amlAudioMixer"); aml_audio_set_cpu23_affinity(); aml_set_thread_priority("amlAudioMixer", audio_mixer->out_mixer_tid, 5); while (!audio_mixer->exit_thread) { if (pstVirtualBuffer == NULL) { audio_virtual_buf_open((void **)&pstVirtualBuffer, "mixer_thread", MIXER_WRITE_PERIOD_TIME_NANO * 4, MIXER_WRITE_PERIOD_TIME_NANO * 4, 0); audio_virtual_buf_process((void *)pstVirtualBuffer, MIXER_WRITE_PERIOD_TIME_NANO * 4); } pthread_mutex_lock(&audio_mixer->lock); mixer_inports_read(audio_mixer); pthread_mutex_unlock(&audio_mixer->lock); audio_virtual_buf_process((void *)pstVirtualBuffer, MIXER_WRITE_PERIOD_TIME_NANO); pthread_mutex_lock(&audio_mixer->lock); notify_mixer_input_avail(audio_mixer); mixer_do_mixing(audio_mixer); pthread_mutex_unlock(&audio_mixer->lock); if (!audio_mixer->inportsMasks && audio_mixer->enable_standby) {// no input, need standby submix and close alsa device if (!audio_mixer->submix_standby) { mixer_thread_sleep(audio_mixer); continue; } else { continue; } } if (!is_submix_disable(audio_mixer)) { pthread_mutex_lock(&audio_mixer->lock); mixer_output_write(audio_mixer); mixer_update_tstamp(audio_mixer); pthread_mutex_unlock(&audio_mixer->lock); } } if (pstVirtualBuffer != NULL) { audio_virtual_buf_close((void **)&pstVirtualBuffer); } AM_LOGI("exit thread"); return NULL; } uint32_t mixer_get_inport_latency_frames(struct amlAudioMixer *audio_mixer, int port_index) { R_CHECK_POINTER_LEGAL(0, audio_mixer, ""); R_CHECK_PARAM_LEGAL(0, port_index, 0, NR_INPORTS - 1, ""); input_port *in_port = audio_mixer->in_ports[port_index]; R_CHECK_POINTER_LEGAL(-EINVAL, in_port, "port_index:%d", port_index); if (in_port->first_read) { return in_port->inport_start_threshold / in_port->cfg.frame_size; } return in_port->get_latency_frames(in_port); } uint32_t mixer_get_outport_latency_frames(struct amlAudioMixer *audio_mixer) { R_CHECK_POINTER_LEGAL(0, audio_mixer, ""); MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; output_port *out_port = NULL; R_CHECK_PARAM_LEGAL(-1, port_index, MIXER_OUTPUT_PORT_STEREO_PCM, MIXER_OUTPUT_PORT_NUM - 1, ""); out_port = audio_mixer->out_ports[port_index]; if (out_port == NULL) { AM_LOGW("out_port is null"); return -1; } uint32_t ret = outport_get_latency_frames(out_port); return ret; } int pcm_mixer_thread_run(struct amlAudioMixer *audio_mixer) { int ret = 0; audio_format_t format = AUDIO_FORMAT_PCM; AM_LOGI("++"); R_CHECK_POINTER_LEGAL(-EINVAL, audio_mixer, ""); output_port *out_port = NULL; MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; R_CHECK_PARAM_LEGAL(-1, port_index, MIXER_OUTPUT_PORT_STEREO_PCM, MIXER_OUTPUT_PORT_NUM - 1, ""); out_port = mixer_get_cur_outport_with_lock(audio_mixer, port_index); if (out_port) { format = out_port->cfg.format; release_cur_outport_lock( audio_mixer, port_index); } if (audio_mixer->out_mixer_tid > 0) { AM_LOGE("out mixer thread already running"); return -EINVAL; } audio_mixer->mixing_enable = 1; switch (format) { case AUDIO_FORMAT_PCM_32_BIT: case AUDIO_FORMAT_PCM_16_BIT: ret = pthread_create(&audio_mixer->out_mixer_tid, NULL, mixer_threadloop, audio_mixer); break; default: AM_LOGE("format not supported"); break; } if (ret < 0) { AM_LOGE("thread run failed."); } AM_LOGI("++mixing_enable:%d, format:%#x", audio_mixer->mixing_enable, format); return ret; } int pcm_mixer_thread_exit(struct amlAudioMixer *audio_mixer) { audio_mixer->mixing_enable = 0; AM_LOGI("++ audio_mixer->mixing_enable %d", audio_mixer->mixing_enable); // block exit audio_mixer->exit_thread = 1; pthread_join(audio_mixer->out_mixer_tid, NULL); audio_mixer->out_mixer_tid = 0; notify_mixer_exit(audio_mixer); return 0; } struct amlAudioMixer *newAmlAudioMixer(struct aml_audio_device *adev, struct audioCfg cfg) { struct amlAudioMixer *audio_mixer = NULL; int ret = 0; AM_LOGD(""); audio_mixer = aml_audio_calloc(1, sizeof(*audio_mixer)); R_CHECK_POINTER_LEGAL(NULL, audio_mixer, "allocate amlAudioMixer:%zu no memory", sizeof(struct amlAudioMixer)); audio_mixer->adev = adev; audio_mixer->submix_standby = 1; audio_mixer->enable_standby = true; audio_mixer->mc_out_enable = false; mixer_set_state(audio_mixer, MIXER_IDLE); for (int i = 0; i < MIXER_OUTPUT_PORT_NUM; i++) { pthread_mutex_init(&audio_mixer->outport_locks[i], NULL); } ret = init_mixer_output_port(audio_mixer, MIXER_OUTPUT_PORT_STEREO_PCM, &cfg, MIXER_FRAME_COUNT); if (ret < 0) { AM_LOGE("init mixer out port failed"); goto err_state; } init_multich_mixer(audio_mixer, &cfg); init_aml_pcm_downmix(&audio_mixer->pcm_downmix); init_mixer_limiter(audio_mixer); audio_mixer->inportsMasks = 0; audio_mixer->inportsAvailMasks = (1 << NR_INPORTS) - 1; pthread_mutex_init(&audio_mixer->lock, NULL); pthread_mutex_init(&audio_mixer->inport_lock, NULL); return audio_mixer; err_state: aml_audio_free(audio_mixer); return NULL; } void freeAmlAudioMixer(struct amlAudioMixer *audio_mixer) { R_CHECK_POINTER_LEGAL((void)0, audio_mixer, ""); unsigned int masks = audio_mixer->inportsMasks; while (masks) { uint8_t index = get_bit_position_in_mask(NR_INPORTS - 1, &masks); delete_mixer_input_port(audio_mixer, index); } pthread_mutex_destroy(&audio_mixer->lock); pthread_mutex_destroy(&audio_mixer->inport_lock); for (int port_index = 0; port_index < MIXER_OUTPUT_PORT_NUM; port_index++) { delete_mixer_output_port(audio_mixer, port_index); } for (int i = 0; i < MIXER_OUTPUT_PORT_NUM; i++) { pthread_mutex_destroy(&audio_mixer->outport_locks[i]); } deinit_multich_mixer(audio_mixer); deinit_aml_pcm_downmix(&audio_mixer->pcm_downmix); deinit_mixer_limiter(audio_mixer); aml_audio_free(audio_mixer); } int mixer_outport_pcm_restart(struct amlAudioMixer *audio_mixer) { output_port *out_port = NULL; MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; out_port = mixer_get_cur_outport_with_lock(audio_mixer, port_index); if (out_port) { outport_pcm_restart(out_port); release_cur_outport_lock(audio_mixer, port_index); } return 0; } void mixer_dump(const struct aml_audio_device *pstAmlDev, int s32Fd) { if (NULL == pstAmlDev || NULL == pstAmlDev->audio_mixer) { dprintf(s32Fd, "[AML_HAL] [%s:%d] device or sub mixing is NULL !\n", __func__, __LINE__); return; } struct amlAudioMixer *pstAudioMixer = (struct amlAudioMixer *)pstAmlDev->audio_mixer; if (NULL == pstAudioMixer) { dprintf(s32Fd, "[AML_HAL] [%s:%d] struct amlAudioMixer is NULL !\n", __func__, __LINE__); return; } dprintf(s32Fd, "[AML_HAL]---------------Submix submix_standby: [%d]---------\n", pstAudioMixer->submix_standby); dprintf(s32Fd, "[AML_HAL]---------------Submix input port description cnt: [%d](masks:%#x)---------\n", get_mixer_inport_count(pstAudioMixer), pstAudioMixer->inportsMasks); for (uint8_t index=0; index < NR_INPORTS; index++) { input_port *pstInputPort = pstAudioMixer->in_ports[index]; if (pstInputPort) { dprintf(s32Fd, "[AML_HAL] input port type: %s(ID:%d)\n", mixerInputType2Str(pstInputPort->enInPortType), pstInputPort->ID); dprintf(s32Fd, "[AML_HAL] Status : %d\n", pstInputPort->port_status); dprintf(s32Fd, "[AML_HAL] Channel : %10d | Format : %#10x\n", pstInputPort->cfg.channelCnt, pstInputPort->cfg.format); dprintf(s32Fd, "[AML_HAL] FrameCnt : %zu | data size : %zu Byte\n", pstInputPort->data_buf_frame_cnt, pstInputPort->data_len_bytes); dprintf(s32Fd, "[AML_HAL] rbuf size : %10d Byte| Avail size : %10d Byte\n", pstInputPort->r_buf->size, get_buffer_read_space(pstInputPort->r_buf)); dprintf(s32Fd, "[AML_HAL] start_threshold : %10d Byte\n", pstInputPort->inport_start_threshold); } } dprintf(s32Fd, "[AML_HAL]---------------------Submix output port description----------------------\n"); output_port *pstOutPort = NULL; MIXER_OUTPUT_PORT port_index = MIXER_OUTPUT_PORT_STEREO_PCM; pstOutPort = mixer_get_cur_outport_with_lock(pstAudioMixer, port_index); if (pstOutPort) { dprintf(s32Fd, "[AML_HAL] output port type: %s\n", mixerOutputType2Str(pstOutPort->enOutPortType)); dprintf(s32Fd, "[AML_HAL] Channel : %10d | Format : %#10x\n", pstOutPort->cfg.channelCnt, pstOutPort->cfg.format); dprintf(s32Fd, "[AML_HAL] FrameCnt : %zu | data size : %zu Byte\n", pstOutPort->data_buf_frame_cnt, pstOutPort->data_buf_len); release_cur_outport_lock(pstAudioMixer, port_index); } else { dprintf(s32Fd, "[AML_HAL] not find output port description!!!\n"); } } int on_notify_cbk(void *data) { struct aml_stream_out *out = data; pthread_cond_broadcast(&out->cond); return 0; } int on_input_avail_cbk(void *data) { struct aml_stream_out *out = data; pthread_cond_broadcast(&out->cond); return 0; } static ssize_t aml_out_write_to_mixer(struct audio_stream_out *stream, const void* buffer, size_t bytes) { struct aml_stream_out *out = (struct aml_stream_out *)stream; struct aml_audio_device *adev = out->dev; const char *data = (char *)buffer; size_t written_total = 0; struct timespec ts; if (adev->is_netflix && STREAM_AUX_INPUT == out->stream_type) { aml_audio_data_handle(stream, buffer, bytes); } /*if (out->stream_type != STREAM_MAIN_INPUT) { int channel = 2; switch (out->audioCfg.channel_mask) { case AUDIO_CHANNEL_OUT_MONO: channel = 1; break; case AUDIO_CHANNEL_OUT_STEREO: channel = 2; break; case AUDIO_CHANNEL_OUT_5POINT1: channel = 6; break; case AUDIO_CHANNEL_OUT_7POINT1: channel = 8; break; default: channel = 2; break; } if (48000 != out->audioCfg.sample_rate) { int ret = aml_audio_resample_process_wrapper(&out->resample_handle, (char *)buffer, bytes, out->audioCfg.sample_rate, channel); if (0 != ret) { ALOGE("aml_audio_resample_process_wrapper failed"); } else { data = out->resample_handle->resample_buffer; bytes = out->resample_handle->resample_size; } } out->config.rate = 48000; }*/ do { ssize_t written = 0; AM_LOGV("stream streamtype: %s, written_total %zu, bytes %zu", streamtype2Str(out->stream_type), written_total, bytes); pthread_mutex_lock(&adev->audio_mixer_lock); written = mixer_write_inport(adev->audio_mixer, out->inputPortID, data, bytes - written_total); pthread_mutex_unlock(&adev->audio_mixer_lock); if (written < 0) { AM_LOGE("write failed, errno = %zu", written); return written; } if (written > 0) { written_total += written; data += written; //latency_frames = mixer_get_inport_latency_frames(audio_mixer, out->port_index) + // mixer_get_outport_latency_frames(audio_mixer); //pthread_mutex_lock(&out->lock); //clock_gettime(CLOCK_MONOTONIC, &out->timestamp); //out->last_frames_postion += written / frame_size - latency_frames; //pthread_mutex_unlock(&out->lock); } AM_LOGV("port index(%d) written(%zu), written_total(%zu), bytes(%zu)", out->inputPortID, written, written_total, bytes); if (written_total >= bytes) { AM_LOGV("exit"); break; } //usleep((bytes- written_total) * 1000 / 5 / 48); //if (out->port_index == 1) { ts_wait_time_us(&ts, 5000); AM_LOGV("-wait...."); bool wakeup = false; pthread_mutex_lock(&out->cond_lock); while (!wakeup) { pthread_cond_timedwait(&out->cond, &out->cond_lock, &ts); wakeup = true; } AM_LOGV("--wait wakeup"); pthread_mutex_unlock(&out->cond_lock); //} } while (1); return written_total; } ssize_t out_write_direct_pcm(struct audio_stream_out *stream, const void *buffer, size_t bytes) { struct aml_stream_out *out = (struct aml_stream_out *)stream; struct aml_audio_device *adev = out->dev; struct timespec tval, new_tval; uint64_t us_since_last_write = 0; uint64_t pre_last_frames_position = out->last_frames_position; //uint64_t begin_time, end_time; ssize_t written = 0; ssize_t remain = 0; int frame_size = 4; int64_t throttle_timeus = 0;//aml_audio_get_throttle_timeus(bytes); clock_gettime(CLOCK_MONOTONIC, &tval); //begin_time = get_systime_ns(); R_CHECK_PARAM_LEGAL(-1, out->inputPortID, 0, NR_INPORTS - 1, ""); pthread_mutex_lock(&adev->audio_mixer_lock); set_mixer_inport_volume(adev->audio_mixer, out->inputPortID, out->volume_l); set_mixer_inport_mute(adev->audio_mixer, out->inputPortID, out->final_mute); pthread_mutex_unlock(&adev->audio_mixer_lock); out->last_volume_l = out->volume_l; out->last_volume_r = out->volume_r; written = aml_out_write_to_mixer(stream, buffer, bytes); if (written >= 0) { remain = bytes - written; out->frame_write_sum += written / frame_size; if (remain > 0) { AM_LOGE("INVALID partial written"); } clock_gettime(CLOCK_MONOTONIC, &new_tval); if (tval.tv_sec > new_tval.tv_sec) AM_LOGE("FATAL ERROR"); //AM_LOGD(" %lld us, %lld", new_tval.tv_sec, tval.tv_sec); us_since_last_write = (new_tval.tv_sec - out->timestamp.tv_sec) * 1000000ull + (new_tval.tv_nsec - out->timestamp.tv_nsec) / 1000; //out->timestamp = new_tval; int used_this_write = (new_tval.tv_sec - tval.tv_sec) * 1000000 + (new_tval.tv_nsec - tval.tv_nsec) / 1000; int target_us = bytes * 1000 / frame_size / 48; AM_LOGI_IF(adev->debug_flag, "++bytes %zu, written %zu, out->port_index %d(out %p)used_this_write %d us", bytes, written, out->inputPortID, out, used_this_write); AM_LOGV("time spent on write %" PRId64 " us, written %zd", us_since_last_write, written); AM_LOGV("used_this_write %d us, target %d us", used_this_write, target_us); throttle_timeus = target_us - us_since_last_write; if (throttle_timeus > 0 && throttle_timeus < 200000) { AM_LOGV("throttle time %" PRId64 " us", throttle_timeus); if (throttle_timeus > 1800) { AM_LOGV("actual throttle %" PRId64 " us, since last %" PRId64 " us", throttle_timeus, us_since_last_write); } else { AM_LOGV("%" PRId64 " us, but un-throttle", throttle_timeus); } } else if (throttle_timeus != 0) { // first time write, sleep //usleep(target_us - 100); AM_LOGV("invalid throttle time %" PRId64 " us, us since last %" PRId64 " us", throttle_timeus, us_since_last_write); AM_LOGV("\n\n"); } } else { AM_LOGE("write fail, err = %zd", written); } // TODO: means first write, need check this by method if (us_since_last_write > 500000) { //usleep(bytes * 1000 / 48 / frame_size); AM_LOGV("invalid duration %" PRIu64 " us", us_since_last_write); //AM_LOGE("last write %ld s, %ld ms", out->timestamp.tv_sec, out->timestamp.tv_nsec/1000000); //AM_LOGE("before write %ld s, %ld ms", tval.tv_sec, tval.tv_nsec/1000000); //AM_LOGE("after write %ld s, %ld ms", new_tval.tv_sec, new_tval.tv_nsec/1000000); } // update new timestamp clock_gettime(CLOCK_MONOTONIC, &out->timestamp); out->lasttimestamp.tv_sec = out->timestamp.tv_sec; out->lasttimestamp.tv_nsec = out->timestamp.tv_nsec; if (written >= 0) { pthread_mutex_lock(&adev->audio_mixer_lock); uint32_t latency_frames = mixer_get_inport_latency_frames(adev->audio_mixer, out->inputPortID); pthread_mutex_unlock(&adev->audio_mixer_lock); //+ mixer_get_outport_latency_frames(audio_mixer); if (out->frame_write_sum > latency_frames) out->last_frames_position = out->frame_write_sum - latency_frames; else out->last_frames_position = pre_last_frames_position; if (0) { AM_LOGI("last position %" PRId64 ", latency_frames %d", out->last_frames_position, latency_frames); } } return written; } static int startMixingThread(struct amlAudioMixer *audio_mixer) { return pcm_mixer_thread_run(audio_mixer); } static int initSubMixingOutput( enum MIXER_TYPE type, struct aml_audio_device *adev) { R_CHECK_POINTER_LEGAL(-EINVAL, adev, ""); if (type == MIXER_LPCM) { struct audioCfg cfg; output_get_default_config(&cfg); struct amlAudioMixer *amixer = newAmlAudioMixer(adev, cfg); R_CHECK_POINTER_LEGAL(-ENOMEM, amixer, "newAmlAudioMixer failed"); adev->audio_mixer = amixer; startMixingThread(adev->audio_mixer); } else if (type == MIXER_MS12) { //TODO AM_LOGW("not support yet, in TODO list"); } else { AM_LOGE("not support"); return -EINVAL; } return 0; }; int initHalSubMixing(enum MIXER_TYPE type, struct aml_audio_device *adev, bool isTV) { int ret = 0; ALOGI("type %d, isTV %d", type, isTV); ret = initSubMixingOutput(type, adev); if (ret < 0) { AM_LOGE("fail to init mixer"); goto err1; } return 0; err1: return ret; } static int exitMixingThread(struct amlAudioMixer *audio_mixer) { return pcm_mixer_thread_exit(audio_mixer); } static int releaseSubMixingOutput(struct amlAudioMixer *audio_mixer) { R_CHECK_POINTER_LEGAL(-EINVAL, audio_mixer, ""); AM_LOGI("++"); exitMixingThread(audio_mixer); freeAmlAudioMixer(audio_mixer); return 0; } int deleteHalSubMixing(struct aml_audio_device *adev) { releaseSubMixingOutput(adev->audio_mixer); adev->audio_mixer = NULL; return 0; } int subMixingOutputRestart(struct aml_audio_device *adev) { struct amlAudioMixer *audio_mixer = adev->audio_mixer; return mixer_outport_pcm_restart(audio_mixer); }