Pārlūkot izejas kodu

AudioStreamSample can now be saved to a WAV file

8 and 16 bit sample saving has been implemented.
Gustav Lund 7 gadi atpakaļ
vecāks
revīzija
adb43b6976
1 mainītis faili ar 71 papildinājumiem un 1 dzēšanām
  1. 71 1
      scene/resources/audio_stream_sample.cpp

+ 71 - 1
scene/resources/audio_stream_sample.cpp

@@ -29,6 +29,8 @@
 /*************************************************************************/
 
 #include "audio_stream_sample.h"
+#include "io/marshalls.h"
+#include "os/file_access.h"
 
 void AudioStreamPlaybackSample::start(float p_from_pos) {
 
@@ -510,7 +512,73 @@ PoolVector<uint8_t> AudioStreamSample::get_data() const {
 }
 
 void AudioStreamSample::save_to_wav(String p_path) {
-//	TODO! Implement saving to wav file
+	if (format == AudioStreamSample::FORMAT_IMA_ADPCM) {
+		WARN_PRINTS("Saving IMA_ADPC samples are not supported yet");
+		return;
+	}
+
+	int sub_chunk_2_size = data_bytes; //Subchunk2Size = Size of data in bytes
+
+	// Format code
+	// 1:PCM format (for 8 or 16 bit)
+	// 3:IEEE float format
+	int format_code = (format == FORMAT_IMA_ADPCM) ? 3 : 1;
+
+	int n_channels = stereo ? 2 : 1;
+
+	long sample_rate = mix_rate;
+
+	int byte_pr_sample = 0;
+	switch (format) {
+		case AudioStreamSample::FORMAT_8_BITS: byte_pr_sample = 1; break;
+		case AudioStreamSample::FORMAT_16_BITS: byte_pr_sample = 2; break;
+		case AudioStreamSample::FORMAT_IMA_ADPCM: byte_pr_sample = 4; break;
+	}
+
+	String file_path = p_path;
+	if (!(file_path.substr(file_path.length() - 4, 4) == ".wav")) {
+		file_path += ".wav";
+	}
+
+	Error err;
+	FileAccess *file = FileAccess::open(file_path, FileAccess::WRITE, &err); //Overrides existing file if present
+
+	// Create WAV Header
+	file->store_string("RIFF"); //ChunkID
+	file->store_32(sub_chunk_2_size + 36); //ChunkSize = 36 + SubChunk2Size (size of entire file minus the 8 bits for this and previous header)
+	file->store_string("WAVE"); //Format
+	file->store_string("fmt "); //Subchunk1ID
+	file->store_32(16); //Subchunk1Size = 16
+	file->store_16(format_code); //AudioFormat
+	file->store_16(n_channels); //Number of Channels
+	file->store_32(sample_rate); //SampleRate
+	file->store_32(sample_rate * n_channels * byte_pr_sample); //ByteRate
+	file->store_16(n_channels * byte_pr_sample); //BlockAlign = NumChannels * BytePrSample
+	file->store_16(byte_pr_sample * 8); //BitsPerSample
+	file->store_string("data"); //Subchunk2ID
+	file->store_32(sub_chunk_2_size); //Subchunk2Size
+
+	// Add data
+	PoolVector<uint8_t>::Read read_data = get_data().read();
+	switch (format) {
+		case AudioStreamSample::FORMAT_8_BITS:
+			for (int i = 0; i < data_bytes; i++) {
+				uint8_t data_point = (read_data[i] + 128);
+				file->store_8(data_point);
+			}
+			break;
+		case AudioStreamSample::FORMAT_16_BITS:
+			for (int i = 0; i < data_bytes / 2; i++) {
+				uint16_t data_point = decode_uint16(&read_data[i * 2]);
+				file->store_16(data_point);
+			}
+			break;
+		case AudioStreamSample::FORMAT_IMA_ADPCM:
+			//Unimplemented
+			break;
+	}
+
+	file->close();
 }
 
 Ref<AudioStreamPlayback> AudioStreamSample::instance_playback() {
@@ -549,6 +617,8 @@ void AudioStreamSample::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_stereo", "stereo"), &AudioStreamSample::set_stereo);
 	ClassDB::bind_method(D_METHOD("is_stereo"), &AudioStreamSample::is_stereo);
 
+	ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamSample::save_to_wav);
+
 	ADD_PROPERTY(PropertyInfo(Variant::POOL_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_data", "get_data");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong"), "set_loop_mode", "get_loop_mode");