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