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