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