1 /* 2 DSFML - The Simple and Fast Multimedia Library for D 3 4 Copyright (c) 2013 - 2015 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 use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, including commercial applications, 10 and to alter it and redistribute it freely, subject to the following restrictions: 11 12 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. 13 If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 14 15 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 16 17 3. This notice may not be removed or altered from any source distribution 18 */ 19 20 module dsfml.audio.soundrecorder; 21 22 import core.thread; 23 import dsfml.system.err; 24 25 26 /++ 27 + Abstract base class for capturing sound data. 28 + 29 + SoundBuffer provides a simple interface to access the audio recording capabilities of the computer (the microphone). 30 + 31 + As an abstract base class, it only cares about capturing sound samples, the task of making something useful with them is left to the derived class. Note that SFML provides a built-in specialization for saving the captured data to a sound buffer (see SoundBufferRecorder). 32 + 33 + A derived class has only one virtual function to override: 34 + 35 + onProcessSamples provides the new chunks of audio samples while the capture happens 36 + 37 + Moreover, two additionnal virtual functions can be overriden as well if necessary: 38 + 39 + onStart is called before the capture happens, to perform custom initializations 40 + onStop is called after the capture ends, to perform custom cleanup 41 + 42 + The audio capture feature may not be supported or activated on every platform, thus it is recommended to check its availability with the isAvailable() function. If it returns false, then any attempt to use an audio recorder will fail. 43 + 44 + 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 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. 45 + 46 + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1SoundRecorder.php#details 47 + Authors: Laurent Gomila, Jeremy DeHaan 48 +/ 49 class SoundRecorder 50 { 51 package sfSoundRecorder* sfPtr; 52 private SoundRecorderCallBacks callBacks; 53 54 55 protected this() 56 { 57 import dsfml.system.string; 58 callBacks = new SoundRecorderCallBacks(this); 59 sfPtr = sfSoundRecorder_construct(callBacks); 60 61 err.write(dsfml.system..string.toString(sfErr_getOutput())); 62 63 //Fix for some strange bug that I can't seem to track down. 64 //This bug causes the array in SoundBufferRecorder to segfault if 65 //its length reaches 1024, but creating an array of this size before capturing happens 66 //seems to fix it. This fix should allow other implementations to not segfault as well. 67 //I will look into the cause when I have more time, but this at least renders it usable. 68 short[] temp; 69 temp.length = 1024; 70 temp.length =0; 71 } 72 73 ~this() 74 { 75 import dsfml.system.config; 76 mixin(destructorOutput); 77 sfSoundRecorder_destroy(sfPtr); 78 } 79 80 /** 81 * Start the capture. 82 * The sampleRate parameter defines the number of audio samples captured per second. The higher, the better the quality (for example, 44100 samples/sec is CD quality). 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. 83 * 84 * Params: 85 * sampleRate = Desired capture rate, in number of samples per second 86 */ 87 void start(uint theSampleRate = 44100) 88 { 89 import dsfml.system.string; 90 sfSoundRecorder_start(sfPtr, theSampleRate); 91 92 err.write(dsfml.system..string.toString(sfErr_getOutput())); 93 } 94 95 /// Stop the capture. 96 void stop() 97 { 98 sfSoundRecorder_stop(sfPtr); 99 } 100 101 /** 102 * Get the sample rate in samples per second. 103 * 104 * The sample rate defines the number of audio samples captured per second. The higher, the better the quality (for example, 44100 samples/sec is CD quality). 105 */ 106 @property 107 { 108 uint sampleRate() 109 { 110 return sfSoundRecorder_getSampleRate(sfPtr); 111 } 112 } 113 114 /** 115 * Check if the system supports audio capture. 116 * 117 * This function should always be called before using the audio capture features. If it returns false, then any attempt to use SoundRecorder or one of its derived classes will fail. 118 * 119 * Returns: True if audio capture is supported, false otherwise 120 */ 121 static bool isAvailable() 122 { 123 return sfSoundRecorder_isAvailable(); 124 } 125 126 protected 127 { 128 /** 129 * Start capturing audio data. 130 * 131 * This virtual function may be overriden by a derived class if something has to be done every time a new capture starts. If not, this function can be ignored; the default implementation does nothing. 132 * 133 * Returns: True to the start the capture, or false to abort it. 134 */ 135 bool onStart() 136 { 137 return true; 138 } 139 140 /** 141 * Process a new chunk of recorded samples. 142 * 143 * This virtual function is called every time a new chunk of recorded data is available. The derived class can then do whatever it wants with it (storing it, playing it, sending it over the network, etc.). 144 * 145 * Params: 146 * samples = Array of the new chunk of recorded samples 147 * 148 * Returns: True to continue the capture, or false to stop it 149 */ 150 abstract bool onProcessSamples(const(short)[] samples); 151 152 /** 153 * Stop capturing audio data. 154 * 155 * This virtual function may be overriden by a derived class if something has to be done every time the capture ends. If not, this function can be ignored; the default implementation does nothing. 156 */ 157 void onStop() 158 { 159 } 160 161 } 162 163 164 } 165 166 private: 167 168 extern(C++) interface sfmlSoundRecorderCallBacks 169 { 170 171 bool onStart(); 172 bool onProcessSamples(const(short)* samples, size_t sampleCount); 173 void onStop(); 174 } 175 176 class SoundRecorderCallBacks: sfmlSoundRecorderCallBacks 177 { 178 import std.stdio; 179 180 SoundRecorder m_recorder; 181 182 this(SoundRecorder recorder) 183 { 184 185 m_recorder = recorder; 186 } 187 188 extern(C++) bool onStart() 189 { 190 return m_recorder.onStart(); 191 } 192 extern(C++) bool onProcessSamples(const(short)* samples, size_t sampleCount) 193 { 194 return m_recorder.onProcessSamples(samples[0..sampleCount]); 195 } 196 extern(C++) void onStop() 197 { 198 m_recorder.onStop(); 199 } 200 } 201 202 private extern(C): 203 204 struct sfSoundRecorder; 205 206 sfSoundRecorder* sfSoundRecorder_construct(sfmlSoundRecorderCallBacks newCallBacks); 207 208 void sfSoundRecorder_destroy(sfSoundRecorder* soundRecorder); 209 210 void sfSoundRecorder_start(sfSoundRecorder* soundRecorder, uint sampleRate); 211 212 void sfSoundRecorder_stop(sfSoundRecorder* soundRecorder); 213 214 uint sfSoundRecorder_getSampleRate(const sfSoundRecorder* soundRecorder); 215 216 bool sfSoundRecorder_isAvailable(); 217 218 const(char)* sfErr_getOutput(); 219