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 }