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 if (_flacPositionFrame == _lengthInFrames) 434 return 0; // internally the decoder might be elsewhere 435 436 int* integerData = cast(int*)outData; 437 438 int samples = cast(int) drflac_read_s32(_flacDecoder, frames * _numChannels, integerData); 439 440 // "Samples are always output as interleaved signed 32-bit PCM." 441 // Convert to float with type-punning. Note that this looses some precision. 442 double factor = 1.0 / int.max; 443 foreach(n; 0..samples) 444 { 445 outData[n] = integerData[n] * factor; 446 } 447 int framesDecoded = samples / _numChannels; 448 _flacPositionFrame += framesDecoded; 449 return framesDecoded; 450 } 451 else 452 { 453 assert(false); // Impossible 454 } 455 } 456 457 case AudioFileFormat.ogg: 458 { 459 version(decodeOGG) 460 { 461 assert(_oggHandle !is null); 462 int framesRead = stb_vorbis_get_samples_float_interleaved(_oggHandle, _numChannels, outData, frames * _numChannels); 463 _oggPositionFrame += framesRead; 464 return framesRead; 465 } 466 else 467 { 468 assert(false); // Impossible 469 } 470 } 471 472 case AudioFileFormat.mp3: 473 { 474 version(decodeMP3) 475 { 476 assert(_mp3DecoderNew !is null); 477 478 int samplesNeeded = frames * _numChannels; 479 int result = cast(int) mp3dec_ex_read(_mp3DecoderNew, outData, samplesNeeded); 480 if (result < 0) // error 481 return 0; 482 return result / _numChannels; 483 } 484 else 485 { 486 assert(false); // Impossible 487 } 488 } 489 case AudioFileFormat.wav: 490 version(decodeWAV) 491 { 492 assert(_wavDecoder !is null); 493 int readFrames = _wavDecoder.readSamples!float(outData, frames); 494 return readFrames; 495 } 496 else 497 { 498 assert(false); // Impossible 499 } 500 501 case AudioFileFormat.xm: 502 version(decodeXM) 503 { 504 assert(_xmDecoder !is null); 505 506 if (xm_get_loop_count(_xmDecoder) >= 1) 507 return 0; // song is finished 508 509 xm_generate_samples(_xmDecoder, outData, frames); 510 return frames; // Note: XM decoder pads end with zeroes. 511 } 512 else 513 { 514 assert(false); // Impossible 515 } 516 517 case AudioFileFormat.mod: 518 version(decodeMOD) 519 { 520 if (pocketmod_loop_count(_modDecoder) >= 1) 521 return 0; // end stream after MOD finishes, looping not supported 522 assert(_modDecoder !is null); 523 int bytesReturned = pocketmod_render(_modDecoder, outData, frames * 2 * 4); 524 assert((bytesReturned % 8) == 0); 525 return bytesReturned / 8; 526 } 527 else 528 { 529 assert(false); // Impossible 530 } 531 532 case AudioFileFormat.unknown: 533 // One shouldn't ever get there, since in this case 534 // opening has failed. 535 assert(false); 536 } 537 } 538 ///ditto 539 int readSamplesFloat(float[] outData) @nogc 540 { 541 assert( (outData.length % _numChannels) == 0); 542 return readSamplesFloat(outData.ptr, cast(int)(outData.length / _numChannels) ); 543 } 544 545 /// Read interleaved double samples in the given buffer `outData`. 546 /// 547 /// Params: 548 /// outData Buffer where to put decoded samples. Samples are arranged in an interleaved fashion. 549 /// Must have room for `frames` x `getNumChannels()` samples. 550 /// For a stereo file, the output data will contain LRLRLR... repeated `result` times. 551 /// 552 /// frames The number of multichannel frames to be read. 553 /// A frame is `getNumChannels()` samples. 554 /// 555 /// Note: the only formats to possibly take advantage of double decoding are WAV and FLAC. 556 /// 557 /// Returns: Number of actually read frames. Multiply by `getNumChannels()` to get the number of read samples. 558 /// When that number is less than `frames`, it means the stream is done decoding, or that there was a decoding error. 559 /// 560 /// TODO: once this returned less than `frames`, are we guaranteed we can keep calling that and it returns 0? 561 int readSamplesDouble(double* outData, int frames) @nogc 562 { 563 assert(isOpenForReading()); 564 565 switch(_format) 566 { 567 case AudioFileFormat.wav: 568 version(decodeWAV) 569 { 570 assert(_wavDecoder !is null); 571 int readFrames = _wavDecoder.readSamples!double(outData, frames); 572 return readFrames; 573 } 574 else 575 { 576 assert(false); // Impossible 577 } 578 579 case AudioFileFormat.flac: 580 { 581 version(decodeFLAC) 582 { 583 assert(_flacDecoder !is null); 584 585 if (_flacPositionFrame == _lengthInFrames) 586 return 0; // internally the decoder might be elsewhere 587 588 // use second half of the output buffer as temporary integer decoding area 589 int* integerData = (cast(int*)outData) + frames; 590 int samples = cast(int) drflac_read_s32(_flacDecoder, frames, integerData); 591 592 // "Samples are always output as interleaved signed 32-bit PCM." 593 // Converting to double doesn't loose mantissa, unlike float. 594 double factor = 1.0 / int.max; 595 foreach(n; 0..samples) 596 { 597 outData[n] = integerData[n] * factor; 598 } 599 int framesDecoded = samples / _numChannels; 600 _flacPositionFrame += framesDecoded; 601 return framesDecoded; 602 } 603 else 604 { 605 assert(false); // Impossible 606 } 607 } 608 609 case AudioFileFormat.unknown: 610 // One shouldn't ever get there 611 assert(false); 612 613 default: 614 // Decode to float buffer, and then convert 615 if (_floatDecodeBuf.length < frames * _numChannels) 616 _floatDecodeBuf.reallocBuffer(frames * _numChannels); 617 int read = readSamplesFloat(_floatDecodeBuf.ptr, frames); 618 for (int n = 0; n < read * _numChannels; ++n) 619 outData[n] = _floatDecodeBuf[n]; 620 return read; 621 } 622 } 623 ///ditto 624 int readSamplesDouble(double[] outData) @nogc 625 { 626 assert( (outData.length % _numChannels) == 0); 627 return readSamplesDouble(outData.ptr, cast(int)(outData.length / _numChannels) ); 628 } 629 630 /// Write interleaved float samples to the stream, from the given buffer `inData[0..frames]`. 631 /// 632 /// Params: 633 /// inData Buffer of interleaved samples to append to the stream. 634 /// Must contain `frames` x `getNumChannels()` samples. 635 /// For a stereo file, `inData` contains LRLRLR... repeated `frames` times. 636 /// 637 /// frames The number of frames to append to the stream. 638 /// A frame is `getNumChannels()` samples. 639 /// 640 /// Returns: Number of actually written frames. Multiply by `getNumChannels()` to get the number of written samples. 641 /// When that number is less than `frames`, it means the stream had a write error. 642 int writeSamplesFloat(float* inData, int frames) nothrow @nogc 643 { 644 assert(_io && _io.write !is null); 645 646 final switch(_format) 647 { 648 case AudioFileFormat.mp3: 649 case AudioFileFormat.flac: 650 case AudioFileFormat.ogg: 651 case AudioFileFormat.opus: 652 case AudioFileFormat.mod: 653 case AudioFileFormat.xm: 654 case AudioFileFormat.unknown: 655 { 656 assert(false); // Shouldn't have arrived here, such encoding aren't supported. 657 } 658 case AudioFileFormat.wav: 659 { 660 version(encodeWAV) 661 { 662 return _wavEncoder.writeSamples(inData, frames); 663 } 664 else 665 { 666 assert(false, "no support for WAV encoding"); 667 } 668 } 669 } 670 } 671 ///ditto 672 int writeSamplesFloat(float[] inData) nothrow @nogc 673 { 674 assert( (inData.length % _numChannels) == 0); 675 return writeSamplesFloat(inData.ptr, cast(int)(inData.length / _numChannels)); 676 } 677 678 /// Write interleaved double samples to the stream, from the given buffer `inData[0..frames]`. 679 /// 680 /// Params: 681 /// inData Buffer of interleaved samples to append to the stream. 682 /// Must contain `frames` x `getNumChannels()` samples. 683 /// For a stereo file, `inData` contains LRLRLR... repeated `frames` times. 684 /// 685 /// frames The number of frames to append to the stream. 686 /// A frame is `getNumChannels()` samples. 687 /// 688 /// Note: this only does something if the output format is WAV and was setup for 64-bit output. 689 /// 690 /// Returns: Number of actually written frames. Multiply by `getNumChannels()` to get the number of written samples. 691 /// When that number is less than `frames`, it means the stream had a write error. 692 int writeSamplesDouble(double* inData, int frames) nothrow @nogc 693 { 694 assert(_io && _io.write !is null); 695 696 switch(_format) 697 { 698 case AudioFileFormat.unknown: 699 // One shouldn't ever get there 700 assert(false); 701 702 case AudioFileFormat.wav: 703 { 704 version(encodeWAV) 705 { 706 return _wavEncoder.writeSamples(inData, frames); 707 } 708 else 709 { 710 assert(false, "no support for WAV encoding"); 711 } 712 } 713 714 default: 715 // Decode to float buffer, and then convert 716 if (_floatDecodeBuf.length < frames * _numChannels) 717 _floatDecodeBuf.reallocBuffer(frames * _numChannels); 718 719 for (int n = 0; n < frames * _numChannels; ++n) 720 _floatDecodeBuf[n] = inData[n]; 721 722 return writeSamplesFloat(_floatDecodeBuf.ptr, frames); 723 } 724 } 725 ///ditto 726 int writeSamplesDouble(double[] inData) nothrow @nogc 727 { 728 assert( (inData.length % _numChannels) == 0); 729 return writeSamplesDouble(inData.ptr, cast(int)(inData.length / _numChannels)); 730 } 731 732 // ----------------------------------------------------------------------------------------------------- 733 // <module functions> 734 // Those tracker module-specific functions below can only be called when `isModule()` returns `true`. 735 // Additionally, seeking function can only be called if `canSeek()` also returns `true`. 736 // ----------------------------------------------------------------------------------------------------- 737 738 /// Length. Returns the amount of patterns in the module 739 /// Formats that support this: MOD, XM. 740 int countModulePatterns() 741 { 742 assert(isOpenForReading() && isModule()); 743 final switch(_format) with (AudioFileFormat) 744 { 745 case mp3: 746 case flac: 747 case ogg: 748 case opus: 749 case wav: 750 case unknown: 751 assert(false); 752 case mod: 753 return _modDecoder.num_patterns; 754 case xm: 755 return xm_get_number_of_patterns(_xmDecoder); 756 } 757 } 758 759 /// Length. Returns the amount of PLAYED patterns in the module 760 /// Formats that support this: MOD, XM. 761 int getModuleLength() 762 { 763 assert(isOpenForReading() && isModule()); 764 final switch(_format) with (AudioFileFormat) 765 { 766 case mp3: 767 case flac: 768 case ogg: 769 case opus: 770 case wav: 771 case unknown: 772 assert(false); 773 case mod: 774 return _modDecoder.length; 775 case xm: 776 return xm_get_module_length(_xmDecoder); 777 } 778 } 779 780 /// Tell. Returns amount of rows in a pattern. 781 /// Formats that support this: MOD, XM. 782 /// Returns: -1 on error. Else, number of patterns. 783 int rowsInPattern(int pattern) 784 { 785 assert(isOpenForReading() && isModule()); 786 final switch(_format) with (AudioFileFormat) 787 { 788 case mp3: 789 case flac: 790 case ogg: 791 case opus: 792 case wav: 793 case unknown: 794 assert(false); 795 796 case mod: 797 // According to http://lclevy.free.fr/mo3/mod.txt 798 // there's 64 lines (aka rows) per pattern. 799 // TODO: error checking, make sure no out of bounds happens. 800 return 64; 801 802 case xm: 803 { 804 int numPatterns = xm_get_number_of_patterns(_xmDecoder); 805 if (pattern < 0 || pattern >= numPatterns) 806 return -1; 807 808 return xm_get_number_of_rows(_xmDecoder, cast(ushort) pattern); 809 } 810 } 811 } 812 813 /// Tell. Returns the current playing pattern id 814 /// Formats that support this: MOD, XM 815 int tellModulePattern() 816 { 817 assert(isOpenForReading() && isModule()); 818 final switch(_format) with (AudioFileFormat) 819 { 820 case mp3: 821 case flac: 822 case ogg: 823 case opus: 824 case wav: 825 case unknown: 826 assert(false); 827 case mod: 828 return _modDecoder.pattern; 829 case xm: 830 return _xmDecoder.current_table_index; 831 } 832 } 833 834 /// Tell. Returns the current playing row id 835 /// Formats that support this: MOD, XM 836 int tellModuleRow() 837 { 838 assert(isOpenForReading() && isModule()); 839 final switch(_format) with (AudioFileFormat) 840 { 841 case mp3: 842 case flac: 843 case ogg: 844 case opus: 845 case wav: 846 case unknown: 847 assert(false); 848 case mod: 849 return _modDecoder.line; 850 case xm: 851 return _xmDecoder.current_row; 852 } 853 } 854 855 /// Playback info. Returns the amount of multi-channel frames remaining in the current playing pattern. 856 /// Formats that support this: MOD 857 int framesRemainingInPattern() 858 { 859 assert(isOpenForReading() && isModule()); 860 final switch(_format) with (AudioFileFormat) 861 { 862 case mp3: 863 case flac: 864 case ogg: 865 case opus: 866 case wav: 867 case unknown: 868 assert(false); 869 870 case mod: 871 return pocketmod_count_remaining_samples(_modDecoder); 872 case xm: 873 return xm_count_remaining_samples(_xmDecoder); 874 } 875 } 876 877 /// Seeking. Subsequent reads start from pattern + row, 0 index 878 /// Only available for input streams. 879 /// Formats that support seeking per pattern/row: MOD, XM 880 /// Returns: `true` in case of success. 881 bool seekPosition(int pattern, int row) 882 { 883 assert(isOpenForReading() && isModule() && canSeek()); 884 final switch(_format) with (AudioFileFormat) 885 { 886 case mp3: 887 case flac: 888 case ogg: 889 case opus: 890 case wav: 891 case unknown: 892 assert(false); 893 894 case mod: 895 // NOTE: This is untested. 896 return pocketmod_seek(_modDecoder, pattern, row, 0); 897 898 case xm: 899 return xm_seek(_xmDecoder, pattern, row, 0); 900 } 901 } 902 903 // ----------------------------------------------------------------------------------------------------- 904 // </module functions> 905 // ----------------------------------------------------------------------------------------------------- 906 907 // ----------------------------------------------------------------------------------------------------- 908 // <non-module functions> 909 // Those functions below can't be used for tracker module formats, because there is no real concept of 910 // absolute position in these formats. 911 // ----------------------------------------------------------------------------------------------------- 912 913 /// Seeking. Subsequent reads start from multi-channel frame index `frames`. 914 /// Only available for input streams, for streams whose `canSeek()` returns `true`. 915 /// Warning: `seekPosition(lengthInFrames)` is Undefined Behaviour for now. (it works in MP3 916 bool seekPosition(int frame) 917 { 918 assert(isOpenForReading() && !isModule() && canSeek()); // seeking doesn't have the same sense with modules. 919 final switch(_format) with (AudioFileFormat) 920 { 921 case mp3: 922 version(decodeMP3) 923 { 924 assert(_lengthInFrames != audiostreamUnknownLength); 925 if (frame < 0 || frame > _lengthInFrames) 926 return false; 927 return (mp3dec_ex_seek(_mp3DecoderNew, frame * _numChannels) == 0); 928 } 929 else 930 assert(false); 931 case flac: 932 version(decodeFLAC) 933 { 934 if (frame < 0 || frame > _lengthInFrames) 935 return false; 936 if (_flacPositionFrame == frame) 937 return true; 938 939 // Note: seeking + FLAC is a dark side of that library. 940 // I'm not entirely sure we are handling all cases perfectly. 941 // But weren't able to fault the current situation. 942 // Would probably be easier to re-tanslate drflac if a problem arised. 943 944 bool success = drflac_seek_to_sample(_flacDecoder, frame * _numChannels); 945 if (success || frame == _lengthInFrames) // always succeed if end of stream is requested 946 _flacPositionFrame = frame; 947 return success; 948 } 949 else 950 assert(false); 951 case ogg: 952 version(decodeOGG) 953 { 954 if (_oggPositionFrame == frame) 955 return true; 956 957 if (_oggPositionFrame == _lengthInFrames) 958 { 959 // When the OGG stream is finished, and an earlier position is detected, 960 // the OGG decoder has to be restarted 961 assert(_oggHandle !is null); 962 cleanUpCodecs(); 963 assert(_oggHandle is null); 964 startDecoding(); 965 assert(_oggHandle !is null); 966 } 967 968 if (stb_vorbis_seek(_oggHandle, frame) == 1) 969 { 970 _oggPositionFrame = frame; 971 return true; 972 } 973 else 974 return false; 975 } 976 else 977 assert(false); 978 case opus: 979 version(decodeOPUS) 980 { 981 if (frame < 0 || frame > _lengthInFrames) 982 return false; 983 long where = _opusDecoder.ogg.seekPCM(frame); 984 _opusPositionFrame = where; 985 int toSkip = cast(int)(frame - where); 986 987 // skip remaining samples for sample-accurate seeking 988 // Note: this also updates _opusPositionFrame 989 int skipped = readSamplesFloat(null, cast(int) toSkip); 990 // TODO: if decoding `toSkip` samples failed, restore previous state? 991 return skipped == toSkip; 992 } 993 else 994 assert(false); 995 996 case mod: 997 case xm: 998 assert(false); 999 1000 case wav: 1001 version(decodeWAV) 1002 return _wavDecoder.seekPosition(frame); 1003 else 1004 assert(false); 1005 case unknown: 1006 assert(false); 1007 1008 } 1009 } 1010 1011 /// Tell. Returns the current position in multichannel frames. -1 on error. 1012 int tellPosition() 1013 { 1014 assert(isOpenForReading() && !isModule() && canSeek()); // seeking doesn't have the same sense with modules. 1015 final switch(_format) with (AudioFileFormat) 1016 { 1017 case mp3: 1018 version(decodeMP3) 1019 { 1020 return cast(int) _mp3DecoderNew.cur_sample / _numChannels; 1021 } 1022 else 1023 assert(false); 1024 case flac: 1025 version(decodeFLAC) 1026 { 1027 // Implemented externally since drflac is impenetrable. 1028 return cast(int) _flacPositionFrame; 1029 } 1030 else 1031 assert(false); 1032 case ogg: 1033 version(decodeOGG) 1034 return cast(int) _oggPositionFrame; 1035 else 1036 assert(false); 1037 1038 case opus: 1039 version(decodeOPUS) 1040 { 1041 return cast(int) _opusPositionFrame; // implemented externally 1042 } 1043 else 1044 assert(false); 1045 1046 case wav: 1047 version(decodeWAV) 1048 return _wavDecoder.tellPosition(); 1049 else 1050 assert(false); 1051 1052 case mod: 1053 case xm: 1054 case unknown: 1055 assert(false); 1056 1057 } 1058 } 1059 1060 1061 // ----------------------------------------------------------------------------------------------------- 1062 // </non-module functions> 1063 // ----------------------------------------------------------------------------------------------------- 1064 1065 /// Call `fflush()` on written samples, if any. 1066 /// It is only useful for streamable output formats, that may want to flush things to disk. 1067 void flush() nothrow @nogc 1068 { 1069 assert( _io && (_io.write !is null) ); 1070 _io.flush(userData); 1071 } 1072 1073 /// Finalize encoding. After finalization, further writes are not possible anymore 1074 /// however the stream is considered complete and valid for storage. 1075 void finalizeEncoding() @nogc 1076 { 1077 // If you crash here, it's because `finalizeEncoding` has been called twice. 1078 assert(isOpenForWriting()); 1079 1080 final switch(_format) with (AudioFileFormat) 1081 { 1082 case mp3: 1083 case flac: 1084 case ogg: 1085 case opus: 1086 case mod: 1087 case xm: 1088 assert(false); // unsupported output encoding 1089 case wav: 1090 { 1091 _wavEncoder.finalizeEncoding(); 1092 break; 1093 } 1094 case unknown: 1095 assert(false); 1096 } 1097 _io.write = null; // prevents further encodings 1098 } 1099 1100 // Finalize encoding and get internal buffer. 1101 // This can be called multiple times, in which cases the stream is finalized only the first time. 1102 const(ubyte)[] finalizeAndGetEncodedResult() @nogc 1103 { 1104 // only callable while appending, else it's a programming error 1105 assert( (memoryContext !is null) && ( memoryContext.bufferCanGrow ) ); 1106 1107 finalizeEncodingIfNeeded(); 1108 return memoryContext.buffer[0..memoryContext.size]; 1109 } 1110 1111 private: 1112 IOCallbacks* _io; 1113 1114 // This type of context is a closure to remember where the data is. 1115 void* userData; // is equal to either fileContext or memoryContext 1116 FileContext* fileContext; 1117 MemoryContext* memoryContext; 1118 1119 // This type of context is a closure to remember where _io and user Data is. 1120 DecoderContext* _decoderContext; 1121 1122 AudioFileFormat _format; 1123 float _sampleRate; 1124 int _numChannels; 1125 long _lengthInFrames; 1126 1127 float[] _floatDecodeBuf; 1128 1129 // Decoders 1130 version(decodeMP3) 1131 { 1132 mp3dec_ex_t* _mp3DecoderNew; // allocated on heap since it's a 16kb object 1133 mp3dec_io_t* _mp3io; 1134 } 1135 version(decodeFLAC) 1136 { 1137 drflac* _flacDecoder; 1138 long _flacPositionFrame; 1139 } 1140 version(decodeOGG) 1141 { 1142 ubyte[] _oggBuffer; // all allocations from the ogg decoder 1143 stb_vorbis* _oggHandle; 1144 long _oggPositionFrame; 1145 } 1146 version(decodeWAV) 1147 { 1148 WAVDecoder _wavDecoder; 1149 } 1150 version(decodeMOD) 1151 { 1152 pocketmod_context* _modDecoder = null; 1153 ubyte[] _modContent = null; // whole buffer, copied 1154 } 1155 version(decodeXM) 1156 { 1157 xm_context_t* _xmDecoder = null; 1158 ubyte* _xmContent = null; 1159 } 1160 1161 version(decodeOPUS) 1162 { 1163 OpusFile _opusDecoder; 1164 short[] _opusBuffer; 1165 long _opusPositionFrame; 1166 } 1167 1168 // Encoder 1169 version(encodeWAV) 1170 { 1171 WAVEncoder _wavEncoder; 1172 } 1173 1174 // Clean-up encoder/decoder-related data, but not I/O related things. Useful to restart the decoder. 1175 // After callign that, you can call `startDecoder` again. 1176 void cleanUpCodecs() @nogc 1177 { 1178 // Write the last needed bytes if needed 1179 finalizeEncodingIfNeeded(); 1180 1181 version(decodeMP3) 1182 { 1183 if (_mp3DecoderNew !is null) 1184 { 1185 mp3dec_ex_close(_mp3DecoderNew); 1186 free(_mp3DecoderNew); 1187 _mp3DecoderNew = null; 1188 } 1189 if (_mp3io !is null) 1190 { 1191 free(_mp3io); 1192 _mp3io = null; 1193 } 1194 } 1195 1196 version(decodeFLAC) 1197 { 1198 if (_flacDecoder !is null) 1199 { 1200 drflac_close(_flacDecoder); 1201 _flacDecoder = null; 1202 _flacPositionFrame = 0; 1203 } 1204 } 1205 1206 version(decodeOGG) 1207 { 1208 if (_oggHandle !is null) 1209 { 1210 stb_vorbis_close(_oggHandle); 1211 _oggHandle = null; 1212 _oggPositionFrame = 0; 1213 } 1214 _oggBuffer.reallocBuffer(0); 1215 } 1216 1217 version(decodeOPUS) 1218 { 1219 if (_opusDecoder !is null) 1220 { 1221 opusClose(_opusDecoder); 1222 _opusDecoder = null; 1223 } 1224 _opusBuffer = null; 1225 } 1226 1227 version(decodeWAV) 1228 { 1229 if (_wavDecoder !is null) 1230 { 1231 destroyFree(_wavDecoder); 1232 _wavDecoder = null; 1233 } 1234 } 1235 1236 version(decodeXM) 1237 { 1238 if (_xmDecoder !is null) 1239 { 1240 xm_free_context(_xmDecoder); 1241 _xmDecoder = null; 1242 } 1243 if (_xmContent != null) 1244 { 1245 free(_xmContent); 1246 _xmContent = null; 1247 } 1248 } 1249 1250 version(decodeMOD) 1251 { 1252 if (_modDecoder !is null) 1253 { 1254 free(_modDecoder); 1255 _modDecoder = null; 1256 _modContent.reallocBuffer(0); 1257 } 1258 } 1259 1260 version(encodeWAV) 1261 { 1262 if (_wavEncoder !is null) 1263 { 1264 destroyFree(_wavEncoder); 1265 _wavEncoder = null; 1266 } 1267 } 1268 } 1269 1270 // clean-up the whole Stream object so that it can be reused for anything else. 1271 void cleanUp() @nogc 1272 { 1273 cleanUpCodecs(); 1274 1275 if (_decoderContext) 1276 { 1277 destroyFree(_decoderContext); 1278 _decoderContext = null; 1279 } 1280 1281 if (fileContext !is null) 1282 { 1283 if (fileContext.file !is null) 1284 { 1285 int result = fclose(fileContext.file); 1286 if (result) 1287 throw mallocNew!Exception("Closing of audio file errored"); 1288 } 1289 destroyFree(fileContext); 1290 fileContext = null; 1291 } 1292 1293 if (memoryContext !is null) 1294 { 1295 // TODO destroy buffer if any is owned 1296 destroyFree(memoryContext); 1297 memoryContext = null; 1298 } 1299 1300 if (_io !is null) 1301 { 1302 destroyFree(_io); 1303 _io = null; 1304 } 1305 } 1306 1307 void startDecoding() @nogc 1308 { 1309 // Create a decoder context 1310 if ( _decoderContext is null) 1311 { 1312 _decoderContext = mallocNew!DecoderContext; 1313 _decoderContext.userDataIO = userData; 1314 _decoderContext.callbacks = _io; 1315 } 1316 1317 version(decodeOPUS) 1318 { 1319 try 1320 { 1321 _opusDecoder = opusOpen(_io, userData); 1322 assert(_opusDecoder !is null); 1323 _format = AudioFileFormat.opus; 1324 _sampleRate = _opusDecoder.rate; // Note: Opus file are always 48Khz 1325 _numChannels = _opusDecoder.channels(); 1326 _lengthInFrames = _opusDecoder.smpduration(); 1327 _opusPositionFrame = 0; 1328 return; 1329 } 1330 catch(Exception e) 1331 { 1332 destroyFree(e); 1333 } 1334 _opusDecoder = null; 1335 } 1336 1337 version(decodeFLAC) 1338 { 1339 _io.seek(0, false, userData); 1340 1341 // Is it a FLAC? 1342 { 1343 drflac_read_proc onRead = &flac_read; 1344 drflac_seek_proc onSeek = &flac_seek; 1345 void* pUserData = _decoderContext; 1346 _flacDecoder = drflac_open (onRead, onSeek, _decoderContext); 1347 if (_flacDecoder !is null) 1348 { 1349 _format = AudioFileFormat.flac; 1350 _sampleRate = _flacDecoder.sampleRate; 1351 _numChannels = _flacDecoder.channels; 1352 _lengthInFrames = _flacDecoder.totalSampleCount / _numChannels; 1353 _flacPositionFrame = 0; 1354 return; 1355 } 1356 } 1357 } 1358 1359 version(decodeWAV) 1360 { 1361 // Check if it's a WAV. 1362 1363 _io.seek(0, false, userData); 1364 1365 try 1366 { 1367 _wavDecoder = mallocNew!WAVDecoder(_io, userData); 1368 _wavDecoder.scan(); 1369 1370 // WAV detected 1371 _format = AudioFileFormat.wav; 1372 _sampleRate = _wavDecoder._sampleRate; 1373 _numChannels = _wavDecoder._channels; 1374 _lengthInFrames = _wavDecoder._lengthInFrames; 1375 return; 1376 } 1377 catch(Exception e) 1378 { 1379 // not a WAV 1380 destroyFree(e); 1381 } 1382 destroyFree(_wavDecoder); 1383 _wavDecoder = null; 1384 } 1385 1386 version(decodeOGG) 1387 { 1388 _io.seek(0, false, userData); 1389 1390 // Is it an OGG? 1391 { 1392 //"In my test files the maximal-size usage is ~150KB", so let's take a bit more 1393 _oggBuffer.reallocBuffer(200 * 1024); 1394 1395 stb_vorbis_alloc alloc; 1396 alloc.alloc_buffer = cast(ubyte*)(_oggBuffer.ptr); 1397 alloc.alloc_buffer_length_in_bytes = cast(int)(_oggBuffer.length); 1398 1399 int error; 1400 1401 _oggHandle = stb_vorbis_open_file(_io, userData, &error, &alloc); 1402 if (error == VORBIS__no_error) 1403 { 1404 _format = AudioFileFormat.ogg; 1405 _sampleRate = _oggHandle.sample_rate; 1406 _numChannels = _oggHandle.channels; 1407 _lengthInFrames = stb_vorbis_stream_length_in_samples(_oggHandle); 1408 return; 1409 } 1410 else 1411 { 1412 _oggHandle = null; 1413 } 1414 } 1415 } 1416 1417 version(decodeMP3) 1418 { 1419 // Check if it's a MP3. 1420 { 1421 _io.seek(0, false, userData); 1422 1423 ubyte* scratchBuffer = cast(ubyte*) malloc(MINIMP3_BUF_SIZE*2); 1424 scope(exit) free(scratchBuffer); 1425 1426 _mp3io = cast(mp3dec_io_t*) malloc(mp3dec_io_t.sizeof); 1427 _mp3io.read = &mp3_io_read; 1428 _mp3io.read_data = _decoderContext; 1429 _mp3io.seek = &mp3_io_seek; 1430 _mp3io.seek_data = _decoderContext; 1431 1432 if ( mp3dec_detect_cb(_mp3io, scratchBuffer, MINIMP3_BUF_SIZE*2) == 0 ) 1433 { 1434 // This is a MP3. Try to open a stream. 1435 1436 // Allocate a mp3dec_ex_t object 1437 _mp3DecoderNew = cast(mp3dec_ex_t*) malloc(mp3dec_ex_t.sizeof); 1438 1439 int result = mp3dec_ex_open_cb(_mp3DecoderNew, _mp3io, MP3D_SEEK_TO_SAMPLE); 1440 1441 if (0 == result) 1442 { 1443 // MP3 detected 1444 // but it seems we need to iterate all frames to know the length... 1445 _format = AudioFileFormat.mp3; 1446 _sampleRate = _mp3DecoderNew.info.hz; 1447 _numChannels = _mp3DecoderNew.info.channels; 1448 _lengthInFrames = _mp3DecoderNew.samples / _numChannels; 1449 return; 1450 } 1451 else 1452 { 1453 free(_mp3DecoderNew); 1454 _mp3DecoderNew = null; 1455 free(_mp3io); 1456 _mp3io = null; 1457 } 1458 } 1459 } 1460 } 1461 1462 version(decodeXM) 1463 { 1464 { 1465 // we need the first 60 bytes to check if XM 1466 char[60] xmHeader; 1467 int bytes; 1468 1469 _io.seek(0, false, userData); 1470 long lenBytes = _io.getFileLength(userData); 1471 if (lenBytes < 60) 1472 goto not_a_xm; 1473 1474 bytes = _io.read(xmHeader.ptr, 60, userData); 1475 if (bytes != 60) 1476 goto not_a_xm; 1477 1478 if (0 != xm_check_sanity_preload(xmHeader.ptr, 60)) 1479 goto not_a_xm; 1480 1481 _xmContent = cast(ubyte*) malloc(cast(int)lenBytes); 1482 _io.seek(0, false, userData); 1483 bytes = _io.read(_xmContent, cast(int)lenBytes, userData); 1484 if (bytes != cast(int)lenBytes) 1485 goto not_a_xm; 1486 1487 if (0 == xm_create_context_safe(&_xmDecoder, cast(const(char)*)_xmContent, cast(size_t)lenBytes, 44100)) 1488 { 1489 assert(_xmDecoder !is null); 1490 1491 xm_set_max_loop_count(_xmDecoder, 1); 1492 1493 _format = AudioFileFormat.xm; 1494 _sampleRate = 44100.0f; 1495 _numChannels = 2; 1496 _lengthInFrames = audiostreamUnknownLength; 1497 return; 1498 } 1499 1500 not_a_xm: 1501 assert(_xmDecoder == null); 1502 free(_xmContent); 1503 _xmContent = null; 1504 } 1505 } 1506 1507 version(decodeMOD) 1508 { 1509 { 1510 // we need either the first 1084 or 600 bytes if available 1511 _io.seek(0, false, userData); 1512 long lenBytes = _io.getFileLength(userData); 1513 if (lenBytes >= 600) 1514 { 1515 int headerBytes = lenBytes > 1084 ? 1084 : cast(int)lenBytes; 1516 1517 ubyte[1084] header; 1518 int bytes = _io.read(header.ptr, headerBytes, userData); 1519 1520 if (_pocketmod_ident(null, header.ptr, bytes)) 1521 { 1522 // This is a MOD, allocate a proper context, and read the whole file. 1523 _modDecoder = cast(pocketmod_context*) malloc(pocketmod_context.sizeof); 1524 1525 // Read whole .mod in a buffer, since the decoder work all from memory 1526 _io.seek(0, false, userData); 1527 _modContent.reallocBuffer(cast(size_t)lenBytes); 1528 bytes = _io.read(_modContent.ptr, cast(int)lenBytes, userData); 1529 1530 if (pocketmod_init(_modDecoder, _modContent.ptr, bytes, 44100)) 1531 { 1532 _format = AudioFileFormat.mod; 1533 _sampleRate = 44100.0f; 1534 _numChannels = 2; 1535 _lengthInFrames = audiostreamUnknownLength; 1536 return; 1537 } 1538 } 1539 } 1540 } 1541 } 1542 1543 _format = AudioFileFormat.unknown; 1544 _sampleRate = float.nan; 1545 _numChannels = 0; 1546 _lengthInFrames = -1; 1547 1548 throw mallocNew!Exception("Cannot decode stream: unrecognized encoding."); 1549 } 1550 1551 void startEncoding(AudioFileFormat format, float sampleRate, int numChannels, EncodingOptions options) @nogc 1552 { 1553 _format = format; 1554 _sampleRate = sampleRate; 1555 _numChannels = numChannels; 1556 1557 final switch(format) with (AudioFileFormat) 1558 { 1559 case mp3: 1560 throw mallocNew!Exception("Unsupported encoding format: MP3"); 1561 case flac: 1562 throw mallocNew!Exception("Unsupported encoding format: FLAC"); 1563 case ogg: 1564 throw mallocNew!Exception("Unsupported encoding format: OGG"); 1565 case opus: 1566 throw mallocNew!Exception("Unsupported encoding format: Opus"); 1567 case mod: 1568 throw mallocNew!Exception("Unsupported encoding format: MOD"); 1569 case xm: 1570 throw mallocNew!Exception("Unsupported encoding format: XM"); 1571 case wav: 1572 { 1573 // Note: fractional sample rates not supported by WAV, signal an integer one 1574 int isampleRate = cast(int)(sampleRate + 0.5f); 1575 1576 WAVEncoder.Format wavfmt; 1577 final switch (options.sampleFormat) 1578 { 1579 case AudioSampleFormat.s8: wavfmt = WAVEncoder.Format.s8; break; 1580 case AudioSampleFormat.s16: wavfmt = WAVEncoder.Format.s16le; break; 1581 case AudioSampleFormat.s24: wavfmt = WAVEncoder.Format.s24le; break; 1582 case AudioSampleFormat.fp32: wavfmt = WAVEncoder.Format.fp32le; break; 1583 case AudioSampleFormat.fp64: wavfmt = WAVEncoder.Format.fp64le; break; 1584 } 1585 _wavEncoder = mallocNew!WAVEncoder(_io, userData, isampleRate, numChannels, wavfmt, options.enableDither); 1586 break; 1587 } 1588 case unknown: 1589 throw mallocNew!Exception("Can't encode using 'unknown' coding"); 1590 } 1591 } 1592 1593 void finalizeEncodingIfNeeded() @nogc 1594 { 1595 if (_io && (_io.write !is null)) // if we have been encoding something 1596 { 1597 finalizeEncoding(); 1598 } 1599 } 1600 } 1601 1602 // AudioStream should be able to go on a smallish 32-bit stack, 1603 // and malloc the rest on the heap when needed. 1604 static assert(AudioStream.sizeof <= 256); 1605 1606 private: // not meant to be imported at all 1607 1608 1609 1610 // Internal object for audio-formats 1611 1612 1613 // File callbacks 1614 // The file callbacks are using the C stdlib. 1615 1616 struct FileContext // this is what is passed to I/O when used in file mode 1617 { 1618 // Used when streaming of writing a file 1619 FILE* file = null; 1620 1621 // Size of the file in bytes, only used when reading/writing a file. 1622 long fileSize; 1623 1624 // Initialize this context 1625 void initialize(const(char)[] path, bool forWrite) @nogc 1626 { 1627 CString strZ = CString(path); 1628 file = fopen(strZ.storage, forWrite ? "wb".ptr : "rb".ptr); 1629 if (file is null) 1630 throw mallocNew!Exception("File not found"); 1631 // finds the size of the file 1632 fseek(file, 0, SEEK_END); 1633 fileSize = ftell(file); 1634 fseek(file, 0, SEEK_SET); 1635 } 1636 } 1637 1638 long file_tell(void* userData) nothrow @nogc 1639 { 1640 FileContext* context = cast(FileContext*)userData; 1641 return ftell(context.file); 1642 } 1643 1644 bool file_seek(long offset, bool relative, void* userData) nothrow @nogc 1645 { 1646 FileContext* context = cast(FileContext*)userData; 1647 assert(offset <= int.max); 1648 int r = fseek(context.file, cast(int)offset, relative ? SEEK_CUR : SEEK_SET); // Limitations: file larger than 2gb not supported 1649 return r == 0; 1650 } 1651 1652 long file_getFileLength(void* userData) nothrow @nogc 1653 { 1654 FileContext* context = cast(FileContext*)userData; 1655 return context.fileSize; 1656 } 1657 1658 int file_read(void* outData, int bytes, void* userData) nothrow @nogc 1659 { 1660 FileContext* context = cast(FileContext*)userData; 1661 size_t bytesRead = fread(outData, 1, bytes, context.file); 1662 return cast(int)bytesRead; 1663 } 1664 1665 int file_write(void* inData, int bytes, void* userData) nothrow @nogc 1666 { 1667 FileContext* context = cast(FileContext*)userData; 1668 size_t bytesWritten = fwrite(inData, 1, bytes, context.file); 1669 return cast(int)bytesWritten; 1670 } 1671 1672 bool file_skip(int bytes, void* userData) nothrow @nogc 1673 { 1674 FileContext* context = cast(FileContext*)userData; 1675 return (0 == fseek(context.file, bytes, SEEK_CUR)); 1676 } 1677 1678 bool file_flush(void* userData) nothrow @nogc 1679 { 1680 FileContext* context = cast(FileContext*)userData; 1681 return ( fflush(context.file) == 0 ); 1682 } 1683 1684 // Memory read callback 1685 // Using the read buffer instead 1686 1687 struct MemoryContext 1688 { 1689 bool bufferIsOwned; 1690 bool bufferCanGrow; 1691 1692 // Buffer 1693 ubyte* buffer = null; 1694 1695 size_t size; // current buffer size 1696 size_t cursor; // where we are in the buffer 1697 size_t capacity; // max buffer size before realloc 1698 1699 void initializeWithConstantInput(const(ubyte)* data, size_t length) nothrow @nogc 1700 { 1701 // Make a copy of the input buffer, since it could be temporary. 1702 bufferIsOwned = true; 1703 bufferCanGrow = false; 1704 1705 buffer = mallocDup(data[0..length]).ptr; // Note: the copied slice is made mutable. 1706 size = length; 1707 cursor = 0; 1708 capacity = length; 1709 } 1710 1711 void initializeWithExternalOutputBuffer(ubyte* data, size_t length) nothrow @nogc 1712 { 1713 bufferIsOwned = false; 1714 bufferCanGrow = false; 1715 buffer = data; 1716 size = 0; 1717 cursor = 0; 1718 capacity = length; 1719 } 1720 1721 void initializeWithInternalGrowableBuffer() nothrow @nogc 1722 { 1723 bufferIsOwned = true; 1724 bufferCanGrow = true; 1725 buffer = null; 1726 size = 0; 1727 cursor = 0; 1728 capacity = 0; 1729 } 1730 1731 ~this() 1732 { 1733 if (bufferIsOwned) 1734 { 1735 if (buffer !is null) 1736 { 1737 free(buffer); 1738 buffer = null; 1739 } 1740 } 1741 } 1742 } 1743 1744 long memory_tell(void* userData) nothrow @nogc 1745 { 1746 MemoryContext* context = cast(MemoryContext*)userData; 1747 return cast(long)(context.cursor); 1748 } 1749 1750 bool memory_seek(long offset, bool relative, void* userData) nothrow @nogc 1751 { 1752 MemoryContext* context = cast(MemoryContext*)userData; 1753 if (relative) offset += context.cursor; 1754 if (offset < 0) 1755 return false; 1756 1757 bool r = true; 1758 if (offset >= context.size) // can't seek past end of buffer, stick to the end so that read return 0 byte 1759 { 1760 offset = context.size; 1761 r = false; 1762 } 1763 context.cursor = cast(size_t)offset; // Note: memory streams larger than 2gb not supported 1764 return r; 1765 } 1766 1767 long memory_getFileLength(void* userData) nothrow @nogc 1768 { 1769 MemoryContext* context = cast(MemoryContext*)userData; 1770 return cast(long)(context.size); 1771 } 1772 1773 int memory_read(void* outData, int bytes, void* userData) nothrow @nogc 1774 { 1775 MemoryContext* context = cast(MemoryContext*)userData; 1776 size_t cursor = context.cursor; 1777 size_t size = context.size; 1778 size_t available = size - cursor; 1779 if (bytes < available) 1780 { 1781 outData[0..bytes] = context.buffer[cursor..cursor + bytes]; 1782 context.cursor += bytes; 1783 return bytes; 1784 } 1785 else 1786 { 1787 outData[0..available] = context.buffer[cursor..cursor + available]; 1788 context.cursor = context.size; 1789 return cast(int)available; 1790 } 1791 } 1792 1793 int memory_write_limited(void* inData, int bytes, void* userData) nothrow @nogc 1794 { 1795 MemoryContext* context = cast(MemoryContext*)userData; 1796 size_t cursor = context.cursor; 1797 size_t size = context.size; 1798 size_t available = size - cursor; 1799 ubyte* buffer = context.buffer; 1800 ubyte* source = cast(ubyte*) inData; 1801 1802 if (cursor + bytes > available) 1803 { 1804 bytes = cast(int)(available - cursor); 1805 } 1806 1807 buffer[cursor..(cursor + bytes)] = source[0..bytes]; 1808 context.size += bytes; 1809 context.cursor += bytes; 1810 return bytes; 1811 } 1812 1813 int memory_write_append(void* inData, int bytes, void* userData) nothrow @nogc 1814 { 1815 MemoryContext* context = cast(MemoryContext*)userData; 1816 size_t cursor = context.cursor; 1817 size_t size = context.size; 1818 size_t available = size - cursor; 1819 ubyte* buffer = context.buffer; 1820 ubyte* source = cast(ubyte*) inData; 1821 1822 if (cursor + bytes > available) 1823 { 1824 size_t oldSize = context.capacity; 1825 size_t newSize = cursor + bytes; 1826 if (newSize < oldSize * 2 + 1) 1827 newSize = oldSize * 2 + 1; 1828 buffer = cast(ubyte*) realloc(buffer, newSize); 1829 context.capacity = newSize; 1830 1831 assert( cursor + bytes <= available ); 1832 } 1833 1834 buffer[cursor..(cursor + bytes)] = source[0..bytes]; 1835 context.size += bytes; 1836 context.cursor += bytes; 1837 return bytes; 1838 } 1839 1840 bool memory_skip(int bytes, void* userData) nothrow @nogc 1841 { 1842 MemoryContext* context = cast(MemoryContext*)userData; 1843 context.cursor += bytes; 1844 return context.cursor <= context.size; 1845 } 1846 1847 bool memory_flush(void* userData) nothrow @nogc 1848 { 1849 // do nothing, no flushign to do for memory 1850 return true; 1851 } 1852 1853 1854 // Decoder context 1855 struct DecoderContext 1856 { 1857 void* userDataIO; 1858 IOCallbacks* callbacks; 1859 } 1860 1861 // MP3 decoder read callback 1862 static int mp3ReadDelegate(void[] buf, void* userDataDecoder) @nogc nothrow 1863 { 1864 DecoderContext* context = cast(DecoderContext*) userDataDecoder; 1865 1866 // read bytes into the buffer, return number of bytes read or 0 for EOF, -1 on error 1867 // will never be called with empty buffer, or buffer more than 128KB 1868 1869 int bytes = context.callbacks.read(buf.ptr, cast(int)(buf.length), context.userDataIO); 1870 return bytes; 1871 } 1872 1873 1874 // FLAC decoder read callbacks 1875 1876 size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead) @nogc nothrow 1877 { 1878 DecoderContext* context = cast(DecoderContext*) pUserData; 1879 return context.callbacks.read(pBufferOut, cast(int)(bytesToRead), context.userDataIO); 1880 } 1881 1882 bool flac_seek(void* pUserData, int offset, drflac_seek_origin origin) @nogc nothrow 1883 { 1884 DecoderContext* context = cast(DecoderContext*) pUserData; 1885 if (origin == drflac_seek_origin_start) 1886 { 1887 context.callbacks.seek(offset, false, context.userDataIO); 1888 } 1889 else if (origin == drflac_seek_origin_current) 1890 { 1891 context.callbacks.seek(offset, true, context.userDataIO); 1892 } 1893 return true; 1894 } 1895 1896 // MP3 decoder read callbacks 1897 1898 size_t mp3_io_read(void *buf, size_t size, void *user_data) @nogc nothrow 1899 { 1900 DecoderContext* context = cast(DecoderContext*) user_data; 1901 return context.callbacks.read(buf, cast(int)(size), context.userDataIO); 1902 } 1903 1904 int mp3_io_seek(ulong position, void *user_data) @nogc nothrow 1905 { 1906 DecoderContext* context = cast(DecoderContext*) user_data; 1907 context.callbacks.seek(position, false, context.userDataIO); 1908 return 0; // doesn't detect seeking errors 1909 }