diff options
author | Chris Robinson <[email protected]> | 2009-08-13 12:28:46 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2009-08-13 12:28:46 -0700 |
commit | 243939f94f26b1840255590b2454e931374de73f (patch) | |
tree | 2739c389486d703c5d31ab262aecb1be02d11b5c /Alc | |
parent | 739385bd89c4eaae662ee1e017f74ba0da31f350 (diff) |
Allow delaying playback start until context creation, and don't use UpdateSize to store the buffer size
This will make it possible to support the context attributes (frequency,
refresh, etc) for some backends
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/ALc.c | 48 | ||||
-rw-r--r-- | Alc/alsa.c | 68 | ||||
-rw-r--r-- | Alc/dsound.c | 19 | ||||
-rw-r--r-- | Alc/oss.c | 71 | ||||
-rw-r--r-- | Alc/portaudio.c | 23 | ||||
-rw-r--r-- | Alc/pulseaudio.c | 20 | ||||
-rw-r--r-- | Alc/wave.c | 85 | ||||
-rw-r--r-- | Alc/winmm.c | 2 |
8 files changed, 231 insertions, 105 deletions
@@ -38,7 +38,7 @@ #include "alu.h" -#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } +#define EmptyFuncs { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } static struct { const char *name; void (*Init)(BackendFuncs*); @@ -391,10 +391,14 @@ static ALCboolean IsContext(ALCcontext *pContext) { ALCcontext *pTempContext; + SuspendContext(NULL); + pTempContext = g_pContextList; while (pTempContext && pTempContext != pContext) pTempContext = pTempContext->next; + ProcessContext(NULL); + return (pTempContext ? ALC_TRUE : ALC_FALSE); } @@ -1045,9 +1049,9 @@ ALCAPI ALCcontext* ALCAPIENTRY alcCreateContext(ALCdevice *device, const ALCint // Check for attributes if (attrList) { - ALCint numMono = ALContext->Device->lNumMonoSources; - ALCint numStereo = ALContext->Device->lNumStereoSources; - ALCuint numSends = ALContext->Device->NumAuxSends; + ALCint numMono = device->lNumMonoSources; + ALCint numStereo = device->lNumStereoSources; + ALCuint numSends = device->NumAuxSends; ulAttributeIndex = 0; while ((ulAttributeIndex < 10) && (attrList[ulAttributeIndex])) @@ -1056,19 +1060,19 @@ ALCAPI ALCcontext* ALCAPIENTRY alcCreateContext(ALCdevice *device, const ALCint { ulRequestedStereoSources = attrList[ulAttributeIndex + 1]; - if (ulRequestedStereoSources > ALContext->Device->MaxNoOfSources) - ulRequestedStereoSources = ALContext->Device->MaxNoOfSources; + if (ulRequestedStereoSources > device->MaxNoOfSources) + ulRequestedStereoSources = device->MaxNoOfSources; numStereo = ulRequestedStereoSources; - numMono = ALContext->Device->MaxNoOfSources - numStereo; + numMono = device->MaxNoOfSources - numStereo; } if(attrList[ulAttributeIndex] == ALC_MAX_AUXILIARY_SENDS) { RequestedSends = attrList[ulAttributeIndex + 1]; - if(RequestedSends > ALContext->Device->NumAuxSends) - RequestedSends = ALContext->Device->NumAuxSends; + if(RequestedSends > device->NumAuxSends) + RequestedSends = device->NumAuxSends; numSends = RequestedSends; } @@ -1076,9 +1080,16 @@ ALCAPI ALCcontext* ALCAPIENTRY alcCreateContext(ALCdevice *device, const ALCint ulAttributeIndex += 2; } - ALContext->Device->lNumMonoSources = numMono; - ALContext->Device->lNumStereoSources = numStereo; - ALContext->Device->NumAuxSends = numSends; + device->lNumMonoSources = numMono; + device->lNumStereoSources = numStereo; + device->NumAuxSends = numSends; + } + + if(ALCdevice_StartContext(device, ALContext) == ALC_FALSE) + { + alcDestroyContext(ALContext); + ALContext = NULL; + SetALCError(ALC_INVALID_VALUE); } } else @@ -1105,11 +1116,10 @@ ALCAPI ALCvoid ALCAPIENTRY alcDestroyContext(ALCcontext *context) InitAL(); - // Lock context list - SuspendContext(NULL); - if (IsContext(context)) { + ALCdevice_StopContext(context->Device, context); + // Lock context SuspendContext(context); @@ -1136,8 +1146,6 @@ ALCAPI ALCvoid ALCAPIENTRY alcDestroyContext(ALCcontext *context) } else SetALCError(ALC_INVALID_CONTEXT); - - ProcessContext(NULL); } @@ -1268,9 +1276,9 @@ ALCAPI ALCdevice* ALCAPIENTRY alcOpenDevice(const ALCchar *deviceName) if(!aluChannelsFromFormat(device->Format)) device->Format = AL_FORMAT_STEREO16; - device->UpdateSize = GetConfigValueInt(NULL, "refresh", 4096); - if((ALint)device->UpdateSize <= 0) - device->UpdateSize = 4096; + device->BufferSize = GetConfigValueInt(NULL, "refresh", 4096); + if((ALint)device->BufferSize <= 0) + device->BufferSize = 4096; device->MaxNoOfSources = GetConfigValueInt(NULL, "sources", 256); if((ALint)device->MaxNoOfSources <= 0) @@ -324,17 +324,8 @@ static ALuint ALSANoMMapCaptureProc(ALvoid *ptr) static ALCboolean alsa_open_playback(ALCdevice *device, const ALCchar *deviceName) { - snd_pcm_uframes_t bufferSizeInFrames; - snd_pcm_sw_params_t *sp = NULL; - snd_pcm_hw_params_t *p = NULL; - snd_pcm_access_t access; - unsigned int periods; - unsigned int rate; alsa_data *data; char driver[64]; - const char *str; - int allowmmap; - char *err; int i; if(!alsa_handle) @@ -389,6 +380,35 @@ open_alsa: return ALC_FALSE; } + device->ExtraData = data; + return ALC_TRUE; +} + +static void alsa_close_playback(ALCdevice *device) +{ + alsa_data *data = (alsa_data*)device->ExtraData; + + psnd_pcm_close(data->pcmHandle); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean alsa_start_context(ALCdevice *device, ALCcontext *context) +{ + alsa_data *data = (alsa_data*)device->ExtraData; + snd_pcm_uframes_t bufferSizeInFrames; + snd_pcm_sw_params_t *sp = NULL; + snd_pcm_hw_params_t *p = NULL; + snd_pcm_access_t access; + unsigned int periods; + unsigned int rate; + const char *str; + int allowmmap; + char *err; + int i; + (void)context; + + switch(aluBytesFromFormat(device->Format)) { case 1: @@ -403,7 +423,7 @@ open_alsa: } periods = GetConfigValueInt("alsa", "periods", 0); - bufferSizeInFrames = device->UpdateSize; + bufferSizeInFrames = device->BufferSize; rate = device->Frequency; str = GetConfigValue("alsa", "mmap", "true"); @@ -449,8 +469,6 @@ open_alsa: { AL_PRINT("%s failed: %s\n", err, psnd_strerror(i)); psnd_pcm_hw_params_free(p); - psnd_pcm_close(data->pcmHandle); - free(data); return ALC_FALSE; } @@ -469,8 +487,6 @@ open_alsa: { AL_PRINT("%s failed: %s\n", err, psnd_strerror(i)); psnd_pcm_sw_params_free(sp); - psnd_pcm_close(data->pcmHandle); - free(data); return ALC_FALSE; } @@ -483,8 +499,6 @@ open_alsa: if(!data->buffer) { AL_PRINT("buffer malloc failed\n"); - psnd_pcm_close(data->pcmHandle); - free(data); return ALC_FALSE; } } @@ -494,14 +508,12 @@ open_alsa: if(i < 0) { AL_PRINT("prepare error: %s\n", psnd_strerror(i)); - psnd_pcm_close(data->pcmHandle); free(data->buffer); - free(data); + data->buffer = NULL; return ALC_FALSE; } } - device->ExtraData = data; if(access == SND_PCM_ACCESS_RW_INTERLEAVED) data->thread = StartThread(ALSANoMMapProc, device); else @@ -509,10 +521,8 @@ open_alsa: if(data->thread == NULL) { AL_PRINT("Could not create playback thread\n"); - psnd_pcm_close(data->pcmHandle); - device->ExtraData = NULL; free(data->buffer); - free(data); + data->buffer = NULL; return ALC_FALSE; } @@ -522,16 +532,20 @@ open_alsa: return ALC_TRUE; } -static void alsa_close_playback(ALCdevice *device) +static void alsa_stop_context(ALCdevice *device, ALCcontext *context) { alsa_data *data = (alsa_data*)device->ExtraData; + (void)context; + + if(!data->thread) + return; + data->killNow = 1; StopThread(data->thread); - psnd_pcm_close(data->pcmHandle); + data->thread = NULL; free(data->buffer); - free(data); - device->ExtraData = NULL; + data->buffer = NULL; } @@ -909,6 +923,8 @@ static ALCuint alsa_available_samples(ALCdevice *pDevice) BackendFuncs alsa_funcs = { alsa_open_playback, alsa_close_playback, + alsa_start_context, + alsa_stop_context, alsa_open_capture, alsa_close_capture, alsa_start_capture, diff --git a/Alc/dsound.c b/Alc/dsound.c index 2329a4d7..92ee8073 100644 --- a/Alc/dsound.c +++ b/Alc/dsound.c @@ -309,7 +309,7 @@ static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceNam memset(&DSBDescription,0,sizeof(DSBUFFERDESC)); DSBDescription.dwSize=sizeof(DSBUFFERDESC); DSBDescription.dwFlags=DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2; - DSBDescription.dwBufferBytes=(device->UpdateSize/num_frags) * num_frags * frameSize; + DSBDescription.dwBufferBytes=(device->BufferSize/num_frags) * num_frags * frameSize; DSBDescription.lpwfxFormat=&OutputType.Format; hr = IDirectSound_CreateSoundBuffer(pData->lpDS, &DSBDescription, &pData->DSsbuffer, NULL); } @@ -339,7 +339,7 @@ static ALCboolean DSoundOpenPlayback(ALCdevice *device, const ALCchar *deviceNam } device->Format = format; - device->UpdateSize /= num_frags; + device->UpdateSize = device->BufferSize/num_frags; return ALC_TRUE; } @@ -360,6 +360,19 @@ static void DSoundClosePlayback(ALCdevice *device) device->ExtraData = NULL; } +static ALCboolean DSoundStartContext(ALCdevice *device, ALCcontext *context) +{ + return ALC_TRUE; + (void)device; + (void)context; +} + +static void DSoundStopContext(ALCdevice *device, ALCcontext *context) +{ + (void)device; + (void)context; +} + static ALCboolean DSoundOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) { @@ -403,6 +416,8 @@ static ALCuint DSoundAvailableSamples(ALCdevice *pDevice) BackendFuncs DSoundFuncs = { DSoundOpenPlayback, DSoundClosePlayback, + DSoundStartContext, + DSoundStopContext, DSoundOpenCapture, DSoundCloseCapture, DSoundStartCapture, @@ -145,18 +145,8 @@ static ALuint OSSCaptureProc(ALvoid *ptr) static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName) { - int numFragmentsLogSize; - int log2FragmentSize; - unsigned int periods; - audio_buf_info info; - ALuint frameSize; char driver[64]; - int numChannels; oss_data *data; - int ossFormat; - int ossSpeed; - char *err; - int i; strncpy(driver, GetConfigValue("oss", "device", "/dev/dsp"), sizeof(driver)-1); driver[sizeof(driver)-1] = 0; @@ -180,6 +170,34 @@ static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName return ALC_FALSE; } + device->ExtraData = data; + return ALC_TRUE; +} + +static void oss_close_playback(ALCdevice *device) +{ + oss_data *data = (oss_data*)device->ExtraData; + + close(data->fd); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean oss_start_context(ALCdevice *device, ALCcontext *context) +{ + oss_data *data = (oss_data*)device->ExtraData; + int numFragmentsLogSize; + int log2FragmentSize; + unsigned int periods; + audio_buf_info info; + ALuint frameSize; + int numChannels; + int ossFormat; + int ossSpeed; + char *err; + int i; + (void)context; + switch(aluBytesFromFormat(device->Format)) { case 1: @@ -200,7 +218,7 @@ static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName frameSize = numChannels * aluBytesFromFormat(device->Format); ossSpeed = device->Frequency; - log2FragmentSize = log2i(device->UpdateSize * frameSize / periods); + log2FragmentSize = log2i(device->BufferSize * frameSize / periods); /* according to the OSS spec, 16 bytes are the minimum */ if (log2FragmentSize < 4) @@ -215,8 +233,6 @@ static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName ok(ioctl(data->fd, SNDCTL_DSP_GETOSPACE, &info), "get space"))) { AL_PRINT("%s failed: %s\n", err, strerror(errno)); - close(data->fd); - free(data); return ALC_FALSE; } #undef ok @@ -224,8 +240,6 @@ static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName if((int)aluChannelsFromFormat(device->Format) != numChannels) { AL_PRINT("Could not set %d channels, got %d instead\n", aluChannelsFromFormat(device->Format), numChannels); - close(data->fd); - free(data); return ALC_FALSE; } @@ -233,41 +247,40 @@ static ALCboolean oss_open_playback(ALCdevice *device, const ALCchar *deviceName (ossFormat == AFMT_S16_NE && aluBytesFromFormat(device->Format) == 2))) { AL_PRINT("Could not set %d-bit output, got format %#x\n", aluBytesFromFormat(device->Format)*8, ossFormat); - close(data->fd); - free(data); return ALC_FALSE; } + device->Frequency = ossSpeed; + device->UpdateSize = info.fragsize / frameSize; + data->data_size = device->UpdateSize * frameSize; data->mix_data = calloc(1, data->data_size); - device->ExtraData = data; data->thread = StartThread(OSSProc, device); if(data->thread == NULL) { - device->ExtraData = NULL; free(data->mix_data); - free(data); + data->mix_data = NULL; return ALC_FALSE; } - device->Frequency = ossSpeed; - device->UpdateSize = info.fragsize / frameSize; - return ALC_TRUE; } -static void oss_close_playback(ALCdevice *device) +static void oss_stop_context(ALCdevice *device, ALCcontext *context) { oss_data *data = (oss_data*)device->ExtraData; + (void)context; + + if(!data->thread) + return; + data->killNow = 1; StopThread(data->thread); - - close(data->fd); + data->thread = NULL; free(data->mix_data); - free(data); - device->ExtraData = NULL; + data->mix_data = NULL; } @@ -434,6 +447,8 @@ static ALCuint oss_available_samples(ALCdevice *pDevice) BackendFuncs oss_funcs = { oss_open_playback, oss_close_playback, + oss_start_context, + oss_stop_context, oss_open_capture, oss_close_capture, oss_start_capture, diff --git a/Alc/portaudio.c b/Alc/portaudio.c index 47333c8f..ccc8647c 100644 --- a/Alc/portaudio.c +++ b/Alc/portaudio.c @@ -97,7 +97,7 @@ static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName) outParams.device = GetConfigValueInt("port", "device", -1); if(outParams.device < 0) outParams.device = pPa_GetDefaultOutputDevice(); - outParams.suggestedLatency = (float)device->UpdateSize / + outParams.suggestedLatency = (float)device->BufferSize / (float)device->Frequency; outParams.hostApiSpecificStreamInfo = NULL; @@ -120,8 +120,8 @@ static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName) outParams.channelCount = aluChannelsFromFormat(device->Format); err = pPa_OpenStream(&data->stream, NULL, &outParams, device->Frequency, - device->UpdateSize/periods, paNoFlag, - pa_callback, device); + device->BufferSize/periods, paNoFlag, + pa_callback, device); if(err != paNoError) { AL_PRINT("Pa_OpenStream() returned an error: %s\n", pPa_GetErrorText(err)); @@ -140,7 +140,7 @@ static ALCboolean pa_open_playback(ALCdevice *device, const ALCchar *deviceName) return ALC_FALSE; } - device->UpdateSize /= periods; + device->UpdateSize = device->BufferSize/periods; return ALC_TRUE; } @@ -161,6 +161,19 @@ static void pa_close_playback(ALCdevice *device) device->ExtraData = NULL; } +static ALCboolean pa_start_context(ALCdevice *device, ALCcontext *context) +{ + return ALC_TRUE; + (void)device; + (void)context; +} + +static void pa_stop_context(ALCdevice *device, ALCcontext *context) +{ + (void)device; + (void)context; +} + static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName, ALCuint frequency, ALCenum format, ALCsizei SampleSize) { @@ -177,6 +190,8 @@ static ALCboolean pa_open_capture(ALCdevice *device, const ALCchar *deviceName, static const BackendFuncs pa_funcs = { pa_open_playback, pa_close_playback, + pa_start_context, + pa_stop_context, pa_open_capture, NULL, NULL, diff --git a/Alc/pulseaudio.c b/Alc/pulseaudio.c index 874b5ca4..fc853faf 100644 --- a/Alc/pulseaudio.c +++ b/Alc/pulseaudio.c @@ -239,7 +239,7 @@ static ALCboolean pulse_open(ALCdevice *device, ALCchar *device_name, ALCenum fo } else { - data->attr.tlength = data->frame_size * (device->UpdateSize&~3); + data->attr.tlength = data->frame_size * (device->BufferSize&~3); data->attr.fragsize = -1; data->stream_name = "Playback Stream"; } @@ -377,7 +377,7 @@ static ALCboolean pulse_open(ALCdevice *device, ALCchar *device_name, ALCenum fo ppa_threaded_mainloop_accept(data->loop); } - device->UpdateSize /= 4; + device->UpdateSize = device->BufferSize/4; ppa_threaded_mainloop_unlock(data->loop); return ALC_TRUE; @@ -446,6 +446,20 @@ static void pulse_close_playback(ALCdevice *device) //{{{ pulse_close(device); } //}}} +static ALCboolean pulse_start_context(ALCdevice *device, ALCcontext *context) //{{{ +{ + return ALC_TRUE; + (void)device; + (void)context; +} //}}} + +static void pulse_stop_context(ALCdevice *device, ALCcontext *context) //{{{ +{ + (void)device; + (void)context; +} //}}} + + static ALCboolean pulse_open_capture(ALCdevice *device, const ALCchar *device_name, ALCuint frequency, ALCenum format, ALCsizei samples) //{{{ { if(!pa_handle) @@ -503,6 +517,8 @@ static ALCuint pulse_available_samples(ALCdevice *device) //{{{ BackendFuncs pulse_funcs = { //{{{ pulse_open_playback, pulse_close_playback, + pulse_start_context, + pulse_stop_context, pulse_open_capture, pulse_close_capture, pulse_start_capture, @@ -67,7 +67,7 @@ static ALuint WaveProc(ALvoid *ptr) now = timeGetTime(); avail = (now-last) * pDevice->Frequency / 1000; - if(avail < pDevice->UpdateSize/4) + if(avail < pDevice->UpdateSize) { Sleep(1); continue; @@ -109,10 +109,7 @@ static ALuint WaveProc(ALvoid *ptr) static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceName) { wave_data *data; - ALuint channels; - ALuint bits; const char *fname; - int i; fname = GetConfigValue("wave", "file", ""); if(!fname[0]) @@ -137,6 +134,54 @@ static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceNam return ALC_FALSE; } + device->ExtraData = data; + return ALC_TRUE; +} + +static void wave_close_playback(ALCdevice *device) +{ + wave_data *data = (wave_data*)device->ExtraData; + ALuint dataLen; + long size; + + data->killNow = 1; + StopThread(data->thread); + + size = ftell(data->f); + if(size > 0) + { + dataLen = size - data->DataStart; + if(fseek(data->f, data->DataStart-4, SEEK_SET) == 0) + { + fputc(dataLen&0xff, data->f); // 'data' header len + fputc((dataLen>>8)&0xff, data->f); + fputc((dataLen>>16)&0xff, data->f); + fputc((dataLen>>24)&0xff, data->f); + } + if(fseek(data->f, 4, SEEK_SET) == 0) + { + size -= 8; + fputc(size&0xff, data->f); // 'WAVE' header len + fputc((size>>8)&0xff, data->f); + fputc((size>>16)&0xff, data->f); + fputc((size>>24)&0xff, data->f); + } + } + + fclose(data->f); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean wave_start_context(ALCdevice *device, ALCcontext *Context) +{ + wave_data *data = (wave_data*)device->ExtraData; + ALuint channels, bits, i; + (void)Context; + + fseek(data->f, 0, SEEK_SET); + clearerr(data->f); + bits = aluBytesFromFormat(device->Format) * 8; channels = aluChannelsFromFormat(device->Format); switch(bits) @@ -146,16 +191,12 @@ static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceNam if(channels == 0) { AL_PRINT("Unknown format?! %x\n", device->Format); - fclose(data->f); - free(data); return ALC_FALSE; } break; default: AL_PRINT("Unknown format?! %x\n", device->Format); - fclose(data->f); - free(data); return ALC_FALSE; } @@ -206,47 +247,48 @@ static ALCboolean wave_open_playback(ALCdevice *device, const ALCchar *deviceNam if(ferror(data->f)) { AL_PRINT("Error writing header: %s\n", strerror(errno)); - fclose(data->f); - free(data); return ALC_FALSE; } data->DataStart = ftell(data->f); - device->UpdateSize = max(device->UpdateSize, 2048); + device->UpdateSize = max(device->BufferSize/4, 2048); data->size = device->UpdateSize; data->buffer = malloc(data->size * channels * bits / 8); if(!data->buffer) { AL_PRINT("buffer malloc failed\n"); - fclose(data->f); - free(data); return ALC_FALSE; } - device->ExtraData = data; data->thread = StartThread(WaveProc, device); if(data->thread == NULL) { - device->ExtraData = NULL; - fclose(data->f); free(data->buffer); - free(data); + data->buffer = NULL; return ALC_FALSE; } return ALC_TRUE; } -static void wave_close_playback(ALCdevice *device) +static void wave_stop_context(ALCdevice *device, ALCcontext *Context) { wave_data *data = (wave_data*)device->ExtraData; ALuint dataLen; long size; + (void)Context; + + if(!data->thread) + return; data->killNow = 1; StopThread(data->thread); + data->thread = NULL; + + free(data->buffer); + data->buffer = NULL; size = ftell(data->f); if(size > 0) @@ -268,11 +310,6 @@ static void wave_close_playback(ALCdevice *device) fputc((size>>24)&0xff, data->f); } } - - fclose(data->f); - free(data->buffer); - free(data); - device->ExtraData = NULL; } @@ -318,6 +355,8 @@ static ALCuint wave_available_samples(ALCdevice *pDevice) BackendFuncs wave_funcs = { wave_open_playback, wave_close_playback, + wave_start_context, + wave_stop_context, wave_open_capture, wave_close_capture, wave_start_capture, diff --git a/Alc/winmm.c b/Alc/winmm.c index 0261e373..ea97b695 100644 --- a/Alc/winmm.c +++ b/Alc/winmm.c @@ -413,6 +413,8 @@ static ALCuint WinMMAvailableSamples(ALCdevice *pDevice) BackendFuncs WinMMFuncs = { WinMMOpenPlayback, WinMMClosePlayback, + NULL, + NULL, WinMMOpenCapture, WinMMCloseCapture, WinMMStartCapture, |