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