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