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