Merge pull request #470 from Foaly/MultiSoundInput

Added support for selecting the audio capture device (fixes #220)
This commit is contained in:
Laurent Gomila 2013-10-11 07:39:07 -07:00
commit db77b76d91
2 changed files with 161 additions and 15 deletions

View File

@ -32,6 +32,7 @@
#include <SFML/System/Thread.hpp>
#include <SFML/System/Time.hpp>
#include <vector>
#include <string>
namespace sf
@ -59,13 +60,20 @@ public :
/// This function uses its own thread so that it doesn't block
/// the rest of the program while the capture runs.
/// Please note that only one capture can happen at the same time.
/// You can select which capture device will be used, by passing
/// the name to the setDevice() method. If none was selected
/// before, the default capture device will be used. You can get a
/// list of the names of all available capture devices by calling
/// getAvailableDevices().
///
/// \param sampleRate Desired capture rate, in number of samples per second
///
/// \see stop
/// \return True, if start of capture was successful
///
/// \see stop, getAvailableDevices
///
////////////////////////////////////////////////////////////
void start(unsigned int sampleRate = 44100);
bool start(unsigned int sampleRate = 44100);
////////////////////////////////////////////////////////////
/// \brief Stop the capture
@ -87,6 +95,54 @@ public :
////////////////////////////////////////////////////////////
unsigned int getSampleRate() const;
////////////////////////////////////////////////////////////
/// \brief Get a list of the names of all availabe audio capture devices
///
/// This function returns a vector of strings, containing
/// the names of all availabe audio capture devices.
///
/// \return A vector of strings containing the names
///
////////////////////////////////////////////////////////////
static std::vector<std::string> getAvailableDevices();
////////////////////////////////////////////////////////////
/// \brief Get the name of the default audio capture device
///
/// This function returns the name of the default audio
/// capture device. If none is available, an empty string
/// is returned.
///
/// \return The name of the default audio capture device
///
////////////////////////////////////////////////////////////
static std::string getDefaultDevice();
////////////////////////////////////////////////////////////
/// \brief Set the audio capture device
///
/// This function sets the audio capture device to the device
/// with the given \a name. It can be called on the fly (i.e:
/// while recording). If you do so while recording and
/// opening the device fails, it stops the recording.
///
/// \param The name of the audio capture device
///
/// \return True, if it was able to set the requested device
///
/// \see getAvailableDevices, getDefaultDevice
///
////////////////////////////////////////////////////////////
bool setDevice(const std::string& name);
////////////////////////////////////////////////////////////
/// \brief Get the name of the current audio capture device
///
/// \return The name of the current audio capture device
///
////////////////////////////////////////////////////////////
const std::string& getDevice() const;
////////////////////////////////////////////////////////////
/// \brief Check if the system supports audio capture
///
@ -205,6 +261,7 @@ private :
unsigned int m_sampleRate; ///< Sample rate
sf::Time m_processingInterval; ///< Time period between calls to onProcessSamples
bool m_isCapturing; ///< Capturing state
std::string m_deviceName; ///< Name of the audio capture device
};
} // namespace sf
@ -244,10 +301,17 @@ private :
/// availability with the isAvailable() function. If it returns
/// false, then any attempt to use an audio recorder will fail.
///
/// If you have multiple sound input devices connected to your
/// computer (for example: microphone, external soundcard, webcam mic, ...)
/// you can get a list of all available devices throught the
/// getAvailableDevices() function. You can then select a device
/// by calling setDevice() with the appropiate device. Otherwise
/// the default capturing device will be used.
///
/// It is important to note that the audio capture happens in a
/// separate thread, so that it doesn't block the rest of the
/// program. In particular, the onProcessSamples and onStop
/// virtual functions (but not onStart) will be called
/// program. In particular, the onProcessSamples virtual function
/// (but not onStart and not onStop) will be called
/// from this separate thread. It is important to keep this in
/// mind, because you may have to take care of synchronization
/// issues if you share data between threads.
@ -285,7 +349,10 @@ private :
/// if (CustomRecorder::isAvailable())
/// {
/// CustomRecorder recorder;
/// recorder.start();
///
/// if (!recorder.start())
/// return -1;
///
/// ...
/// recorder.stop();
/// }

View File

@ -30,6 +30,7 @@
#include <SFML/Audio/ALCheck.hpp>
#include <SFML/System/Sleep.hpp>
#include <SFML/System/Err.hpp>
#include <cstring>
#ifdef _MSC_VER
#pragma warning(disable : 4355) // 'this' used in base member initializer list
@ -51,6 +52,9 @@ m_processingInterval(milliseconds(100)),
m_isCapturing (false)
{
priv::ensureALInit();
// Set the device name to the default device
m_deviceName = getDefaultDevice();
}
@ -62,28 +66,28 @@ SoundRecorder::~SoundRecorder()
////////////////////////////////////////////////////////////
void SoundRecorder::start(unsigned int sampleRate)
bool SoundRecorder::start(unsigned int sampleRate)
{
// Check if the device can do audio capture
if (!isAvailable())
{
err() << "Failed to start capture : your system cannot capture audio data (call SoundRecorder::isAvailable to check it)" << std::endl;
return;
return false;
}
// Check that another capture is not already running
if (captureDevice)
{
err() << "Trying to start audio capture, but another capture is already running" << std::endl;
return;
return false;
}
// Open the capture device for capturing 16 bits mono samples
captureDevice = alcCaptureOpenDevice(NULL, sampleRate, AL_FORMAT_MONO16, sampleRate);
captureDevice = alcCaptureOpenDevice(m_deviceName.c_str(), sampleRate, AL_FORMAT_MONO16, sampleRate);
if (!captureDevice)
{
err() << "Failed to open the audio capture device" << std::endl;
return;
err() << "Failed to open the audio capture device with the name: " << m_deviceName << std::endl;
return false;
}
// Clear the array of samples
@ -101,7 +105,11 @@ void SoundRecorder::start(unsigned int sampleRate)
// Start the capture in a new thread, to avoid blocking the main thread
m_isCapturing = true;
m_thread.launch();
return true;
}
return false;
}
@ -111,6 +119,9 @@ void SoundRecorder::stop()
// Stop the capturing thread
m_isCapturing = false;
m_thread.wait();
// Notify derived class
onStop();
}
@ -121,6 +132,77 @@ unsigned int SoundRecorder::getSampleRate() const
}
////////////////////////////////////////////////////////////
std::vector<std::string> SoundRecorder::getAvailableDevices()
{
std::vector<std::string> deviceNameList;
const ALchar *deviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
if (deviceList)
{
while (*deviceList)
{
deviceNameList.push_back(deviceList);
deviceList += std::strlen(deviceList) + 1;
}
}
return deviceNameList;
}
////////////////////////////////////////////////////////////
std::string SoundRecorder::getDefaultDevice()
{
return alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
}
////////////////////////////////////////////////////////////
bool SoundRecorder::setDevice(const std::string& name)
{
// Store the device name
if (name.empty())
m_deviceName = getDefaultDevice();
else
m_deviceName = name;
if (m_isCapturing)
{
// Stop the capturing thread
m_isCapturing = false;
m_thread.wait();
// Open the requested capture device for capturing 16 bits mono samples
captureDevice = alcCaptureOpenDevice(name.c_str(), m_sampleRate, AL_FORMAT_MONO16, m_sampleRate);
if (!captureDevice)
{
// Notify derived class
onStop();
err() << "Failed to open the audio capture device with the name: " << m_deviceName << std::endl;
return false;
}
// Start the capture
alcCaptureStart(captureDevice);
// Start the capture in a new thread, to avoid blocking the main thread
m_isCapturing = true;
m_thread.launch();
}
return true;
}
////////////////////////////////////////////////////////////
const std::string& SoundRecorder::getDevice() const
{
return m_deviceName;
}
////////////////////////////////////////////////////////////
bool SoundRecorder::isAvailable()
{
@ -165,9 +247,6 @@ void SoundRecorder::record()
// Capture is finished : clean up everything
cleanup();
// Notify derived class
onStop();
}