/*************************************************************************** * Copyright (c) 2012, Broadcom Corporation * All Rights Reserved * Confidential Property of Broadcom Corporation * * THIS SOFTWARE MAY ONLY BE USED SUBJECT TO AN EXECUTED SOFTWARE LICENSE * AGREEMENT BETWEEN THE USER AND BROADCOM. YOU HAVE NO RIGHT TO USE OR * EXPLOIT THIS MATERIAL EXCEPT SUBJECT TO THE TERMS OF SUCH AN AGREEMENT. * * $brcm_Workfile: $ * $brcm_Revision: $ * $brcm_Date: $ * * Module Description: * * Revision History: * * $brcm_Log: $ * ***************************************************************************/ #include "nexus_platform.h" #include "nexus_audio_decoder.h" #include "nexus_stc_channel.h" #include "nexus_timebase.h" #include "nexus_auto_volume_level.h" #include "nexus_audio_capture.h" #include "bstd.h" #include "bkni.h" #include "ts_psi.h" #include "bavc.h" #include "bsettop.h" #include "bsettop_stream_n_priv.h" BDBG_MODULE(bdecode_audio); /* following definition should be matched the level of application's vol */ #define vol_min 0 #define vol_max 32 #define MAX_EBUFFER 200 /* for 500 msec */ struct baudio_decode { NEXUS_AudioDecoderHandle pcmDecoder; NEXUS_AudioDecoderHandle compressedDecoder; NEXUS_AutoVolumeLevelHandle avlHandle; NEXUS_AudioCaptureHandle captureHandle; NEXUS_AudioDecoderStartSettings audioSettings; NEXUS_PlatformConfiguration platformConfig; bool initialized; baudio_decode_config config; bool audio_decoding; bool start_compressed; bstream_info_t *p_stream_info; bstream_t stream; bool locked; int first_pts_cnt; int tsm_failed_cnt; int ewptr; long energy[MAX_EBUFFER]; }; static struct baudio_decode s_audio; static bresult baudio_decode_p_set_dolby(baudio_decode_t audio, baudio_dolby_settings dolby, baudio_aac_downmix aac_downmix); #ifdef CONFIG_AUD_PWR static void baudio_decode_p_capture_cb(void *pparam, int iparam); #endif static void baudio_decode_switch_mai_output(baudio_decode_t audio, bool compressed); static void baudio_decode_lock_cb(void *pparam, int iparam) { NEXUS_AudioDecoderStatus status; baudio_decode_t audio = (baudio_decode_t)pparam; BSTD_UNUSED(iparam); NEXUS_AudioDecoder_GetStatus(audio->pcmDecoder, &status); audio->locked = status.locked; BDBG_MSG(("Lock status : %d", audio->locked)); } static void baudio_decode_first_pts_cb(void *pparam, int iparam) { baudio_decode_t audio = (baudio_decode_t)pparam; BSTD_UNUSED(iparam); audio->first_pts_cnt++; BDBG_MSG(("First PTS : %d", audio->first_pts_cnt)); } static void baudio_decode_tsm_failed_cb(void *pparam, int iparam) { baudio_decode_t audio = (baudio_decode_t)pparam; BSTD_UNUSED(iparam); audio->tsm_failed_cnt++; BDBG_MSG(("TSM failed : %d", audio->tsm_failed_cnt)); } baudio_decode_t baudio_decode_open(bobject_t id) { NEXUS_AutoVolumeLevelSettings avlSettings; NEXUS_AudioDecoderSettings decoderSettings; #ifdef CONFIG_AUD_PWR NEXUS_AudioCaptureStartSettings captureSettings; #endif BSTD_UNUSED(id); BKNI_Memset(&s_audio, 0, sizeof(s_audio)); NEXUS_Platform_GetConfiguration(&s_audio.platformConfig); s_audio.pcmDecoder = NEXUS_AudioDecoder_Open(0, NULL); s_audio.compressedDecoder = NEXUS_AudioDecoder_Open(1, NULL); s_audio.avlHandle = NEXUS_AutoVolumeLevel_Open(NULL); s_audio.captureHandle = NEXUS_AudioCapture_Open(0, NULL); NEXUS_AutoVolumeLevel_AddInput(s_audio.avlHandle, NEXUS_AudioDecoder_GetConnector(s_audio.pcmDecoder, NEXUS_AudioDecoderConnectorType_eStereo)); NEXUS_AudioOutput_AddInput(NEXUS_AudioDac_GetConnector(s_audio.platformConfig.outputs.audioDacs[0]), NEXUS_AutoVolumeLevel_GetConnector(s_audio.avlHandle)); NEXUS_AudioOutput_AddInput(NEXUS_HdmiOutput_GetAudioConnector(s_audio.platformConfig.outputs.hdmi[0]), NEXUS_AutoVolumeLevel_GetConnector(s_audio.avlHandle)); #if NEXUS_NUM_RFM_OUTPUTS NEXUS_AudioOutput_AddInput(NEXUS_Rfm_GetAudioConnector(s_audio.platformConfig.outputs.rfm[0]), NEXUS_AutoVolumeLevel_GetConnector(s_audio.avlHandle)); #endif NEXUS_AutoVolumeLevel_GetSettings(s_audio.avlHandle, &avlSettings); avlSettings.enabled = false; NEXUS_AutoVolumeLevel_SetSettings(s_audio.avlHandle, &avlSettings); s_audio.config.avl = avlSettings.enabled; s_audio.config.avl_target = avlSettings.target; s_audio.config.avl_fixedboost = avlSettings.fixedBoost; s_audio.config.avl_lowerbound = avlSettings.lowerBound; #ifdef CONFIG_AUD_PWR NEXUS_AudioOutput_AddInput(NEXUS_AudioCapture_GetConnector(s_audio.captureHandle), NEXUS_AutoVolumeLevel_GetConnector(s_audio.avlHandle)); NEXUS_AudioCapture_GetDefaultStartSettings(&captureSettings); captureSettings.dataCallback.callback = baudio_decode_p_capture_cb; captureSettings.dataCallback.context = &s_audio.capture; NEXUS_AudioCapture_Start(s_audio.captureHandle, &captureSettings); #endif NEXUS_AudioDecoder_GetSettings(s_audio.pcmDecoder, &decoderSettings); decoderSettings.firstPts.callback = baudio_decode_first_pts_cb; decoderSettings.firstPts.context = &s_audio; decoderSettings.lockChanged.callback = baudio_decode_lock_cb; decoderSettings.lockChanged.context = &s_audio; decoderSettings.ptsError.callback = baudio_decode_tsm_failed_cb; decoderSettings.ptsError.context = &s_audio; NEXUS_AudioDecoder_SetSettings(s_audio.pcmDecoder, &decoderSettings); return &s_audio; } void baudio_decode_stop(baudio_decode_t audio) { if (!audio->audio_decoding) return; NEXUS_AudioDecoder_Stop(audio->pcmDecoder); if (audio->start_compressed) { NEXUS_AudioDecoder_Stop(audio->compressedDecoder); baudio_decode_switch_mai_output(audio, false); } audio->audio_decoding = false; audio->start_compressed = false; } bresult baudio_decode_start(baudio_decode_t audio, bdisplay_t display, bstream_t p_stream) { bstream_status status; bstream_info_t *stream_info; NEXUS_TimebaseSettings timebaseSettings; BDBG_ASSERT(p_stream); BSTD_UNUSED(display); audio->stream = p_stream; bstream_get_status(p_stream, &status); stream_info = bstream_get_info(p_stream); audio->p_stream_info = stream_info; if ((status.mpeg.audio[0].pid == 0) || !stream_info) return eBAPP_RESULT_FAILURE; if (audio->audio_decoding) baudio_decode_stop(audio); BDBG_ASSERT(stream_info->audioPidChannel); BDBG_ASSERT(stream_info->stcChannel); if (((status.mpeg.audio[0].format == NEXUS_AudioCodec_eAc3) || (status.mpeg.audio[0].format == NEXUS_AudioCodec_eAc3Plus)) && audio->config.hdmi_compress) { audio->start_compressed = true; baudio_decode_switch_mai_output(audio, true); } NEXUS_AudioDecoder_GetDefaultStartSettings(&audio->audioSettings); audio->audioSettings.codec = status.mpeg.audio[0].format;; audio->audioSettings.pidChannel = stream_info->audioPidChannel; audio->audioSettings.stcChannel = stream_info->stcChannel; if (status.mpeg.video[0].pid == 0) { NEXUS_Timebase_GetSettings(NEXUS_Timebase_e0, &timebaseSettings); timebaseSettings.sourceType = NEXUS_TimebaseSourceType_ePcr; timebaseSettings.sourceSettings.pcr.pidChannel = stream_info->pcrPidChannel; timebaseSettings.sourceSettings.pcr.maxPcrError = 0xff; timebaseSettings.sourceSettings.pcr.trackRange = NEXUS_TimebaseTrackRange_e61ppm; NEXUS_Timebase_SetSettings(NEXUS_Timebase_e0, &timebaseSettings); } NEXUS_AudioDecoder_Start(audio->pcmDecoder, &audio->audioSettings); audio->audio_decoding = true; audio->config.copyright = 0; if (audio->start_compressed ) { NEXUS_AudioDecoder_Start(audio->compressedDecoder, &audio->audioSettings); } return b_ok; } bresult baudio_decode_set_config(baudio_decode_t audio, const baudio_decode_config *config) { NEXUS_AudioDecoderSettings settings; NEXUS_AutoVolumeLevelSettings avlSettings; int lvol, rvol; NEXUS_AudioDecoder_GetSettings(audio->pcmDecoder, &settings); if (audio->pcmDecoder) { /* baudio_decode_set_ac3_status(audio, config->copyright, config->category_code); */ } if ((audio->config.left_volume != config->left_volume) || (audio->config.right_volume != config->right_volume) || (audio->config.mute != config->mute)) { lvol = config->left_volume; rvol = config->right_volume; if (lvol < vol_min) lvol = vol_min; else if (lvol > vol_max) lvol = vol_max; if (rvol < vol_min) rvol = vol_min; else if (rvol > vol_max) rvol = vol_max; settings.muted = config->mute; /* TODO: need adjustment to meet comcast volume level */ lvol = (lvol + NEXUS_AUDIO_VOLUME_LINEAR_MIN - vol_min) * (NEXUS_AUDIO_VOLUME_LINEAR_NORMAL - NEXUS_AUDIO_VOLUME_LINEAR_MIN) / (vol_max-vol_min); rvol = (rvol + NEXUS_AUDIO_VOLUME_LINEAR_MIN - vol_min) * (NEXUS_AUDIO_VOLUME_LINEAR_NORMAL - NEXUS_AUDIO_VOLUME_LINEAR_MIN) / (vol_max-vol_min); settings.volumeMatrix[NEXUS_AudioChannel_eLeft][NEXUS_AudioChannel_eLeft] = lvol; settings.volumeMatrix[NEXUS_AudioChannel_eRight][NEXUS_AudioChannel_eRight] = rvol; settings.volumeMatrix[NEXUS_AudioChannel_eLeft][NEXUS_AudioChannel_eRight] = 0; settings.volumeMatrix[NEXUS_AudioChannel_eRight][NEXUS_AudioChannel_eLeft] = 0; } if (audio->config.mono_mix != config->mono_mix) { settings.outputMode = (config->mono_mix)?NEXUS_AudioDecoderOutputMode_e1_0:NEXUS_AudioDecoderOutputMode_e2_0; } if (audio->config.dualmono != config->dualmono) { switch (config->dualmono) { case baudio_dualmono_left: settings.dualMonoMode = NEXUS_AudioDecoderDualMonoMode_eLeft; break; case baudio_dualmono_right: settings.dualMonoMode = NEXUS_AudioDecoderDualMonoMode_eRight; break; case baudio_dualmono_monomix: settings.dualMonoMode = NEXUS_AudioDecoderDualMonoMode_eMix; break; case baudio_dualmono_stereo: default: settings.dualMonoMode = NEXUS_AudioDecoderDualMonoMode_eStereo; break; } } NEXUS_AudioDecoder_SetSettings(audio->pcmDecoder, &settings); baudio_decode_p_set_dolby(audio, config->dolby, config->aac_downmix); if ((audio->config.avl != config->avl) || (audio->config.avl_target != config->avl_target) || (audio->config.avl_lowerbound != config->avl_lowerbound) || (audio->config.avl_fixedboost != config->avl_fixedboost)) { NEXUS_AutoVolumeLevel_GetSettings(audio->avlHandle, &avlSettings); avlSettings.enabled = config->avl; avlSettings.target = config->avl_target; avlSettings.fixedBoost = config->avl_fixedboost; avlSettings.lowerBound = config->avl_lowerbound; NEXUS_AutoVolumeLevel_SetSettings(audio->avlHandle, &avlSettings); } audio->config = *config; return b_ok; } bresult baudio_decode_get_status(baudio_decode_t audio, baudio_decode_status *status) { NEXUS_AudioDecoderStatus astatus; bstream_status stream_status; NEXUS_AudioDecoder_GetStatus(audio->pcmDecoder, &astatus); if (audio->stream) bstream_get_status(audio->stream, &stream_status); else BKNI_Memset(&stream_status, 0, sizeof(bstream_status)); status->fifo_depth = astatus.fifoDepth; status->fifo_size = astatus.fifoSize; status->pts = astatus.pts; status->stc = astatus.pts + astatus.ptsStcDifference; status->pid = stream_status.mpeg.audio[0].pid; if (stream_status.mpeg.audio[0].format == NEXUS_AudioCodec_eAc3 || stream_status.mpeg.audio[0].format == NEXUS_AudioCodec_eAc3Plus) status->copyright = astatus.codecStatus.ac3.copyright; else status->copyright = 0; return b_ok; } void baudio_decode_get_config(baudio_decode_t audio, baudio_decode_config *config) { BSTD_UNUSED(audio); *config = audio->config; } void baudio_decode_get_avl_level(baudio_decode_t audio, int num_energy, long *level) { int start_idx, remain; BDBG_ASSERT(level); if(num_energy>MAX_EBUFFER) return; start_idx = audio->ewptr-num_energy; if (start_idx >= 0) { BKNI_Memcpy(level, &audio->energy[start_idx], num_energy*sizeof(long)); } else { remain = -1*start_idx; BKNI_Memcpy(level, &audio->energy[MAX_EBUFFER+start_idx], remain*sizeof(long)); BKNI_Memcpy(level+remain, &audio->energy[0], audio->ewptr*sizeof(long)); } } const bsettop_av_stream_type_t s_audio_stream_types[] = { { TS_PSI_ST_11172_3_Audio, BAVC_AudioCompressionStd_eMpegL1, 0, "MPEG"}, { TS_PSI_ST_13818_3_Audio, BAVC_AudioCompressionStd_eMpegL1, 0,"MPEG"}, { TS_PSI_ST_ATSC_AC3, BAVC_AudioCompressionStd_eAc3, 0,"AC3"}, { TS_PSI_ST_ATSC_EAC3, BAVC_AudioCompressionStd_eAc3Plus, 0,"AC3+"} }; const int s_audio_stream_types_num = sizeof(s_audio_stream_types)/sizeof(s_audio_stream_types[0]); bsettop_av_stream_type_t *bdecode_supported_audio(unsigned char stream_type) { int i; for (i = 0; i < s_audio_stream_types_num; ++i) { BDBG_MSG(("Table Entry[%d]: 0x%02x,0x%02x %s\n",i,s_audio_stream_types[i].format,s_audio_stream_types[i].codec_id,s_audio_stream_types[i].format_name)); if (stream_type == s_audio_stream_types[i].format) { BDBG_MSG(("Audio format[0x%02x]: %s\n",s_audio_stream_types[i].format,s_audio_stream_types[i].format_name)); return(bsettop_av_stream_type_t*)&s_audio_stream_types[i]; } } BDBG_MSG(("Unsupported Audio format[0x%02x]\n",stream_type)); return NULL; } /* * Dolby stuff */ static NEXUS_AudioDecoderDolbyDrcMode baudio_p_map_drc_mode(baudio_dolby_drc_mode mode) { switch (mode) { case baudio_dolby_drc_mode_line: return NEXUS_AudioDecoderDolbyDrcMode_eLine; case baudio_dolby_drc_mode_custom_a: return NEXUS_AudioDecoderDolbyDrcMode_eCustomA; case baudio_dolby_drc_mode_custom_d: return NEXUS_AudioDecoderDolbyDrcMode_eCustomD; default: break; } return NEXUS_AudioDecoderDolbyDrcMode_eRf; } static NEXUS_AudioDecoderDolbyStereoDownmixMode baudio_p_map_stereo_downmix_mode(baudio_dolby_stereo_downmix_mode mode) { switch (mode) { case baudio_dolby_stereo_downmix_mode_dolby_surround_compatible: return NEXUS_AudioDecoderDolbyStereoDownmixMode_eDolbySurroundCompatible; case baudio_dolby_stereo_downmix_mode_standard: return NEXUS_AudioDecoderDolbyStereoDownmixMode_eStandard; default: break; } return NEXUS_AudioDecoderDolbyStereoDownmixMode_eAutomatic; } static bresult baudio_decode_p_set_dolby(baudio_decode_t audio, baudio_dolby_settings dolby, baudio_aac_downmix aac_downmix) { NEXUS_AudioDecoderCodecSettings settings; NEXUS_AudioDecoderDolbySettings *dolbySettings; NEXUS_AudioCodec codec ; bstream_status status; NEXUS_AudioDecoderDolbyDrcMode drc; NEXUS_AudioDecoderDolbyStereoDownmixMode stereo; BSTD_UNUSED(aac_downmix); if ( (audio->config.dolby.stereo_downmix_mode != dolby.stereo_downmix_mode) || (audio->config.dolby.drc_mode != dolby.drc_mode) || (audio->config.dolby.cut != dolby.cut) || (audio->config.dolby.dialog_norm != dolby.dialog_norm)) { bstream_get_status(audio->stream, &status); codec = (NEXUS_AudioCodec)status.mpeg.audio[0].format; if ((codec != NEXUS_AudioCodec_eAc3) && (codec != NEXUS_AudioCodec_eAc3Plus)) return b_ok; NEXUS_AudioDecoder_GetCodecSettings(audio->pcmDecoder, codec, &settings); if (settings.codec == NEXUS_AudioCodec_eAc3) dolbySettings = &settings.codecSettings.ac3; else dolbySettings = &settings.codecSettings.ac3Plus; /* baudio_dolby_stereo_downmix_mode should match NEXUS_AudioDecoderDolbyStereoDownmixMode */ stereo = baudio_p_map_stereo_downmix_mode(dolby.stereo_downmix_mode); drc = baudio_p_map_drc_mode(dolby.drc_mode); dolbySettings->drcMode = drc; dolbySettings->stereoDownmixMode = stereo; dolbySettings->cut = dolby.cut; dolbySettings->boost = dolby.boost; dolbySettings->dialogNormalization = dolby.dialog_norm; NEXUS_AudioDecoder_SetCodecSettings(audio->pcmDecoder, &settings); } return b_ok; } /* * Summary: * power management for RAAGA block */ void baudio_decode_standby(baudio_decode_t audio, bool standby) { BSTD_UNUSED(audio); BSTD_UNUSED(standby); } #ifdef CONFIG_AUD_PWR #define NESUB 48 /* 1ms in 48KHz sample rate */ #include "avl_dspfunc.h" static void baudio_decode_p_capture_cb(void *pparam, int iparam) { NEXUS_AudioCaptureHandle capture = (NEXUS_AudioCaptureHandle)pparam; for (;;) { void *pBuffer; size_t bufferSize; NEXUS_AudioCapture_GetBuffer(capture, (void **)&pBuffer, &bufferSize); if (bufferSize>0) { NEXUS_AudioCapture_ReadComplete(capture, bufferSize); } else break; } } #endif void baudio_decode_test_tone(baudio_decode_t audio, uint32_t *samples, /* array of samples, num_samples long */ uint32_t num_samples, /* Number of samples, should be less than sample array size */ bool leftChannel, /* true - program left channel, flase program right channel */ bool enable /* enable/disable hifidac tone test */ ) { BSTD_UNUSED(audio); BSTD_UNUSED(samples); BSTD_UNUSED(num_samples); BSTD_UNUSED(leftChannel); BSTD_UNUSED(enable); } static void baudio_decode_switch_mai_output(baudio_decode_t audio, bool compressed) { if (compressed) { NEXUS_AudioOutput_RemoveAllInputs(NEXUS_HdmiOutput_GetAudioConnector(audio->platformConfig.outputs.hdmi[0])); NEXUS_AudioOutput_AddInput(NEXUS_HdmiOutput_GetAudioConnector(audio->platformConfig.outputs.hdmi[0]), NEXUS_AudioDecoder_GetConnector(audio->compressedDecoder, NEXUS_AudioDecoderConnectorType_eCompressed)); } else { NEXUS_AudioOutput_RemoveAllInputs(NEXUS_HdmiOutput_GetAudioConnector(audio->platformConfig.outputs.hdmi[0])); NEXUS_AudioOutput_AddInput(NEXUS_HdmiOutput_GetAudioConnector(audio->platformConfig.outputs.hdmi[0]), NEXUS_AutoVolumeLevel_GetConnector(audio->avlHandle)); } }