|
28 | 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
29 | 29 | /**************************************************************************/
|
30 | 30 |
|
31 |
| -#include "audio_stream_wav.h" |
32 |
| - |
33 |
| -#include "core/io/file_access_memory.h" |
34 | 31 | #include "core/io/marshalls.h"
|
| 32 | +#include "thirdparty/dr_libs/dr_wav_defines.h" |
| 33 | + |
| 34 | +#include "audio_stream_wav.h" |
35 | 35 |
|
36 | 36 | const float TRIM_DB_LIMIT = -50;
|
37 | 37 | const int TRIM_FADE_OUT_FRAMES = 500;
|
@@ -724,221 +724,78 @@ Ref<AudioSample> AudioStreamWAV::generate_sample() const {
|
724 | 724 | }
|
725 | 725 |
|
726 | 726 | Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_stream_data, const Dictionary &p_options) {
|
727 |
| - // /* STEP 1, READ WAVE FILE */ |
728 |
| - |
729 |
| - Ref<FileAccessMemory> file; |
730 |
| - file.instantiate(); |
731 |
| - Error err = file->open_custom(p_stream_data.ptr(), p_stream_data.size()); |
732 |
| - ERR_FAIL_COND_V_MSG(err != OK, Ref<AudioStreamWAV>(), "Cannot create memfile for WAV file buffer."); |
733 |
| - |
734 |
| - /* CHECK RIFF */ |
735 |
| - char riff[5]; |
736 |
| - riff[4] = 0; |
737 |
| - file->get_buffer((uint8_t *)&riff, 4); //RIFF |
738 |
| - |
739 |
| - if (riff[0] != 'R' || riff[1] != 'I' || riff[2] != 'F' || riff[3] != 'F') { |
740 |
| - ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), vformat("Not a WAV file. File should start with 'RIFF', but found '%s', in file of size %d bytes", riff, file->get_length())); |
741 |
| - } |
742 |
| - |
743 |
| - /* GET FILESIZE */ |
| 727 | + drwav wav; |
744 | 728 |
|
745 |
| - // The file size in header is 8 bytes less than the actual size. |
746 |
| - // See https://docs.fileformat.com/audio/wav/ |
747 |
| - const int FILE_SIZE_HEADER_OFFSET = 8; |
748 |
| - uint32_t file_size_header = file->get_32() + FILE_SIZE_HEADER_OFFSET; |
749 |
| - uint64_t file_size = file->get_length(); |
750 |
| - if (file_size != file_size_header) { |
751 |
| - WARN_PRINT(vformat("File size %d is %s than the expected size %d.", file_size, file_size > file_size_header ? "larger" : "smaller", file_size_header)); |
| 729 | + if (!drwav_init_memory_with_metadata(&wav, p_stream_data.ptr(), p_stream_data.size(), DRWAV_WITH_METADATA, nullptr)) { |
| 730 | + ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Audio data is invalid, corrupted, or an unsupported format."); |
752 | 731 | }
|
753 | 732 |
|
754 |
| - /* CHECK WAVE */ |
755 |
| - |
756 |
| - char wave[5]; |
757 |
| - wave[4] = 0; |
758 |
| - file->get_buffer((uint8_t *)&wave, 4); //WAVE |
759 |
| - |
760 |
| - if (wave[0] != 'W' || wave[1] != 'A' || wave[2] != 'V' || wave[3] != 'E') { |
761 |
| - ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), vformat("Not a WAV file. Header should contain 'WAVE', but found '%s', in file of size %d bytes", wave, file->get_length())); |
762 |
| - } |
763 |
| - |
764 |
| - // Let users override potential loop points from the WAV. |
765 |
| - // We parse the WAV loop points only with "Detect From WAV" (0). |
766 |
| - int import_loop_mode = p_options["edit/loop_mode"]; |
767 |
| - |
768 |
| - int format_bits = 0; |
769 |
| - int format_channels = 0; |
| 733 | + return _load_from_dr_wav(wav, p_options); |
| 734 | +} |
770 | 735 |
|
771 |
| - AudioStreamWAV::LoopMode loop_mode = AudioStreamWAV::LOOP_DISABLED; |
772 |
| - uint16_t compression_code = 1; |
773 |
| - bool format_found = false; |
774 |
| - bool data_found = false; |
775 |
| - int format_freq = 0; |
776 |
| - int loop_begin = 0; |
777 |
| - int loop_end = 0; |
778 |
| - int frames = 0; |
| 736 | +Ref<AudioStreamWAV> AudioStreamWAV::load_from_file(const String &p_path, const Dictionary &p_options) { |
| 737 | + Error err; |
779 | 738 |
|
780 |
| - Vector<float> data; |
| 739 | + Ref<FileAccess> file = FileAccess::open(p_path, FileAccess::READ, &err); |
| 740 | + ERR_FAIL_COND_V_MSG(err != OK, Ref<AudioStreamWAV>(), vformat("Cannot open file '%s'.", p_path)); |
781 | 741 |
|
782 |
| - while (!file->eof_reached()) { |
783 |
| - /* chunk */ |
784 |
| - char chunk_id[4]; |
785 |
| - file->get_buffer((uint8_t *)&chunk_id, 4); //RIFF |
| 742 | + drwav_read_proc read_fa = [](void *p_user_data, void *p_out, size_t to_read) -> size_t { |
| 743 | + Ref<FileAccess> lfile = *(Ref<FileAccess> *)p_user_data; |
| 744 | + return lfile->get_buffer((uint8_t *)p_out, to_read); |
| 745 | + }; |
786 | 746 |
|
787 |
| - /* chunk size */ |
788 |
| - uint32_t chunksize = file->get_32(); |
789 |
| - uint32_t file_pos = file->get_position(); //save file pos, so we can skip to next chunk safely |
| 747 | + drwav_seek_proc seek_fa = [](void *p_user_data, int p_offset, drwav_seek_origin origin) -> drwav_bool32 { |
| 748 | + Ref<FileAccess> lfile = *(Ref<FileAccess> *)p_user_data; |
| 749 | + uint64_t new_offset = p_offset + (origin == drwav_seek_origin_current ? lfile->get_position() : 0); |
790 | 750 |
|
791 |
| - if (file->eof_reached()) { |
792 |
| - //ERR_PRINT("EOF REACH"); |
793 |
| - break; |
| 751 | + if (new_offset > lfile->get_length()) { |
| 752 | + return DRWAV_FALSE; |
794 | 753 | }
|
795 | 754 |
|
796 |
| - if (chunk_id[0] == 'f' && chunk_id[1] == 'm' && chunk_id[2] == 't' && chunk_id[3] == ' ' && !format_found) { |
797 |
| - /* IS FORMAT CHUNK */ |
| 755 | + lfile->seek(new_offset); |
| 756 | + return DRWAV_TRUE; |
| 757 | + }; |
798 | 758 |
|
799 |
| - //Issue: #7755 : Not a bug - usage of other formats (format codes) are unsupported in current importer version. |
800 |
| - //Consider revision for engine version 3.0 |
801 |
| - compression_code = file->get_16(); |
802 |
| - if (compression_code != 1 && compression_code != 3) { |
803 |
| - ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM or IEEE float instead."); |
804 |
| - } |
| 759 | + drwav wav; |
805 | 760 |
|
806 |
| - format_channels = file->get_16(); |
807 |
| - if (format_channels != 1 && format_channels != 2) { |
808 |
| - ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Format not supported for WAVE file (not stereo or mono)."); |
809 |
| - } |
810 |
| - |
811 |
| - format_freq = file->get_32(); //sampling rate |
812 |
| - |
813 |
| - file->get_32(); // average bits/second (unused) |
814 |
| - file->get_16(); // block align (unused) |
815 |
| - format_bits = file->get_16(); // bits per sample |
| 761 | + if (!drwav_init_with_metadata(&wav, read_fa, seek_fa, &file, DRWAV_WITH_METADATA, nullptr)) { |
| 762 | + ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Cannot read data from file '" + p_path + "'. Data is invalid, corrupted, or an unsupported format."); |
| 763 | + } |
816 | 764 |
|
817 |
| - if (format_bits % 8 || format_bits == 0) { |
818 |
| - ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32)."); |
819 |
| - } |
| 765 | + return _load_from_dr_wav(wav, p_options); |
| 766 | +} |
820 | 767 |
|
821 |
| - if (compression_code == 3 && format_bits % 32) { |
822 |
| - ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Invalid amount of bits in the IEEE float sample (should be 32 or 64)."); |
823 |
| - } |
| 768 | +Ref<AudioStreamWAV> AudioStreamWAV::_load_from_dr_wav(drwav &p_wav, const Dictionary &p_options) { |
| 769 | + // STEP 1, READ DATA |
824 | 770 |
|
825 |
| - /* Don't need anything else, continue */ |
826 |
| - format_found = true; |
827 |
| - } |
| 771 | + int format_bits = p_wav.bitsPerSample; |
| 772 | + int format_channels = p_wav.channels; |
| 773 | + int format_freq = p_wav.sampleRate; |
| 774 | + int frames = p_wav.totalPCMFrameCount; |
828 | 775 |
|
829 |
| - if (chunk_id[0] == 'd' && chunk_id[1] == 'a' && chunk_id[2] == 't' && chunk_id[3] == 'a' && !data_found) { |
830 |
| - /* IS DATA CHUNK */ |
831 |
| - data_found = true; |
| 776 | + int import_loop_mode = p_options["edit/loop_mode"]; |
832 | 777 |
|
833 |
| - if (!format_found) { |
834 |
| - ERR_PRINT("'data' chunk before 'format' chunk found."); |
| 778 | + int loop_begin = 0; |
| 779 | + int loop_end = 0; |
| 780 | + LoopMode loop_mode = LOOP_DISABLED; |
| 781 | + if (import_loop_mode == 0) { |
| 782 | + for (uint32_t meta = 0; meta < p_wav.metadataCount; ++meta) { |
| 783 | + drwav_metadata md = p_wav.pMetadata[meta]; |
| 784 | + if (md.type == drwav_metadata_type_smpl && md.data.smpl.sampleLoopCount > 0) { |
| 785 | + drwav_smpl_loop loop = md.data.smpl.pLoops[0]; |
| 786 | + loop_mode = (LoopMode)(loop.type + 1); |
| 787 | + loop_begin = loop.firstSampleByteOffset; |
| 788 | + loop_end = loop.lastSampleByteOffset; |
835 | 789 | break;
|
836 | 790 | }
|
837 |
| - |
838 |
| - uint64_t remaining_bytes = file_size - file_pos; |
839 |
| - frames = chunksize; |
840 |
| - if (remaining_bytes < chunksize) { |
841 |
| - WARN_PRINT("Data chunk size is smaller than expected. Proceeding with actual data size."); |
842 |
| - frames = remaining_bytes; |
843 |
| - } |
844 |
| - |
845 |
| - ERR_FAIL_COND_V(format_channels == 0, Ref<AudioStreamWAV>()); |
846 |
| - frames /= format_channels; |
847 |
| - frames /= (format_bits >> 3); |
848 |
| - |
849 |
| - /*print_line("chunksize: "+itos(chunksize)); |
850 |
| - print_line("channels: "+itos(format_channels)); |
851 |
| - print_line("bits: "+itos(format_bits)); |
852 |
| - */ |
853 |
| - |
854 |
| - data.resize(frames * format_channels); |
855 |
| - |
856 |
| - if (compression_code == 1) { |
857 |
| - if (format_bits == 8) { |
858 |
| - for (int i = 0; i < frames * format_channels; i++) { |
859 |
| - // 8 bit samples are UNSIGNED |
860 |
| - |
861 |
| - data.write[i] = int8_t(file->get_8() - 128) / 128.f; |
862 |
| - } |
863 |
| - } else if (format_bits == 16) { |
864 |
| - for (int i = 0; i < frames * format_channels; i++) { |
865 |
| - //16 bit SIGNED |
866 |
| - |
867 |
| - data.write[i] = int16_t(file->get_16()) / 32768.f; |
868 |
| - } |
869 |
| - } else { |
870 |
| - for (int i = 0; i < frames * format_channels; i++) { |
871 |
| - //16+ bits samples are SIGNED |
872 |
| - // if sample is > 16 bits, just read extra bytes |
873 |
| - |
874 |
| - uint32_t s = 0; |
875 |
| - for (int b = 0; b < (format_bits >> 3); b++) { |
876 |
| - s |= ((uint32_t)file->get_8()) << (b * 8); |
877 |
| - } |
878 |
| - s <<= (32 - format_bits); |
879 |
| - |
880 |
| - data.write[i] = (int32_t(s) >> 16) / 32768.f; |
881 |
| - } |
882 |
| - } |
883 |
| - } else if (compression_code == 3) { |
884 |
| - if (format_bits == 32) { |
885 |
| - for (int i = 0; i < frames * format_channels; i++) { |
886 |
| - //32 bit IEEE Float |
887 |
| - |
888 |
| - data.write[i] = file->get_float(); |
889 |
| - } |
890 |
| - } else if (format_bits == 64) { |
891 |
| - for (int i = 0; i < frames * format_channels; i++) { |
892 |
| - //64 bit IEEE Float |
893 |
| - |
894 |
| - data.write[i] = file->get_double(); |
895 |
| - } |
896 |
| - } |
897 |
| - } |
898 |
| - |
899 |
| - // This is commented out due to some weird edge case seemingly in FileAccessMemory, doesn't seem to have any side effects though. |
900 |
| - // if (file->eof_reached()) { |
901 |
| - // ERR_FAIL_V_MSG(Ref<AudioStreamWAV>(), "Premature end of file."); |
902 |
| - // } |
903 | 791 | }
|
904 |
| - |
905 |
| - if (import_loop_mode == 0 && chunk_id[0] == 's' && chunk_id[1] == 'm' && chunk_id[2] == 'p' && chunk_id[3] == 'l') { |
906 |
| - // Loop point info! |
907 |
| - |
908 |
| - /** |
909 |
| - * Consider exploring next document: |
910 |
| - * http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Docs/RIFFNEW.pdf |
911 |
| - * Especially on page: |
912 |
| - * 16 - 17 |
913 |
| - * Timestamp: |
914 |
| - * 22:38 06.07.2017 GMT |
915 |
| - **/ |
916 |
| - |
917 |
| - for (int i = 0; i < 10; i++) { |
918 |
| - file->get_32(); // i wish to know why should i do this... no doc! |
919 |
| - } |
920 |
| - |
921 |
| - // only read 0x00 (loop forward), 0x01 (loop ping-pong) and 0x02 (loop backward) |
922 |
| - // Skip anything else because it's not supported, reserved for future uses or sampler specific |
923 |
| - // from https://sites.google.com/site/musicgapi/technical-documents/wav-file-format#smpl (loop type values table) |
924 |
| - int loop_type = file->get_32(); |
925 |
| - if (loop_type == 0x00 || loop_type == 0x01 || loop_type == 0x02) { |
926 |
| - if (loop_type == 0x00) { |
927 |
| - loop_mode = AudioStreamWAV::LOOP_FORWARD; |
928 |
| - } else if (loop_type == 0x01) { |
929 |
| - loop_mode = AudioStreamWAV::LOOP_PINGPONG; |
930 |
| - } else if (loop_type == 0x02) { |
931 |
| - loop_mode = AudioStreamWAV::LOOP_BACKWARD; |
932 |
| - } |
933 |
| - loop_begin = file->get_32(); |
934 |
| - loop_end = file->get_32(); |
935 |
| - } |
936 |
| - } |
937 |
| - // Move to the start of the next chunk. Note that RIFF requires a padding byte for odd |
938 |
| - // chunk sizes. |
939 |
| - file->seek(file_pos + chunksize + (chunksize & 1)); |
940 | 792 | }
|
941 | 793 |
|
| 794 | + Vector<float> data; |
| 795 | + data.resize(frames * format_channels); |
| 796 | + drwav_read_pcm_frames_f32(&p_wav, frames, data.ptrw()); |
| 797 | + drwav_uninit(&p_wav); |
| 798 | + |
942 | 799 | // STEP 2, APPLY CONVERSIONS
|
943 | 800 |
|
944 | 801 | bool is16 = format_bits != 8;
|
@@ -1176,12 +1033,6 @@ Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_st
|
1176 | 1033 | return sample;
|
1177 | 1034 | }
|
1178 | 1035 |
|
1179 |
| -Ref<AudioStreamWAV> AudioStreamWAV::load_from_file(const String &p_path, const Dictionary &p_options) { |
1180 |
| - const Vector<uint8_t> stream_data = FileAccess::get_file_as_bytes(p_path); |
1181 |
| - ERR_FAIL_COND_V_MSG(stream_data.is_empty(), Ref<AudioStreamWAV>(), vformat("Cannot open file '%s'.", p_path)); |
1182 |
| - return load_from_buffer(stream_data, p_options); |
1183 |
| -} |
1184 |
| - |
1185 | 1036 | void AudioStreamWAV::_bind_methods() {
|
1186 | 1037 | ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_buffer", "stream_data", "options"), &AudioStreamWAV::load_from_buffer, DEFVAL(Dictionary()));
|
1187 | 1038 | ClassDB::bind_static_method("AudioStreamWAV", D_METHOD("load_from_file", "path", "options"), &AudioStreamWAV::load_from_file, DEFVAL(Dictionary()));
|
|
0 commit comments