text: /**
   * OpenAL cross platform audio library
   * Copyright (C) 1999-2007 by authors.
   * This library is free software; you can redistribute it and/or
   *  modify it under the terms of the GNU Library General Public
   *  License as published by the Free Software Foundation; either
   *  version 2 of the License, or (at your option) any later version.
   *
   * This library 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
   *  Library General Public License for more details.
   *
   * You should have received a copy of the GNU Library General Public
   *  License along with this library; if not, write to the
   *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   *  Boston, MA  02111-1307, USA.
   * Or go to http://www.gnu.org/copyleft/lgpl.html
   */
  
  #include "config.h"
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <memory.h>
  
  #include <windows.h>
  #include <mmsystem.h>
  
  #include "alMain.h"
  #include "alu.h"
  
  #ifndef WAVE_FORMAT_IEEE_FLOAT
  #define WAVE_FORMAT_IEEE_FLOAT  0x0003
  #endif
  
  
  typedef struct {
      // MMSYSTEM Device
      volatile ALboolean killNow;
      HANDLE  WaveThreadEvent;
      HANDLE  WaveThread;
      DWORD   WaveThreadID;
      volatile LONG WaveBuffersCommitted;
      WAVEHDR WaveBuffer[4];
  
      union {
          HWAVEIN  In;
          HWAVEOUT Out;
      } WaveHandle;
  
      WAVEFORMATEX Format;
  
      RingBuffer *Ring;
  } WinMMData;
  
  
  static ALCchar **PlaybackDeviceList;
  static ALuint  NumPlaybackDevices;
  static ALCchar **CaptureDeviceList;
  static ALuint  NumCaptureDevices;
  
  
  static void ProbePlaybackDevices(void)
  {
      ALuint i;
  
      for(i = 0;i < NumPlaybackDevices;i++)
          free(PlaybackDeviceList[i]);
  
      NumPlaybackDevices = waveOutGetNumDevs();
      PlaybackDeviceList = realloc(PlaybackDeviceList, sizeof(ALCchar*) * NumPlaybackDevices);
      for(i = 0;i < NumPlaybackDevices;i++)
      {
          WAVEOUTCAPS WaveCaps;
  
          PlaybackDeviceList[i] = NULL;
          if(waveOutGetDevCaps(i, &WaveCaps, sizeof(WaveCaps)) == MMSYSERR_NOERROR)
          {
              char name[1024];
              ALuint count, j;
  
              count = 0;
              do {
                  if(count == 0)
                      sprintf(name, "%s", WaveCaps.szPname);
                  else
                      sprintf(name, "%s #%d", WaveCaps.szPname, count+1);
                  count++;
  
                  for(j = 0;j < i;j++)
                  {
                      if(strcmp(name, PlaybackDeviceList[j]) == 0)
                          break;
                  }
              } while(j != i);
  
              PlaybackDeviceList[i] = strdup(name);
          }
      }
  }
  
  static void ProbeCaptureDevices(void)
  {
      ALuint i;
  
      for(i = 0;i < NumCaptureDevices;i++)
          free(CaptureDeviceList[i]);
  
      NumCaptureDevices = waveInGetNumDevs();
      CaptureDeviceList = realloc(CaptureDeviceList, sizeof(ALCchar*) * NumCaptureDevices);
      for(i = 0;i < NumCaptureDevices;i++)
      {
          WAVEINCAPS WaveInCaps;
  
          CaptureDeviceList[i] = NULL;
          if(waveInGetDevCaps(i, &WaveInCaps, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR)
          {
              char name[1024];
              ALuint count, j;
  
              count = 0;
              do {
                  if(count == 0)
                      sprintf(name, "%s", WaveInCaps.szPname);
                  else
                      sprintf(name, "%s #%d", WaveInCaps.szPname, count+1);
                  count++;
  
                  for(j = 0;j < i;j++)
                  {
                      if(strcmp(name, CaptureDeviceList[j]) == 0)
                          break;
                  }
              } while(j != i);
  
              CaptureDeviceList[i] = strdup(name);
          }
      }
  }
  
  
  /*
      WaveOutProc
  
      Posts a message to 'PlaybackThreadProc' everytime a WaveOut Buffer is completed and
      returns to the application (for more data)
  */
  static void CALLBACK WaveOutProc(HWAVEOUT device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
  {
      ALCdevice *Device = (ALCdevice*)instance;
      WinMMData *data = Device->ExtraData;
  
      (void)device;
      (void)param2;
  
      if(msg != WOM_DONE)
          return;
  
      InterlockedDecrement(&data->WaveBuffersCommitted);
      PostThreadMessage(data->WaveThreadID, msg, 0, param1);
  }
  
  /*
      PlaybackThreadProc
  
      Used by "MMSYSTEM" Device.  Called when a WaveOut buffer has used up its
      audio data.
  */
  static DWORD WINAPI PlaybackThreadProc(LPVOID param)
  {
      ALCdevice *Device = (ALCdevice*)param;
      WinMMData *data = Device->ExtraData;
      LPWAVEHDR WaveHdr;
      ALuint FrameSize;
      MSG msg;
  
      FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
  
      SetRTPriority();
  
      while(GetMessage(&msg, NULL, 0, 0))
      {
          if(msg.message != WOM_DONE)
              continue;
  
          if(data->killNow)
          {
              if(data->WaveBuffersCommitted == 0)
                  break;
              continue;
          }
  
          WaveHdr = ((LPWAVEHDR)msg.lParam);
          aluMixData(Device, WaveHdr->lpData, WaveHdr->dwBufferLength/FrameSize);
  
          // Send buffer back to play more data
          waveOutWrite(data->WaveHandle.Out, WaveHdr, sizeof(WAVEHDR));
          InterlockedIncrement(&data->WaveBuffersCommitted);
      }
  
      // Signal Wave Thread completed event
      if(data->WaveThreadEvent)
          SetEvent(data->WaveThreadEvent);
  
      ExitThread(0);
      return 0;
  }
  
  /*
      WaveInProc
  
      Posts a message to 'CaptureThreadProc' everytime a WaveIn Buffer is completed and
      returns to the application (with more data)
  */
  static void CALLBACK WaveInProc(HWAVEIN device, UINT msg, DWORD_PTR instance, DWORD_PTR param1, DWORD_PTR param2)
  {
      ALCdevice *Device = (ALCdevice*)instance;
      WinMMData *data = Device->ExtraData;
  
      (void)device;
      (void)param2;
  
      if(msg != WIM_DATA)
          return;
  
      InterlockedDecrement(&data->WaveBuffersCommitted);
      PostThreadMessage(data->WaveThreadID, msg, 0, param1);
  }
  
  /*
      CaptureThreadProc
  
      Used by "MMSYSTEM" Device.  Called when a WaveIn buffer had been filled with new
      audio data.
  */
  static DWORD WINAPI CaptureThreadProc(LPVOID param)
  {
      ALCdevice *Device = (ALCdevice*)param;
      WinMMData *data = Device->ExtraData;
      LPWAVEHDR WaveHdr;
      ALuint FrameSize;
      MSG msg;
  
      FrameSize = FrameSizeFromDevFmt(Device->FmtChans, Device->FmtType);
  
      while(GetMessage(&msg, NULL, 0, 0))
      {
          if(msg.message != WIM_DATA)
              continue;
          /* Don't wait for other buffers to finish before quitting. We're
           * closing so we don't need them. */
          if(data->killNow)
              break;
  
          WaveHdr = ((LPWAVEHDR)msg.lParam);
          WriteRingBuffer(data->Ring, (ALubyte*)WaveHdr->lpData, WaveHdr->dwBytesRecorded/FrameSize);
  
          // Send buffer back to capture more data
          waveInAddBuffer(data->WaveHandle.In, WaveHdr, sizeof(WAVEHDR));
          InterlockedIncrement(&data->WaveBuffersCommitted);
      }
  
      // Signal Wave Thread completed event
      if(data->WaveThreadEvent)
          SetEvent(data->WaveThreadEvent);
  
      ExitThread(0);
      return 0;
  }
  
  
  static ALCenum WinMMOpenPlayback(ALCdevice *Device, const ALCchar *deviceName)
  {
      WinMMData *data = NULL;
      UINT DeviceID = 0;
      MMRESULT res;
      ALuint i = 0;
  
      if(!PlaybackDeviceList)
          ProbePlaybackDevices();
  
      // Find the Device ID matching the deviceName if valid
      for(i = 0;i < NumPlaybackDevices;i++)
      {
          if(PlaybackDeviceList[i] &&
             (!deviceName || strcmp(deviceName, PlaybackDeviceList[i]) == 0))
          {
              DeviceID = i;
              break;
          }
      }
      if(i == NumPlaybackDevices)
          return ALC_INVALID_VALUE;
  
      data = calloc(1, sizeof(*data));
      if(!data)
          return ALC_OUT_OF_MEMORY;
      Device->ExtraData = data;
  
  retry_open:
      memset(&data->Format, 0, sizeof(WAVEFORMATEX));
      if(Device->FmtType == DevFmtFloat)
      {
          data->Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
          data->Format.wBitsPerSample = 32;
      }
      else
      {
          data->Format.wFormatTag = WAVE_FORMAT_PCM;
          if(Device->FmtType == DevFmtUByte || Device->FmtType == DevFmtByte)
              data->Format.wBitsPerSample = 8;
          else
              data->Format.wBitsPerSample = 16;
      }
      data->Format.nChannels = ((Device->FmtChans == DevFmtMono) ? 1 : 2);
      data->Format.nBlockAlign = data->Format.wBitsPerSample *
                                 data->Format.nChannels / 8;
      data->Format.nSamplesPerSec = Device->Frequency;
      data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
                                     data->Format.nBlockAlign;
      data->Format.cbSize = 0;
  
      if((res=waveOutOpen(&data->WaveHandle.Out, DeviceID, &data->Format, (DWORD_PTR)&WaveOutProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
      {
          if(Device->FmtType == DevFmtFloat)
          {
              Device->FmtType = DevFmtShort;
              goto retry_open;
          }
          ERR("waveOutOpen failed: %u\n", res);
          goto failure;
      }
  
      data->WaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
      if(data->WaveThreadEvent == NULL)
      {
          ERR("CreateEvent failed: %lu\n", GetLastError());
          goto failure;
      }
  
      Device->DeviceName = strdup(PlaybackDeviceList[DeviceID]);
      return ALC_NO_ERROR;
  
  failure:
      if(data->WaveThreadEvent)
          CloseHandle(data->WaveThreadEvent);
  
      if(data->WaveHandle.Out)
          waveOutClose(data->WaveHandle.Out);
  
      free(data);
      Device->ExtraData = NULL;
      return ALC_INVALID_VALUE;
  }
  
  static void WinMMClosePlayback(ALCdevice *device)
  {
      WinMMData *data = (WinMMData*)device->ExtraData;
  
      // Close the Wave device
      CloseHandle(data->WaveThreadEvent);
      data->WaveThreadEvent = 0;
  
      waveOutClose(data->WaveHandle.Out);
      data->WaveHandle.Out = 0;
  
      free(data);
      device->ExtraData = NULL;
  }
  
  static ALCboolean WinMMResetPlayback(ALCdevice *device)
  {
      WinMMData *data = (WinMMData*)device->ExtraData;
  
      device->UpdateSize = (ALuint)((ALuint64)device->UpdateSize *
                                    data->Format.nSamplesPerSec /
                                    device->Frequency);
      device->UpdateSize = (device->UpdateSize*device->NumUpdates + 3) / 4;
      device->NumUpdates = 4;
      device->Frequency = data->Format.nSamplesPerSec;
  
      if(data->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
      {
          if(data->Format.wBitsPerSample == 32)
              device->FmtType = DevFmtFloat;
          else
          {
              ERR("Unhandled IEEE float sample depth: %d\n", data->Format.wBitsPerSample);
              return ALC_FALSE;
          }
      }
      else if(data->Format.wFormatTag == WAVE_FORMAT_PCM)
      {
          if(data->Format.wBitsPerSample == 16)
              device->FmtType = DevFmtShort;
          else if(data->Format.wBitsPerSample == 8)
              device->FmtType = DevFmtUByte;
          else
          {
              ERR("Unhandled PCM sample depth: %d\n", data->Format.wBitsPerSample);
              return ALC_FALSE;
          }
      }
      else
      {
          ERR("Unhandled format tag: 0x%04x\n", data->Format.wFormatTag);
          return ALC_FALSE;
      }
  
      if(data->Format.nChannels == 2)
          device->FmtChans = DevFmtStereo;
      else if(data->Format.nChannels == 1)
          device->FmtChans = DevFmtMono;
      else
      {
          ERR("Unhandled channel count: %d\n", data->Format.nChannels);
          return ALC_FALSE;
      }
      SetDefaultWFXChannelOrder(device);
  
      return ALC_TRUE;
  }
  
  static ALCboolean WinMMStartPlayback(ALCdevice *device)
  {
      WinMMData *data = (WinMMData*)device->ExtraData;
      ALbyte *BufferData;
      ALint BufferSize;
      ALuint i;
  
      data->WaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)PlaybackThreadProc, (LPVOID)device, 0, &data->WaveThreadID);
      if(data->WaveThread == NULL)
          return ALC_FALSE;
  
      data->WaveBuffersCommitted = 0;
  
      // Create 4 Buffers
      BufferSize  = device->UpdateSize*device->NumUpdates / 4;
      BufferSize *= FrameSizeFromDevFmt(device->FmtChans, device->FmtType);
  
      BufferData = calloc(4, BufferSize);
      for(i = 0;i < 4;i++)
      {
          memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
          data->WaveBuffer[i].dwBufferLength = BufferSize;
          data->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
                                        (data->WaveBuffer[i-1].lpData +
                                         data->WaveBuffer[i-1].dwBufferLength));
          waveOutPrepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
          waveOutWrite(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
          InterlockedIncrement(&data->WaveBuffersCommitted);
      }
  
      return ALC_TRUE;
  }
  
  static void WinMMStopPlayback(ALCdevice *device)
  {
      WinMMData *data = (WinMMData*)device->ExtraData;
      void *buffer = NULL;
      int i;
  
      if(data->WaveThread == NULL)
          return;
  
      // Set flag to stop processing headers
      data->killNow = AL_TRUE;
  
      // Wait for signal that Wave Thread has been destroyed
      WaitForSingleObjectEx(data->WaveThreadEvent, 5000, FALSE);
  
      CloseHandle(data->WaveThread);
      data->WaveThread = 0;
  
      data->killNow = AL_FALSE;
  
      // Release the wave buffers
      for(i = 0;i < 4;i++)
      {
          waveOutUnprepareHeader(data->WaveHandle.Out, &data->WaveBuffer[i], sizeof(WAVEHDR));
          if(i == 0) buffer = data->WaveBuffer[i].lpData;
          data->WaveBuffer[i].lpData = NULL;
      }
      free(buffer);
  }
  
  
  static ALCenum WinMMOpenCapture(ALCdevice *Device, const ALCchar *deviceName)
  {
      ALbyte *BufferData = NULL;
      DWORD CapturedDataSize;
      WinMMData *data = NULL;
      UINT DeviceID = 0;
      ALint BufferSize;
      MMRESULT res;
      ALuint i;
  
      if(!CaptureDeviceList)
          ProbeCaptureDevices();
  
      // Find the Device ID matching the deviceName if valid
      for(i = 0;i < NumCaptureDevices;i++)
      {
          if(CaptureDeviceList[i] &&
             (!deviceName || strcmp(deviceName, CaptureDeviceList[i]) == 0))
          {
              DeviceID = i;
              break;
          }
      }
      if(i == NumCaptureDevices)
          return ALC_INVALID_VALUE;
  
      switch(Device->FmtChans)
      {
          case DevFmtMono:
          case DevFmtStereo:
              break;
  
          case DevFmtQuad:
          case DevFmtX51:
          case DevFmtX51Side:
          case DevFmtX61:
          case DevFmtX71:
              return ALC_INVALID_ENUM;
      }
  
      switch(Device->FmtType)
      {
          case DevFmtUByte:
          case DevFmtShort:
          case DevFmtInt:
          case DevFmtFloat:
              break;
  
          case DevFmtByte:
          case DevFmtUShort:
          case DevFmtUInt:
              return ALC_INVALID_ENUM;
      }
  
      data = calloc(1, sizeof(*data));
      if(!data)
          return ALC_OUT_OF_MEMORY;
      Device->ExtraData = data;
  
      memset(&data->Format, 0, sizeof(WAVEFORMATEX));
      data->Format.wFormatTag = ((Device->FmtType == DevFmtFloat) ?
                                 WAVE_FORMAT_IEEE_FLOAT : WAVE_FORMAT_PCM);
      data->Format.nChannels = ChannelsFromDevFmt(Device->FmtChans);
      data->Format.wBitsPerSample = BytesFromDevFmt(Device->FmtType) * 8;
      data->Format.nBlockAlign = data->Format.wBitsPerSample *
                                 data->Format.nChannels / 8;
      data->Format.nSamplesPerSec = Device->Frequency;
      data->Format.nAvgBytesPerSec = data->Format.nSamplesPerSec *
                                     data->Format.nBlockAlign;
      data->Format.cbSize = 0;
  
      if((res=waveInOpen(&data->WaveHandle.In, DeviceID, &data->Format, (DWORD_PTR)&WaveInProc, (DWORD_PTR)Device, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
      {
          ERR("waveInOpen failed: %u\n", res);
          goto failure;
      }
  
      data->WaveThreadEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
      if(data->WaveThreadEvent == NULL)
      {
          ERR("CreateEvent failed: %lu\n", GetLastError());
          goto failure;
      }
  
      // Allocate circular memory buffer for the captured audio
      CapturedDataSize = Device->UpdateSize*Device->NumUpdates;
  
      // Make sure circular buffer is at least 100ms in size
      if(CapturedDataSize < (data->Format.nSamplesPerSec / 10))
          CapturedDataSize = data->Format.nSamplesPerSec / 10;
  
      data->Ring = CreateRingBuffer(data->Format.nBlockAlign, CapturedDataSize);
      if(!data->Ring)
          goto failure;
  
      data->WaveBuffersCommitted = 0;
  
      // Create 4 Buffers of 50ms each
      BufferSize = data->Format.nAvgBytesPerSec / 20;
      BufferSize -= (BufferSize % data->Format.nBlockAlign);
  
      BufferData = calloc(4, BufferSize);
      if(!BufferData)
          goto failure;
  
      for(i = 0;i < 4;i++)
      {
          memset(&data->WaveBuffer[i], 0, sizeof(WAVEHDR));
          data->WaveBuffer[i].dwBufferLength = BufferSize;
          data->WaveBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
                                        (data->WaveBuffer[i-1].lpData +
                                         data->WaveBuffer[i-1].dwBufferLength));
          data->WaveBuffer[i].dwFlags = 0;
          data->WaveBuffer[i].dwLoops = 0;
          waveInPrepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
          waveInAddBuffer(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
          InterlockedIncrement(&data->WaveBuffersCommitted);
      }
  
      data->WaveThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)Device, 0, &data->WaveThreadID);
      if (data->WaveThread == NULL)
          goto failure;
  
      Device->DeviceName = strdup(CaptureDeviceList[DeviceID]);
      return ALC_NO_ERROR;
  
  failure:
      if(data->WaveThread)
          CloseHandle(data->WaveThread);
  
      if(BufferData)
      {
          for(i = 0;i < 4;i++)
              waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
          free(BufferData);
      }
  
      if(data->Ring)
          DestroyRingBuffer(data->Ring);
  
      if(data->WaveThreadEvent)
          CloseHandle(data->WaveThreadEvent);
  
      if(data->WaveHandle.In)
          waveInClose(data->WaveHandle.In);
  
      free(data);
      Device->ExtraData = NULL;
      return ALC_INVALID_VALUE;
  }
  
  static void WinMMCloseCapture(ALCdevice *Device)
  {
      WinMMData *data = (WinMMData*)Device->ExtraData;
      void *buffer = NULL;
      int i;
  
      /* Tell the processing thread to quit and wait for it to do so. */
      data->killNow = AL_TRUE;
      PostThreadMessage(data->WaveThreadID, WM_QUIT, 0, 0);
  
      WaitForSingleObjectEx(data->WaveThreadEvent, 5000, FALSE);
  
      /* Make sure capture is stopped and all pending buffers are flushed. */
      waveInReset(data->WaveHandle.In);
  
      CloseHandle(data->WaveThread);
      data->WaveThread = 0;
  
      // Release the wave buffers
      for(i = 0;i < 4;i++)
      {
          waveInUnprepareHeader(data->WaveHandle.In, &data->WaveBuffer[i], sizeof(WAVEHDR));
          if(i == 0) buffer = data->WaveBuffer[i].lpData;
          data->WaveBuffer[i].lpData = NULL;
      }
      free(buffer);
  
      DestroyRingBuffer(data->Ring);
      data->Ring = NULL;
  
      // Close the Wave device
      CloseHandle(data->WaveThreadEvent);
      data->WaveThreadEvent = 0;
  
      waveInClose(data->WaveHandle.In);
      data->WaveHandle.In = 0;
  
      free(data);
      Device->ExtraData = NULL;
  }
  
  static void WinMMStartCapture(ALCdevice *Device)
  {
      WinMMData *data = (WinMMData*)Device->ExtraData;
      waveInStart(data->WaveHandle.In);
  }
  
  static void WinMMStopCapture(ALCdevice *Device)
  {
      WinMMData *data = (WinMMData*)Device->ExtraData;
      waveInStop(data->WaveHandle.In);
  }
  
  static ALCenum WinMMCaptureSamples(ALCdevice *Device, ALCvoid *Buffer, ALCuint Samples)
  {
      WinMMData *data = (WinMMData*)Device->ExtraData;
      ReadRingBuffer(data->Ring, Buffer, Samples);
      return ALC_NO_ERROR;
  }
  
  static ALCuint WinMMAvailableSamples(ALCdevice *Device)
  {
      WinMMData *data = (WinMMData*)Device->ExtraData;
      return RingBufferSize(data->Ring);
  }
  
  
  static const BackendFuncs WinMMFuncs = {
      WinMMOpenPlayback,
      WinMMClosePlayback,
      WinMMResetPlayback,
      WinMMStartPlayback,
      WinMMStopPlayback,
      WinMMOpenCapture,
      WinMMCloseCapture,
      WinMMStartCapture,
      WinMMStopCapture,
      WinMMCaptureSamples,
      WinMMAvailableSamples,
      ALCdevice_LockDefault,
      ALCdevice_UnlockDefault,
      ALCdevice_GetLatencyDefault
  };
  
  ALCboolean alcWinMMInit(BackendFuncs *FuncList)
  {
      *FuncList = WinMMFuncs;
      return ALC_TRUE;
  }
  
  void alcWinMMDeinit()
  {
      ALuint i;
  
      for(i = 0;i < NumPlaybackDevices;i++)
          free(PlaybackDeviceList[i]);
      free(PlaybackDeviceList);
      PlaybackDeviceList = NULL;
  
      NumPlaybackDevices = 0;
  
  
      for(i = 0;i < NumCaptureDevices;i++)
          free(CaptureDeviceList[i]);
      free(CaptureDeviceList);
      CaptureDeviceList = NULL;
  
      NumCaptureDevices = 0;
  }
  
  void alcWinMMProbe(enum DevProbe type)
  {
      ALuint i;
  
      switch(type)
      {
          case ALL_DEVICE_PROBE:
              ProbePlaybackDevices();
              for(i = 0;i < NumPlaybackDevices;i++)
              {
                  if(PlaybackDeviceList[i])
                      AppendAllDevicesList(PlaybackDeviceList[i]);
              }
              break;
  
          case CAPTURE_DEVICE_PROBE:
              ProbeCaptureDevices();
              for(i = 0;i < NumCaptureDevices;i++)
              {
                  if(CaptureDeviceList[i])
                      AppendCaptureDeviceList(CaptureDeviceList[i]);
              }
              break;
      }
  }
  