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