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  * A sound buffer holds the data of a sound, which is an array of audio samples.
27  * A sample is a 16 bits signed integer that defines the amplitude of the sound
28  * at a given time. The sound is then restituted by playing these samples at a
29  * high rate (for example, 44100 samples per second is the standard rate used
30  * for playing CDs). In short, audio samples are like texture pixels, and a
31  * SoundBuffer is similar to a Texture.
32  *
33  * A sound buffer can be loaded from a file (see `loadFromFile()` for the
34  * complete list of supported formats), from memory, from a custom stream
35  * (see $(INPUTSTREAM_LINK)) or directly from an array of samples. It can also
36  * be saved back to a file.
37  *
38  * Sound buffers alone are not very useful: they hold the audio data but cannot
39  * be played. To do so, you need to use the $(SOUND_LINK) class, which provides
40  * functions to play/pause/stop the sound as well as changing the way it is
41  * outputted (volume, pitch, 3D position, ...).
42  *
43  * This separation allows more flexibility and better performances: indeed a
44  * $(U SoundBuffer) is a heavy resource, and any operation on it is slow (often
45  * too slow for real-time applications). On the other side, a $(SOUND_LINK) is a
46  * lightweight object, which can use the audio data of a sound buffer and change
47  * the way it is played without actually modifying that data. Note that it is
48  * also possible to bind several $(SOUND_LINK) instances to the same
49  * $(U SoundBuffer).
50  *
51  * It is important to note that the Sound instance doesn't copy the buffer that
52  * it uses, it only keeps a reference to it. Thus, a $(U SoundBuffer) must not
53  * be destructed while it is used by a Sound (i.e. never write a function that
54  * uses a local $(U SoundBuffer) instance for loading a sound).
55  *
56  *Example:
57  * ---
58  * // Declare a new sound buffer
59  * auto buffer = SoundBuffer();
60  *
61  * // Load it from a file
62  * if (!buffer.loadFromFile("sound.wav"))
63  * {
64  *     // error...
65  * }
66  *
67  * // Create a sound source and bind it to the buffer
68  * auto sound1 = new Sound();
69  * sound1.setBuffer(buffer);
70  *
71  * // Play the sound
72  * sound1.play();
73  *
74  * // Create another sound source bound to the same buffer
75  * auto sound2 = new Sound();
76  * sound2.setBuffer(buffer);
77  *
78  * // Play it with a higher pitch -- the first sound remains unchanged
79  * sound2.pitch = 2;
80  * sound2.play();
81  * ---
82  *
83  * See_Also:
84  * $(SOUND_LINK), $(SOUNDBUFFERRECORDER_LINK)
85  */
86 module dsfml.audio.soundbuffer;
87 
88 public import core.time;
89 
90 import dsfml.audio.inputsoundfile;
91 import dsfml.audio.sound;
92 
93 import dsfml.system.inputstream;
94 
95 import std.stdio;
96 
97 import std..string;
98 
99 import std.algorithm;
100 import std.array;
101 
102 import dsfml.system.err;
103 
104 /**
105  * Storage for audio samples defining a sound.
106  */
107 class SoundBuffer
108 {
109     package sfSoundBuffer* sfPtr;
110 
111     /// Default constructor.
112     this()
113     {
114         import dsfml.system..string;
115         sfPtr = sfSoundBuffer_construct();
116         err.write(dsfml.system..string.toString(sfErr_getOutput()));
117     }
118 
119     /// Destructor.
120     ~this()
121     {
122         import dsfml.system.config;
123         mixin(destructorOutput);
124         sfSoundBuffer_destroy(sfPtr);
125     }
126 
127     /**
128      * Get the array of audio samples stored in the buffer.
129      *
130      * The format of the returned samples is 16 bits signed integer (short).
131      *
132      *  Returns: Read-only array of sound samples.
133      */
134     const(short[]) getSamples() const
135     {
136         auto sampleCount = sfSoundBuffer_getSampleCount(sfPtr);
137         if(sampleCount > 0)
138             return sfSoundBuffer_getSamples(sfPtr)[0 .. sampleCount];
139 
140         return null;
141     }
142 
143     /**
144      * Get the sample rate of the sound.
145      *
146      * The sample rate is the number of samples played per second. The higher,
147      * the better the quality (for example, 44100 samples/s is CD quality).
148      *
149      * Returns: Sample rate (number of samples per second).
150      */
151     uint getSampleRate() const
152     {
153         return sfSoundBuffer_getSampleRate(sfPtr);
154     }
155 
156     /**
157      * Get the number of channels used by the sound.
158      *
159      * If the sound is mono then the number of channels will be 1, 2 for stereo,
160      * etc.
161      *
162      * Returns: Number of channels.
163      */
164     uint getChannelCount() const
165     {
166         return sfSoundBuffer_getChannelCount(sfPtr);
167     }
168 
169     /**
170      * Get the total duration of the sound.
171      *
172      * Returns: Sound duration.
173      */
174     Duration getDuration() const
175     {
176         import core.time;
177         return usecs(sfSoundBuffer_getDuration(sfPtr));
178     }
179 
180     /**
181      * Load the sound buffer from a file.
182      *
183      * The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. The
184      * supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
185      *
186      * Params:
187      * 		filename =	Path of the sound file to load
188      *
189      * Returns: true if loading succeeded, false if it failed.
190      */
191     bool loadFromFile(const(char)[] filename)
192     {
193         import dsfml.system..string;
194         if(sfSoundBuffer_loadFromFile(sfPtr, filename.ptr, filename.length))
195         {
196             return true;
197         }
198         else
199         {
200             err.write(dsfml.system..string.toString(sfErr_getOutput()));
201             return false;
202         }
203     }
204 
205     /**
206      * Load the sound buffer from a file in memory.
207      *
208      * The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. The
209      * supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
210      *
211      * Params:
212      * 		data =	The array of data
213      *
214      * Returns: true if loading succeeded, false if it failed.
215      */
216     bool loadFromMemory(const(void)[] data)
217     {
218         if(sfSoundBuffer_loadFromMemory(sfPtr, data.ptr, data.length))
219         {
220             return true;
221         }
222         else
223         {
224             import dsfml.system..string;
225             err.write(dsfml.system..string.toString(sfErr_getOutput()));
226             return false;
227         }
228     }
229 
230     /*
231      * Load the sound buffer from a custom stream.
232      *
233      * The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. The
234      * supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
235      *
236      * Params:
237      * 		stream =	Source stream to read from
238      *
239      * Returns: true if loading succeeded, false if it failed.
240      */
241     bool loadFromStream(InputStream stream)
242     {
243         if(sfSoundBuffer_loadFromStream(sfPtr, new SoundBufferStream(stream)))
244         {
245             return true;
246         }
247         else
248         {
249             import dsfml.system..string;
250             err.write(dsfml.system..string.toString(sfErr_getOutput()));
251             return false;
252         }
253     }
254 
255     /**
256      * Load the sound buffer from an array of audio samples.
257      *
258      * The assumed format of the audio samples is 16 bits signed integer
259      * (short).
260      *
261      * Params:
262      * 		samples      = Array of samples in memory
263      * 		channelCount = Number of channels (1 = mono, 2 = stereo, ...)
264      * 		sampleRate   = Sample rate (number of samples to play per second)
265      *
266      * Returns: true if loading succeeded, false if it failed.
267      */
268     bool loadFromSamples(const(short[]) samples, uint channelCount, uint sampleRate)
269     {
270         if(sfSoundBuffer_loadFromSamples(sfPtr, samples.ptr, samples.length, channelCount, sampleRate))
271         {
272             return true;
273         }
274         else
275         {
276             import dsfml.system..string;
277             err.write(dsfml.system..string.toString(sfErr_getOutput()));
278             return false;
279         }
280     }
281 
282     /**
283      * Save the sound buffer to an audio file.
284      *
285      * The supported audio formats are: WAV, OGG/Vorbis, FLAC.
286      *
287      * Params:
288      * 		filename =	Path of the sound file to write
289      *
290      * Returns: true if saving succeeded, false if it failed.
291      */
292     bool saveToFile(const(char)[] filename)
293     {
294         import dsfml.system..string;
295         if(sfSoundBuffer_saveToFile(sfPtr, filename.ptr, filename.length))
296         {
297             return true;
298         }
299         else
300         {
301 
302             err.write(dsfml.system..string.toString(sfErr_getOutput()));
303             return false;
304         }
305     }
306 
307 }
308 
309 unittest
310 {
311     version(DSFML_Unittest_Audio)
312     {
313         import std.stdio;
314 
315         writeln("Unit test for sound buffer");
316 
317         auto soundbuffer = new SoundBuffer();
318 
319         if(!soundbuffer.loadFromFile("res/TestSound.ogg"))
320         {
321             //error
322             return;
323         }
324 
325         writeln("Sample Rate: ", soundbuffer.getSampleRate());
326 
327         writeln("Channel Count: ", soundbuffer.getChannelCount());
328 
329         writeln("Duration: ", soundbuffer.getDuration().total!"seconds");
330 
331         writeln("Sample Count: ", soundbuffer.getSamples().length);
332 
333         //use sound buffer here
334 
335         writeln();
336     }
337 }
338 
339 
340 private extern(C++) interface sfmlInputStream
341 {
342     long read(void* data, long size);
343 
344     long seek(long position);
345 
346     long tell();
347 
348     long getSize();
349 }
350 
351 
352 private class SoundBufferStream:sfmlInputStream
353 {
354     private InputStream myStream;
355 
356     this(InputStream stream)
357     {
358         myStream = stream;
359     }
360 
361     extern(C++)long read(void* data, long size)
362     {
363         return myStream.read(data[0..cast(size_t)size]);
364     }
365 
366     extern(C++)long seek(long position)
367     {
368         return myStream.seek(position);
369     }
370 
371     extern(C++)long tell()
372     {
373         return myStream.tell();
374     }
375 
376     extern(C++)long getSize()
377     {
378         return myStream.getSize();
379     }
380 }
381 
382 package struct sfSoundBuffer;
383 
384 private extern(C):
385 
386 sfSoundBuffer* sfSoundBuffer_construct();
387 
388 bool sfSoundBuffer_loadFromFile(sfSoundBuffer* soundBuffer, const char* filename, size_t length);
389 
390 bool sfSoundBuffer_loadFromMemory(sfSoundBuffer* soundBuffer, const void* data, size_t sizeInBytes);
391 
392 bool sfSoundBuffer_loadFromStream(sfSoundBuffer* soundBuffer, sfmlInputStream stream);
393 
394 bool sfSoundBuffer_loadFromSamples(sfSoundBuffer* soundBuffer, const short* samples, size_t sampleCount, uint channelCount, uint sampleRate);
395 
396 sfSoundBuffer* sfSoundBuffer_copy(const sfSoundBuffer* soundBuffer);
397 
398 void sfSoundBuffer_destroy(sfSoundBuffer* soundBuffer);
399 
400 bool sfSoundBuffer_saveToFile(const sfSoundBuffer* soundBuffer, const char* filename, size_t length);
401 
402 const(short)* sfSoundBuffer_getSamples(const sfSoundBuffer* soundBuffer);
403 
404 size_t sfSoundBuffer_getSampleCount(const sfSoundBuffer* soundBuffer);
405 
406 uint sfSoundBuffer_getSampleRate(const sfSoundBuffer* soundBuffer);
407 
408 uint sfSoundBuffer_getChannelCount(const sfSoundBuffer* soundBuffer);
409 
410 long sfSoundBuffer_getDuration(const sfSoundBuffer* soundBuffer);
411 
412 const(char)* sfErr_getOutput();