I need a little coding help for my BRR program
BotB Academy Bulletins
 
 
210703
Level 12 Mixist
Da Flarf
 
 
post #210703 :: 2025.02.11 7:59am :: edit 2025.02.11 7:59am
  
  nitrofurano liēkd this
Hi, a while ago I expressed interest in writing tools to help with .spc music. Well, the first step on that journey is a little program I'm working on called "FlarfBrr".

FlarfBrr is essentially going to be a graphical version of Optiroc's Brr encoder program. The user would drag and drop a wav file into the window, have its loop points automatically read, and then have a straight conversion to Brr based on the loop points.

I've added the drag and drop functionality, and now I'm working on a script to test detecting the loop points. After researching the .wav filetype here's what I've come up with in godot

extends Node

# Function to extract and check loop points
func get_loop_points(wav_path: String):
var file = FileAccess.open(wav_path, FileAccess.READ)
if file == null:
print("Error: Could not open file")
return

var buffer = file.get_buffer(file.get_length()) # Read full file
var smpl_bytes = "smpl".to_utf8_buffer() # Convert string to PackedByteArray
var smpl_index = -1

# Manually search for the 'smpl' bytes in the buffer
for i in range(buffer.size() - smpl_bytes.size()):
if buffer.slice(i, i + smpl_bytes.size()) == smpl_bytes:
smpl_index = i
break

if smpl_index == -1:
print("doesn't loop")
return

# Extract loop start and loop end values (little-endian format)
var loop_start_bytes = buffer.slice(smpl_index + 28, smpl_index + 32)
var loop_end_bytes = buffer.slice(smpl_index + 32, smpl_index + 36)

# Reverse manually and decode to uint32
loop_start_bytes.reverse()
loop_end_bytes.reverse()
var loop_start = loop_start_bytes.decode_u32(0) # Decode from the beginning of the slice
var loop_end = loop_end_bytes.decode_u32(0) # Decode from the beginning of the slice

# If loop start and loop end are the same, there's no loop
if loop_start == loop_end:
print("doesn't loop")
return

# If loop points are divisible by 16, it's compatible
if loop_start % 16 == 0 and loop_end % 16 == 0:
print("loopable")
else:
print("incompatible")

# Usage example
func _ready():
get_loop_points("res://OverdriveGuitar.wav") # Change the path to your WAV file

For some reason, samples that loop are incorrectly labeled as non-looping. Can someone help me rewrite this script to properly get the loop points? (I tried posting this on stack overflow but they didn't like it for some reason. jerks)
 
 
210705
Level 29 Chipist
Jangler
 
 
 
post #210705 :: 2025.02.11 8:09am
  
  Da Flarf liēkd this
 
 
210706
Level 12 Mixist
Da Flarf
 
 
post #210706 :: 2025.02.11 8:35am :: edit 2025.02.11 9:04am
Thanks for the quick reply! What language are you using? I need to take a look at the documentation for those methods so I can convert it to godot

(edit) NVM I see it's rust

(edit 2) It works now :D

extends Node

# Function to extract and check loop points
func get_loop_points(wav_path: String):
var file = FileAccess.open(wav_path, FileAccess.READ)
if file == null:
print("Error: Could not open file")
return

var buffer = file.get_buffer(file.get_length()) # Read entire file
var smpl_bytes = "smpl".to_utf8_buffer() # Convert "smpl" to PackedByteArray
var smpl_index = -1

# Find the "smpl" chunk manually
for i in range(buffer.size() - smpl_bytes.size()):
if buffer.slice(i, i + smpl_bytes.size()) == smpl_bytes:
smpl_index = i
break

if smpl_index == -1:
print("doesn't loop")
return

# Read number of sample loops (4 bytes at offset 0x24 from "smpl" start)
var num_loops_offset = smpl_index + 36
if num_loops_offset + 4 > buffer.size():
print("Invalid WAV: smpl chunk too short")
return

var num_loops = _read_little_endian_u32(buffer, num_loops_offset)

if num_loops == 0:
print("doesn't loop")
return

# Read loop points
var all_compatible = true
for i in range(num_loops):
var start_offset = smpl_index + 52 + (i * 28) # 0x34 + (i * 28)
var end_offset = smpl_index + 56 + (i * 28) # 0x38 + (i * 28)

if end_offset + 4 > buffer.size():
print("Invalid WAV: loop point out of range")
return

var start_sample = _read_little_endian_u32(buffer, start_offset)
var end_sample = _read_little_endian_u32(buffer, end_offset)

#idk why but it's always 1 off
end_sample += 1

print("Loop Start:", start_sample, "Loop End:", end_sample)

# Validate loop points
if start_sample == end_sample:
print("Loop points are the same: doesn't loop")
return

if start_sample % 16 != 0 or end_sample % 16 != 0:
all_compatible = false

if all_compatible:
print("loopable")
else:
print("incompatible")


# Helper function to read a little-endian 32-bit unsigned integer
func _read_little_endian_u32(buffer: PackedByteArray, offset: int) -> int:
if offset + 4 > buffer.size():
return 0 # Prevent out-of-bounds reads
var bytes = buffer.slice(offset, offset + 4)
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24)


# Usage example
func _ready():
get_loop_points("res://bass_elec.wav") # Change the path to your WAV file
 
 

LOGIN or REGISTER to add your own comments!