Skip to content

Commit

Permalink
Add support for 24 and 32 bit .wav's
Browse files Browse the repository at this point in the history
Apparently those .wav's are always format_code 3?

The conversion is SLOW on long files (like songs).
It requires going through every byte to do the conversion.
So if performance is important, GDScript is just not fast enough for this.
  • Loading branch information
L4Vo5 committed Apr 16, 2022
1 parent 2fd9821 commit dfd3957
Showing 1 changed file with 53 additions and 7 deletions.
60 changes: 53 additions & 7 deletions GDScriptAudioImport.gd
Original file line number Diff line number Diff line change
Expand Up @@ -56,15 +56,15 @@ func loadfile(filepath):
return AudioStreamSample.new()

var bytes = file.get_buffer(file.get_len())

# if File is wav
if filepath.ends_with(".wav"):

var newstream = AudioStreamSample.new()

#---------------------------
#parrrrseeeeee!!! :D


var bits_per_sample = 0

for i in range(0, 100):
var those4bytes = str(char(bytes[i])+char(bytes[i+1])+char(bytes[i+2])+char(bytes[i+3]))

Expand All @@ -90,6 +90,9 @@ func loadfile(filepath):
if format_code == 0: format_name = "8_BITS"
elif format_code == 1: format_name = "16_BITS"
elif format_code == 2: format_name = "IMA_ADPCM"
else:
format_name = "UNKNOWN (trying to interpret as 16_BITS)"
format_code = 1
print ("Format: " + str(format_code) + " " + format_name)
#assign format to our AudioStreamSample
newstream.format = format_code
Expand All @@ -114,20 +117,25 @@ func loadfile(filepath):
var bits_sample_channel = bytes[fsc0+12] + (bytes[fsc0+13] << 8)
print ("BitsPerSample * Channel / 8: " + str(bits_sample_channel))

#aaaand bits per sample/bitrate [Bytes 14-15] - TODO: Handle different bitrates
var bits_per_sample = bytes[fsc0+14] + (bytes[fsc0+15] << 8)
#aaaand bits per sample/bitrate [Bytes 14-15]
bits_per_sample = bytes[fsc0+14] + (bytes[fsc0+15] << 8)
print ("Bits per sample: " + str(bits_per_sample))


if those4bytes == "data":
assert(bits_per_sample != 0)

var audio_data_size = bytes[i+4] + (bytes[i+5] << 8) + (bytes[i+6] << 16) + (bytes[i+7] << 24)
print ("Audio data/stream size is " + str(audio_data_size) + " bytes")

var data_entry_point = (i+8)
print ("Audio data starts at byte " + str(data_entry_point))

newstream.data = bytes.subarray(data_entry_point, data_entry_point+audio_data_size-1)
var data = bytes.subarray(data_entry_point, data_entry_point+audio_data_size-1)

if bits_per_sample in [24, 32]:
newstream.data = convert_to_16bit(data, bits_per_sample)
else:
newstream.data = data
# end of parsing
#---------------------------

Expand Down Expand Up @@ -155,6 +163,44 @@ func loadfile(filepath):
print ("ERROR: Wrong filetype or format")
file.close()

# Converts .wav data from 24 or 32 bits to 16
#
# These conversions are SLOW in GDScript
# on my one test song, 32 -> 16 was around 3x slower than 24 -> 16
#
# I couldn't get threads to help very much
# They made the 24bit case about 2x faster in my test file
# And the 32bit case abour 50% slower
# I don't wanna risk it always being slower on other files
# And really, the solution would be to handle it in a low-level language
func convert_to_16bit(data: PoolByteArray, from: int) -> PoolByteArray:
print("converting to 16-bit from %d" % from)
var time = OS.get_ticks_msec()
# 24 bit .wav's are typically stored as integers
# so we just grab the 2 most significant bytes and ignore the other
if from == 24:
var j = 0
for i in range(0, data.size(), 3):

This comment has been minimized.

Copy link
@SubSage

SubSage Nov 6, 2022

Tested this, ran into issue with out of bounds. Fixed by limiting end of the range by the step size.
for i in range(0, data.size() - 3, 3):

Same in the next snippet below
for i in range(0, data.size() - 4, 4):

data[j] = data[i+1]
data[j+1] = data[i+2]
j += 2
data.resize(data.size() * 2 / 3)
# 32 bit .wav's are typically stored as floating point numbers
# so we need to grab all 4 bytes and interpret them as a float first
if from == 32:
var spb := StreamPeerBuffer.new()
var single_float: float
var value: int
for i in range(0, data.size(), 4):
spb.data_array = data.subarray(i, i+3)
single_float = spb.get_float()
value = single_float * 32768
data[i/2] = value
data[i/2+1] = value >> 8
data.resize(data.size() / 2)
print("Took %f seconds for slow conversion" % ((OS.get_ticks_msec() - time) / 1000.0))
return data


# ---------- REFERENCE ---------------
# note: typical values doesn't always match
Expand Down

0 comments on commit dfd3957

Please sign in to comment.