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