This source file includes following definitions.
- gf_afc_load
 
- gf_afc_setup
 
- gf_afc_process
 
- gf_afc_unload
 
- gf_afc_reset
 
- gf_ar_setup_output_format
 
- gf_ar_pause
 
- gf_ar_fill_output
 
- gf_sc_flush_next_audio
 
- gf_sc_check_audio_pending
 
- gf_ar_proc
 
- gf_sc_ar_load
 
- gf_sc_ar_del
 
- gf_sc_ar_reset
 
- gf_sc_ar_control
 
- gf_sc_ar_set_volume
 
- gf_sc_ar_mute
 
- gf_sc_ar_set_pan
 
- gf_sc_ar_add_src
 
- gf_sc_ar_remove_src
 
- gf_sc_ar_set_priority
 
- gf_sc_ar_reconfig
 
- gf_sc_ar_get_delay
 
- gf_sc_ar_get_clock
 
- gf_sc_reload_audio_filters
 
- gf_sc_add_audio_listener
 
- gf_sc_remove_audio_listener
 
#include <gpac/internal/compositor_dev.h>
GF_Err gf_afc_load(GF_AudioFilterChain *afc, GF_User *user, char *filterstring)
{
        struct _audiofilterentry *prev_filter = NULL;
        while (filterstring) {
                u32 i, count;
                GF_AudioFilter *filter;
                char *sep = strstr(filterstring, ";;");
                if (sep) sep[0] = 0;
                count = gf_modules_get_count(user->modules);
                filter = NULL;
                for (i=0; i<count; i++) {
                        filter = (GF_AudioFilter *)gf_modules_load_interface(user->modules, i, GF_AUDIO_FILTER_INTERFACE);
                        if (filter) {
                                if (filter->SetFilter
                                        && filter->Configure
                                        && filter->Process
                                        && filter->Reset
                                        && filter->SetOption
                                        && filter->GetOption
                                        && filter->SetFilter(filter, filterstring)
                                   )
                                        break;
                                gf_modules_close_interface((GF_BaseInterface *)filter);
                        }
                        filter = NULL;
                }
                if (filter) {
                        struct _audiofilterentry *entry;
                        GF_SAFEALLOC(entry, struct _audiofilterentry);
                        if (!entry) {
                                GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate audio filter entry\n"));
                        } else {
                                entry->filter = filter;
                                if (prev_filter) prev_filter->next = entry;
                                else afc->filters = entry;
                                prev_filter = entry;
                        }
                }
                if (sep) {
                        sep[0] = ';';
                        filterstring = sep + 2;
                } else {
                        break;
                }
        }
        return GF_OK;
}
GF_Err gf_afc_setup(GF_AudioFilterChain *afc, u32 bps, u32 sr, u32 chan, u32 ch_cfg, u32 *ch_out, u32 *ch_cfg_out)
{
        struct _audiofilterentry *entry;
        u32 block_len;
        u32 och, ocfg, in_ch;
        Bool not_in_place;
        if (afc->tmp_block1) gf_free(afc->tmp_block1);
        afc->tmp_block1 = NULL;
        if (afc->tmp_block2) gf_free(afc->tmp_block2);
        afc->tmp_block2 = NULL;
        *ch_out = *ch_cfg_out = 0;
        in_ch = chan;
        afc->min_block_size = 0;
        afc->max_block_size = 0;
        afc->delay_ms = 0;
        not_in_place = GF_FALSE;
        entry = afc->filters;
        while (entry) {
                if (entry->in_block) {
                        gf_free(entry->in_block);
                        entry->in_block = NULL;
                }
                if (entry->filter->Configure(entry->filter, sr, bps, chan, ch_cfg, &och, &ocfg, &block_len, &entry->delay_ms, &entry->in_place)==GF_OK) {
                        u32 out_block_size;
                        entry->in_block_size = chan * bps * block_len / 8;
                        if (!afc->min_block_size || (afc->min_block_size > entry->in_block_size))
                                afc->min_block_size = entry->in_block_size;
                        out_block_size = och * bps * block_len / 8;
                        if (afc->max_block_size < out_block_size) afc->max_block_size = out_block_size;
                        entry->enable = GF_TRUE;
                        chan = och;
                        ch_cfg = ocfg;
                        if (!entry->in_place) not_in_place = GF_TRUE;
                        afc->delay_ms += entry->delay_ms;
                } else {
                        entry->enable = GF_FALSE;
                }
                entry = entry->next;
        }
        if (!afc->max_block_size) afc->max_block_size = 1000;
        if (!afc->min_block_size) afc->min_block_size = afc->max_block_size * in_ch / chan;
        afc->tmp_block1 = (char*)gf_malloc(sizeof(char) * afc->max_block_size * 2);
        if (!afc->tmp_block1) return GF_OUT_OF_MEM;
        if (not_in_place) {
                afc->tmp_block2 = (char*)gf_malloc(sizeof(char) * afc->max_block_size * 2);
                if (!afc->tmp_block2) return GF_OUT_OF_MEM;
        }
        
        entry = afc->filters;
        while (entry) {
                if (entry->enable && entry->in_block_size) {
                        entry->in_block = (char*)gf_malloc(sizeof(char) * (entry->in_block_size + afc->max_block_size) );
                        if (!entry->in_block) return GF_OUT_OF_MEM;
                }
                entry = entry->next;
        }
        *ch_out = chan;
        *ch_cfg_out = ch_cfg;
        afc->enable_filters = GF_TRUE;
        return GF_OK;
}
u32 gf_afc_process(GF_AudioFilterChain *afc, u32 nb_bytes)
{
        struct _audiofilterentry *entry = afc->filters;
        while (entry) {
                char *inptr, *outptr;
                if (!nb_bytes || !entry->enable) {
                        entry = entry->next;
                        continue;
                }
                inptr = afc->tmp_block1;
                outptr = entry->in_place ? afc->tmp_block1 : afc->tmp_block2;
                
                if (!entry->in_block) {
                        entry->filter->Process(entry->filter, inptr, nb_bytes, outptr, &nb_bytes);
                } else {
                        u32 processed = 0;
                        u32 nb_bytes_out = 0;
                        assert(nb_bytes + entry->nb_bytes <= entry->in_block_size + afc->max_block_size);
                        
                        memcpy(entry->in_block + entry->nb_bytes, inptr, nb_bytes);
                        entry->nb_bytes += nb_bytes;
                        
                        while (entry->nb_bytes >= entry->in_block_size) {
                                u32 done;
                                entry->filter->Process(entry->filter, entry->in_block + processed, entry->in_block_size, outptr + nb_bytes_out, &done);
                                done = entry->in_block_size;
                                nb_bytes_out += done;
                                entry->nb_bytes -= entry->in_block_size;
                                processed += entry->in_block_size;
                        }
                        
                        if (processed && entry->nb_bytes)
                                memmove(entry->in_block, entry->in_block+processed, entry->nb_bytes);
                        nb_bytes = nb_bytes_out;
                }
                
                if (inptr != outptr) {
                        afc->tmp_block1 = outptr;
                        afc->tmp_block2 = inptr;
                }
                entry = entry->next;
        }
        return nb_bytes;
}
void gf_afc_unload(GF_AudioFilterChain *afc)
{
        while (afc->filters) {
                struct _audiofilterentry *tmp = afc->filters;
                afc->filters = tmp->next;
                gf_modules_close_interface((GF_BaseInterface *)tmp->filter);
                if (tmp->in_block) gf_free(tmp->in_block);
                gf_free(tmp);
        }
        if (afc->tmp_block1) gf_free(afc->tmp_block1);
        if (afc->tmp_block2) gf_free(afc->tmp_block2);
        memset(afc, 0, sizeof(GF_AudioFilterChain));
}
void gf_afc_reset(GF_AudioFilterChain *afc)
{
        struct _audiofilterentry *filter = afc->filters;
        while (filter) {
                filter->filter->Reset(filter->filter);
                filter->nb_bytes = 0;
                filter = filter->next;
        }
}
static GF_Err gf_ar_setup_output_format(GF_AudioRenderer *ar)
{
        GF_Err e;
        u32 freq, nb_bits, nb_chan, ch_cfg;
        u32 in_ch, in_cfg, in_bps, in_freq;
        gf_mixer_get_config(ar->mixer, &freq, &nb_chan, &nb_bits, &ch_cfg);
        
        if (ar->disable_multichannel && (nb_chan>2) ) nb_chan = 2;
        in_ch = nb_chan;
        in_cfg = ch_cfg;
        in_bps = nb_bits;
        in_freq = freq;
        if (ar->filter_chain.filters) {
                u32 osr, obps, och, ocfg;
                e = gf_afc_setup(&ar->filter_chain, nb_bits, freq, nb_chan, ch_cfg, &och, &ocfg);
                osr = freq;
                obps = nb_bits;
                nb_chan = och;
                
                if (!e)
                        e = ar->audio_out->ConfigureOutput(ar->audio_out, &osr, &och, &obps, ocfg);
                
                if (e || (osr != freq) || (och != nb_chan) || (obps != nb_bits)) {
                        nb_bits = in_bps;
                        freq = in_freq;
                        nb_chan = in_ch;
                        ar->filter_chain.enable_filters = GF_FALSE;
                        e = ar->audio_out->ConfigureOutput(ar->audio_out, &freq, &nb_chan, &nb_bits, ch_cfg);
                }
        } else {
                e = ar->audio_out->ConfigureOutput(ar->audio_out, &freq, &nb_chan, &nb_bits, ch_cfg);
        }
        if (e) {
                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("[AudioRender] reconfigure error %d\n", e));
                if (nb_chan>2) {
                        nb_chan=2;
                        in_ch=2;
                        ch_cfg=0;
                        e = ar->audio_out->ConfigureOutput(ar->audio_out, &freq, &nb_chan, &nb_bits, ch_cfg);
                }
                if (e) return e;
        }
        gf_mixer_set_config(ar->mixer, freq, nb_chan, nb_bits, in_cfg);
        ar->audio_delay = ar->audio_out->GetAudioDelay(ar->audio_out);
        ar->audio_out->SetVolume(ar->audio_out, ar->volume);
        ar->audio_out->SetPan(ar->audio_out, ar->pan);
        ar->time_at_last_config = ar->current_time;
        ar->bytes_requested = 0;
        ar->bytes_per_second = freq * nb_chan * nb_bits / 8;
        if (ar->audio_listeners) {
                u32 k=0;
                GF_AudioListener *l;
                while ((l = (GF_AudioListener*)gf_list_enum(ar->audio_listeners, &k))) {
                        l->on_audio_reconfig(l->udta, in_freq, in_bps, in_ch, in_cfg);
                }
        }
        return GF_OK;
}
static void gf_ar_pause(GF_AudioRenderer *ar, Bool DoFreeze, Bool for_reconfig, Bool reset_hw_buffer)
{
        gf_mixer_lock(ar->mixer, GF_TRUE);
        if (DoFreeze) {
                if (!ar->Frozen) {
                        ar->freeze_time = gf_sys_clock_high_res();
                        if (!for_reconfig && ar->audio_out && ar->audio_out->Play) ar->audio_out->Play(ar->audio_out, 0);
                        ar->Frozen = GF_TRUE;
                        GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[Audio] pausing master clock - time "LLD" (sys time "LLD")\n", ar->freeze_time, gf_sys_clock_high_res()));
                }
        } else {
                if (ar->Frozen) {
                        if (!for_reconfig && ar->audio_out && ar->audio_out->Play) ar->audio_out->Play(ar->audio_out, reset_hw_buffer ? 2 : 1);
                        ar->Frozen = GF_FALSE;
                        ar->start_time += gf_sys_clock_high_res() - ar->freeze_time;
                        GF_LOG(GF_LOG_DEBUG, GF_LOG_SYNC, ("[Audio] resuming master clock - new time "LLD" (sys time "LLD") \n", ar->start_time, gf_sys_clock_high_res()));
                }
        }
        gf_mixer_lock(ar->mixer, GF_FALSE);
}
static u32 gf_ar_fill_output(void *ptr, char *buffer, u32 buffer_size)
{
        u32 written;
        GF_AudioRenderer *ar = (GF_AudioRenderer *) ptr;
        if (!ar->need_reconfig) {
                u32 delay_ms = ar->disable_resync ?     0 : ar->audio_delay;
                if (ar->Frozen) {
                        memset(buffer, 0, buffer_size);
                        return buffer_size;
                }
                gf_mixer_lock(ar->mixer, GF_TRUE);
                if (ar->filter_chain.enable_filters) {
                        char *ptr = buffer;
                        written = 0;
                        delay_ms += ar->filter_chain.delay_ms;
                        while (buffer_size) {
                                u32 to_copy;
                                if (!ar->nb_used) {
                                        u32 nb_bytes;
                                        
                                        nb_bytes = gf_mixer_get_output(ar->mixer, ar->filter_chain.tmp_block1, ar->filter_chain.min_block_size, delay_ms);
                                        if (!nb_bytes)
                                                return written;
                                        
                                        delay_ms = 0;
                                        ar->nb_filled = gf_afc_process(&ar->filter_chain, nb_bytes);
                                        if (!ar->nb_filled) continue;
                                }
                                to_copy = ar->nb_filled - ar->nb_used;
                                if (to_copy>buffer_size) to_copy = buffer_size;
                                memcpy(ptr, ar->filter_chain.tmp_block1 + ar->nb_used, to_copy);
                                ptr += to_copy;
                                buffer_size -= to_copy;
                                written += to_copy;
                                ar->nb_used += to_copy;
                                if (ar->nb_used==ar->nb_filled) ar->nb_used = 0;
                        }
                } else {
                        gf_mixer_get_output(ar->mixer, buffer, buffer_size, delay_ms);
                }
                gf_mixer_lock(ar->mixer, GF_FALSE);
                
                if (ar->step_mode) {
                        ar->step_mode = GF_FALSE;
                        gf_ar_pause(ar, GF_TRUE, GF_FALSE, GF_FALSE);
                }
                if (!ar->need_reconfig) {
                        if (ar->audio_listeners) {
                                u32 k=0;
                                GF_AudioListener *l;
                                while ((l = (GF_AudioListener*)gf_list_enum(ar->audio_listeners, &k))) {
                                        l->on_audio_frame(l->udta, buffer, buffer_size, gf_sc_ar_get_clock(ar), delay_ms);
                                }
                        }
                        ar->bytes_requested += buffer_size;
                        ar->current_time = ar->time_at_last_config + (u32) (ar->bytes_requested * 1000 / ar->bytes_per_second);
                }
                
                return buffer_size;
        }
        return 0;
}
void gf_sc_flush_next_audio(GF_Compositor *compositor)
{
        if (!compositor->audio_renderer->audio_out)
                return;
        gf_mixer_lock(compositor->audio_renderer->mixer, GF_TRUE);
        compositor->audio_renderer->step_mode = GF_TRUE;
        
        if (compositor->audio_renderer->Frozen) {
                gf_ar_pause(compositor->audio_renderer, GF_FALSE, GF_FALSE, GF_FALSE);
        }
        gf_mixer_lock(compositor->audio_renderer->mixer, GF_FALSE);
}
Bool gf_sc_check_audio_pending(GF_Compositor *compositor)
{
        Bool res = GF_FALSE;
        gf_mixer_lock(compositor->audio_renderer->mixer, GF_TRUE);
        if (compositor->audio_renderer->step_mode)
                res = GF_FALSE;
        gf_mixer_lock(compositor->audio_renderer->mixer, GF_FALSE);
        return res;
}
u32 gf_ar_proc(void *p)
{
        GF_AudioRenderer *ar = (GF_AudioRenderer *) p;
        ar->audio_th_state = 1;
        GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[AudioRender] Entering audio thread ID %d\n", gf_th_id() ));
        gf_mixer_lock(ar->mixer, GF_TRUE);
        ar->need_reconfig = GF_TRUE;
        gf_sc_ar_reconfig(ar);
        gf_mixer_lock(ar->mixer, GF_FALSE);
        while (ar->audio_th_state == 1) {
                
                
              {
                        if (ar->need_reconfig) gf_sc_ar_reconfig(ar);
                        ar->audio_out->WriteAudio(ar->audio_out);
                }
        }
        GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[AudioRender] Exiting audio thread\n"));
        ar->audio_out->Shutdown(ar->audio_out);
        ar->audio_th_state = 3;
        return 0;
}
GF_AudioRenderer *gf_sc_ar_load(GF_User *user)
{
        const char *sOpt;
        u32 i, count;
        u32 num_buffers, total_duration;
        GF_Err e;
        GF_AudioRenderer *ar;
        ar = (GF_AudioRenderer *) gf_malloc(sizeof(GF_AudioRenderer));
        memset(ar, 0, sizeof(GF_AudioRenderer));
        num_buffers = total_duration = 0;
        sOpt = gf_cfg_get_key(user->config, "Audio", "ForceConfig");
        if (sOpt && !stricmp(sOpt, "yes")) {
                sOpt = gf_cfg_get_key(user->config, "Audio", "NumBuffers");
                num_buffers = sOpt ? atoi(sOpt) : 6;
                sOpt = gf_cfg_get_key(user->config, "Audio", "TotalDuration");
                total_duration = sOpt ? atoi(sOpt) : 400;
        }
        sOpt = gf_cfg_get_key(user->config, "Audio", "NoResync");
        ar->disable_resync = (sOpt && !stricmp(sOpt, "yes")) ? GF_TRUE : GF_FALSE;
        sOpt = gf_cfg_get_key(user->config, "Audio", "DisableMultiChannel");
        ar->disable_multichannel = (sOpt && !stricmp(sOpt, "yes")) ? GF_TRUE : GF_FALSE;
        ar->mixer = gf_mixer_new(ar);
        ar->user = user;
        ar->volume = 100;
        sOpt = gf_cfg_get_key(user->config, "Audio", "Volume");
        if (!sOpt) gf_cfg_set_key(user->config, "Audio", "Volume", "100");
        else ar->volume = atoi(sOpt);
        
        sOpt = gf_cfg_get_key(user->config, "Audio", "Pan");
        ar->pan = sOpt ? atoi(sOpt) : 50;
        if (! (user->init_flags & GF_TERM_NO_AUDIO) ) {
                
                sOpt = gf_cfg_get_key(user->config, "Audio", "DriverName");
                if (sOpt) {
                        ar->audio_out = (GF_AudioOutput *) gf_modules_load_interface_by_name(user->modules, sOpt, GF_AUDIO_OUTPUT_INTERFACE);
                        if (!ar->audio_out) {
                                ar->audio_out = NULL;
                                sOpt = NULL;
                        }
                }
                if (!ar->audio_out) {
                        GF_AudioOutput *raw_out = NULL;
                        count = gf_modules_get_count(ar->user->modules);
                        for (i=0; i<count; i++) {
                                ar->audio_out = (GF_AudioOutput *) gf_modules_load_interface(ar->user->modules, i, GF_AUDIO_OUTPUT_INTERFACE);
                                if (!ar->audio_out) continue;
                                
                                if (!stricmp(ar->audio_out->module_name, "Raw Audio Output")) {
                                        raw_out = ar->audio_out;
                                        ar->audio_out = NULL;
                                        continue;
                                }
                                GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[AudioRender] Audio output module %s loaded\n", ar->audio_out->module_name));
                                
                                if ((ar->audio_out->SelfThreaded && ar->audio_out->SetPriority) || ar->audio_out->WriteAudio) {
                                        
                                        gf_cfg_set_key(user->config, "Audio", "DriverName", ar->audio_out->module_name);
                                        break;
                                }
                                gf_modules_close_interface((GF_BaseInterface *)ar->audio_out);
                                ar->audio_out = NULL;
                        }
                        if (raw_out) {
                                if (ar->audio_out) gf_modules_close_interface((GF_BaseInterface *)raw_out);
                                else ar->audio_out = raw_out;
                        }
                }
                
                if (ar->audio_out) {
                        ar->audio_out->FillBuffer = gf_ar_fill_output;
                        ar->audio_out->audio_renderer = ar;
                        GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[AudioRender] Setting up audio module %s\n", ar->audio_out->module_name));
                        e = ar->audio_out->Setup(ar->audio_out, ar->user->os_window_handler, num_buffers, total_duration);
                        
                        gf_afc_load(&ar->filter_chain, user, (char*)gf_cfg_get_key(user->config, "Audio", "Filter"));
                        if (e != GF_OK) {
                                GF_LOG(GF_LOG_ERROR, GF_LOG_MMIO, ("Could not setup audio out %s\n", ar->audio_out->module_name));
                                gf_modules_close_interface((GF_BaseInterface *)ar->audio_out);
                                ar->audio_out = NULL;
                        } else {
                                if (!ar->audio_out->SelfThreaded) {
                                        ar->th = gf_th_new("AudioRenderer");
                                        gf_th_run(ar->th, gf_ar_proc, ar);
                                } else {
                                        gf_ar_setup_output_format(ar);
                                        if (ar->audio_out->SetPriority) ar->audio_out->SetPriority(ar->audio_out, GF_THREAD_PRIORITY_REALTIME);
                                }
                        }
                }
                if (!ar->audio_out) {
                        gf_cfg_set_key(user->config, "Audio", "DriverName", "No Audio Output Available");
                } else {
                        if (user->init_flags & GF_TERM_USE_AUDIO_HW_CLOCK)
                                ar->clock_use_audio_out = GF_TRUE;
                }
        }
        
        ar->start_time = gf_sys_clock_high_res();
        ar->current_time = 0;
        return ar;
}
void gf_sc_ar_del(GF_AudioRenderer *ar)
{
        if (!ar) return;
        GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[AudioRender] Destroying compositor\n"));
        
        if (ar->Frozen) gf_sc_ar_control(ar, GF_SC_AR_RESUME);
        
        if (ar->audio_out) {
                
                if (!ar->audio_out->SelfThreaded) {
                        GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[AudioRender] stopping audio thread\n"));
                        ar->audio_th_state = 2;
                        while (ar->audio_th_state != 3) {
                                gf_sleep(33);
                        }
                        GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[AudioRender] audio thread stopped\n"));
                        gf_th_del(ar->th);
                        GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[AudioRender] audio thread destroyed\n"));
                }
                
                ar->need_reconfig = GF_TRUE;
                gf_mixer_lock(ar->mixer, GF_TRUE);
                if (ar->audio_out->SelfThreaded) ar->audio_out->Shutdown(ar->audio_out);
                gf_modules_close_interface((GF_BaseInterface *)ar->audio_out);
                ar->audio_out = NULL;
                gf_mixer_lock(ar->mixer, GF_FALSE);
        }
        gf_mixer_del(ar->mixer);
        if (ar->audio_listeners) gf_list_del(ar->audio_listeners);
        gf_afc_unload(&ar->filter_chain);
        gf_free(ar);
        GF_LOG(GF_LOG_DEBUG, GF_LOG_AUDIO, ("[AudioRender] Renderer destroyed\n"));
}
void gf_sc_ar_reset(GF_AudioRenderer *ar)
{
        gf_mixer_remove_all(ar->mixer);
}
void gf_sc_ar_control(GF_AudioRenderer *ar, u32 PauseType)
{
        gf_ar_pause(ar, (PauseType==GF_SC_AR_PAUSE) ? GF_TRUE : GF_FALSE, GF_FALSE, (PauseType==GF_SC_AR_RESET_HW_AND_PLAY) ? GF_TRUE : GF_FALSE);
}
void gf_sc_ar_set_volume(GF_AudioRenderer *ar, u32 Volume)
{
        char sOpt[10];
        gf_mixer_lock(ar->mixer, GF_TRUE);
        ar->volume = MIN(Volume, 100);
        if (ar->audio_out) ar->audio_out->SetVolume(ar->audio_out, ar->volume);
        sprintf(sOpt, "%d", ar->volume);
        gf_cfg_set_key(ar->user->config, "Audio", "Volume", sOpt);
        gf_mixer_lock(ar->mixer, GF_FALSE);
}
void gf_sc_ar_mute(GF_AudioRenderer *ar, Bool mute)
{
        gf_mixer_lock(ar->mixer, GF_TRUE);
        ar->mute = mute;
        if (ar->audio_out) ar->audio_out->SetVolume(ar->audio_out, mute ? 0 : ar->volume);
        gf_mixer_lock(ar->mixer, GF_FALSE);
}
void gf_sc_ar_set_pan(GF_AudioRenderer *ar, u32 Balance)
{
        gf_mixer_lock(ar->mixer, GF_TRUE);
        ar->pan = MIN(Balance, 100);
        if (ar->audio_out) ar->audio_out->SetPan(ar->audio_out, ar->pan);
        gf_mixer_lock(ar->mixer, GF_FALSE);
}
void gf_sc_ar_add_src(GF_AudioRenderer *ar, GF_AudioInterface *source)
{
        Bool recfg;
        if (!ar) return;
        
        gf_mixer_lock(ar->mixer, GF_TRUE);
        gf_mixer_add_input(ar->mixer, source);
        
        recfg = gf_mixer_reconfig(ar->mixer);
        if (!ar->need_reconfig) ar->need_reconfig = recfg;
        if (!gf_mixer_empty(ar->mixer) && ar->audio_out && ar->audio_out->Play)
                ar->audio_out->Play(ar->audio_out, 1);
        
        gf_mixer_lock(ar->mixer, GF_FALSE);
}
void gf_sc_ar_remove_src(GF_AudioRenderer *ar, GF_AudioInterface *source)
{
        if (ar) {
                gf_mixer_remove_input(ar->mixer, source);
                if (gf_mixer_empty(ar->mixer) && ar->audio_out && ar->audio_out->Play)
                        ar->audio_out->Play(ar->audio_out, 0);
        }
}
void gf_sc_ar_set_priority(GF_AudioRenderer *ar, u32 priority)
{
        if (ar->audio_out && ar->audio_out->SelfThreaded) {
                ar->audio_out->SetPriority(ar->audio_out, priority);
        } else {
                gf_th_set_priority(ar->th, priority);
        }
}
void gf_sc_ar_reconfig(GF_AudioRenderer *ar)
{
        Bool frozen;
        if (!ar->need_reconfig || !ar->audio_out) return;
        
        gf_mixer_lock(ar->mixer, GF_TRUE);
        frozen = ar->Frozen;
        if (!frozen ) 
                gf_ar_pause(ar, GF_TRUE, GF_TRUE, GF_FALSE);
        
        ar->need_reconfig = GF_FALSE;
        gf_ar_setup_output_format(ar);
        
        if (!frozen)
                gf_ar_pause(ar, GF_FALSE, GF_TRUE, GF_FALSE);
        
        gf_mixer_lock(ar->mixer, GF_FALSE);
}
u32 gf_sc_ar_get_delay(GF_AudioRenderer *ar)
{
        return ar->audio_out->GetAudioDelay(ar->audio_out);
}
u32 gf_sc_ar_get_clock(GF_AudioRenderer *ar)
{
        if (ar->clock_use_audio_out) return ar->current_time;
        if (ar->Frozen) {
                return (u32) ((ar->freeze_time - ar->start_time) / 1000);
        }
        return (u32) ((gf_sys_clock_high_res() - ar->start_time) / 1000);
}
GF_EXPORT
void gf_sc_reload_audio_filters(GF_Compositor *compositor)
{
        GF_AudioRenderer *ar = compositor->audio_renderer;
        if (!ar) return;
        gf_mixer_lock(ar->mixer, GF_TRUE);
        gf_afc_unload(&ar->filter_chain);
        gf_afc_load(&ar->filter_chain, ar->user, (char*)gf_cfg_get_key(ar->user->config, "Audio", "Filter"));
        gf_ar_pause(ar, GF_TRUE, GF_TRUE, GF_FALSE);
        ar->need_reconfig = GF_FALSE;
        gf_ar_setup_output_format(ar);
        gf_ar_pause(ar, GF_FALSE, GF_TRUE, GF_FALSE);
        gf_mixer_lock(ar->mixer, GF_FALSE);
}
GF_EXPORT
GF_Err gf_sc_add_audio_listener(GF_Compositor *compositor, GF_AudioListener *al)
{
        GF_AudioMixer *mixer;
        u32 sr, ch, bps, ch_cfg;
        if (!compositor || !al || !al->on_audio_frame || !al->on_audio_reconfig) return GF_BAD_PARAM;
        if (!compositor->audio_renderer) return GF_NOT_SUPPORTED;
        mixer = compositor->audio_renderer->mixer;
        gf_mixer_lock(mixer, GF_TRUE);
        if (!compositor->audio_renderer->audio_listeners) compositor->audio_renderer->audio_listeners = gf_list_new();
        gf_list_add(compositor->audio_renderer->audio_listeners, al);
        gf_mixer_get_config(mixer, &sr, &ch, &bps, &ch_cfg);
        al->on_audio_reconfig(al->udta, sr, bps, ch, ch_cfg);
        gf_mixer_lock(mixer, GF_FALSE);
        return GF_OK;
}
GF_EXPORT
GF_Err gf_sc_remove_audio_listener(GF_Compositor *compositor, GF_AudioListener *al)
{
        if (!compositor || !al) return GF_BAD_PARAM;
        if (!compositor->audio_renderer) return GF_NOT_SUPPORTED;
        gf_mixer_lock(compositor->audio_renderer->mixer, GF_TRUE);
        gf_list_del_item(compositor->audio_renderer->audio_listeners, al);
        if (!gf_list_count(compositor->audio_renderer->audio_listeners)) {
                gf_list_del(compositor->audio_renderer->audio_listeners);
                compositor->audio_renderer->audio_listeners = NULL;
        }
        gf_mixer_lock(compositor->audio_renderer->mixer, GF_FALSE);
        return GF_OK;
}