Merge pull request #470 from Foaly/MultiSoundInput
Added support for selecting the audio capture device (fixes #220)
This commit is contained in:
commit
db77b76d91
@ -32,6 +32,7 @@
|
|||||||
#include <SFML/System/Thread.hpp>
|
#include <SFML/System/Thread.hpp>
|
||||||
#include <SFML/System/Time.hpp>
|
#include <SFML/System/Time.hpp>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
namespace sf
|
namespace sf
|
||||||
@ -59,13 +60,20 @@ public :
|
|||||||
/// This function uses its own thread so that it doesn't block
|
/// This function uses its own thread so that it doesn't block
|
||||||
/// the rest of the program while the capture runs.
|
/// the rest of the program while the capture runs.
|
||||||
/// Please note that only one capture can happen at the same time.
|
/// 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
|
/// \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
|
/// \brief Stop the capture
|
||||||
@ -87,6 +95,54 @@ public :
|
|||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
unsigned int getSampleRate() const;
|
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
|
/// \brief Check if the system supports audio capture
|
||||||
///
|
///
|
||||||
@ -205,6 +261,7 @@ private :
|
|||||||
unsigned int m_sampleRate; ///< Sample rate
|
unsigned int m_sampleRate; ///< Sample rate
|
||||||
sf::Time m_processingInterval; ///< Time period between calls to onProcessSamples
|
sf::Time m_processingInterval; ///< Time period between calls to onProcessSamples
|
||||||
bool m_isCapturing; ///< Capturing state
|
bool m_isCapturing; ///< Capturing state
|
||||||
|
std::string m_deviceName; ///< Name of the audio capture device
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sf
|
} // namespace sf
|
||||||
@ -244,10 +301,17 @@ private :
|
|||||||
/// availability with the isAvailable() function. If it returns
|
/// availability with the isAvailable() function. If it returns
|
||||||
/// false, then any attempt to use an audio recorder will fail.
|
/// 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
|
/// It is important to note that the audio capture happens in a
|
||||||
/// separate thread, so that it doesn't block the rest of the
|
/// separate thread, so that it doesn't block the rest of the
|
||||||
/// program. In particular, the onProcessSamples and onStop
|
/// program. In particular, the onProcessSamples virtual function
|
||||||
/// virtual functions (but not onStart) will be called
|
/// (but not onStart and not onStop) will be called
|
||||||
/// from this separate thread. It is important to keep this in
|
/// from this separate thread. It is important to keep this in
|
||||||
/// mind, because you may have to take care of synchronization
|
/// mind, because you may have to take care of synchronization
|
||||||
/// issues if you share data between threads.
|
/// issues if you share data between threads.
|
||||||
@ -285,7 +349,10 @@ private :
|
|||||||
/// if (CustomRecorder::isAvailable())
|
/// if (CustomRecorder::isAvailable())
|
||||||
/// {
|
/// {
|
||||||
/// CustomRecorder recorder;
|
/// CustomRecorder recorder;
|
||||||
/// recorder.start();
|
///
|
||||||
|
/// if (!recorder.start())
|
||||||
|
/// return -1;
|
||||||
|
///
|
||||||
/// ...
|
/// ...
|
||||||
/// recorder.stop();
|
/// recorder.stop();
|
||||||
/// }
|
/// }
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
#include <SFML/Audio/ALCheck.hpp>
|
#include <SFML/Audio/ALCheck.hpp>
|
||||||
#include <SFML/System/Sleep.hpp>
|
#include <SFML/System/Sleep.hpp>
|
||||||
#include <SFML/System/Err.hpp>
|
#include <SFML/System/Err.hpp>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(disable : 4355) // 'this' used in base member initializer list
|
#pragma warning(disable : 4355) // 'this' used in base member initializer list
|
||||||
@ -51,6 +52,9 @@ m_processingInterval(milliseconds(100)),
|
|||||||
m_isCapturing (false)
|
m_isCapturing (false)
|
||||||
{
|
{
|
||||||
priv::ensureALInit();
|
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
|
// Check if the device can do audio capture
|
||||||
if (!isAvailable())
|
if (!isAvailable())
|
||||||
{
|
{
|
||||||
err() << "Failed to start capture : your system cannot capture audio data (call SoundRecorder::isAvailable to check it)" << std::endl;
|
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
|
// Check that another capture is not already running
|
||||||
if (captureDevice)
|
if (captureDevice)
|
||||||
{
|
{
|
||||||
err() << "Trying to start audio capture, but another capture is already running" << std::endl;
|
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
|
// 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)
|
if (!captureDevice)
|
||||||
{
|
{
|
||||||
err() << "Failed to open the audio capture device" << std::endl;
|
err() << "Failed to open the audio capture device with the name: " << m_deviceName << std::endl;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the array of samples
|
// 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
|
// Start the capture in a new thread, to avoid blocking the main thread
|
||||||
m_isCapturing = true;
|
m_isCapturing = true;
|
||||||
m_thread.launch();
|
m_thread.launch();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -111,6 +119,9 @@ void SoundRecorder::stop()
|
|||||||
// Stop the capturing thread
|
// Stop the capturing thread
|
||||||
m_isCapturing = false;
|
m_isCapturing = false;
|
||||||
m_thread.wait();
|
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()
|
bool SoundRecorder::isAvailable()
|
||||||
{
|
{
|
||||||
@ -165,9 +247,6 @@ void SoundRecorder::record()
|
|||||||
|
|
||||||
// Capture is finished : clean up everything
|
// Capture is finished : clean up everything
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
||||||
// Notify derived class
|
|
||||||
onStop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user