/* * Copyright (C) 2001-2018 the xine project * * This file is part of xine, a unix video player. * * xine is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * xine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA * * audio_directx_out.c, direct sound audio output plugin for xine * by Matthew Grooms */ /* * TODO: * - stop looping on stop (requires AO_CTRL_PLAY_STOP?) * - posibility of bad state when buffer overrun */ typedef unsigned char boolean; #include #include #define LOG_MODULE "audio_directx_out" #define LOG_VERBOSE /* #define LOG */ #include #include #define MAX_CHANNELS 6 #define MAX_BITS 16 #define MAX_SAMPLE_RATE 44100 #define SOUND_BUFFER_DIV 32 #define SOUND_BUFFER_MAX MAX_CHANNELS * (MAX_BITS / 8) * (((MAX_SAMPLE_RATE / SOUND_BUFFER_DIV) + 1) & ~1) #define DSBUFF_INIT 0 #define DSBUFF_LEFT 1 #define DSBUFF_RIGHT 2 #define AO_DIRECTX_IFACE_VERSION 9 /***************************************************************************** * DirectDraw GUIDs. * Defining them here allows us to get rid of the dxguid library during * the linking stage. *****************************************************************************/ #if 1 static const GUID xine_IID_IDirectSoundNotify = { 0xB0210783,0x89CD,0x11D0,{0xAF,0x08,0x00,0xA0,0xC9,0x25,0xCD,0x16} }; #ifdef IID_IDirectSoundNotify # undef IID_IDirectSoundNotify #endif #define IID_IDirectSoundNotify xine_IID_IDirectSoundNotify #endif /* ----------------------------------------- * * ao_directx driver struct * * ----------------------------------------- */ typedef struct { ao_driver_t ao_driver; int capabilities; xine_t *xine; /* directx objects */ LPDIRECTSOUND dsobj; LPDIRECTSOUNDBUFFER dsbuffer; DSBCAPS dsbcaps; LPDIRECTSOUNDNOTIFY notify; DSBPOSITIONNOTIFY notify_events[ 2 ]; /* buffer vars */ long buffer_size; int write_status; unsigned long write_pos; uint8_t prebuff[ SOUND_BUFFER_MAX ]; uint32_t prebuff_size; /* current buffer properties */ int bits; int rate; int chnn; int frsz; /* current mixer settings */ int mute; int volume; } ao_directx_t; typedef struct { audio_driver_class_t driver_class; xine_t *xine; } audiox_class_t; /* ------------------------------------------- * * BEGIN : Direct Sound and win32 handlers * for xine audio output plugins. * * ------------------------------------------- */ /* Display formatted error message in * popup message box. */ static void Error( HWND hwnd, LPCSTR szfmt, ... ) { char tempbuff[ 256 ]; *tempbuff = 0; wvsprintf( &tempbuff[ strlen( tempbuff ) ], szfmt, ( char * )( &szfmt + 1 ) ); MessageBox( hwnd, tempbuff, "Error", MB_ICONERROR | MB_OK | MB_APPLMODAL | MB_SYSTEMMODAL ); } /* Create our direct sound object and * set the cooperative level. */ static boolean CreateDirectSound( ao_directx_t * ao_directx ) { DSCAPS dscaps; HWND hxinewnd; lprintf("CreateDirectSound(%p) Enter\n", (void *)ao_directx); /* create direct sound object */ if( DirectSoundCreate( 0, &ao_directx->dsobj, 0 ) != DS_OK ) { Error( 0, "DirectSoundCreate : Unable to create direct sound object" ); lprintf("CreateDirectSound() Exit! Returning False\n"); return FALSE; } /* try to get our current xine window */ hxinewnd = FindWindow( "xinectrlwindow", "xine" ); if( !hxinewnd ) hxinewnd = GetDesktopWindow(); /* set direct sound cooperative level */ if( IDirectSound_SetCooperativeLevel( ao_directx->dsobj, hxinewnd, DSSCL_EXCLUSIVE ) != DS_OK ) { Error( 0, "IDirectSound_SetCooperativeLevel : could not set direct sound cooperative level" ); lprintf("CreateDirectSound() Exit! Returning False\n"); return FALSE; } /* get the direct sound device caps */ memset( &dscaps, 0, sizeof( dscaps ) ); dscaps.dwSize = sizeof( dscaps ); if( IDirectSound_GetCaps( ao_directx->dsobj, &dscaps ) != DS_OK ) { Error( 0, "IDirectSound_GetCaps : Unable to get direct sound device capabilities" ); lprintf("CreateDirectSound() Exit! Returning False\n"); return FALSE; } lprintf("CreateDirectSound() Exit! Returning True\n"); return TRUE; } /* Destroy all direct sound allocated * resources. */ static void DestroyDirectSound( ao_directx_t * ao_directx ) { lprintf("DestroyDirectSound(%p) Enter\n", (void *)ao_directx); if( ao_directx->dsobj ) { lprintf("IDirectSound_Release()\n"); IDirectSound_Release( ao_directx->dsobj ); ao_directx->dsobj = 0; } lprintf("DestroyDirectSound() Exit\n"); } /* Used to fill our looping sound buffer * with data. */ static uint32_t FillSoundBuffer( ao_directx_t * ao_directx, int code, unsigned char * samples ) { uint8_t * buff_pointer; /* pointer inside circular buffer */ DWORD buff_length; /* bytes locked by pointer */ uint32_t half_size; /* half our sound buffer size */ uint32_t result; /* error result */ #ifdef LOG if ((void*)samples != (void*)0) printf("audio_directx_out: FillSoundBuffer(%08x, %d, Null) Enter\n", (unsigned long)ao_directx, code); else printf("audio_directx_out: FillSoundBuffer(%08x, %d, Null) Enter\n", (unsigned long)ao_directx, code); #endif half_size = ao_directx->buffer_size / 2; if( code == DSBUFF_INIT ) { lprintf("FillSoundBuffer: DSBUFF_INIT\n"); /* set our new status code */ ao_directx->write_status = DSBUFF_RIGHT; /* lock our sound buffer for write access */ result = IDirectSoundBuffer_Lock( ao_directx->dsbuffer, 0, 0, (LPVOID *)&buff_pointer, &buff_length, NULL, 0, DSBLOCK_ENTIREBUFFER ); if( result != DS_OK ) { Error( 0, "IDirectSoundBuffer_Lock : could not lock sound buffer" ); return 0; } /* clear our entire sound buffer */ memset( buff_pointer, 0, buff_length ); /* unlock our sound buffer */ if( IDirectSoundBuffer_Unlock( ao_directx->dsbuffer, buff_pointer, buff_length, 0, 0 ) != DS_OK ) { Error( 0, "IDirectSoundBuffer_Unlock : could not unlock sound buffer" ); return 0; } /* start the buffer playing */ if( IDirectSoundBuffer_Play( ao_directx->dsbuffer, 0, 0, DSBPLAY_LOOPING ) != DS_OK ) { Error( 0, "IDirectSoundBuffer_Play : could not play sound buffer" ); return 0 ; } else IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, ao_directx->volume ); } else if( code == DSBUFF_LEFT ) { lprintf("FillSoundBuffer: DSBUFF_LEFT\n"); /* set our new status code */ ao_directx->write_status = DSBUFF_RIGHT; /* lock our sound buffer for write access */ result = IDirectSoundBuffer_Lock( ao_directx->dsbuffer, 0, half_size, (LPVOID *)&buff_pointer, &buff_length, 0, 0, 0 ); if( result != DS_OK ) { Error( 0, "IDirectSoundBuffer_Lock : could not lock sound buffer" ); return 0; } /* write data to our sound buffer */ memcpy( buff_pointer, samples, buff_length ); /* unlock our sound buffer */ if( IDirectSoundBuffer_Unlock( ao_directx->dsbuffer, buff_pointer, buff_length, 0, 0 ) != DS_OK ) { Error( 0, "IDirectSoundBuffer_Unlock : could not unlock sound buffer" ); return 0; } } else if( code == DSBUFF_RIGHT ) { lprintf("FillSoundBuffer: DSBUFF_RIGHT\n"); /* set our new status code */ ao_directx->write_status = DSBUFF_LEFT; /* lock our sound buffer for write access */ result = IDirectSoundBuffer_Lock( ao_directx->dsbuffer, half_size, half_size, (LPVOID *)&buff_pointer, &buff_length, 0, 0, 0 ); if( result != DS_OK ) { Error( 0, "IDirectSoundBuffer_Lock : could not lock sound buffer" ); return 0; } /* write data to our sound buffer */ memcpy( buff_pointer, samples, buff_length ); /* unlock our sound buffer */ if( IDirectSoundBuffer_Unlock( ao_directx->dsbuffer, buff_pointer, buff_length, 0, 0 ) != DS_OK ) { Error( 0, "IDirectSoundBuffer_Unlock : could not unlock sound buffer" ); return 0; } } lprintf("FillSoundBuffer() Exit\n"); return buff_length; } /* Destroy all direct sound buffer allocated * resources. */ static void DestroySoundBuffer( ao_directx_t * ao_directx ) { lprintf("DestroySoundBuffer(%p) Enter\n", (void *)ao_directx); /* stop our buffer and zero it out */ if( ao_directx->dsbuffer ) { IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MIN ); IDirectSoundBuffer_Stop( ao_directx->dsbuffer ); FillSoundBuffer( ao_directx, DSBUFF_INIT, 0 ); } /* release our notification events */ if( ao_directx->notify_events[ 0 ].hEventNotify ) { CloseHandle( ao_directx->notify_events[ 0 ].hEventNotify ); ao_directx->notify_events[ 0 ].hEventNotify = 0; } if( ao_directx->notify_events[ 1 ].hEventNotify ) { CloseHandle( ao_directx->notify_events[ 1 ].hEventNotify ); ao_directx->notify_events[ 1 ].hEventNotify = 0; } /* release our buffer notification interface */ if( ao_directx->notify ) { IDirectSoundNotify_Release( ao_directx->notify ); ao_directx->notify = 0; } /* release our direct sound buffer */ if( ao_directx->dsbuffer ) { IDirectSoundBuffer_Release( ao_directx->dsbuffer ); ao_directx->dsbuffer = 0; } lprintf("DestroySoundBuffer() Exit\n"); } /* Used to create directx sound buffer, * notification events, and initialize * buffer to null sample data. */ static boolean CreateSoundBuffer( ao_directx_t * ao_directx ) { DSBUFFERDESC dsbdesc; PCMWAVEFORMAT pcmwf; lprintf("CreateSoundBuffer(%p) Enter\n", (void *)ao_directx); /* calculate buffer and frame size */ ao_directx->frsz = ( ao_directx->bits / 8 ) * ao_directx->chnn; /* buffer size, must be even and aligned to frame size */ ao_directx->buffer_size = (ao_directx->frsz * ((ao_directx->rate / SOUND_BUFFER_DIV + 1) & ~1)); /* release any existing sound buffer * related resources */ DestroySoundBuffer( ao_directx ); /* create a secondary sound buffer */ memset( &pcmwf, 0, sizeof( PCMWAVEFORMAT ) ); pcmwf.wBitsPerSample = ( unsigned short ) ao_directx->bits; pcmwf.wf.wFormatTag = WAVE_FORMAT_PCM; pcmwf.wf.nChannels = ao_directx->chnn; pcmwf.wf.nSamplesPerSec = ao_directx->rate; pcmwf.wf.nBlockAlign = ao_directx->frsz; pcmwf.wf.nAvgBytesPerSec = ao_directx->rate * ao_directx->frsz; memset( &dsbdesc, 0, sizeof( DSBUFFERDESC ) ); dsbdesc.dwSize = sizeof( DSBUFFERDESC ); dsbdesc.dwFlags = (DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_CTRLPOSITIONNOTIFY); dsbdesc.dwBufferBytes = ao_directx->buffer_size; dsbdesc.lpwfxFormat = ( LPWAVEFORMATEX ) &pcmwf; if( IDirectSound_CreateSoundBuffer( ao_directx->dsobj, &dsbdesc, &ao_directx->dsbuffer, 0 ) != DS_OK ) { Error( 0, "IDirectSound_CreateSoundBuffer : Unable to create secondary sound buffer" ); return FALSE; } /* get the buffer capabilities */ memset( &ao_directx->dsbcaps, 0, sizeof( DSBCAPS ) ); ao_directx->dsbcaps.dwSize = sizeof( DSBCAPS ); if( IDirectSound_GetCaps( ao_directx->dsbuffer, &ao_directx->dsbcaps ) != DS_OK ) { Error( 0, "IDirectSound_GetCaps : Unable to get secondary sound buffer capabilities" ); return FALSE; } /* create left side notification ( non-signaled ) */ ao_directx->notify_events[ 0 ].hEventNotify = CreateEvent( NULL, FALSE, FALSE, NULL ); /* create right side notification ( signaled ) */ ao_directx->notify_events[ 1 ].hEventNotify = CreateEvent( NULL, FALSE, FALSE, NULL ); if( !ao_directx->notify_events[ 0 ].hEventNotify || !ao_directx->notify_events[ 1 ].hEventNotify ) { Error( 0, "CreateEvent : Unable to create sound notification events" ); return FALSE; } /* get the direct sound notification interface */ if( IDirectSoundBuffer_QueryInterface( ao_directx->dsbuffer, &IID_IDirectSoundNotify, (LPVOID *)&ao_directx->notify ) != DS_OK ) { Error( 0, "IDirectSoundBuffer_QueryInterface : Unable to get notification interface" ); return FALSE; } /* set notification events */ ao_directx->notify_events[ 0 ].dwOffset = 0; ao_directx->notify_events[ 1 ].dwOffset = ao_directx->buffer_size / 2; if( IDirectSoundNotify_SetNotificationPositions( ao_directx->notify, 2, ao_directx->notify_events ) != DS_OK ) { Error( 0, "IDirectSoundNotify_SetNotificationPositions : Unable to set notification positions" ); return FALSE; } /* DEBUG : set sound buffer volume */ if( IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MAX ) != DS_OK ) { Error( 0, "IDirectSoundBuffer_SetVolume : Unable to set sound buffer volume" ); return FALSE; } /* initialize our sound buffer */ IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MIN ); FillSoundBuffer( ao_directx, DSBUFF_INIT, 0 ); return TRUE; lprintf("CreateSoundBuffer() Exit\n"); } /* ----------------------------------------- * * BEGIN : Xine driver audio output plugin * handlers. * * ----------------------------------------- */ static int ao_directx_control(ao_driver_t *this_gen, int cmd, ...) { switch (cmd) { case AO_CTRL_PLAY_PAUSE: break; case AO_CTRL_PLAY_RESUME: break; case AO_CTRL_FLUSH_BUFFERS: break; } return 0; } static int ao_directx_open( ao_driver_t * ao_driver, uint32_t bits, uint32_t rate, int mode ) { ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver; lprintf("ao_directx_open(%p, %u, %u, %u) Enter\n", (void *)ao_directx, bits, rate, mode); /* store input rate and bits */ ao_directx->bits = bits; ao_directx->rate = rate; /* store channel count */ switch( mode ) { case AO_CAP_MODE_MONO: ao_directx->chnn = 1; xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_MONO mode\n" ); break; case AO_CAP_MODE_STEREO: ao_directx->chnn = 2; xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_STEREO mode\n" ); break; case AO_CAP_MODE_4CHANNEL: ao_directx->chnn = 4; xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_4CHANNEL mode\n" ); break; case AO_CAP_MODE_5CHANNEL: ao_directx->chnn = 5; xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_5CHANNEL mode\n" ); break; case AO_CAP_MODE_5_1CHANNEL: ao_directx->chnn = 6; xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : opened in AO_CAP_MODE_5_1CHANNEL mode\n" ); break; case AO_CAP_MODE_A52: case AO_CAP_MODE_AC5: return 0; } if (!CreateSoundBuffer( ao_directx )) return 0; lprintf("ao_directx_open() Exit! Returning ao_directx->rate=%d\n", ao_directx->rate); return ao_directx->rate; } static int ao_directx_num_channels( ao_driver_t * ao_driver ) { ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver; return ao_directx->chnn; } static int ao_directx_bytes_per_frame( ao_driver_t * ao_driver ) { ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver; return ao_directx->frsz; } static int ao_directx_get_gap_tolerance( ao_driver_t * ao_driver ) { return 5000; } static int ao_directx_delay( ao_driver_t * ao_driver ) { DWORD current_read; DWORD bytes_left; DWORD frames_left; ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver; lprintf("ao_directx_delay(%p) Enter\n", (void *)ao_directx); IDirectSoundBuffer_GetCurrentPosition( ao_directx->dsbuffer, ¤t_read, 0 ); if( ao_directx->write_pos > current_read ) bytes_left = ( ao_directx->write_pos - current_read ); else bytes_left = ( ao_directx->write_pos + ao_directx->buffer_size - current_read ); frames_left = ( ao_directx->prebuff_size + bytes_left ) / ao_directx->frsz; lprintf("ao_directx_delay() Exit! Returning frames_left=%lu\n", (unsigned long)frames_left); return frames_left; } static int ao_directx_write( ao_driver_t * ao_driver, int16_t * frame_buffer, uint32_t num_frames ) { ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver; uint32_t frame_bytes; /* how many bytes to lock */ uint32_t wrote; /* number of bytes written */ uint32_t half_size; /* half our sound buffer size */ lprintf("ao_directx_write(%p, %p, %u) Enter\n", (void *)ao_directx, (void *)frame_buffer, num_frames); /* zero write counter */ wrote = 0; /* calculate how many bytes in frame_buffer */ frame_bytes = num_frames * ao_directx->frsz; /* calculate half our buffer size */ half_size = ao_directx->buffer_size / 2; /* fill audio prebuff */ memcpy( ao_directx->prebuff + ao_directx->prebuff_size, frame_buffer, frame_bytes ); ao_directx->prebuff_size = ao_directx->prebuff_size + frame_bytes; /* check to see if we have enough in prebuff to * fill half of our sound buffer */ while( ao_directx->prebuff_size >= half_size ) { /* write to our sound buffer */ if( ao_directx->write_status == DSBUFF_LEFT ) { /* wait for our read pointer to reach the right half * of our sound buffer, we only want to write to the * left side */ WaitForSingleObject( ao_directx->notify_events[ 1 ].hEventNotify, INFINITE ); /* fill left half of our buffer */ wrote = FillSoundBuffer( ao_directx, DSBUFF_LEFT, ao_directx->prebuff ); } else if( ao_directx->write_status == DSBUFF_RIGHT ) { /* wait for our read pointer to reach the left half, * of our sound buffer, we only want to write to the * right side */ WaitForSingleObject( ao_directx->notify_events[ 0 ].hEventNotify, INFINITE ); /* fill right half of our buffer */ wrote = FillSoundBuffer( ao_directx, DSBUFF_RIGHT, ao_directx->prebuff ); } /* calc bytes written and store position for next write */ ao_directx->write_pos = ( ao_directx->write_pos + wrote ) % ao_directx->buffer_size; /* copy remaining contents of prebuff and recalc size */ memcpy( ao_directx->prebuff, ao_directx->prebuff + wrote, ao_directx->prebuff_size - wrote ); ao_directx->prebuff_size = ao_directx->prebuff_size - wrote; } lprintf("ao_directx_write() Exit! Returning num_frames=%u\n", num_frames); return num_frames; } static void ao_directx_close( ao_driver_t * ao_driver ) { ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver; lprintf("ao_directx_close(%p) Enter\n", (void *)ao_directx); /* release any existing sound buffer * related resources */ DestroySoundBuffer( ao_directx ); lprintf("ao_directx_close() Exit!\n"); } static uint32_t ao_directx_get_capabilities( ao_driver_t * ao_driver ) { return AO_CAP_MODE_STEREO | AO_CAP_MIXER_VOL | AO_CAP_PCM_VOL | AO_CAP_MUTE_VOL; } static void ao_directx_exit( ao_driver_t * ao_driver ) { ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver; lprintf("ao_directx_exit(%p) Enter\n", (void *)ao_directx); /* release any existing sound buffer * related resources */ DestroySoundBuffer( ao_directx ); /* release any existing direct sound * related resources */ DestroyDirectSound( ao_directx ); /* free our driver */ free( ao_directx ); lprintf("ao_directx_exit() Exit!\n"); } static int ao_directx_get_property( ao_driver_t * ao_driver, int property ) { return 0; } static int ao_directx_set_property( ao_driver_t * ao_driver, int property, int value ) { ao_directx_t *ao_directx = ( ao_directx_t * ) ao_driver; lprintf("ao_directx_set_property(%p, %d, %d) Enter\n", (void *)ao_directx, property, value); switch( property ) { case AO_PROP_PCM_VOL: case AO_PROP_MIXER_VOL: lprintf("ao_directx_set_property: AO_PROP_PCM_VOL|AO_PROP_MIXER_VOL\n"); ao_directx->volume = value * ( DSBVOLUME_MIN / 100 / 3); if( !ao_directx->mute && ao_directx->dsbuffer ) IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, ao_directx->volume ); xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : volume set to %d - directX volume = %d\n", value, ao_directx->volume); return value; break; case AO_PROP_MUTE_VOL: lprintf("ao_directx_set_property: AO_PROP_MUTE_VOL\n"); ao_directx->mute = value; if( !ao_directx->mute && ao_directx->dsbuffer ) IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, ao_directx->volume ); if( ao_directx->mute && ao_directx->dsbuffer ) IDirectSoundBuffer_SetVolume( ao_directx->dsbuffer, DSBVOLUME_MIN ); xprintf(ao_directx->xine, XINE_VERBOSITY_DEBUG, "ao_directx : mute toggled" ); return value; break; } lprintf("ao_directx_set_property() Exit! Returning ~value=%d\n", ~value); return ~value; } static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data) { audiox_class_t *class = (audiox_class_t *) class_gen; ao_directx_t *ao_directx; ao_directx = calloc(1, sizeof(ao_directx_t)); if (!ao_directx) return NULL; lprintf("open_plugin(%p, %p) Enter\n", (void *)class_gen, (void *)data); lprintf("open_plugin: ao_directx=%p\n", (void *)ao_directx); ao_directx->xine = class->xine; ao_directx->ao_driver.get_capabilities = ao_directx_get_capabilities; ao_directx->ao_driver.get_property = ao_directx_get_property; ao_directx->ao_driver.set_property = ao_directx_set_property; ao_directx->ao_driver.open = ao_directx_open; ao_directx->ao_driver.num_channels = ao_directx_num_channels; ao_directx->ao_driver.bytes_per_frame = ao_directx_bytes_per_frame; ao_directx->ao_driver.delay = ao_directx_delay; ao_directx->ao_driver.write = ao_directx_write; ao_directx->ao_driver.close = ao_directx_close; ao_directx->ao_driver.exit = ao_directx_exit; ao_directx->ao_driver.get_gap_tolerance = ao_directx_get_gap_tolerance; ao_directx->ao_driver.control = ao_directx_control; CreateDirectSound( ao_directx ); lprintf("open_plugin() Exit! Returning ao_directx=%p\n", (void *)ao_directx); return &ao_directx->ao_driver; } static void *init_class (xine_t *xine, const void *data) { audiox_class_t *audiox; lprintf("init_class() Enter\n"); /* * from this point on, nothing should go wrong anymore */ audiox = calloc(1, sizeof (audiox_class_t)); if (!audiox) return NULL; audiox->driver_class.open_plugin = open_plugin; audiox->driver_class.identifier = "DirectX"; audiox->driver_class.description = N_("xine audio output plugin for win32 using directx"); audiox->driver_class.dispose = default_audio_driver_class_dispose; audiox->xine = xine; lprintf("init_class() Exit! Returning audiox=%p\n", (void *)audiox); return audiox; } static const ao_info_t ao_info_directx = { .priority = 1, }; /* * exported plugin catalog entry */ const plugin_info_t xine_plugin_info[] EXPORTED = { /* type, API, "name", version, special_info, init_function */ { PLUGIN_AUDIO_OUT, AO_DIRECTX_IFACE_VERSION, "directx", XINE_VERSION_CODE, &ao_info_directx, init_class }, { PLUGIN_NONE, 0, NULL, 0, NULL, NULL } };