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