|
@@ -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");
|