void aout_PacketInit (audio_output_t *aout, aout_packet_t *p, unsigned samples) { assert (p == aout_packet (aout)); vlc_mutex_init (&p->lock); aout_FifoInit (&p->partial, aout->format.i_rate); aout_FifoInit (&p->fifo, aout->format.i_rate); p->pause_date = VLC_TS_INVALID; p->time_report = INT64_MIN; p->samples = samples; p->starving = true; }
/***************************************************************************** * aout_InputCheckAndRestart : restart an input ***************************************************************************** * This function must be entered with the input and mixer lock. *****************************************************************************/ void aout_InputCheckAndRestart( aout_instance_t * p_aout, aout_input_t * p_input ) { AOUT_ASSERT_MIXER_LOCKED; AOUT_ASSERT_INPUT_LOCKED; if( !p_input->b_restart ) return; aout_lock_input_fifos( p_aout ); /* A little trick to avoid loosing our input fifo and properties */ uint8_t *p_first_byte_to_mix = p_input->mixer.begin; aout_fifo_t fifo = p_input->mixer.fifo; bool b_paused = p_input->b_paused; mtime_t i_pause_date = p_input->i_pause_date; aout_FifoInit( p_aout, &p_input->mixer.fifo, p_aout->mixer_format.i_rate ); aout_InputDelete( p_aout, p_input ); aout_InputNew( p_aout, p_input, &p_input->request_vout ); p_input->mixer.begin = p_first_byte_to_mix; p_input->mixer.fifo = fifo; p_input->b_paused = b_paused; p_input->i_pause_date = i_pause_date; p_input->b_restart = false; aout_unlock_input_fifos( p_aout ); }
/***************************************************************************** * aout_InputNew : allocate a new input and rework the filter pipeline *****************************************************************************/ int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input, const aout_request_vout_t *p_request_vout ) { audio_sample_format_t chain_input_format; audio_sample_format_t chain_output_format; vlc_value_t val, text; char *psz_filters, *psz_visual, *psz_scaletempo; int i_visual; aout_FormatPrint( p_aout, "input", &p_input->input ); p_input->i_nb_resamplers = p_input->i_nb_filters = 0; /* Prepare FIFO. */ aout_FifoInit( p_aout, &p_input->mixer.fifo, p_aout->mixer_format.i_rate ); p_input->mixer.begin = NULL; /* */ if( p_request_vout ) { p_input->request_vout = *p_request_vout; } else { p_input->request_vout.pf_request_vout = RequestVout; p_input->request_vout.p_private = p_aout; } /* Prepare format structure */ chain_input_format = p_input->input; chain_output_format = p_aout->mixer_format; chain_output_format.i_rate = p_input->input.i_rate; aout_FormatPrepare( &chain_output_format ); /* Now add user filters */ if( var_Type( p_aout, "visual" ) == 0 ) { var_Create( p_aout, "visual", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); text.psz_string = _("Visualizations"); var_Change( p_aout, "visual", VLC_VAR_SETTEXT, &text, NULL ); val.psz_string = (char*)""; text.psz_string = _("Disable"); var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); val.psz_string = (char*)"spectrometer"; text.psz_string = _("Spectrometer"); var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); val.psz_string = (char*)"scope"; text.psz_string = _("Scope"); var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); val.psz_string = (char*)"spectrum"; text.psz_string = _("Spectrum"); var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); val.psz_string = (char*)"vuMeter"; text.psz_string = _("Vu meter"); var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); /* Look for goom plugin */ if( module_exists( "goom" ) ) { val.psz_string = (char*)"goom"; text.psz_string = (char*)"Goom"; var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); } /* Look for libprojectM plugin */ if( module_exists( "projectm" ) ) { val.psz_string = (char*)"projectm"; text.psz_string = (char*)"projectM"; var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); } if( var_Get( p_aout, "effect-list", &val ) == VLC_SUCCESS ) { var_SetString( p_aout, "visual", val.psz_string ); free( val.psz_string ); } var_AddCallback( p_aout, "visual", VisualizationCallback, NULL ); } if( var_Type( p_aout, "equalizer" ) == 0 ) { module_config_t *p_config; int i; p_config = config_FindConfig( VLC_OBJECT(p_aout), "equalizer-preset" ); if( p_config && p_config->i_list ) { var_Create( p_aout, "equalizer", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); text.psz_string = _("Equalizer"); var_Change( p_aout, "equalizer", VLC_VAR_SETTEXT, &text, NULL ); val.psz_string = (char*)""; text.psz_string = _("Disable"); var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text ); for( i = 0; i < p_config->i_list; i++ ) { val.psz_string = (char *)p_config->ppsz_list[i]; text.psz_string = (char *)p_config->ppsz_list_text[i]; var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text ); } var_AddCallback( p_aout, "equalizer", EqualizerCallback, NULL ); } } if( var_Type( p_aout, "audio-filter" ) == 0 ) { var_Create( p_aout, "audio-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); text.psz_string = _("Audio filters"); var_Change( p_aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL ); } if( var_Type( p_aout, "audio-visual" ) == 0 ) { var_Create( p_aout, "audio-visual", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); text.psz_string = _("Audio visualizations"); var_Change( p_aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL ); } if( var_Type( p_aout, "audio-replay-gain-mode" ) == 0 ) { module_config_t *p_config; int i; p_config = config_FindConfig( VLC_OBJECT(p_aout), "audio-replay-gain-mode" ); if( p_config && p_config->i_list ) { var_Create( p_aout, "audio-replay-gain-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); text.psz_string = _("Replay gain"); var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_SETTEXT, &text, NULL ); for( i = 0; i < p_config->i_list; i++ ) { val.psz_string = (char *)p_config->ppsz_list[i]; text.psz_string = (char *)p_config->ppsz_list_text[i]; var_Change( p_aout, "audio-replay-gain-mode", VLC_VAR_ADDCHOICE, &val, &text ); } var_AddCallback( p_aout, "audio-replay-gain-mode", ReplayGainCallback, NULL ); } } if( var_Type( p_aout, "audio-replay-gain-preamp" ) == 0 ) { var_Create( p_aout, "audio-replay-gain-preamp", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); } if( var_Type( p_aout, "audio-replay-gain-default" ) == 0 ) { var_Create( p_aout, "audio-replay-gain-default", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT ); } if( var_Type( p_aout, "audio-replay-gain-peak-protection" ) == 0 ) { var_Create( p_aout, "audio-replay-gain-peak-protection", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); } if( var_Type( p_aout, "audio-time-stretch" ) == 0 ) { var_Create( p_aout, "audio-time-stretch", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); } psz_filters = var_GetString( p_aout, "audio-filter" ); psz_visual = var_GetString( p_aout, "audio-visual"); psz_scaletempo = var_GetBool( p_aout, "audio-time-stretch" ) ? strdup( "scaletempo" ) : NULL; p_input->b_recycle_vout = psz_visual && *psz_visual; /* parse user filter lists */ char *const ppsz_array[] = { psz_scaletempo, psz_filters, psz_visual }; p_input->p_playback_rate_filter = NULL; for( i_visual = 0; i_visual < 3 && !AOUT_FMT_NON_LINEAR(&chain_output_format); i_visual++ ) { char *psz_next = NULL; char *psz_parser = ppsz_array[i_visual]; if( psz_parser == NULL || !*psz_parser ) continue; while( psz_parser && *psz_parser ) { filter_t * p_filter = NULL; if( p_input->i_nb_filters >= AOUT_MAX_FILTERS ) { msg_Dbg( p_aout, "max filters reached (%d)", AOUT_MAX_FILTERS ); break; } while( *psz_parser == ' ' && *psz_parser == ':' ) { psz_parser++; } if( ( psz_next = strchr( psz_parser , ':' ) ) ) { *psz_next++ = '\0'; } if( *psz_parser =='\0' ) { break; } /* Create a VLC object */ static const char typename[] = "audio filter"; p_filter = vlc_custom_create( p_aout, sizeof(*p_filter), VLC_OBJECT_GENERIC, typename ); if( p_filter == NULL ) { msg_Err( p_aout, "cannot add user filter %s (skipped)", psz_parser ); psz_parser = psz_next; continue; } vlc_object_attach( p_filter , p_aout ); p_filter->p_owner = malloc( sizeof(*p_filter->p_owner) ); p_filter->p_owner->p_aout = p_aout; p_filter->p_owner->p_input = p_input; /* request format */ memcpy( &p_filter->fmt_in.audio, &chain_output_format, sizeof(audio_sample_format_t) ); p_filter->fmt_in.i_codec = chain_output_format.i_format; memcpy( &p_filter->fmt_out.audio, &chain_output_format, sizeof(audio_sample_format_t) ); p_filter->fmt_out.i_codec = chain_output_format.i_format; p_filter->pf_audio_buffer_new = aout_FilterBufferNew; /* try to find the requested filter */ if( i_visual == 2 ) /* this can only be a visualization module */ { p_filter->p_module = module_need( p_filter, "visualization2", psz_parser, true ); } else /* this can be a audio filter module as well as a visualization module */ { p_filter->p_module = module_need( p_filter, "audio filter", psz_parser, true ); if ( p_filter->p_module == NULL ) { /* if the filter requested a special format, retry */ if ( !( AOUT_FMTS_IDENTICAL( &p_filter->fmt_in.audio, &chain_input_format ) && AOUT_FMTS_IDENTICAL( &p_filter->fmt_out.audio, &chain_output_format ) ) ) { aout_FormatPrepare( &p_filter->fmt_in.audio ); aout_FormatPrepare( &p_filter->fmt_out.audio ); p_filter->p_module = module_need( p_filter, "audio filter", psz_parser, true ); } /* try visual filters */ else { memcpy( &p_filter->fmt_in.audio, &chain_output_format, sizeof(audio_sample_format_t) ); memcpy( &p_filter->fmt_out.audio, &chain_output_format, sizeof(audio_sample_format_t) ); p_filter->p_module = module_need( p_filter, "visualization2", psz_parser, true ); } } } /* failure */ if ( p_filter->p_module == NULL ) { msg_Err( p_aout, "cannot add user filter %s (skipped)", psz_parser ); free( p_filter->p_owner ); vlc_object_release( p_filter ); psz_parser = psz_next; continue; } /* complete the filter chain if necessary */ if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &p_filter->fmt_in.audio ) ) { if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters, &p_input->i_nb_filters, &chain_input_format, &p_filter->fmt_in.audio ) < 0 ) { msg_Err( p_aout, "cannot add user filter %s (skipped)", psz_parser ); module_unneed( p_filter, p_filter->p_module ); free( p_filter->p_owner ); vlc_object_release( p_filter ); psz_parser = psz_next; continue; } } /* success */ p_input->pp_filters[p_input->i_nb_filters++] = p_filter; memcpy( &chain_input_format, &p_filter->fmt_out.audio, sizeof( audio_sample_format_t ) ); if( i_visual == 0 ) /* scaletempo */ p_input->p_playback_rate_filter = p_filter; /* next filter if any */ psz_parser = psz_next; } } free( psz_visual ); free( psz_filters ); free( psz_scaletempo ); /* complete the filter chain if necessary */ if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &chain_output_format ) ) { if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters, &p_input->i_nb_filters, &chain_input_format, &chain_output_format ) < 0 ) { inputFailure( p_aout, p_input, "couldn't set an input pipeline" ); return -1; } } /* Prepare hints for the buffer allocator. */ p_input->input_alloc.b_alloc = true; p_input->input_alloc.i_bytes_per_sec = -1; /* Create resamplers. */ if ( !AOUT_FMT_NON_LINEAR( &p_aout->mixer_format ) ) { chain_output_format.i_rate = (__MAX(p_input->input.i_rate, p_aout->mixer_format.i_rate) * (100 + AOUT_MAX_RESAMPLING)) / 100; if ( chain_output_format.i_rate == p_aout->mixer_format.i_rate ) { /* Just in case... */ chain_output_format.i_rate++; } if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_resamplers, &p_input->i_nb_resamplers, &chain_output_format, &p_aout->mixer_format ) < 0 ) { inputFailure( p_aout, p_input, "couldn't set a resampler pipeline"); return -1; } aout_FiltersHintBuffers( p_aout, p_input->pp_resamplers, p_input->i_nb_resamplers, &p_input->input_alloc ); p_input->input_alloc.b_alloc = true; /* Setup the initial rate of the resampler */ p_input->pp_resamplers[0]->fmt_in.audio.i_rate = p_input->input.i_rate; } p_input->i_resampling_type = AOUT_RESAMPLING_NONE; if( ! p_input->p_playback_rate_filter && p_input->i_nb_resamplers > 0 ) { p_input->p_playback_rate_filter = p_input->pp_resamplers[0]; } aout_FiltersHintBuffers( p_aout, p_input->pp_filters, p_input->i_nb_filters, &p_input->input_alloc ); p_input->input_alloc.b_alloc = true; /* i_bytes_per_sec is still == -1 if no filters */ p_input->input_alloc.i_bytes_per_sec = __MAX( p_input->input_alloc.i_bytes_per_sec, (int)(p_input->input.i_bytes_per_frame * p_input->input.i_rate / p_input->input.i_frame_length) ); ReplayGainSelect( p_aout, p_input ); /* Success */ p_input->b_error = false; p_input->i_last_input_rate = INPUT_RATE_DEFAULT; return 0; }
/***************************************************************************** * aout_OutputNew : allocate a new output and rework the filter pipeline ***************************************************************************** * This function is entered with the mixer lock. *****************************************************************************/ int aout_OutputNew( aout_instance_t * p_aout, audio_sample_format_t * p_format ) { /* Retrieve user defaults. */ int i_rate = var_InheritInteger( p_aout, "aout-rate" ); vlc_value_t val, text; /* kludge to avoid a fpu error when rate is 0... */ if( i_rate == 0 ) i_rate = -1; memcpy( &p_aout->output.output, p_format, sizeof(audio_sample_format_t) ); if ( i_rate != -1 ) p_aout->output.output.i_rate = i_rate; aout_FormatPrepare( &p_aout->output.output ); /* Find the best output plug-in. */ p_aout->output.p_module = module_need( p_aout, "audio output", "$aout", false ); if ( p_aout->output.p_module == NULL ) { msg_Err( p_aout, "no suitable audio output module" ); return -1; } if ( var_Type( p_aout, "audio-channels" ) == (VLC_VAR_INTEGER | VLC_VAR_HASCHOICE) ) { /* The user may have selected a different channels configuration. */ var_Get( p_aout, "audio-channels", &val ); if ( val.i_int == AOUT_VAR_CHAN_RSTEREO ) { p_aout->output.output.i_original_channels |= AOUT_CHAN_REVERSESTEREO; } else if ( val.i_int == AOUT_VAR_CHAN_STEREO ) { p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; } else if ( val.i_int == AOUT_VAR_CHAN_LEFT ) { p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT; } else if ( val.i_int == AOUT_VAR_CHAN_RIGHT ) { p_aout->output.output.i_original_channels = AOUT_CHAN_RIGHT; } else if ( val.i_int == AOUT_VAR_CHAN_DOLBYS ) { p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO; } } else if ( p_aout->output.output.i_physical_channels == AOUT_CHAN_CENTER && (p_aout->output.output.i_original_channels & AOUT_CHAN_PHYSMASK) == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) ) { /* Mono - create the audio-channels variable. */ var_Create( p_aout, "audio-channels", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); text.psz_string = _("Audio Channels"); var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL ); val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DUALMONO ) { /* Go directly to the left channel. */ p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT; var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT ); } var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart, NULL ); } else if ( p_aout->output.output.i_physical_channels == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) && (p_aout->output.output.i_original_channels & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) ) { /* Stereo - create the audio-channels variable. */ var_Create( p_aout, "audio-channels", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); text.psz_string = _("Audio Channels"); var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL ); if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DOLBYSTEREO ) { val.i_int = AOUT_VAR_CHAN_DOLBYS; text.psz_string = _("Dolby Surround"); } else { val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo"); } var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_RSTEREO; text.psz_string=_("Reverse stereo"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DUALMONO ) { /* Go directly to the left channel. */ p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT; var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT ); } var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart, NULL ); } var_SetBool( p_aout, "intf-change", true ); aout_FormatPrepare( &p_aout->output.output ); aout_lock_output_fifo( p_aout ); /* Prepare FIFO. */ aout_FifoInit( p_aout, &p_aout->output.fifo, p_aout->output.output.i_rate ); aout_unlock_output_fifo( p_aout ); aout_FormatPrint( p_aout, "output", &p_aout->output.output ); /* Calculate the resulting mixer output format. */ p_aout->mixer_format = p_aout->output.output; if ( !AOUT_FMT_NON_LINEAR(&p_aout->output.output) ) { /* Non-S/PDIF mixer only deals with float32 or fixed32. */ p_aout->mixer_format.i_format = HAVE_FPU ? VLC_CODEC_FL32 : VLC_CODEC_FI32; aout_FormatPrepare( &p_aout->mixer_format ); } else { p_aout->mixer_format.i_format = p_format->i_format; } aout_FormatPrint( p_aout, "mixer", &p_aout->mixer_format ); /* Create filters. */ p_aout->output.i_nb_filters = 0; if ( aout_FiltersCreatePipeline( p_aout, p_aout->output.pp_filters, &p_aout->output.i_nb_filters, &p_aout->mixer_format, &p_aout->output.output ) < 0 ) { msg_Err( p_aout, "couldn't create audio output pipeline" ); module_unneed( p_aout, p_aout->output.p_module ); return -1; } /* Prepare hints for the buffer allocator. */ p_aout->mixer_allocation.b_alloc = true; p_aout->mixer_allocation.i_bytes_per_sec = p_aout->mixer_format.i_bytes_per_frame * p_aout->mixer_format.i_rate / p_aout->mixer_format.i_frame_length; aout_FiltersHintBuffers( p_aout, p_aout->output.pp_filters, p_aout->output.i_nb_filters, &p_aout->mixer_allocation ); p_aout->output.b_error = 0; return 0; }
/***************************************************************************** * aout_OutputNew : allocate a new output and rework the filter pipeline ***************************************************************************** * This function is entered with the mixer lock. *****************************************************************************/ int aout_OutputNew( aout_instance_t * p_aout, const audio_sample_format_t * p_format ) { vlc_assert_locked( &p_aout->lock ); p_aout->output.output = *p_format; /* Retrieve user defaults. */ int i_rate = var_InheritInteger( p_aout, "aout-rate" ); if ( i_rate != 0 ) p_aout->output.output.i_rate = i_rate; aout_FormatPrepare( &p_aout->output.output ); /* Find the best output plug-in. */ p_aout->output.p_module = module_need( p_aout, "audio output", "$aout", false ); if ( p_aout->output.p_module == NULL ) { msg_Err( p_aout, "no suitable audio output module" ); return -1; } if ( var_Type( p_aout, "audio-channels" ) == (VLC_VAR_INTEGER | VLC_VAR_HASCHOICE) ) { /* The user may have selected a different channels configuration. */ switch( var_InheritInteger( p_aout, "audio-channels" ) ) { case AOUT_VAR_CHAN_RSTEREO: p_aout->output.output.i_original_channels |= AOUT_CHAN_REVERSESTEREO; break; case AOUT_VAR_CHAN_STEREO: p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; break; case AOUT_VAR_CHAN_LEFT: p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT; break; case AOUT_VAR_CHAN_RIGHT: p_aout->output.output.i_original_channels = AOUT_CHAN_RIGHT; break; case AOUT_VAR_CHAN_DOLBYS: p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_DOLBYSTEREO; break; } } else if ( p_aout->output.output.i_physical_channels == AOUT_CHAN_CENTER && (p_aout->output.output.i_original_channels & AOUT_CHAN_PHYSMASK) == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) ) { vlc_value_t val, text; /* Mono - create the audio-channels variable. */ var_Create( p_aout, "audio-channels", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); text.psz_string = _("Audio Channels"); var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL ); val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DUALMONO ) { /* Go directly to the left channel. */ p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT; var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT ); } var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart, NULL ); } else if ( p_aout->output.output.i_physical_channels == (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT) && (p_aout->output.output.i_original_channels & (AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT)) ) { vlc_value_t val, text; /* Stereo - create the audio-channels variable. */ var_Create( p_aout, "audio-channels", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); text.psz_string = _("Audio Channels"); var_Change( p_aout, "audio-channels", VLC_VAR_SETTEXT, &text, NULL ); if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DOLBYSTEREO ) { val.i_int = AOUT_VAR_CHAN_DOLBYS; text.psz_string = _("Dolby Surround"); } else { val.i_int = AOUT_VAR_CHAN_STEREO; text.psz_string = _("Stereo"); } var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_LEFT; text.psz_string = _("Left"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_RIGHT; text.psz_string = _("Right"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); val.i_int = AOUT_VAR_CHAN_RSTEREO; text.psz_string=_("Reverse stereo"); var_Change( p_aout, "audio-channels", VLC_VAR_ADDCHOICE, &val, &text ); if ( p_aout->output.output.i_original_channels & AOUT_CHAN_DUALMONO ) { /* Go directly to the left channel. */ p_aout->output.output.i_original_channels = AOUT_CHAN_LEFT; var_SetInteger( p_aout, "audio-channels", AOUT_VAR_CHAN_LEFT ); } var_AddCallback( p_aout, "audio-channels", aout_ChannelsRestart, NULL ); } var_TriggerCallback( p_aout, "intf-change" ); aout_FormatPrepare( &p_aout->output.output ); /* Prepare FIFO. */ aout_FifoInit( p_aout, &p_aout->output.fifo, p_aout->output.output.i_rate ); aout_FormatPrint( p_aout, "output", &p_aout->output.output ); /* Choose the mixer format. */ p_aout->mixer_format = p_aout->output.output; if ( AOUT_FMT_NON_LINEAR(&p_aout->output.output) ) p_aout->mixer_format.i_format = p_format->i_format; else /* Most audio filters can only deal with single-precision, * so lets always use that when hardware supports floating point. */ if( HAVE_FPU ) p_aout->mixer_format.i_format = VLC_CODEC_FL32; else /* Otherwise, audio filters will not work. Use fixed-point if the input has * more than 16-bits depth. */ if( p_format->i_bitspersample > 16 ) p_aout->mixer_format.i_format = VLC_CODEC_FI32; else /* Fallback to 16-bits. This avoids pointless conversion to and from * 32-bits samples for the sole purpose of software mixing. */ p_aout->mixer_format.i_format = VLC_CODEC_S16N; aout_FormatPrepare( &p_aout->mixer_format ); aout_FormatPrint( p_aout, "mixer", &p_aout->mixer_format ); /* Create filters. */ p_aout->output.i_nb_filters = 0; if ( aout_FiltersCreatePipeline( p_aout, p_aout->output.pp_filters, &p_aout->output.i_nb_filters, &p_aout->mixer_format, &p_aout->output.output ) < 0 ) { msg_Err( p_aout, "couldn't create audio output pipeline" ); module_unneed( p_aout, p_aout->output.p_module ); p_aout->output.p_module = NULL; return -1; } return 0; }
/***************************************************************************** * aout_InputNew : allocate a new input and rework the filter pipeline *****************************************************************************/ int aout_InputNew( aout_instance_t * p_aout, aout_input_t * p_input ) { audio_sample_format_t chain_input_format; audio_sample_format_t chain_output_format; vlc_value_t val, text; char * psz_filters, *psz_visual; int i_visual; aout_FormatPrint( p_aout, "input", &p_input->input ); p_input->i_nb_filters = 0; /* Prepare FIFO. */ aout_FifoInit( p_aout, &p_input->fifo, p_aout->mixer.mixer.i_rate ); p_input->p_first_byte_to_mix = NULL; /* Prepare format structure */ memcpy( &chain_input_format, &p_input->input, sizeof(audio_sample_format_t) ); memcpy( &chain_output_format, &p_aout->mixer.mixer, sizeof(audio_sample_format_t) ); chain_output_format.i_rate = p_input->input.i_rate; aout_FormatPrepare( &chain_output_format ); /* Now add user filters */ if( var_Type( p_aout, "visual" ) == 0 ) { module_t *p_module; var_Create( p_aout, "visual", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); text.psz_string = _("Visualizations"); var_Change( p_aout, "visual", VLC_VAR_SETTEXT, &text, NULL ); val.psz_string = ""; text.psz_string = _("Disable"); var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); val.psz_string = "spectrometer"; text.psz_string = _("Spectrometer"); var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); val.psz_string = "scope"; text.psz_string = _("Scope"); var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); val.psz_string = "spectrum"; text.psz_string = _("Spectrum"); var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); /* Look for goom plugin */ p_module = config_FindModule( VLC_OBJECT(p_aout), "goom" ); if( p_module ) { val.psz_string = "goom"; text.psz_string = "Goom"; var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); } /* Look for galaktos plugin */ p_module = config_FindModule( VLC_OBJECT(p_aout), "galaktos" ); if( p_module ) { val.psz_string = "galaktos"; text.psz_string = "GaLaktos"; var_Change( p_aout, "visual", VLC_VAR_ADDCHOICE, &val, &text ); } if( var_Get( p_aout, "effect-list", &val ) == VLC_SUCCESS ) { var_Set( p_aout, "visual", val ); if( val.psz_string ) free( val.psz_string ); } var_AddCallback( p_aout, "visual", VisualizationCallback, NULL ); } if( var_Type( p_aout, "equalizer" ) == 0 ) { module_config_t *p_config; int i; p_config = config_FindConfig( VLC_OBJECT(p_aout), "equalizer-preset" ); if( p_config && p_config->i_list ) { var_Create( p_aout, "equalizer", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); text.psz_string = _("Equalizer"); var_Change( p_aout, "equalizer", VLC_VAR_SETTEXT, &text, NULL ); val.psz_string = ""; text.psz_string = _("Disable"); var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text ); for( i = 0; i < p_config->i_list; i++ ) { val.psz_string = p_config->ppsz_list[i]; text.psz_string = p_config->ppsz_list_text[i]; var_Change( p_aout, "equalizer", VLC_VAR_ADDCHOICE, &val, &text ); } var_AddCallback( p_aout, "equalizer", EqualizerCallback, NULL ); } } if( var_Type( p_aout, "audio-filter" ) == 0 ) { var_Create( p_aout, "audio-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); text.psz_string = _("Audio filters"); var_Change( p_aout, "audio-filter", VLC_VAR_SETTEXT, &text, NULL ); } if( var_Type( p_aout, "audio-visual" ) == 0 ) { var_Create( p_aout, "audio-visual", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); text.psz_string = _("Audio visualizations"); var_Change( p_aout, "audio-visual", VLC_VAR_SETTEXT, &text, NULL ); } var_Get( p_aout, "audio-filter", &val ); psz_filters = val.psz_string; var_Get( p_aout, "audio-visual", &val ); psz_visual = val.psz_string; /* parse user filter lists */ for( i_visual = 0; i_visual < 2; i_visual++ ) { char *psz_next = NULL; char *psz_parser = i_visual ? psz_visual : psz_filters; if( psz_parser == NULL || !*psz_parser ) continue; while( psz_parser && *psz_parser ) { aout_filter_t * p_filter = NULL; if( p_input->i_nb_filters >= AOUT_MAX_FILTERS ) { msg_Dbg( p_aout, "max filters reached (%d)", AOUT_MAX_FILTERS ); break; } while( *psz_parser == ' ' && *psz_parser == ':' ) { psz_parser++; } if( ( psz_next = strchr( psz_parser , ':' ) ) ) { *psz_next++ = '\0'; } if( *psz_parser =='\0' ) { break; } /* Create a VLC object */ p_filter = vlc_object_create( p_aout, sizeof(aout_filter_t) ); if( p_filter == NULL ) { msg_Err( p_aout, "cannot add user filter %s (skipped)", psz_parser ); psz_parser = psz_next; continue; } vlc_object_attach( p_filter , p_aout ); /* try to find the requested filter */ if( i_visual == 1 ) /* this can only be a visualization module */ { /* request format */ memcpy( &p_filter->input, &chain_output_format, sizeof(audio_sample_format_t) ); memcpy( &p_filter->output, &chain_output_format, sizeof(audio_sample_format_t) ); p_filter->p_module = module_Need( p_filter, "visualization", psz_parser, VLC_TRUE ); } else /* this can be a audio filter module as well as a visualization module */ { /* request format */ memcpy( &p_filter->input, &chain_input_format, sizeof(audio_sample_format_t) ); memcpy( &p_filter->output, &chain_output_format, sizeof(audio_sample_format_t) ); p_filter->p_module = module_Need( p_filter, "audio filter", psz_parser, VLC_TRUE ); if ( p_filter->p_module == NULL ) { /* if the filter requested a special format, retry */ if ( !( AOUT_FMTS_IDENTICAL( &p_filter->input, &chain_input_format ) && AOUT_FMTS_IDENTICAL( &p_filter->output, &chain_output_format ) ) ) { aout_FormatPrepare( &p_filter->input ); aout_FormatPrepare( &p_filter->output ); p_filter->p_module = module_Need( p_filter, "audio filter", psz_parser, VLC_TRUE ); } /* try visual filters */ else { memcpy( &p_filter->input, &chain_output_format, sizeof(audio_sample_format_t) ); memcpy( &p_filter->output, &chain_output_format, sizeof(audio_sample_format_t) ); p_filter->p_module = module_Need( p_filter, "visualization", psz_parser, VLC_TRUE ); } } } /* failure */ if ( p_filter->p_module == NULL ) { msg_Err( p_aout, "cannot add user filter %s (skipped)", psz_parser ); vlc_object_detach( p_filter ); vlc_object_destroy( p_filter ); psz_parser = psz_next; continue; } /* complete the filter chain if necessary */ if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &p_filter->input ) ) { if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters, &p_input->i_nb_filters, &chain_input_format, &p_filter->input ) < 0 ) { msg_Err( p_aout, "cannot add user filter %s (skipped)", psz_parser ); module_Unneed( p_filter, p_filter->p_module ); vlc_object_detach( p_filter ); vlc_object_destroy( p_filter ); psz_parser = psz_next; continue; } } /* success */ p_filter->b_continuity = VLC_FALSE; p_input->pp_filters[p_input->i_nb_filters++] = p_filter; memcpy( &chain_input_format, &p_filter->output, sizeof( audio_sample_format_t ) ); /* next filter if any */ psz_parser = psz_next; } } if( psz_filters ) free( psz_filters ); if( psz_visual ) free( psz_visual ); /* complete the filter chain if necessary */ if ( !AOUT_FMTS_IDENTICAL( &chain_input_format, &chain_output_format ) ) { if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_filters, &p_input->i_nb_filters, &chain_input_format, &chain_output_format ) < 0 ) { inputFailure( p_aout, p_input, "couldn't set an input pipeline" ); return -1; } } /* Prepare hints for the buffer allocator. */ p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP; p_input->input_alloc.i_bytes_per_sec = -1; /* Create resamplers. */ if ( AOUT_FMT_NON_LINEAR( &p_aout->mixer.mixer ) ) { p_input->i_nb_resamplers = 0; } else { chain_output_format.i_rate = (__MAX(p_input->input.i_rate, p_aout->mixer.mixer.i_rate) * (100 + AOUT_MAX_RESAMPLING)) / 100; if ( chain_output_format.i_rate == p_aout->mixer.mixer.i_rate ) { /* Just in case... */ chain_output_format.i_rate++; } p_input->i_nb_resamplers = 0; if ( aout_FiltersCreatePipeline( p_aout, p_input->pp_resamplers, &p_input->i_nb_resamplers, &chain_output_format, &p_aout->mixer.mixer ) < 0 ) { inputFailure( p_aout, p_input, "couldn't set a resampler pipeline"); return -1; } aout_FiltersHintBuffers( p_aout, p_input->pp_resamplers, p_input->i_nb_resamplers, &p_input->input_alloc ); p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP; /* Setup the initial rate of the resampler */ p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; } p_input->i_resampling_type = AOUT_RESAMPLING_NONE; aout_FiltersHintBuffers( p_aout, p_input->pp_filters, p_input->i_nb_filters, &p_input->input_alloc ); p_input->input_alloc.i_alloc_type = AOUT_ALLOC_HEAP; /* i_bytes_per_sec is still == -1 if no filters */ p_input->input_alloc.i_bytes_per_sec = __MAX( p_input->input_alloc.i_bytes_per_sec, (int)(p_input->input.i_bytes_per_frame * p_input->input.i_rate / p_input->input.i_frame_length) ); /* Success */ p_input->b_error = VLC_FALSE; p_input->b_restart = VLC_FALSE; return 0; }
/***************************************************************************** * aout_InputPlay : play a buffer ***************************************************************************** * This function must be entered with the input lock. *****************************************************************************/ int aout_InputPlay( aout_instance_t * p_aout, aout_input_t * p_input, aout_buffer_t * p_buffer ) { mtime_t start_date; if( p_input->b_restart ) { aout_fifo_t fifo, dummy_fifo; byte_t *p_first_byte_to_mix; vlc_mutex_lock( &p_aout->mixer_lock ); /* A little trick to avoid loosing our input fifo */ aout_FifoInit( p_aout, &dummy_fifo, p_aout->mixer.mixer.i_rate ); p_first_byte_to_mix = p_input->p_first_byte_to_mix; fifo = p_input->fifo; p_input->fifo = dummy_fifo; aout_InputDelete( p_aout, p_input ); aout_InputNew( p_aout, p_input ); p_input->p_first_byte_to_mix = p_first_byte_to_mix; p_input->fifo = fifo; vlc_mutex_unlock( &p_aout->mixer_lock ); } /* We don't care if someone changes the start date behind our back after * this. We'll deal with that when pushing the buffer, and compensate * with the next incoming buffer. */ vlc_mutex_lock( &p_aout->input_fifos_lock ); start_date = aout_FifoNextStart( p_aout, &p_input->fifo ); vlc_mutex_unlock( &p_aout->input_fifos_lock ); if ( start_date != 0 && start_date < mdate() ) { /* The decoder is _very_ late. This can only happen if the user * pauses the stream (or if the decoder is buggy, which cannot * happen :). */ msg_Warn( p_aout, "computed PTS is out of range ("I64Fd"), " "clearing out", mdate() - start_date ); vlc_mutex_lock( &p_aout->input_fifos_lock ); aout_FifoSet( p_aout, &p_input->fifo, 0 ); p_input->p_first_byte_to_mix = NULL; vlc_mutex_unlock( &p_aout->input_fifos_lock ); if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) msg_Warn( p_aout, "timing screwed, stopping resampling" ); p_input->i_resampling_type = AOUT_RESAMPLING_NONE; if ( p_input->i_nb_resamplers != 0 ) { p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; p_input->pp_resamplers[0]->b_continuity = VLC_FALSE; } start_date = 0; if( p_input->p_input_thread ) { stats_UpdateInteger( p_input->p_input_thread, STATS_LOST_ABUFFERS, 1, NULL ); } } if ( p_buffer->start_date < mdate() + AOUT_MIN_PREPARE_TIME ) { /* The decoder gives us f*cked up PTS. It's its business, but we * can't present it anyway, so drop the buffer. */ msg_Warn( p_aout, "PTS is out of range ("I64Fd"), dropping buffer", mdate() - p_buffer->start_date ); if( p_input->p_input_thread ) { stats_UpdateInteger( p_input->p_input_thread, STATS_LOST_ABUFFERS, 1, NULL ); } aout_BufferFree( p_buffer ); p_input->i_resampling_type = AOUT_RESAMPLING_NONE; if ( p_input->i_nb_resamplers != 0 ) { p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; p_input->pp_resamplers[0]->b_continuity = VLC_FALSE; } return 0; } /* If the audio drift is too big then it's not worth trying to resample * the audio. */ if ( start_date != 0 && ( start_date < p_buffer->start_date - 3 * AOUT_PTS_TOLERANCE ) ) { msg_Warn( p_aout, "audio drift is too big ("I64Fd"), clearing out", start_date - p_buffer->start_date ); vlc_mutex_lock( &p_aout->input_fifos_lock ); aout_FifoSet( p_aout, &p_input->fifo, 0 ); p_input->p_first_byte_to_mix = NULL; vlc_mutex_unlock( &p_aout->input_fifos_lock ); if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) msg_Warn( p_aout, "timing screwed, stopping resampling" ); p_input->i_resampling_type = AOUT_RESAMPLING_NONE; if ( p_input->i_nb_resamplers != 0 ) { p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; p_input->pp_resamplers[0]->b_continuity = VLC_FALSE; } start_date = 0; } else if ( start_date != 0 && ( start_date > p_buffer->start_date + 3 * AOUT_PTS_TOLERANCE ) ) { msg_Warn( p_aout, "audio drift is too big ("I64Fd"), dropping buffer", start_date - p_buffer->start_date ); aout_BufferFree( p_buffer ); if( p_input->p_input_thread ) { stats_UpdateInteger( p_input->p_input_thread, STATS_LOST_ABUFFERS, 1, NULL ); } return 0; } if ( start_date == 0 ) start_date = p_buffer->start_date; /* Run pre-filters. */ aout_FiltersPlay( p_aout, p_input->pp_filters, p_input->i_nb_filters, &p_buffer ); /* Run the resampler if needed. * We first need to calculate the output rate of this resampler. */ if ( ( p_input->i_resampling_type == AOUT_RESAMPLING_NONE ) && ( start_date < p_buffer->start_date - AOUT_PTS_TOLERANCE || start_date > p_buffer->start_date + AOUT_PTS_TOLERANCE ) && p_input->i_nb_resamplers > 0 ) { /* Can happen in several circumstances : * 1. A problem at the input (clock drift) * 2. A small pause triggered by the user * 3. Some delay in the output stage, causing a loss of lip * synchronization * Solution : resample the buffer to avoid a scratch. */ mtime_t drift = p_buffer->start_date - start_date; p_input->i_resamp_start_date = mdate(); p_input->i_resamp_start_drift = (int)drift; if ( drift > 0 ) p_input->i_resampling_type = AOUT_RESAMPLING_DOWN; else p_input->i_resampling_type = AOUT_RESAMPLING_UP; msg_Warn( p_aout, "buffer is "I64Fd" %s, triggering %ssampling", drift > 0 ? drift : -drift, drift > 0 ? "in advance" : "late", drift > 0 ? "down" : "up"); } if ( p_input->i_resampling_type != AOUT_RESAMPLING_NONE ) { /* Resampling has been triggered previously (because of dates * mismatch). We want the resampling to happen progressively so * it isn't too audible to the listener. */ if( p_input->i_resampling_type == AOUT_RESAMPLING_UP ) { p_input->pp_resamplers[0]->input.i_rate += 2; /* Hz */ } else { p_input->pp_resamplers[0]->input.i_rate -= 2; /* Hz */ } /* Check if everything is back to normal, in which case we can stop the * resampling */ if( p_input->pp_resamplers[0]->input.i_rate == p_input->input.i_rate ) { p_input->i_resampling_type = AOUT_RESAMPLING_NONE; msg_Warn( p_aout, "resampling stopped after "I64Fi" usec " "(drift: "I64Fi")", mdate() - p_input->i_resamp_start_date, p_buffer->start_date - start_date); } else if( abs( (int)(p_buffer->start_date - start_date) ) < abs( p_input->i_resamp_start_drift ) / 2 ) { /* if we reduced the drift from half, then it is time to switch * back the resampling direction. */ if( p_input->i_resampling_type == AOUT_RESAMPLING_UP ) p_input->i_resampling_type = AOUT_RESAMPLING_DOWN; else p_input->i_resampling_type = AOUT_RESAMPLING_UP; p_input->i_resamp_start_drift = 0; } else if( p_input->i_resamp_start_drift && ( abs( (int)(p_buffer->start_date - start_date) ) > abs( p_input->i_resamp_start_drift ) * 3 / 2 ) ) { /* If the drift is increasing and not decreasing, than something * is bad. We'd better stop the resampling right now. */ msg_Warn( p_aout, "timing screwed, stopping resampling" ); p_input->i_resampling_type = AOUT_RESAMPLING_NONE; p_input->pp_resamplers[0]->input.i_rate = p_input->input.i_rate; } } /* Adding the start date will be managed by aout_FifoPush(). */ p_buffer->end_date = start_date + (p_buffer->end_date - p_buffer->start_date); p_buffer->start_date = start_date; /* Actually run the resampler now. */ if ( p_input->i_nb_resamplers > 0 ) { aout_FiltersPlay( p_aout, p_input->pp_resamplers, p_input->i_nb_resamplers, &p_buffer ); } vlc_mutex_lock( &p_aout->input_fifos_lock ); aout_FifoPush( p_aout, &p_input->fifo, p_buffer ); vlc_mutex_unlock( &p_aout->input_fifos_lock ); return 0; }