📄 directx.c
字号:
} 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_CTRLPOSITIONNOTIFY /* We need notification */ | 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; } /* Backup the size of a frame */ p_aout->output.p_sys->p_notif->i_frame_size = i_bytes_per_frame; /* Now the secondary buffer is created, we need to setup its position * notification */ for( i = 0; i < FRAMES_NUM; i++ ) { p_aout->output.p_sys->p_notif->p_events[i].dwOffset = i * p_aout->output.p_sys->p_notif->i_frame_size; p_aout->output.p_sys->p_notif->i_frame_status[i] = FRAME_EMPTY; } /* Get the IDirectSoundNotify interface */ if FAILED( IDirectSoundBuffer_QueryInterface( p_aout->output.p_sys->p_dsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&p_aout->output.p_sys->p_dsnotify ) ) { msg_Err( p_aout, "cannot get IDirectSoundNotify interface" ); goto error; } if FAILED( IDirectSoundNotify_SetNotificationPositions( p_aout->output.p_sys->p_dsnotify, FRAMES_NUM, p_aout->output.p_sys->p_notif->p_events ) ) { msg_Err( p_aout, "cannot set position notification" ); goto error; } 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; error: DestroyDSBuffer( p_aout ); return VLC_EGENERIC;}/***************************************************************************** * 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, vlc_bool_t 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_dsnotify ) { IDirectSoundNotify_Release( p_aout->output.p_sys->p_dsnotify ); p_aout->output.p_sys->p_dsnotify = NULL; } 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 ); } p_aout->p_vlc->pf_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 ); 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( notification_thread_t *p_notif ){ HANDLE notification_events[FRAMES_NUM]; HRESULT dsresult; aout_instance_t *p_aout = p_notif->p_aout; int i, i_which_frame, i_last_frame, i_next_frame; mtime_t mtime; vlc_bool_t b_sleek; for( i = 0; i < FRAMES_NUM; i++ ) notification_events[i] = p_notif->p_events[i].hEventNotify; /* 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( notification_events[0], INFINITE ); if( !p_notif->b_die ) { 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" ); } } while( !p_notif->b_die ) { aout_buffer_t *p_buffer; long l_latency; /* wait for the position notification */ i_which_frame = WaitForMultipleObjects( FRAMES_NUM, notification_events, 0, INFINITE ) - WAIT_OBJECT_0; if( p_notif->b_die ) break; mtime = mdate(); /* We take into account the current latency */ if SUCCEEDED( IDirectSoundBuffer_GetCurrentPosition( p_aout->output.p_sys->p_dsbuffer, &l_latency, NULL ) ) { if( l_latency > (i_which_frame * FRAME_SIZE) && l_latency < ((i_which_frame+1) * FRAME_SIZE) ) { l_latency = - ( l_latency / p_aout->output.output.i_bytes_per_frame % FRAME_SIZE ); } else { l_latency = FRAME_SIZE - ( l_latency / p_aout->output.output.i_bytes_per_frame % FRAME_SIZE ); } } else { l_latency = 0; } /* Mark last frame as empty */ i_last_frame = (i_which_frame + FRAMES_NUM -1) % FRAMES_NUM; i_next_frame = (i_which_frame + 1) % FRAMES_NUM; p_notif->i_frame_status[i_last_frame] = FRAME_EMPTY; /* Try to fill in as many frame buffers as possible */ for( i = i_next_frame; (i % FRAMES_NUM) != i_which_frame; i++ ) { /* Check if frame buf is already filled */ if( p_notif->i_frame_status[i % FRAMES_NUM] == FRAME_QUEUED ) continue; p_buffer = aout_OutputNextBuffer( p_aout, mtime + 1000000 / p_aout->output.output.i_rate * ((i - i_next_frame + 1) * FRAME_SIZE + l_latency), 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 != i_next_frame) ) { //msg_Err( p_aout, "only %i frame buffers filled!", // i - i_next_frame ); break; } if( FillBuffer( p_aout, (i%FRAMES_NUM), p_buffer ) != VLC_SUCCESS ) break; /* Mark the frame buffer as QUEUED */ p_notif->i_frame_status[i%FRAMES_NUM] = FRAME_QUEUED; } } /* make sure the buffer isn't playing */ IDirectSoundBuffer_Stop( p_aout->output.p_sys->p_dsbuffer ); /* free the events */ for( i = 0; i < FRAMES_NUM; i++ ) CloseHandle( notification_events[i] ); msg_Dbg( p_notif, "DirectSoundThread exiting" );}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -