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);