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