/* * 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_mixer" #include #include #include #include #include "aml_audio_log.h" #include "aml_hw_mixer.h" #include "aml_malloc_debug.h" #include "aml_audio_log.h" //code here for audio hw mixer when hwsync with af mixer output stream output //at the same,need do a software mixer in audio hw c. int aml_hw_mixer_init(struct aml_hw_mixer *mixer) { int ret = 0; pthread_mutex_init(&mixer->lock, NULL); pthread_mutex_lock(&mixer->lock); mixer->wp = 0; mixer->rp = 0; if (mixer->buf_size == 0) mixer->buf_size = AML_HW_MIXER_BUF_SIZE; mixer->start_buf = aml_audio_calloc(1, mixer->buf_size); if (!mixer->start_buf) { AM_LOGE("no mem"); ret = -ENOMEM; goto exit; } mixer->need_cache_flag = 1; if (mixer->tmp_buf_size == 0) { mixer->tmp_buf_size = 1024; } mixer->tmp_buf = aml_audio_calloc(1, mixer->tmp_buf_size); if (!mixer->tmp_buf) { AM_LOGE("no mem, mixer->tmp_buf calloc fail"); ret = -ENOMEM; aml_audio_free(mixer->start_buf); mixer->start_buf = NULL; goto exit; } exit: pthread_mutex_unlock(&mixer->lock); AM_LOGI("done, mixer %p, mix buf %p, tmp_buf%p", mixer, mixer->start_buf, mixer->tmp_buf); return ret; } void aml_hw_mixer_deinit(struct aml_hw_mixer *mixer) { pthread_mutex_lock(&mixer->lock); AM_LOGI("mixer %p, mix buf %p, tmp_buf %p", mixer, mixer->start_buf, mixer->tmp_buf); aml_audio_free(mixer->start_buf); mixer->start_buf = NULL; aml_audio_free(mixer->tmp_buf); mixer->tmp_buf = NULL; mixer->wp = 0; mixer->rp = 0; mixer->buf_size = 0; mixer->tmp_buf_size = 0; mixer->need_cache_flag = 0; pthread_mutex_unlock(&mixer->lock); } void aml_hw_mixer_reset(struct aml_hw_mixer *mixer) { pthread_mutex_lock(&mixer->lock); mixer->wp = 0; mixer->rp = 0; mixer->need_cache_flag = 1; memset(mixer->start_buf, 0, mixer->buf_size); pthread_mutex_unlock(&mixer->lock); AM_LOGI("done, mixer %p, mix buf %p", mixer, mixer->start_buf); } static uint32_t aml_hw_mixer_get_space(struct aml_hw_mixer *mixer) { unsigned space; if (mixer->wp >= mixer->rp) space = mixer->buf_size - (mixer->wp - mixer->rp); else space = mixer->rp - mixer->wp; return space > 64 ? (space - 64) : 0; } int aml_hw_mixer_get_content_l(struct aml_hw_mixer *mixer) { unsigned content = 0; if (mixer->wp >= mixer->rp) { content = mixer->wp - mixer->rp; } else { content = mixer->wp - mixer->rp + mixer->buf_size; } //ALOGI("wp %d,rp %d\n",mixer->wp,mixer->rp); //ALOGI("%s(), buffer level = %d", __func__, content); return content; } //we assume the cached size is always smaller than buffer size //need called by device mutux locked int aml_hw_mixer_write(struct aml_hw_mixer *mixer, const void *buffer, size_t bytes) { int retry = 15; unsigned tail, space, write_bytes = bytes; /*coverity[missing_lock]: mixer->start_buf does not need to be lock*/ if (!mixer || !mixer->start_buf) { AM_LOGE("no mixer or mixer not inited!"); return bytes; } while (retry--) { pthread_mutex_lock(&mixer->lock); space = aml_hw_mixer_get_space(mixer); if (space < bytes) { pthread_mutex_unlock(&mixer->lock); usleep(10 * 1000); } else break; } if (retry < 0) { AM_LOGE("write data no space,space %d,bytes %zu,rp %d,wp %d, reset all ptr", space, bytes, mixer->rp, mixer->wp); mixer->wp = 0; mixer->rp = 0; return 0; } if (write_bytes + mixer->wp > mixer->buf_size) { tail = mixer->buf_size - mixer->wp; memcpy(mixer->start_buf + mixer->wp, buffer, tail); write_bytes -= tail; memcpy(mixer->start_buf, (unsigned char*)buffer + tail, write_bytes); mixer->wp = write_bytes; } else { memcpy(mixer->start_buf + mixer->wp, buffer, write_bytes); mixer->wp += write_bytes; mixer->wp %= mixer->buf_size; } pthread_mutex_unlock(&mixer->lock); return bytes; } static inline short CLIPSHORT(int32_t r) { if (r > 32767) r = 32767; else if (r < -32768) r = -32768; return r; } static inline int CLIPINT(int64_t r) { if (r > 2147483647) r = 2147483647; else if (r < -2147483648) r = -2147483648; return r; } int aml_hw_mixer_mixing(struct aml_hw_mixer *mixer, void *buffer, int bytes, audio_format_t format) { int32_t i, tail; int32_t cached_bytes, read_bytes = bytes; if (!mixer || !mixer->start_buf) { AM_LOGE("no mixer or mixer not inited!"); return 0; } pthread_mutex_lock(&mixer->lock); cached_bytes = aml_hw_mixer_get_content_l(mixer); if (cached_bytes < bytes) { AM_LOGV("no enough aux data for mixing, cached %d, need %d\n", cached_bytes, bytes); pthread_mutex_unlock(&mixer->lock); return 0; } if (format == AUDIO_FORMAT_PCM_32_BIT) { int64_t tmp; int32_t *tmp_buffer = (int32_t *)buffer; int32_t *cached_buf = (int32_t *)(mixer->start_buf + mixer->rp); if (read_bytes + mixer->rp > mixer->buf_size) { tail = mixer->buf_size - mixer->rp; for (i = 0; i < tail / 4; i++) { tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++; *tmp_buffer++ = CLIPINT(tmp); } read_bytes -= tail; cached_buf = (int32_t *)mixer->start_buf; for (i = 0; i < read_bytes / 4; i++) { tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++; *tmp_buffer++ = CLIPINT(tmp); } mixer->rp = read_bytes; } else { for (i = 0; i < read_bytes / 4; i++) { tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++; *tmp_buffer++ = CLIPINT(tmp); } mixer->rp += read_bytes; mixer->rp %= mixer->buf_size; } } else if (format == AUDIO_FORMAT_PCM_16_BIT) { int32_t tmp; int16_t *tmp_buffer = (int16_t *)buffer; int16_t *cached_buf = (int16_t *)(mixer->start_buf + mixer->rp); if (read_bytes + mixer->rp > mixer->buf_size) { tail = mixer->buf_size - mixer->rp; for (i = 0; i < tail / 2; i++) { tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++; *tmp_buffer++ = CLIPSHORT(tmp); } read_bytes -= tail; cached_buf = (int16_t *)mixer->start_buf; for (i = 0; i < read_bytes / 2; i++) { tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++; *tmp_buffer++ = CLIPSHORT(tmp); } mixer->rp = read_bytes; } else { for (i = 0; i < read_bytes / 2; i++) { tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++; *tmp_buffer++ = CLIPSHORT(tmp); } mixer->rp += read_bytes; mixer->rp %= mixer->buf_size; } } else { AM_LOGE("format %#x not supporte!", format); } pthread_mutex_unlock(&mixer->lock); return 0; } int aml_hw_mixer_mch_in_mixing(struct aml_hw_mixer *mixer, void *buffer, int in_frames, audio_format_t format, int in_ch) { int32_t i, tail, j; int32_t cached_bytes, read_bytes = in_frames * audio_bytes_per_sample(format) * 2;// system sound is 2ch int need_mixed_ch = 2; if (in_ch <= 0) { AM_LOGE("ch(%d) not support!", in_ch); return -1; } else if (in_ch == 1) { need_mixed_ch = 1; } pthread_mutex_lock(&mixer->lock); cached_bytes = aml_hw_mixer_get_content_l(mixer); if (cached_bytes < read_bytes) { read_bytes = cached_bytes; } if (format == AUDIO_FORMAT_PCM_16_BIT) { int32_t tmp; int16_t *tmp_buffer = (int16_t *)buffer; int16_t *cached_buf = (int16_t *)(mixer->start_buf + mixer->rp); if (read_bytes + mixer->rp > mixer->buf_size) { tail = mixer->buf_size - mixer->rp; for (i = 0; i < tail / 4; i++) {// tail / 4 is frame num for (j = 0; j < need_mixed_ch; j++) { tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++; *tmp_buffer++ = CLIPSHORT(tmp); } if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 16 bit data,we only mix cached_buf to the L/R ch of tmp_buffer tmp_buffer = tmp_buffer + (in_ch -2); } else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 16 bit data, we only mix L ch data of cached_buf to tmp_buffer cached_buf = cached_buf + 1; } } read_bytes -= tail; cached_buf = (int16_t *)mixer->start_buf; for (i = 0; i < read_bytes / 4; i++) {// read_bytes / 4 is frame num for (j = 0; j < need_mixed_ch; j++) { tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++; *tmp_buffer++ = CLIPSHORT(tmp); } if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 16 bit data,we only mix cached_buf to the L/R ch of tmp_buffer tmp_buffer = tmp_buffer + (in_ch -2); } else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 16 bit data, we only mix L ch data of cached_buf to tmp_buffer cached_buf = cached_buf + 1; } } mixer->rp = read_bytes; } else { for (i = 0; i < read_bytes / 4; i++) {// read_bytes / 4 is frame num for (j = 0; j < need_mixed_ch; j++) { tmp = (int32_t)*tmp_buffer + (int32_t)*cached_buf++; *tmp_buffer++ = CLIPSHORT(tmp); } if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 16 bit data,we only mix cached_buf to the L/R ch of tmp_buffer tmp_buffer = tmp_buffer + (in_ch -2); } else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 16 bit data, we only mix L ch data of cached_buf to tmp_buffer cached_buf = cached_buf + 1; } } mixer->rp += read_bytes; mixer->rp %= mixer->buf_size; } } else if (format == AUDIO_FORMAT_PCM_32_BIT) { int64_t tmp; int32_t *tmp_buffer = (int32_t *)buffer; int32_t *cached_buf = (int32_t *)(mixer->start_buf + mixer->rp); if (read_bytes + mixer->rp > mixer->buf_size) { tail = mixer->buf_size - mixer->rp; for (i = 0; i < tail / 8; i++) {// tail / 8 is frame num for (j = 0; j < need_mixed_ch; j++) { tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++; *tmp_buffer++ = CLIPINT(tmp); } if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 32 bit data,we only mix cached_buf to the L/R ch of tmp_buffer tmp_buffer = tmp_buffer + (in_ch -2); } else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 32 bit data, we only mix L ch data of cached_buf to tmp_buffer cached_buf = cached_buf + 1; } } read_bytes -= tail; cached_buf = (int32_t *)mixer->start_buf; for (i = 0; i < read_bytes / 8; i++) {// read_bytes / 8 is frame num for (j = 0; j < need_mixed_ch; j++) { tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++; *tmp_buffer++ = CLIPINT(tmp); } if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 32 bit data,we only mix cached_buf to the L/R ch of tmp_buffer tmp_buffer = tmp_buffer + (in_ch -2); } else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 32 bit data, we only mix L ch data of cached_buf to tmp_buffer cached_buf = cached_buf + 1; } } mixer->rp = read_bytes; } else { for (i = 0; i < read_bytes / 8; i++) {// read_bytes / 8 is frame num for (j = 0; j < need_mixed_ch; j++) { tmp = (int64_t)*tmp_buffer + (int64_t)*cached_buf++; *tmp_buffer++ = CLIPINT(tmp); } if (in_ch > 2) {// cached_buf is 2ch 16bit data, tmp_buffer is in_ch 32 bit data,we only mix cached_buf to the L/R ch of tmp_buffer tmp_buffer = tmp_buffer + (in_ch -2); } else if (need_mixed_ch == 1) {//cached_buf is 2ch 16bit data, tmp_buffer is 1ch 32 bit data, we only mix L ch data of cached_buf to tmp_buffer cached_buf = cached_buf + 1; } } mixer->rp += read_bytes; mixer->rp %= mixer->buf_size; } } else { AM_LOGE("format %#x not support!", format); } pthread_mutex_unlock(&mixer->lock); return 0; } //need called by device mutux locked int aml_hw_mixer_read(struct aml_hw_mixer *mixer, void *r_buf, uint32_t size) { unsigned cached_size; unsigned read_size = size; unsigned tail = 0; pthread_mutex_lock(&mixer->lock); cached_size = aml_hw_mixer_get_content_l(mixer); // we always assue we have enough data to read when hwsync enabled. // if we do not have,insert zero data. if (cached_size < size) { AM_LOGV("read data has not enough data to mixer,read %d, have %d,rp %d,wp %d\n", size, cached_size, mixer->rp, mixer->wp); memset((unsigned char*)r_buf + cached_size, 0, size - cached_size); read_size = cached_size; } if (read_size + mixer->rp > mixer->buf_size) { tail = mixer->buf_size - mixer->rp; memcpy(r_buf, mixer->start_buf + mixer->rp, tail); read_size -= tail; memcpy((unsigned char*)r_buf + tail, mixer->start_buf, read_size); mixer->rp = read_size; } else { memcpy(r_buf, mixer->start_buf + mixer->rp, read_size); mixer->rp += read_size; mixer->rp %= mixer->buf_size; } pthread_mutex_unlock(&mixer->lock); return size; } // aml audio hw c mixer code end