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