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