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