AuroraOpenALSoft/Alc/winmm.c
2010-08-01 16:20:28 -07:00

455 lines
12 KiB
C

/**
* 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"
#define _WIN32_WINNT 0x0500
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include <windows.h>
#include <mmsystem.h>
#include "alMain.h"
#include "AL/al.h"
#include "AL/alc.h"
typedef struct {
// MMSYSTEM Capture Device
ALboolean bWaveInShutdown;
HANDLE hWaveInHdrEvent;
HANDLE hWaveInThreadEvent;
HANDLE hWaveInThread;
DWORD ulWaveInThreadID;
ALint lWaveInBuffersCommitted;
HWAVEIN hWaveInHandle;
WAVEHDR WaveInBuffer[4];
RingBuffer *pRing;
} WinMMData;
static ALCchar **CaptureDeviceList;
static ALuint NumCaptureDevices;
static void ProbeDevices(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)
snprintf(name, sizeof(name), "%s via WaveIn", WaveInCaps.szPname);
else
snprintf(name, sizeof(name), "%s #%d via WaveIn", 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);
}
}
}
/*
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 hDevice,UINT uMsg,DWORD_PTR dwInstance,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
{
ALCdevice *pDevice = (ALCdevice*)dwInstance;
WinMMData *pData = pDevice->ExtraData;
(void)hDevice;
(void)dwParam2;
if(uMsg != WIM_DATA)
return;
// Decrement number of buffers in use
pData->lWaveInBuffersCommitted--;
if(pData->bWaveInShutdown == AL_FALSE)
{
// Notify Wave Processor Thread that a Wave Header has returned
PostThreadMessage(pData->ulWaveInThreadID,uMsg,0,dwParam1);
}
else
{
if(pData->lWaveInBuffersCommitted == 0)
{
// Signal Wave Buffers Returned event
if(pData->hWaveInHdrEvent)
SetEvent(pData->hWaveInHdrEvent);
// Post 'Quit' Message to WaveIn Processor Thread
PostThreadMessage(pData->ulWaveInThreadID,WM_QUIT,0,0);
}
}
}
/*
CaptureThreadProc
Used by "MMSYSTEM" Device. Called when a WaveIn buffer had been filled with new
audio data.
*/
static DWORD WINAPI CaptureThreadProc(LPVOID lpParameter)
{
ALCdevice *pDevice = (ALCdevice*)lpParameter;
WinMMData *pData = pDevice->ExtraData;
LPWAVEHDR pWaveHdr;
ALuint FrameSize;
MSG msg;
FrameSize = aluFrameSizeFromFormat(pDevice->Format);
while(GetMessage(&msg, NULL, 0, 0))
{
if(msg.message != WIM_DATA || pData->bWaveInShutdown)
continue;
pWaveHdr = ((LPWAVEHDR)msg.lParam);
WriteRingBuffer(pData->pRing, (ALubyte*)pWaveHdr->lpData,
pWaveHdr->dwBytesRecorded/FrameSize);
// Send buffer back to capture more data
waveInAddBuffer(pData->hWaveInHandle,pWaveHdr,sizeof(WAVEHDR));
pData->lWaveInBuffersCommitted++;
}
// Signal Wave Thread completed event
if(pData->hWaveInThreadEvent)
SetEvent(pData->hWaveInThreadEvent);
ExitThread(0);
return 0;
}
static ALCboolean WinMMOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
{
(void)device;
(void)deviceName;
return ALC_FALSE;
}
static void WinMMClosePlayback(ALCdevice *device)
{
(void)device;
}
static ALCboolean WinMMOpenCapture(ALCdevice *pDevice, const ALCchar *deviceName)
{
WAVEFORMATEX wfexCaptureFormat;
DWORD ulCapturedDataSize;
WinMMData *pData = NULL;
ALint lDeviceID = 0;
ALbyte *BufferData;
ALint lBufferSize;
MMRESULT res;
ALuint i;
if(!CaptureDeviceList)
ProbeDevices();
// Find the Device ID matching the deviceName if valid
if(deviceName)
{
for(i = 0;i < NumCaptureDevices;i++)
{
if(CaptureDeviceList[i] &&
strcmp(deviceName, CaptureDeviceList[i]) == 0)
{
lDeviceID = i;
break;
}
}
}
else
{
for(i = 0;i < NumCaptureDevices;i++)
{
if(CaptureDeviceList[i])
{
lDeviceID = i;
break;
}
}
}
if(i == NumCaptureDevices)
return ALC_FALSE;
pData = calloc(1, sizeof(*pData));
if(!pData)
{
alcSetError(pDevice, ALC_OUT_OF_MEMORY);
return ALC_FALSE;
}
memset(&wfexCaptureFormat, 0, sizeof(WAVEFORMATEX));
wfexCaptureFormat.wFormatTag = WAVE_FORMAT_PCM;
wfexCaptureFormat.nChannels = aluChannelsFromFormat(pDevice->Format);
wfexCaptureFormat.wBitsPerSample = aluBytesFromFormat(pDevice->Format) * 8;
wfexCaptureFormat.nBlockAlign = wfexCaptureFormat.wBitsPerSample *
wfexCaptureFormat.nChannels / 8;
wfexCaptureFormat.nSamplesPerSec = pDevice->Frequency;
wfexCaptureFormat.nAvgBytesPerSec = wfexCaptureFormat.nSamplesPerSec *
wfexCaptureFormat.nBlockAlign;
wfexCaptureFormat.cbSize = 0;
if((res=waveInOpen(&pData->hWaveInHandle, lDeviceID, &wfexCaptureFormat, (DWORD_PTR)&WaveInProc, (DWORD_PTR)pDevice, CALLBACK_FUNCTION)) != MMSYSERR_NOERROR)
{
AL_PRINT("waveInOpen failed: %u\n", res);
goto failure;
}
pData->hWaveInHdrEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInAllHeadersReturned");
pData->hWaveInThreadEvent = CreateEvent(NULL, AL_TRUE, AL_FALSE, "WaveInThreadDestroyed");
if(pData->hWaveInHdrEvent == NULL || pData->hWaveInThreadEvent == NULL)
{
AL_PRINT("CreateEvent failed: %lu\n", GetLastError());
goto failure;
}
// Allocate circular memory buffer for the captured audio
ulCapturedDataSize = pDevice->UpdateSize*pDevice->NumUpdates;
// Make sure circular buffer is at least 100ms in size
if(ulCapturedDataSize < (wfexCaptureFormat.nSamplesPerSec / 10))
ulCapturedDataSize = wfexCaptureFormat.nSamplesPerSec / 10;
pData->pRing = CreateRingBuffer(wfexCaptureFormat.nBlockAlign, ulCapturedDataSize);
if(!pData->pRing)
goto failure;
pData->lWaveInBuffersCommitted=0;
// Create 4 Buffers of 50ms each
lBufferSize = wfexCaptureFormat.nAvgBytesPerSec / 20;
lBufferSize -= (lBufferSize % wfexCaptureFormat.nBlockAlign);
BufferData = calloc(4, lBufferSize);
if(!BufferData)
goto failure;
for(i = 0;i < 4;i++)
{
memset(&pData->WaveInBuffer[i], 0, sizeof(WAVEHDR));
pData->WaveInBuffer[i].dwBufferLength = lBufferSize;
pData->WaveInBuffer[i].lpData = ((i==0) ? (LPSTR)BufferData :
(pData->WaveInBuffer[i-1].lpData +
pData->WaveInBuffer[i-1].dwBufferLength));
pData->WaveInBuffer[i].dwFlags = 0;
pData->WaveInBuffer[i].dwLoops = 0;
waveInPrepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
waveInAddBuffer(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
pData->lWaveInBuffersCommitted++;
}
pDevice->ExtraData = pData;
pData->hWaveInThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CaptureThreadProc, (LPVOID)pDevice, 0, &pData->ulWaveInThreadID);
if (pData->hWaveInThread == NULL)
goto failure;
pDevice->szDeviceName = strdup(CaptureDeviceList[lDeviceID]);
return ALC_TRUE;
failure:
if(pData->hWaveInThread)
CloseHandle(pData->hWaveInThread);
for(i = 0;i < 4;i++)
{
if(pData->WaveInBuffer[i].lpData)
{
waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
if(i == 0)
free(pData->WaveInBuffer[i].lpData);
}
}
if(pData->pRing)
DestroyRingBuffer(pData->pRing);
if(pData->hWaveInThreadEvent)
CloseHandle(pData->hWaveInThreadEvent);
if(pData->hWaveInHdrEvent)
CloseHandle(pData->hWaveInHdrEvent);
if(pData->hWaveInHandle)
waveInClose(pData->hWaveInHandle);
free(pData);
pDevice->ExtraData = NULL;
return ALC_FALSE;
}
static void WinMMCloseCapture(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
int i;
// Call waveOutReset to shutdown wave device
pData->bWaveInShutdown = AL_TRUE;
waveInReset(pData->hWaveInHandle);
// Wait for signal that all Wave Buffers have returned
WaitForSingleObjectEx(pData->hWaveInHdrEvent, 5000, FALSE);
// Wait for signal that Wave Thread has been destroyed
WaitForSingleObjectEx(pData->hWaveInThreadEvent, 5000, FALSE);
CloseHandle(pData->hWaveInThread);
pData->hWaveInThread = 0;
// Release the wave buffers
for(i = 0;i < 4;i++)
{
waveInUnprepareHeader(pData->hWaveInHandle, &pData->WaveInBuffer[i], sizeof(WAVEHDR));
if(i == 0)
free(pData->WaveInBuffer[i].lpData);
pData->WaveInBuffer[i].lpData = NULL;
}
DestroyRingBuffer(pData->pRing);
pData->pRing = NULL;
// Close the Wave device
CloseHandle(pData->hWaveInThreadEvent);
pData->hWaveInThreadEvent = 0;
CloseHandle(pData->hWaveInHdrEvent);
pData->hWaveInHdrEvent = 0;
waveInClose(pData->hWaveInHandle);
pData->hWaveInHandle = 0;
free(pData);
pDevice->ExtraData = NULL;
}
static void WinMMStartCapture(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
waveInStart(pData->hWaveInHandle);
}
static void WinMMStopCapture(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
waveInStop(pData->hWaveInHandle);
}
static ALCuint WinMMAvailableSamples(ALCdevice *pDevice)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
return RingBufferSize(pData->pRing);
}
static void WinMMCaptureSamples(ALCdevice *pDevice, ALCvoid *pBuffer, ALCuint lSamples)
{
WinMMData *pData = (WinMMData*)pDevice->ExtraData;
if(WinMMAvailableSamples(pDevice) >= lSamples)
ReadRingBuffer(pData->pRing, pBuffer, lSamples);
else
alcSetError(pDevice, ALC_INVALID_VALUE);
}
static ALuint64 WinMMGetTime(ALCdevice *Device)
{
return Device->SamplesPlayed * 1000000000 / Device->Frequency;
}
static BackendFuncs WinMMFuncs = {
WinMMOpenPlayback,
WinMMClosePlayback,
NULL,
NULL,
WinMMOpenCapture,
WinMMCloseCapture,
WinMMStartCapture,
WinMMStopCapture,
WinMMCaptureSamples,
WinMMAvailableSamples,
WinMMGetTime
};
void alcWinMMInit(BackendFuncs *FuncList)
{
*FuncList = WinMMFuncs;
}
void alcWinMMDeinit()
{
ALuint lLoop;
for(lLoop = 0; lLoop < NumCaptureDevices; lLoop++)
free(CaptureDeviceList[lLoop]);
free(CaptureDeviceList);
CaptureDeviceList = NULL;
NumCaptureDevices = 0;
}
void alcWinMMProbe(int type)
{
ALuint i;
if(type != CAPTURE_DEVICE_PROBE)
return;
ProbeDevices();
for(i = 0;i < NumCaptureDevices;i++)
{
if(CaptureDeviceList[i])
AppendCaptureDeviceList(CaptureDeviceList[i]);
}
}