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