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