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