1 /**
2 Audio decoder and encoder abstraction. This delegates to format-specific encoders/decoders.
3 
4 Copyright: Guillaume Piolats 2020.
5 License:   $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 */
7 module audioformats.stream;
8 
9 import core.stdc.stdio;
10 import core.stdc.string;
11 import core.stdc.stdlib: malloc, realloc, free;
12 
13 import dplug.core.nogc;
14 import dplug.core.vec;
15 
16 import audioformats.io;
17 
18 version(decodeMP3) import audioformats.minimp3_ex;
19 version(decodeFLAC) import audioformats.drflac;
20 version(decodeOGG) import audioformats.stb_vorbis2;
21 version(decodeOPUS) import audioformats.dopus;
22 version(decodeMOD) import audioformats.pocketmod;
23 
24 version(decodeWAV) import audioformats.wav;
25 else version(encodeWAV) import audioformats.wav;
26 
27 version(decodeXM) import audioformats.libxm;
28 
29 /// Library for sound file decoding and encoding.
30 /// All operations are blocking, and should not be done in a real-time audio thread.
31 /// (besides, you would also need resampling for playback).
32 /// Also not thread-safe, synchronization in on yours.
33 
34 /// Format of audio files.
35 enum AudioFileFormat
36 {
37     wav,  /// WAVE format
38     mp3,  /// MP3  format
39     flac, /// FLAC format
40     ogg,  /// OGG  format
41     opus, /// Opus format
42     mod,  /// ProTracker MOD format
43     xm,   /// FastTracker II Extended Module format
44     unknown
45 }
46 
47 /// Output sample format.
48 enum AudioSampleFormat
49 {
50     s8,   /// Signed 8-bit PCM
51     s16,  /// Signed 16-bit PCM
52     s24,  /// Signed 24-bit PCM
53     fp32, /// 32-bit floating-point
54     fp64  /// 64-bit floating-point
55 }
56 
57 /// An optional struct, passed when encoding a sound.
58 struct EncodingOptions
59 {
60     /// The desired sample bitdepth to encode with.
61     AudioSampleFormat sampleFormat = AudioSampleFormat.fp32; // defaults to 32-bit float
62 
63     /// Enable dither when exporting 8-bit, 16-bit, 24-bit WAV
64     bool enableDither = true;
65 }
66 
67 /// Returns: String representation of an `AudioFileFormat`.
68 string convertAudioFileFormatToString(AudioFileFormat fmt)
69 {
70     final switch(fmt) with (AudioFileFormat)
71     {
72         case wav:     return "wav";
73         case mp3:     return "mp3";
74         case flac:    return "flac";
75         case ogg:     return "ogg";
76         case opus:    return "opus";
77         case mod:     return "mod";
78         case xm:      return "xm";
79         case unknown: return "unknown";
80     }
81 }
82 
83 
84 /// The length of things you shouldn't query a length about:
85 ///    - files that are being written
86 ///    - audio files you don't know the extent
87 enum audiostreamUnknownLength = -1;
88 
89 /// An AudioStream is a pointer to a dynamically allocated `Stream`.
90 public struct AudioStream
91 {
92 public: // This is also part of the public API
93 
94 
95     /// Opens an audio stream that decodes from a file.
96     /// This stream will be opened for reading only.
97     ///
98     /// Params: 
99     ///     path An UTF-8 path to the sound file.
100     ///
101     /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`.
102     void openFromFile(const(char)[] path) @nogc
103     {
104         cleanUp();
105 
106         fileContext = mallocNew!FileContext();
107         fileContext.initialize(path, false);
108         userData = fileContext;
109 
110         _io = mallocNew!IOCallbacks();
111         _io.seek          = &file_seek;
112         _io.tell          = &file_tell;
113         _io.getFileLength = &file_getFileLength;
114         _io.read          = &file_read;
115         _io.write         = null;
116         _io.skip          = &file_skip;
117         _io.flush         = null;
118 
119         startDecoding();
120     }
121 
122     /// Opens an audio stream that decodes from memory.
123     /// This stream will be opened for reading only.
124     /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`.
125     ///
126     /// Params: inputData The whole file to decode.
127     void openFromMemory(const(ubyte)[] inputData) @nogc
128     {
129         cleanUp();
130 
131         memoryContext = mallocNew!MemoryContext();
132         memoryContext.initializeWithConstantInput(inputData.ptr, inputData.length);
133 
134         userData = memoryContext;
135 
136         _io = mallocNew!IOCallbacks();
137         _io.seek          = &memory_seek;
138         _io.tell          = &memory_tell;
139         _io.getFileLength = &memory_getFileLength;
140         _io.read          = &memory_read;
141         _io.write         = null;
142         _io.skip          = &memory_skip;
143         _io.flush         = null;
144 
145         startDecoding();
146     }
147 
148     /// Opens an audio stream that writes to file.
149     /// This stream will be open for writing only.
150     /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`.
151     ///
152     /// Params: 
153     ///     path An UTF-8 path to the sound file.
154     ///     format Audio file format to generate.
155     ///     sampleRate Sample rate of this audio stream. This samplerate might be rounded up to the nearest integer number.
156     ///     numChannels Number of channels of this audio stream.
157     void openToFile(const(char)[] path, 
158                     AudioFileFormat format, 
159                     float sampleRate, 
160                     int numChannels, 
161                     EncodingOptions options = EncodingOptions.init) @nogc
162     {
163         cleanUp();
164         
165         fileContext = mallocNew!FileContext();
166         fileContext.initialize(path, true);
167         userData = fileContext;
168 
169         _io = mallocNew!IOCallbacks();
170         _io.seek          = &file_seek;
171         _io.tell          = &file_tell;
172         _io.getFileLength = null;
173         _io.read          = null;
174         _io.write         = &file_write;
175         _io.skip          = null;
176         _io.flush         = &file_flush;
177 
178         startEncoding(format, sampleRate, numChannels, options);
179     }
180 
181     /// Opens an audio stream that writes to a dynamically growable output buffer.
182     /// This stream will be open for writing only.
183     /// Access to the internal buffer after encoding with `finalizeAndGetEncodedResult`.
184     /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`.
185     ///
186     /// Params: 
187     ///     format Audio file format to generate.
188     ///     sampleRate Sample rate of this audio stream. This samplerate might be rounded up to the nearest integer number.
189     ///     numChannels Number of channels of this audio stream.
190     void openToBuffer(AudioFileFormat format, 
191                       float sampleRate, 
192                       int numChannels,
193                       EncodingOptions options = EncodingOptions.init) @nogc
194     {
195         cleanUp();
196 
197         memoryContext = mallocNew!MemoryContext();
198         memoryContext.initializeWithInternalGrowableBuffer();
199         userData = memoryContext;
200 
201         _io = mallocNew!IOCallbacks();
202         _io.seek          = &memory_seek;
203         _io.tell          = &memory_tell;
204         _io.getFileLength = null;
205         _io.read          = null;
206         _io.write         = &memory_write_append;
207         _io.skip          = null;
208         _io.flush         = &memory_flush;
209 
210         startEncoding(format, sampleRate, numChannels, options);
211     }
212 
213     /// Opens an audio stream that writes to a pre-defined area in memory of `maxLength` bytes.
214     /// This stream will be open for writing only.
215     /// Destroy this stream with `closeAudioStream`.
216     /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`.
217     ///
218     /// Params: 
219     ///     data Pointer to output memory.
220     ///     size_t maxLength.
221     ///     format Audio file format to generate.
222     ///     sampleRate Sample rate of this audio stream. This samplerate might be rounded up to the nearest integer number.
223     ///     numChannels Number of channels of this audio stream.
224     void openToMemory(ubyte* data, 
225                       size_t maxLength,
226                       AudioFileFormat format,
227                       float sampleRate, 
228                       int numChannels,
229                       EncodingOptions options = EncodingOptions.init) @nogc
230     {
231         cleanUp();
232 
233         memoryContext = mallocNew!MemoryContext();
234         memoryContext.initializeWithExternalOutputBuffer(data, maxLength);
235         userData = memoryContext;
236 
237         _io = mallocNew!IOCallbacks();
238         _io.seek          = &memory_seek;
239         _io.tell          = &memory_tell;
240         _io.getFileLength = null;
241         _io.read          = null;
242         _io.write         = &memory_write_limited;
243         _io.skip          = null;
244         _io.flush         = &memory_flush;
245 
246         startEncoding(format, sampleRate, numChannels, options);
247     }
248 
249     ~this() @nogc
250     {
251         cleanUp();
252     }
253 
254     /// Returns: File format of this stream.
255     AudioFileFormat getFormat() nothrow @nogc
256     {
257         return _format;
258     }
259 
260     /// Returns: `true` if using this stream's operations is acceptable in an audio thread (eg: no file I/O).
261     bool realtimeSafe() @nogc
262     {
263         return fileContext is null;
264     }
265 
266     /// Returns: `true` if this stream is concerning a tracker module format.
267     /// This is useful because the seek/tell functions are different.
268     bool isModule() @nogc
269     {
270         final switch(_format) with (AudioFileFormat)
271         {
272             case wav:
273             case mp3:
274             case flac:
275             case ogg:
276             case opus:
277                 return false;
278             case mod:
279             case xm:
280                 return true;
281             case unknown:
282                 assert(false);
283 
284         }
285     }
286 
287     /// Returns: `true` if this stream allows seeking.
288     /// Note: the particular function to call for seeking depends on whether the stream is a tracker module.
289     /// See_also: `seekPosition`.
290     bool canSeek() @nogc
291     {
292         final switch(_format) with (AudioFileFormat)
293         {
294             case wav:
295             case mp3:
296             case flac:
297             case ogg:
298             case opus:
299                 return true;
300             case mod:
301             case xm:
302                 return true;
303             case unknown:
304                 assert(false);
305 
306         }
307     }
308 
309     /// Returns: `true` if this stream is currently open for reading (decoding).
310     ///          `false` if the stream has been destroyed, or if it was created for encoding instead.
311     bool isOpenForReading() nothrow @nogc
312     {
313         return (_io !is null) && (_io.read !is null);
314     }
315 
316     deprecated("Use isOpenForWriting instead") alias isOpenedForWriting = isOpenForWriting;
317 
318     /// Returns: `true` if this stream is currently open for writing (encoding).
319     ///          `false` if the stream has been destroyed, finalized with `finalizeEncoding()`, 
320     ///          or if it was created for decoding instead.    
321     bool isOpenForWriting() nothrow @nogc
322     {
323         // Note: 
324         //  * when opened for reading, I/O operations given are: seek/tell/getFileLength/read.
325         //  * when opened for writing, I/O operations given are: seek/tell/write/flush.
326         return (_io !is null) && (_io.read is null);
327     }
328 
329     /// Returns: Number of channels in this stream. 1 means mono, 2 means stereo...
330     int getNumChannels() nothrow @nogc
331     {
332         return _numChannels;
333     }
334 
335     /// Returns: Length of this stream in frames.
336     /// Note: may return the special value `audiostreamUnknownLength` if the length is unknown.
337     long getLengthInFrames() nothrow @nogc
338     {
339         return _lengthInFrames;
340     }
341 
342     /// Returns: Sample-rate of this stream in Hz.
343     float getSamplerate() nothrow @nogc
344     {
345         return _sampleRate;
346     }
347 
348     /// Read interleaved float samples in the given buffer `outData`.
349     /// 
350     /// Params:
351     ///     outData Buffer where to put decoded samples. Samples are arranged in an interleaved fashion.
352     ///             Must have room for `frames` x `getNumChannels()` samples.
353     ///             For a stereo file, the output data will contain LRLRLR... repeated `result` times.
354     ///
355     ///     frames The number of multichannel frames to be read. 
356     ///            A frame is `getNumChannels()` samples.
357     ///
358     /// Returns: Number of actually read frames. Multiply by `getNumChannels()` to get the number of read samples.
359     ///          When that number is less than `frames`, it means the stream is done decoding, or that there was a decoding error.
360     ///
361     /// TODO: once this returned less than `frames`, are we guaranteed we can keep calling that and it returns 0?
362     int readSamplesFloat(float* outData, int frames) @nogc
363     {
364         assert(isOpenForReading());
365 
366         final switch(_format)
367         {
368             case AudioFileFormat.opus:
369             {
370                 version(decodeOPUS)
371                 {
372                     try
373                     {
374                         // Can't decoder further than end of the stream.
375                         if (_opusPositionFrame + frames > _lengthInFrames)
376                         {
377                             frames = cast(int)(_lengthInFrames - _opusPositionFrame);
378                         }
379 
380                         int decoded = 0;
381                         while (decoded < frames)
382                         {
383                             // Is there any sample left in _opusBuffer?
384                             // If not decode some frames.
385                             if (_opusBuffer is null || _opusBuffer.length == 0)
386                             {
387                                 _opusBuffer = _opusDecoder.readFrame();
388                                 if (_opusBuffer is null)
389                                     break;
390                             }
391 
392                             int samplesInBuffer = cast(int) _opusBuffer.length;
393                             int framesInBuffer  = samplesInBuffer / _numChannels;
394                             if (framesInBuffer == 0)
395                                 break;
396 
397                             // Frames to pull are min( frames left to decode, frames available)
398                             int framesToDecode = frames - decoded;
399                             int framesToUse = framesToDecode < framesInBuffer ? framesToDecode : framesInBuffer;
400                             assert(framesToUse != 0);
401 
402                             int samplesToUse = framesToUse * _numChannels;
403                             int outOffset = decoded*_numChannels;
404 
405                             if (outData !is null) // for seeking, we have the ability in OPUS to call readSamplesFloat with no outData
406                             {
407                                 for (int n = 0; n < samplesToUse; ++n)
408                                 {
409                                     outData[outOffset + n] = _opusBuffer[n] / 32767.0f;
410                                 }
411                             }
412                             _opusBuffer = _opusBuffer[samplesToUse..$]; // reduce size of intermediate buffer
413                             decoded += framesToUse;
414                         }
415                         _opusPositionFrame += decoded;
416                         assert(_opusPositionFrame <= _lengthInFrames);
417                         return decoded;
418                     }
419                     catch(Exception e)
420                     {
421                         destroyFree(e);
422                         return 0; // decoding might fail, in which case return zero samples
423                     }
424                 }
425             }
426 
427             case AudioFileFormat.flac:
428             {
429                 version(decodeFLAC)
430                 {
431                     assert(_flacDecoder !is null);
432 
433                     int* integerData = cast(int*)outData;
434                     int samples = cast(int) drflac_read_s32(_flacDecoder, frames, integerData);
435 
436                     // "Samples are always output as interleaved signed 32-bit PCM."
437                     // Convert to float with type-punning. Note that this looses some precision.
438                     double factor = 1.0 / int.max;
439                     foreach(n; 0..samples)
440                     {
441                         outData[n] = integerData[n]  * factor;
442                     }
443                     int framesDecoded = samples / _numChannels;
444                     _flacPositionFrame += framesDecoded;
445                     return framesDecoded;
446                 }
447                 else
448                 {
449                     assert(false); // Impossible
450                 }
451             }
452             
453             case AudioFileFormat.ogg:
454             {
455                 version(decodeOGG)
456                 {
457                     assert(_oggHandle !is null);
458                     return stb_vorbis_get_samples_float_interleaved(_oggHandle, _numChannels, outData, frames * _numChannels);
459                 }
460                 else
461                 {
462                     assert(false); // Impossible
463                 }
464             }
465 
466             case AudioFileFormat.mp3:
467             {
468                 version(decodeMP3)
469                 {
470                     assert(_mp3DecoderNew !is null);
471 
472                     int samplesNeeded = frames * _numChannels;
473                     int result = cast(int) mp3dec_ex_read(_mp3DecoderNew, outData, samplesNeeded);
474                     if (result < 0) // error
475                         return 0;
476                     return result / _numChannels;
477                 }
478                 else
479                 {
480                     assert(false); // Impossible
481                 }
482             }
483             case AudioFileFormat.wav:
484                 version(decodeWAV)
485                 {
486                     assert(_wavDecoder !is null);
487                     int readFrames = _wavDecoder.readSamples!float(outData, frames); 
488                     return readFrames;
489                 }
490                 else
491                 {
492                     assert(false); // Impossible
493                 }
494 
495             case AudioFileFormat.xm:
496                 version(decodeXM)
497                 {
498                     assert(_xmDecoder !is null);
499 
500                     if (xm_get_loop_count(_xmDecoder) >= 1)
501                         return 0; // song is finished
502 
503                     xm_generate_samples(_xmDecoder, outData, frames);
504                     return frames; // Note: XM decoder pads end with zeroes.
505                 }
506                 else
507                 {
508                     assert(false); // Impossible
509                 }
510 
511             case AudioFileFormat.mod:
512                 version(decodeMOD)
513                 {
514                     if (pocketmod_loop_count(_modDecoder) >= 1)
515                         return 0; // end stream after MOD finishes, looping not supported
516                     assert(_modDecoder !is null);
517                     int bytesReturned = pocketmod_render(_modDecoder, outData, frames * 2 * 4);
518                     assert((bytesReturned % 8) == 0);
519                     return bytesReturned / 8;
520                 }
521                 else
522                 {
523                     assert(false); // Impossible
524                 }
525 
526             case AudioFileFormat.unknown:
527                 // One shouldn't ever get there, since in this case
528                 // opening has failed.
529                 assert(false);
530         }
531     }
532     ///ditto
533     int readSamplesFloat(float[] outData) @nogc
534     {
535         assert( (outData.length % _numChannels) == 0);
536         return readSamplesFloat(outData.ptr, cast(int)(outData.length / _numChannels) );
537     }
538 
539     /// Read interleaved double samples in the given buffer `outData`.
540     /// 
541     /// Params:
542     ///     outData Buffer where to put decoded samples. Samples are arranged in an interleaved fashion.
543     ///             Must have room for `frames` x `getNumChannels()` samples.
544     ///             For a stereo file, the output data will contain LRLRLR... repeated `result` times.
545     ///
546     ///     frames The number of multichannel frames to be read. 
547     ///            A frame is `getNumChannels()` samples.
548     ///
549     /// Note: the only formats to possibly take advantage of double decoding are WAV and FLAC.
550     ///
551     /// Returns: Number of actually read frames. Multiply by `getNumChannels()` to get the number of read samples.
552     ///          When that number is less than `frames`, it means the stream is done decoding, or that there was a decoding error.
553     ///
554     /// TODO: once this returned less than `frames`, are we guaranteed we can keep calling that and it returns 0?
555     int readSamplesDouble(double* outData, int frames) @nogc
556     {
557         assert(isOpenForReading());
558 
559         switch(_format)
560         {
561             case AudioFileFormat.wav:
562                 version(decodeWAV)
563                 {
564                     assert(_wavDecoder !is null);
565                     int readFrames = _wavDecoder.readSamples!double(outData, frames); 
566                     return readFrames;
567                 }
568                 else
569                 {
570                     assert(false); // Impossible
571                 }
572 
573             case AudioFileFormat.flac:
574             {
575                 version(decodeFLAC)
576                 {
577                     assert(_flacDecoder !is null);
578 
579                     // use second half of the output buffer as temporary integer decoding area
580                     int* integerData = (cast(int*)outData) + frames;
581                     int samples = cast(int) drflac_read_s32(_flacDecoder, frames, integerData);
582 
583                     // "Samples are always output as interleaved signed 32-bit PCM."
584                     // Converting to double doesn't loose mantissa, unlike float.
585                     double factor = 1.0 / int.max;
586                     foreach(n; 0..samples)
587                     {
588                         outData[n] = integerData[n]  * factor;
589                     }
590                     int framesDecoded = samples / _numChannels;
591                     _flacPositionFrame += framesDecoded;
592                     return framesDecoded;
593                 }
594                 else
595                 {
596                     assert(false); // Impossible
597                 }
598             }
599 
600             case AudioFileFormat.unknown:
601                 // One shouldn't ever get there
602                 assert(false);
603 
604             default:
605                 // Decode to float buffer, and then convert
606                 if (_floatDecodeBuf.length < frames * _numChannels)
607                     _floatDecodeBuf.reallocBuffer(frames * _numChannels);
608                 int read = readSamplesFloat(_floatDecodeBuf.ptr, frames);
609                 for (int n = 0; n < read * _numChannels; ++n)
610                     outData[n] = _floatDecodeBuf[n];
611                 return read;
612         }
613     }
614     ///ditto
615     int readSamplesDouble(double[] outData) @nogc
616     {
617         assert( (outData.length % _numChannels) == 0);
618         return readSamplesDouble(outData.ptr, cast(int)(outData.length / _numChannels) );
619     }
620 
621     /// Write interleaved float samples to the stream, from the given buffer `inData[0..frames]`.
622     /// 
623     /// Params:
624     ///     inData Buffer of interleaved samples to append to the stream.
625     ///            Must contain `frames` x `getNumChannels()` samples.
626     ///            For a stereo file, `inData` contains LRLRLR... repeated `frames` times.
627     ///
628     ///     frames The number of frames to append to the stream.
629     ///            A frame is `getNumChannels()` samples.
630     ///
631     /// Returns: Number of actually written frames. Multiply by `getNumChannels()` to get the number of written samples.
632     ///          When that number is less than `frames`, it means the stream had a write error.
633     int writeSamplesFloat(float* inData, int frames) nothrow @nogc
634     {
635         assert(_io && _io.write !is null);
636 
637         final switch(_format)
638         {
639             case AudioFileFormat.mp3:
640             case AudioFileFormat.flac:
641             case AudioFileFormat.ogg:
642             case AudioFileFormat.opus:
643             case AudioFileFormat.mod:
644             case AudioFileFormat.xm:
645             case AudioFileFormat.unknown:
646             {
647                 assert(false); // Shouldn't have arrived here, such encoding aren't supported.
648             }
649             case AudioFileFormat.wav:
650             {
651                 version(encodeWAV)
652                 {
653                     return _wavEncoder.writeSamples(inData, frames);
654                 }
655                 else
656                 {
657                     assert(false, "no support for WAV encoding");
658                 }
659             }
660         }
661     }
662     ///ditto
663     int writeSamplesFloat(float[] inData) nothrow @nogc
664     {
665         assert( (inData.length % _numChannels) == 0);
666         return writeSamplesFloat(inData.ptr, cast(int)(inData.length / _numChannels));
667     }
668 
669     /// Write interleaved double samples to the stream, from the given buffer `inData[0..frames]`.
670     /// 
671     /// Params:
672     ///     inData Buffer of interleaved samples to append to the stream.
673     ///            Must contain `frames` x `getNumChannels()` samples.
674     ///            For a stereo file, `inData` contains LRLRLR... repeated `frames` times.
675     ///
676     ///     frames The number of frames to append to the stream.
677     ///            A frame is `getNumChannels()` samples.
678     ///
679     /// Note: this only does something if the output format is WAV and was setup for 64-bit output.
680     ///
681     /// Returns: Number of actually written frames. Multiply by `getNumChannels()` to get the number of written samples.
682     ///          When that number is less than `frames`, it means the stream had a write error.
683     int writeSamplesDouble(double* inData, int frames) nothrow @nogc
684     {
685         assert(_io && _io.write !is null);
686 
687         switch(_format)
688         {
689             case AudioFileFormat.unknown:
690                 // One shouldn't ever get there
691                 assert(false);
692 
693             case AudioFileFormat.wav:
694                 {
695                     version(encodeWAV)
696                     {
697                         return _wavEncoder.writeSamples(inData, frames);
698                     }
699                     else
700                     {
701                         assert(false, "no support for WAV encoding");
702                     }
703                 }
704 
705             default:
706                 // Decode to float buffer, and then convert
707                 if (_floatDecodeBuf.length < frames * _numChannels)
708                     _floatDecodeBuf.reallocBuffer(frames * _numChannels);
709 
710                 for (int n = 0; n < frames * _numChannels; ++n)
711                     _floatDecodeBuf[n] = inData[n];
712                
713                 return writeSamplesFloat(_floatDecodeBuf.ptr, frames);
714         }
715     }
716     ///ditto
717     int writeSamplesDouble(double[] inData) nothrow @nogc
718     {
719         assert( (inData.length % _numChannels) == 0);
720         return writeSamplesDouble(inData.ptr, cast(int)(inData.length / _numChannels));
721     }
722 
723     // -----------------------------------------------------------------------------------------------------
724     // <module functions>
725     // Those tracker module-specific functions below can only be called when `isModule()` returns `true`.
726     // Additionally, seeking function can only be called if `canSeek()` also returns `true`.
727     // -----------------------------------------------------------------------------------------------------
728 
729     /// Length. Returns the amount of patterns in the module
730     /// Formats that support this: MOD, XM.
731     int countModulePatterns() 
732     {
733         assert(isOpenForReading() && isModule());
734         final switch(_format) with (AudioFileFormat)
735         {
736             case mp3: 
737             case flac:
738             case ogg:
739             case opus:
740             case wav:
741             case unknown:
742                 assert(false);
743             case mod:
744                 return _modDecoder.num_patterns;
745             case xm:
746                 return xm_get_number_of_patterns(_xmDecoder);
747         }
748     }
749 
750     /// Length. Returns the amount of PLAYED patterns in the module
751     /// Formats that support this: MOD, XM.
752     int getModuleLength() 
753     {
754         assert(isOpenForReading() && isModule());
755         final switch(_format) with (AudioFileFormat)
756         {
757             case mp3: 
758             case flac:
759             case ogg:
760             case opus:
761             case wav:
762             case unknown:
763                 assert(false);
764             case mod:
765                 return _modDecoder.length;
766             case xm:
767                 return xm_get_module_length(_xmDecoder);
768         }
769     }
770 
771     /// Tell. Returns amount of rows in a pattern.
772     /// Formats that support this: MOD, XM.
773     /// Returns: -1 on error. Else, number of patterns.
774     int rowsInPattern(int pattern) 
775     {
776         assert(isOpenForReading() && isModule());
777         final switch(_format) with (AudioFileFormat)
778         {
779             case mp3: 
780             case flac:
781             case ogg:
782             case opus:
783             case wav:
784             case unknown:
785                 assert(false);
786 
787             case mod:
788                 // According to http://lclevy.free.fr/mo3/mod.txt
789                 // there's 64 lines (aka rows) per pattern.
790                 // TODO: error checking, make sure no out of bounds happens.
791                 return 64;
792 
793             case xm:
794             {
795                 int numPatterns = xm_get_number_of_patterns(_xmDecoder);
796                 if (pattern < 0 || pattern >= numPatterns)
797                     return -1;
798 
799                 return xm_get_number_of_rows(_xmDecoder, cast(ushort) pattern);
800             }
801         }
802     }
803 
804     /// Tell. Returns the current playing pattern id
805     /// Formats that support this: MOD, XM
806     int tellModulePattern() 
807     {
808         assert(isOpenForReading() && isModule());
809         final switch(_format) with (AudioFileFormat)
810         {
811             case mp3: 
812             case flac:
813             case ogg:
814             case opus:
815             case wav:
816             case unknown:
817                 assert(false);
818             case mod:
819                 return _modDecoder.pattern;
820             case xm:
821                 return _xmDecoder.current_table_index;
822         }
823     }
824 
825     /// Tell. Returns the current playing row id
826     /// Formats that support this: MOD, XM
827     int tellModuleRow() 
828     {
829         assert(isOpenForReading() && isModule());
830         final switch(_format) with (AudioFileFormat)
831         {
832             case mp3: 
833             case flac:
834             case ogg:
835             case opus:
836             case wav:
837             case unknown:
838                 assert(false);
839             case mod:
840                 return _modDecoder.line;
841             case xm:
842                 return _xmDecoder.current_row;
843         }
844     }
845 
846     /// Playback info. Returns the amount of multi-channel frames remaining in the current playing pattern.
847     /// Formats that support this: MOD
848     int framesRemainingInPattern() 
849     {
850         assert(isOpenForReading() && isModule());
851         final switch(_format) with (AudioFileFormat)
852         {
853             case mp3: 
854             case flac:
855             case ogg:
856             case opus:
857             case wav:
858             case unknown:
859                 assert(false);
860 
861             case mod:
862                 return pocketmod_count_remaining_samples(_modDecoder);
863             case xm:
864                 return xm_count_remaining_samples(_xmDecoder);
865         }
866     }
867 
868     /// Seeking. Subsequent reads start from pattern + row, 0 index
869     /// Only available for input streams.
870     /// Formats that support seeking per pattern/row: MOD, XM
871     /// Returns: `true` in case of success.
872     bool seekPosition(int pattern, int row) 
873     {
874         assert(isOpenForReading() && isModule() && canSeek());
875         final switch(_format) with (AudioFileFormat)
876         {
877             case mp3: 
878             case flac:
879             case ogg:
880             case opus:
881             case wav:
882             case unknown:
883                 assert(false);
884 
885             case mod:
886                 // NOTE: This is untested.
887                 return pocketmod_seek(_modDecoder, pattern, row, 0);
888 
889             case xm:
890                 return xm_seek(_xmDecoder, pattern, row, 0);
891         }
892     }
893 
894     // -----------------------------------------------------------------------------------------------------
895     // </module functions>
896     // -----------------------------------------------------------------------------------------------------
897 
898     // -----------------------------------------------------------------------------------------------------
899     // <non-module functions>
900     // Those functions below can't be used for tracker module formats, because there is no real concept of 
901     // absolute position in these formats.
902     // -----------------------------------------------------------------------------------------------------
903 
904     /// Seeking. Subsequent reads start from multi-channel frame index `frames`.
905     /// Only available for input streams, for streams whose `canSeek()` returns `true`.
906     /// Warning: `seekPosition(lengthInFrames)` is Undefined Behaviour for now. (it works in MP3
907     bool seekPosition(int frame)
908     {
909         assert(isOpenForReading() && !isModule() && canSeek()); // seeking doesn't have the same sense with modules.
910         final switch(_format) with (AudioFileFormat)
911         {
912             case mp3: 
913                 version(decodeMP3)
914                 {
915                     assert(_lengthInFrames != audiostreamUnknownLength);
916                     if (frame < 0 || frame > _lengthInFrames)
917                         return false;
918                     return (mp3dec_ex_seek(_mp3DecoderNew, frame * _numChannels) == 0);
919                 }
920                 else
921                     assert(false);
922             case flac:
923                 version(decodeFLAC)
924                 {
925                     if (frame < 0 || frame > _lengthInFrames)
926                         return false;
927                     bool success = drflac__seek_to_sample__brute_force (_flacDecoder, frame * _numChannels);
928                     if (success)
929                         _flacPositionFrame = frame;
930                     return success;
931                 }
932                 else
933                     assert(false);
934             case ogg:
935                 version(decodeOGG)
936                 {
937                     return stb_vorbis_seek(_oggHandle, frame) == 1;
938                 }
939                 else 
940                     assert(false);
941             case opus:
942                 version(decodeOPUS)
943                 {
944                     if (frame < 0 || frame > _lengthInFrames)
945                         return false;
946                     long where = _opusDecoder.ogg.seekPCM(frame);
947                     _opusPositionFrame = where;
948                     int toSkip = cast(int)(frame - where);
949 
950                     // skip remaining samples for sample-accurate seeking
951                     // Note: this also updates _opusPositionFrame
952                     int skipped = readSamplesFloat(null, cast(int) toSkip);
953                     // TODO: if decoding `toSkip` samples failed, restore previous state?
954                     return skipped == toSkip;
955                 }
956                 else 
957                     assert(false);
958 
959             case mod:
960             case xm:
961                 assert(false);
962 
963             case wav:
964                 version(decodeWAV)
965                     return _wavDecoder.seekPosition(frame);
966                 else
967                     assert(false);
968             case unknown:
969                 assert(false);
970 
971         }
972     }
973 
974     /// Tell. Returns the current position in multichannel frames. -1 on error.
975     int tellPosition()
976     {
977         assert(isOpenForReading() && !isModule() && canSeek()); // seeking doesn't have the same sense with modules.
978         final switch(_format) with (AudioFileFormat)
979         {
980             case mp3: 
981                 version(decodeMP3)
982                 {
983                     return cast(int) _mp3DecoderNew.cur_sample / _numChannels;
984                 }
985                 else
986                     assert(false);
987             case flac:
988                 version(decodeFLAC)
989                 {
990                     // Implemented externally since drflac is impenetrable.
991                     //return cast(int) _flacPositionFrame;
992                     return -1; // doesn't work in last frame though... seekPosition buggy in FLAC?
993                 }
994                 else
995                     assert(false);
996             case ogg:
997                 version(decodeOGG)
998                     return cast(int) stb_vorbis_get_sample_offset(_oggHandle);
999                 else 
1000                     assert(false);
1001 
1002             case opus:
1003                 version(decodeOPUS)
1004                 {
1005                     return cast(int) _opusPositionFrame; // implemented externally
1006                 }
1007                 else 
1008                     assert(false);
1009 
1010             case wav:
1011                 version(decodeWAV)
1012                     return _wavDecoder.tellPosition();
1013                 else
1014                     assert(false);
1015 
1016             case mod:
1017             case xm:
1018             case unknown:
1019                 assert(false);
1020 
1021         }
1022     }
1023 
1024 
1025     // -----------------------------------------------------------------------------------------------------
1026     // </non-module functions>
1027     // -----------------------------------------------------------------------------------------------------
1028 
1029     /// Call `fflush()` on written samples, if any. 
1030     /// It is only useful for streamable output formats, that may want to flush things to disk.
1031     void flush() nothrow @nogc
1032     {
1033         assert( _io && (_io.write !is null) );
1034         _io.flush(userData);
1035     }
1036     
1037     /// Finalize encoding. After finalization, further writes are not possible anymore
1038     /// however the stream is considered complete and valid for storage.
1039     void finalizeEncoding() @nogc 
1040     {
1041         // If you crash here, it's because `finalizeEncoding` has been called twice.
1042         assert(isOpenForWriting());
1043 
1044         final switch(_format) with (AudioFileFormat)
1045         {
1046             case mp3:
1047             case flac:
1048             case ogg:
1049             case opus:
1050             case mod:
1051             case xm:
1052                 assert(false); // unsupported output encoding
1053             case wav:
1054                 { 
1055                     _wavEncoder.finalizeEncoding();
1056                     break;
1057                 }
1058             case unknown:
1059                 assert(false);
1060         }
1061         _io.write = null; // prevents further encodings
1062     }
1063 
1064     // Finalize encoding and get internal buffer.
1065     // This can be called multiple times, in which cases the stream is finalized only the first time.
1066     const(ubyte)[] finalizeAndGetEncodedResult() @nogc
1067     {
1068         // only callable while appending, else it's a programming error
1069         assert( (memoryContext !is null) && ( memoryContext.bufferCanGrow ) );
1070 
1071         finalizeEncodingIfNeeded(); 
1072         return memoryContext.buffer[0..memoryContext.size];
1073     }
1074 
1075 private:
1076     IOCallbacks* _io;
1077 
1078     // This type of context is a closure to remember where the data is.
1079     void* userData; // is equal to either fileContext or memoryContext
1080     FileContext* fileContext;
1081     MemoryContext* memoryContext;
1082 
1083     // This type of context is a closure to remember where _io and user Data is.
1084     DecoderContext* _decoderContext;
1085 
1086     AudioFileFormat _format;
1087     float _sampleRate; 
1088     int _numChannels;
1089     long _lengthInFrames;
1090 
1091     float[] _floatDecodeBuf;
1092 
1093     // Decoders
1094     version(decodeMP3)
1095     {
1096         mp3dec_ex_t* _mp3DecoderNew; // allocated on heap since it's a 16kb object
1097         mp3dec_io_t* _mp3io;
1098     }
1099     version(decodeFLAC)
1100     {
1101         drflac* _flacDecoder;
1102         long _flacPositionFrame;
1103     }
1104     version(decodeOGG)
1105     {
1106         ubyte[] _oggBuffer; // all allocations from the ogg decoder
1107         stb_vorbis* _oggHandle;        
1108     }
1109     version(decodeWAV)
1110     {
1111         WAVDecoder _wavDecoder;
1112     }
1113     version(decodeMOD)
1114     {
1115         pocketmod_context* _modDecoder = null;
1116         ubyte[] _modContent = null; // whole buffer, copied
1117     }
1118     version(decodeXM)
1119     {
1120         xm_context_t* _xmDecoder = null;
1121         ubyte* _xmContent = null;
1122     }
1123 
1124     version(decodeOPUS)
1125     {
1126         OpusFile _opusDecoder;
1127         short[] _opusBuffer;
1128         long _opusPositionFrame;
1129     }
1130 
1131     // Encoder
1132     version(encodeWAV)
1133     {
1134         WAVEncoder _wavEncoder;
1135     }
1136 
1137     void cleanUp() @nogc
1138     {
1139         // Write the last needed bytes if needed
1140         finalizeEncodingIfNeeded();
1141 
1142         version(decodeMP3)
1143         {
1144             if (_mp3DecoderNew !is null)
1145             {
1146                 mp3dec_ex_close(_mp3DecoderNew);
1147                 free(_mp3DecoderNew);
1148                 _mp3DecoderNew = null;
1149             }
1150             if (_mp3io !is null)
1151             {
1152                 free(_mp3io);
1153                 _mp3io = null;
1154             }
1155         }
1156 
1157         version(decodeFLAC)
1158         {
1159             if (_flacDecoder !is null)
1160             {
1161                 drflac_close(_flacDecoder);
1162                 _flacDecoder = null;
1163                 _flacPositionFrame = 0;
1164             }
1165         }
1166 
1167         version(decodeOGG)
1168         {
1169             if (_oggHandle !is null)
1170             {
1171                 stb_vorbis_close(_oggHandle);
1172                 _oggHandle = null;
1173             }
1174             _oggBuffer.reallocBuffer(0);
1175         }
1176 
1177         version(decodeOPUS)
1178         {
1179             if (_opusDecoder !is null)
1180             {
1181                 opusClose(_opusDecoder);
1182                 _opusDecoder = null;
1183             }
1184             _opusBuffer = null;
1185         }
1186 
1187         version(decodeWAV)
1188         {
1189             if (_wavDecoder !is null)
1190             {
1191                 destroyFree(_wavDecoder);
1192                 _wavDecoder = null;
1193             }
1194         }
1195 
1196         version(decodeXM)
1197         {
1198             if (_xmDecoder !is null)
1199             {
1200                 xm_free_context(_xmDecoder);
1201                 _xmDecoder = null;
1202             }
1203             if (_xmContent != null)
1204             {
1205                 free(_xmContent);
1206                 _xmContent = null;
1207             }
1208         }
1209 
1210         version(decodeMOD)
1211         {
1212             if (_modDecoder !is null)
1213             {
1214                 free(_modDecoder);
1215                 _modDecoder = null;
1216                 _modContent.reallocBuffer(0);
1217             }
1218         }
1219 
1220         version(encodeWAV)
1221         {
1222             if (_wavEncoder !is null)
1223             {
1224                 destroyFree(_wavEncoder);
1225                 _wavEncoder = null;
1226             }
1227         }
1228 
1229         if (_decoderContext)
1230         {
1231             destroyFree(_decoderContext);
1232             _decoderContext = null;
1233         }
1234 
1235         if (fileContext !is null)
1236         {
1237             if (fileContext.file !is null)
1238             {
1239                 int result = fclose(fileContext.file);
1240                 if (result)
1241                     throw mallocNew!Exception("Closing of audio file errored");
1242             }
1243             destroyFree(fileContext);
1244             fileContext = null;
1245         }
1246 
1247         if (memoryContext !is null)
1248         {
1249             // TODO destroy buffer if any is owned
1250             destroyFree(memoryContext);
1251             memoryContext = null;
1252         }
1253 
1254         if (_io !is null)
1255         {
1256             destroyFree(_io);
1257             _io = null;
1258         }
1259     }
1260 
1261     void startDecoding() @nogc
1262     {
1263         // Create a decoder context
1264         _decoderContext = mallocNew!DecoderContext;
1265         _decoderContext.userDataIO = userData;
1266         _decoderContext.callbacks = _io;
1267 
1268         version(decodeOPUS)
1269         {
1270             try
1271             {
1272                 _opusDecoder = opusOpen(_io, userData);
1273                 assert(_opusDecoder !is null);
1274                 _format = AudioFileFormat.opus;
1275                 _sampleRate = _opusDecoder.rate; // Note: Opus file are always 48Khz
1276                 _numChannels = _opusDecoder.channels();
1277                 _lengthInFrames = _opusDecoder.smpduration();
1278                 _opusPositionFrame = 0;
1279                 return;
1280             }
1281             catch(Exception e)
1282             {
1283                 destroyFree(e);
1284             }
1285             _opusDecoder = null;
1286         }
1287 
1288         version(decodeFLAC)
1289         {
1290             _io.seek(0, false, userData);
1291             
1292             // Is it a FLAC?
1293             {
1294                 drflac_read_proc onRead = &flac_read;
1295                 drflac_seek_proc onSeek = &flac_seek;
1296                 void* pUserData = _decoderContext;
1297                 _flacDecoder = drflac_open (onRead, onSeek, _decoderContext);
1298                 if (_flacDecoder !is null)
1299                 {
1300                     _format = AudioFileFormat.flac;
1301                     _sampleRate = _flacDecoder.sampleRate;
1302                     _numChannels = _flacDecoder.channels;
1303                     _lengthInFrames = _flacDecoder.totalSampleCount / _numChannels;
1304                     _flacPositionFrame = 0;
1305                     return;
1306                 }
1307             }
1308         }
1309 
1310         version(decodeWAV)
1311         {
1312             // Check if it's a WAV.
1313 
1314             _io.seek(0, false, userData);
1315 
1316             try
1317             {
1318                 _wavDecoder = mallocNew!WAVDecoder(_io, userData);
1319                 _wavDecoder.scan();
1320 
1321                 // WAV detected
1322                 _format = AudioFileFormat.wav;
1323                 _sampleRate = _wavDecoder._sampleRate;
1324                 _numChannels = _wavDecoder._channels;
1325                 _lengthInFrames = _wavDecoder._lengthInFrames;
1326                 return;
1327             }
1328             catch(Exception e)
1329             {
1330                 // not a WAV
1331                 destroyFree(e);
1332             }
1333             destroyFree(_wavDecoder);
1334             _wavDecoder = null;
1335         }
1336 
1337         version(decodeOGG)
1338         {
1339             _io.seek(0, false, userData);
1340             
1341             // Is it an OGG?
1342             {
1343                 //"In my test files the maximal-size usage is ~150KB", so let's take a bit more
1344                 _oggBuffer.reallocBuffer(200 * 1024);
1345 
1346                 stb_vorbis_alloc alloc;
1347                 alloc.alloc_buffer = cast(ubyte*)(_oggBuffer.ptr);
1348                 alloc.alloc_buffer_length_in_bytes = cast(int)(_oggBuffer.length);
1349 
1350                 int error;
1351 
1352                 _oggHandle = stb_vorbis_open_file(_io, userData, &error, &alloc);
1353                 if (error == VORBIS__no_error)
1354                 {
1355                     _format = AudioFileFormat.ogg;
1356                     _sampleRate = _oggHandle.sample_rate;
1357                     _numChannels = _oggHandle.channels;
1358                     _lengthInFrames = stb_vorbis_stream_length_in_samples(_oggHandle);
1359                     return;
1360                 }
1361                 else
1362                 {
1363                     _oggHandle = null;
1364                 }
1365             }
1366         }
1367 
1368         version(decodeMP3)
1369         {
1370             // Check if it's a MP3.
1371             {
1372                 _io.seek(0, false, userData);
1373 
1374                 ubyte* scratchBuffer = cast(ubyte*) malloc(MINIMP3_BUF_SIZE*2);
1375                 scope(exit) free(scratchBuffer);
1376 
1377                 _mp3io = cast(mp3dec_io_t*) malloc(mp3dec_io_t.sizeof);
1378                 _mp3io.read      = &mp3_io_read;
1379                 _mp3io.read_data = _decoderContext;
1380                 _mp3io.seek      = &mp3_io_seek;
1381                 _mp3io.seek_data = _decoderContext;
1382 
1383                 if ( mp3dec_detect_cb(_mp3io, scratchBuffer, MINIMP3_BUF_SIZE*2) == 0 )
1384                 {
1385                     // This is a MP3. Try to open a stream.
1386 
1387                     // Allocate a mp3dec_ex_t object
1388                     _mp3DecoderNew = cast(mp3dec_ex_t*) malloc(mp3dec_ex_t.sizeof);
1389 
1390                     int result = mp3dec_ex_open_cb(_mp3DecoderNew, _mp3io, MP3D_SEEK_TO_SAMPLE);
1391 
1392                     if (0 == result)
1393                     {
1394                         // MP3 detected
1395                         // but it seems we need to iterate all frames to know the length...
1396                         _format = AudioFileFormat.mp3;
1397                         _sampleRate = _mp3DecoderNew.info.hz;
1398                         _numChannels = _mp3DecoderNew.info.channels;
1399                         _lengthInFrames = _mp3DecoderNew.samples / _numChannels;
1400                         return;
1401                     }
1402                     else
1403                     {
1404                         free(_mp3DecoderNew);
1405                         _mp3DecoderNew = null;
1406                         free(_mp3io);
1407                         _mp3io = null;
1408                     }
1409                 }
1410             }
1411         }
1412 
1413         version(decodeXM)
1414         {
1415             {
1416                 // we need the first 60 bytes to check if XM
1417                 char[60] xmHeader;
1418                 int bytes;
1419 
1420                 _io.seek(0, false, userData);
1421                 long lenBytes = _io.getFileLength(userData);
1422                 if (lenBytes < 60) 
1423                     goto not_a_xm;
1424 
1425                 bytes = _io.read(xmHeader.ptr, 60, userData);
1426                 if (bytes != 60)
1427                     goto not_a_xm;
1428 
1429                if (0 != xm_check_sanity_preload(xmHeader.ptr, 60))
1430                    goto not_a_xm;
1431 
1432                 _xmContent = cast(ubyte*) malloc(cast(int)lenBytes);
1433                 _io.seek(0, false, userData);
1434                 bytes = _io.read(_xmContent, cast(int)lenBytes, userData);
1435                 if (bytes != cast(int)lenBytes)
1436                     goto not_a_xm;
1437 
1438                 if (0 == xm_create_context_safe(&_xmDecoder, cast(const(char)*)_xmContent, cast(size_t)lenBytes, 44100))
1439                 {
1440                     assert(_xmDecoder !is null);
1441 
1442                     xm_set_max_loop_count(_xmDecoder, 1);
1443 
1444                     _format = AudioFileFormat.xm;
1445                     _sampleRate = 44100.0f;
1446                     _numChannels = 2;
1447                     _lengthInFrames = audiostreamUnknownLength;
1448                     return;
1449                 }
1450 
1451                 not_a_xm:
1452                 assert(_xmDecoder == null);
1453                 free(_xmContent);
1454                 _xmContent = null;
1455             }
1456         } 
1457 
1458         version(decodeMOD)
1459         {
1460             {
1461                 // we need either the first 1084 or 600 bytes if available
1462                 _io.seek(0, false, userData);
1463                 long lenBytes = _io.getFileLength(userData);
1464                 if (lenBytes >= 600)
1465                 {
1466                     int headerBytes = lenBytes > 1084 ? 1084 : cast(int)lenBytes;
1467 
1468                     ubyte[1084] header;
1469                     int bytes = _io.read(header.ptr, headerBytes, userData);
1470 
1471                     if (_pocketmod_ident(null, header.ptr, bytes))
1472                     {
1473                         // This is a MOD, allocate a proper context, and read the whole file.
1474                         _modDecoder = cast(pocketmod_context*) malloc(pocketmod_context.sizeof);
1475 
1476                         // Read whole .mod in a buffer, since the decoder work all from memory
1477                         _io.seek(0, false, userData);
1478                         _modContent.reallocBuffer(cast(size_t)lenBytes);
1479                         bytes = _io.read(_modContent.ptr, cast(int)lenBytes, userData);
1480 
1481                         if (pocketmod_init(_modDecoder, _modContent.ptr, bytes, 44100))
1482                         {
1483                             _format = AudioFileFormat.mod;
1484                             _sampleRate = 44100.0f;
1485                             _numChannels = 2;
1486                             _lengthInFrames = audiostreamUnknownLength;
1487                             return;
1488                         }
1489                     }
1490                 }
1491             }
1492         }
1493 
1494         _format = AudioFileFormat.unknown;
1495         _sampleRate = float.nan;
1496         _numChannels = 0;
1497         _lengthInFrames = -1;
1498 
1499         throw mallocNew!Exception("Cannot decode stream: unrecognized encoding.");
1500     }
1501 
1502     void startEncoding(AudioFileFormat format, float sampleRate, int numChannels, EncodingOptions options) @nogc
1503     { 
1504         _format = format;
1505         _sampleRate = sampleRate;
1506         _numChannels = numChannels;
1507 
1508         final switch(format) with (AudioFileFormat)
1509         {
1510             case mp3:
1511                 throw mallocNew!Exception("Unsupported encoding format: MP3");
1512             case flac:
1513                 throw mallocNew!Exception("Unsupported encoding format: FLAC");
1514             case ogg:
1515                 throw mallocNew!Exception("Unsupported encoding format: OGG");
1516             case opus:
1517                 throw mallocNew!Exception("Unsupported encoding format: Opus");
1518             case mod:
1519                 throw mallocNew!Exception("Unsupported encoding format: MOD");
1520             case xm:
1521                 throw mallocNew!Exception("Unsupported encoding format: XM");
1522             case wav:
1523             {
1524                 // Note: fractional sample rates not supported by WAV, signal an integer one
1525                 int isampleRate = cast(int)(sampleRate + 0.5f);
1526 
1527                 WAVEncoder.Format wavfmt;
1528                 final switch (options.sampleFormat)
1529                 {
1530                     case AudioSampleFormat.s8:   wavfmt = WAVEncoder.Format.s8; break;
1531                     case AudioSampleFormat.s16:  wavfmt = WAVEncoder.Format.s16le; break;
1532                     case AudioSampleFormat.s24:  wavfmt = WAVEncoder.Format.s24le; break;
1533                     case AudioSampleFormat.fp32: wavfmt = WAVEncoder.Format.fp32le; break;
1534                     case AudioSampleFormat.fp64: wavfmt = WAVEncoder.Format.fp64le; break;
1535                 }
1536                 _wavEncoder = mallocNew!WAVEncoder(_io, userData, isampleRate, numChannels, wavfmt, options.enableDither);
1537                 break;
1538             }
1539             case unknown:
1540                 throw mallocNew!Exception("Can't encode using 'unknown' coding");
1541         }        
1542     }   
1543 
1544     void finalizeEncodingIfNeeded() @nogc
1545     {
1546         if (_io && (_io.write !is null)) // if we have been encoding something
1547         {
1548             finalizeEncoding();
1549         }
1550     }
1551 }
1552 
1553 // AudioStream should be able to go on a smallish 32-bit stack,
1554 // and malloc the rest on the heap when needed.
1555 static assert(AudioStream.sizeof <= 256); 
1556 
1557 private: // not meant to be imported at all
1558 
1559 
1560 
1561 // Internal object for audio-formats
1562 
1563 
1564 // File callbacks
1565 // The file callbacks are using the C stdlib.
1566 
1567 struct FileContext // this is what is passed to I/O when used in file mode
1568 {
1569     // Used when streaming of writing a file
1570     FILE* file = null;
1571 
1572     // Size of the file in bytes, only used when reading/writing a file.
1573     long fileSize;
1574 
1575     // Initialize this context
1576     void initialize(const(char)[] path, bool forWrite) @nogc
1577     {
1578         CString strZ = CString(path);
1579         file = fopen(strZ.storage, forWrite ? "wb".ptr : "rb".ptr);
1580         if (file is null)
1581             throw mallocNew!Exception("File not found");
1582         // finds the size of the file
1583         fseek(file, 0, SEEK_END);
1584         fileSize = ftell(file);
1585         fseek(file, 0, SEEK_SET);
1586     }
1587 }
1588 
1589 long file_tell(void* userData) nothrow @nogc
1590 {
1591     FileContext* context = cast(FileContext*)userData;
1592     return ftell(context.file);
1593 }
1594 
1595 bool file_seek(long offset, bool relative, void* userData) nothrow @nogc
1596 {
1597     FileContext* context = cast(FileContext*)userData;
1598     assert(offset <= int.max);
1599     int r = fseek(context.file, cast(int)offset, relative ? SEEK_CUR : SEEK_SET); // Limitations: file larger than 2gb not supported
1600     return r == 0;
1601 }
1602 
1603 long file_getFileLength(void* userData) nothrow @nogc
1604 {
1605     FileContext* context = cast(FileContext*)userData;
1606     return context.fileSize;
1607 }
1608 
1609 int file_read(void* outData, int bytes, void* userData) nothrow @nogc
1610 {
1611     FileContext* context = cast(FileContext*)userData;
1612     size_t bytesRead = fread(outData, 1, bytes, context.file);
1613     return cast(int)bytesRead;
1614 }
1615 
1616 int file_write(void* inData, int bytes, void* userData) nothrow @nogc
1617 {
1618     FileContext* context = cast(FileContext*)userData;
1619     size_t bytesWritten = fwrite(inData, 1, bytes, context.file);
1620     return cast(int)bytesWritten;
1621 }
1622 
1623 bool file_skip(int bytes, void* userData) nothrow @nogc
1624 {
1625     FileContext* context = cast(FileContext*)userData;
1626     return (0 == fseek(context.file, bytes, SEEK_CUR));
1627 }
1628 
1629 bool file_flush(void* userData) nothrow @nogc
1630 {
1631     FileContext* context = cast(FileContext*)userData;
1632     return ( fflush(context.file) == 0 );
1633 }
1634 
1635 // Memory read callback
1636 // Using the read buffer instead
1637 
1638 struct MemoryContext
1639 {
1640     bool bufferIsOwned;
1641     bool bufferCanGrow;
1642 
1643     // Buffer
1644     ubyte* buffer = null;
1645 
1646     size_t size;     // current buffer size
1647     size_t cursor;   // where we are in the buffer
1648     size_t capacity; // max buffer size before realloc
1649 
1650     void initializeWithConstantInput(const(ubyte)* data, size_t length) nothrow @nogc
1651     {
1652         // Make a copy of the input buffer, since it could be temporary.
1653         bufferIsOwned = true;
1654         bufferCanGrow = false;
1655 
1656         buffer = mallocDup(data[0..length]).ptr; // Note: the copied slice is made mutable.
1657         size = length;
1658         cursor = 0;
1659         capacity = length;
1660     }
1661 
1662     void initializeWithExternalOutputBuffer(ubyte* data, size_t length) nothrow @nogc
1663     {
1664         bufferIsOwned = false;
1665         bufferCanGrow = false;
1666         buffer = data;
1667         size = 0;
1668         cursor = 0;
1669         capacity = length;
1670     }
1671 
1672     void initializeWithInternalGrowableBuffer() nothrow @nogc
1673     {
1674         bufferIsOwned = true;
1675         bufferCanGrow = true;
1676         buffer = null;
1677         size = 0;
1678         cursor = 0;
1679         capacity = 0;
1680     }
1681 
1682     ~this()
1683     {
1684         if (bufferIsOwned)
1685         {
1686             if (buffer !is null)
1687             {
1688                 free(buffer);
1689                 buffer = null;
1690             }
1691         }
1692     }
1693 }
1694 
1695 long memory_tell(void* userData) nothrow @nogc
1696 {
1697     MemoryContext* context = cast(MemoryContext*)userData;
1698     return cast(long)(context.cursor);
1699 }
1700 
1701 bool memory_seek(long offset, bool relative, void* userData) nothrow @nogc
1702 {
1703     MemoryContext* context = cast(MemoryContext*)userData;    
1704     if (relative) offset += context.cursor;
1705     if (offset < 0)
1706         return false;
1707 
1708     bool r = true;
1709     if (offset >= context.size) // can't seek past end of buffer, stick to the end so that read return 0 byte
1710     {
1711         offset = context.size;
1712         r = false;
1713     }
1714     context.cursor = cast(size_t)offset; // Note: memory streams larger than 2gb not supported
1715     return r;
1716 }
1717 
1718 long memory_getFileLength(void* userData) nothrow @nogc
1719 {
1720     MemoryContext* context = cast(MemoryContext*)userData;
1721     return cast(long)(context.size);
1722 }
1723 
1724 int memory_read(void* outData, int bytes, void* userData) nothrow @nogc
1725 {
1726     MemoryContext* context = cast(MemoryContext*)userData;
1727     size_t cursor = context.cursor;
1728     size_t size = context.size;
1729     size_t available = size - cursor;
1730     if (bytes < available)
1731     {
1732         outData[0..bytes] = context.buffer[cursor..cursor + bytes];
1733         context.cursor += bytes;
1734         return bytes;
1735     }
1736     else
1737     {
1738         outData[0..available] = context.buffer[cursor..cursor + available];
1739         context.cursor = context.size;
1740         return cast(int)available;
1741     }
1742 }
1743 
1744 int memory_write_limited(void* inData, int bytes, void* userData) nothrow @nogc
1745 {
1746     MemoryContext* context = cast(MemoryContext*)userData;
1747     size_t cursor = context.cursor;
1748     size_t size = context.size;
1749     size_t available = size - cursor;
1750     ubyte* buffer = context.buffer;
1751     ubyte* source = cast(ubyte*) inData;
1752 
1753     if (cursor + bytes > available)
1754     {
1755         bytes = cast(int)(available - cursor);       
1756     }
1757 
1758     buffer[cursor..(cursor + bytes)] = source[0..bytes];
1759     context.size += bytes;
1760     context.cursor += bytes;
1761     return bytes;
1762 }
1763 
1764 int memory_write_append(void* inData, int bytes, void* userData) nothrow @nogc
1765 {
1766     MemoryContext* context = cast(MemoryContext*)userData;
1767     size_t cursor = context.cursor;
1768     size_t size = context.size;
1769     size_t available = size - cursor;
1770     ubyte* buffer = context.buffer;
1771     ubyte* source = cast(ubyte*) inData;
1772 
1773     if (cursor + bytes > available)
1774     {
1775         size_t oldSize = context.capacity;
1776         size_t newSize = cursor + bytes;
1777         if (newSize < oldSize * 2 + 1) 
1778             newSize = oldSize * 2 + 1;
1779         buffer = cast(ubyte*) realloc(buffer, newSize);
1780         context.capacity = newSize;
1781 
1782         assert( cursor + bytes <= available );
1783     }
1784 
1785     buffer[cursor..(cursor + bytes)] = source[0..bytes];
1786     context.size += bytes;
1787     context.cursor += bytes;
1788     return bytes;
1789 }
1790 
1791 bool memory_skip(int bytes, void* userData) nothrow @nogc
1792 {
1793     MemoryContext* context = cast(MemoryContext*)userData;
1794     context.cursor += bytes;
1795     return context.cursor <= context.size;
1796 }
1797 
1798 bool memory_flush(void* userData) nothrow @nogc
1799 {
1800     // do nothing, no flushign to do for memory
1801     return true;
1802 }
1803 
1804 
1805 // Decoder context
1806 struct DecoderContext
1807 {
1808     void* userDataIO;
1809     IOCallbacks* callbacks;
1810 }
1811 
1812 // MP3 decoder read callback
1813 static int mp3ReadDelegate(void[] buf, void* userDataDecoder) @nogc nothrow
1814 {
1815     DecoderContext* context = cast(DecoderContext*) userDataDecoder;
1816 
1817     // read bytes into the buffer, return number of bytes read or 0 for EOF, -1 on error
1818     // will never be called with empty buffer, or buffer more than 128KB
1819 
1820     int bytes = context.callbacks.read(buf.ptr, cast(int)(buf.length), context.userDataIO);
1821     return bytes;
1822 }
1823 
1824 
1825 // FLAC decoder read callbacks
1826 
1827 size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead) @nogc nothrow
1828 {
1829     DecoderContext* context = cast(DecoderContext*) pUserData;
1830     return context.callbacks.read(pBufferOut, cast(int)(bytesToRead), context.userDataIO);
1831 }
1832 
1833 bool flac_seek(void* pUserData, int offset, drflac_seek_origin origin) @nogc nothrow
1834 {
1835     DecoderContext* context = cast(DecoderContext*) pUserData;
1836     if (origin == drflac_seek_origin_start)
1837     {
1838         context.callbacks.seek(offset, false, context.userDataIO);
1839     }
1840     else if (origin == drflac_seek_origin_current)
1841     {
1842         context.callbacks.seek(offset, true, context.userDataIO);
1843     }
1844     return true;
1845 }
1846 
1847 // MP3 decoder read callbacks
1848 
1849 size_t mp3_io_read(void *buf, size_t size, void *user_data) @nogc nothrow
1850 {
1851     DecoderContext* context = cast(DecoderContext*) user_data;
1852     return context.callbacks.read(buf, cast(int)(size), context.userDataIO);
1853 }
1854 
1855 int mp3_io_seek(ulong position, void *user_data) @nogc nothrow
1856 {
1857     DecoderContext* context = cast(DecoderContext*) user_data;
1858     context.callbacks.seek(position, false, context.userDataIO);
1859     return 0; // doesn't detect seeking errors
1860 }