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.stb_vorbis2; 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 /// Output sample format. 48 enum AudioSampleFormat 49 { 50 fp32, /// 32-bit floating-point 51 fp64 /// 64-bit floating-point 52 } 53 54 /// An optional struct, passed when encoding a sound. 55 struct EncodingOptions 56 { 57 /// The desired sample bitdepth to encode with. 58 AudioSampleFormat sampleFormat = AudioSampleFormat.fp32; // defaults to 32-bit float 59 } 60 61 /// Returns: String representation of an `AudioFileFormat`. 62 string convertAudioFileFormatToString(AudioFileFormat fmt) 63 { 64 final switch(fmt) with (AudioFileFormat) 65 { 66 case wav: return "wav"; 67 case mp3: return "mp3"; 68 case flac: return "flac"; 69 case ogg: return "ogg"; 70 case opus: return "opus"; 71 case mod: return "mod"; 72 case xm: return "xm"; 73 case unknown: return "unknown"; 74 } 75 } 76 77 78 /// The length of things you shouldn't query a length about: 79 /// - files that are being written 80 /// - audio files you don't know the extent 81 enum audiostreamUnknownLength = -1; 82 83 /// An AudioStream is a pointer to a dynamically allocated `Stream`. 84 public struct AudioStream 85 { 86 public: // This is also part of the public API 87 88 89 /// Opens an audio stream that decodes from a file. 90 /// This stream will be opened for reading only. 91 /// 92 /// Params: 93 /// path An UTF-8 path to the sound file. 94 /// 95 /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`. 96 void openFromFile(const(char)[] path) @nogc 97 { 98 cleanUp(); 99 100 fileContext = mallocNew!FileContext(); 101 fileContext.initialize(path, false); 102 userData = fileContext; 103 104 _io = mallocNew!IOCallbacks(); 105 _io.seek = &file_seek; 106 _io.tell = &file_tell; 107 _io.getFileLength = &file_getFileLength; 108 _io.read = &file_read; 109 _io.write = null; 110 _io.skip = &file_skip; 111 _io.flush = null; 112 113 startDecoding(); 114 } 115 116 /// Opens an audio stream that decodes from memory. 117 /// This stream will be opened for reading only. 118 /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`. 119 /// 120 /// Params: inputData The whole file to decode. 121 void openFromMemory(const(ubyte)[] inputData) @nogc 122 { 123 cleanUp(); 124 125 memoryContext = mallocNew!MemoryContext(); 126 memoryContext.initializeWithConstantInput(inputData.ptr, inputData.length); 127 128 userData = memoryContext; 129 130 _io = mallocNew!IOCallbacks(); 131 _io.seek = &memory_seek; 132 _io.tell = &memory_tell; 133 _io.getFileLength = &memory_getFileLength; 134 _io.read = &memory_read; 135 _io.write = null; 136 _io.skip = &memory_skip; 137 _io.flush = null; 138 139 startDecoding(); 140 } 141 142 /// Opens an audio stream that writes to file. 143 /// This stream will be open for writing only. 144 /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`. 145 /// 146 /// Params: 147 /// path An UTF-8 path to the sound file. 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 openToFile(const(char)[] path, 152 AudioFileFormat format, 153 float sampleRate, 154 int numChannels, 155 EncodingOptions options = EncodingOptions.init) @nogc 156 { 157 cleanUp(); 158 159 fileContext = mallocNew!FileContext(); 160 fileContext.initialize(path, true); 161 userData = fileContext; 162 163 _io = mallocNew!IOCallbacks(); 164 _io.seek = &file_seek; 165 _io.tell = &file_tell; 166 _io.getFileLength = null; 167 _io.read = null; 168 _io.write = &file_write; 169 _io.skip = null; 170 _io.flush = &file_flush; 171 172 startEncoding(format, sampleRate, numChannels, options); 173 } 174 175 /// Opens an audio stream that writes to a dynamically growable output buffer. 176 /// This stream will be open for writing only. 177 /// Access to the internal buffer after encoding with `finalizeAndGetEncodedResult`. 178 /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`. 179 /// 180 /// Params: 181 /// format Audio file format to generate. 182 /// sampleRate Sample rate of this audio stream. This samplerate might be rounded up to the nearest integer number. 183 /// numChannels Number of channels of this audio stream. 184 void openToBuffer(AudioFileFormat format, 185 float sampleRate, 186 int numChannels, 187 EncodingOptions options = EncodingOptions.init) @nogc 188 { 189 cleanUp(); 190 191 memoryContext = mallocNew!MemoryContext(); 192 memoryContext.initializeWithInternalGrowableBuffer(); 193 userData = memoryContext; 194 195 _io = mallocNew!IOCallbacks(); 196 _io.seek = &memory_seek; 197 _io.tell = &memory_tell; 198 _io.getFileLength = null; 199 _io.read = null; 200 _io.write = &memory_write_append; 201 _io.skip = null; 202 _io.flush = &memory_flush; 203 204 startEncoding(format, sampleRate, numChannels, options); 205 } 206 207 /// Opens an audio stream that writes to a pre-defined area in memory of `maxLength` bytes. 208 /// This stream will be open for writing only. 209 /// Destroy this stream with `closeAudioStream`. 210 /// Note: throws a manually allocated exception in case of error. Free it with `dplug.core.destroyFree`. 211 /// 212 /// Params: 213 /// data Pointer to output memory. 214 /// size_t maxLength. 215 /// format Audio file format to generate. 216 /// sampleRate Sample rate of this audio stream. This samplerate might be rounded up to the nearest integer number. 217 /// numChannels Number of channels of this audio stream. 218 void openToMemory(ubyte* data, 219 size_t maxLength, 220 AudioFileFormat format, 221 float sampleRate, 222 int numChannels, 223 EncodingOptions options = EncodingOptions.init) @nogc 224 { 225 cleanUp(); 226 227 memoryContext = mallocNew!MemoryContext(); 228 memoryContext.initializeWithExternalOutputBuffer(data, maxLength); 229 userData = memoryContext; 230 231 _io = mallocNew!IOCallbacks(); 232 _io.seek = &memory_seek; 233 _io.tell = &memory_tell; 234 _io.getFileLength = null; 235 _io.read = null; 236 _io.write = &memory_write_limited; 237 _io.skip = null; 238 _io.flush = &memory_flush; 239 240 startEncoding(format, sampleRate, numChannels, options); 241 } 242 243 ~this() @nogc 244 { 245 cleanUp(); 246 } 247 248 void cleanUp() @nogc 249 { 250 // Write the last needed bytes if needed 251 finalizeEncodingIfNeeded(); 252 253 version(decodeMP3) 254 { 255 if (_mp3DecoderNew !is null) 256 { 257 mp3dec_ex_close(_mp3DecoderNew); 258 free(_mp3DecoderNew); 259 _mp3DecoderNew = null; 260 } 261 if (_mp3io !is null) 262 { 263 free(_mp3io); 264 _mp3io = null; 265 } 266 } 267 268 version(decodeFLAC) 269 { 270 if (_flacDecoder !is null) 271 { 272 drflac_close(_flacDecoder); 273 _flacDecoder = null; 274 _flacPositionFrame = 0; 275 } 276 } 277 278 version(decodeOGG) 279 { 280 if (_oggHandle !is null) 281 { 282 stb_vorbis_close(_oggHandle); 283 _oggHandle = null; 284 } 285 _oggBuffer.reallocBuffer(0); 286 } 287 288 version(decodeOPUS) 289 { 290 if (_opusDecoder !is null) 291 { 292 opusClose(_opusDecoder); 293 _opusDecoder = null; 294 } 295 _opusBuffer = null; 296 } 297 298 version(decodeWAV) 299 { 300 if (_wavDecoder !is null) 301 { 302 destroyFree(_wavDecoder); 303 _wavDecoder = null; 304 } 305 } 306 307 version(decodeXM) 308 { 309 if (_xmDecoder !is null) 310 { 311 xm_free_context(_xmDecoder); 312 _xmDecoder = null; 313 } 314 if (_xmContent != null) 315 { 316 free(_xmContent); 317 _xmContent = null; 318 } 319 } 320 321 version(decodeMOD) 322 { 323 if (_modDecoder !is null) 324 { 325 free(_modDecoder); 326 _modDecoder = null; 327 _modContent.reallocBuffer(0); 328 } 329 } 330 331 version(encodeWAV) 332 { 333 if (_wavEncoder !is null) 334 { 335 destroyFree(_wavEncoder); 336 _wavEncoder = null; 337 } 338 } 339 340 if (_decoderContext) 341 { 342 destroyFree(_decoderContext); 343 _decoderContext = null; 344 } 345 346 if (fileContext !is null) 347 { 348 if (fileContext.file !is null) 349 { 350 int result = fclose(fileContext.file); 351 if (result) 352 throw mallocNew!Exception("Closing of audio file errored"); 353 } 354 destroyFree(fileContext); 355 fileContext = null; 356 } 357 358 if (memoryContext !is null) 359 { 360 // TODO destroy buffer if any is owned 361 destroyFree(memoryContext); 362 memoryContext = null; 363 } 364 365 if (_io !is null) 366 { 367 destroyFree(_io); 368 _io = null; 369 } 370 } 371 372 /// Returns: File format of this stream. 373 AudioFileFormat getFormat() nothrow @nogc 374 { 375 return _format; 376 } 377 378 /// Returns: `true` if using this stream's operations is acceptable in an audio thread (eg: no file I/O). 379 bool realtimeSafe() @nogc 380 { 381 return fileContext is null; 382 } 383 384 /// Returns: `true` if this stream is concerning a tracker module format. 385 /// This is useful because the seek/tell functions are different. 386 bool isModule() @nogc 387 { 388 final switch(_format) with (AudioFileFormat) 389 { 390 case wav: 391 case mp3: 392 case flac: 393 case ogg: 394 case opus: 395 return false; 396 case mod: 397 case xm: 398 return true; 399 case unknown: 400 assert(false); 401 402 } 403 } 404 405 /// Returns: `true` if this stream allows seeking. 406 /// Note: the particular function to call for seeking depends on whether the stream is a tracker module. 407 /// See_also: `seekPosition`. 408 bool canSeek() @nogc 409 { 410 final switch(_format) with (AudioFileFormat) 411 { 412 case wav: 413 case mp3: 414 case flac: 415 case ogg: 416 case opus: 417 return true; 418 case mod: 419 case xm: 420 return true; 421 case unknown: 422 assert(false); 423 424 } 425 } 426 427 /// Returns: `true` if this stream is currently open for reading (decoding). 428 /// `false` if the stream has been destroyed, or if it was created for encoding instead. 429 bool isOpenForReading() nothrow @nogc 430 { 431 return (_io !is null) && (_io.read !is null); 432 } 433 434 deprecated("Use isOpenForWriting instead") alias isOpenedForWriting = isOpenForWriting; 435 436 /// Returns: `true` if this stream is currently open for writing (encoding). 437 /// `false` if the stream has been destroyed, finalized with `finalizeEncoding()`, 438 /// or if it was created for decoding instead. 439 bool isOpenForWriting() nothrow @nogc 440 { 441 // Note: 442 // * when opened for reading, I/O operations given are: seek/tell/getFileLength/read. 443 // * when opened for writing, I/O operations given are: seek/tell/write/flush. 444 return (_io !is null) && (_io.read is null); 445 } 446 447 /// Returns: Number of channels in this stream. 1 means mono, 2 means stereo... 448 int getNumChannels() nothrow @nogc 449 { 450 return _numChannels; 451 } 452 453 /// Returns: Length of this stream in frames. 454 /// Note: may return the special value `audiostreamUnknownLength` if the length is unknown. 455 long getLengthInFrames() nothrow @nogc 456 { 457 return _lengthInFrames; 458 } 459 460 /// Returns: Sample-rate of this stream in Hz. 461 float getSamplerate() nothrow @nogc 462 { 463 return _sampleRate; 464 } 465 466 /// Read interleaved float samples in the given buffer `outData`. 467 /// 468 /// Params: 469 /// outData Buffer where to put decoded samples. Samples are arranged in an interleaved fashion. 470 /// Must have room for `frames` x `getNumChannels()` samples. 471 /// For a stereo file, the output data will contain LRLRLR... repeated `result` times. 472 /// 473 /// frames The number of multichannel frames to be read. 474 /// A frame is `getNumChannels()` samples. 475 /// 476 /// Returns: Number of actually read frames. Multiply by `getNumChannels()` to get the number of read samples. 477 /// When that number is less than `frames`, it means the stream is done decoding, or that there was a decoding error. 478 /// 479 /// TODO: once this returned less than `frames`, are we guaranteed we can keep calling that and it returns 0? 480 int readSamplesFloat(float* outData, int frames) @nogc 481 { 482 assert(isOpenForReading()); 483 484 final switch(_format) 485 { 486 case AudioFileFormat.opus: 487 { 488 version(decodeOPUS) 489 { 490 try 491 { 492 // Can't decoder further than end of the stream. 493 if (_opusPositionFrame + frames > _lengthInFrames) 494 { 495 frames = cast(int)(_lengthInFrames - _opusPositionFrame); 496 } 497 498 int decoded = 0; 499 while (decoded < frames) 500 { 501 // Is there any sample left in _opusBuffer? 502 // If not decode some frames. 503 if (_opusBuffer is null || _opusBuffer.length == 0) 504 { 505 _opusBuffer = _opusDecoder.readFrame(); 506 if (_opusBuffer is null) 507 break; 508 } 509 510 int samplesInBuffer = cast(int) _opusBuffer.length; 511 int framesInBuffer = samplesInBuffer / _numChannels; 512 if (framesInBuffer == 0) 513 break; 514 515 // Frames to pull are min( frames left to decode, frames available) 516 int framesToDecode = frames - decoded; 517 int framesToUse = framesToDecode < framesInBuffer ? framesToDecode : framesInBuffer; 518 assert(framesToUse != 0); 519 520 int samplesToUse = framesToUse * _numChannels; 521 int outOffset = decoded*_numChannels; 522 523 if (outData !is null) // for seeking, we have the ability in OPUS to call readSamplesFloat with no outData 524 { 525 for (int n = 0; n < samplesToUse; ++n) 526 { 527 outData[outOffset + n] = _opusBuffer[n] / 32767.0f; 528 } 529 } 530 _opusBuffer = _opusBuffer[samplesToUse..$]; // reduce size of intermediate buffer 531 decoded += framesToUse; 532 } 533 _opusPositionFrame += decoded; 534 assert(_opusPositionFrame <= _lengthInFrames); 535 return decoded; 536 } 537 catch(Exception e) 538 { 539 destroyFree(e); 540 return 0; // decoding might fail, in which case return zero samples 541 } 542 } 543 } 544 545 case AudioFileFormat.flac: 546 { 547 version(decodeFLAC) 548 { 549 assert(_flacDecoder !is null); 550 551 int* integerData = cast(int*)outData; 552 int samples = cast(int) drflac_read_s32(_flacDecoder, frames, integerData); 553 554 // "Samples are always output as interleaved signed 32-bit PCM." 555 // Convert to float with type-punning. Note that this looses some precision. 556 double factor = 1.0 / int.max; 557 foreach(n; 0..samples) 558 { 559 outData[n] = integerData[n] * factor; 560 } 561 int framesDecoded = samples / _numChannels; 562 _flacPositionFrame += framesDecoded; 563 return framesDecoded; 564 } 565 else 566 { 567 assert(false); // Impossible 568 } 569 } 570 571 case AudioFileFormat.ogg: 572 { 573 version(decodeOGG) 574 { 575 assert(_oggHandle !is null); 576 return stb_vorbis_get_samples_float_interleaved(_oggHandle, _numChannels, outData, frames * _numChannels); 577 } 578 else 579 { 580 assert(false); // Impossible 581 } 582 } 583 584 case AudioFileFormat.mp3: 585 { 586 version(decodeMP3) 587 { 588 assert(_mp3DecoderNew !is null); 589 590 int samplesNeeded = frames * _numChannels; 591 int result = cast(int) mp3dec_ex_read(_mp3DecoderNew, outData, samplesNeeded); 592 if (result < 0) // error 593 return 0; 594 return result / _numChannels; 595 } 596 else 597 { 598 assert(false); // Impossible 599 } 600 } 601 case AudioFileFormat.wav: 602 version(decodeWAV) 603 { 604 assert(_wavDecoder !is null); 605 int readFrames = _wavDecoder.readSamples!float(outData, frames); 606 return readFrames; 607 } 608 else 609 { 610 assert(false); // Impossible 611 } 612 613 case AudioFileFormat.xm: 614 version(decodeXM) 615 { 616 assert(_xmDecoder !is null); 617 618 if (xm_get_loop_count(_xmDecoder) >= 1) 619 return 0; // song is finished 620 621 xm_generate_samples(_xmDecoder, outData, frames); 622 return frames; // Note: XM decoder pads end with zeroes. 623 } 624 else 625 { 626 assert(false); // Impossible 627 } 628 629 case AudioFileFormat.mod: 630 version(decodeMOD) 631 { 632 if (pocketmod_loop_count(_modDecoder) >= 1) 633 return 0; // end stream after MOD finishes, looping not supported 634 assert(_modDecoder !is null); 635 int bytesReturned = pocketmod_render(_modDecoder, outData, frames * 2 * 4); 636 assert((bytesReturned % 8) == 0); 637 return bytesReturned / 8; 638 } 639 else 640 { 641 assert(false); // Impossible 642 } 643 644 case AudioFileFormat.unknown: 645 // One shouldn't ever get there, since in this case 646 // opening has failed. 647 assert(false); 648 } 649 } 650 ///ditto 651 int readSamplesFloat(float[] outData) @nogc 652 { 653 assert( (outData.length % _numChannels) == 0); 654 return readSamplesFloat(outData.ptr, cast(int)(outData.length / _numChannels) ); 655 } 656 657 /// Read interleaved double samples in the given buffer `outData`. 658 /// 659 /// Params: 660 /// outData Buffer where to put decoded samples. Samples are arranged in an interleaved fashion. 661 /// Must have room for `frames` x `getNumChannels()` samples. 662 /// For a stereo file, the output data will contain LRLRLR... repeated `result` times. 663 /// 664 /// frames The number of multichannel frames to be read. 665 /// A frame is `getNumChannels()` samples. 666 /// 667 /// Note: the only formats to possibly take advantage of double decoding are WAV and FLAC. 668 /// 669 /// Returns: Number of actually read frames. Multiply by `getNumChannels()` to get the number of read samples. 670 /// When that number is less than `frames`, it means the stream is done decoding, or that there was a decoding error. 671 /// 672 /// TODO: once this returned less than `frames`, are we guaranteed we can keep calling that and it returns 0? 673 int readSamplesDouble(double* outData, int frames) @nogc 674 { 675 assert(isOpenForReading()); 676 677 switch(_format) 678 { 679 case AudioFileFormat.wav: 680 version(decodeWAV) 681 { 682 assert(_wavDecoder !is null); 683 int readFrames = _wavDecoder.readSamples!double(outData, frames); 684 return readFrames; 685 } 686 else 687 { 688 assert(false); // Impossible 689 } 690 691 case AudioFileFormat.unknown: 692 // One shouldn't ever get there 693 assert(false); 694 695 default: 696 // Decode to float buffer, and then convert 697 if (_floatDecodeBuf.length < frames * _numChannels) 698 _floatDecodeBuf.reallocBuffer(frames * _numChannels); 699 int read = readSamplesFloat(_floatDecodeBuf.ptr, frames); 700 for (int n = 0; n < read * _numChannels; ++n) 701 outData[n] = _floatDecodeBuf[n]; 702 return read; 703 } 704 } 705 ///ditto 706 int readSamplesDouble(double[] outData) @nogc 707 { 708 assert( (outData.length % _numChannels) == 0); 709 return readSamplesDouble(outData.ptr, cast(int)(outData.length / _numChannels) ); 710 } 711 712 /// Write interleaved float samples to the stream, from the given buffer `inData[0..frames]`. 713 /// 714 /// Params: 715 /// inData Buffer of interleaved samples to append to the stream. 716 /// Must contain `frames` x `getNumChannels()` samples. 717 /// For a stereo file, `inData` contains LRLRLR... repeated `frames` times. 718 /// 719 /// frames The number of frames to append to the stream. 720 /// A frame is `getNumChannels()` samples. 721 /// 722 /// Returns: Number of actually written frames. Multiply by `getNumChannels()` to get the number of written samples. 723 /// When that number is less than `frames`, it means the stream had a write error. 724 int writeSamplesFloat(float* inData, int frames) nothrow @nogc 725 { 726 assert(_io && _io.write !is null); 727 728 final switch(_format) 729 { 730 case AudioFileFormat.mp3: 731 case AudioFileFormat.flac: 732 case AudioFileFormat.ogg: 733 case AudioFileFormat.opus: 734 case AudioFileFormat.mod: 735 case AudioFileFormat.xm: 736 case AudioFileFormat.unknown: 737 { 738 assert(false); // Shouldn't have arrived here, such encoding aren't supported. 739 } 740 case AudioFileFormat.wav: 741 { 742 version(encodeWAV) 743 { 744 return _wavEncoder.writeSamples(inData, frames); 745 } 746 else 747 { 748 assert(false, "no support for WAV encoding"); 749 } 750 } 751 } 752 } 753 ///ditto 754 int writeSamplesFloat(float[] inData) nothrow @nogc 755 { 756 assert( (inData.length % _numChannels) == 0); 757 return writeSamplesFloat(inData.ptr, cast(int)(inData.length / _numChannels)); 758 } 759 760 /// Write interleaved double samples to the stream, from the given buffer `inData[0..frames]`. 761 /// 762 /// Params: 763 /// inData Buffer of interleaved samples to append to the stream. 764 /// Must contain `frames` x `getNumChannels()` samples. 765 /// For a stereo file, `inData` contains LRLRLR... repeated `frames` times. 766 /// 767 /// frames The number of frames to append to the stream. 768 /// A frame is `getNumChannels()` samples. 769 /// 770 /// Note: this only does something if the output format is WAV and was setup for 64-bit output. 771 /// 772 /// Returns: Number of actually written frames. Multiply by `getNumChannels()` to get the number of written samples. 773 /// When that number is less than `frames`, it means the stream had a write error. 774 int writeSamplesDouble(double* inData, int frames) nothrow @nogc 775 { 776 assert(_io && _io.write !is null); 777 778 switch(_format) 779 { 780 case AudioFileFormat.unknown: 781 // One shouldn't ever get there 782 assert(false); 783 784 case AudioFileFormat.wav: 785 { 786 version(encodeWAV) 787 { 788 return _wavEncoder.writeSamples(inData, frames); 789 } 790 else 791 { 792 assert(false, "no support for WAV encoding"); 793 } 794 } 795 796 default: 797 // Decode to float buffer, and then convert 798 if (_floatDecodeBuf.length < frames * _numChannels) 799 _floatDecodeBuf.reallocBuffer(frames * _numChannels); 800 801 for (int n = 0; n < frames * _numChannels; ++n) 802 _floatDecodeBuf[n] = inData[n]; 803 804 return writeSamplesFloat(_floatDecodeBuf.ptr, frames); 805 } 806 } 807 ///ditto 808 int writeSamplesDouble(double[] inData) nothrow @nogc 809 { 810 assert( (inData.length % _numChannels) == 0); 811 return writeSamplesDouble(inData.ptr, cast(int)(inData.length / _numChannels)); 812 } 813 814 // ----------------------------------------------------------------------------------------------------- 815 // <module functions> 816 // Those tracker module-specific functions below can only be called when `isModule()` returns `true`. 817 // Additionally, seeking function can only be called if `canSeek()` also returns `true`. 818 // ----------------------------------------------------------------------------------------------------- 819 820 /// Length. Returns the amount of patterns in the module 821 /// Formats that support this: MOD, XM. 822 int countModulePatterns() 823 { 824 assert(isOpenForReading() && isModule()); 825 final switch(_format) with (AudioFileFormat) 826 { 827 case mp3: 828 case flac: 829 case ogg: 830 case opus: 831 case wav: 832 case unknown: 833 assert(false); 834 case mod: 835 return _modDecoder.num_patterns; 836 case xm: 837 return xm_get_number_of_patterns(_xmDecoder); 838 } 839 } 840 841 /// Length. Returns the amount of PLAYED patterns in the module 842 /// Formats that support this: MOD, XM. 843 int getModuleLength() 844 { 845 assert(isOpenForReading() && isModule()); 846 final switch(_format) with (AudioFileFormat) 847 { 848 case mp3: 849 case flac: 850 case ogg: 851 case opus: 852 case wav: 853 case unknown: 854 assert(false); 855 case mod: 856 return _modDecoder.length; 857 case xm: 858 return xm_get_module_length(_xmDecoder); 859 } 860 } 861 862 /// Tell. Returns amount of rows in a pattern. 863 /// Formats that support this: MOD, XM. 864 /// Returns: -1 on error. Else, number of patterns. 865 int rowsInPattern(int pattern) 866 { 867 assert(isOpenForReading() && isModule()); 868 final switch(_format) with (AudioFileFormat) 869 { 870 case mp3: 871 case flac: 872 case ogg: 873 case opus: 874 case wav: 875 case unknown: 876 assert(false); 877 878 case mod: 879 // According to http://lclevy.free.fr/mo3/mod.txt 880 // there's 64 lines (aka rows) per pattern. 881 // TODO: error checking, make sure no out of bounds happens. 882 return 64; 883 884 case xm: 885 { 886 int numPatterns = xm_get_number_of_patterns(_xmDecoder); 887 if (pattern < 0 || pattern >= numPatterns) 888 return -1; 889 890 return xm_get_number_of_rows(_xmDecoder, cast(ushort) pattern); 891 } 892 } 893 } 894 895 /// Tell. Returns the current playing pattern id 896 /// Formats that support this: MOD, XM 897 int tellModulePattern() 898 { 899 assert(isOpenForReading() && isModule()); 900 final switch(_format) with (AudioFileFormat) 901 { 902 case mp3: 903 case flac: 904 case ogg: 905 case opus: 906 case wav: 907 case unknown: 908 assert(false); 909 case mod: 910 return _modDecoder.pattern; 911 case xm: 912 return _xmDecoder.current_table_index; 913 } 914 } 915 916 /// Tell. Returns the current playing row id 917 /// Formats that support this: MOD, XM 918 int tellModuleRow() 919 { 920 assert(isOpenForReading() && isModule()); 921 final switch(_format) with (AudioFileFormat) 922 { 923 case mp3: 924 case flac: 925 case ogg: 926 case opus: 927 case wav: 928 case unknown: 929 assert(false); 930 case mod: 931 return _modDecoder.line; 932 case xm: 933 return _xmDecoder.current_row; 934 } 935 } 936 937 /// Playback info. Returns the amount of multi-channel frames remaining in the current playing pattern. 938 /// Formats that support this: MOD 939 int framesRemainingInPattern() 940 { 941 assert(isOpenForReading() && isModule()); 942 final switch(_format) with (AudioFileFormat) 943 { 944 case mp3: 945 case flac: 946 case ogg: 947 case opus: 948 case wav: 949 case unknown: 950 assert(false); 951 952 case mod: 953 return pocketmod_count_remaining_samples(_modDecoder); 954 case xm: 955 return xm_count_remaining_samples(_xmDecoder); 956 } 957 } 958 959 /// Seeking. Subsequent reads start from pattern + row, 0 index 960 /// Only available for input streams. 961 /// Formats that support seeking per pattern/row: MOD, XM 962 /// Returns: `true` in case of success. 963 bool seekPosition(int pattern, int row) 964 { 965 assert(isOpenForReading() && isModule() && canSeek()); 966 final switch(_format) with (AudioFileFormat) 967 { 968 case mp3: 969 case flac: 970 case ogg: 971 case opus: 972 case wav: 973 case unknown: 974 assert(false); 975 976 case mod: 977 // NOTE: This is untested. 978 return pocketmod_seek(_modDecoder, pattern, row, 0); 979 980 case xm: 981 return xm_seek(_xmDecoder, pattern, row, 0); 982 } 983 } 984 985 // ----------------------------------------------------------------------------------------------------- 986 // </module functions> 987 // ----------------------------------------------------------------------------------------------------- 988 989 // ----------------------------------------------------------------------------------------------------- 990 // <non-module functions> 991 // Those functions below can't be used for tracker module formats, because there is no real concept of 992 // absolute position in these formats. 993 // ----------------------------------------------------------------------------------------------------- 994 995 /// Seeking. Subsequent reads start from multi-channel frame index `frames`. 996 /// Only available for input streams, for streams whose `canSeek()` returns `true`. 997 /// Warning: `seekPosition(lengthInFrames)` is Undefined Behaviour for now. (it works in MP3 998 bool seekPosition(int frame) 999 { 1000 assert(isOpenForReading() && !isModule() && canSeek()); // seeking doesn't have the same sense with modules. 1001 final switch(_format) with (AudioFileFormat) 1002 { 1003 case mp3: 1004 version(decodeMP3) 1005 { 1006 assert(_lengthInFrames != audiostreamUnknownLength); 1007 if (frame < 0 || frame > _lengthInFrames) 1008 return false; 1009 return (mp3dec_ex_seek(_mp3DecoderNew, frame * _numChannels) == 0); 1010 } 1011 else 1012 assert(false); 1013 case flac: 1014 version(decodeFLAC) 1015 { 1016 if (frame < 0 || frame > _lengthInFrames) 1017 return false; 1018 bool success = drflac__seek_to_sample__brute_force (_flacDecoder, frame * _numChannels); 1019 if (success) 1020 _flacPositionFrame = frame; 1021 return success; 1022 } 1023 else 1024 assert(false); 1025 case ogg: 1026 version(decodeOGG) 1027 { 1028 return stb_vorbis_seek(_oggHandle, frame) == 1; 1029 } 1030 else 1031 assert(false); 1032 case opus: 1033 version(decodeOPUS) 1034 { 1035 if (frame < 0 || frame > _lengthInFrames) 1036 return false; 1037 long where = _opusDecoder.ogg.seekPCM(frame); 1038 _opusPositionFrame = where; 1039 int toSkip = cast(int)(frame - where); 1040 1041 // skip remaining samples for sample-accurate seeking 1042 // Note: this also updates _opusPositionFrame 1043 int skipped = readSamplesFloat(null, cast(int) toSkip); 1044 // TODO: if decoding `toSkip` samples failed, restore previous state? 1045 return skipped == toSkip; 1046 } 1047 else 1048 assert(false); 1049 1050 case mod: 1051 case xm: 1052 assert(false); 1053 1054 case wav: 1055 version(decodeWAV) 1056 return _wavDecoder.seekPosition(frame); 1057 else 1058 assert(false); 1059 case unknown: 1060 assert(false); 1061 1062 } 1063 } 1064 1065 /// Tell. Returns the current position in multichannel frames. -1 on error. 1066 int tellPosition() 1067 { 1068 assert(isOpenForReading() && !isModule() && canSeek()); // seeking doesn't have the same sense with modules. 1069 final switch(_format) with (AudioFileFormat) 1070 { 1071 case mp3: 1072 version(decodeMP3) 1073 { 1074 return cast(int) _mp3DecoderNew.cur_sample / _numChannels; 1075 } 1076 else 1077 assert(false); 1078 case flac: 1079 version(decodeFLAC) 1080 { 1081 // Implemented externally since drflac is impenetrable. 1082 //return cast(int) _flacPositionFrame; 1083 return -1; // doesn't work in last frame though... seekPosition buggy in FLAC? 1084 } 1085 else 1086 assert(false); 1087 case ogg: 1088 version(decodeOGG) 1089 return cast(int) stb_vorbis_get_sample_offset(_oggHandle); 1090 else 1091 assert(false); 1092 1093 case opus: 1094 version(decodeOPUS) 1095 { 1096 return cast(int) _opusPositionFrame; // implemented externally 1097 } 1098 else 1099 assert(false); 1100 1101 case wav: 1102 version(decodeWAV) 1103 return _wavDecoder.tellPosition(); 1104 else 1105 assert(false); 1106 1107 case mod: 1108 case xm: 1109 case unknown: 1110 assert(false); 1111 1112 } 1113 } 1114 1115 1116 // ----------------------------------------------------------------------------------------------------- 1117 // </non-module functions> 1118 // ----------------------------------------------------------------------------------------------------- 1119 1120 /// Call `fflush()` on written samples, if any. 1121 /// It is only useful for streamable output formats, that may want to flush things to disk. 1122 void flush() nothrow @nogc 1123 { 1124 assert( _io && (_io.write !is null) ); 1125 _io.flush(userData); 1126 } 1127 1128 /// Finalize encoding. After finalization, further writes are not possible anymore 1129 /// however the stream is considered complete and valid for storage. 1130 void finalizeEncoding() @nogc 1131 { 1132 // If you crash here, it's because `finalizeEncoding` has been called twice. 1133 assert(isOpenForWriting()); 1134 1135 final switch(_format) with (AudioFileFormat) 1136 { 1137 case mp3: 1138 case flac: 1139 case ogg: 1140 case opus: 1141 case mod: 1142 case xm: 1143 assert(false); // unsupported output encoding 1144 case wav: 1145 { 1146 _wavEncoder.finalizeEncoding(); 1147 break; 1148 } 1149 case unknown: 1150 assert(false); 1151 } 1152 _io.write = null; // prevents further encodings 1153 } 1154 1155 // Finalize encoding and get internal buffer. 1156 // This can be called multiple times, in which cases the stream is finalized only the first time. 1157 const(ubyte)[] finalizeAndGetEncodedResult() @nogc 1158 { 1159 // only callable while appending, else it's a programming error 1160 assert( (memoryContext !is null) && ( memoryContext.bufferCanGrow ) ); 1161 1162 finalizeEncodingIfNeeded(); 1163 return memoryContext.buffer[0..memoryContext.size]; 1164 } 1165 1166 private: 1167 IOCallbacks* _io; 1168 1169 // This type of context is a closure to remember where the data is. 1170 void* userData; // is equal to either fileContext or memoryContext 1171 FileContext* fileContext; 1172 MemoryContext* memoryContext; 1173 1174 // This type of context is a closure to remember where _io and user Data is. 1175 DecoderContext* _decoderContext; 1176 1177 AudioFileFormat _format; 1178 float _sampleRate; 1179 int _numChannels; 1180 long _lengthInFrames; 1181 1182 float[] _floatDecodeBuf; 1183 1184 // Decoders 1185 version(decodeMP3) 1186 { 1187 mp3dec_ex_t* _mp3DecoderNew; // allocated on heap since it's a 16kb object 1188 mp3dec_io_t* _mp3io; 1189 } 1190 version(decodeFLAC) 1191 { 1192 drflac* _flacDecoder; 1193 long _flacPositionFrame; 1194 } 1195 version(decodeOGG) 1196 { 1197 ubyte[] _oggBuffer; // all allocations from the ogg decoder 1198 stb_vorbis* _oggHandle; 1199 } 1200 version(decodeWAV) 1201 { 1202 WAVDecoder _wavDecoder; 1203 } 1204 version(decodeMOD) 1205 { 1206 pocketmod_context* _modDecoder = null; 1207 ubyte[] _modContent = null; // whole buffer, copied 1208 } 1209 version(decodeXM) 1210 { 1211 xm_context_t* _xmDecoder = null; 1212 ubyte* _xmContent = null; 1213 } 1214 1215 version(decodeOPUS) 1216 { 1217 OpusFile _opusDecoder; 1218 short[] _opusBuffer; 1219 long _opusPositionFrame; 1220 } 1221 1222 // Encoder 1223 version(encodeWAV) 1224 { 1225 WAVEncoder _wavEncoder; 1226 } 1227 1228 void startDecoding() @nogc 1229 { 1230 // Create a decoder context 1231 _decoderContext = mallocNew!DecoderContext; 1232 _decoderContext.userDataIO = userData; 1233 _decoderContext.callbacks = _io; 1234 1235 version(decodeOPUS) 1236 { 1237 try 1238 { 1239 _opusDecoder = opusOpen(_io, userData); 1240 assert(_opusDecoder !is null); 1241 _format = AudioFileFormat.opus; 1242 _sampleRate = _opusDecoder.rate; // Note: Opus file are always 48Khz 1243 _numChannels = _opusDecoder.channels(); 1244 _lengthInFrames = _opusDecoder.smpduration(); 1245 _opusPositionFrame = 0; 1246 return; 1247 } 1248 catch(Exception e) 1249 { 1250 destroyFree(e); 1251 } 1252 _opusDecoder = null; 1253 } 1254 1255 version(decodeFLAC) 1256 { 1257 _io.seek(0, false, userData); 1258 1259 // Is it a FLAC? 1260 { 1261 drflac_read_proc onRead = &flac_read; 1262 drflac_seek_proc onSeek = &flac_seek; 1263 void* pUserData = _decoderContext; 1264 _flacDecoder = drflac_open (onRead, onSeek, _decoderContext); 1265 if (_flacDecoder !is null) 1266 { 1267 _format = AudioFileFormat.flac; 1268 _sampleRate = _flacDecoder.sampleRate; 1269 _numChannels = _flacDecoder.channels; 1270 _lengthInFrames = _flacDecoder.totalSampleCount / _numChannels; 1271 _flacPositionFrame = 0; 1272 return; 1273 } 1274 } 1275 } 1276 1277 version(decodeWAV) 1278 { 1279 // Check if it's a WAV. 1280 1281 _io.seek(0, false, userData); 1282 1283 try 1284 { 1285 _wavDecoder = mallocNew!WAVDecoder(_io, userData); 1286 _wavDecoder.scan(); 1287 1288 // WAV detected 1289 _format = AudioFileFormat.wav; 1290 _sampleRate = _wavDecoder._sampleRate; 1291 _numChannels = _wavDecoder._channels; 1292 _lengthInFrames = _wavDecoder._lengthInFrames; 1293 return; 1294 } 1295 catch(Exception e) 1296 { 1297 // not a WAV 1298 destroyFree(e); 1299 } 1300 destroyFree(_wavDecoder); 1301 _wavDecoder = null; 1302 } 1303 1304 version(decodeOGG) 1305 { 1306 _io.seek(0, false, userData); 1307 1308 // Is it an OGG? 1309 { 1310 //"In my test files the maximal-size usage is ~150KB", so let's take a bit more 1311 _oggBuffer.reallocBuffer(200 * 1024); 1312 1313 stb_vorbis_alloc alloc; 1314 alloc.alloc_buffer = cast(ubyte*)(_oggBuffer.ptr); 1315 alloc.alloc_buffer_length_in_bytes = cast(int)(_oggBuffer.length); 1316 1317 int error; 1318 1319 _oggHandle = stb_vorbis_open_file(_io, userData, &error, &alloc); 1320 if (error == VORBIS__no_error) 1321 { 1322 _format = AudioFileFormat.ogg; 1323 _sampleRate = _oggHandle.sample_rate; 1324 _numChannels = _oggHandle.channels; 1325 _lengthInFrames = stb_vorbis_stream_length_in_samples(_oggHandle); 1326 return; 1327 } 1328 else 1329 { 1330 _oggHandle = null; 1331 } 1332 } 1333 } 1334 1335 version(decodeMP3) 1336 { 1337 // Check if it's a MP3. 1338 { 1339 _io.seek(0, false, userData); 1340 1341 ubyte* scratchBuffer = cast(ubyte*) malloc(MINIMP3_BUF_SIZE*2); 1342 scope(exit) free(scratchBuffer); 1343 1344 _mp3io = cast(mp3dec_io_t*) malloc(mp3dec_io_t.sizeof); 1345 _mp3io.read = &mp3_io_read; 1346 _mp3io.read_data = _decoderContext; 1347 _mp3io.seek = &mp3_io_seek; 1348 _mp3io.seek_data = _decoderContext; 1349 1350 if ( mp3dec_detect_cb(_mp3io, scratchBuffer, MINIMP3_BUF_SIZE*2) == 0 ) 1351 { 1352 // This is a MP3. Try to open a stream. 1353 1354 // Allocate a mp3dec_ex_t object 1355 _mp3DecoderNew = cast(mp3dec_ex_t*) malloc(mp3dec_ex_t.sizeof); 1356 1357 int result = mp3dec_ex_open_cb(_mp3DecoderNew, _mp3io, MP3D_SEEK_TO_SAMPLE); 1358 1359 if (0 == result) 1360 { 1361 // MP3 detected 1362 // but it seems we need to iterate all frames to know the length... 1363 _format = AudioFileFormat.mp3; 1364 _sampleRate = _mp3DecoderNew.info.hz; 1365 _numChannels = _mp3DecoderNew.info.channels; 1366 _lengthInFrames = _mp3DecoderNew.samples / _numChannels; 1367 return; 1368 } 1369 else 1370 { 1371 free(_mp3DecoderNew); 1372 _mp3DecoderNew = null; 1373 free(_mp3io); 1374 _mp3io = null; 1375 } 1376 } 1377 } 1378 } 1379 1380 version(decodeXM) 1381 { 1382 { 1383 // we need the first 60 bytes to check if XM 1384 char[60] xmHeader; 1385 int bytes; 1386 1387 _io.seek(0, false, userData); 1388 long lenBytes = _io.getFileLength(userData); 1389 if (lenBytes < 60) 1390 goto not_a_xm; 1391 1392 bytes = _io.read(xmHeader.ptr, 60, userData); 1393 if (bytes != 60) 1394 goto not_a_xm; 1395 1396 if (0 != xm_check_sanity_preload(xmHeader.ptr, 60)) 1397 goto not_a_xm; 1398 1399 _xmContent = cast(ubyte*) malloc(cast(int)lenBytes); 1400 _io.seek(0, false, userData); 1401 bytes = _io.read(_xmContent, cast(int)lenBytes, userData); 1402 if (bytes != cast(int)lenBytes) 1403 goto not_a_xm; 1404 1405 if (0 == xm_create_context_safe(&_xmDecoder, cast(const(char)*)_xmContent, cast(size_t)lenBytes, 44100)) 1406 { 1407 assert(_xmDecoder !is null); 1408 1409 xm_set_max_loop_count(_xmDecoder, 1); 1410 1411 _format = AudioFileFormat.xm; 1412 _sampleRate = 44100.0f; 1413 _numChannels = 2; 1414 _lengthInFrames = audiostreamUnknownLength; 1415 return; 1416 } 1417 1418 not_a_xm: 1419 assert(_xmDecoder == null); 1420 free(_xmContent); 1421 _xmContent = null; 1422 } 1423 } 1424 1425 version(decodeMOD) 1426 { 1427 { 1428 // we need either the first 1084 or 600 bytes if available 1429 _io.seek(0, false, userData); 1430 long lenBytes = _io.getFileLength(userData); 1431 if (lenBytes >= 600) 1432 { 1433 int headerBytes = lenBytes > 1084 ? 1084 : cast(int)lenBytes; 1434 1435 ubyte[1084] header; 1436 int bytes = _io.read(header.ptr, headerBytes, userData); 1437 1438 if (_pocketmod_ident(null, header.ptr, bytes)) 1439 { 1440 // This is a MOD, allocate a proper context, and read the whole file. 1441 _modDecoder = cast(pocketmod_context*) malloc(pocketmod_context.sizeof); 1442 1443 // Read whole .mod in a buffer, since the decoder work all from memory 1444 _io.seek(0, false, userData); 1445 _modContent.reallocBuffer(cast(size_t)lenBytes); 1446 bytes = _io.read(_modContent.ptr, cast(int)lenBytes, userData); 1447 1448 if (pocketmod_init(_modDecoder, _modContent.ptr, bytes, 44100)) 1449 { 1450 _format = AudioFileFormat.mod; 1451 _sampleRate = 44100.0f; 1452 _numChannels = 2; 1453 _lengthInFrames = audiostreamUnknownLength; 1454 return; 1455 } 1456 } 1457 } 1458 } 1459 } 1460 1461 _format = AudioFileFormat.unknown; 1462 _sampleRate = float.nan; 1463 _numChannels = 0; 1464 _lengthInFrames = -1; 1465 1466 throw mallocNew!Exception("Cannot decode stream: unrecognized encoding."); 1467 } 1468 1469 void startEncoding(AudioFileFormat format, float sampleRate, int numChannels, EncodingOptions options) @nogc 1470 { 1471 _format = format; 1472 _sampleRate = sampleRate; 1473 _numChannels = numChannels; 1474 1475 final switch(format) with (AudioFileFormat) 1476 { 1477 case mp3: 1478 throw mallocNew!Exception("Unsupported encoding format: MP3"); 1479 case flac: 1480 throw mallocNew!Exception("Unsupported encoding format: FLAC"); 1481 case ogg: 1482 throw mallocNew!Exception("Unsupported encoding format: OGG"); 1483 case opus: 1484 throw mallocNew!Exception("Unsupported encoding format: Opus"); 1485 case mod: 1486 throw mallocNew!Exception("Unsupported encoding format: MOD"); 1487 case xm: 1488 throw mallocNew!Exception("Unsupported encoding format: XM"); 1489 case wav: 1490 { 1491 // Note: fractional sample rates not supported by WAV, signal an integer one 1492 int isampleRate = cast(int)(sampleRate + 0.5f); 1493 1494 WAVEncoder.Format wavfmt; 1495 final switch (options.sampleFormat) 1496 { 1497 case AudioSampleFormat.fp32: wavfmt = WAVEncoder.Format.fp32le; break; 1498 case AudioSampleFormat.fp64: wavfmt = WAVEncoder.Format.fp64le; break; 1499 } 1500 _wavEncoder = mallocNew!WAVEncoder(_io, userData, isampleRate, numChannels, wavfmt); 1501 break; 1502 } 1503 case unknown: 1504 throw mallocNew!Exception("Can't encode using 'unknown' coding"); 1505 } 1506 } 1507 1508 void finalizeEncodingIfNeeded() @nogc 1509 { 1510 if (_io && (_io.write !is null)) // if we have been encoding something 1511 { 1512 finalizeEncoding(); 1513 } 1514 } 1515 } 1516 1517 // AudioStream should be able to go on a smallish 32-bit stack, 1518 // and malloc the rest on the heap when needed. 1519 static assert(AudioStream.sizeof <= 256); 1520 1521 private: // not meant to be imported at all 1522 1523 1524 1525 // Internal object for audio-formats 1526 1527 1528 // File callbacks 1529 // The file callbacks are using the C stdlib. 1530 1531 struct FileContext // this is what is passed to I/O when used in file mode 1532 { 1533 // Used when streaming of writing a file 1534 FILE* file = null; 1535 1536 // Size of the file in bytes, only used when reading/writing a file. 1537 long fileSize; 1538 1539 // Initialize this context 1540 void initialize(const(char)[] path, bool forWrite) @nogc 1541 { 1542 CString strZ = CString(path); 1543 file = fopen(strZ.storage, forWrite ? "wb".ptr : "rb".ptr); 1544 if (file is null) 1545 throw mallocNew!Exception("File not found"); 1546 // finds the size of the file 1547 fseek(file, 0, SEEK_END); 1548 fileSize = ftell(file); 1549 fseek(file, 0, SEEK_SET); 1550 } 1551 } 1552 1553 long file_tell(void* userData) nothrow @nogc 1554 { 1555 FileContext* context = cast(FileContext*)userData; 1556 return ftell(context.file); 1557 } 1558 1559 bool file_seek(long offset, bool relative, void* userData) nothrow @nogc 1560 { 1561 FileContext* context = cast(FileContext*)userData; 1562 assert(offset <= int.max); 1563 int r = fseek(context.file, cast(int)offset, relative ? SEEK_CUR : SEEK_SET); // Limitations: file larger than 2gb not supported 1564 return r == 0; 1565 } 1566 1567 long file_getFileLength(void* userData) nothrow @nogc 1568 { 1569 FileContext* context = cast(FileContext*)userData; 1570 return context.fileSize; 1571 } 1572 1573 int file_read(void* outData, int bytes, void* userData) nothrow @nogc 1574 { 1575 FileContext* context = cast(FileContext*)userData; 1576 size_t bytesRead = fread(outData, 1, bytes, context.file); 1577 return cast(int)bytesRead; 1578 } 1579 1580 int file_write(void* inData, int bytes, void* userData) nothrow @nogc 1581 { 1582 FileContext* context = cast(FileContext*)userData; 1583 size_t bytesWritten = fwrite(inData, 1, bytes, context.file); 1584 return cast(int)bytesWritten; 1585 } 1586 1587 bool file_skip(int bytes, void* userData) nothrow @nogc 1588 { 1589 FileContext* context = cast(FileContext*)userData; 1590 return (0 == fseek(context.file, bytes, SEEK_CUR)); 1591 } 1592 1593 bool file_flush(void* userData) nothrow @nogc 1594 { 1595 FileContext* context = cast(FileContext*)userData; 1596 return ( fflush(context.file) == 0 ); 1597 } 1598 1599 // Memory read callback 1600 // Using the read buffer instead 1601 1602 struct MemoryContext 1603 { 1604 bool bufferIsOwned; 1605 bool bufferCanGrow; 1606 1607 // Buffer 1608 ubyte* buffer = null; 1609 1610 size_t size; // current buffer size 1611 size_t cursor; // where we are in the buffer 1612 size_t capacity; // max buffer size before realloc 1613 1614 void initializeWithConstantInput(const(ubyte)* data, size_t length) nothrow @nogc 1615 { 1616 // Make a copy of the input buffer, since it could be temporary. 1617 bufferIsOwned = true; 1618 bufferCanGrow = false; 1619 1620 buffer = mallocDup(data[0..length]).ptr; // Note: the copied slice is made mutable. 1621 size = length; 1622 cursor = 0; 1623 capacity = length; 1624 } 1625 1626 void initializeWithExternalOutputBuffer(ubyte* data, size_t length) nothrow @nogc 1627 { 1628 bufferIsOwned = false; 1629 bufferCanGrow = false; 1630 buffer = data; 1631 size = 0; 1632 cursor = 0; 1633 capacity = length; 1634 } 1635 1636 void initializeWithInternalGrowableBuffer() nothrow @nogc 1637 { 1638 bufferIsOwned = true; 1639 bufferCanGrow = true; 1640 buffer = null; 1641 size = 0; 1642 cursor = 0; 1643 capacity = 0; 1644 } 1645 1646 ~this() 1647 { 1648 if (bufferIsOwned) 1649 { 1650 if (buffer !is null) 1651 { 1652 free(buffer); 1653 buffer = null; 1654 } 1655 } 1656 } 1657 } 1658 1659 long memory_tell(void* userData) nothrow @nogc 1660 { 1661 MemoryContext* context = cast(MemoryContext*)userData; 1662 return cast(long)(context.cursor); 1663 } 1664 1665 bool memory_seek(long offset, bool relative, void* userData) nothrow @nogc 1666 { 1667 MemoryContext* context = cast(MemoryContext*)userData; 1668 if (relative) offset += context.cursor; 1669 if (offset < 0) 1670 return false; 1671 1672 bool r = true; 1673 if (offset >= context.size) // can't seek past end of buffer, stick to the end so that read return 0 byte 1674 { 1675 offset = context.size; 1676 r = false; 1677 } 1678 context.cursor = cast(size_t)offset; // Note: memory streams larger than 2gb not supported 1679 return r; 1680 } 1681 1682 long memory_getFileLength(void* userData) nothrow @nogc 1683 { 1684 MemoryContext* context = cast(MemoryContext*)userData; 1685 return cast(long)(context.size); 1686 } 1687 1688 int memory_read(void* outData, int bytes, void* userData) nothrow @nogc 1689 { 1690 MemoryContext* context = cast(MemoryContext*)userData; 1691 size_t cursor = context.cursor; 1692 size_t size = context.size; 1693 size_t available = size - cursor; 1694 if (bytes < available) 1695 { 1696 outData[0..bytes] = context.buffer[cursor..cursor + bytes]; 1697 context.cursor += bytes; 1698 return bytes; 1699 } 1700 else 1701 { 1702 outData[0..available] = context.buffer[cursor..cursor + available]; 1703 context.cursor = context.size; 1704 return cast(int)available; 1705 } 1706 } 1707 1708 int memory_write_limited(void* inData, int bytes, void* userData) nothrow @nogc 1709 { 1710 MemoryContext* context = cast(MemoryContext*)userData; 1711 size_t cursor = context.cursor; 1712 size_t size = context.size; 1713 size_t available = size - cursor; 1714 ubyte* buffer = context.buffer; 1715 ubyte* source = cast(ubyte*) inData; 1716 1717 if (cursor + bytes > available) 1718 { 1719 bytes = cast(int)(available - cursor); 1720 } 1721 1722 buffer[cursor..(cursor + bytes)] = source[0..bytes]; 1723 context.size += bytes; 1724 context.cursor += bytes; 1725 return bytes; 1726 } 1727 1728 int memory_write_append(void* inData, int bytes, void* userData) nothrow @nogc 1729 { 1730 MemoryContext* context = cast(MemoryContext*)userData; 1731 size_t cursor = context.cursor; 1732 size_t size = context.size; 1733 size_t available = size - cursor; 1734 ubyte* buffer = context.buffer; 1735 ubyte* source = cast(ubyte*) inData; 1736 1737 if (cursor + bytes > available) 1738 { 1739 size_t oldSize = context.capacity; 1740 size_t newSize = cursor + bytes; 1741 if (newSize < oldSize * 2 + 1) 1742 newSize = oldSize * 2 + 1; 1743 buffer = cast(ubyte*) realloc(buffer, newSize); 1744 context.capacity = newSize; 1745 1746 assert( cursor + bytes <= available ); 1747 } 1748 1749 buffer[cursor..(cursor + bytes)] = source[0..bytes]; 1750 context.size += bytes; 1751 context.cursor += bytes; 1752 return bytes; 1753 } 1754 1755 bool memory_skip(int bytes, void* userData) nothrow @nogc 1756 { 1757 MemoryContext* context = cast(MemoryContext*)userData; 1758 context.cursor += bytes; 1759 return context.cursor <= context.size; 1760 } 1761 1762 bool memory_flush(void* userData) nothrow @nogc 1763 { 1764 // do nothing, no flushign to do for memory 1765 return true; 1766 } 1767 1768 1769 // Decoder context 1770 struct DecoderContext 1771 { 1772 void* userDataIO; 1773 IOCallbacks* callbacks; 1774 } 1775 1776 // MP3 decoder read callback 1777 static int mp3ReadDelegate(void[] buf, void* userDataDecoder) @nogc nothrow 1778 { 1779 DecoderContext* context = cast(DecoderContext*) userDataDecoder; 1780 1781 // read bytes into the buffer, return number of bytes read or 0 for EOF, -1 on error 1782 // will never be called with empty buffer, or buffer more than 128KB 1783 1784 int bytes = context.callbacks.read(buf.ptr, cast(int)(buf.length), context.userDataIO); 1785 return bytes; 1786 } 1787 1788 1789 // FLAC decoder read callbacks 1790 1791 size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead) @nogc nothrow 1792 { 1793 DecoderContext* context = cast(DecoderContext*) pUserData; 1794 return context.callbacks.read(pBufferOut, cast(int)(bytesToRead), context.userDataIO); 1795 } 1796 1797 bool flac_seek(void* pUserData, int offset, drflac_seek_origin origin) @nogc nothrow 1798 { 1799 DecoderContext* context = cast(DecoderContext*) pUserData; 1800 if (origin == drflac_seek_origin_start) 1801 { 1802 context.callbacks.seek(offset, false, context.userDataIO); 1803 } 1804 else if (origin == drflac_seek_origin_current) 1805 { 1806 context.callbacks.seek(offset, true, context.userDataIO); 1807 } 1808 return true; 1809 } 1810 1811 // MP3 decoder read callbacks 1812 1813 size_t mp3_io_read(void *buf, size_t size, void *user_data) @nogc nothrow 1814 { 1815 DecoderContext* context = cast(DecoderContext*) user_data; 1816 return context.callbacks.read(buf, cast(int)(size), context.userDataIO); 1817 } 1818 1819 int mp3_io_seek(ulong position, void *user_data) @nogc nothrow 1820 { 1821 DecoderContext* context = cast(DecoderContext*) user_data; 1822 context.callbacks.seek(position, false, context.userDataIO); 1823 return 0; // doesn't detect seeking errors 1824 }