📄 audio_mixer.c
字号:
/* * GPAC - Multimedia Framework C SDK * * Copyright (c) Jean Le Feuvre 2000-2005 * All rights reserved * * This file is part of GPAC / Scene Rendering sub-project * * GPAC is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * GPAC is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */#include <gpac/internal/renderer_dev.h>/*max number of channels we support in mixer*/#define GF_SR_MAX_CHANNELS 16/* Notes about the mixer: 1- spatialization is out of scope for the mixer (eg that's the sound node responsability) 2- mixing is performed by resampling input source & deinterleaving its channels into dedicated buffer. We could directly deinterleave in the main mixer ouput buffer, but this would prevent any future gain correction.*/typedef struct{ GF_AudioInterface *src; s32 *ch_buf[GF_SR_MAX_CHANNELS]; /*resampled buffer*/ u32 buffer_size; u32 bytes_per_sec; Bool has_prev; s32 last_channels[GF_SR_MAX_CHANNELS]; u32 in_bytes_used, out_samples_written, out_samples_to_write; Fixed speed; Fixed pan[6];} MixerInput;struct __audiomix{ /*src*/ GF_List *sources; /*output config*/ u32 sample_rate; u32 nb_channels; u32 bits_per_sample; u32 channel_cfg; GF_Mutex *mx; /*if set forces stereo/mono*/ Bool force_channel_out; /*set to true by mixer when detecting an audio config change*/ Bool must_reconfig; Bool isEmpty; /*set to non null if this outputs directly to the driver, in which case audio formats have to be checked*/ struct _audio_render *ar; s32 *output; u32 output_size;};GF_AudioMixer *gf_mixer_new(struct _audio_render *ar){ GF_AudioMixer *am; am = (GF_AudioMixer *) malloc(sizeof(GF_AudioMixer)); if (!am) return NULL; memset(am, 0, sizeof(GF_AudioMixer)); am->mx = gf_mx_new(); am->sources = gf_list_new(); am->isEmpty = 1; am->ar = ar; am->sample_rate = 44100; am->bits_per_sample = 16; am->nb_channels = 2; am->output = NULL; am->output_size = 0; return am;}Bool gf_mixer_must_reconfig(GF_AudioMixer *am) { return am->must_reconfig;}void gf_mixer_del(GF_AudioMixer *am){ gf_list_del(am->sources); gf_mx_del(am->mx); if (am->output) free(am->output); free(am);}void gf_mixer_remove_all(GF_AudioMixer *am){ u32 j; gf_mixer_lock(am, 1); while (gf_list_count(am->sources)) { MixerInput *in = (MixerInput *)gf_list_get(am->sources, 0); gf_list_rem(am->sources, 0); for (j=0; j<GF_SR_MAX_CHANNELS; j++) { if (in->ch_buf[j]) free(in->ch_buf[j]); } free(in); } am->isEmpty = 1, gf_mixer_lock(am, 0);}Bool gf_mixer_is_src_present(GF_AudioMixer *am, GF_AudioInterface *ifce){ MixerInput *in; u32 i = 0; while ((in = (MixerInput *)gf_list_enum(am->sources, &i))) { if (in->src == ifce) return 1; } return 0;}u32 gf_mixer_get_src_count(GF_AudioMixer *am){ return gf_list_count(am->sources);}void gf_mixer_force_chanel_out(GF_AudioMixer *am, u32 num_channels){ am->force_channel_out = 1; am->nb_channels = num_channels;}u32 gf_mixer_get_block_align(GF_AudioMixer *am){ return am->nb_channels*am->bits_per_sample/8;}void gf_mixer_lock(GF_AudioMixer *am, Bool lockIt){ //GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[AudioRender] Thread ID %d is %s the audio mixer\n", gf_th_id(), lockIt ? "locking" : "unlocking" )); if (lockIt) { gf_mx_p(am->mx); } else { gf_mx_v(am->mx); }}void gf_mixer_add_input(GF_AudioMixer *am, GF_AudioInterface *src){ MixerInput *in; if (gf_mixer_is_src_present(am, src)) return; gf_mixer_lock(am, 1); GF_SAFEALLOC(in, MixerInput); in->src = src; gf_list_add(am->sources, in); am->must_reconfig = 1; am->isEmpty = 0; gf_mixer_lock(am, 0);}void gf_mixer_remove_input(GF_AudioMixer *am, GF_AudioInterface *src){ u32 i, j, count; if (am->isEmpty) return; gf_mixer_lock(am, 1); count = gf_list_count(am->sources); for (i=0; i<count; i++) { MixerInput *in = (MixerInput *)gf_list_get(am->sources, i); if (in->src != src) continue; gf_list_rem(am->sources, i); for (j=0; j<GF_SR_MAX_CHANNELS; j++) { if (in->ch_buf[j]) free(in->ch_buf[j]); } free(in); break; } am->isEmpty = gf_list_count(am->sources) ? 0 : 1; /*we don't ask for reconfig when removing a node*/ gf_mixer_lock(am, 0);}static GF_Err get_best_samplerate(GF_AudioMixer *am, u32 *out_sr, u32 *out_ch, u32 *out_bps){ if (!am->ar) return GF_OK; if (!am->ar->audio_out || !am->ar->audio_out->QueryOutputSampleRate) return GF_OK; return am->ar->audio_out->QueryOutputSampleRate(am->ar->audio_out, out_sr, out_ch, out_bps);}void gf_mixer_get_config(GF_AudioMixer *am, u32 *outSR, u32 *outCH, u32 *outBPS, u32 *outChCfg){ (*outBPS) = am->bits_per_sample; (*outCH) = am->nb_channels; (*outSR) = am->sample_rate; (*outChCfg) = am->channel_cfg;}void gf_mixer_set_config(GF_AudioMixer *am, u32 outSR, u32 outCH, u32 outBPS, u32 outChCfg){ if ((am->bits_per_sample == outBPS) && (am->nb_channels == outCH) && (am->sample_rate==outSR) && (am->channel_cfg == outChCfg)) return; gf_mixer_lock(am, 1); am->bits_per_sample = outBPS; if (!am->force_channel_out) am->nb_channels = outCH; if (get_best_samplerate(am, &outSR, &outCH, &outBPS) == GF_OK) { am->sample_rate = outSR; if (outCH>2) am->channel_cfg = outChCfg; else if (outCH==2) am->channel_cfg = GF_AUDIO_CH_FRONT_LEFT | GF_AUDIO_CH_FRONT_RIGHT; else am->channel_cfg = GF_AUDIO_CH_FRONT_LEFT; } /*if main mixer recfg output*/ if (am->ar) am->ar->need_reconfig = 1; gf_mixer_lock(am, 0);}Bool gf_mixer_reconfig(GF_AudioMixer *am){ u32 i, count, numInit, max_sample_rate, max_channels, max_bps, cfg_changed, ch_cfg; gf_mixer_lock(am, 1); if (am->isEmpty || !am->must_reconfig) { gf_mixer_lock(am, 0); return 0; } numInit = 0; max_sample_rate = am->sample_rate; max_channels = am->nb_channels; max_bps = am->bits_per_sample; cfg_changed = 0; ch_cfg = 0; max_sample_rate = 0, count = gf_list_count(am->sources); assert(count); for (i=0; i<count; i++) { Bool has_cfg; MixerInput *in = (MixerInput *) gf_list_get(am->sources, i); has_cfg = in->src->GetConfig(in->src, 1); if (has_cfg) { /*check same cfg...*/ if (in->src->sr * in->src->chan * in->src->bps == 8*in->bytes_per_sec) { numInit++; continue; } } else continue; /*update out cfg*/ if ((count==1) && (max_sample_rate != in->src->sr)) {// cfg_changed = 1; max_sample_rate = in->src->sr; } else if (max_sample_rate<in->src->sr) {// cfg_changed = 1; max_sample_rate = in->src->sr; } if ((count==1) && (max_bps!=in->src->bps)) { cfg_changed = 1; max_bps = in->src->bps; } else if (max_bps<in->src->bps) { cfg_changed = 1; max_bps = in->src->bps; } if (!am->force_channel_out) { if ((count==1) && (max_channels!=in->src->chan)) { cfg_changed = 1; max_channels = in->src->chan; if (in->src->chan>2) ch_cfg |= in->src->ch_cfg; } else if (max_channels < in->src->chan) { cfg_changed = 1; max_channels = in->src->chan; if (in->src->chan>2) ch_cfg |= in->src->ch_cfg; } } numInit++; in->bytes_per_sec = in->src->sr * in->src->chan * in->src->bps / 8; /*cfg has changed, we must reconfig everything*/ if (cfg_changed || (max_sample_rate != am->sample_rate) ) { in->has_prev = 0; memset(&in->last_channels, 0, sizeof(s16)*GF_SR_MAX_CHANNELS); } } if (cfg_changed || (max_sample_rate && (max_sample_rate != am->sample_rate)) ) { if (max_channels>2) { if (ch_cfg != am->channel_cfg) { /*recompute num channel based on all input channels*/ max_channels = 0; if (ch_cfg & GF_AUDIO_CH_FRONT_LEFT) max_channels ++; if (ch_cfg & GF_AUDIO_CH_FRONT_RIGHT) max_channels ++; if (ch_cfg & GF_AUDIO_CH_FRONT_CENTER) max_channels ++; if (ch_cfg & GF_AUDIO_CH_LFE) max_channels ++; if (ch_cfg & GF_AUDIO_CH_BACK_LEFT) max_channels ++; if (ch_cfg & GF_AUDIO_CH_BACK_RIGHT) max_channels ++; if (ch_cfg & GF_AUDIO_CH_BACK_CENTER) max_channels ++; if (ch_cfg & GF_AUDIO_CH_SIDE_LEFT) max_channels ++; if (ch_cfg & GF_AUDIO_CH_SIDE_RIGHT) max_channels ++; } } else { ch_cfg = GF_AUDIO_CH_FRONT_LEFT; if (max_channels==2) ch_cfg |= GF_AUDIO_CH_FRONT_RIGHT; } gf_mixer_set_config(am, max_sample_rate, max_channels, max_bps, ch_cfg); } if (numInit == count) am->must_reconfig = 0; gf_mixer_lock(am, 0); return cfg_changed;}static GFINLINE u32 get_channel_out_pos(u32 in_ch, u32 out_cfg){ u32 i, cfg, pos; pos = 0; for (i=0; i<9; i++) { cfg = 1<<(i); if (out_cfg & cfg) { if (cfg == in_ch) return pos; pos++; } } return GF_SR_MAX_CHANNELS;}/*this is crude, we'd need a matrix or something*/static GFINLINE void am_map_channels(s32 *inChan, u32 nb_in, u32 in_cfg, u32 nb_out, u32 out_cfg){ u32 i; if (nb_in==1) { /*mono to stereo*/ if (nb_out==2) { inChan[1] = inChan[0]; } else if (nb_out>2) { /*if center channel use it (we assume we always have stereo channels)*/ if (out_cfg & GF_AUDIO_CH_FRONT_CENTER) { inChan[2] = inChan[0]; inChan[0] = 0; for (i=3; i<nb_out; i++) inChan[i] = 0; } else { /*mono to stereo*/ inChan[1] = inChan[0]; for (i=2; i<nb_out; i++) inChan[i] = 0; } } } else if (nb_in==2) { if (nb_out==1) { inChan[0] = (inChan[0]+inChan[1])/2; } else { for (i=2; i<nb_out; i++) inChan[i] = 0; } } /*same output than input channels, nothing to reorder*/ /*more output than input channels*/ else if (nb_in<nb_out) { s32 bckup[GF_SR_MAX_CHANNELS]; u32 pos; u32 cfg = in_cfg; u32 ch = 0; memcpy(bckup, inChan, sizeof(s32)*nb_in); for (i=0; i<nb_in; i++) { /*get first in channel*/ while (! (cfg & 1)) { ch++; cfg>>=1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -