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                     if (_flacPositionFrame == _lengthInFrames)
434                         return 0; // internally the decoder might be elsewhere
435 
436                     int* integerData = cast(int*)outData;
437 
438                     int samples = cast(int) drflac_read_s32(_flacDecoder, frames * _numChannels, integerData);
439 
440                     // "Samples are always output as interleaved signed 32-bit PCM."
441                     // Convert to float with type-punning. Note that this looses some precision.
442                     double factor = 1.0 / int.max;
443                     foreach(n; 0..samples)
444                     {
445                         outData[n] = integerData[n]  * factor;
446                     }
447                     int framesDecoded = samples / _numChannels;
448                     _flacPositionFrame += framesDecoded;
449                     return framesDecoded;
450                 }
451                 else
452                 {
453                     assert(false); // Impossible
454                 }
455             }
456             
457             case AudioFileFormat.ogg:
458             {
459                 version(decodeOGG)
460                 {
461                     assert(_oggHandle !is null);
462                     int framesRead = stb_vorbis_get_samples_float_interleaved(_oggHandle, _numChannels, outData, frames * _numChannels);
463                     _oggPositionFrame += framesRead;
464                     return framesRead;
465                 }
466                 else
467                 {
468                     assert(false); // Impossible
469                 }
470             }
471 
472             case AudioFileFormat.mp3:
473             {
474                 version(decodeMP3)
475                 {
476                     assert(_mp3DecoderNew !is null);
477 
478                     int samplesNeeded = frames * _numChannels;
479                     int result = cast(int) mp3dec_ex_read(_mp3DecoderNew, outData, samplesNeeded);
480                     if (result < 0) // error
481                         return 0;
482                     return result / _numChannels;
483                 }
484                 else
485                 {
486                     assert(false); // Impossible
487                 }
488             }
489             case AudioFileFormat.wav:
490                 version(decodeWAV)
491                 {
492                     assert(_wavDecoder !is null);
493                     int readFrames = _wavDecoder.readSamples!float(outData, frames); 
494                     return readFrames;
495                 }
496                 else
497                 {
498                     assert(false); // Impossible
499                 }
500 
501             case AudioFileFormat.xm:
502                 version(decodeXM)
503                 {
504                     assert(_xmDecoder !is null);
505 
506                     if (xm_get_loop_count(_xmDecoder) >= 1)
507                         return 0; // song is finished
508 
509                     xm_generate_samples(_xmDecoder, outData, frames);
510                     return frames; // Note: XM decoder pads end with zeroes.
511                 }
512                 else
513                 {
514                     assert(false); // Impossible
515                 }
516 
517             case AudioFileFormat.mod:
518                 version(decodeMOD)
519                 {
520                     if (pocketmod_loop_count(_modDecoder) >= 1)
521                         return 0; // end stream after MOD finishes, looping not supported
522                     assert(_modDecoder !is null);
523                     int bytesReturned = pocketmod_render(_modDecoder, outData, frames * 2 * 4);
524                     assert((bytesReturned % 8) == 0);
525                     return bytesReturned / 8;
526                 }
527                 else
528                 {
529                     assert(false); // Impossible
530                 }
531 
532             case AudioFileFormat.unknown:
533                 // One shouldn't ever get there, since in this case
534                 // opening has failed.
535                 assert(false);
536         }
537     }
538     ///ditto
539     int readSamplesFloat(float[] outData) @nogc
540     {
541         assert( (outData.length % _numChannels) == 0);
542         return readSamplesFloat(outData.ptr, cast(int)(outData.length / _numChannels) );
543     }
544 
545     /// Read interleaved double samples in the given buffer `outData`.
546     /// 
547     /// Params:
548     ///     outData Buffer where to put decoded samples. Samples are arranged in an interleaved fashion.
549     ///             Must have room for `frames` x `getNumChannels()` samples.
550     ///             For a stereo file, the output data will contain LRLRLR... repeated `result` times.
551     ///
552     ///     frames The number of multichannel frames to be read. 
553     ///            A frame is `getNumChannels()` samples.
554     ///
555     /// Note: the only formats to possibly take advantage of double decoding are WAV and FLAC.
556     ///
557     /// Returns: Number of actually read frames. Multiply by `getNumChannels()` to get the number of read samples.
558     ///          When that number is less than `frames`, it means the stream is done decoding, or that there was a decoding error.
559     ///
560     /// TODO: once this returned less than `frames`, are we guaranteed we can keep calling that and it returns 0?
561     int readSamplesDouble(double* outData, int frames) @nogc
562     {
563         assert(isOpenForReading());
564 
565         switch(_format)
566         {
567             case AudioFileFormat.wav:
568                 version(decodeWAV)
569                 {
570                     assert(_wavDecoder !is null);
571                     int readFrames = _wavDecoder.readSamples!double(outData, frames); 
572                     return readFrames;
573                 }
574                 else
575                 {
576                     assert(false); // Impossible
577                 }
578 
579             case AudioFileFormat.flac:
580             {
581                 version(decodeFLAC)
582                 {
583                     assert(_flacDecoder !is null);
584 
585                     if (_flacPositionFrame == _lengthInFrames)
586                         return 0; // internally the decoder might be elsewhere
587 
588                     // use second half of the output buffer as temporary integer decoding area
589                     int* integerData = (cast(int*)outData) + frames;
590                     int samples = cast(int) drflac_read_s32(_flacDecoder, frames, integerData);
591 
592                     // "Samples are always output as interleaved signed 32-bit PCM."
593                     // Converting to double doesn't loose mantissa, unlike float.
594                     double factor = 1.0 / int.max;
595                     foreach(n; 0..samples)
596                     {
597                         outData[n] = integerData[n]  * factor;
598                     }
599                     int framesDecoded = samples / _numChannels;
600                     _flacPositionFrame += framesDecoded;
601                     return framesDecoded;
602                 }
603                 else
604                 {
605                     assert(false); // Impossible
606                 }
607             }
608 
609             case AudioFileFormat.unknown:
610                 // One shouldn't ever get there
611                 assert(false);
612 
613             default:
614                 // Decode to float buffer, and then convert
615                 if (_floatDecodeBuf.length < frames * _numChannels)
616                     _floatDecodeBuf.reallocBuffer(frames * _numChannels);
617                 int read = readSamplesFloat(_floatDecodeBuf.ptr, frames);
618                 for (int n = 0; n < read * _numChannels; ++n)
619                     outData[n] = _floatDecodeBuf[n];
620                 return read;
621         }
622     }
623     ///ditto
624     int readSamplesDouble(double[] outData) @nogc
625     {
626         assert( (outData.length % _numChannels) == 0);
627         return readSamplesDouble(outData.ptr, cast(int)(outData.length / _numChannels) );
628     }
629 
630     /// Write interleaved float samples to the stream, from the given buffer `inData[0..frames]`.
631     /// 
632     /// Params:
633     ///     inData Buffer of interleaved samples to append to the stream.
634     ///            Must contain `frames` x `getNumChannels()` samples.
635     ///            For a stereo file, `inData` contains LRLRLR... repeated `frames` times.
636     ///
637     ///     frames The number of frames to append to the stream.
638     ///            A frame is `getNumChannels()` samples.
639     ///
640     /// Returns: Number of actually written frames. Multiply by `getNumChannels()` to get the number of written samples.
641     ///          When that number is less than `frames`, it means the stream had a write error.
642     int writeSamplesFloat(float* inData, int frames) nothrow @nogc
643     {
644         assert(_io && _io.write !is null);
645 
646         final switch(_format)
647         {
648             case AudioFileFormat.mp3:
649             case AudioFileFormat.flac:
650             case AudioFileFormat.ogg:
651             case AudioFileFormat.opus:
652             case AudioFileFormat.mod:
653             case AudioFileFormat.xm:
654             case AudioFileFormat.unknown:
655             {
656                 assert(false); // Shouldn't have arrived here, such encoding aren't supported.
657             }
658             case AudioFileFormat.wav:
659             {
660                 version(encodeWAV)
661                 {
662                     return _wavEncoder.writeSamples(inData, frames);
663                 }
664                 else
665                 {
666                     assert(false, "no support for WAV encoding");
667                 }
668             }
669         }
670     }
671     ///ditto
672     int writeSamplesFloat(float[] inData) nothrow @nogc
673     {
674         assert( (inData.length % _numChannels) == 0);
675         return writeSamplesFloat(inData.ptr, cast(int)(inData.length / _numChannels));
676     }
677 
678     /// Write interleaved double samples to the stream, from the given buffer `inData[0..frames]`.
679     /// 
680     /// Params:
681     ///     inData Buffer of interleaved samples to append to the stream.
682     ///            Must contain `frames` x `getNumChannels()` samples.
683     ///            For a stereo file, `inData` contains LRLRLR... repeated `frames` times.
684     ///
685     ///     frames The number of frames to append to the stream.
686     ///            A frame is `getNumChannels()` samples.
687     ///
688     /// Note: this only does something if the output format is WAV and was setup for 64-bit output.
689     ///
690     /// Returns: Number of actually written frames. Multiply by `getNumChannels()` to get the number of written samples.
691     ///          When that number is less than `frames`, it means the stream had a write error.
692     int writeSamplesDouble(double* inData, int frames) nothrow @nogc
693     {
694         assert(_io && _io.write !is null);
695 
696         switch(_format)
697         {
698             case AudioFileFormat.unknown:
699                 // One shouldn't ever get there
700                 assert(false);
701 
702             case AudioFileFormat.wav:
703                 {
704                     version(encodeWAV)
705                     {
706                         return _wavEncoder.writeSamples(inData, frames);
707                     }
708                     else
709                     {
710                         assert(false, "no support for WAV encoding");
711                     }
712                 }
713 
714             default:
715                 // Decode to float buffer, and then convert
716                 if (_floatDecodeBuf.length < frames * _numChannels)
717                     _floatDecodeBuf.reallocBuffer(frames * _numChannels);
718 
719                 for (int n = 0; n < frames * _numChannels; ++n)
720                     _floatDecodeBuf[n] = inData[n];
721                
722                 return writeSamplesFloat(_floatDecodeBuf.ptr, frames);
723         }
724     }
725     ///ditto
726     int writeSamplesDouble(double[] inData) nothrow @nogc
727     {
728         assert( (inData.length % _numChannels) == 0);
729         return writeSamplesDouble(inData.ptr, cast(int)(inData.length / _numChannels));
730     }
731 
732     // -----------------------------------------------------------------------------------------------------
733     // <module functions>
734     // Those tracker module-specific functions below can only be called when `isModule()` returns `true`.
735     // Additionally, seeking function can only be called if `canSeek()` also returns `true`.
736     // -----------------------------------------------------------------------------------------------------
737 
738     /// Length. Returns the amount of patterns in the module
739     /// Formats that support this: MOD, XM.
740     int countModulePatterns() 
741     {
742         assert(isOpenForReading() && isModule());
743         final switch(_format) with (AudioFileFormat)
744         {
745             case mp3: 
746             case flac:
747             case ogg:
748             case opus:
749             case wav:
750             case unknown:
751                 assert(false);
752             case mod:
753                 return _modDecoder.num_patterns;
754             case xm:
755                 return xm_get_number_of_patterns(_xmDecoder);
756         }
757     }
758 
759     /// Length. Returns the amount of PLAYED patterns in the module
760     /// Formats that support this: MOD, XM.
761     int getModuleLength() 
762     {
763         assert(isOpenForReading() && isModule());
764         final switch(_format) with (AudioFileFormat)
765         {
766             case mp3: 
767             case flac:
768             case ogg:
769             case opus:
770             case wav:
771             case unknown:
772                 assert(false);
773             case mod:
774                 return _modDecoder.length;
775             case xm:
776                 return xm_get_module_length(_xmDecoder);
777         }
778     }
779 
780     /// Tell. Returns amount of rows in a pattern.
781     /// Formats that support this: MOD, XM.
782     /// Returns: -1 on error. Else, number of patterns.
783     int rowsInPattern(int pattern) 
784     {
785         assert(isOpenForReading() && isModule());
786         final switch(_format) with (AudioFileFormat)
787         {
788             case mp3: 
789             case flac:
790             case ogg:
791             case opus:
792             case wav:
793             case unknown:
794                 assert(false);
795 
796             case mod:
797                 // According to http://lclevy.free.fr/mo3/mod.txt
798                 // there's 64 lines (aka rows) per pattern.
799                 // TODO: error checking, make sure no out of bounds happens.
800                 return 64;
801 
802             case xm:
803             {
804                 int numPatterns = xm_get_number_of_patterns(_xmDecoder);
805                 if (pattern < 0 || pattern >= numPatterns)
806                     return -1;
807 
808                 return xm_get_number_of_rows(_xmDecoder, cast(ushort) pattern);
809             }
810         }
811     }
812 
813     /// Tell. Returns the current playing pattern id
814     /// Formats that support this: MOD, XM
815     int tellModulePattern() 
816     {
817         assert(isOpenForReading() && isModule());
818         final switch(_format) with (AudioFileFormat)
819         {
820             case mp3: 
821             case flac:
822             case ogg:
823             case opus:
824             case wav:
825             case unknown:
826                 assert(false);
827             case mod:
828                 return _modDecoder.pattern;
829             case xm:
830                 return _xmDecoder.current_table_index;
831         }
832     }
833 
834     /// Tell. Returns the current playing row id
835     /// Formats that support this: MOD, XM
836     int tellModuleRow() 
837     {
838         assert(isOpenForReading() && isModule());
839         final switch(_format) with (AudioFileFormat)
840         {
841             case mp3: 
842             case flac:
843             case ogg:
844             case opus:
845             case wav:
846             case unknown:
847                 assert(false);
848             case mod:
849                 return _modDecoder.line;
850             case xm:
851                 return _xmDecoder.current_row;
852         }
853     }
854 
855     /// Playback info. Returns the amount of multi-channel frames remaining in the current playing pattern.
856     /// Formats that support this: MOD
857     int framesRemainingInPattern() 
858     {
859         assert(isOpenForReading() && isModule());
860         final switch(_format) with (AudioFileFormat)
861         {
862             case mp3: 
863             case flac:
864             case ogg:
865             case opus:
866             case wav:
867             case unknown:
868                 assert(false);
869 
870             case mod:
871                 return pocketmod_count_remaining_samples(_modDecoder);
872             case xm:
873                 return xm_count_remaining_samples(_xmDecoder);
874         }
875     }
876 
877     /// Seeking. Subsequent reads start from pattern + row, 0 index
878     /// Only available for input streams.
879     /// Formats that support seeking per pattern/row: MOD, XM
880     /// Returns: `true` in case of success.
881     bool seekPosition(int pattern, int row) 
882     {
883         assert(isOpenForReading() && isModule() && canSeek());
884         final switch(_format) with (AudioFileFormat)
885         {
886             case mp3: 
887             case flac:
888             case ogg:
889             case opus:
890             case wav:
891             case unknown:
892                 assert(false);
893 
894             case mod:
895                 // NOTE: This is untested.
896                 return pocketmod_seek(_modDecoder, pattern, row, 0);
897 
898             case xm:
899                 return xm_seek(_xmDecoder, pattern, row, 0);
900         }
901     }
902 
903     // -----------------------------------------------------------------------------------------------------
904     // </module functions>
905     // -----------------------------------------------------------------------------------------------------
906 
907     // -----------------------------------------------------------------------------------------------------
908     // <non-module functions>
909     // Those functions below can't be used for tracker module formats, because there is no real concept of 
910     // absolute position in these formats.
911     // -----------------------------------------------------------------------------------------------------
912 
913     /// Seeking. Subsequent reads start from multi-channel frame index `frames`.
914     /// Only available for input streams, for streams whose `canSeek()` returns `true`.
915     /// Warning: `seekPosition(lengthInFrames)` is Undefined Behaviour for now. (it works in MP3
916     bool seekPosition(int frame)
917     {
918         assert(isOpenForReading() && !isModule() && canSeek()); // seeking doesn't have the same sense with modules.
919         final switch(_format) with (AudioFileFormat)
920         {
921             case mp3: 
922                 version(decodeMP3)
923                 {
924                     assert(_lengthInFrames != audiostreamUnknownLength);
925                     if (frame < 0 || frame > _lengthInFrames)
926                         return false;
927                     return (mp3dec_ex_seek(_mp3DecoderNew, frame * _numChannels) == 0);
928                 }
929                 else
930                     assert(false);
931             case flac:
932                 version(decodeFLAC)
933                 {
934                     if (frame < 0 || frame > _lengthInFrames)
935                         return false;
936                     if (_flacPositionFrame == frame)
937                         return true;
938 
939                     // Note: seeking + FLAC is a dark side of that library. 
940                     // I'm not entirely sure we are handling all cases perfectly.
941                     // But weren't able to fault the current situation.
942                     // Would probably be easier to re-tanslate drflac if a problem arised.
943 
944                     bool success = drflac_seek_to_sample(_flacDecoder, frame * _numChannels);
945                     if (success || frame == _lengthInFrames) // always succeed if end of stream is requested
946                         _flacPositionFrame = frame;
947                     return success;
948                 }
949                 else
950                     assert(false);
951             case ogg:
952                 version(decodeOGG)
953                 {
954                     if (_oggPositionFrame == frame)
955                         return true;
956 
957                     if (_oggPositionFrame == _lengthInFrames)
958                     {
959                         // When the OGG stream is finished, and an earlier position is detected, 
960                         // the OGG decoder has to be restarted
961                         assert(_oggHandle !is null);
962                         cleanUpCodecs();
963                         assert(_oggHandle is null);
964                         startDecoding();
965                         assert(_oggHandle !is null);
966                     }
967 
968                     if (stb_vorbis_seek(_oggHandle, frame) == 1)
969                     {
970                         _oggPositionFrame = frame;
971                         return true;
972                     }
973                     else
974                       return false;
975                 }
976                 else 
977                     assert(false);
978             case opus:
979                 version(decodeOPUS)
980                 {
981                     if (frame < 0 || frame > _lengthInFrames)
982                         return false;
983                     long where = _opusDecoder.ogg.seekPCM(frame);
984                     _opusPositionFrame = where;
985                     int toSkip = cast(int)(frame - where);
986 
987                     // skip remaining samples for sample-accurate seeking
988                     // Note: this also updates _opusPositionFrame
989                     int skipped = readSamplesFloat(null, cast(int) toSkip);
990                     // TODO: if decoding `toSkip` samples failed, restore previous state?
991                     return skipped == toSkip;
992                 }
993                 else 
994                     assert(false);
995 
996             case mod:
997             case xm:
998                 assert(false);
999 
1000             case wav:
1001                 version(decodeWAV)
1002                     return _wavDecoder.seekPosition(frame);
1003                 else
1004                     assert(false);
1005             case unknown:
1006                 assert(false);
1007 
1008         }
1009     }
1010 
1011     /// Tell. Returns the current position in multichannel frames. -1 on error.
1012     int tellPosition()
1013     {
1014         assert(isOpenForReading() && !isModule() && canSeek()); // seeking doesn't have the same sense with modules.
1015         final switch(_format) with (AudioFileFormat)
1016         {
1017             case mp3: 
1018                 version(decodeMP3)
1019                 {
1020                     return cast(int) _mp3DecoderNew.cur_sample / _numChannels;
1021                 }
1022                 else
1023                     assert(false);
1024             case flac:
1025                 version(decodeFLAC)
1026                 {
1027                     // Implemented externally since drflac is impenetrable.
1028                     return cast(int) _flacPositionFrame;
1029                 }
1030                 else
1031                     assert(false);
1032             case ogg:
1033                 version(decodeOGG)
1034                     return cast(int) _oggPositionFrame;
1035                 else 
1036                     assert(false);
1037 
1038             case opus:
1039                 version(decodeOPUS)
1040                 {
1041                     return cast(int) _opusPositionFrame; // implemented externally
1042                 }
1043                 else 
1044                     assert(false);
1045 
1046             case wav:
1047                 version(decodeWAV)
1048                     return _wavDecoder.tellPosition();
1049                 else
1050                     assert(false);
1051 
1052             case mod:
1053             case xm:
1054             case unknown:
1055                 assert(false);
1056 
1057         }
1058     }
1059 
1060 
1061     // -----------------------------------------------------------------------------------------------------
1062     // </non-module functions>
1063     // -----------------------------------------------------------------------------------------------------
1064 
1065     /// Call `fflush()` on written samples, if any. 
1066     /// It is only useful for streamable output formats, that may want to flush things to disk.
1067     void flush() nothrow @nogc
1068     {
1069         assert( _io && (_io.write !is null) );
1070         _io.flush(userData);
1071     }
1072     
1073     /// Finalize encoding. After finalization, further writes are not possible anymore
1074     /// however the stream is considered complete and valid for storage.
1075     void finalizeEncoding() @nogc 
1076     {
1077         // If you crash here, it's because `finalizeEncoding` has been called twice.
1078         assert(isOpenForWriting());
1079 
1080         final switch(_format) with (AudioFileFormat)
1081         {
1082             case mp3:
1083             case flac:
1084             case ogg:
1085             case opus:
1086             case mod:
1087             case xm:
1088                 assert(false); // unsupported output encoding
1089             case wav:
1090                 { 
1091                     _wavEncoder.finalizeEncoding();
1092                     break;
1093                 }
1094             case unknown:
1095                 assert(false);
1096         }
1097         _io.write = null; // prevents further encodings
1098     }
1099 
1100     // Finalize encoding and get internal buffer.
1101     // This can be called multiple times, in which cases the stream is finalized only the first time.
1102     const(ubyte)[] finalizeAndGetEncodedResult() @nogc
1103     {
1104         // only callable while appending, else it's a programming error
1105         assert( (memoryContext !is null) && ( memoryContext.bufferCanGrow ) );
1106 
1107         finalizeEncodingIfNeeded(); 
1108         return memoryContext.buffer[0..memoryContext.size];
1109     }
1110 
1111 private:
1112     IOCallbacks* _io;
1113 
1114     // This type of context is a closure to remember where the data is.
1115     void* userData; // is equal to either fileContext or memoryContext
1116     FileContext* fileContext;
1117     MemoryContext* memoryContext;
1118 
1119     // This type of context is a closure to remember where _io and user Data is.
1120     DecoderContext* _decoderContext;
1121 
1122     AudioFileFormat _format;
1123     float _sampleRate; 
1124     int _numChannels;
1125     long _lengthInFrames;
1126 
1127     float[] _floatDecodeBuf;
1128 
1129     // Decoders
1130     version(decodeMP3)
1131     {
1132         mp3dec_ex_t* _mp3DecoderNew; // allocated on heap since it's a 16kb object
1133         mp3dec_io_t* _mp3io;
1134     }
1135     version(decodeFLAC)
1136     {
1137         drflac* _flacDecoder;
1138         long _flacPositionFrame;
1139     }
1140     version(decodeOGG)
1141     {
1142         ubyte[] _oggBuffer; // all allocations from the ogg decoder
1143         stb_vorbis* _oggHandle;
1144         long _oggPositionFrame;
1145     }
1146     version(decodeWAV)
1147     {
1148         WAVDecoder _wavDecoder;
1149     }
1150     version(decodeMOD)
1151     {
1152         pocketmod_context* _modDecoder = null;
1153         ubyte[] _modContent = null; // whole buffer, copied
1154     }
1155     version(decodeXM)
1156     {
1157         xm_context_t* _xmDecoder = null;
1158         ubyte* _xmContent = null;
1159     }
1160 
1161     version(decodeOPUS)
1162     {
1163         OpusFile _opusDecoder;
1164         short[] _opusBuffer;
1165         long _opusPositionFrame;
1166     }
1167 
1168     // Encoder
1169     version(encodeWAV)
1170     {
1171         WAVEncoder _wavEncoder;
1172     }
1173 
1174     // Clean-up encoder/decoder-related data, but not I/O related things. Useful to restart the decoder.
1175     // After callign that, you can call `startDecoder` again.
1176     void cleanUpCodecs() @nogc
1177     {
1178         // Write the last needed bytes if needed
1179         finalizeEncodingIfNeeded();
1180 
1181         version(decodeMP3)
1182         {
1183             if (_mp3DecoderNew !is null)
1184             {
1185                 mp3dec_ex_close(_mp3DecoderNew);
1186                 free(_mp3DecoderNew);
1187                 _mp3DecoderNew = null;
1188             }
1189             if (_mp3io !is null)
1190             {
1191                 free(_mp3io);
1192                 _mp3io = null;
1193             }
1194         }
1195 
1196         version(decodeFLAC)
1197         {
1198             if (_flacDecoder !is null)
1199             {
1200                 drflac_close(_flacDecoder);
1201                 _flacDecoder = null;
1202                 _flacPositionFrame = 0;
1203             }
1204         }
1205 
1206         version(decodeOGG)
1207         {
1208             if (_oggHandle !is null)
1209             {
1210                 stb_vorbis_close(_oggHandle);
1211                 _oggHandle = null;
1212                 _oggPositionFrame = 0;
1213             }
1214             _oggBuffer.reallocBuffer(0);
1215         }
1216 
1217         version(decodeOPUS)
1218         {
1219             if (_opusDecoder !is null)
1220             {
1221                 opusClose(_opusDecoder);
1222                 _opusDecoder = null;
1223             }
1224             _opusBuffer = null;
1225         }
1226 
1227         version(decodeWAV)
1228         {
1229             if (_wavDecoder !is null)
1230             {
1231                 destroyFree(_wavDecoder);
1232                 _wavDecoder = null;
1233             }
1234         }
1235 
1236         version(decodeXM)
1237         {
1238             if (_xmDecoder !is null)
1239             {
1240                 xm_free_context(_xmDecoder);
1241                 _xmDecoder = null;
1242             }
1243             if (_xmContent != null)
1244             {
1245                 free(_xmContent);
1246                 _xmContent = null;
1247             }
1248         }
1249 
1250         version(decodeMOD)
1251         {
1252             if (_modDecoder !is null)
1253             {
1254                 free(_modDecoder);
1255                 _modDecoder = null;
1256                 _modContent.reallocBuffer(0);
1257             }
1258         }
1259 
1260         version(encodeWAV)
1261         {
1262             if (_wavEncoder !is null)
1263             {
1264                 destroyFree(_wavEncoder);
1265                 _wavEncoder = null;
1266             }
1267         }
1268     }
1269 
1270     // clean-up the whole Stream object so that it can be reused for anything else.
1271     void cleanUp() @nogc
1272     {
1273         cleanUpCodecs();
1274 
1275         if (_decoderContext)
1276         {
1277             destroyFree(_decoderContext);
1278             _decoderContext = null;
1279         }
1280 
1281         if (fileContext !is null)
1282         {
1283             if (fileContext.file !is null)
1284             {
1285                 int result = fclose(fileContext.file);
1286                 if (result)
1287                     throw mallocNew!Exception("Closing of audio file errored");
1288             }
1289             destroyFree(fileContext);
1290             fileContext = null;
1291         }
1292 
1293         if (memoryContext !is null)
1294         {
1295             // TODO destroy buffer if any is owned
1296             destroyFree(memoryContext);
1297             memoryContext = null;
1298         }
1299 
1300         if (_io !is null)
1301         {
1302             destroyFree(_io);
1303             _io = null;
1304         }
1305     }
1306 
1307     void startDecoding() @nogc
1308     {
1309         // Create a decoder context
1310         if ( _decoderContext is null)
1311         {
1312             _decoderContext = mallocNew!DecoderContext;
1313             _decoderContext.userDataIO = userData;
1314             _decoderContext.callbacks = _io;
1315         }
1316 
1317         version(decodeOPUS)
1318         {
1319             try
1320             {
1321                 _opusDecoder = opusOpen(_io, userData);
1322                 assert(_opusDecoder !is null);
1323                 _format = AudioFileFormat.opus;
1324                 _sampleRate = _opusDecoder.rate; // Note: Opus file are always 48Khz
1325                 _numChannels = _opusDecoder.channels();
1326                 _lengthInFrames = _opusDecoder.smpduration();
1327                 _opusPositionFrame = 0;
1328                 return;
1329             }
1330             catch(Exception e)
1331             {
1332                 destroyFree(e);
1333             }
1334             _opusDecoder = null;
1335         }
1336 
1337         version(decodeFLAC)
1338         {
1339             _io.seek(0, false, userData);
1340             
1341             // Is it a FLAC?
1342             {
1343                 drflac_read_proc onRead = &flac_read;
1344                 drflac_seek_proc onSeek = &flac_seek;
1345                 void* pUserData = _decoderContext;
1346                 _flacDecoder = drflac_open (onRead, onSeek, _decoderContext);
1347                 if (_flacDecoder !is null)
1348                 {
1349                     _format = AudioFileFormat.flac;
1350                     _sampleRate = _flacDecoder.sampleRate;
1351                     _numChannels = _flacDecoder.channels;
1352                     _lengthInFrames = _flacDecoder.totalSampleCount / _numChannels;
1353                     _flacPositionFrame = 0;
1354                     return;
1355                 }
1356             }
1357         }
1358 
1359         version(decodeWAV)
1360         {
1361             // Check if it's a WAV.
1362 
1363             _io.seek(0, false, userData);
1364 
1365             try
1366             {
1367                 _wavDecoder = mallocNew!WAVDecoder(_io, userData);
1368                 _wavDecoder.scan();
1369 
1370                 // WAV detected
1371                 _format = AudioFileFormat.wav;
1372                 _sampleRate = _wavDecoder._sampleRate;
1373                 _numChannels = _wavDecoder._channels;
1374                 _lengthInFrames = _wavDecoder._lengthInFrames;
1375                 return;
1376             }
1377             catch(Exception e)
1378             {
1379                 // not a WAV
1380                 destroyFree(e);
1381             }
1382             destroyFree(_wavDecoder);
1383             _wavDecoder = null;
1384         }
1385 
1386         version(decodeOGG)
1387         {
1388             _io.seek(0, false, userData);
1389             
1390             // Is it an OGG?
1391             {
1392                 //"In my test files the maximal-size usage is ~150KB", so let's take a bit more
1393                 _oggBuffer.reallocBuffer(200 * 1024);
1394 
1395                 stb_vorbis_alloc alloc;
1396                 alloc.alloc_buffer = cast(ubyte*)(_oggBuffer.ptr);
1397                 alloc.alloc_buffer_length_in_bytes = cast(int)(_oggBuffer.length);
1398 
1399                 int error;
1400 
1401                 _oggHandle = stb_vorbis_open_file(_io, userData, &error, &alloc);
1402                 if (error == VORBIS__no_error)
1403                 {
1404                     _format = AudioFileFormat.ogg;
1405                     _sampleRate = _oggHandle.sample_rate;
1406                     _numChannels = _oggHandle.channels;
1407                     _lengthInFrames = stb_vorbis_stream_length_in_samples(_oggHandle);
1408                     return;
1409                 }
1410                 else
1411                 {
1412                     _oggHandle = null;
1413                 }
1414             }
1415         }
1416 
1417         version(decodeMP3)
1418         {
1419             // Check if it's a MP3.
1420             {
1421                 _io.seek(0, false, userData);
1422 
1423                 ubyte* scratchBuffer = cast(ubyte*) malloc(MINIMP3_BUF_SIZE*2);
1424                 scope(exit) free(scratchBuffer);
1425 
1426                 _mp3io = cast(mp3dec_io_t*) malloc(mp3dec_io_t.sizeof);
1427                 _mp3io.read      = &mp3_io_read;
1428                 _mp3io.read_data = _decoderContext;
1429                 _mp3io.seek      = &mp3_io_seek;
1430                 _mp3io.seek_data = _decoderContext;
1431 
1432                 if ( mp3dec_detect_cb(_mp3io, scratchBuffer, MINIMP3_BUF_SIZE*2) == 0 )
1433                 {
1434                     // This is a MP3. Try to open a stream.
1435 
1436                     // Allocate a mp3dec_ex_t object
1437                     _mp3DecoderNew = cast(mp3dec_ex_t*) malloc(mp3dec_ex_t.sizeof);
1438 
1439                     int result = mp3dec_ex_open_cb(_mp3DecoderNew, _mp3io, MP3D_SEEK_TO_SAMPLE);
1440 
1441                     if (0 == result)
1442                     {
1443                         // MP3 detected
1444                         // but it seems we need to iterate all frames to know the length...
1445                         _format = AudioFileFormat.mp3;
1446                         _sampleRate = _mp3DecoderNew.info.hz;
1447                         _numChannels = _mp3DecoderNew.info.channels;
1448                         _lengthInFrames = _mp3DecoderNew.samples / _numChannels;
1449                         return;
1450                     }
1451                     else
1452                     {
1453                         free(_mp3DecoderNew);
1454                         _mp3DecoderNew = null;
1455                         free(_mp3io);
1456                         _mp3io = null;
1457                     }
1458                 }
1459             }
1460         }
1461 
1462         version(decodeXM)
1463         {
1464             {
1465                 // we need the first 60 bytes to check if XM
1466                 char[60] xmHeader;
1467                 int bytes;
1468 
1469                 _io.seek(0, false, userData);
1470                 long lenBytes = _io.getFileLength(userData);
1471                 if (lenBytes < 60) 
1472                     goto not_a_xm;
1473 
1474                 bytes = _io.read(xmHeader.ptr, 60, userData);
1475                 if (bytes != 60)
1476                     goto not_a_xm;
1477 
1478                if (0 != xm_check_sanity_preload(xmHeader.ptr, 60))
1479                    goto not_a_xm;
1480 
1481                 _xmContent = cast(ubyte*) malloc(cast(int)lenBytes);
1482                 _io.seek(0, false, userData);
1483                 bytes = _io.read(_xmContent, cast(int)lenBytes, userData);
1484                 if (bytes != cast(int)lenBytes)
1485                     goto not_a_xm;
1486 
1487                 if (0 == xm_create_context_safe(&_xmDecoder, cast(const(char)*)_xmContent, cast(size_t)lenBytes, 44100))
1488                 {
1489                     assert(_xmDecoder !is null);
1490 
1491                     xm_set_max_loop_count(_xmDecoder, 1);
1492 
1493                     _format = AudioFileFormat.xm;
1494                     _sampleRate = 44100.0f;
1495                     _numChannels = 2;
1496                     _lengthInFrames = audiostreamUnknownLength;
1497                     return;
1498                 }
1499 
1500                 not_a_xm:
1501                 assert(_xmDecoder == null);
1502                 free(_xmContent);
1503                 _xmContent = null;
1504             }
1505         } 
1506 
1507         version(decodeMOD)
1508         {
1509             {
1510                 // we need either the first 1084 or 600 bytes if available
1511                 _io.seek(0, false, userData);
1512                 long lenBytes = _io.getFileLength(userData);
1513                 if (lenBytes >= 600)
1514                 {
1515                     int headerBytes = lenBytes > 1084 ? 1084 : cast(int)lenBytes;
1516 
1517                     ubyte[1084] header;
1518                     int bytes = _io.read(header.ptr, headerBytes, userData);
1519 
1520                     if (_pocketmod_ident(null, header.ptr, bytes))
1521                     {
1522                         // This is a MOD, allocate a proper context, and read the whole file.
1523                         _modDecoder = cast(pocketmod_context*) malloc(pocketmod_context.sizeof);
1524 
1525                         // Read whole .mod in a buffer, since the decoder work all from memory
1526                         _io.seek(0, false, userData);
1527                         _modContent.reallocBuffer(cast(size_t)lenBytes);
1528                         bytes = _io.read(_modContent.ptr, cast(int)lenBytes, userData);
1529 
1530                         if (pocketmod_init(_modDecoder, _modContent.ptr, bytes, 44100))
1531                         {
1532                             _format = AudioFileFormat.mod;
1533                             _sampleRate = 44100.0f;
1534                             _numChannels = 2;
1535                             _lengthInFrames = audiostreamUnknownLength;
1536                             return;
1537                         }
1538                     }
1539                 }
1540             }
1541         }
1542 
1543         _format = AudioFileFormat.unknown;
1544         _sampleRate = float.nan;
1545         _numChannels = 0;
1546         _lengthInFrames = -1;
1547 
1548         throw mallocNew!Exception("Cannot decode stream: unrecognized encoding.");
1549     }
1550 
1551     void startEncoding(AudioFileFormat format, float sampleRate, int numChannels, EncodingOptions options) @nogc
1552     { 
1553         _format = format;
1554         _sampleRate = sampleRate;
1555         _numChannels = numChannels;
1556 
1557         final switch(format) with (AudioFileFormat)
1558         {
1559             case mp3:
1560                 throw mallocNew!Exception("Unsupported encoding format: MP3");
1561             case flac:
1562                 throw mallocNew!Exception("Unsupported encoding format: FLAC");
1563             case ogg:
1564                 throw mallocNew!Exception("Unsupported encoding format: OGG");
1565             case opus:
1566                 throw mallocNew!Exception("Unsupported encoding format: Opus");
1567             case mod:
1568                 throw mallocNew!Exception("Unsupported encoding format: MOD");
1569             case xm:
1570                 throw mallocNew!Exception("Unsupported encoding format: XM");
1571             case wav:
1572             {
1573                 // Note: fractional sample rates not supported by WAV, signal an integer one
1574                 int isampleRate = cast(int)(sampleRate + 0.5f);
1575 
1576                 WAVEncoder.Format wavfmt;
1577                 final switch (options.sampleFormat)
1578                 {
1579                     case AudioSampleFormat.s8:   wavfmt = WAVEncoder.Format.s8; break;
1580                     case AudioSampleFormat.s16:  wavfmt = WAVEncoder.Format.s16le; break;
1581                     case AudioSampleFormat.s24:  wavfmt = WAVEncoder.Format.s24le; break;
1582                     case AudioSampleFormat.fp32: wavfmt = WAVEncoder.Format.fp32le; break;
1583                     case AudioSampleFormat.fp64: wavfmt = WAVEncoder.Format.fp64le; break;
1584                 }
1585                 _wavEncoder = mallocNew!WAVEncoder(_io, userData, isampleRate, numChannels, wavfmt, options.enableDither);
1586                 break;
1587             }
1588             case unknown:
1589                 throw mallocNew!Exception("Can't encode using 'unknown' coding");
1590         }        
1591     }   
1592 
1593     void finalizeEncodingIfNeeded() @nogc
1594     {
1595         if (_io && (_io.write !is null)) // if we have been encoding something
1596         {
1597             finalizeEncoding();
1598         }
1599     }
1600 }
1601 
1602 // AudioStream should be able to go on a smallish 32-bit stack,
1603 // and malloc the rest on the heap when needed.
1604 static assert(AudioStream.sizeof <= 256); 
1605 
1606 private: // not meant to be imported at all
1607 
1608 
1609 
1610 // Internal object for audio-formats
1611 
1612 
1613 // File callbacks
1614 // The file callbacks are using the C stdlib.
1615 
1616 struct FileContext // this is what is passed to I/O when used in file mode
1617 {
1618     // Used when streaming of writing a file
1619     FILE* file = null;
1620 
1621     // Size of the file in bytes, only used when reading/writing a file.
1622     long fileSize;
1623 
1624     // Initialize this context
1625     void initialize(const(char)[] path, bool forWrite) @nogc
1626     {
1627         CString strZ = CString(path);
1628         file = fopen(strZ.storage, forWrite ? "wb".ptr : "rb".ptr);
1629         if (file is null)
1630             throw mallocNew!Exception("File not found");
1631         // finds the size of the file
1632         fseek(file, 0, SEEK_END);
1633         fileSize = ftell(file);
1634         fseek(file, 0, SEEK_SET);
1635     }
1636 }
1637 
1638 long file_tell(void* userData) nothrow @nogc
1639 {
1640     FileContext* context = cast(FileContext*)userData;
1641     return ftell(context.file);
1642 }
1643 
1644 bool file_seek(long offset, bool relative, void* userData) nothrow @nogc
1645 {
1646     FileContext* context = cast(FileContext*)userData;
1647     assert(offset <= int.max);
1648     int r = fseek(context.file, cast(int)offset, relative ? SEEK_CUR : SEEK_SET); // Limitations: file larger than 2gb not supported
1649     return r == 0;
1650 }
1651 
1652 long file_getFileLength(void* userData) nothrow @nogc
1653 {
1654     FileContext* context = cast(FileContext*)userData;
1655     return context.fileSize;
1656 }
1657 
1658 int file_read(void* outData, int bytes, void* userData) nothrow @nogc
1659 {
1660     FileContext* context = cast(FileContext*)userData;
1661     size_t bytesRead = fread(outData, 1, bytes, context.file);
1662     return cast(int)bytesRead;
1663 }
1664 
1665 int file_write(void* inData, int bytes, void* userData) nothrow @nogc
1666 {
1667     FileContext* context = cast(FileContext*)userData;
1668     size_t bytesWritten = fwrite(inData, 1, bytes, context.file);
1669     return cast(int)bytesWritten;
1670 }
1671 
1672 bool file_skip(int bytes, void* userData) nothrow @nogc
1673 {
1674     FileContext* context = cast(FileContext*)userData;
1675     return (0 == fseek(context.file, bytes, SEEK_CUR));
1676 }
1677 
1678 bool file_flush(void* userData) nothrow @nogc
1679 {
1680     FileContext* context = cast(FileContext*)userData;
1681     return ( fflush(context.file) == 0 );
1682 }
1683 
1684 // Memory read callback
1685 // Using the read buffer instead
1686 
1687 struct MemoryContext
1688 {
1689     bool bufferIsOwned;
1690     bool bufferCanGrow;
1691 
1692     // Buffer
1693     ubyte* buffer = null;
1694 
1695     size_t size;     // current buffer size
1696     size_t cursor;   // where we are in the buffer
1697     size_t capacity; // max buffer size before realloc
1698 
1699     void initializeWithConstantInput(const(ubyte)* data, size_t length) nothrow @nogc
1700     {
1701         // Make a copy of the input buffer, since it could be temporary.
1702         bufferIsOwned = true;
1703         bufferCanGrow = false;
1704 
1705         buffer = mallocDup(data[0..length]).ptr; // Note: the copied slice is made mutable.
1706         size = length;
1707         cursor = 0;
1708         capacity = length;
1709     }
1710 
1711     void initializeWithExternalOutputBuffer(ubyte* data, size_t length) nothrow @nogc
1712     {
1713         bufferIsOwned = false;
1714         bufferCanGrow = false;
1715         buffer = data;
1716         size = 0;
1717         cursor = 0;
1718         capacity = length;
1719     }
1720 
1721     void initializeWithInternalGrowableBuffer() nothrow @nogc
1722     {
1723         bufferIsOwned = true;
1724         bufferCanGrow = true;
1725         buffer = null;
1726         size = 0;
1727         cursor = 0;
1728         capacity = 0;
1729     }
1730 
1731     ~this()
1732     {
1733         if (bufferIsOwned)
1734         {
1735             if (buffer !is null)
1736             {
1737                 free(buffer);
1738                 buffer = null;
1739             }
1740         }
1741     }
1742 }
1743 
1744 long memory_tell(void* userData) nothrow @nogc
1745 {
1746     MemoryContext* context = cast(MemoryContext*)userData;
1747     return cast(long)(context.cursor);
1748 }
1749 
1750 bool memory_seek(long offset, bool relative, void* userData) nothrow @nogc
1751 {
1752     MemoryContext* context = cast(MemoryContext*)userData;    
1753     if (relative) offset += context.cursor;
1754     if (offset < 0)
1755         return false;
1756 
1757     bool r = true;
1758     if (offset >= context.size) // can't seek past end of buffer, stick to the end so that read return 0 byte
1759     {
1760         offset = context.size;
1761         r = false;
1762     }
1763     context.cursor = cast(size_t)offset; // Note: memory streams larger than 2gb not supported
1764     return r;
1765 }
1766 
1767 long memory_getFileLength(void* userData) nothrow @nogc
1768 {
1769     MemoryContext* context = cast(MemoryContext*)userData;
1770     return cast(long)(context.size);
1771 }
1772 
1773 int memory_read(void* outData, int bytes, void* userData) nothrow @nogc
1774 {
1775     MemoryContext* context = cast(MemoryContext*)userData;
1776     size_t cursor = context.cursor;
1777     size_t size = context.size;
1778     size_t available = size - cursor;
1779     if (bytes < available)
1780     {
1781         outData[0..bytes] = context.buffer[cursor..cursor + bytes];
1782         context.cursor += bytes;
1783         return bytes;
1784     }
1785     else
1786     {
1787         outData[0..available] = context.buffer[cursor..cursor + available];
1788         context.cursor = context.size;
1789         return cast(int)available;
1790     }
1791 }
1792 
1793 int memory_write_limited(void* inData, int bytes, void* userData) nothrow @nogc
1794 {
1795     MemoryContext* context = cast(MemoryContext*)userData;
1796     size_t cursor = context.cursor;
1797     size_t size = context.size;
1798     size_t available = size - cursor;
1799     ubyte* buffer = context.buffer;
1800     ubyte* source = cast(ubyte*) inData;
1801 
1802     if (cursor + bytes > available)
1803     {
1804         bytes = cast(int)(available - cursor);       
1805     }
1806 
1807     buffer[cursor..(cursor + bytes)] = source[0..bytes];
1808     context.size += bytes;
1809     context.cursor += bytes;
1810     return bytes;
1811 }
1812 
1813 int memory_write_append(void* inData, int bytes, void* userData) nothrow @nogc
1814 {
1815     MemoryContext* context = cast(MemoryContext*)userData;
1816     size_t cursor = context.cursor;
1817     size_t size = context.size;
1818     size_t available = size - cursor;
1819     ubyte* buffer = context.buffer;
1820     ubyte* source = cast(ubyte*) inData;
1821 
1822     if (cursor + bytes > available)
1823     {
1824         size_t oldSize = context.capacity;
1825         size_t newSize = cursor + bytes;
1826         if (newSize < oldSize * 2 + 1) 
1827             newSize = oldSize * 2 + 1;
1828         buffer = cast(ubyte*) realloc(buffer, newSize);
1829         context.capacity = newSize;
1830 
1831         assert( cursor + bytes <= available );
1832     }
1833 
1834     buffer[cursor..(cursor + bytes)] = source[0..bytes];
1835     context.size += bytes;
1836     context.cursor += bytes;
1837     return bytes;
1838 }
1839 
1840 bool memory_skip(int bytes, void* userData) nothrow @nogc
1841 {
1842     MemoryContext* context = cast(MemoryContext*)userData;
1843     context.cursor += bytes;
1844     return context.cursor <= context.size;
1845 }
1846 
1847 bool memory_flush(void* userData) nothrow @nogc
1848 {
1849     // do nothing, no flushign to do for memory
1850     return true;
1851 }
1852 
1853 
1854 // Decoder context
1855 struct DecoderContext
1856 {
1857     void* userDataIO;
1858     IOCallbacks* callbacks;
1859 }
1860 
1861 // MP3 decoder read callback
1862 static int mp3ReadDelegate(void[] buf, void* userDataDecoder) @nogc nothrow
1863 {
1864     DecoderContext* context = cast(DecoderContext*) userDataDecoder;
1865 
1866     // read bytes into the buffer, return number of bytes read or 0 for EOF, -1 on error
1867     // will never be called with empty buffer, or buffer more than 128KB
1868 
1869     int bytes = context.callbacks.read(buf.ptr, cast(int)(buf.length), context.userDataIO);
1870     return bytes;
1871 }
1872 
1873 
1874 // FLAC decoder read callbacks
1875 
1876 size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead) @nogc nothrow
1877 {
1878     DecoderContext* context = cast(DecoderContext*) pUserData;
1879     return context.callbacks.read(pBufferOut, cast(int)(bytesToRead), context.userDataIO);
1880 }
1881 
1882 bool flac_seek(void* pUserData, int offset, drflac_seek_origin origin) @nogc nothrow
1883 {
1884     DecoderContext* context = cast(DecoderContext*) pUserData;
1885     if (origin == drflac_seek_origin_start)
1886     {
1887         context.callbacks.seek(offset, false, context.userDataIO);
1888     }
1889     else if (origin == drflac_seek_origin_current)
1890     {
1891         context.callbacks.seek(offset, true, context.userDataIO);
1892     }
1893     return true;
1894 }
1895 
1896 // MP3 decoder read callbacks
1897 
1898 size_t mp3_io_read(void *buf, size_t size, void *user_data) @nogc nothrow
1899 {
1900     DecoderContext* context = cast(DecoderContext*) user_data;
1901     return context.callbacks.read(buf, cast(int)(size), context.userDataIO);
1902 }
1903 
1904 int mp3_io_seek(ulong position, void *user_data) @nogc nothrow
1905 {
1906     DecoderContext* context = cast(DecoderContext*) user_data;
1907     context.callbacks.seek(position, false, context.userDataIO);
1908     return 0; // doesn't detect seeking errors
1909 }