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 dsfml.audio.soundbuffer; 23 import dsfml.audio.soundsource; 24 25 import dsfml.system.time; 26 import dsfml.system.vector3; 27 28 /++ 29 + Regular sound that can be played in the audio environment. 30 + Sound is the class used to play sounds. 31 + 32 + It provides: 33 + - Control (play, pause, stop) 34 + - Ability to modify output parameters in real-time (pitch, volume, ...) 35 + - 3D spatial features (position, attenuation, ...). 36 + 37 + 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). 38 + 39 + 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. 40 + 41 + See_Also: http://www.sfml-dev.org/documentation/2.0/classsf_1_1Sound.php#details 42 + Authors: Laurent Gomila, Jeremy DeHaan 43 +/ 44 class Sound : SoundSource 45 { 46 import std.typecons:Rebindable; 47 48 //Const AND able to be rebound. Word. 49 private Rebindable!(const(SoundBuffer)) m_buffer; 50 package sfSound* sfPtr; 51 52 this() 53 { 54 sfPtr = sfSound_construct(); 55 } 56 57 this(const(SoundBuffer) buffer) 58 { 59 this(); 60 61 setBuffer(buffer); 62 63 } 64 //TODO: copy constructor? 65 66 ~this() 67 { 68 import dsfml.system.config; 69 mixin(destructorOutput); 70 //stop the sound 71 stop(); 72 73 sfSound_destroy(sfPtr); 74 } 75 76 /** 77 * Whether or not the sound should loop after reaching the end. 78 * 79 * If set, the sound will restart from beginning after reaching the end and so on, until it is stopped or setLoop(false) is called. 80 * 81 * The default looping state for sound is false. 82 */ 83 @property 84 { 85 void isLooping(bool loop) 86 { 87 sfSound_setLoop(sfPtr, loop); 88 } 89 90 bool isLooping() 91 { 92 return sfSound_getLoop(sfPtr); 93 } 94 } 95 96 /** 97 * Change the current playing position (from the beginning) of the sound. 98 * 99 * The playing position can be changed when the sound is either paused or playing. 100 */ 101 @property 102 { 103 void playingOffset(Time offset) 104 { 105 sfSound_setPlayingOffset(sfPtr, offset.asMicroseconds()); 106 } 107 108 Time playingOffset() 109 { 110 return microseconds(sfSound_getPlayingOffset(sfPtr)); 111 } 112 } 113 114 /// Get the current status of the sound (stopped, paused, playing). 115 /// Returns: Current status of the sound 116 @property 117 { 118 Status status() 119 { 120 return cast(Status)sfSound_getStatus(sfPtr); 121 } 122 } 123 124 //from SoundSource 125 /** 126 * The pitch of the sound. 127 * 128 * 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. 129 */ 130 @property 131 { 132 void pitch(float newPitch) 133 { 134 sfSound_setPitch(sfPtr, newPitch); 135 } 136 137 float pitch() 138 { 139 return sfSound_getPitch(sfPtr); 140 } 141 } 142 143 /** 144 * The volume of the sound. 145 * 146 * The volume is a vlue between 0 (mute) and 100 (full volume). The default value for the volume is 100. 147 */ 148 @property 149 { 150 void volume(float newVolume) 151 { 152 sfSound_setVolume(sfPtr, newVolume); 153 } 154 155 float volume() 156 { 157 return sfSound_getVolume(sfPtr); 158 } 159 } 160 161 /** 162 * The 3D position of the sound in the audio scene. 163 * 164 * Only sounds with one channel (mono sounds) can be spatialized. The default position of a sound is (0, 0, 0). 165 */ 166 @property 167 { 168 void position(Vector3f newPosition) 169 { 170 sfSound_setPosition(sfPtr, newPosition.x, newPosition.y, newPosition.z); 171 } 172 173 Vector3f position() 174 { 175 Vector3f temp; 176 sfSound_getPosition(sfPtr, &temp.x, &temp.y, &temp.z); 177 return temp; 178 } 179 } 180 181 /** 182 * Make the sound's position relative to the listener (true) or absolute (false). 183 * 184 * 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). 185 */ 186 @property 187 { 188 void relativeToListener(bool relative) 189 { 190 sfSound_setRelativeToListener(sfPtr, relative); 191 } 192 193 bool relativeToListener() 194 { 195 return sfSound_isRelativeToListener(sfPtr); 196 } 197 } 198 199 /** 200 * The minimum distance of the sound. 201 * 202 * 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. 203 */ 204 @property 205 { 206 void minDistance(float distance) 207 { 208 sfSound_setMinDistance(sfPtr, distance); 209 } 210 211 float minDistance() 212 { 213 return sfSound_getMinDistance(sfPtr); 214 } 215 } 216 217 /** 218 * The attenuation factor of the sound. 219 * 220 * 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. 221 * 222 * 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. 223 */ 224 @property 225 { 226 void attenuation(float newAttenuation) 227 { 228 sfSound_setAttenuation(sfPtr, newAttenuation); 229 } 230 231 float attenuation() 232 { 233 return sfSound_getAttenuation(sfPtr); 234 } 235 } 236 237 238 239 //soundsource 240 241 242 // Property? 243 // (note: if this is changed to a property, change the 244 // documentation at the top of the file accordingly) 245 /* 246 * 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. 247 * 248 * Params: 249 * buffer = Sound buffer to attach to the sound 250 */ 251 void setBuffer(const(SoundBuffer) buffer) 252 { 253 m_buffer = buffer; 254 sfSound_setBuffer(sfPtr, buffer.sfPtr); 255 } 256 257 /// Pause the sound. 258 /// 259 /// This function pauses the sound if it was playing, otherwise (sound already paused or stopped) it has no effect. 260 void pause() 261 { 262 sfSound_pause(sfPtr); 263 } 264 265 /** 266 * Start or resume playing the sound. 267 * 268 * 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. 269 * 270 * This function uses its own thread so that it doesn't block the rest of the program while the sound is played. 271 */ 272 void play() 273 { 274 sfSound_play(sfPtr); 275 } 276 277 /// Stop playing the sound. 278 /// 279 /// 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()). 280 void stop() 281 { 282 sfSound_stop(sfPtr); 283 } 284 285 } 286 287 unittest 288 { 289 version(DSFML_Unittest_Audio) 290 { 291 import std.stdio; 292 import dsfml.system.clock; 293 import dsfml.system.time; 294 295 296 writeln("Unit test for Sound class"); 297 298 //first, get a sound buffer 299 300 auto soundbuffer = new SoundBuffer(); 301 302 if(!soundbuffer.loadFromFile("res/TestSound.ogg")) 303 { 304 //error 305 return; 306 } 307 308 float duration = soundbuffer.getDuration().asSeconds(); 309 310 auto sound = new Sound(soundbuffer); 311 312 //sound.relativeToListener(true); 313 314 auto clock = new Clock(); 315 //play the sound! 316 sound.play(); 317 318 319 while(clock.getElapsedTime().asSeconds()< duration) 320 { 321 //wait for sound to finish 322 } 323 324 //clock.restart(); 325 326 //sound.relativeToListener(false); 327 328 329 330 writeln(); 331 } 332 } 333 334 private extern(C): 335 336 struct sfSound; 337 338 sfSound* sfSound_construct(); 339 340 sfSound* sfSound_copy(const sfSound* sound); 341 342 void sfSound_destroy(sfSound* sound); 343 344 void sfSound_play(sfSound* sound); 345 346 void sfSound_pause(sfSound* sound); 347 348 void sfSound_stop(sfSound* sound); 349 350 void sfSound_setBuffer(sfSound* sound, const sfSoundBuffer* buffer); 351 352 void sfSound_setLoop(sfSound* sound, bool loop); 353 354 bool sfSound_getLoop(const sfSound* sound); 355 356 int sfSound_getStatus(const sfSound* sound); 357 358 void sfSound_setPitch(sfSound* sound, float pitch); 359 360 void sfSound_setVolume(sfSound* sound, float volume); 361 362 void sfSound_setPosition(sfSound* sound, float positionX, float positionY, float positionZ); 363 364 void sfSound_setRelativeToListener(sfSound* sound, bool relative); 365 366 void sfSound_setMinDistance(sfSound* sound, float distance); 367 368 void sfSound_setAttenuation(sfSound* sound, float attenuation); 369 370 void sfSound_setPlayingOffset(sfSound* sound, long timeOffset); 371 372 float sfSound_getPitch(const sfSound* sound); 373 374 float sfSound_getVolume(const sfSound* sound); 375 376 void sfSound_getPosition(const sfSound* sound, float* positionX, float* positionY, float* positionZ); 377 378 bool sfSound_isRelativeToListener(const sfSound* sound); 379 380 float sfSound_getMinDistance(const sfSound* sound); 381 382 float sfSound_getAttenuation(const sfSound* sound); 383 384 long sfSound_getPlayingOffset(const sfSound* sound); 385