Open PeterMarques opened 1 year ago
PackedByteArray is an array of bytes (8-bit). I assume for things to work with the 16-bit mode, you would need to manually decompose your 16-bit numbers into two bytes (two PackedByteArray elements).
Edit: Checked the code and confirmed:
int byte_pr_sample = 0;
switch (format) {
case AudioStreamWAV::FORMAT_8_BITS:
byte_pr_sample = 1;
break;
case AudioStreamWAV::FORMAT_16_BITS:
byte_pr_sample = 2;
break;
case AudioStreamWAV::FORMAT_IMA_ADPCM:
byte_pr_sample = 4;
break;
}
If so this would need better documentation.
I'm not expert on audio or byte array stuff, but I believe you'd have to do something like this for your 16-bit samples:
var size = 44100 * 10
arr2.resize(size * 2)
for each in size:
var sample = randi_range(-pow(2, 14), pow(2, 14))
arr2.encode_s16(each * 2, sample)
Edit: It may need to be encode_u16
, not sure what's the deal with signedness here. It seems to read the 8-bit stuff as signed and make it unsigned, but read the 16-bit stuff as unsigned directly?
// Add data
Vector<uint8_t> stream_data = get_data();
const uint8_t *read_data = stream_data.ptr();
switch (format) {
case AudioStreamWAV::FORMAT_8_BITS:
for (unsigned int i = 0; i < data_bytes; i++) {
uint8_t data_point = (read_data[i] + 128);
file->store_8(data_point);
}
break;
case AudioStreamWAV::FORMAT_16_BITS:
for (unsigned 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 AudioStreamWAV::FORMAT_IMA_ADPCM:
//Unimplemented
break;
}
I'm not expert on audio or byte array stuff, but I believe you'd have to do something like this for your 16-bit samples:
var size = 44100 * 10 arr2.resize(size * 2) for each in size: var sample = randi_range(-pow(2, 14), pow(2, 14)) arr2.encode_s16(each * 2, sample)
Edit: It may need to be
encode_u16
It is indeed the solution to push the 16 bits long frames via encode_u16 as your script shows.
The code
extends Node
func _ready():
var size = 44100 *10
var rec1 = AudioStreamWAV.new()
var rec2 = AudioStreamWAV.new()
var rec3 = AudioStreamWAV.new()
var rec4 = AudioStreamWAV.new()
var arr1 = PackedByteArray([])
var arr2 = PackedByteArray([])
rec1.format = AudioStreamWAV.FORMAT_8_BITS
rec2.format = AudioStreamWAV.FORMAT_16_BITS
rec3.format = AudioStreamWAV.FORMAT_8_BITS
rec4.format = AudioStreamWAV.FORMAT_16_BITS
for each in size:
arr1.append(randi_range(pow(2,6)*-1,pow(2,6)))
arr2.resize(size * 2)
for each in size:
arr2.encode_u16(each*2,randi_range(pow(2,14)*-1,pow(2,14)))
rec1.set_data(arr1)
rec1.save_to_wav("/home/peterpm/Downloads/Godot4/rec1")
rec2.set_data(arr1)
rec2.save_to_wav("/home/peterpm/Downloads/Godot4/rec2")
rec3.set_data(arr2)
rec3.save_to_wav("/home/peterpm/Downloads/Godot4/rec3")
rec4.set_data(arr2)
rec4.save_to_wav("/home/peterpm/Downloads/Godot4/rec4")
now produces the files:
And the rec4 file is the 16bit file with the 16 bit sound, as it need to be.
Should i close the issue or it need to stay opens to fix the docs?
And if working with stereo audio, the channel works in a pair. So, you need to create the 2 buffers and intercalate then so it works correctly.
extends Node
func _ready():
var size = 44100 *10
var rec4 = AudioStreamWAV.new()
var arr2 = PackedByteArray([])
var arr3 = PackedByteArray([])
var arr4 = PackedByteArray([])
rec4.format = AudioStreamWAV.FORMAT_16_BITS
rec4.stereo = true
arr2.resize(size * 2)
arr3.resize(size * 2)
for each in size:
arr2.encode_u16(each*2,randi_range(pow(2,14)*-1,pow(2,14)))
for each in size:
arr3.encode_u16(each*2,randi_range(pow(2,12)*-1,pow(2,12)))
for each in size:
arr4.append(arr2[each*2])
arr4.append(arr2[(each*2)+1])
arr4.append(arr3[each*2])
arr4.append(arr3[(each*2)+1])
rec4.set_data(arr4)
rec4.save_to_wav("/home/peterpm/Downloads/Godot4/rec4")
generates the expected:
So it need to have that in the docs as well.
All that is counterproductive, but its the nature of the object, but lacks the ease of usage of Vector2 in the AudioStreamGenerator in the pushframe(), that handles both the bitdepth and the stereo issue in a single pass
well, just need better documentation in the AudioStreamWAV object.
And, there is another more performant way to mescle the arrays?
Godot version
4.1
System information
linux
Issue description
Yes, the data() Method indeed alerts you that it wants a signed 8 bit array (-128 to 127), but there is a format property that has the enum:
● FORMAT_8_BITS = 0 8-bit audio codec. ● FORMAT_16_BITS = 1 16-bit audio codec. ● FORMAT_IMA_ADPCM = 2 Audio is compressed using IMA ADPCM.
So, it should have suport for 16 bit audio in the data.
But, the alert indeed shows to be true, and even if setting the audio to be 16 bits, a random generator from -64 to 64, produces the same dinamic range, in either 8 or 16 bits.
And the lenght of the 16 bit file is half the lenght of the 8 bit one. Thats odd.
So, the bitchange instead of changing the depth of volume, changes the lenght of execution.
High resolution audio is a need, and the lenght issue is a undefined behaviours, so that is pretty bugged.
Thats it.
Steps to reproduce
Run the project and see the files (ajust the path)
Minimal reproduction project