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 nudsfml.audio.old.soundbuffer;
90 
91 import bindbc.sfml.audio;
92 import bindbc.sfml.system;
93 
94 public import nudsfml.system.time;
95 
96 import nudsfml.audio.inputsoundfile;
97 import nudsfml.audio.sound;
98 
99 import nudsfml.system.inputstream;
100 
101 import std.stdio;
102 import std.string;
103 import std.algorithm;
104 import std.array;
105 
106 import nudsfml.system.err;
107 
108 /**
109  * Storage for audio samples defining a sound.
110  */
111 class SoundBuffer {
112     package sfSoundBuffer* sfPtr;
113 
114     /// Default constructor.
115     this() {
116         sfPtr = null;
117     }
118 
119     /// Destructor.
120     ~this() {
121         import nudsfml.system.config;
122 
123         mixin(destructorOutput);
124         if (sfPtr !is null) {
125             sfSoundBuffer_destroy(sfPtr);
126         }
127     }
128 
129     /**
130      * Get the array of audio samples stored in the buffer.
131      *
132      * The format of the returned samples is 16 bits signed integer (short).
133      *
134      *  Returns: Read-only array of sound samples.
135      */
136     const(short[]) getSamples() const {
137         auto sampleCount = sfSoundBuffer_getSampleCount(sfPtr);
138         if (sampleCount > 0)
139             return sfSoundBuffer_getSamples(sfPtr)[0 .. sampleCount];
140 
141         return null;
142     }
143 
144     /**
145      * Get the sample rate of the sound.
146      *
147      * The sample rate is the number of samples played per second. The higher,
148      * the better the quality (for example, 44100 samples/s is CD quality).
149      *
150      * Returns: Sample rate (number of samples per second).
151      */
152     uint getSampleRate() const {
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         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         return cast(Time) sfSoundBuffer_getDuration(sfPtr);
175     }
176 
177     /**
178      * Load the sound buffer from a file.
179      *
180      * The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. The
181      * supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
182      *
183      * Params:
184      * 		filename =	Path of the sound file to load
185      *
186      * Returns: true if loading succeeded, false if it failed.
187      */
188     bool loadFromFile(const(char)[] filename) {
189         import std.string;
190 
191         if (sfPtr !is null) {
192             sfSoundBuffer_destroy(sfPtr);
193         }
194         sfPtr = sfSoundBuffer_createFromFile(filename.toStringz);
195         return sfPtr !is null;
196     }
197 
198     /**
199      * Load the sound buffer from a file in memory.
200      *
201      * The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. The
202      * supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
203      *
204      * Params:
205      * 		data =	The array of data
206      *
207      * Returns: true if loading succeeded, false if it failed.
208      */
209     bool loadFromMemory(const(void)[] data) {
210         if (sfPtr !is null) {
211             sfSoundBuffer_destroy(sfPtr);
212         }
213         sfPtr = sfSoundBuffer_createFromMemory(data.ptr, data.length);
214         return sfPtr !is null;
215     }
216 
217     /*
218      * Load the sound buffer from a custom stream.
219      *
220      * The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. The
221      * supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit.
222      *
223      * Params:
224      * 		stream =	Source stream to read from
225      *
226      * Returns: true if loading succeeded, false if it failed.
227      */
228     /*bool loadFromStream(InputStream stream)
229     {
230         return sfSoundBuffer_loadFromStream(sfPtr, new SoundBufferStream(stream));
231     }
232     */
233     /**
234      * Load the sound buffer from an array of audio samples.
235      *
236      * The assumed format of the audio samples is 16 bits signed integer
237      * (short).
238      *
239      * Params:
240      * 		samples      = Array of samples in memory
241      * 		channelCount = Number of channels (1 = mono, 2 = stereo, ...)
242      * 		sampleRate   = Sample rate (number of samples to play per second)
243      *
244      * Returns: true if loading succeeded, false if it failed.
245      */
246     bool loadFromSamples(const(short[]) samples, uint channelCount, uint sampleRate) {
247         if (sfPtr !is null) {
248             sfSoundBuffer_destroy(sfPtr);
249         }
250         sfPtr = sfSoundBuffer_createFromSamples(samples.ptr, samples.length, channelCount, sampleRate);
251         return sfPtr !is null;
252     }
253 
254     /**
255      * Save the sound buffer to an audio file.
256      *
257      * The supported audio formats are: WAV, OGG/Vorbis, FLAC.
258      *
259      * Params:
260      * 		filename =	Path of the sound file to write
261      *
262      * Returns: true if saving succeeded, false if it failed.
263      */
264     bool saveToFile(const(char)[] filename) const {
265         import std.string;
266 
267         return sfSoundBuffer_saveToFile(sfPtr, filename.toStringz) > 0;
268     }
269 
270 }
271 
272 unittest {
273     version (DSFML_Unittest_Audio) {
274         import std.stdio;
275 
276         writeln("Unit test for sound buffer");
277 
278         auto soundbuffer = new SoundBuffer();
279 
280         if (!soundbuffer.loadFromFile("res/TestSound.ogg")) {
281             //error
282             return;
283         }
284 
285         writeln("Sample Rate: ", soundbuffer.getSampleRate());
286 
287         writeln("Channel Count: ", soundbuffer.getChannelCount());
288 
289         writeln("Duration: ", soundbuffer.getDuration().asSeconds());
290 
291         writeln("Sample Count: ", soundbuffer.getSamples().length);
292 
293         //use sound buffer here
294 
295         writeln();
296     }
297 }
298 
299 private extern (C++) interface sfmlInputStream {
300     long read(void* data, long size);
301 
302     long seek(long position);
303 
304     long tell();
305 
306     long getSize();
307 }
308 
309 private class SoundBufferStream : sfmlInputStream {
310     private InputStream myStream;
311 
312     this(InputStream stream) {
313         myStream = stream;
314     }
315 
316     extern (C++) long read(void* data, long size) {
317         return myStream.read(data[0 .. cast(size_t) size]);
318     }
319 
320     extern (C++) long seek(long position) {
321         return myStream.seek(position);
322     }
323 
324     extern (C++) long tell() {
325         return myStream.tell();
326     }
327 
328     extern (C++) long getSize() {
329         return myStream.getSize();
330     }
331 }