ao_dsound: remove global variables

This commit is contained in:
wm4 2013-06-04 01:17:36 +02:00
parent 8afcb84ee5
commit 3725ab980c
1 changed files with 129 additions and 100 deletions

View File

@ -77,20 +77,24 @@ typedef struct {
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE; } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE;
#endif #endif
static HINSTANCE hdsound_dll = NULL; ///handle to the dll struct priv {
static LPDIRECTSOUND hds = NULL; ///direct sound object HINSTANCE hdsound_dll; ///handle to the dll
static LPDIRECTSOUNDBUFFER hdspribuf = NULL; ///primary direct sound buffer LPDIRECTSOUND hds; ///direct sound object
static LPDIRECTSOUNDBUFFER hdsbuf = NULL; ///secondary direct sound buffer (stream buffer) LPDIRECTSOUNDBUFFER hdspribuf; ///primary direct sound buffer
static int buffer_size = 0; ///size in bytes of the direct sound buffer LPDIRECTSOUNDBUFFER hdsbuf; ///secondary direct sound buffer (stream buffer)
static int write_offset = 0; ///offset of the write cursor in the direct sound buffer int buffer_size; ///size in bytes of the direct sound buffer
static int min_free_space = 0; ///if the free space is below this value get_space() will return 0 int write_offset; ///offset of the write cursor in the direct sound buffer
///there will always be at least this amout of free space to prevent int min_free_space; ///if the free space is below this value get_space() will return 0
///get_space() from returning wrong values when buffer is 100% full. ///there will always be at least this amout of free space to prevent
///will be replaced with nBlockAlign in init() ///get_space() from returning wrong values when buffer is 100% full.
static int underrun_check = 0; ///0 or last reported free space (underrun detection) ///will be replaced with nBlockAlign in init()
static int device_num = 0; ///wanted device number int underrun_check; ///0 or last reported free space (underrun detection)
static GUID device; ///guid of the device int device_num; ///wanted device number
static int audio_volume; GUID device; ///guid of the device
int audio_volume;
int device_index;
};
static float get_delay(struct ao *ao); static float get_delay(struct ao *ao);
@ -130,17 +134,19 @@ static char * dserr2str(int err)
/** /**
\brief uninitialize direct sound \brief uninitialize direct sound
*/ */
static void UninitDirectSound(void) static void UninitDirectSound(struct ao *ao)
{ {
struct priv *p = ao->priv;
// finally release the DirectSound object // finally release the DirectSound object
if (hds) { if (p->hds) {
IDirectSound_Release(hds); IDirectSound_Release(p->hds);
hds = NULL; p->hds = NULL;
} }
// free DSOUND.DLL // free DSOUND.DLL
if (hdsound_dll) { if (p->hdsound_dll) {
FreeLibrary(hdsound_dll); FreeLibrary(p->hdsound_dll);
hdsound_dll = NULL; p->hdsound_dll = NULL;
} }
mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound uninitialized\n"); mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound uninitialized\n");
} }
@ -167,15 +173,17 @@ static void print_help(void)
static BOOL CALLBACK DirectSoundEnum(LPGUID guid, LPCSTR desc, LPCSTR module, static BOOL CALLBACK DirectSoundEnum(LPGUID guid, LPCSTR desc, LPCSTR module,
LPVOID context) LPVOID context)
{ {
int *device_index = context; struct ao *ao = context;
mp_msg(MSGT_AO, MSGL_V, "%i %s ", *device_index, desc); struct priv *p = ao->priv;
if (device_num == *device_index) {
mp_msg(MSGT_AO, MSGL_V, "%i %s ", p->device_index, desc);
if (p->device_num == p->device_index) {
mp_msg(MSGT_AO, MSGL_V, "<--"); mp_msg(MSGT_AO, MSGL_V, "<--");
if (guid) if (guid)
memcpy(&device, guid, sizeof(GUID)); memcpy(&p->device, guid, sizeof(GUID));
} }
mp_msg(MSGT_AO, MSGL_V, "\n"); mp_msg(MSGT_AO, MSGL_V, "\n");
(*device_index)++; p->device_index++;
return TRUE; return TRUE;
} }
@ -184,50 +192,52 @@ static BOOL CALLBACK DirectSoundEnum(LPGUID guid, LPCSTR desc, LPCSTR module,
\brief initilize direct sound \brief initilize direct sound
\return 0 if error, 1 if ok \return 0 if error, 1 if ok
*/ */
static int InitDirectSound(void) static int InitDirectSound(struct ao *ao, char *params)
{ {
struct priv *p = ao->priv;
DSCAPS dscaps; DSCAPS dscaps;
// initialize directsound // initialize directsound
HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN); HRESULT (WINAPI *OurDirectSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKA, LPVOID); HRESULT (WINAPI *OurDirectSoundEnumerate)(LPDSENUMCALLBACKA, LPVOID);
int device_index = 0; p->device_index = 0;
const opt_t subopts[] = { const opt_t subopts[] = {
{"device", OPT_ARG_INT, &device_num, NULL}, {"device", OPT_ARG_INT, &p->device_num, NULL},
{NULL} {NULL}
}; };
if (subopt_parse(ao_subdevice, subopts) != 0) { if (subopt_parse(params, subopts) != 0) {
print_help(); print_help();
return 0; return 0;
} }
hdsound_dll = LoadLibrary("DSOUND.DLL"); p->hdsound_dll = LoadLibrary("DSOUND.DLL");
if (hdsound_dll == NULL) { if (p->hdsound_dll == NULL) {
mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot load DSOUND.DLL\n"); mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: cannot load DSOUND.DLL\n");
return 0; return 0;
} }
OurDirectSoundCreate = (void *)GetProcAddress(hdsound_dll, OurDirectSoundCreate = (void *)GetProcAddress(p->hdsound_dll,
"DirectSoundCreate"); "DirectSoundCreate");
OurDirectSoundEnumerate = (void *)GetProcAddress(hdsound_dll, OurDirectSoundEnumerate = (void *)GetProcAddress(p->hdsound_dll,
"DirectSoundEnumerateA"); "DirectSoundEnumerateA");
if (OurDirectSoundCreate == NULL || OurDirectSoundEnumerate == NULL) { if (OurDirectSoundCreate == NULL || OurDirectSoundEnumerate == NULL) {
mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: GetProcAddress FAILED\n"); mp_msg(MSGT_AO, MSGL_ERR, "ao_dsound: GetProcAddress FAILED\n");
FreeLibrary(hdsound_dll); FreeLibrary(p->hdsound_dll);
return 0; return 0;
} }
// Enumerate all directsound devices // Enumerate all directsound p->devices
mp_msg(MSGT_AO, MSGL_V, "ao_dsound: Output Devices:\n"); mp_msg(MSGT_AO, MSGL_V, "ao_dsound: Output Devices:\n");
OurDirectSoundEnumerate(DirectSoundEnum, &device_index); OurDirectSoundEnumerate(DirectSoundEnum, ao);
// Create the direct sound object // Create the direct sound object
if (FAILED(OurDirectSoundCreate((device_num) ? &device : NULL, &hds, if (FAILED(OurDirectSoundCreate((p->device_num) ? &p->device : NULL,
NULL))) &p->hds, NULL)))
{ {
mp_msg(MSGT_AO, MSGL_ERR, mp_msg(MSGT_AO, MSGL_ERR,
"ao_dsound: cannot create a DirectSound device\n"); "ao_dsound: cannot create a DirectSound device\n");
FreeLibrary(hdsound_dll); FreeLibrary(p->hdsound_dll);
return 0; return 0;
} }
@ -241,20 +251,20 @@ static int InitDirectSound(void)
* sound without any video, and so what window handle should we use ??? * sound without any video, and so what window handle should we use ???
* The hack for now is to use the Desktop window handle - it seems to be * The hack for now is to use the Desktop window handle - it seems to be
* working */ * working */
if (IDirectSound_SetCooperativeLevel(hds, GetDesktopWindow(), if (IDirectSound_SetCooperativeLevel(p->hds, GetDesktopWindow(),
DSSCL_EXCLUSIVE)) DSSCL_EXCLUSIVE))
{ {
mp_msg(MSGT_AO, MSGL_ERR, mp_msg(MSGT_AO, MSGL_ERR,
"ao_dsound: cannot set direct sound cooperative level\n"); "ao_dsound: cannot set direct sound cooperative level\n");
IDirectSound_Release(hds); IDirectSound_Release(p->hds);
FreeLibrary(hdsound_dll); FreeLibrary(p->hdsound_dll);
return 0; return 0;
} }
mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound initialized\n"); mp_msg(MSGT_AO, MSGL_V, "ao_dsound: DirectSound initialized\n");
memset(&dscaps, 0, sizeof(DSCAPS)); memset(&dscaps, 0, sizeof(DSCAPS));
dscaps.dwSize = sizeof(DSCAPS); dscaps.dwSize = sizeof(DSCAPS);
if (DS_OK == IDirectSound_GetCaps(hds, &dscaps)) { if (DS_OK == IDirectSound_GetCaps(p->hds, &dscaps)) {
if (dscaps.dwFlags & DSCAPS_EMULDRIVER) if (dscaps.dwFlags & DSCAPS_EMULDRIVER)
mp_msg(MSGT_AO, MSGL_V, mp_msg(MSGT_AO, MSGL_V,
"ao_dsound: DirectSound is emulated, waveOut may give better performance\n"); "ao_dsound: DirectSound is emulated, waveOut may give better performance\n");
@ -268,15 +278,17 @@ static int InitDirectSound(void)
/** /**
\brief destroy the direct sound buffer \brief destroy the direct sound buffer
*/ */
static void DestroyBuffer(void) static void DestroyBuffer(struct ao *ao)
{ {
if (hdsbuf) { struct priv *p = ao->priv;
IDirectSoundBuffer_Release(hdsbuf);
hdsbuf = NULL; if (p->hdsbuf) {
IDirectSoundBuffer_Release(p->hdsbuf);
p->hdsbuf = NULL;
} }
if (hdspribuf) { if (p->hdspribuf) {
IDirectSoundBuffer_Release(hdspribuf); IDirectSoundBuffer_Release(p->hdspribuf);
hdspribuf = NULL; p->hdspribuf = NULL;
} }
} }
@ -288,21 +300,22 @@ static void DestroyBuffer(void)
*/ */
static int write_buffer(struct ao *ao, unsigned char *data, int len) static int write_buffer(struct ao *ao, unsigned char *data, int len)
{ {
struct priv *p = ao->priv;
HRESULT res; HRESULT res;
LPVOID lpvPtr1; LPVOID lpvPtr1;
DWORD dwBytes1; DWORD dwBytes1;
LPVOID lpvPtr2; LPVOID lpvPtr2;
DWORD dwBytes2; DWORD dwBytes2;
underrun_check = 0; p->underrun_check = 0;
// Lock the buffer // Lock the buffer
res = IDirectSoundBuffer_Lock(hdsbuf, write_offset, len, &lpvPtr1, &dwBytes1, res = IDirectSoundBuffer_Lock(p->hdsbuf, p->write_offset, len, &lpvPtr1,
&lpvPtr2, &dwBytes2, 0); &dwBytes1, &lpvPtr2, &dwBytes2, 0);
// If the buffer was lost, restore and retry lock. // If the buffer was lost, restore and retry lock.
if (DSERR_BUFFERLOST == res) { if (DSERR_BUFFERLOST == res) {
IDirectSoundBuffer_Restore(hdsbuf); IDirectSoundBuffer_Restore(p->hdsbuf);
res = IDirectSoundBuffer_Lock(hdsbuf, write_offset, len, &lpvPtr1, res = IDirectSoundBuffer_Lock(p->hdsbuf, p->write_offset, len, &lpvPtr1,
&dwBytes1, &lpvPtr2, &dwBytes2, 0); &dwBytes1, &lpvPtr2, &dwBytes2, 0);
} }
@ -313,28 +326,28 @@ static int write_buffer(struct ao *ao, unsigned char *data, int len)
if (lpvPtr2 != NULL) if (lpvPtr2 != NULL)
memcpy(lpvPtr2, (char *)data + dwBytes1, dwBytes2); memcpy(lpvPtr2, (char *)data + dwBytes1, dwBytes2);
write_offset += dwBytes1 + dwBytes2; p->write_offset += dwBytes1 + dwBytes2;
if (write_offset >= buffer_size) if (p->write_offset >= p->buffer_size)
write_offset = dwBytes2; p->write_offset = dwBytes2;
} else { } else {
// Write to pointers without reordering. // Write to pointers without reordering.
memcpy(lpvPtr1, data, dwBytes1); memcpy(lpvPtr1, data, dwBytes1);
if (NULL != lpvPtr2) if (NULL != lpvPtr2)
memcpy(lpvPtr2, data + dwBytes1, dwBytes2); memcpy(lpvPtr2, data + dwBytes1, dwBytes2);
write_offset += dwBytes1 + dwBytes2; p->write_offset += dwBytes1 + dwBytes2;
if (write_offset >= buffer_size) if (p->write_offset >= p->buffer_size)
write_offset = dwBytes2; p->write_offset = dwBytes2;
} }
// Release the data back to DirectSound. // Release the data back to DirectSound.
res = IDirectSoundBuffer_Unlock(hdsbuf, lpvPtr1, dwBytes1, lpvPtr2, res = IDirectSoundBuffer_Unlock(p->hdsbuf, lpvPtr1, dwBytes1, lpvPtr2,
dwBytes2); dwBytes2);
if (SUCCEEDED(res)) { if (SUCCEEDED(res)) {
// Success. // Success.
DWORD status; DWORD status;
IDirectSoundBuffer_GetStatus(hdsbuf, &status); IDirectSoundBuffer_GetStatus(p->hdsbuf, &status);
if (!(status & DSBSTATUS_PLAYING)) if (!(status & DSBSTATUS_PLAYING))
res = IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING); res = IDirectSoundBuffer_Play(p->hdsbuf, 0, 0, DSBPLAY_LOOPING);
return dwBytes1 + dwBytes2; return dwBytes1 + dwBytes2;
} }
} }
@ -352,20 +365,21 @@ static int write_buffer(struct ao *ao, unsigned char *data, int len)
*/ */
static int control(struct ao *ao, enum aocontrol cmd, void *arg) static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{ {
struct priv *p = ao->priv;
DWORD volume; DWORD volume;
switch (cmd) { switch (cmd) {
case AOCONTROL_GET_VOLUME: { case AOCONTROL_GET_VOLUME: {
ao_control_vol_t *vol = (ao_control_vol_t *)arg; ao_control_vol_t *vol = (ao_control_vol_t *)arg;
vol->left = vol->right = audio_volume; vol->left = vol->right = p->audio_volume;
return CONTROL_OK; return CONTROL_OK;
} }
case AOCONTROL_SET_VOLUME: { case AOCONTROL_SET_VOLUME: {
ao_control_vol_t *vol = (ao_control_vol_t *)arg; ao_control_vol_t *vol = (ao_control_vol_t *)arg;
volume = audio_volume = vol->right; volume = p->audio_volume = vol->right;
if (volume < 1) if (volume < 1)
volume = 1; volume = 1;
volume = (DWORD)(log10(volume) * 5000.0) - 10000; volume = (DWORD)(log10(volume) * 5000.0) - 10000;
IDirectSoundBuffer_SetVolume(hdsbuf, volume); IDirectSoundBuffer_SetVolume(p->hdsbuf, volume);
return CONTROL_OK; return CONTROL_OK;
} }
} }
@ -382,12 +396,16 @@ static int control(struct ao *ao, enum aocontrol cmd, void *arg)
*/ */
static int init(struct ao *ao, char *params) static int init(struct ao *ao, char *params)
{ {
struct priv *p = talloc_zero(ao, struct priv);
int res; int res;
if (!InitDirectSound())
ao->priv = p;
if (!InitDirectSound(ao, params))
return -1; return -1;
ao->no_persistent_volume = true; ao->no_persistent_volume = true;
audio_volume = 100; p->audio_volume = 100;
// ok, now create the buffers // ok, now create the buffers
WAVEFORMATEXTENSIBLE wformat; WAVEFORMATEXTENSIBLE wformat;
@ -472,22 +490,22 @@ static int init(struct ao *ao, char *params)
dsbdesc.dwBufferBytes = ao->buffersize; dsbdesc.dwBufferBytes = ao->buffersize;
dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat; dsbdesc.lpwfxFormat = (WAVEFORMATEX *)&wformat;
buffer_size = dsbdesc.dwBufferBytes; p->buffer_size = dsbdesc.dwBufferBytes;
write_offset = 0; p->write_offset = 0;
min_free_space = wformat.Format.nBlockAlign; p->min_free_space = wformat.Format.nBlockAlign;
ao->outburst = wformat.Format.nBlockAlign * 512; ao->outburst = wformat.Format.nBlockAlign * 512;
// create primary buffer and set its format // create primary buffer and set its format
res = IDirectSound_CreateSoundBuffer(hds, &dsbpridesc, &hdspribuf, NULL); res = IDirectSound_CreateSoundBuffer(p->hds, &dsbpridesc, &p->hdspribuf, NULL);
if (res != DS_OK) { if (res != DS_OK) {
UninitDirectSound(); UninitDirectSound(ao);
mp_msg(MSGT_AO, MSGL_ERR, mp_msg(MSGT_AO, MSGL_ERR,
"ao_dsound: cannot create primary buffer (%s)\n", "ao_dsound: cannot create primary buffer (%s)\n",
dserr2str(res)); dserr2str(res));
return -1; return -1;
} }
res = IDirectSoundBuffer_SetFormat(hdspribuf, (WAVEFORMATEX *)&wformat); res = IDirectSoundBuffer_SetFormat(p->hdspribuf, (WAVEFORMATEX *)&wformat);
if (res != DS_OK) { if (res != DS_OK) {
mp_msg(MSGT_AO, MSGL_WARN, mp_msg(MSGT_AO, MSGL_WARN,
"ao_dsound: cannot set primary buffer format (%s), using " "ao_dsound: cannot set primary buffer format (%s), using "
@ -498,15 +516,15 @@ static int init(struct ao *ao, char *params)
// now create the stream buffer // now create the stream buffer
res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL); res = IDirectSound_CreateSoundBuffer(p->hds, &dsbdesc, &p->hdsbuf, NULL);
if (res != DS_OK) { if (res != DS_OK) {
if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) { if (dsbdesc.dwFlags & DSBCAPS_LOCHARDWARE) {
// Try without DSBCAPS_LOCHARDWARE // Try without DSBCAPS_LOCHARDWARE
dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE; dsbdesc.dwFlags &= ~DSBCAPS_LOCHARDWARE;
res = IDirectSound_CreateSoundBuffer(hds, &dsbdesc, &hdsbuf, NULL); res = IDirectSound_CreateSoundBuffer(p->hds, &dsbdesc, &p->hdsbuf, NULL);
} }
if (res != DS_OK) { if (res != DS_OK) {
UninitDirectSound(); UninitDirectSound(ao);
mp_msg(MSGT_AO, MSGL_ERR, mp_msg(MSGT_AO, MSGL_ERR,
"ao_dsound: cannot create secondary (stream)buffer (%s)\n", "ao_dsound: cannot create secondary (stream)buffer (%s)\n",
dserr2str(res)); dserr2str(res));
@ -524,11 +542,13 @@ static int init(struct ao *ao, char *params)
*/ */
static void reset(struct ao *ao) static void reset(struct ao *ao)
{ {
IDirectSoundBuffer_Stop(hdsbuf); struct priv *p = ao->priv;
IDirectSoundBuffer_Stop(p->hdsbuf);
// reset directsound buffer // reset directsound buffer
IDirectSoundBuffer_SetCurrentPosition(hdsbuf, 0); IDirectSoundBuffer_SetCurrentPosition(p->hdsbuf, 0);
write_offset = 0; p->write_offset = 0;
underrun_check = 0; p->underrun_check = 0;
} }
/** /**
@ -536,7 +556,9 @@ static void reset(struct ao *ao)
*/ */
static void audio_pause(struct ao *ao) static void audio_pause(struct ao *ao)
{ {
IDirectSoundBuffer_Stop(hdsbuf); struct priv *p = ao->priv;
IDirectSoundBuffer_Stop(p->hdsbuf);
} }
/** /**
@ -544,7 +566,9 @@ static void audio_pause(struct ao *ao)
*/ */
static void audio_resume(struct ao *ao) static void audio_resume(struct ao *ao)
{ {
IDirectSoundBuffer_Play(hdsbuf, 0, 0, DSBPLAY_LOOPING); struct priv *p = ao->priv;
IDirectSoundBuffer_Play(p->hdsbuf, 0, 0, DSBPLAY_LOOPING);
} }
/** /**
@ -557,36 +581,37 @@ static void uninit(struct ao *ao, bool immed)
mp_sleep_us(get_delay(ao) * 1000000); mp_sleep_us(get_delay(ao) * 1000000);
reset(ao); reset(ao);
DestroyBuffer(); DestroyBuffer(ao);
UninitDirectSound(); UninitDirectSound(ao);
} }
// return exact number of free (safe to write) bytes // return exact number of free (safe to write) bytes
static int check_free_buffer_size(struct ao *ao) static int check_free_buffer_size(struct ao *ao)
{ {
struct priv *p = ao->priv;
int space; int space;
DWORD play_offset; DWORD play_offset;
IDirectSoundBuffer_GetCurrentPosition(hdsbuf, &play_offset, NULL); IDirectSoundBuffer_GetCurrentPosition(p->hdsbuf, &play_offset, NULL);
space = buffer_size - (write_offset - play_offset); space = p->buffer_size - (p->write_offset - play_offset);
// | | <-- const --> | | | // | | <-- const --> | | |
// buffer start play_cursor write_cursor write_offset buffer end // buffer start play_cursor write_cursor p->write_offset buffer end
// play_cursor is the actual postion of the play cursor // play_cursor is the actual postion of the play cursor
// write_cursor is the position after which it is assumed to be save to write data // write_cursor is the position after which it is assumed to be save to write data
// write_offset is the postion where we actually write the data to // p->write_offset is the postion where we actually write the data to
if (space > buffer_size) if (space > p->buffer_size)
space -= buffer_size; // write_offset < play_offset space -= p->buffer_size; // p->write_offset < play_offset
// Check for buffer underruns. An underrun happens if DirectSound // Check for buffer underruns. An underrun happens if DirectSound
// started to play old data beyond the current write_offset. Detect this // started to play old data beyond the current p->write_offset. Detect this
// by checking whether the free space shrinks, even though no data was // by checking whether the free space shrinks, even though no data was
// written (i.e. no write_buffer). Doesn't always work, but the only // written (i.e. no write_buffer). Doesn't always work, but the only
// reason we need this is to deal with the situation when playback ends, // reason we need this is to deal with the situation when playback ends,
// and the buffer is only half-filled. // and the buffer is only half-filled.
if (space < underrun_check) { if (space < p->underrun_check) {
// there's no useful data in the buffers // there's no useful data in the buffers
space = buffer_size; space = p->buffer_size;
reset(ao); reset(ao);
} }
underrun_check = space; p->underrun_check = space;
return space; return space;
} }
@ -596,10 +621,12 @@ static int check_free_buffer_size(struct ao *ao)
*/ */
static int get_space(struct ao *ao) static int get_space(struct ao *ao)
{ {
struct priv *p = ao->priv;
int space = check_free_buffer_size(ao); int space = check_free_buffer_size(ao);
if (space < min_free_space) if (space < p->min_free_space)
return 0; return 0;
return space - min_free_space; return space - p->min_free_space;
} }
/** /**
@ -627,8 +654,10 @@ static int play(struct ao *ao, void *data, int len, int flags)
*/ */
static float get_delay(struct ao *ao) static float get_delay(struct ao *ao)
{ {
struct priv *p = ao->priv;
int space = check_free_buffer_size(ao); int space = check_free_buffer_size(ao);
return (float)(buffer_size - space) / (float)ao->bps; return (float)(p->buffer_size - space) / (float)ao->bps;
} }
const struct ao_driver audio_out_dsound = { const struct ao_driver audio_out_dsound = {