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 * Unlike audio buffers (see $(SOUNDBUFFER_LINK)), audio streams are never 30 * completely loaded in memory. Instead, the audio data is acquired continuously 31 * while the stream is playing. This behaviour allows to play a sound with no 32 * loading delay, and keeps the memory consumption very low. 33 * 34 * Sound sources that need to be streamed are usually big files (compressed 35 * audio musics that would eat hundreds of MB in memory) or files that would 36 * take a lot of time to be received (sounds played over the network). 37 * 38 * $(U SoundStream) is a base class that doesn't care about the stream source, 39 * which is left to the derived class. SFML provides a built-in specialization 40 * for big files (see $(MUSIC_LINK)). No network stream source is provided, but 41 * you can write your own by combining this class with the network module. 42 * 43 * A derived class has to override two virtual functions: 44 * $(UL 45 * $(LI `onGetData` fills a new chunk of audio data to be played) 46 * $(LI `onSeek` changes the current playing position in the source)) 47 * 48 * $(PARA 49 * It is important to note that each $(U SoundStream) is played in its own 50 * separate thread, so that the streaming loop doesn't block the rest of the 51 * program. In particular, the `onGetData` and `onSeek` virtual functions may 52 * sometimes be called from this separate thread. It is important to keep this 53 * in mind, because you may have to take care of synchronization issues if you 54 * share data between threads.) 55 * 56 * Example: 57 * --- 58 * class CustomStream : SoundStream 59 * { 60 * 61 * bool open(const(char)[] location) 62 * { 63 * // Open the source and get audio settings 64 * ... 65 * uint channelCount = ...; 66 * unint sampleRate = ...; 67 * 68 * // Initialize the stream -- important! 69 * initialize(channelCount, sampleRate); 70 * } 71 * 72 * protected: 73 * override bool onGetData(ref const(short)[] samples) 74 * { 75 * // Fill the chunk with audio data from the stream source 76 * // (note: must not be empty if you want to continue playing) 77 * 78 * // Return true to continue playing 79 * return true; 80 * } 81 * 82 * override void onSeek(Uint32 timeOffset) 83 * { 84 * // Change the current position in the stream source 85 * ... 86 * } 87 * } 88 * 89 * // Usage 90 * auto stream = CustomStream(); 91 * stream.open("path/to/stream"); 92 * stream.play(); 93 * --- 94 * 95 * See_Also: 96 * $(MUSIC_LINK) 97 */ 98 module nudsfml.audio.old.soundstream; 99 100 import bindbc.sfml.audio; 101 import bindbc.sfml.system; 102 103 import core.thread; 104 105 import nudsfml.audio.soundsource; 106 107 import nudsfml.system.vector3; 108 import nudsfml.system.err; 109 110 public import nudsfml.system.time; 111 112 /** 113 * Abstract base class for streamed audio sources. 114 115 private extern (C++) { 116 struct Chunk { 117 const(short)* samples; 118 size_t sampleCount; 119 } 120 } 121 122 private extern (C++) interface sfmlSoundStreamCallBacks { 123 public: 124 bool onGetData(Chunk* chunk); 125 void onSeek(long time); 126 } 127 128 extern (C++) bool onGetData(Chunk* chunk) { 129 const(short)[] samples; 130 131 auto ret = m_stream.onGetData(samples); 132 133 (*chunk).samples = samples.ptr; 134 (*chunk).sampleCount = samples.length; 135 136 return ret; 137 } 138 139 extern (C++) void onSeek(long time) { 140 m_stream.onSeek(microseconds(time)); 141 } 142 143 144 */ 145 class SoundStream : SoundSource { 146 package sfSoundStream* sfPtr; 147 private SoundStreamCallBacks callBacks; 148 149 /// Internal constructor required to set up callbacks. 150 protected this() { 151 callBacks = new SoundStreamCallBacks(this); 152 sfPtr = sfSoundStream_construct(callBacks); 153 } 154 155 /// Destructor. 156 ~this() { 157 import nudsfml.system.config; 158 159 mixin(destructorOutput); 160 161 if (sfPtr !is null) { 162 sfSoundStream_destroy(sfPtr); 163 } 164 } 165 166 /** 167 * Define the audio stream parameters. 168 * 169 * This function must be called by derived classes as soon as they know the 170 * audio settings of the stream to play. Any attempt to manipulate the 171 * stream (`play()`, ...) before calling this function will fail. It can be 172 * called multiple times if the settings of the audio stream change, but 173 * only when the stream is stopped. 174 * 175 * Params: 176 * channelCount = Number of channels of the stream 177 * sampleRate = Sample rate, in samples per second 178 */ 179 protected void initialize(uint channelCount, uint sampleRate) { 180 //sfSoundStream_initialize(sfPtr, channelCount, sampleRate); 181 } 182 183 @property { 184 /** 185 * The pitch of the sound. 186 * 187 * The pitch represents the perceived fundamental frequency of a sound; thus 188 * you can make a sound more acute or grave by changing its pitch. A side 189 * effect of changing the pitch is to modify the playing speed of the sound 190 * as well. The default value for the pitch is 1. 191 */ 192 void pitch(float newPitch) { 193 sfSoundStream_setPitch(sfPtr, newPitch); 194 } 195 196 /// ditto 197 float pitch() const { 198 return sfSoundStream_getPitch(sfPtr); 199 } 200 } 201 202 @property { 203 /** 204 * The volume of the sound. 205 * 206 * The volume is a vlue between 0 (mute) and 100 (full volume). The default 207 * value for the volume is 100. 208 */ 209 void volume(float newVolume) { 210 sfSoundStream_setVolume(sfPtr, newVolume); 211 } 212 213 /// ditto 214 float volume() const { 215 return sfSoundStream_getVolume(sfPtr); 216 } 217 } 218 219 @property { 220 /** 221 * The 3D position of the sound in the audio scene. 222 * 223 * Only sounds with one channel (mono sounds) can be spatialized. The 224 * default position of a sound is (0, 0, 0). 225 */ 226 void position(Vector3f newPosition) { 227 sfSoundStream_setPosition(sfPtr, cast(sfVector3f) newPosition); 228 } 229 230 /// ditto 231 Vector3f position() const { 232 Vector3f temp = cast(Vector3f) sfSoundStream_getPosition(sfPtr); 233 return temp; 234 } 235 } 236 237 @property { 238 /** 239 * Whether or not the stream should loop after reaching the end. 240 * 241 * If set, the stream will restart from the beginning after reaching the end 242 * and so on, until it is stopped or looping is set to false. 243 * 244 * Default looping state for streams is false. 245 */ 246 void isLooping(bool loop) { 247 sfSoundStream_setLoop(sfPtr, loop); 248 } 249 250 /// ditto 251 bool isLooping() const { 252 return sfSoundStream_getLoop(sfPtr) > 0; 253 } 254 } 255 256 @property { 257 /** 258 * The current playing position (from the beginning) of the stream. 259 * 260 * The playing position can be changed when the stream is either paused or 261 * playing. 262 */ 263 void playingOffset(Time offset) { 264 sfSoundStream_setPlayingOffset(sfPtr, cast(sfTime) offset); 265 266 } 267 268 /// ditto 269 Time playingOffset() const { 270 return cast(Time) sfSoundStream_getPlayingOffset(sfPtr); 271 } 272 } 273 274 @property { 275 /** 276 * Make the sound's position relative to the listener (true) or absolute 277 * (false). 278 * 279 * Making a sound relative to the listener will ensure that it will always 280 * be played the same way regardless the position of the listener. This can 281 * be useful for non-spatialized sounds, sounds that are produced by the 282 * listener, or sounds attached to it. The default value is false (position 283 * is absolute). 284 */ 285 void relativeToListener(bool relative) { 286 sfSoundStream_setRelativeToListener(sfPtr, relative); 287 } 288 289 /// ditto 290 bool relativeToListener() const { 291 return sfSoundStream_isRelativeToListener(sfPtr) > 0; 292 } 293 } 294 295 @property { 296 /** 297 * The minimum distance of the sound. 298 * 299 * The "minimum distance" of a sound is the maximum distance at which it is 300 * heard at its maximum volume. Further than the minimum distance, it will 301 * start to fade out according to its attenuation factor. A value of 0 302 * ("inside the head of the listener") is an invalid value and is forbidden. 303 * The default value of the minimum distance is 1. 304 */ 305 void minDistance(float distance) { 306 sfSoundStream_setMinDistance(sfPtr, distance); 307 } 308 309 /// ditto 310 float minDistance() const { 311 return sfSoundStream_getMinDistance(sfPtr); 312 } 313 } 314 315 @property { 316 /** 317 * The attenuation factor of the sound. 318 * 319 * The attenuation is a multiplicative factor which makes the sound more or 320 * less loud according to its distance from the listener. An attenuation of 321 * 0 will produce a non-attenuated sound, i.e. its volume will always be the 322 * same whether it is heard from near or from far. 323 * 324 * On the other hand, an attenuation value such as 100 will make the sound 325 * fade out very quickly as it gets further from the listener. The default 326 * value of the attenuation is 1. 327 */ 328 void attenuation(float newAttenuation) { 329 sfSoundStream_setAttenuation(sfPtr, newAttenuation); 330 } 331 332 /// ditto 333 float attenuation() const { 334 return sfSoundStream_getAttenuation(sfPtr); 335 } 336 } 337 338 @property { 339 /** 340 * The number of channels of the stream. 341 * 342 * 1 channel means mono sound, 2 means stereo, etc. 343 */ 344 uint channelCount() const { 345 return sfSoundStream_getChannelCount(sfPtr); 346 } 347 } 348 349 @property { 350 /** 351 * The stream sample rate of the stream 352 * 353 * The sample rate is the number of audio samples played per second. The 354 * higher, the better the quality. 355 */ 356 uint sampleRate() const { 357 return sfSoundStream_getSampleRate(sfPtr); 358 } 359 } 360 361 @property { 362 /// The current status of the stream (stopped, paused, playing) 363 Status status() const { 364 return cast(Status) sfSoundStream_getStatus(sfPtr); 365 } 366 } 367 368 /** 369 * Start or resume playing the audio stream. 370 * 371 * This function starts the stream if it was stopped, resumes it if it was 372 * paused, and restarts it from the beginning if it was already playing. This 373 * function uses its own thread so that it doesn't block the rest of the 374 * program while the stream is played. 375 */ 376 void play() { 377 sfSoundStream_play(sfPtr); 378 } 379 380 /** 381 * Pause the audio stream. 382 * 383 * This function pauses the stream if it was playing, otherwise (stream 384 * already paused or stopped) it has no effect. 385 */ 386 void pause() { 387 sfSoundStream_pause(sfPtr); 388 } 389 390 /** 391 * Stop playing the audio stream. 392 * 393 * This function stops the stream if it was playing or paused, and does 394 * nothing if it was already stopped. It also resets the playing position 395 * (unlike pause()). 396 */ 397 void stop() { 398 sfSoundStream_stop(sfPtr); 399 } 400 401 /** 402 * Request a new chunk of audio samples from the stream source. 403 * 404 * This function must be overridden by derived classes to provide the audio 405 * samples to play. It is called continuously by the streaming loop, in a 406 * separate thread. The source can choose to stop the streaming loop at any 407 * time, by returning false to the caller. If you return true (i.e. continue 408 * streaming) it is important that the returned array of samples is not 409 * empty; this would stop the stream due to an internal limitation. 410 * 411 * Params: 412 * samples = Array of samples to fill 413 */ 414 protected abstract bool onGetData(ref const(short)[] samples); 415 416 /** 417 * Change the current playing position in the stream source. 418 * 419 * This function must be overridden by derived classes to allow random 420 * seeking into the stream source. 421 * 422 * Params: 423 * timeOffset = New playing position, relative to the start of the stream 424 */ 425 protected abstract void onSeek(Time timeOffset); 426 } 427