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