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