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