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