📄 directx.c
字号:
{ FreeLibrary( p_aout->output.p_sys->hdsound_dll ); p_aout->output.p_sys->hdsound_dll = NULL; } return VLC_EGENERIC;}/***************************************************************************** * CreateDSBuffer: Creates a direct sound buffer of the required format. ***************************************************************************** * This function creates the buffer we'll use to play audio. * In DirectSound there are two kinds of buffers: * - the primary buffer: which is the actual buffer that the soundcard plays * - the secondary buffer(s): these buffers are the one actually used by * applications and DirectSound takes care of mixing them into the primary. * * Once you create a secondary buffer, you cannot change its format anymore so * you have to release the current one and create another. *****************************************************************************/static int CreateDSBuffer( aout_instance_t *p_aout, int i_format, int i_channels, int i_nb_channels, int i_rate, int i_bytes_per_frame, bool b_probe ){ WAVEFORMATEXTENSIBLE waveformat; DSBUFFERDESC dsbdesc; unsigned int i; /* First set the sound buffer format */ waveformat.dwChannelMask = 0; for( i = 0; i < sizeof(pi_channels_src)/sizeof(uint32_t); i++ ) { if( i_channels & pi_channels_src[i] ) waveformat.dwChannelMask |= pi_channels_in[i]; } switch( i_format ) { case VLC_FOURCC('s','p','d','i'): i_nb_channels = 2; /* To prevent channel re-ordering */ waveformat.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; waveformat.Format.wBitsPerSample = 16; waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; waveformat.Format.wFormatTag = WAVE_FORMAT_DOLBY_AC3_SPDIF; waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_DOLBY_AC3_SPDIF; break; case VLC_FOURCC('f','l','3','2'): waveformat.Format.wBitsPerSample = sizeof(float) * 8; waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; waveformat.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; break; case VLC_FOURCC('s','1','6','l'): waveformat.Format.wBitsPerSample = 16; waveformat.Samples.wValidBitsPerSample = waveformat.Format.wBitsPerSample; waveformat.Format.wFormatTag = WAVE_FORMAT_PCM; waveformat.SubFormat = _KSDATAFORMAT_SUBTYPE_PCM; break; } waveformat.Format.nChannels = i_nb_channels; waveformat.Format.nSamplesPerSec = i_rate; waveformat.Format.nBlockAlign = waveformat.Format.wBitsPerSample / 8 * i_nb_channels; waveformat.Format.nAvgBytesPerSec = waveformat.Format.nSamplesPerSec * waveformat.Format.nBlockAlign; p_aout->output.p_sys->i_bits_per_sample = waveformat.Format.wBitsPerSample; p_aout->output.p_sys->i_channels = i_nb_channels; /* Then fill in the direct sound descriptor */ memset(&dsbdesc, 0, sizeof(DSBUFFERDESC)); dsbdesc.dwSize = sizeof(DSBUFFERDESC); dsbdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2/* Better position accuracy */ | DSBCAPS_GLOBALFOCUS; /* Allows background playing */ /* Only use the new WAVE_FORMAT_EXTENSIBLE format for multichannel audio */ if( i_nb_channels <= 2 ) { waveformat.Format.cbSize = 0; } else { waveformat.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; waveformat.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); /* Needed for 5.1 on emu101k */ dsbdesc.dwFlags |= DSBCAPS_LOCHARDWARE; } dsbdesc.dwBufferBytes = FRAMES_NUM * i_bytes_per_frame; /* buffer size */ dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&waveformat; if FAILED( IDirectSound_CreateSoundBuffer( p_aout->output.p_sys->p_dsobject, &dsbdesc, &p_aout->output.p_sys->p_dsbuffer, NULL) ) { if( dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE ) { /* Try without DSBCAPS_LOCHARDWARE */ dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE; if FAILED( IDirectSound_CreateSoundBuffer( p_aout->output.p_sys->p_dsobject, &dsbdesc, &p_aout->output.p_sys->p_dsbuffer, NULL) ) { return VLC_EGENERIC; } if( !b_probe ) msg_Dbg( p_aout, "couldn't use hardware sound buffer" ); } else { return VLC_EGENERIC; } } /* Stop here if we were just probing */ if( b_probe ) { IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer ); p_aout->output.p_sys->p_dsbuffer = NULL; return VLC_SUCCESS; } p_aout->output.p_sys->i_frame_size = i_bytes_per_frame; p_aout->output.p_sys->i_channel_mask = waveformat.dwChannelMask; p_aout->output.p_sys->b_chan_reorder = aout_CheckChannelReorder( pi_channels_in, pi_channels_out, waveformat.dwChannelMask, i_nb_channels, p_aout->output.p_sys->pi_chan_table ); if( p_aout->output.p_sys->b_chan_reorder ) { msg_Dbg( p_aout, "channel reordering needed" ); } return VLC_SUCCESS;}/***************************************************************************** * CreateDSBufferPCM: creates a PCM direct sound buffer. ***************************************************************************** * We first try to create a WAVE_FORMAT_IEEE_FLOAT buffer if supported by * the hardware, otherwise we create a WAVE_FORMAT_PCM buffer. ****************************************************************************/static int CreateDSBufferPCM( aout_instance_t *p_aout, int *i_format, int i_channels, int i_nb_channels, int i_rate, bool b_probe ){ vlc_value_t val; var_Get( p_aout, "directx-audio-float32", &val ); /* Float32 audio samples are not supported for 5.1 output on the emu101k */ if( !val.b_bool || i_nb_channels > 2 || CreateDSBuffer( p_aout, VLC_FOURCC('f','l','3','2'), i_channels, i_nb_channels, i_rate, FRAME_SIZE * 4 * i_nb_channels, b_probe ) != VLC_SUCCESS ) { if ( CreateDSBuffer( p_aout, VLC_FOURCC('s','1','6','l'), i_channels, i_nb_channels, i_rate, FRAME_SIZE * 2 * i_nb_channels, b_probe ) != VLC_SUCCESS ) { return VLC_EGENERIC; } else { *i_format = VLC_FOURCC('s','1','6','l'); return VLC_SUCCESS; } } else { *i_format = VLC_FOURCC('f','l','3','2'); return VLC_SUCCESS; }}/***************************************************************************** * DestroyDSBuffer ***************************************************************************** * This function destroys the secondary buffer. *****************************************************************************/static void DestroyDSBuffer( aout_instance_t *p_aout ){ if( p_aout->output.p_sys->p_dsbuffer ) { IDirectSoundBuffer_Release( p_aout->output.p_sys->p_dsbuffer ); p_aout->output.p_sys->p_dsbuffer = NULL; }}/***************************************************************************** * FillBuffer: Fill in one of the direct sound frame buffers. ***************************************************************************** * Returns VLC_SUCCESS on success. *****************************************************************************/static int FillBuffer( aout_instance_t *p_aout, int i_frame, aout_buffer_t *p_buffer ){ notification_thread_t *p_notif = p_aout->output.p_sys->p_notif; aout_sys_t *p_sys = p_aout->output.p_sys; void *p_write_position, *p_wrap_around; long l_bytes1, l_bytes2; HRESULT dsresult; /* Before copying anything, we have to lock the buffer */ dsresult = IDirectSoundBuffer_Lock( p_sys->p_dsbuffer, /* DS buffer */ i_frame * p_notif->i_frame_size, /* Start offset */ p_notif->i_frame_size, /* Number of bytes */ &p_write_position, /* Address of lock start */ &l_bytes1, /* Count of bytes locked before wrap around */ &p_wrap_around, /* Buffer adress (if wrap around) */ &l_bytes2, /* Count of bytes after wrap around */ 0 ); /* Flags */ if( dsresult == DSERR_BUFFERLOST ) { IDirectSoundBuffer_Restore( p_sys->p_dsbuffer ); dsresult = IDirectSoundBuffer_Lock( p_sys->p_dsbuffer, i_frame * p_notif->i_frame_size, p_notif->i_frame_size, &p_write_position, &l_bytes1, &p_wrap_around, &l_bytes2, 0 ); } if( dsresult != DS_OK ) { msg_Warn( p_notif, "cannot lock buffer" ); if( p_buffer ) aout_BufferFree( p_buffer ); return VLC_EGENERIC; } if( p_buffer == NULL ) { memset( p_write_position, 0, l_bytes1 ); } else { if( p_sys->b_chan_reorder ) { /* Do the channel reordering here */ aout_ChannelReorder( p_buffer->p_buffer, p_buffer->i_nb_bytes, p_sys->i_channels, p_sys->pi_chan_table, p_sys->i_bits_per_sample ); } vlc_memcpy( p_write_position, p_buffer->p_buffer, l_bytes1 ); aout_BufferFree( p_buffer ); } /* Now the data has been copied, unlock the buffer */ IDirectSoundBuffer_Unlock( p_sys->p_dsbuffer, p_write_position, l_bytes1, p_wrap_around, l_bytes2 ); p_notif->i_write_slot = (i_frame + 1) % FRAMES_NUM; return VLC_SUCCESS;}/***************************************************************************** * DirectSoundThread: this thread will capture play notification events. ***************************************************************************** * We use this thread to emulate a callback mechanism. The thread probes for * event notification and fills up the DS secondary buffer when needed. *****************************************************************************/static void* DirectSoundThread( vlc_object_t *p_this ){ notification_thread_t *p_notif = (notification_thread_t*)p_this; aout_instance_t *p_aout = p_notif->p_aout; bool b_sleek; mtime_t last_time; HRESULT dsresult; long l_queued = 0; /* We don't want any resampling when using S/PDIF output */ b_sleek = p_aout->output.output.i_format == VLC_FOURCC('s','p','d','i'); /* Tell the main thread that we are ready */ vlc_thread_ready( p_notif ); msg_Dbg( p_notif, "DirectSoundThread ready" ); /* Wait here until Play() is called */ WaitForSingleObject( p_notif->event, INFINITE ); if( vlc_object_alive (p_notif) ) { mwait( p_notif->start_date - AOUT_PTS_TOLERANCE / 2 ); /* start playing the buffer */ dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer, 0, /* Unused */ 0, /* Unused */ DSBPLAY_LOOPING ); /* Flags */ if( dsresult == DSERR_BUFFERLOST ) { IDirectSoundBuffer_Restore( p_aout->output.p_sys->p_dsbuffer ); dsresult = IDirectSoundBuffer_Play( p_aout->output.p_sys->p_dsbuffer, 0, /* Unused */ 0, /* Unused */ DSBPLAY_LOOPING ); /* Flags */ } if( dsresult != DS_OK ) { msg_Err( p_aout, "cannot start playing buffer" ); } } last_time = mdate(); while( vlc_object_alive (p_notif) ) { long l_read, l_free_slots; mtime_t mtime = mdate(); int i; /* * Fill in as much audio data as we can in our circular buffer */ /* Find out current play position */ if FAILED( IDirectSoundBuffer_GetCurrentPosition( p_aout->output.p_sys->p_dsbuffer, &l_read, NULL ) ) { msg_Err( p_aout, "GetCurrentPosition() failed!" ); l_read = 0; } /* Detect underruns */ if( l_queued && mtime - last_time > INT64_C(1000000) * l_queued / p_aout->output.output.i_rate ) { msg_Dbg( p_aout, "detected underrun!" ); } last_time = mtime; /* Try to fill in as many frame buffers as possible */ l_read /= p_aout->output.output.i_bytes_per_frame; l_queued = p_notif->i_write_slot * FRAME_SIZE - l_read; if( l_queued < 0 ) l_queued += (FRAME_SIZE * FRAMES_NUM); l_free_slots = (FRAMES_NUM * FRAME_SIZE - l_queued) / FRAME_SIZE; for( i = 0; i < l_free_slots; i++ ) { aout_buffer_t *p_buffer = aout_OutputNextBuffer( p_aout, mtime + INT64_C(1000000) * (i * FRAME_SIZE + l_queued) / p_aout->output.output.i_rate, b_sleek ); /* If there is no audio data available and we have some buffered * already, then just wait for the next time */ if( !p_buffer && (i || l_queued / FRAME_SIZE) ) break; if( FillBuffer( p_aout, p_notif->i_write_slot % FRAMES_NUM, p_buffer ) != VLC_SUCCESS ) break; } /* Sleep a reasonable amount of time */ l_queued += (i * FRAME_SIZE); msleep( INT64_C(1000000) * l_queued / p_aout->output.output.i_rate / 2 ); } /* make sure the buffer isn't playing */ IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer ); /* free the event */ CloseHandle( p_notif->event ); msg_Dbg( p_notif, "DirectSoundThread exiting" ); return NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -