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 * $(U InputSoundFile) decodes audio samples from a sound file. It is used 27 * internally by higher-level classes such as $(SOUNDBUFFER_LINK) and 28 * $(MUSIC_LINK), but can also be useful if you want to process or analyze audio 29 * files without playing them, or if you want to implement your own version of 30 * $(MUSIC_LINK) with more specific features. 31 * 32 * Example: 33 * --- 34 * // Open a sound file 35 * auto file = new InputSoundFile(); 36 * if (!file.openFromFile("music.ogg")) 37 * { 38 * //error 39 * } 40 * 41 * // Print the sound attributes 42 * writeln("duration: ", file.getDuration().total!"seconds"); 43 * writeln("channels: ", file.getChannelCount()); 44 * writeln("sample rate: ", file.getSampleRate()); 45 * writeln("sample count: ", file.getSampleCount()); 46 * 47 * // Read and process batches of samples until the end of file is reached 48 * short samples[1024]; 49 * long count; 50 * do 51 * { 52 * count = file.read(samples, 1024); 53 * 54 * // process, analyze, play, convert, or whatever 55 * // you want to do with the samples... 56 * } 57 * while (count > 0); 58 * --- 59 * 60 * See_Also: 61 * $(OUTPUTSOUNDFILE_LINK) 62 */ 63 module dsfml.audio.inputsoundfile; 64 65 import std..string; 66 import dsfml.system.inputstream; 67 import dsfml.system.err; 68 69 public import core.time; 70 71 /** 72 * Provide read access to sound files. 73 */ 74 class InputSoundFile 75 { 76 private sfInputSoundFile* m_soundFile; 77 78 //keeps an instance of the C++ interface stored if used 79 private soundFileStream m_stream; 80 81 /// Default constructor. 82 this() 83 { 84 m_soundFile = sfInputSoundFile_create(); 85 } 86 87 /// Destructor. 88 ~this() 89 { 90 import dsfml.system.config: destructorOutput; 91 mixin(destructorOutput); 92 sfInputSoundFile_destroy(m_soundFile); 93 } 94 95 /** 96 * Open a sound file from the disk for reading. 97 * 98 * The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. The 99 * supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit. 100 * 101 * Params: 102 * filename = Path of the sound file to load 103 * 104 * Returns: true if the file was successfully opened. 105 */ 106 bool openFromFile(const(char)[] filename) 107 { 108 import dsfml.system..string; 109 bool toReturn = sfInputSoundFile_openFromFile(m_soundFile, filename.ptr, filename.length); 110 err.write(dsfml.system..string.toString(sfErr_getOutput())); 111 return toReturn; 112 } 113 114 /** 115 * Open a sound file in memory for reading. 116 * 117 * The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. The 118 * supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit. 119 * 120 * Params: 121 * data = file data in memory 122 * 123 * Returns: true if the file was successfully opened. 124 */ 125 bool openFromMemory(const(void)[] data) 126 { 127 import dsfml.system..string; 128 bool toReturn = sfInputSoundFile_openFromMemory(m_soundFile, data.ptr, data.length); 129 err.write(dsfml.system..string.toString(sfErr_getOutput())); 130 return toReturn; 131 } 132 133 /** 134 * Open a sound file from a custom stream for reading. 135 * 136 * The supported audio formats are: WAV (PCM only), OGG/Vorbis, FLAC. The 137 * supported sample sizes for FLAC and WAV are 8, 16, 24 and 32 bit. 138 * 139 * Params: 140 * stream = Source stream to read from 141 * 142 * Returns: true if the file was successfully opened. 143 */ 144 bool openFromStream(InputStream stream) 145 { 146 import dsfml.system..string; 147 m_stream = new soundFileStream(stream); 148 149 bool toReturn = sfInputSoundFile_openFromStream(m_soundFile, m_stream); 150 err.write(dsfml.system..string.toString(sfErr_getOutput())); 151 return toReturn; 152 } 153 154 /** 155 * Read audio samples from the open file. 156 * 157 * Params: 158 * samples = array of samples to fill 159 * 160 * Returns: Number of samples actually read (may be less samples.length) 161 */ 162 long read(short[] samples) 163 { 164 return sfInputSoundFile_read(m_soundFile, samples.ptr, samples.length); 165 166 } 167 168 /** 169 * Change the current read position to the given sample offset. 170 * 171 * This function takes a sample offset to provide maximum precision. If you 172 * need to jump to a given time, use the other overload. 173 * 174 * The sample offset takes the channels into account. Offsets can be 175 * calculated like this: sampleNumber * sampleRate * channelCount. 176 * If the given offset exceeds to total number of samples, this function 177 * jumps to the end of the sound file. 178 * 179 * Params: 180 * sampleOffset = Index of the sample to jump to, relative to the beginning 181 */ 182 void seek(long sampleOffset) 183 { 184 sfInputSoundFile_seek(m_soundFile, sampleOffset); 185 186 //Temporary fix for a bug where attempting to write to err 187 //throws an exception in a thread created in C++. This causes 188 //the program to explode. Hooray. 189 190 //This fix will skip the call to err.write if there was no error 191 //to report. If there is an error, well, the program will still explode, 192 //but the user should see the error prior to the call that will make the 193 //program explode. 194 195 string temp = dsfml.system..string.toString(sfErr_getOutput()); 196 if(temp.length > 0) 197 { 198 err.write(temp); 199 } 200 } 201 202 /** 203 * Change the current read position to the given time offset. 204 * 205 * Using a time offset is handy but imprecise. If you need an accurate 206 * result, consider using the overload which takes a sample offset. 207 * 208 * If the given time exceeds to total duration, this function jumps to the 209 * end of the sound file. 210 * 211 * Params: 212 * timeOffset = Time to jump to, relative to the beginning 213 */ 214 void seek(Duration timeOffset) 215 { 216 seek(timeOffset.total!"usecs"); 217 } 218 219 /** 220 * Get the total number of audio samples in the file 221 * 222 * Returns: Number of samples. 223 */ 224 long getSampleCount() 225 { 226 return sfInputSoundFile_getSampleCount(m_soundFile); 227 } 228 229 /** 230 * Get the sample rate of the sound 231 * 232 * Returns: Sample rate, in samples per second. 233 */ 234 uint getSampleRate() 235 { 236 return sfInputSoundFile_getSampleRate(m_soundFile); 237 } 238 239 /** 240 * Get the number of channels used by the sound 241 * 242 * Returns: Number of channels (1 = mono, 2 = stereo). 243 */ 244 uint getChannelCount() 245 { 246 return sfInputSoundFile_getChannelCount(m_soundFile); 247 } 248 } 249 250 private 251 { 252 253 extern(C++) interface soundInputStream 254 { 255 long read(void* data, long size); 256 257 long seek(long position); 258 259 long tell(); 260 261 long getSize(); 262 } 263 264 265 class soundFileStream:soundInputStream 266 { 267 private InputStream myStream; 268 269 this(InputStream stream) 270 { 271 myStream = stream; 272 } 273 274 extern(C++)long read(void* data, long size) 275 { 276 return myStream.read(data[0..cast(size_t)size]); 277 } 278 279 extern(C++)long seek(long position) 280 { 281 return myStream.seek(position); 282 } 283 284 extern(C++)long tell() 285 { 286 return myStream.tell(); 287 } 288 289 extern(C++)long getSize() 290 { 291 return myStream.getSize(); 292 } 293 } 294 295 296 extern(C) const(char)* sfErr_getOutput(); 297 298 299 extern(C) 300 { 301 302 struct sfInputSoundFile; 303 304 sfInputSoundFile* sfInputSoundFile_create(); 305 306 void sfInputSoundFile_destroy(sfInputSoundFile* file); 307 308 long sfInputSoundFile_getSampleCount(const sfInputSoundFile* file); 309 310 uint sfInputSoundFile_getChannelCount( const sfInputSoundFile* file); 311 312 uint sfInputSoundFile_getSampleRate(const sfInputSoundFile* file); 313 314 bool sfInputSoundFile_openFromFile(sfInputSoundFile* file, const char* filename, size_t length); 315 316 bool sfInputSoundFile_openFromMemory(sfInputSoundFile* file,const(void)* data, long sizeInBytes); 317 318 bool sfInputSoundFile_openFromStream(sfInputSoundFile* file, soundInputStream stream); 319 320 bool sfInputSoundFile_openForWriting(sfInputSoundFile* file, const(char)* filename,uint channelCount,uint sampleRate); 321 322 long sfInputSoundFile_read(sfInputSoundFile* file, short* data, long sampleCount); 323 324 void sfInputSoundFile_seek(sfInputSoundFile* file, long timeOffset); 325 } 326 }