1////////////////////////////////////////////////////////////
2//
3// SFML - Simple and Fast Multimedia Library
4// Copyright (C) 2007-2020 Laurent Gomila (laurent@sfml-dev.org)
5//
6// This software is provided 'as-is', without any express or implied warranty.
7// In no event will the authors be held liable for any damages arising from the use of this software.
8//
9// Permission is granted to anyone to use this software for any purpose,
10// including commercial applications, and to alter it and redistribute it freely,
11// subject to the following restrictions:
12//
13// 1. The origin of this software must not be misrepresented;
14// you must not claim that you wrote the original software.
15// If you use this software in a product, an acknowledgment
16// in the product documentation would be appreciated but is not required.
17//
18// 2. Altered source versions must be plainly marked as such,
19// and must not be misrepresented as being the original software.
20//
21// 3. This notice may not be removed or altered from any source distribution.
22//
23////////////////////////////////////////////////////////////
24
25////////////////////////////////////////////////////////////
26// Headers
27////////////////////////////////////////////////////////////
28#include <SFML/Audio/SoundRecorder.hpp>
29#include <SFML/Audio/AudioDevice.hpp>
30#include <SFML/Audio/ALCheck.hpp>
31#include <SFML/System/Sleep.hpp>
32#include <SFML/System/Err.hpp>
33#include <cstring>
34#include <cassert>
35
36#ifdef _MSC_VER
37 #pragma warning(disable: 4355) // 'this' used in base member initializer list
38#endif
39
40
41namespace
42{
43 ALCdevice* captureDevice = NULL;
44}
45
46namespace sf
47{
48////////////////////////////////////////////////////////////
49SoundRecorder::SoundRecorder() :
50m_thread (&SoundRecorder::record, this),
51m_sampleRate (0),
52m_processingInterval(milliseconds(100)),
53m_isCapturing (false),
54m_deviceName (getDefaultDevice()),
55m_channelCount (1)
56{
57
58}
59
60
61////////////////////////////////////////////////////////////
62SoundRecorder::~SoundRecorder()
63{
64 // This assertion is triggered if the recording is still running while
65 // the object is destroyed. It ensures that stop() is called in the
66 // destructor of the derived class, which makes sure that the recording
67 // thread finishes before the derived object is destroyed. Otherwise a
68 // "pure virtual method called" exception is triggered.
69 assert(!m_isCapturing && "You must call stop() in the destructor of your derived class, so that the recording thread finishes before your object is destroyed.");
70}
71
72
73////////////////////////////////////////////////////////////
74bool SoundRecorder::start(unsigned int sampleRate)
75{
76 // Check if the device can do audio capture
77 if (!isAvailable())
78 {
79 err() << "Failed to start capture: your system cannot capture audio data (call SoundRecorder::isAvailable to check it)" << std::endl;
80 return false;
81 }
82
83 // Check that another capture is not already running
84 if (captureDevice)
85 {
86 err() << "Trying to start audio capture, but another capture is already running" << std::endl;
87 return false;
88 }
89
90 // Determine the recording format
91 ALCenum format = (m_channelCount == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
92
93 // Open the capture device for capturing 16 bits samples
94 captureDevice = alcCaptureOpenDevice(m_deviceName.c_str(), sampleRate, format, sampleRate);
95 if (!captureDevice)
96 {
97 err() << "Failed to open the audio capture device with the name: " << m_deviceName << std::endl;
98 return false;
99 }
100
101 // Clear the array of samples
102 m_samples.clear();
103
104 // Store the sample rate
105 m_sampleRate = sampleRate;
106
107 // Notify derived class
108 if (onStart())
109 {
110 // Start the capture
111 alcCaptureStart(captureDevice);
112
113 // Start the capture in a new thread, to avoid blocking the main thread
114 m_isCapturing = true;
115 m_thread.launch();
116
117 return true;
118 }
119
120 return false;
121}
122
123
124////////////////////////////////////////////////////////////
125void SoundRecorder::stop()
126{
127 // Stop the capturing thread if there is one
128 if (m_isCapturing)
129 {
130 m_isCapturing = false;
131 m_thread.wait();
132
133 // Notify derived class
134 onStop();
135 }
136}
137
138
139////////////////////////////////////////////////////////////
140unsigned int SoundRecorder::getSampleRate() const
141{
142 return m_sampleRate;
143}
144
145
146////////////////////////////////////////////////////////////
147std::vector<std::string> SoundRecorder::getAvailableDevices()
148{
149 std::vector<std::string> deviceNameList;
150
151 const ALchar* deviceList = alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
152 if (deviceList)
153 {
154 while (*deviceList)
155 {
156 deviceNameList.push_back(deviceList);
157 deviceList += std::strlen(deviceList) + 1;
158 }
159 }
160
161 return deviceNameList;
162}
163
164
165////////////////////////////////////////////////////////////
166std::string SoundRecorder::getDefaultDevice()
167{
168 return alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
169}
170
171
172////////////////////////////////////////////////////////////
173bool SoundRecorder::setDevice(const std::string& name)
174{
175 // Store the device name
176 if (name.empty())
177 m_deviceName = getDefaultDevice();
178 else
179 m_deviceName = name;
180
181 if (m_isCapturing)
182 {
183 // Stop the capturing thread
184 m_isCapturing = false;
185 m_thread.wait();
186
187 // Determine the recording format
188 ALCenum format = (m_channelCount == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
189
190 // Open the requested capture device for capturing 16 bits samples
191 captureDevice = alcCaptureOpenDevice(m_deviceName.c_str(), m_sampleRate, format, m_sampleRate);
192 if (!captureDevice)
193 {
194 // Notify derived class
195 onStop();
196
197 err() << "Failed to open the audio capture device with the name: " << m_deviceName << std::endl;
198 return false;
199 }
200
201 // Start the capture
202 alcCaptureStart(captureDevice);
203
204 // Start the capture in a new thread, to avoid blocking the main thread
205 m_isCapturing = true;
206 m_thread.launch();
207 }
208
209 return true;
210}
211
212
213////////////////////////////////////////////////////////////
214const std::string& SoundRecorder::getDevice() const
215{
216 return m_deviceName;
217}
218
219
220////////////////////////////////////////////////////////////
221void SoundRecorder::setChannelCount(unsigned int channelCount)
222{
223 if (m_isCapturing)
224 {
225 err() << "It's not possible to change the channels while recording." << std::endl;
226 return;
227 }
228
229 if (channelCount < 1 || channelCount > 2)
230 {
231 err() << "Unsupported channel count: " << channelCount << " Currently only mono (1) and stereo (2) recording is supported." << std::endl;
232 return;
233 }
234
235 m_channelCount = channelCount;
236}
237
238
239////////////////////////////////////////////////////////////
240unsigned int SoundRecorder::getChannelCount() const
241{
242 return m_channelCount;
243}
244
245
246////////////////////////////////////////////////////////////
247bool SoundRecorder::isAvailable()
248{
249 return (priv::AudioDevice::isExtensionSupported("ALC_EXT_CAPTURE") != AL_FALSE) ||
250 (priv::AudioDevice::isExtensionSupported("ALC_EXT_capture") != AL_FALSE); // "bug" in Mac OS X 10.5 and 10.6
251}
252
253
254////////////////////////////////////////////////////////////
255void SoundRecorder::setProcessingInterval(Time interval)
256{
257 m_processingInterval = interval;
258}
259
260
261////////////////////////////////////////////////////////////
262bool SoundRecorder::onStart()
263{
264 // Nothing to do
265 return true;
266}
267
268
269////////////////////////////////////////////////////////////
270void SoundRecorder::onStop()
271{
272 // Nothing to do
273}
274
275
276////////////////////////////////////////////////////////////
277void SoundRecorder::record()
278{
279 while (m_isCapturing)
280 {
281 // Process available samples
282 processCapturedSamples();
283
284 // Don't bother the CPU while waiting for more captured data
285 sleep(m_processingInterval);
286 }
287
288 // Capture is finished: clean up everything
289 cleanup();
290}
291
292
293////////////////////////////////////////////////////////////
294void SoundRecorder::processCapturedSamples()
295{
296 // Get the number of samples available
297 ALCint samplesAvailable;
298 alcGetIntegerv(captureDevice, ALC_CAPTURE_SAMPLES, 1, &samplesAvailable);
299
300 if (samplesAvailable > 0)
301 {
302 // Get the recorded samples
303 m_samples.resize(samplesAvailable * getChannelCount());
304 alcCaptureSamples(captureDevice, &m_samples[0], samplesAvailable);
305
306 // Forward them to the derived class
307 if (!onProcessSamples(&m_samples[0], m_samples.size()))
308 {
309 // The user wants to stop the capture
310 m_isCapturing = false;
311 }
312 }
313}
314
315
316////////////////////////////////////////////////////////////
317void SoundRecorder::cleanup()
318{
319 // Stop the capture
320 alcCaptureStop(captureDevice);
321
322 // Get the samples left in the buffer
323 processCapturedSamples();
324
325 // Close the device
326 alcCaptureCloseDevice(captureDevice);
327 captureDevice = NULL;
328}
329
330} // namespace sf
331