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();