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