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.sound;
21 
22 import core.time;
23 
24 import dsfml.audio.soundbuffer;
25 import dsfml.audio.soundsource;
26 
27 import dsfml.system.vector3;
28 
29 /++
30  + Regular sound that can be played in the audio environment.
31  + Sound is the class used to play sounds.
32  + 
33  + It provides:
34  + 		- Control (play, pause, stop)
35  + 		- Ability to modify output parameters in real-time (pitch, volume, ...)
36  + 		- 3D spatial features (position, attenuation, ...).
37  + 
38  + Sound is perfect for playing short sounds that can fit in memory and require no latency, like foot steps or gun shots. For longer sounds, like background musics or long speeches, rather see Music (which is based on streaming).
39  + 
40  + In order to work, a sound must be given a buffer of audio data to play. Audio data (samples) is stored in SoundBuffer, and attached to a sound with the setBuffer() function. The buffer object attached to a sound must remain alive as long as the sound uses it. Note that multiple sounds can use the same sound buffer at the same time.
41  + 
42  + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1Sound.php#details
43  + Authors: Laurent Gomila, Jeremy DeHaan
44  +/
45 class Sound : SoundSource
46 {
47 	import std.typecons:Rebindable;
48 
49 	//Const AND able to be rebound. Word.
50 	private Rebindable!(const(SoundBuffer)) m_buffer;
51 	package sfSound* sfPtr;
52 
53 	this()
54 	{
55 		sfPtr = sfSound_construct();
56 	}
57 
58 	this(const(SoundBuffer) buffer)
59 	{
60 		this();
61 
62 		setBuffer(buffer);
63 
64 	}
65 	//TODO: copy constructor?
66 
67 	~this()
68 	{
69 		import dsfml.system.config;
70 		mixin(destructorOutput);
71 		//stop the sound
72 		stop();
73 
74 		sfSound_destroy(sfPtr);
75 	}
76 
77 	/** 
78 	 * Whether or not the sound should loop after reaching the end.
79 	 * 
80 	 * If set, the sound will restart from beginning after reaching the end and so on, until it is stopped or setLoop(false) is called.
81 	 * 
82 	 * The default looping state for sound is false.
83 	 */
84 	@property
85 	{
86 		void isLooping(bool loop)
87 		{
88 			sfSound_setLoop(sfPtr, loop);
89 		}
90 
91 		bool isLooping()
92 		{
93 			return sfSound_getLoop(sfPtr);
94 		}
95 	}
96 	
97 	/**
98 	 * Change the current playing position (from the beginning) of the sound.
99 	 * 
100 	 * The playing position can be changed when the sound is either paused or playing.
101 	 */
102 	@property
103 	{
104 		void playingOffset(Duration offset)
105 		{
106 			sfSound_setPlayingOffset(sfPtr, offset.total!"usecs");
107 		}
108 
109 		Duration playingOffset()
110 		{
111 			return usecs(sfSound_getPlayingOffset(sfPtr));
112 		}
113 	}
114 
115 	/// Get the current status of the sound (stopped, paused, playing).
116 	/// Returns: Current status of the sound
117 	@property
118 	{
119 		Status status()
120 		{
121 			return cast(Status)sfSound_getStatus(sfPtr);
122 		}
123 	}
124 
125 	//from SoundSource
126 	/**
127 	 * The pitch of the sound.
128 	 * 
129 	 * The pitch represents the perceived fundamental frequency of a sound; thus you can make a sound more acute or grave by changing its pitch. A side effect of changing the pitch is to modify the playing speed of the sound as well. The default value for the pitch is 1.
130 	 */
131 	@property
132 	{
133 		void pitch(float newPitch)
134 		{
135 			sfSound_setPitch(sfPtr, newPitch);
136 		}
137 		
138 		float pitch()
139 		{
140 			return sfSound_getPitch(sfPtr);
141 		}
142 	}
143 	
144 	/**
145 	 * The volume of the sound.
146 	 * 
147 	 * The volume is a vlue between 0 (mute) and 100 (full volume). The default value for the volume is 100.
148 	 */
149 	@property
150 	{
151 		void volume(float newVolume)
152 		{
153 			sfSound_setVolume(sfPtr, newVolume);
154 		}
155 		
156 		float volume()
157 		{
158 			return sfSound_getVolume(sfPtr);
159 		}
160 	}
161 	
162 	/**
163 	 * The 3D position of the sound in the audio scene.
164 	 * 
165 	 * Only sounds with one channel (mono sounds) can be spatialized. The default position of a sound is (0, 0, 0).
166 	 */
167 	@property
168 	{
169 		void position(Vector3f newPosition)
170 		{
171 			sfSound_setPosition(sfPtr, newPosition.x, newPosition.y, newPosition.z);
172 		}
173 		
174 		Vector3f position()
175 		{
176 			Vector3f temp;
177 			sfSound_getPosition(sfPtr, &temp.x, &temp.y, &temp.z);
178 			return temp;
179 		}
180 	}
181 	
182 	/**
183 	 * Make the sound's position relative to the listener (true) or absolute (false).
184 	 * 
185 	 * Making a sound relative to the listener will ensure that it will always be played the same way regardless the position of the listener.  This can be useful for non-spatialized sounds, sounds that are produced by the listener, or sounds attached to it. The default value is false (position is absolute).
186 	 */
187 	@property
188 	{
189 		void relativeToListener(bool relative)
190 		{
191 			sfSound_setRelativeToListener(sfPtr, relative);
192 		}
193 		
194 		bool relativeToListener()
195 		{
196 			return sfSound_isRelativeToListener(sfPtr);
197 		}
198 	}
199 	
200 	/**
201 	 * The minimum distance of the sound.
202 	 * 
203 	 * The "minimum distance" of a sound is the maximum distance at which it is heard at its maximum volume. Further than the minimum distance, it will start to fade out according to its attenuation factor. A value of 0 ("inside the head of the listener") is an invalid value and is forbidden. The default value of the minimum distance is 1.
204 	 */
205 	@property
206 	{
207 		void minDistance(float distance)
208 		{
209 			sfSound_setMinDistance(sfPtr, distance);
210 		}
211 		
212 		float minDistance()
213 		{
214 			return sfSound_getMinDistance(sfPtr);
215 		}
216 	}
217 	
218 	/**
219 	 * The attenuation factor of the sound.
220 	 * 
221 	 * The attenuation is a multiplicative factor which makes the sound more or less loud according to its distance from the listener. An attenuation of 0 will produce a non-attenuated sound, i.e. its volume will always be the same whether it is heard from near or from far. 
222 	 * 
223 	 * On the other hand, an attenuation value such as 100 will make the sound fade out very quickly as it gets further from the listener. The default value of the attenuation is 1.
224 	 */
225 	@property
226 	{
227 		void attenuation(float newAttenuation)
228 		{
229 			sfSound_setAttenuation(sfPtr, newAttenuation);
230 		}
231 		
232 		float attenuation()
233 		{
234 			return sfSound_getAttenuation(sfPtr);
235 		}
236 	}
237 
238 
239 
240 	//soundsource
241 
242 
243 	// Property? 
244 	// (note: if this is changed to a property, change the 
245 	// documentation at the top of the file accordingly)
246 	/*
247 	 * Set the source buffer containing the audio data to play. It is important to note that the sound buffer is not copied, thus the SoundBuffer instance must remain alive as long as it is attached to the sound.
248 	 * 
249 	 * Params:
250 	 * 		buffer =	Sound buffer to attach to the sound
251 	 */
252 	void setBuffer(const(SoundBuffer) buffer)
253 	{
254 		m_buffer = buffer;
255 		sfSound_setBuffer(sfPtr, buffer.sfPtr);
256 	}
257 
258 	/// Pause the sound.
259 	/// 
260 	/// This function pauses the sound if it was playing, otherwise (sound already paused or stopped) it has no effect.
261 	void pause()
262 	{
263 		sfSound_pause(sfPtr);
264 	}
265 
266 	/**
267 	 * Start or resume playing the sound.
268 	 * 
269 	 * This function starts the stream if it was stopped, resumes it if it was paused, and restarts it from beginning if it was it already playing.
270 	 * 
271 	 * This function uses its own thread so that it doesn't block the rest of the program while the sound is played.
272 	 */
273 	void play()
274 	{
275 		sfSound_play(sfPtr);
276 	}
277 
278 	/// Stop playing the sound.
279 	/// 
280 	/// This function stops the sound if it was playing or paused, and does nothing if it was already stopped. It also resets the playing position (unlike pause()).
281 	void stop()
282 	{
283 		sfSound_stop(sfPtr);
284 	}
285 
286 }
287 
288 unittest
289 {
290 	version(DSFML_Unittest_Audio)
291 	{
292 		import std.stdio;
293 		import dsfml.system.clock;
294 		import core.time;
295 
296 
297 		writeln("Unit test for Sound class");
298 
299 		//first, get a sound buffer
300 
301 		auto soundbuffer = new SoundBuffer();
302 	
303 		if(!soundbuffer.loadFromFile("res/TestSound.ogg"))
304 		{
305 			//error
306 			return;
307 		}
308 
309 		float duration = soundbuffer.getDuration().total!"seconds";
310 
311 		auto sound = new Sound(soundbuffer);
312 
313 		//sound.relativeToListener(true);
314 
315 		auto clock = new Clock();
316 		//play the sound!
317 		sound.play();
318 
319 
320 		while(clock.getElapsedTime().total!"seconds" < duration)
321 		{
322 			//wait for sound to finish
323 		}
324 
325 		//clock.restart();
326 
327 		//sound.relativeToListener(false);
328 
329 
330 
331 		writeln();
332 	}
333 }
334 
335 private extern(C):
336 
337 struct sfSound;
338 
339 sfSound* sfSound_construct();
340 
341 sfSound* sfSound_copy(const sfSound* sound);
342 
343 void sfSound_destroy(sfSound* sound);
344 
345 void sfSound_play(sfSound* sound);
346 
347 void sfSound_pause(sfSound* sound);
348 
349 void sfSound_stop(sfSound* sound);
350 
351 void sfSound_setBuffer(sfSound* sound, const sfSoundBuffer* buffer);
352 
353 void sfSound_setLoop(sfSound* sound, bool loop);
354 
355 bool sfSound_getLoop(const sfSound* sound);
356 
357 int sfSound_getStatus(const sfSound* sound);
358 
359 void sfSound_setPitch(sfSound* sound, float pitch);
360 
361 void sfSound_setVolume(sfSound* sound, float volume);
362 
363 void sfSound_setPosition(sfSound* sound, float positionX, float positionY, float positionZ);
364 
365 void sfSound_setRelativeToListener(sfSound* sound, bool relative);
366 
367 void sfSound_setMinDistance(sfSound* sound, float distance);
368 
369 void sfSound_setAttenuation(sfSound* sound, float attenuation);
370 
371 void sfSound_setPlayingOffset(sfSound* sound, long timeOffset);
372 
373 float sfSound_getPitch(const sfSound* sound);
374 
375 float sfSound_getVolume(const sfSound* sound);
376 
377 void sfSound_getPosition(const sfSound* sound, float* positionX, float* positionY, float* positionZ);
378 
379 bool sfSound_isRelativeToListener(const sfSound* sound);
380 
381 float sfSound_getMinDistance(const sfSound* sound);
382 
383 float sfSound_getAttenuation(const sfSound* sound);
384 
385 long sfSound_getPlayingOffset(const sfSound* sound);