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.vorbis;
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 opened 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 opened 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 opened 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     /// Returns: `true` if using operations that are accetpable in an audio thread (eg: no file I/O).
222     bool realtimeSafe() @nogc
223     {
224         return fileContext is null;
225     }
226 
227     ~this() @nogc
228     {
229         cleanUp();
230     }
231 
232     void cleanUp() @nogc
233     {
234         // Write the last needed bytes if needed
235         finalizeEncodingIfNeeded();
236 
237         version(decodeMP3)
238         {
239             if (_mp3DecoderNew !is null)
240             {
241                 mp3dec_ex_close(_mp3DecoderNew);
242                 free(_mp3DecoderNew);
243                 _mp3DecoderNew = null;
244             }
245             if (_mp3io !is null)
246             {
247                 free(_mp3io);
248                 _mp3io = null;
249             }
250         }
251 
252         version(decodeFLAC)
253         {
254             if (_flacDecoder !is null)
255             {
256                 drflac_close(_flacDecoder);
257                 _flacDecoder = null;
258             }
259         }
260 
261         version(decodeOGG)
262         {
263             if (_oggDecoder !is null)
264             {
265                 destroyFree(_oggDecoder);
266                 _oggDecoder = null;
267             }
268             _oggBuffer.reallocBuffer(0);
269         }
270 
271         version(decodeOPUS)
272         {
273             if (_opusDecoder !is null)
274             {
275                 opusClose(_opusDecoder);
276                 _opusDecoder = null;
277             }
278             _opusBuffer = null;
279         }
280 
281         version(decodeWAV)
282         {
283             if (_wavDecoder !is null)
284             {
285                 destroyFree(_wavDecoder);
286                 _wavDecoder = null;
287             }
288         }
289 
290         version(decodeXM)
291         {
292             if (_xmDecoder !is null)
293             {
294                 xm_free_context(_xmDecoder);
295                 _xmDecoder = null;
296             }
297             if (_xmContent != null)
298             {
299                 free(_xmContent);
300                 _xmContent = null;
301             }
302         }
303 
304         version(decodeMOD)
305         {
306             if (_modDecoder !is null)
307             {
308                 free(_modDecoder);
309                 _modDecoder = null;
310                 _modContent.reallocBuffer(0);
311             }
312         }
313 
314         version(encodeWAV)
315         {
316             if (_wavEncoder !is null)
317             {
318                 destroyFree(_wavEncoder);
319                 _wavEncoder = null;
320             }
321         }
322 
323         if (_decoderContext)
324         {
325             destroyFree(_decoderContext);
326             _decoderContext = null;
327         }
328 
329         if (fileContext !is null)
330         {
331             if (fileContext.file !is null)
332             {
333                 int result = fclose(fileContext.file);
334                 if (result)
335                     throw mallocNew!Exception("Closing of audio file errored");
336             }
337             destroyFree(fileContext);
338             fileContext = null;
339         }
340 
341         if (memoryContext !is null)
342         {
343             // TODO destroy buffer if any is owned
344             destroyFree(memoryContext);
345             memoryContext = null;
346         }
347 
348         if (_io !is null)
349         {
350             destroyFree(_io);
351             _io = null;
352         }
353     }
354 
355     /// Returns: File format of this stream.
356     AudioFileFormat getFormat() nothrow @nogc
357     {
358         return _format;
359     }
360 
361     /// Returns: File format of this stream.
362     int getNumChannels() nothrow @nogc
363     {
364         return _numChannels;
365     }
366 
367     /// Returns: Length of this stream in frames.
368     /// Note: may return `audiostreamUnknownLength` if the length is unknown.
369     long getLengthInFrames() nothrow @nogc
370     {
371         return _lengthInFrames;
372     }
373 
374     /// Returns: Sample-rate of this stream in Hz.
375     float getSamplerate() nothrow @nogc
376     {
377         return _sampleRate;
378     }
379 
380     /// Read interleaved float samples.
381     /// `outData` must have enough room for `frames` * `channels` decoded samples.
382     int readSamplesFloat(float* outData, int frames) @nogc
383     {
384         // If you fail here, you are using this `AudioStream` for decoding:
385         // - after it has been destroyed,
386         // - or it was created for encoding instead 
387         assert(_io && _io.read !is null);
388 
389         final switch(_format)
390         {
391             case AudioFileFormat.opus:
392             {
393                 version(decodeOPUS)
394                 {
395                     try
396                     {
397                         int decoded = 0;
398                         while (decoded < frames)
399                         {
400                             // Is there any sample left in _opusBuffer?
401                             // If not decode some frames.
402                             if (_opusBuffer is null || _opusBuffer.length == 0)
403                             {
404                                 _opusBuffer = _opusDecoder.readFrame();
405                                 if (_opusBuffer is null)
406                                     break;
407                             }
408 
409                             int samplesInBuffer = cast(int) _opusBuffer.length;
410                             int framesInBuffer  = samplesInBuffer / _numChannels;
411                             if (framesInBuffer == 0)
412                                 break;
413 
414                             // Frames to pull are min( frames left to decode, frames available)
415                             int framesToDecode = frames - decoded;
416                             int framesToUse = framesToDecode < framesInBuffer ? framesToDecode : framesInBuffer;
417                             assert(framesToUse != 0);
418 
419                             int samplesToUse = framesToUse * _numChannels;
420                             int outOffset = decoded*_numChannels;
421                             for (int n = 0; n < samplesToUse; ++n)
422                             {
423                                 outData[outOffset + n] = _opusBuffer[n] / 32767.0f;
424                             }
425                             _opusBuffer = _opusBuffer[samplesToUse..$]; // reduce size of intermediate buffer
426                             decoded += framesToUse;
427                         }
428                             return decoded;
429                     }
430                     catch(Exception e)
431                     {
432                         destroyFree(e);
433                         return 0; // decoding might fail, in which case return zero samples
434                     }
435                 }
436             }
437 
438             case AudioFileFormat.flac:
439             {
440                 version(decodeFLAC)
441                 {
442                     assert(_flacDecoder !is null);
443 
444                     int* integerData = cast(int*)outData;
445                     int samples = cast(int) drflac_read_s32(_flacDecoder, frames, integerData);
446 
447                     // "Samples are always output as interleaved signed 32-bit PCM."
448                     // Convert to float with type-punning. Note that this looses some precision.
449                     double factor = 1.0 / int.max;
450                     foreach(n; 0..samples)
451                     {
452                         outData[n] = integerData[n]  * factor;
453                     }  
454                     return samples / _numChannels;
455                 }
456                 else
457                 {
458                     assert(false); // Impossible
459                 }
460             }
461             
462             case AudioFileFormat.ogg:
463             {
464                 version(decodeOGG)
465                 {
466                     assert(_oggDecoder !is null);
467                     return _oggDecoder.stb_vorbis_get_samples_float_interleaved(_numChannels, outData, frames * _numChannels);
468                 }
469                 else
470                 {
471                     assert(false); // Impossible
472                 }
473             }
474 
475             case AudioFileFormat.mp3:
476             {
477                 version(decodeMP3)
478                 {
479                     assert(_mp3DecoderNew !is null);
480 
481                     int samplesNeeded = frames * _numChannels;
482                     int result = cast(int) mp3dec_ex_read(_mp3DecoderNew, outData, samplesNeeded);
483                     if (result < 0) // error
484                         return 0;
485                     return result / _numChannels;
486                 }
487                 else
488                 {
489                     assert(false); // Impossible
490                 }
491             }
492             case AudioFileFormat.wav:
493                 version(decodeWAV)
494                 {
495                     assert(_wavDecoder !is null);
496                     int readFrames = _wavDecoder.readSamples(outData, frames); 
497                     return readFrames;
498                 }
499                 else
500                 {
501                     assert(false); // Impossible
502                 }
503 
504             case AudioFileFormat.xm:
505                 version(decodeXM)
506                 {
507                     assert(_xmDecoder !is null);
508 
509                     if (xm_get_loop_count(_xmDecoder) >= 1)
510                         return 0; // song is finished
511 
512                     xm_generate_samples(_xmDecoder, outData, frames);
513                     return frames; // Note: XM decoder pads end with zeroes.
514                 }
515                 else
516                 {
517                     assert(false); // Impossible
518                 }
519 
520             case AudioFileFormat.mod:
521                 version(decodeMOD)
522                 {
523                     if (pocketmod_loop_count(_modDecoder) >= 1)
524                         return 0; // end stream after MOD finishes, looping not supported
525                     assert(_modDecoder !is null);
526                     int bytesReturned = pocketmod_render(_modDecoder, outData, frames * 2 * 4);
527                     assert((bytesReturned % 8) == 0);
528                     return bytesReturned / 8;
529                 }
530                 else
531                 {
532                     assert(false); // Impossible
533                 }
534 
535             case AudioFileFormat.unknown:
536                 // One shouldn't ever get there, since in this case
537                 // opening has failed.
538                 assert(false);
539         }
540     }
541     ///ditto
542     int readSamplesFloat(float[] outData) @nogc
543     {
544         assert( (outData.length % _numChannels) == 0);
545         return readSamplesFloat(outData.ptr, cast(int)(outData.length / _numChannels) );
546     }
547 
548     /// Write interleaved float samples.
549     /// `inData` must have enough data for `frames` * `channels` samples.
550     int writeSamplesFloat(float* inData, int frames) nothrow @nogc
551     {
552         // If you fail here, you are using this `AudioStream` for encoding:
553         // - after it has been destroyed,
554         // - or after encoding has been finalized with 
555         // - or it was created for encoding instead 
556         assert(_io && _io.write !is null);
557 
558         final switch(_format)
559         {
560             case AudioFileFormat.mp3:
561             case AudioFileFormat.flac:
562             case AudioFileFormat.ogg:
563             case AudioFileFormat.opus:
564             case AudioFileFormat.mod:
565             case AudioFileFormat.xm:
566             case AudioFileFormat.unknown:
567             {
568                 assert(false); // Shouldn't have arrived here, such encoding aren't supported.
569             }
570             case AudioFileFormat.wav:
571             {
572                 version(encodeWAV)
573                 {
574                     return _wavEncoder.writeSamples(inData, frames);
575                 }
576                 else
577                 {
578                     assert(false, "no support for WAV encoding");
579                 }
580             }
581         }
582     }
583     ///ditto
584     int writeSamplesFloat(float[] inData) nothrow @nogc
585     {
586         assert( (inData.length % _numChannels) == 0);
587         return writeSamplesFloat(inData.ptr, cast(int)(inData.length / _numChannels));
588     }
589 
590     /// Call `fflush()` on written samples, if any. 
591     /// It is only useful for streamable output formats, that may want to flush things to disk.
592     void flush() nothrow @nogc
593     {
594         assert( _io && (_io.write !is null) );
595         _io.flush(userData);
596     }
597     
598     /// Finalize encoding. After finalization, further writes are not possible anymore
599     /// however the stream is considered complete and valid for storage.
600     void finalizeEncoding() @nogc 
601     {
602         // If you crash here, it's because `finalizeEncoding` has been called twice.
603         assert( _io && (_io.write !is null) );
604 
605         final switch(_format) with (AudioFileFormat)
606         {
607             case mp3:
608             case flac:
609             case ogg:
610             case opus:
611             case mod:
612             case xm:
613                 assert(false); // unsupported output encoding
614             case wav:
615                 { 
616                     _wavEncoder.finalizeEncoding();
617                     break;
618                 }
619             case unknown:
620                 assert(false);
621         }
622         _io.write = null; // prevents further encodings
623     }
624 
625     // Finalize encoding and get internal buffer.
626     // This can be called multiple times, in which cases the stream is finalized only the first time.
627     const(ubyte)[] finalizeAndGetEncodedResult() @nogc
628     {
629         // only callable while appending, else it's a programming error
630         assert( (memoryContext !is null) && ( memoryContext.bufferCanGrow ) );
631 
632         finalizeEncodingIfNeeded(); 
633         return memoryContext.buffer[0..memoryContext.size];
634     }
635 
636 private:
637     IOCallbacks* _io;
638 
639     // This type of context is a closure to remember where the data is.
640     void* userData; // is equal to either fileContext or memoryContext
641     FileContext* fileContext;
642     MemoryContext* memoryContext;
643 
644     // This type of context is a closure to remember where _io and user Data is.
645     DecoderContext* _decoderContext;
646 
647     AudioFileFormat _format;
648     float _sampleRate; 
649     int _numChannels;
650     long _lengthInFrames;
651 
652     // Decoders
653     version(decodeMP3)
654     {
655         mp3dec_ex_t* _mp3DecoderNew; // allocated on heap since it's a 16kb object
656         mp3dec_io_t* _mp3io;
657     }
658     version(decodeFLAC)
659     {
660         drflac* _flacDecoder;
661     }
662     version(decodeOGG)
663     {
664         ubyte[] _oggBuffer; // all allocations from the ogg decoder
665         VorbisDecoder _oggDecoder;
666     }
667     version(decodeWAV)
668     {
669         WAVDecoder _wavDecoder;
670     }
671     version(decodeMOD)
672     {
673         pocketmod_context* _modDecoder = null;
674         ubyte[] _modContent = null; // whole buffer, copied
675     }
676     version(decodeXM)
677     {
678         xm_context_t* _xmDecoder = null;
679         ubyte* _xmContent = null;
680     }
681 
682     version(decodeOPUS)
683     {
684         OpusFile _opusDecoder;
685         short[] _opusBuffer;
686     }
687 
688     // Encoder
689     version(encodeWAV)
690     {
691         WAVEncoder _wavEncoder;
692     }
693 
694     bool isOpenedForWriting() nothrow @nogc
695     {
696         // Note: 
697         //  * when opened for reading, I/O operations given are: seek/tell/getFileLength/read.
698         //  * when opened for writing, I/O operations given are: seek/tell/write/flush.
699         return (_io !is null) && (_io.read is null);
700     }
701 
702     void startDecoding() @nogc
703     {
704         // Create a decoder context
705         _decoderContext = mallocNew!DecoderContext;
706         _decoderContext.userDataIO = userData;
707         _decoderContext.callbacks = _io;
708 
709         version(decodeOPUS)
710         {
711             try
712             {
713                 _opusDecoder = opusOpen(_io, userData);
714                 assert(_opusDecoder !is null);
715                 _format = AudioFileFormat.opus;
716                 _sampleRate = _opusDecoder.rate; // Note: Opus file are always 48Khz
717                 _numChannels = _opusDecoder.channels();
718                 _lengthInFrames = _opusDecoder.smpduration();
719                 return;
720             }
721             catch(Exception e)
722             {
723                 destroyFree(e);
724             }
725             _opusDecoder = null;
726         }
727 
728         version(decodeFLAC)
729         {
730             _io.seek(0, false, userData);
731             
732             // Is it a FLAC?
733             {
734                 drflac_read_proc onRead = &flac_read;
735                 drflac_seek_proc onSeek = &flac_seek;
736                 void* pUserData = _decoderContext;
737                 _flacDecoder = drflac_open (onRead, onSeek, _decoderContext);
738                 if (_flacDecoder !is null)
739                 {
740                     _format = AudioFileFormat.flac;
741                     _sampleRate = _flacDecoder.sampleRate;
742                     _numChannels = _flacDecoder.channels;
743                     _lengthInFrames = _flacDecoder.totalSampleCount / _numChannels;
744                     return;
745                 }
746             }
747         }
748 
749         version(decodeWAV)
750         {
751             // Check if it's a WAV.
752 
753             _io.seek(0, false, userData);
754 
755             try
756             {
757                 _wavDecoder = mallocNew!WAVDecoder(_io, userData);
758                 _wavDecoder.scan();
759 
760                 // WAV detected
761                 _format = AudioFileFormat.wav;
762                 _sampleRate = _wavDecoder._sampleRate;
763                 _numChannels = _wavDecoder._channels;
764                 _lengthInFrames = _wavDecoder._lengthInFrames;
765                 return;
766             }
767             catch(Exception e)
768             {
769                 // not a WAV
770                 destroyFree(e);
771             }
772             destroyFree(_wavDecoder);
773             _wavDecoder = null;
774         }
775 
776         version(decodeOGG)
777         {
778             _io.seek(0, false, userData);
779             
780             // Is it an OGG?
781             {
782                 //"In my test files the maximal-size usage is ~150KB", so let's take a bit more
783                 _oggBuffer.reallocBuffer(200 * 1024);
784 
785                 stb_vorbis_alloc alloc;
786                 alloc.alloc_buffer = cast(ubyte*)(_oggBuffer.ptr);
787                 alloc.alloc_buffer_length_in_bytes = cast(int)(_oggBuffer.length);
788 
789                 int error;
790                 _oggDecoder = mallocNew!VorbisDecoder(_io, userData);
791                 if (_oggDecoder.error == STBVorbisError.no_error)
792                 {
793                     _format = AudioFileFormat.ogg;
794                     _sampleRate = _oggDecoder.sampleRate;
795                     _numChannels = _oggDecoder.chans;
796                     _lengthInFrames = audiostreamUnknownLength;//stb_vorbis_stream_length_in_samples(_oggDecoder);
797                     return;
798                 }
799                 else
800                 {
801                     destroyFree(_oggDecoder);
802                     _oggDecoder = null;
803                 }
804 
805             }
806         }
807 
808         version(decodeMP3)
809         {
810             // Check if it's a MP3.
811             {
812                 _io.seek(0, false, userData);
813 
814                 ubyte* scratchBuffer = cast(ubyte*) malloc(MINIMP3_BUF_SIZE*2);
815                 scope(exit) free(scratchBuffer);
816 
817                 _mp3io = cast(mp3dec_io_t*) malloc(mp3dec_io_t.sizeof);
818                 _mp3io.read      = &mp3_io_read;
819                 _mp3io.read_data = _decoderContext;
820                 _mp3io.seek      = &mp3_io_seek;
821                 _mp3io.seek_data = _decoderContext;
822 
823                 if ( mp3dec_detect_cb(_mp3io, scratchBuffer, MINIMP3_BUF_SIZE*2) == 0 )
824                 {
825                     // This is a MP3. Try to open a stream.
826 
827                     // Allocate a mp3dec_ex_t object
828                     _mp3DecoderNew = cast(mp3dec_ex_t*) malloc(mp3dec_ex_t.sizeof);
829 
830                     int result = mp3dec_ex_open_cb(_mp3DecoderNew, _mp3io, MP3D_SEEK_TO_SAMPLE);
831 
832                     if (0 == result)
833                     {
834                         // MP3 detected
835                         // but it seems we need to iterate all frames to know the length...
836                         _format = AudioFileFormat.mp3;
837                         _sampleRate = _mp3DecoderNew.info.hz;
838                         _numChannels = _mp3DecoderNew.info.channels;
839                         _lengthInFrames = _mp3DecoderNew.samples / _numChannels;
840                         return;
841                     }
842                     else
843                     {
844                         free(_mp3DecoderNew);
845                         _mp3DecoderNew = null;
846                         free(_mp3io);
847                         _mp3io = null;
848                     }
849                 }
850             }
851         }
852 
853         version(decodeXM)
854         {
855             {
856                 // we need the first 60 bytes to check if XM
857                 char[60] xmHeader;
858                 int bytes;
859 
860                 _io.seek(0, false, userData);
861                 long lenBytes = _io.getFileLength(userData);
862                 if (lenBytes < 60) 
863                     goto not_a_xm;
864 
865                 bytes = _io.read(xmHeader.ptr, 60, userData);
866                 if (bytes != 60)
867                     goto not_a_xm;
868 
869                if (0 != xm_check_sanity_preload(xmHeader.ptr, 60))
870                    goto not_a_xm;
871 
872                 _xmContent = cast(ubyte*) malloc(cast(int)lenBytes);
873                 _io.seek(0, false, userData);
874                 bytes = _io.read(_xmContent, cast(int)lenBytes, userData);
875                 if (bytes != cast(int)lenBytes)
876                     goto not_a_xm;
877 
878                 if (0 == xm_create_context_safe(&_xmDecoder, cast(const(char)*)_xmContent, cast(size_t)lenBytes, 44100))
879                 {
880                     assert(_xmDecoder !is null);
881 
882                     xm_set_max_loop_count(_xmDecoder, 1);
883 
884                     _format = AudioFileFormat.xm;
885                     _sampleRate = 44100.0f;
886                     _numChannels = 2;
887                     _lengthInFrames = audiostreamUnknownLength;
888                     return;
889                 }
890 
891                 not_a_xm:
892                 assert(_xmDecoder == null);
893                 free(_xmContent);
894                 _xmContent = null;
895             }
896         } 
897 
898         version(decodeMOD)
899         {
900             {
901                 // we need either the first 1084 or 600 bytes if available
902                 _io.seek(0, false, userData);
903                 long lenBytes = _io.getFileLength(userData);
904                 if (lenBytes >= 600)
905                 {
906                     int headerBytes = lenBytes > 1084 ? 1084 : cast(int)lenBytes;
907 
908                     ubyte[1084] header;
909                     int bytes = _io.read(header.ptr, headerBytes, userData);
910 
911                     if (_pocketmod_ident(null, header.ptr, bytes))
912                     {
913                         // This is a MOD, allocate a proper context, and read the whole file.
914                         _modDecoder = cast(pocketmod_context*) malloc(pocketmod_context.sizeof);
915 
916                         // Read whole .mod in a buffer, since the decoder work all from memory
917                         _io.seek(0, false, userData);
918                         _modContent.reallocBuffer(cast(size_t)lenBytes);
919                         bytes = _io.read(_modContent.ptr, cast(int)lenBytes, userData);
920 
921                         if (pocketmod_init(_modDecoder, _modContent.ptr, bytes, 44100))
922                         {
923                             _format = AudioFileFormat.mod;
924                             _sampleRate = 44100.0f;
925                             _numChannels = 2;
926                             _lengthInFrames = audiostreamUnknownLength;
927                             return;
928                         }
929                     }
930                 }
931             }
932         }
933 
934         _format = AudioFileFormat.unknown;
935         _sampleRate = float.nan;
936         _numChannels = 0;
937         _lengthInFrames = -1;
938 
939         throw mallocNew!Exception("Cannot decode stream: unrecognized encoding.");
940     }
941 
942     void startEncoding(AudioFileFormat format, float sampleRate, int numChannels) @nogc
943     { 
944         _format = format;
945         _sampleRate = sampleRate;
946         _numChannels = numChannels;
947 
948         final switch(format) with (AudioFileFormat)
949         {
950             case mp3:
951                 throw mallocNew!Exception("Unsupported encoding format: MP3");
952             case flac:
953                 throw mallocNew!Exception("Unsupported encoding format: FLAC");
954             case ogg:
955                 throw mallocNew!Exception("Unsupported encoding format: OGG");
956             case opus:
957                 throw mallocNew!Exception("Unsupported encoding format: Opus");
958             case mod:
959                 throw mallocNew!Exception("Unsupported encoding format: MOD");
960             case xm:
961                 throw mallocNew!Exception("Unsupported encoding format: XM");
962             case wav:
963             {
964                 // Note: fractional sample rates not supported by WAV, signal an integer one
965                 int isampleRate = cast(int)(sampleRate + 0.5f);
966                 _wavEncoder = mallocNew!WAVEncoder(_io, userData, isampleRate, numChannels );
967                 break;
968             }
969             case unknown:
970                 throw mallocNew!Exception("Can't encode using 'unknown' coding");
971         }        
972     }   
973 
974     void finalizeEncodingIfNeeded() @nogc
975     {
976         if (_io && (_io.write !is null)) // if we have been encoding something
977         {
978             finalizeEncoding();
979         }
980     }
981 }
982 
983 // AudioStream should be able to go on a smallish 32-bit stack,
984 // and malloc the rest on the heap when needed.
985 static assert(AudioStream.sizeof <= 256); 
986 
987 private: // not meant to be imported at all
988 
989 
990 
991 // Internal object for audio-formats
992 
993 
994 // File callbacks
995 // The file callbacks are using the C stdlib.
996 
997 struct FileContext // this is what is passed to I/O when used in file mode
998 {
999     // Used when streaming of writing a file
1000     FILE* file = null;
1001 
1002     // Size of the file in bytes, only used when reading/writing a file.
1003     long fileSize;
1004 
1005     // Initialize this context
1006     void initialize(const(char)[] path, bool forWrite) @nogc
1007     {
1008         CString strZ = CString(path);
1009         file = fopen(strZ.storage, forWrite ? "wb".ptr : "rb".ptr);
1010         // finds the size of the file
1011         fseek(file, 0, SEEK_END);
1012         fileSize = ftell(file);
1013         fseek(file, 0, SEEK_SET);
1014     }
1015 }
1016 
1017 long file_tell(void* userData) nothrow @nogc
1018 {
1019     FileContext* context = cast(FileContext*)userData;
1020     return ftell(context.file);
1021 }
1022 
1023 void file_seek(long offset, bool relative, void* userData) nothrow @nogc
1024 {
1025     FileContext* context = cast(FileContext*)userData;
1026     assert(offset <= int.max);
1027     fseek(context.file, cast(int)offset, relative ? SEEK_CUR : SEEK_SET); // Limitations: file larger than 2gb not supported
1028 }
1029 
1030 long file_getFileLength(void* userData) nothrow @nogc
1031 {
1032     FileContext* context = cast(FileContext*)userData;
1033     return context.fileSize;
1034 }
1035 
1036 int file_read(void* outData, int bytes, void* userData) nothrow @nogc
1037 {
1038     FileContext* context = cast(FileContext*)userData;
1039     size_t bytesRead = fread(outData, 1, bytes, context.file);
1040     return cast(int)bytesRead;
1041 }
1042 
1043 int file_write(void* inData, int bytes, void* userData) nothrow @nogc
1044 {
1045     FileContext* context = cast(FileContext*)userData;
1046     size_t bytesWritten = fwrite(inData, 1, bytes, context.file);
1047     return cast(int)bytesWritten;
1048 }
1049 
1050 bool file_skip(int bytes, void* userData) nothrow @nogc
1051 {
1052     FileContext* context = cast(FileContext*)userData;
1053     return (0 == fseek(context.file, bytes, SEEK_CUR));
1054 }
1055 
1056 bool file_flush(void* userData) nothrow @nogc
1057 {
1058     FileContext* context = cast(FileContext*)userData;
1059     return ( fflush(context.file) == 0 );
1060 }
1061 
1062 // Memory read callback
1063 // Using the read buffer instead
1064 
1065 struct MemoryContext
1066 {
1067     bool bufferIsOwned;
1068     bool bufferCanGrow;
1069 
1070     // Buffer
1071     ubyte* buffer = null;
1072 
1073     size_t size;     // current buffer size
1074     size_t cursor;   // where we are in the buffer
1075     size_t capacity; // max buffer size before realloc
1076 
1077     void initializeWithConstantInput(const(ubyte)* data, size_t length) nothrow @nogc
1078     {
1079         // Make a copy of the input buffer, since it could be temporary.
1080         bufferIsOwned = true;
1081         bufferCanGrow = false;
1082 
1083         buffer = mallocDup(data[0..length]).ptr; // Note: the copied slice is made mutable.
1084         size = length;
1085         cursor = 0;
1086         capacity = length;
1087     }
1088 
1089     void initializeWithExternalOutputBuffer(ubyte* data, size_t length) nothrow @nogc
1090     {
1091         bufferIsOwned = false;
1092         bufferCanGrow = false;
1093         buffer = data;
1094         size = 0;
1095         cursor = 0;
1096         capacity = length;
1097     }
1098 
1099     void initializeWithInternalGrowableBuffer() nothrow @nogc
1100     {
1101         bufferIsOwned = true;
1102         bufferCanGrow = true;
1103         buffer = null;
1104         size = 0;
1105         cursor = 0;
1106         capacity = 0;
1107     }
1108 
1109     ~this()
1110     {
1111         if (bufferIsOwned)
1112         {
1113             if (buffer !is null)
1114             {
1115                 free(buffer);
1116                 buffer = null;
1117             }
1118         }
1119     }
1120 }
1121 
1122 long memory_tell(void* userData) nothrow @nogc
1123 {
1124     MemoryContext* context = cast(MemoryContext*)userData;
1125     return cast(long)(context.cursor);
1126 }
1127 
1128 void memory_seek(long offset, bool relative, void* userData) nothrow @nogc
1129 {
1130     MemoryContext* context = cast(MemoryContext*)userData;    
1131     if (relative) offset += context.cursor;
1132     if (offset >= context.size) // can't seek past end of buffer, stick to the end so that read return 0 byte
1133         offset = context.size;
1134     context.cursor = cast(size_t)offset; // Note: memory streams larger than 2gb not supported
1135 }
1136 
1137 long memory_getFileLength(void* userData) nothrow @nogc
1138 {
1139     MemoryContext* context = cast(MemoryContext*)userData;
1140     return cast(long)(context.size);
1141 }
1142 
1143 int memory_read(void* outData, int bytes, void* userData) nothrow @nogc
1144 {
1145     MemoryContext* context = cast(MemoryContext*)userData;
1146     size_t cursor = context.cursor;
1147     size_t size = context.size;
1148     size_t available = size - cursor;
1149     if (bytes < available)
1150     {
1151         outData[0..bytes] = context.buffer[cursor..cursor + bytes];
1152         context.cursor += bytes;
1153         return bytes;
1154     }
1155     else
1156     {
1157         outData[0..available] = context.buffer[cursor..cursor + available];
1158         context.cursor = context.size;
1159         return cast(int)available;
1160     }
1161 }
1162 
1163 int memory_write_limited(void* inData, int bytes, void* userData) nothrow @nogc
1164 {
1165     MemoryContext* context = cast(MemoryContext*)userData;
1166     size_t cursor = context.cursor;
1167     size_t size = context.size;
1168     size_t available = size - cursor;
1169     ubyte* buffer = context.buffer;
1170     ubyte* source = cast(ubyte*) inData;
1171 
1172     if (cursor + bytes > available)
1173     {
1174         bytes = cast(int)(available - cursor);       
1175     }
1176 
1177     buffer[cursor..(cursor + bytes)] = source[0..bytes];
1178     context.size += bytes;
1179     context.cursor += bytes;
1180     return bytes;
1181 }
1182 
1183 int memory_write_append(void* inData, int bytes, void* userData) nothrow @nogc
1184 {
1185     MemoryContext* context = cast(MemoryContext*)userData;
1186     size_t cursor = context.cursor;
1187     size_t size = context.size;
1188     size_t available = size - cursor;
1189     ubyte* buffer = context.buffer;
1190     ubyte* source = cast(ubyte*) inData;
1191 
1192     if (cursor + bytes > available)
1193     {
1194         size_t oldSize = context.capacity;
1195         size_t newSize = cursor + bytes;
1196         if (newSize < oldSize * 2 + 1) 
1197             newSize = oldSize * 2 + 1;
1198         buffer = cast(ubyte*) realloc(buffer, newSize);
1199         context.capacity = newSize;
1200 
1201         assert( cursor + bytes <= available );
1202     }
1203 
1204     buffer[cursor..(cursor + bytes)] = source[0..bytes];
1205     context.size += bytes;
1206     context.cursor += bytes;
1207     return bytes;
1208 }
1209 
1210 bool memory_skip(int bytes, void* userData) nothrow @nogc
1211 {
1212     MemoryContext* context = cast(MemoryContext*)userData;
1213     context.cursor += bytes;
1214     return context.cursor <= context.size;
1215 }
1216 
1217 bool memory_flush(void* userData) nothrow @nogc
1218 {
1219     // do nothing, no flushign to do for memory
1220     return true;
1221 }
1222 
1223 
1224 // Decoder context
1225 struct DecoderContext
1226 {
1227     void* userDataIO;
1228     IOCallbacks* callbacks;
1229 }
1230 
1231 // MP3 decoder read callback
1232 static int mp3ReadDelegate(void[] buf, void* userDataDecoder) @nogc nothrow
1233 {
1234     DecoderContext* context = cast(DecoderContext*) userDataDecoder;
1235 
1236     // read bytes into the buffer, return number of bytes read or 0 for EOF, -1 on error
1237     // will never be called with empty buffer, or buffer more than 128KB
1238 
1239     int bytes = context.callbacks.read(buf.ptr, cast(int)(buf.length), context.userDataIO);
1240     return bytes;
1241 }
1242 
1243 
1244 // FLAC decoder read callbacks
1245 
1246 size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead) @nogc nothrow
1247 {
1248     DecoderContext* context = cast(DecoderContext*) pUserData;
1249     return context.callbacks.read(pBufferOut, cast(int)(bytesToRead), context.userDataIO);
1250 }
1251 
1252 bool flac_seek(void* pUserData, int offset, drflac_seek_origin origin) @nogc nothrow
1253 {
1254     DecoderContext* context = cast(DecoderContext*) pUserData;
1255     if (origin == drflac_seek_origin_start)
1256     {
1257         context.callbacks.seek(offset, false, context.userDataIO);
1258     }
1259     else if (origin == drflac_seek_origin_current)
1260     {
1261         context.callbacks.seek(offset, true, context.userDataIO);
1262     }
1263     return true;
1264 }
1265 
1266 // MP3 decoder read callbacks
1267 
1268 size_t mp3_io_read(void *buf, size_t size, void *user_data) @nogc nothrow
1269 {
1270     DecoderContext* context = cast(DecoderContext*) user_data;
1271     return context.callbacks.read(buf, cast(int)(size), context.userDataIO);
1272 }
1273 
1274 int mp3_io_seek(ulong position, void *user_data) @nogc nothrow
1275 {
1276     DecoderContext* context = cast(DecoderContext*) user_data;
1277     context.callbacks.seek(position, false, context.userDataIO);
1278     return 0; // doesn't detect seeking errors
1279 }