caudio/Source/cAudioCapture.cpp
Joshua Jones 30e5b4a99e Added support for all OpenAL Audio Effects. Access can be gotten through the main audio manager using the AudioEffects interface. IEffect provides access to effect parameters and allows for binding to cAudio sources. IFilter provides access to filters that can be attached to sources and effects.
Added conversion functions from our audio format enum to OpenAL's in preparation for supporting more formats.
Reorganized cAudio defines, made it easier to access some compile time parameters.
Fixed a bug where a source could not be played again after it reached the end of its audio stream.
Added better checking for sources.  IsValid() should provide a more useful indication of whether a source is usable.
Added error checking and logging to cAudioCapture.
Prevented initializing of cAudioManager until shutdown() has been called.
Added a tutorial for Audio Effects.
Added a footsteps sound for showing off Audio Effects.
Moved the default compile location for the main library to lib/.  The msvc project will copy the cAudio.dll to bin/win32-visual/
2010-01-11 00:39:08 +00:00

373 lines
9.6 KiB
C++

#include "../Headers/cAudioCapture.h"
#include "../Headers/cUtils.h"
#include "../Headers/cThread.h"
#include "../include/cAudioSleep.h"
#include "../Headers/cLogger.h"
#include <string.h>
#include <set>
namespace cAudio
{
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)
{
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
cAudioCapture::cAudioCapture() : Frequency(22050), Format(EAF_16BIT_MONO), InternalBufferSize(8192),
SampleSize(2), Supported(false), Ready(false), Capturing(false),
CaptureDevice(NULL)
{
checkCaptureExtension();
getAvailableDevices();
}
cAudioCapture::~cAudioCapture()
{
shutdown();
}
bool cAudioCapture::checkCaptureExtension()
{
cAudioMutexBasicLock lock(Mutex);
// Check for Capture Extension support
Supported = ( alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE") == AL_TRUE );
return Supported;
}
bool cAudioCapture::initOpenALDevice()
{
cAudioMutexBasicLock lock(Mutex);
if(Supported)
{
if(CaptureDevice)
shutdownOpenALDevice();
if(DeviceName.empty())
CaptureDevice = alcCaptureOpenDevice(NULL, Frequency, convertAudioFormatEnum(Format), InternalBufferSize / SampleSize);
else
CaptureDevice = alcCaptureOpenDevice(DeviceName.c_str(), Frequency, convertAudioFormatEnum(Format), InternalBufferSize / SampleSize);
if(CaptureDevice)
{
DeviceName = alcGetString(CaptureDevice, ALC_CAPTURE_DEVICE_SPECIFIER);
Ready = true;
checkError();
getLogger()->logDebug("AudioCapture", "OpenAL Capture Device Opened.");
return true;
}
}
checkError();
return false;
}
void cAudioCapture::shutdownOpenALDevice()
{
cAudioMutexBasicLock lock(Mutex);
if(Supported)
{
if(Capturing)
stopCapture();
if(CaptureDevice)
{
alcCaptureCloseDevice(CaptureDevice);
CaptureDevice = NULL;
Ready = false;
getLogger()->logDebug("AudioCapture", "OpenAL Capture Device Closed.");
}
checkError();
CaptureBuffer.clear();
}
}
void cAudioCapture::shutdown()
{
cAudioMutexBasicLock lock(Mutex);
shutdownOpenALDevice();
}
void cAudioCapture::getAvailableDevices()
{
// Get list of available Capture Devices
cAudioMutexBasicLock lock(Mutex);
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);
}
}
const char* cAudioCapture::getAvailableDeviceName(unsigned int index)
{
cAudioMutexBasicLock lock(Mutex);
if(!AvailableDevices.empty())
{
//Bounds check
if( index > (AvailableDevices.size()-1) ) index = (AvailableDevices.size()-1);
const char* deviceName = AvailableDevices[index].c_str();
return deviceName;
}
return "";
}
unsigned int cAudioCapture::getAvailableDeviceCount()
{
cAudioMutexBasicLock lock(Mutex);
return AvailableDevices.size();
}
const char* cAudioCapture::getDefaultDeviceName()
{
cAudioMutexBasicLock lock(Mutex);
return DefaultDevice.empty() ? "" : DefaultDevice.c_str();
}
void cAudioCapture::updateCaptureBuffer(bool force)
{
cAudioMutexBasicLock lock(Mutex);
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)
{
const unsigned int oldBufferSize = CaptureBuffer.size();
CaptureBuffer.resize(oldBufferSize + availbuffersize, 0);
alcCaptureSamples(CaptureDevice, &CaptureBuffer[oldBufferSize], AvailableSamples);
checkError();
getLogger()->logDebug("AudioCapture", "Captured %i bytes of audio data.", availbuffersize);
}
}
}
}
bool cAudioCapture::beginCapture()
{
cAudioMutexBasicLock lock(Mutex);
if(!Capturing)
{
CaptureBuffer.clear();
if(CaptureDevice && Ready)
{
alcCaptureStart(CaptureDevice);
Capturing = true;
getLogger()->logDebug("AudioCapture", "OpenAL Capture Started.");
}
checkError();
return Capturing;
}
checkError();
return false;
}
void cAudioCapture::stopCapture()
{
cAudioMutexBasicLock lock(Mutex);
if(CaptureDevice && Ready)
{
alcCaptureStop(CaptureDevice);
updateCaptureBuffer(true);
checkError();
getLogger()->logDebug("AudioCapture", "OpenAL Capture Stopped.");
}
Capturing = false;
}
unsigned int cAudioCapture::getCapturedAudio(void* outputBuffer, unsigned int outputBufferSize)
{
cAudioMutexBasicLock lock(Mutex);
unsigned int internalBufferSize = CaptureBuffer.size();
if(outputBuffer && outputBufferSize > 0 && internalBufferSize > 0)
{
int sizeToCopy = (outputBufferSize >= internalBufferSize) ? internalBufferSize : outputBufferSize;
memcpy(outputBuffer, &CaptureBuffer[0], sizeToCopy);
CaptureBuffer.erase(CaptureBuffer.begin(), CaptureBuffer.begin()+sizeToCopy);
getLogger()->logDebug("AudioCapture", "Copied out %i bytes of data out of %i bytes in the buffer at user request.", sizeToCopy, internalBufferSize);
return sizeToCopy;
}
return 0;
}
unsigned int cAudioCapture::getCurrentCapturedAudioSize()
{
cAudioMutexBasicLock lock(Mutex);
return CaptureBuffer.size();
}
bool cAudioCapture::setFrequency(unsigned int frequency)
{
cAudioMutexBasicLock lock(Mutex);
Frequency = frequency;
shutdownOpenALDevice();
return initOpenALDevice();
}
bool cAudioCapture::setFormat(AudioFormats format)
{
cAudioMutexBasicLock lock(Mutex);
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();
return initOpenALDevice();
}
bool cAudioCapture::setInternalBufferSize(unsigned int internalBufferSize)
{
cAudioMutexBasicLock lock(Mutex);
InternalBufferSize = internalBufferSize;
shutdownOpenALDevice();
return initOpenALDevice();
}
bool cAudioCapture::setDevice(const char* deviceName)
{
cAudioMutexBasicLock lock(Mutex);
DeviceName = safeCStr(deviceName);
shutdownOpenALDevice();
return initOpenALDevice();
}
bool cAudioCapture::initialize(const char* deviceName, unsigned int frequency, AudioFormats format, unsigned int internalBufferSize)
{
cAudioMutexBasicLock lock(Mutex);
DeviceName = safeCStr(deviceName);
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();
return initOpenALDevice();
}
bool cAudioCapture::checkError()
{
if(CaptureDevice)
{
int error = alcGetError(CaptureDevice);
if (error != AL_NO_ERROR)
{
const char* errorString = alGetString(error);
getLogger()->logError("AudioCapture", "OpenAL Error: %s.", errorString);
return true;
}
}
return false;
}
ALenum cAudioCapture::convertAudioFormatEnum(AudioFormats format)
{
switch(format)
{
case EAF_8BIT_MONO:
return AL_FORMAT_MONO8;
case EAF_16BIT_MONO:
return AL_FORMAT_MONO16;
case EAF_8BIT_STEREO:
return AL_FORMAT_STEREO8;
case EAF_16BIT_STEREO:
return AL_FORMAT_STEREO16;
default:
return AL_FORMAT_MONO8;
};
}
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;
}
};