/** * Prints two formats in a human-readable form */ void aout_FormatsPrint( vlc_object_t *obj, const char * psz_text, const audio_sample_format_t * p_format1, const audio_sample_format_t * p_format2 ) { msg_Dbg( obj, "%s '%4.4s'->'%4.4s' %u Hz->%u Hz %s->%s", psz_text, (char *)&p_format1->i_format, (char *)&p_format2->i_format, p_format1->i_rate, p_format2->i_rate, aout_FormatPrintChannels( p_format1 ), aout_FormatPrintChannels( p_format2 ) ); }
/***************************************************************************** * aout_FormatsPrint : print two formats in a human-readable form *****************************************************************************/ void aout_FormatsPrint( aout_instance_t * p_aout, const char * psz_text, const audio_sample_format_t * p_format1, const audio_sample_format_t * p_format2 ) { msg_Dbg( p_aout, "%s '%4.4s'->'%4.4s' %d Hz->%d Hz %s->%s", psz_text, (char *)&p_format1->i_format, (char *)&p_format2->i_format, p_format1->i_rate, p_format2->i_rate, aout_FormatPrintChannels( p_format1 ), aout_FormatPrintChannels( p_format2 ) ); }
/***************************************************************************** * aout_FormatPrint : print a format in a human-readable form *****************************************************************************/ void aout_FormatPrint( aout_instance_t * p_aout, const char * psz_text, const audio_sample_format_t * p_format ) { msg_Dbg( p_aout, "%s '%4.4s' %d Hz %s frame=%d samples/%d bytes", psz_text, (char *)&p_format->i_format, p_format->i_rate, aout_FormatPrintChannels( p_format ), p_format->i_frame_length, p_format->i_bytes_per_frame ); }
/** * Prints an audio sample format in a human-readable form. */ void aout_FormatPrint( vlc_object_t *obj, const char *psz_text, const audio_sample_format_t *p_format ) { msg_Dbg( obj, "%s '%4.4s' %d Hz %s frame=%u samples/%u bytes", psz_text, (char *)&p_format->i_format, p_format->i_rate, aout_FormatPrintChannels( p_format ), p_format->i_frame_length, p_format->i_bytes_per_frame ); }
/***************************************************************************** * OpenFilter: *****************************************************************************/ static int OpenFilter( vlc_object_t *p_this ) { filter_t *p_filter = (filter_t *)p_this; filter_sys_t *p_sys; audio_format_t *audio_in = &p_filter->fmt_in.audio; audio_format_t *audio_out = &p_filter->fmt_out.audio; if( ( audio_in->i_format != audio_out->i_format ) || ( audio_in->i_rate != audio_out->i_rate ) ) return VLC_EGENERIC; /* Allocate the memory needed to store the module's structure */ p_sys = p_filter->p_sys = malloc( sizeof(filter_sys_t) ); if( unlikely( p_sys == NULL ) ) return VLC_ENOMEM; /* get number of and layout of input channels */ uint32_t i_output_physical = 0; uint8_t pi_map_ch[ AOUT_CHAN_MAX ] = { 0 }; /* which out channel each in channel is mapped to */ p_sys->b_normalize = var_InheritBool( p_this, REMAP_CFG "normalize" ); for( uint8_t in_ch = 0, wg4_i = 0; in_ch < audio_in->i_channels; in_ch++, wg4_i++ ) { /* explode in_channels in the right order */ while( ( audio_in->i_physical_channels & pi_vlc_chan_order_wg4[ wg4_i ] ) == 0 ) { wg4_i++; assert( wg4_i < sizeof( pi_vlc_chan_order_wg4 )/sizeof( pi_vlc_chan_order_wg4[0] ) ); } unsigned channel_wg4idx_len = sizeof( channel_wg4idx )/sizeof( channel_wg4idx[0] ); uint8_t *pi_chnidx = memchr( channel_wg4idx, wg4_i, channel_wg4idx_len ); assert( pi_chnidx != NULL ); uint8_t chnidx = pi_chnidx - channel_wg4idx; uint8_t out_idx = var_InheritInteger( p_this, channel_name[chnidx] ); pi_map_ch[in_ch] = channel_wg4idx[ out_idx ]; i_output_physical |= channel_flag[ out_idx ]; } i_output_physical = CanonicaliseChannels( i_output_physical ); audio_out->i_physical_channels = i_output_physical; aout_FormatPrepare( audio_out ); /* condense out_channels */ uint8_t out_ch_sorted[ AOUT_CHAN_MAX ]; for( uint8_t i = 0, wg4_i = 0; i < audio_out->i_channels; i++, wg4_i++ ) { while( ( audio_out->i_physical_channels & pi_vlc_chan_order_wg4[ wg4_i ] ) == 0 ) { wg4_i++; assert( wg4_i < sizeof( pi_vlc_chan_order_wg4 )/sizeof( pi_vlc_chan_order_wg4[0] ) ); } out_ch_sorted[ i ] = wg4_i; } bool b_multiple = false; /* whether we need to add channels (multiple in mapped to an out) */ memset( p_sys->nb_in_ch, 0, sizeof( p_sys->nb_in_ch ) ); for( uint8_t i = 0; i < audio_in->i_channels; i++ ) { uint8_t wg4_out_ch = pi_map_ch[i]; uint8_t *pi_out_ch = memchr( out_ch_sorted, wg4_out_ch, audio_out->i_channels ); assert( pi_out_ch != NULL ); p_sys->map_ch[i] = pi_out_ch - out_ch_sorted; if( ++p_sys->nb_in_ch[ p_sys->map_ch[i] ] > 1 ) b_multiple = true; } msg_Dbg( p_filter, "%s '%4.4s'->'%4.4s' %d Hz->%d Hz %s->%s", "Remap filter", (char *)&audio_in->i_format, (char *)&audio_out->i_format, audio_in->i_rate, audio_out->i_rate, aout_FormatPrintChannels( audio_in ), aout_FormatPrintChannels( audio_out ) ); p_sys->pf_remap = GetRemapFun( audio_in, b_multiple ); if( !p_sys->pf_remap ) { msg_Err( p_filter, "Could not decide on %s remap function", b_multiple ? "an add" : "a copy" ); free( p_sys ); return VLC_EGENERIC; } p_filter->pf_audio_filter = Remap; return VLC_SUCCESS; }
/***************************************************************************** * Open: open the audio device (the digital sound processor) ***************************************************************************** * This function opens the DSP as a usual non-blocking write-only file, and * modifies the p_aout->p_sys->i_fd with the file's descriptor. *****************************************************************************/ static int Open( vlc_object_t *p_this ) { audio_output_t * p_aout = (audio_output_t *)p_this; struct aout_sys_t * p_sys; char * psz_device; vlc_value_t val; /* Allocate structure */ p_aout->sys = p_sys = malloc( sizeof( aout_sys_t ) ); if( p_sys == NULL ) return VLC_ENOMEM; /* Get device name */ if( (psz_device = var_InheritString( p_aout, "oss-audio-device" )) == NULL ) { msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" ); free( p_sys ); return VLC_EGENERIC; } /* Open the sound device in non-blocking mode, because ALSA's OSS * emulation and some broken OSS drivers would make a blocking call * wait forever until the device is available. Since this breaks the * OSS spec, we immediately put it back to blocking mode if the * operation was successful. */ p_sys->i_fd = vlc_open( psz_device, O_WRONLY | O_NDELAY ); if( p_sys->i_fd < 0 ) { msg_Err( p_aout, "cannot open audio device (%s)", psz_device ); free( psz_device ); free( p_sys ); return VLC_EGENERIC; } /* if the opening was ok, put the device back in blocking mode */ fcntl( p_sys->i_fd, F_SETFL, fcntl( p_sys->i_fd, F_GETFL ) &~ FNDELAY ); free( psz_device ); p_aout->pf_play = aout_PacketPlay; p_aout->pf_pause = aout_PacketPause; p_aout->pf_flush = aout_PacketFlush; if ( var_Type( p_aout, "audio-device" ) == 0 ) Probe( p_aout ); var_AddCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); if ( var_Get( p_aout, "audio-device", &val ) < 0 ) /* Probe() has failed. */ goto error; if ( val.i_int == AOUT_VAR_SPDIF ) { p_aout->format.i_format = VLC_CODEC_SPDIFL; } else if ( val.i_int == AOUT_VAR_5_1 ) { p_aout->format.i_format = VLC_CODEC_S16N; p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; } else if ( val.i_int == AOUT_VAR_2F2R ) { p_aout->format.i_format = VLC_CODEC_S16N; p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; } else if ( val.i_int == AOUT_VAR_STEREO ) { p_aout->format.i_format = VLC_CODEC_S16N; p_aout->format.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; } else if ( val.i_int == AOUT_VAR_MONO ) { p_aout->format.i_format = VLC_CODEC_S16N; p_aout->format.i_physical_channels = AOUT_CHAN_CENTER; } else { /* This should not happen ! */ msg_Err( p_aout, "internal: can't find audio-device (%"PRId64")", val.i_int ); goto error; } var_TriggerCallback( p_aout, "intf-change" ); /* Reset the DSP device */ if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ) { msg_Err( p_aout, "cannot reset OSS audio device" ); goto error; } /* Set the output format */ if ( AOUT_FMT_SPDIF( &p_aout->format ) ) { int i_format = AFMT_AC3; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 || i_format != AFMT_AC3 ) { msg_Err( p_aout, "cannot reset OSS audio device" ); goto error; } p_aout->format.i_format = VLC_CODEC_SPDIFL; p_aout->format.i_bytes_per_frame = AOUT_SPDIF_SIZE; p_aout->format.i_frame_length = A52_FRAME_NB; aout_PacketInit( p_aout, &p_sys->packet, A52_FRAME_NB ); aout_VolumeNoneInit( p_aout ); } else { unsigned int i_format = AFMT_S16_NE; unsigned int i_frame_size, i_fragments; unsigned int i_rate; unsigned int i_nb_channels; audio_buf_info audio_buf; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 ) { msg_Err( p_aout, "cannot set audio output format" ); goto error; } switch ( i_format ) { case AFMT_U8: p_aout->format.i_format = VLC_CODEC_U8; break; case AFMT_S8: p_aout->format.i_format = VLC_CODEC_S8; break; case AFMT_U16_LE: p_aout->format.i_format = VLC_CODEC_U16L; break; case AFMT_S16_LE: p_aout->format.i_format = VLC_CODEC_S16L; break; case AFMT_U16_BE: p_aout->format.i_format = VLC_CODEC_U16B; break; case AFMT_S16_BE: p_aout->format.i_format = VLC_CODEC_S16B; break; default: msg_Err( p_aout, "OSS fell back to an unknown format (%d)", i_format ); goto error; } i_nb_channels = aout_FormatNbChannels( &p_aout->format ); /* Set the number of channels */ if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 || i_nb_channels != aout_FormatNbChannels( &p_aout->format ) ) { msg_Err( p_aout, "cannot set number of audio channels (%s)", aout_FormatPrintChannels( &p_aout->format) ); goto error; } /* Set the output rate */ i_rate = p_aout->format.i_rate; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 ) { msg_Err( p_aout, "cannot set audio output rate (%i)", p_aout->format.i_rate ); goto error; } if( i_rate != p_aout->format.i_rate ) { p_aout->format.i_rate = i_rate; } /* Set the fragment size */ aout_FormatPrepare( &p_aout->format ); /* i_fragment = xxxxyyyy where: xxxx is fragtotal * 1 << yyyy is fragsize */ i_frame_size = ((uint64_t)p_aout->format.i_bytes_per_frame * p_aout->format.i_rate * 65536) / (48000 * 2 * 2) / FRAME_COUNT; i_fragments = 4; while( i_fragments < 12 && (1U << i_fragments) < i_frame_size ) { ++i_fragments; } i_fragments |= FRAME_COUNT << 16; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFRAGMENT, &i_fragments ) < 0 ) { msg_Warn( p_aout, "cannot set fragment size (%.8x)", i_fragments ); } if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 ) { msg_Err( p_aout, "cannot get fragment size" ); goto error; } /* Number of fragments actually allocated */ p_aout->sys->i_fragstotal = audio_buf.fragstotal; /* Maximum duration the soundcard's buffer can hold */ p_aout->sys->max_buffer_duration = (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000 / p_aout->format.i_bytes_per_frame / p_aout->format.i_rate * p_aout->format.i_frame_length; aout_PacketInit( p_aout, &p_sys->packet, audio_buf.fragsize/p_aout->format.i_bytes_per_frame ); aout_VolumeSoftInit( p_aout ); } /* Create OSS thread and wait for its readiness. */ if( vlc_clone( &p_sys->thread, OSSThread, p_aout, VLC_THREAD_PRIORITY_OUTPUT ) ) { msg_Err( p_aout, "cannot create OSS thread (%m)" ); aout_PacketDestroy( p_aout ); goto error; } return VLC_SUCCESS; error: var_DelCallback( p_aout, "audio-device", aout_ChannelsRestart, NULL ); close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; }
/***************************************************************************** * Open: open the audio device (the digital sound processor) ***************************************************************************** * This function opens the DSP as a usual non-blocking write-only file, and * modifies the p_aout->p_sys->i_fd with the file's descriptor. *****************************************************************************/ static int Open( vlc_object_t *p_this ) { aout_instance_t * p_aout = (aout_instance_t *)p_this; struct aout_sys_t * p_sys; char * psz_device; vlc_value_t val; /* Allocate structure */ p_aout->output.p_sys = p_sys = malloc( sizeof( aout_sys_t ) ); if( p_sys == NULL ) { msg_Err( p_aout, "out of memory" ); return VLC_ENOMEM; } /* Get device name */ if( (psz_device = config_GetPsz( p_aout, "dspdev" )) == NULL ) { msg_Err( p_aout, "no audio device specified (maybe /dev/dsp?)" ); free( p_sys ); return VLC_EGENERIC; } /* Open the sound device in non-blocking mode, because ALSA's OSS * emulation and some broken OSS drivers would make a blocking call * wait forever until the device is available. Since this breaks the * OSS spec, we immediately put it back to blocking mode if the * operation was successful. */ p_sys->i_fd = open( psz_device, O_WRONLY | O_NDELAY ); if( p_sys->i_fd < 0 ) { msg_Err( p_aout, "cannot open audio device (%s)", psz_device ); free( p_sys ); return VLC_EGENERIC; } /* if the opening was ok, put the device back in blocking mode */ fcntl( p_sys->i_fd, F_SETFL, fcntl( p_sys->i_fd, F_GETFL ) &~ FNDELAY ); free( psz_device ); p_aout->output.pf_play = Play; if ( var_Type( p_aout, "audio-device" ) == 0 ) { Probe( p_aout ); } if ( var_Get( p_aout, "audio-device", &val ) < 0 ) { /* Probe() has failed. */ free( p_sys ); return VLC_EGENERIC; } if ( val.i_int == AOUT_VAR_SPDIF ) { p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i'); } else if ( val.i_int == AOUT_VAR_5_1 ) { p_aout->output.output.i_format = AOUT_FMT_S16_NE; p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE; } else if ( val.i_int == AOUT_VAR_2F2R ) { p_aout->output.output.i_format = AOUT_FMT_S16_NE; p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT; } else if ( val.i_int == AOUT_VAR_STEREO ) { p_aout->output.output.i_format = AOUT_FMT_S16_NE; p_aout->output.output.i_physical_channels = AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT; } else if ( val.i_int == AOUT_VAR_MONO ) { p_aout->output.output.i_format = AOUT_FMT_S16_NE; p_aout->output.output.i_physical_channels = AOUT_CHAN_CENTER; } else { /* This should not happen ! */ msg_Err( p_aout, "internal: can't find audio-device (%i)", val.i_int ); free( p_sys ); return VLC_EGENERIC; } val.b_bool = VLC_TRUE; var_Set( p_aout, "intf-change", val ); /* Reset the DSP device */ if( ioctl( p_sys->i_fd, SNDCTL_DSP_RESET, NULL ) < 0 ) { msg_Err( p_aout, "cannot reset OSS audio device" ); close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; } /* Set the output format */ if ( AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) { int i_format = AFMT_AC3; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 || i_format != AFMT_AC3 ) { msg_Err( p_aout, "cannot reset OSS audio device" ); close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; } p_aout->output.output.i_format = VLC_FOURCC('s','p','d','i'); p_aout->output.i_nb_samples = A52_FRAME_NB; p_aout->output.output.i_bytes_per_frame = AOUT_SPDIF_SIZE; p_aout->output.output.i_frame_length = A52_FRAME_NB; aout_VolumeNoneInit( p_aout ); } if ( !AOUT_FMT_NON_LINEAR( &p_aout->output.output ) ) { unsigned int i_format = AFMT_S16_NE; unsigned int i_frame_size, i_fragments; unsigned int i_rate; unsigned int i_nb_channels; audio_buf_info audio_buf; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFMT, &i_format ) < 0 ) { msg_Err( p_aout, "cannot set audio output format" ); close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; } switch ( i_format ) { case AFMT_U8: p_aout->output.output.i_format = VLC_FOURCC('u','8',' ',' '); break; case AFMT_S8: p_aout->output.output.i_format = VLC_FOURCC('s','8',' ',' '); break; case AFMT_U16_LE: p_aout->output.output.i_format = VLC_FOURCC('u','1','6','l'); break; case AFMT_S16_LE: p_aout->output.output.i_format = VLC_FOURCC('s','1','6','l'); break; case AFMT_U16_BE: p_aout->output.output.i_format = VLC_FOURCC('u','1','6','b'); break; case AFMT_S16_BE: p_aout->output.output.i_format = VLC_FOURCC('s','1','6','b'); break; default: msg_Err( p_aout, "OSS fell back to an unknown format (%d)", i_format ); close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; } i_nb_channels = aout_FormatNbChannels( &p_aout->output.output ); /* Set the number of channels */ if( ioctl( p_sys->i_fd, SNDCTL_DSP_CHANNELS, &i_nb_channels ) < 0 || i_nb_channels != aout_FormatNbChannels( &p_aout->output.output ) ) { msg_Err( p_aout, "cannot set number of audio channels (%s)", aout_FormatPrintChannels( &p_aout->output.output) ); close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; } /* Set the output rate */ i_rate = p_aout->output.output.i_rate; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SPEED, &i_rate ) < 0 ) { msg_Err( p_aout, "cannot set audio output rate (%i)", p_aout->output.output.i_rate ); close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; } if( i_rate != p_aout->output.output.i_rate ) { p_aout->output.output.i_rate = i_rate; } /* Set the fragment size */ aout_FormatPrepare( &p_aout->output.output ); /* i_fragment = xxxxyyyy where: xxxx is fragtotal * 1 << yyyy is fragsize */ i_fragments = 0; i_frame_size = FRAME_SIZE * p_aout->output.output.i_bytes_per_frame; while( i_frame_size >>= 1 ) { ++i_fragments; } i_fragments |= FRAME_COUNT << 16; if( ioctl( p_sys->i_fd, SNDCTL_DSP_SETFRAGMENT, &i_fragments ) < 0 ) { msg_Warn( p_aout, "cannot set fragment size (%.8x)", i_fragments ); } if( ioctl( p_sys->i_fd, SNDCTL_DSP_GETOSPACE, &audio_buf ) < 0 ) { msg_Err( p_aout, "cannot get fragment size" ); close( p_sys->i_fd ); free( p_sys ); return VLC_EGENERIC; } else { /* Number of fragments actually allocated */ p_aout->output.p_sys->i_fragstotal = audio_buf.fragstotal; /* Maximum duration the soundcard's buffer can hold */ p_aout->output.p_sys->max_buffer_duration = (mtime_t)audio_buf.fragstotal * audio_buf.fragsize * 1000000 / p_aout->output.output.i_bytes_per_frame / p_aout->output.output.i_rate * p_aout->output.output.i_frame_length; p_aout->output.i_nb_samples = audio_buf.fragsize / p_aout->output.output.i_bytes_per_frame; } aout_VolumeSoftInit( p_aout ); }