2009-08-11 19:57:20 +02:00
|
|
|
#include "../Headers/cAudioCapture.h"
|
2009-11-20 04:39:56 +01:00
|
|
|
#include "../Headers/cUtils.h"
|
|
|
|
#include "../Headers/cThread.h"
|
|
|
|
#include "../include/cAudioSleep.h"
|
|
|
|
|
2009-08-11 19:57:20 +02:00
|
|
|
#include <string.h>
|
2009-11-20 04:39:56 +01:00
|
|
|
#include <set>
|
2009-08-11 19:57:20 +02:00
|
|
|
|
|
|
|
namespace cAudio
|
|
|
|
{
|
2009-11-20 04:39:56 +01:00
|
|
|
static bool RunAudioCaptureThread(false);
|
|
|
|
|
|
|
|
//Note: OpenAL is threadsafe, so a mutex only needs to protect the class state
|
|
|
|
#ifdef CAUDIO_USE_INTERNAL_THREAD
|
|
|
|
static cAudioMutex AudioCaptureObjectsMutex;
|
|
|
|
static std::set<IAudioCapture*> AudioCaptureObjects;
|
|
|
|
|
|
|
|
CAUDIO_DECLARE_THREAD_FUNCTION(AudioCaptureUpdateThread)
|
2009-08-11 19:57:20 +02:00
|
|
|
{
|
2009-11-20 04:39:56 +01:00
|
|
|
while(RunAudioCaptureThread)
|
|
|
|
{
|
|
|
|
AudioCaptureObjectsMutex.lock();
|
|
|
|
std::set<IAudioCapture*>::iterator it;
|
|
|
|
for ( it=AudioCaptureObjects.begin() ; it != AudioCaptureObjects.end(); it++ )
|
|
|
|
{
|
|
|
|
(*it)->updateCaptureBuffer();
|
|
|
|
}
|
|
|
|
AudioCaptureObjectsMutex.unlock();
|
|
|
|
cAudioSleep(1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2009-08-11 19:57:20 +02:00
|
|
|
|
2009-11-20 04:39:56 +01:00
|
|
|
cAudioCapture::cAudioCapture() : Frequency(22050), Format(EAF_16BIT_MONO), InternalBufferSize(8192),
|
|
|
|
SampleSize(2), Supported(false), Ready(false), Capturing(false),
|
|
|
|
CaptureDevice(NULL)
|
|
|
|
{
|
|
|
|
checkCaptureExtension();
|
|
|
|
getAvailableDevices();
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
cAudioCapture::~cAudioCapture()
|
|
|
|
{
|
2009-11-20 04:39:56 +01:00
|
|
|
shutdown();
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cAudioCapture::checkCaptureExtension()
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
// Check for Capture Extension support
|
2009-11-20 04:39:56 +01:00
|
|
|
Supported = ( alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE") == AL_TRUE );
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-11 19:57:20 +02:00
|
|
|
return Supported;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cAudioCapture::initOpenALDevice()
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
if(Supported)
|
|
|
|
{
|
|
|
|
if(CaptureDevice)
|
|
|
|
shutdownOpenALDevice();
|
2009-11-20 04:39:56 +01:00
|
|
|
if(DeviceName.empty())
|
|
|
|
CaptureDevice = alcCaptureOpenDevice(NULL, Frequency, Format, InternalBufferSize / SampleSize);
|
|
|
|
else
|
|
|
|
CaptureDevice = alcCaptureOpenDevice(DeviceName.c_str(), Frequency, Format, InternalBufferSize / SampleSize);
|
2009-08-11 19:57:20 +02:00
|
|
|
if(CaptureDevice)
|
|
|
|
{
|
2009-11-20 04:39:56 +01:00
|
|
|
DeviceName = alcGetString(CaptureDevice, ALC_CAPTURE_DEVICE_SPECIFIER);
|
2009-08-11 19:57:20 +02:00
|
|
|
Ready = true;
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-11 19:57:20 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-11 19:57:20 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cAudioCapture::shutdownOpenALDevice()
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
if(Supported)
|
|
|
|
{
|
|
|
|
if(Capturing)
|
|
|
|
stopCapture();
|
|
|
|
|
|
|
|
if(CaptureDevice)
|
|
|
|
{
|
|
|
|
alcCaptureCloseDevice(CaptureDevice);
|
|
|
|
CaptureDevice = NULL;
|
|
|
|
Ready = false;
|
|
|
|
}
|
2009-11-20 04:39:56 +01:00
|
|
|
CaptureBuffer.clear();
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void cAudioCapture::shutdown()
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
shutdownOpenALDevice();
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
2009-11-20 04:39:56 +01:00
|
|
|
void cAudioCapture::getAvailableDevices()
|
|
|
|
{
|
|
|
|
// Get list of available Capture Devices
|
|
|
|
Mutex.lock();
|
|
|
|
if( alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT") == AL_TRUE )
|
|
|
|
{
|
|
|
|
const char* deviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
|
|
|
|
if (deviceList)
|
|
|
|
{
|
|
|
|
while(*deviceList)
|
|
|
|
{
|
|
|
|
std::string device(deviceList);
|
|
|
|
AvailableDevices.push_back(device);
|
|
|
|
deviceList += strlen(deviceList) + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the name of the 'default' capture device
|
|
|
|
DefaultDevice = alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
|
|
|
|
}
|
|
|
|
Mutex.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* cAudioCapture::getAvailableDeviceName(unsigned int index)
|
|
|
|
{
|
|
|
|
Mutex.lock();
|
|
|
|
if(!AvailableDevices.empty())
|
|
|
|
{
|
|
|
|
//Bounds check
|
|
|
|
if( index > (AvailableDevices.size()-1) ) index = (AvailableDevices.size()-1);
|
|
|
|
const char* deviceName = AvailableDevices[index].c_str();
|
|
|
|
Mutex.unlock();
|
|
|
|
return deviceName;
|
|
|
|
}
|
|
|
|
Mutex.unlock();
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int cAudioCapture::getAvailableDeviceCount()
|
|
|
|
{
|
|
|
|
Mutex.lock();
|
|
|
|
unsigned int size = AvailableDevices.size();
|
|
|
|
Mutex.unlock();
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* cAudioCapture::getDefaultDeviceName()
|
|
|
|
{
|
|
|
|
Mutex.lock();
|
|
|
|
const char* deviceName = DefaultDevice.empty() ? "" : DefaultDevice.c_str();
|
|
|
|
Mutex.unlock();
|
|
|
|
return deviceName;
|
|
|
|
}
|
|
|
|
|
2009-08-11 19:57:20 +02:00
|
|
|
void cAudioCapture::updateCaptureBuffer(bool force)
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
if(Capturing && CaptureDevice && Ready)
|
|
|
|
{
|
|
|
|
int AvailableSamples = 0;
|
|
|
|
alcGetIntegerv(CaptureDevice, ALC_CAPTURE_SAMPLES, 1, &AvailableSamples);
|
|
|
|
const unsigned int availbuffersize = AvailableSamples * SampleSize;
|
|
|
|
|
|
|
|
//If the samples in the OpenAL buffer are more than half of its max size, grab them
|
|
|
|
if(availbuffersize > InternalBufferSize / 2 || force)
|
|
|
|
{
|
|
|
|
//Fixes a bug with the capture being forced, but no data being available
|
|
|
|
if(availbuffersize > 0)
|
|
|
|
{
|
2009-11-20 04:39:56 +01:00
|
|
|
const unsigned int oldBufferSize = CaptureBuffer.size();
|
2009-08-11 19:57:20 +02:00
|
|
|
CaptureBuffer.resize(oldBufferSize + availbuffersize, 0);
|
|
|
|
alcCaptureSamples(CaptureDevice, &CaptureBuffer[oldBufferSize], AvailableSamples);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cAudioCapture::beginCapture()
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
if(!Capturing)
|
|
|
|
{
|
|
|
|
CaptureBuffer.clear();
|
|
|
|
if(CaptureDevice && Ready)
|
|
|
|
{
|
|
|
|
alcCaptureStart(CaptureDevice);
|
|
|
|
Capturing = true;
|
|
|
|
}
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-11 19:57:20 +02:00
|
|
|
return Capturing;
|
|
|
|
}
|
|
|
|
else
|
2009-08-29 13:24:31 +02:00
|
|
|
{
|
|
|
|
Mutex.unlock();
|
2009-08-11 19:57:20 +02:00
|
|
|
return false;
|
2009-08-29 13:24:31 +02:00
|
|
|
}
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void cAudioCapture::stopCapture()
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
if(CaptureDevice && Ready)
|
|
|
|
{
|
|
|
|
alcCaptureStop(CaptureDevice);
|
|
|
|
updateCaptureBuffer(true);
|
|
|
|
}
|
|
|
|
Capturing = false;
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
2009-08-26 03:43:45 +02:00
|
|
|
unsigned int cAudioCapture::getCapturedAudio(void* outputBuffer, unsigned int outputBufferSize)
|
2009-08-11 19:57:20 +02:00
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-11-20 04:39:56 +01:00
|
|
|
unsigned int internalBufferSize = CaptureBuffer.size();
|
|
|
|
if(outputBuffer && outputBufferSize > 0 && internalBufferSize > 0)
|
2009-08-11 19:57:20 +02:00
|
|
|
{
|
2009-11-20 04:39:56 +01:00
|
|
|
int sizeToCopy = (outputBufferSize >= internalBufferSize) ? internalBufferSize : outputBufferSize;
|
2009-08-11 19:57:20 +02:00
|
|
|
memcpy(outputBuffer, &CaptureBuffer[0], sizeToCopy);
|
|
|
|
CaptureBuffer.erase(CaptureBuffer.begin(), CaptureBuffer.begin()+sizeToCopy);
|
2009-08-26 03:43:45 +02:00
|
|
|
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-26 03:43:45 +02:00
|
|
|
return sizeToCopy;
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.unlock();
|
2009-08-26 03:43:45 +02:00
|
|
|
return 0;
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int cAudioCapture::getCurrentCapturedAudioSize()
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
|
|
|
unsigned int size = CaptureBuffer.size();
|
|
|
|
Mutex.unlock();
|
|
|
|
return size;
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cAudioCapture::setFrequency(unsigned int frequency)
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
Frequency = frequency;
|
|
|
|
shutdownOpenALDevice();
|
2009-08-29 13:24:31 +02:00
|
|
|
bool state = initOpenALDevice();
|
|
|
|
Mutex.unlock();
|
|
|
|
return state;
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cAudioCapture::setFormat(AudioFormats format)
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
Format = format;
|
|
|
|
if(Format == EAF_8BIT_MONO)
|
|
|
|
SampleSize = 1;
|
|
|
|
else if(Format == EAF_8BIT_STEREO)
|
|
|
|
SampleSize = 2;
|
|
|
|
else if(Format == EAF_16BIT_MONO)
|
|
|
|
SampleSize = 2;
|
|
|
|
else
|
|
|
|
SampleSize = 4;
|
|
|
|
|
|
|
|
shutdownOpenALDevice();
|
2009-08-29 13:24:31 +02:00
|
|
|
bool state = initOpenALDevice();
|
|
|
|
Mutex.unlock();
|
|
|
|
return state;
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool cAudioCapture::setInternalBufferSize(unsigned int internalBufferSize)
|
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-08-11 19:57:20 +02:00
|
|
|
InternalBufferSize = internalBufferSize;
|
|
|
|
|
|
|
|
shutdownOpenALDevice();
|
2009-08-29 13:24:31 +02:00
|
|
|
bool state = initOpenALDevice();
|
|
|
|
Mutex.unlock();
|
|
|
|
return state;
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
|
|
|
|
2009-11-20 04:39:56 +01:00
|
|
|
bool cAudioCapture::setDevice(const char* deviceName)
|
2009-08-11 19:57:20 +02:00
|
|
|
{
|
2009-08-29 13:24:31 +02:00
|
|
|
Mutex.lock();
|
2009-11-20 04:39:56 +01:00
|
|
|
DeviceName = safeCStr(deviceName);
|
|
|
|
|
|
|
|
shutdownOpenALDevice();
|
|
|
|
bool state = initOpenALDevice();
|
|
|
|
Mutex.unlock();
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool cAudioCapture::initialize(const char* deviceName, unsigned int frequency, AudioFormats format, unsigned int internalBufferSize)
|
|
|
|
{
|
|
|
|
Mutex.lock();
|
|
|
|
DeviceName = safeCStr(deviceName);
|
2009-08-11 19:57:20 +02:00
|
|
|
Frequency = frequency;
|
|
|
|
InternalBufferSize = internalBufferSize;
|
|
|
|
|
|
|
|
Format = format;
|
|
|
|
if(Format == EAF_8BIT_MONO)
|
|
|
|
SampleSize = 1;
|
|
|
|
else if(Format == EAF_8BIT_STEREO)
|
|
|
|
SampleSize = 2;
|
|
|
|
else if(Format == EAF_16BIT_MONO)
|
|
|
|
SampleSize = 2;
|
|
|
|
else
|
|
|
|
SampleSize = 4;
|
|
|
|
|
|
|
|
shutdownOpenALDevice();
|
2009-08-29 13:24:31 +02:00
|
|
|
bool state = initOpenALDevice();
|
|
|
|
Mutex.unlock();
|
|
|
|
return state;
|
2009-08-11 19:57:20 +02:00
|
|
|
}
|
2009-11-20 04:39:56 +01:00
|
|
|
|
|
|
|
CAUDIO_API IAudioCapture* createAudioCapture(bool initializeDefault)
|
|
|
|
{
|
|
|
|
cAudioCapture* capture = new cAudioCapture;
|
|
|
|
if(capture)
|
|
|
|
{
|
|
|
|
if(initializeDefault)
|
|
|
|
capture->initialize();
|
|
|
|
|
|
|
|
#ifdef CAUDIO_USE_INTERNAL_THREAD
|
|
|
|
AudioCaptureObjectsMutex.lock();
|
|
|
|
AudioCaptureObjects.insert(capture);
|
|
|
|
|
|
|
|
//First time launch of thread
|
|
|
|
if(!RunAudioCaptureThread && AudioCaptureObjects.size() > 0)
|
|
|
|
RunAudioCaptureThread = (cAudioThread::SpawnThread(AudioCaptureUpdateThread, NULL) == 0);
|
|
|
|
AudioCaptureObjectsMutex.unlock();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return capture;
|
|
|
|
}
|
|
|
|
|
|
|
|
CAUDIO_API void destroyAudioCapture(IAudioCapture* capture)
|
|
|
|
{
|
|
|
|
if(capture)
|
|
|
|
{
|
|
|
|
#ifdef CAUDIO_USE_INTERNAL_THREAD
|
|
|
|
AudioCaptureObjectsMutex.lock();
|
|
|
|
AudioCaptureObjects.erase(capture);
|
|
|
|
|
|
|
|
//Kill the thread if there are no objects to process anymore
|
|
|
|
if(RunAudioCaptureThread && AudioCaptureObjects.empty())
|
|
|
|
RunAudioCaptureThread = false;
|
|
|
|
AudioCaptureObjectsMutex.unlock();
|
|
|
|
#endif
|
|
|
|
delete capture;
|
|
|
|
capture = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CAUDIO_API bool isAudioCaptureThreadRunning()
|
|
|
|
{
|
|
|
|
return RunAudioCaptureThread;
|
|
|
|
}
|
2009-08-11 19:57:20 +02:00
|
|
|
};
|