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                 stb_vorbis_close(_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 stb_vorbis_get_samples_float_interleaved(_oggDecoder, _numChannels, outData, frames);
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         stb_vorbis* _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                 stb_vorbis_alloc alloc;
785                 alloc.alloc_buffer = cast(char*)(_oggBuffer.ptr);
786                 alloc.alloc_buffer_length_in_bytes = cast(int)(_oggBuffer.length);
787 
788                 int error;
789                 _oggDecoder = stb_vorbis_open_audioformats(_io, userData, &error, &alloc);
790                 if (_oggDecoder !is null)
791                 {
792                     _format = AudioFileFormat.ogg;
793                     _sampleRate = _oggDecoder.sample_rate;
794                     _numChannels = _oggDecoder.channels;
795                     _lengthInFrames = stb_vorbis_stream_length_in_samples(_oggDecoder);
796                     return;
797                 }
798             }
799         }
800 
801         version(decodeMP3)
802         {
803             // Check if it's a MP3.
804             {
805                 _io.seek(0, false, userData);
806 
807                 ubyte* scratchBuffer = cast(ubyte*) malloc(MINIMP3_BUF_SIZE*2);
808                 scope(exit) free(scratchBuffer);
809 
810                 _mp3io = cast(mp3dec_io_t*) malloc(mp3dec_io_t.sizeof);
811                 _mp3io.read      = &mp3_io_read;
812                 _mp3io.read_data = _decoderContext;
813                 _mp3io.seek      = &mp3_io_seek;
814                 _mp3io.seek_data = _decoderContext;
815 
816                 if ( mp3dec_detect_cb(_mp3io, scratchBuffer, MINIMP3_BUF_SIZE*2) == 0 )
817                 {
818                     // This is a MP3. Try to open a stream.
819 
820                     // Allocate a mp3dec_ex_t object
821                     _mp3DecoderNew = cast(mp3dec_ex_t*) malloc(mp3dec_ex_t.sizeof);
822 
823                     int result = mp3dec_ex_open_cb(_mp3DecoderNew, _mp3io, MP3D_SEEK_TO_SAMPLE);
824 
825                     if (0 == result)
826                     {
827                         // MP3 detected
828                         // but it seems we need to iterate all frames to know the length...
829                         _format = AudioFileFormat.mp3;
830                         _sampleRate = _mp3DecoderNew.info.hz;
831                         _numChannels = _mp3DecoderNew.info.channels;
832                         _lengthInFrames = _mp3DecoderNew.samples / _numChannels;
833                         return;
834                     }
835                     else
836                     {
837                         free(_mp3DecoderNew);
838                         _mp3DecoderNew = null;
839                         free(_mp3io);
840                         _mp3io = null;
841                     }
842                 }
843             }
844         }
845 
846         version(decodeXM)
847         {
848             {
849                 // we need the first 60 bytes to check if XM
850                 char[60] xmHeader;
851                 int bytes;
852 
853                 _io.seek(0, false, userData);
854                 long lenBytes = _io.getFileLength(userData);
855                 if (lenBytes < 60) 
856                     goto not_a_xm;
857 
858                 bytes = _io.read(xmHeader.ptr, 60, userData);
859                 if (bytes != 60)
860                     goto not_a_xm;
861 
862                if (0 != xm_check_sanity_preload(xmHeader.ptr, 60))
863                    goto not_a_xm;
864 
865                 _xmContent = cast(ubyte*) malloc(cast(int)lenBytes);
866                 _io.seek(0, false, userData);
867                 bytes = _io.read(_xmContent, cast(int)lenBytes, userData);
868                 if (bytes != cast(int)lenBytes)
869                     goto not_a_xm;
870 
871                 if (0 == xm_create_context_safe(&_xmDecoder, cast(const(char)*)_xmContent, cast(size_t)lenBytes, 44100))
872                 {
873                     assert(_xmDecoder !is null);
874 
875                     xm_set_max_loop_count(_xmDecoder, 1);
876 
877                     _format = AudioFileFormat.xm;
878                     _sampleRate = 44100.0f;
879                     _numChannels = 2;
880                     _lengthInFrames = audiostreamUnknownLength;
881                     return;
882                 }
883 
884                 not_a_xm:
885                 assert(_xmDecoder == null);
886                 free(_xmContent);
887                 _xmContent = null;
888             }
889         } 
890 
891         version(decodeMOD)
892         {
893             {
894                 // we need either the first 1084 or 600 bytes if available
895                 _io.seek(0, false, userData);
896                 long lenBytes = _io.getFileLength(userData);
897                 if (lenBytes >= 600)
898                 {
899                     int headerBytes = lenBytes > 1084 ? 1084 : cast(int)lenBytes;
900 
901                     ubyte[1084] header;
902                     int bytes = _io.read(header.ptr, headerBytes, userData);
903 
904                     if (_pocketmod_ident(null, header.ptr, bytes))
905                     {
906                         // This is a MOD, allocate a proper context, and read the whole file.
907                         _modDecoder = cast(pocketmod_context*) malloc(pocketmod_context.sizeof);
908 
909                         // Read whole .mod in a buffer, since the decoder work all from memory
910                         _io.seek(0, false, userData);
911                         _modContent.reallocBuffer(cast(size_t)lenBytes);
912                         bytes = _io.read(_modContent.ptr, cast(int)lenBytes, userData);
913 
914                         if (pocketmod_init(_modDecoder, _modContent.ptr, bytes, 44100))
915                         {
916                             _format = AudioFileFormat.mod;
917                             _sampleRate = 44100.0f;
918                             _numChannels = 2;
919                             _lengthInFrames = audiostreamUnknownLength;
920                             return;
921                         }
922                     }
923                 }
924             }
925         }
926 
927         _format = AudioFileFormat.unknown;
928         _sampleRate = float.nan;
929         _numChannels = 0;
930         _lengthInFrames = -1;
931 
932         throw mallocNew!Exception("Cannot decode stream: unrecognized encoding.");
933     }
934 
935     void startEncoding(AudioFileFormat format, float sampleRate, int numChannels) @nogc
936     { 
937         _format = format;
938         _sampleRate = sampleRate;
939         _numChannels = numChannels;
940 
941         final switch(format) with (AudioFileFormat)
942         {
943             case mp3:
944                 throw mallocNew!Exception("Unsupported encoding format: MP3");
945             case flac:
946                 throw mallocNew!Exception("Unsupported encoding format: FLAC");
947             case ogg:
948                 throw mallocNew!Exception("Unsupported encoding format: OGG");
949             case opus:
950                 throw mallocNew!Exception("Unsupported encoding format: Opus");
951             case mod:
952                 throw mallocNew!Exception("Unsupported encoding format: MOD");
953             case xm:
954                 throw mallocNew!Exception("Unsupported encoding format: XM");
955             case wav:
956             {
957                 // Note: fractional sample rates not supported by WAV, signal an integer one
958                 int isampleRate = cast(int)(sampleRate + 0.5f);
959                 _wavEncoder = mallocNew!WAVEncoder(_io, userData, isampleRate, numChannels );
960                 break;
961             }
962             case unknown:
963                 throw mallocNew!Exception("Can't encode using 'unknown' coding");
964         }        
965     }   
966 
967     void finalizeEncodingIfNeeded() @nogc
968     {
969         if (_io && (_io.write !is null)) // if we have been encoding something
970         {
971             finalizeEncoding();
972         }
973     }
974 }
975 
976 // AudioStream should be able to go on a smallish 32-bit stack,
977 // and malloc the rest on the heap when needed.
978 static assert(AudioStream.sizeof <= 256); 
979 
980 private: // not meant to be imported at all
981 
982 
983 
984 // Internal object for audio-formats
985 
986 
987 // File callbacks
988 // The file callbacks are using the C stdlib.
989 
990 struct FileContext // this is what is passed to I/O when used in file mode
991 {
992     // Used when streaming of writing a file
993     FILE* file = null;
994 
995     // Size of the file in bytes, only used when reading/writing a file.
996     long fileSize;
997 
998     // Initialize this context
999     void initialize(const(char)[] path, bool forWrite) @nogc
1000     {
1001         CString strZ = CString(path);
1002         file = fopen(strZ.storage, forWrite ? "wb".ptr : "rb".ptr);
1003         // finds the size of the file
1004         fseek(file, 0, SEEK_END);
1005         fileSize = ftell(file);
1006         fseek(file, 0, SEEK_SET);
1007     }
1008 }
1009 
1010 long file_tell(void* userData) nothrow @nogc
1011 {
1012     FileContext* context = cast(FileContext*)userData;
1013     return ftell(context.file);
1014 }
1015 
1016 void file_seek(long offset, bool relative, void* userData) nothrow @nogc
1017 {
1018     FileContext* context = cast(FileContext*)userData;
1019     assert(offset <= int.max);
1020     fseek(context.file, cast(int)offset, relative ? SEEK_CUR : SEEK_SET); // Limitations: file larger than 2gb not supported
1021 }
1022 
1023 long file_getFileLength(void* userData) nothrow @nogc
1024 {
1025     FileContext* context = cast(FileContext*)userData;
1026     return context.fileSize;
1027 }
1028 
1029 int file_read(void* outData, int bytes, void* userData) nothrow @nogc
1030 {
1031     FileContext* context = cast(FileContext*)userData;
1032     size_t bytesRead = fread(outData, 1, bytes, context.file);
1033     return cast(int)bytesRead;
1034 }
1035 
1036 int file_write(void* inData, int bytes, void* userData) nothrow @nogc
1037 {
1038     FileContext* context = cast(FileContext*)userData;
1039     size_t bytesWritten = fwrite(inData, 1, bytes, context.file);
1040     return cast(int)bytesWritten;
1041 }
1042 
1043 bool file_skip(int bytes, void* userData) nothrow @nogc
1044 {
1045     FileContext* context = cast(FileContext*)userData;
1046     return (0 == fseek(context.file, bytes, SEEK_CUR));
1047 }
1048 
1049 bool file_flush(void* userData) nothrow @nogc
1050 {
1051     FileContext* context = cast(FileContext*)userData;
1052     return ( fflush(context.file) == 0 );
1053 }
1054 
1055 // Memory read callback
1056 // Using the read buffer instead
1057 
1058 struct MemoryContext
1059 {
1060     bool bufferIsOwned;
1061     bool bufferCanGrow;
1062 
1063     // Buffer
1064     ubyte* buffer = null;
1065 
1066     size_t size;     // current buffer size
1067     size_t cursor;   // where we are in the buffer
1068     size_t capacity; // max buffer size before realloc
1069 
1070     void initializeWithConstantInput(const(ubyte)* data, size_t length) nothrow @nogc
1071     {
1072         // Make a copy of the input buffer, since it could be temporary.
1073         bufferIsOwned = true;
1074         bufferCanGrow = false;
1075 
1076         buffer = mallocDup(data[0..length]).ptr; // Note: the copied slice is made mutable.
1077         size = length;
1078         cursor = 0;
1079         capacity = length;
1080     }
1081 
1082     void initializeWithExternalOutputBuffer(ubyte* data, size_t length) nothrow @nogc
1083     {
1084         bufferIsOwned = false;
1085         bufferCanGrow = false;
1086         buffer = data;
1087         size = 0;
1088         cursor = 0;
1089         capacity = length;
1090     }
1091 
1092     void initializeWithInternalGrowableBuffer() nothrow @nogc
1093     {
1094         bufferIsOwned = true;
1095         bufferCanGrow = true;
1096         buffer = null;
1097         size = 0;
1098         cursor = 0;
1099         capacity = 0;
1100     }
1101 
1102     ~this()
1103     {
1104         if (bufferIsOwned)
1105         {
1106             if (buffer !is null)
1107             {
1108                 free(buffer);
1109                 buffer = null;
1110             }
1111         }
1112     }
1113 }
1114 
1115 long memory_tell(void* userData) nothrow @nogc
1116 {
1117     MemoryContext* context = cast(MemoryContext*)userData;
1118     return cast(long)(context.cursor);
1119 }
1120 
1121 void memory_seek(long offset, bool relative, void* userData) nothrow @nogc
1122 {
1123     MemoryContext* context = cast(MemoryContext*)userData;    
1124     if (relative) offset += context.cursor;
1125     if (offset >= context.size) // can't seek past end of buffer, stick to the end so that read return 0 byte
1126         offset = context.size;
1127     context.cursor = cast(size_t)offset; // Note: memory streams larger than 2gb not supported
1128 }
1129 
1130 long memory_getFileLength(void* userData) nothrow @nogc
1131 {
1132     MemoryContext* context = cast(MemoryContext*)userData;
1133     return cast(long)(context.size);
1134 }
1135 
1136 int memory_read(void* outData, int bytes, void* userData) nothrow @nogc
1137 {
1138     MemoryContext* context = cast(MemoryContext*)userData;
1139     size_t cursor = context.cursor;
1140     size_t size = context.size;
1141     size_t available = size - cursor;
1142     if (bytes < available)
1143     {
1144         outData[0..bytes] = context.buffer[cursor..cursor + bytes];
1145         context.cursor += bytes;
1146         return bytes;
1147     }
1148     else
1149     {
1150         outData[0..available] = context.buffer[cursor..cursor + available];
1151         context.cursor = context.size;
1152         return cast(int)available;
1153     }
1154 }
1155 
1156 int memory_write_limited(void* inData, int bytes, void* userData) nothrow @nogc
1157 {
1158     MemoryContext* context = cast(MemoryContext*)userData;
1159     size_t cursor = context.cursor;
1160     size_t size = context.size;
1161     size_t available = size - cursor;
1162     ubyte* buffer = context.buffer;
1163     ubyte* source = cast(ubyte*) inData;
1164 
1165     if (cursor + bytes > available)
1166     {
1167         bytes = cast(int)(available - cursor);       
1168     }
1169 
1170     buffer[cursor..(cursor + bytes)] = source[0..bytes];
1171     context.size += bytes;
1172     context.cursor += bytes;
1173     return bytes;
1174 }
1175 
1176 int memory_write_append(void* inData, int bytes, void* userData) nothrow @nogc
1177 {
1178     MemoryContext* context = cast(MemoryContext*)userData;
1179     size_t cursor = context.cursor;
1180     size_t size = context.size;
1181     size_t available = size - cursor;
1182     ubyte* buffer = context.buffer;
1183     ubyte* source = cast(ubyte*) inData;
1184 
1185     if (cursor + bytes > available)
1186     {
1187         size_t oldSize = context.capacity;
1188         size_t newSize = cursor + bytes;
1189         if (newSize < oldSize * 2 + 1) 
1190             newSize = oldSize * 2 + 1;
1191         buffer = cast(ubyte*) realloc(buffer, newSize);
1192         context.capacity = newSize;
1193 
1194         assert( cursor + bytes <= available );
1195     }
1196 
1197     buffer[cursor..(cursor + bytes)] = source[0..bytes];
1198     context.size += bytes;
1199     context.cursor += bytes;
1200     return bytes;
1201 }
1202 
1203 bool memory_skip(int bytes, void* userData) nothrow @nogc
1204 {
1205     MemoryContext* context = cast(MemoryContext*)userData;
1206     context.cursor += bytes;
1207     return context.cursor <= context.size;
1208 }
1209 
1210 bool memory_flush(void* userData) nothrow @nogc
1211 {
1212     // do nothing, no flushign to do for memory
1213     return true;
1214 }
1215 
1216 
1217 // Decoder context
1218 struct DecoderContext
1219 {
1220     void* userDataIO;
1221     IOCallbacks* callbacks;
1222 }
1223 
1224 // MP3 decoder read callback
1225 static int mp3ReadDelegate(void[] buf, void* userDataDecoder) @nogc nothrow
1226 {
1227     DecoderContext* context = cast(DecoderContext*) userDataDecoder;
1228 
1229     // read bytes into the buffer, return number of bytes read or 0 for EOF, -1 on error
1230     // will never be called with empty buffer, or buffer more than 128KB
1231 
1232     int bytes = context.callbacks.read(buf.ptr, cast(int)(buf.length), context.userDataIO);
1233     return bytes;
1234 }
1235 
1236 
1237 // FLAC decoder read callbacks
1238 
1239 size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead) @nogc nothrow
1240 {
1241     DecoderContext* context = cast(DecoderContext*) pUserData;
1242     return context.callbacks.read(pBufferOut, cast(int)(bytesToRead), context.userDataIO);
1243 }
1244 
1245 bool flac_seek(void* pUserData, int offset, drflac_seek_origin origin) @nogc nothrow
1246 {
1247     DecoderContext* context = cast(DecoderContext*) pUserData;
1248     if (origin == drflac_seek_origin_start)
1249     {
1250         context.callbacks.seek(offset, false, context.userDataIO);
1251     }
1252     else if (origin == drflac_seek_origin_current)
1253     {
1254         context.callbacks.seek(offset, true, context.userDataIO);
1255     }
1256     return true;
1257 }
1258 
1259 // MP3 decoder read callbacks
1260 
1261 size_t mp3_io_read(void *buf, size_t size, void *user_data) @nogc nothrow
1262 {
1263     DecoderContext* context = cast(DecoderContext*) user_data;
1264     return context.callbacks.read(buf, cast(int)(size), context.userDataIO);
1265 }
1266 
1267 int mp3_io_seek(ulong position, void *user_data) @nogc nothrow
1268 {
1269     DecoderContext* context = cast(DecoderContext*) user_data;
1270     context.callbacks.seek(position, false, context.userDataIO);
1271     return 0; // doesn't detect seeking errors
1272 }