1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2017 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
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
8  * use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not claim
15  * that you wrote the original software. If you use this software in a product,
16  * an acknowledgment in the product documentation would be appreciated but is
17  * not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  * misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source distribution
23  */
24 
25 /**
26  *
27  * $(U SoundRecorder) provides a simple interface to access the audio recording
28  * capabilities of the computer (the microphone).
29  *
30  * As an abstract base class, it only cares about capturing sound samples, the
31  * task of making something useful with them is left to the derived class. Note
32  * that DSFML provides a built-in specialization for saving the captured data to
33  * a sound buffer (see $(SOUNDBUFFERRECORDER_LINK)).
34  *
35  * A derived class has only one virtual function to override:
36  * $(UL $(LI onProcessSamples provides the new chunks of audio samples while the
37  * capture happens))
38  *
39  * $(PARA Moreover, two additionnal virtual functions can be overriden as well
40  * if necessary:)
41  * $(UL
42  * $(LI onStart is called before the capture happens, to perform custom
43  * initializations)
44  * $(LI onStop is called after the capture ends, to perform custom cleanup))
45  *
46  * $(PARA
47  * A derived class can also control the frequency of the onProcessSamples calls,
48  * with the setProcessingInterval protected function. The default interval is
49  * chosen so that recording thread doesn't consume too much CPU, but it can be
50  * changed to a smaller value if you need to process the recorded data in real
51  * time, for example.
52  *
53  * The audio capture feature may not be supported or activated on every
54  * platform, thus it is recommended to check its availability with the
55  * `isAvailable()` function. If it returns false, then any attempt to use an
56  * audio recorder will fail.
57  *
58  * If you have multiple sound input devices connected to your  computer (for
59  * example: microphone, external soundcard, webcam mic, ...) you can get a list
60  * of all available devices through the `getAvailableDevices()` function. You
61  * can then select a device by calling `setDevice()` with the appropriate
62  * device. Otherwise the default capturing device will be used.
63  *
64  * By default the recording is in 16-bit mono. Using the setChannelCount method
65  * you can change the number of channels used by the audio capture device to
66  * record. Note that you have to decide whether you want to record in mono or
67  * stereo before starting the recording.
68  *
69  * It is important to note that the audio capture happens in a separate thread,
70  * so that it doesn't block the rest of the program. In particular, the
71  * `onProcessSamples` and `onStop` virtual functions (but not `onStart`) will be
72  * called from this separate thread. It is important to keep this in mind,
73  * because you may have to take care of synchronization issues if you share data
74  * between threads.)
75  *
76  * Example:
77  * ---
78  * class CustomRecorder : SoundRecorder
79  * {
80  *     ~this()
81  *     {
82  *         // Make sure to stop the recording thread
83  *         stop();
84  *     }
85  *
86  *     override bool onStart() // optional
87  *     {
88  *         // Initialize whatever has to be done before the capture starts
89  *         ...
90  *
91  *         // Return true to start playing
92  *         return true;
93  *     }
94  *
95  *     bool onProcessSamples(const(short)[] samples)
96  *     {
97  *         // Do something with the new chunk of samples (store them, send them, ...)
98  *         ...
99  *
100  *         // Return true to continue playing
101  *         return true;
102  *     }
103  *
104  *     override void onStop() // optional
105  *     {
106  *         // Clean up whatever has to be done after the capture ends
107  *         ...
108  *     }
109  * }
110  *
111  * // Usage
112  * if (CustomRecorder.isAvailable())
113  * {
114  *     auto recorder = new CustomRecorder();
115  *
116  *     if (!recorder.start())
117  *         return -1;
118  *
119  *     ...
120  *     recorder.stop();
121  * }
122  * ---
123  *
124  * See_Also:
125  * $(SOUNDBUFFERRECORDER_LINK)
126  */
127 module dsfml.audio.soundrecorder;
128 
129 import core.thread;
130 import core.time;
131 import dsfml.system..string;
132 import dsfml.system.err;
133 
134 
135 /**
136  * Abstract base class for capturing sound data.
137  */
138 class SoundRecorder
139 {
140     package sfSoundRecorder* sfPtr;
141     private SoundRecorderCallBacks callBacks;
142 
143     /// Default constructor.
144     protected this()
145     {
146         import dsfml.system..string;
147         callBacks = new SoundRecorderCallBacks(this);
148         sfPtr = sfSoundRecorder_construct(callBacks);
149 
150         err.write(dsfml.system..string.toString(sfErr_getOutput()));
151 
152         //Fix for some strange bug that I can't seem to track down.
153         //This bug causes the array in SoundBufferRecorder to segfault if
154         //its length reaches 1024, but creating an array of this size before capturing happens
155         //seems to fix it. This fix should allow other implementations to not segfault as well.
156         //I will look into the cause when I have more time, but this at least renders it usable.
157         short[] temp;
158         temp.length = 1024;
159         temp.length =0;
160     }
161 
162     /// Destructor.
163     ~this()
164     {
165         import dsfml.system.config;
166         mixin(destructorOutput);
167         sfSoundRecorder_destroy(sfPtr);
168     }
169 
170     /**
171      * Start the capture.
172      *
173      * The sampleRate parameter defines the number of audio samples captured per
174      * second. The higher, the better the quality (for example, 44100
175      * samples/sec is CD quality). This function uses its own thread so that it
176      * doesn't block the rest of the program while the capture runs. Please note
177      * that only one capture can happen at the same time.
178      *
179      * Params:
180      *  theSampleRate = Desired capture rate, in number of samples per second
181      */
182     void start(uint theSampleRate = 44100)
183     {
184         import dsfml.system..string;
185         sfSoundRecorder_start(sfPtr, theSampleRate);
186 
187         err.write(.toString(sfErr_getOutput()));
188     }
189 
190     /// Stop the capture.
191     void stop()
192     {
193         sfSoundRecorder_stop(sfPtr);
194     }
195 
196     @property
197     {
198         /**
199          * Get the sample rate in samples per second.
200          *
201          * The sample rate defines the number of audio samples captured per second.
202          * The higher, the better the quality (for example, 44100 samples/sec is CD
203          * quality).
204          */
205         uint sampleRate()
206         {
207             return sfSoundRecorder_getSampleRate(sfPtr);
208         }
209     }
210 
211     /**
212      * Get the name of the current audio capture device.
213      *
214      * Returns: The name of the current audio capture device.
215      */
216     string getDevice() const {
217         return .toString(sfSoundRecorder_getDevice(sfPtr));
218     }
219 
220     /**
221      * Set the audio capture device.
222      *
223      * This function sets the audio capture device to the device with the given
224      * name. It can be called on the fly (i.e: while recording). If you do so
225      * while recording and opening the device fails, it stops the recording.
226      *
227      * Params:
228      *  name = The name of the audio capture device
229      *
230      * Returns: true, if it was able to set the requested device.
231      *
232      * See_Also:
233      * `getAvailableDevices`, `getDefaultDevice`
234      */
235     bool setDevice (const(char)[] name)
236     {
237         return sfSoundRecorder_setDevice(sfPtr, name.ptr, name.length);
238     }
239 
240     /**
241      * Get a list of the names of all available audio capture devices.
242      *
243      * This function returns an array of strings, containing the names of all
244      * available audio capture devices.
245      *
246      * Returns: An array of strings containing the names.
247      */
248     static const(string)[] getAvailableDevices()
249     {
250         //stores all available devices after the first call
251         static string[] availableDevices;
252 
253         //if getAvailableDevices hasn't been called yet
254         if(availableDevices.length == 0)
255         {
256             const (char)** devices;
257             size_t counts;
258 
259             devices = sfSoundRecorder_getAvailableDevices(&counts);
260 
261             //calculate real length
262             availableDevices.length = counts;
263 
264             //populate availableDevices
265             for(uint i = 0; i < counts; i++)
266             {
267                 availableDevices[i] = .toString(devices[i]);
268             }
269 
270         }
271 
272         return availableDevices;
273     }
274 
275     /**
276      * Get the name of the default audio capture device.
277      *
278      * This function returns the name of the default audio capture device. If
279      * none is available, an empty string is returned.
280      *
281      * Returns: The name of the default audio capture device.
282      */
283     static string getDefaultDevice() {
284         return .toString(sfSoundRecorder_getDefaultDevice());
285     }
286 
287     /**
288      * Check if the system supports audio capture.
289      *
290      * This function should always be called before using the audio capture
291      * features. If it returns false, then any attempt to use SoundRecorder or
292      * one of its derived classes will fail.
293      *
294      * Returns: true if audio capture is supported, false otherwise.
295      */
296     static bool isAvailable()
297     {
298         return sfSoundRecorder_isAvailable();
299     }
300 
301     protected
302     {
303         /**
304          * Set the processing interval.
305          *
306          * The processing interval controls the period between calls to the
307          * onProcessSamples function. You may want to use a small interval if
308          * you want to process the recorded data in real time, for example.
309          *
310          * Note: this is only a hint, the actual period may vary. So don't rely
311          * on this parameter to implement precise timing.
312          *
313          * The default processing interval is 100 ms.
314          *
315          * Params:
316          *  interval = Processing interval
317          */
318         void setProcessingInterval (Duration interval) {
319             sfSoundRecorder_setProcessingInterval(sfPtr,
320                                                   interval.total!"usecs");
321         }
322 
323         /**
324          * Start capturing audio data.
325          *
326          * This virtual function may be overriden by a derived class if
327          * something has to be done every time a new capture starts. If not,
328          * this function can be ignored; the default implementation does
329          * nothing.
330          *
331          * Returns: true to the start the capture, or false to abort it.
332          */
333         bool onStart()
334         {
335             return true;
336         }
337 
338         /**
339          * Process a new chunk of recorded samples.
340          *
341          * This virtual function is called every time a new chunk of recorded
342          * data is available. The derived class can then do whatever it wants
343          * with it (storing it, playing it, sending it over the network, etc.).
344          *
345          * Params:
346          * 		samples =	Array of the new chunk of recorded samples
347          *
348          * Returns: true to continue the capture, or false to stop it.
349          */
350         abstract bool onProcessSamples(const(short)[] samples);
351 
352         /**
353          * Stop capturing audio data.
354          *
355          * This virtual function may be overriden by a derived class if
356          * something has to be done every time the capture ends. If not, this
357          * function can be ignored; the default implementation does nothing.
358          */
359         void onStop()
360         {
361         }
362     }
363 }
364 
365 private:
366 
367 extern(C++) interface sfmlSoundRecorderCallBacks
368 {
369 
370     bool onStart();
371     bool onProcessSamples(const(short)* samples, size_t sampleCount);
372     void onStop();
373 }
374 
375 class SoundRecorderCallBacks: sfmlSoundRecorderCallBacks
376 {
377     import std.stdio;
378 
379     SoundRecorder m_recorder;
380 
381     this(SoundRecorder recorder)
382     {
383 
384         m_recorder = recorder;
385     }
386 
387     extern(C++) bool onStart()
388     {
389         return m_recorder.onStart();
390     }
391     extern(C++) bool onProcessSamples(const(short)* samples, size_t sampleCount)
392     {
393         return m_recorder.onProcessSamples(samples[0..sampleCount]);
394     }
395     extern(C++) void onStop()
396     {
397         m_recorder.onStop();
398     }
399 }
400 
401 private extern(C):
402 
403 struct sfSoundRecorder;
404 
405 sfSoundRecorder* sfSoundRecorder_construct(sfmlSoundRecorderCallBacks newCallBacks);
406 
407 void sfSoundRecorder_destroy(sfSoundRecorder* soundRecorder);
408 
409 void sfSoundRecorder_start(sfSoundRecorder* soundRecorder, uint sampleRate);
410 
411 void sfSoundRecorder_stop(sfSoundRecorder* soundRecorder);
412 
413 uint sfSoundRecorder_getSampleRate(const sfSoundRecorder* soundRecorder);
414 
415 bool sfSoundRecorder_setDevice(sfSoundRecorder* soundRecorder, const(char)* name, size_t length);
416 
417 const(char)* sfSoundRecorder_getDevice(const (sfSoundRecorder)* soundRecorder);
418 
419 const(char)** sfSoundRecorder_getAvailableDevices(size_t* count);
420 
421 const(char)* sfSoundRecorder_getDefaultDevice();
422 
423 bool sfSoundRecorder_isAvailable();
424 
425 void sfSoundRecorder_setProcessingInterval(sfSoundRecorder* soundRecorder, ulong time);
426 
427 const(char)* sfErr_getOutput();
428