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