What I have | What I want |
---|---|
For a long time I was messing around with struct
and wave
modules approach this, but these things (and possibly more) still happen in this code:
marker_name
argument is not working: Its name is just empty.wave.Error: unknown format: 3
is occurring when audio bit depth is32
- When trying to use function multiple times, previously set markers are removed
Is there a fix to these issues, or more possibly a library that may do it?
import wave
import struct
def add_wav_cue(input_path: str, marker_name: str, sample_position: int, output_path: str = None) -> str:
"""
Adds CUE marker to WAV file.
Arguments:
input_path (str): Existing audio file path.
marker_name (str): Name of CUE point.
sample_position (int): Sample at which CUE point will be set.
output_path (str): If None, writes back to input file.
Returns:
str: Output file path.
"""
# Open file
with wave.open(input_path, "rb") as wav_file:
params = wav_file.getparams()
if any((sample_position > params.nframes, sample_position < 0)):
raise IndexError("Invalid sample position")
frames = wav_file.readframes(params.nframes)
with open(input_path, "rb") as wav_file:
rest_of_file = wav_file.read()
if output_path is None:
output_path = input_path
#-=-=-=-#
# CUE
# Create chunk
chunk_cue_id = b"cue "
num_cue_points = 1
cue_point_id = 1
id_chunk_data = b"data"
chunk_start = 0
block_start = 0
# Create the point data
cue_chunk_size = 4 + (num_cue_points * 24)
cue_point_data = struct.pack(
"<iiiiiiii",
cue_point_id,
sample_position,
struct.unpack("<I", id_chunk_data)[0],
chunk_start,
block_start,
sample_position,
0,
0
)
# Pack the chunk
cue_chunk_data = struct.pack(
"<4sI",
chunk_cue_id,
cue_chunk_size
)
cue_chunk_data += struct.pack(
"<I",
num_cue_points
)
cue_chunk_data += cue_point_data
#-=-=-=-#
# ADTL
# Create the chunk with label for the cue point
adtl_chunk_id = b"adtl"
label_chunk_id = b"labl"
label_chunk_size = 4 + len(marker_name) + 1
label_data = struct.pack(
"<4sIii",
label_chunk_id,
label_chunk_size,
cue_point_id,
len(marker_name) + 1
)
label_data += marker_name.encode("ascii") + b"x00"
# Pack the chunk
adtl_chunk_size = len(label_data)
adtl_chunk_data = struct.pack(
"<4sI",
adtl_chunk_id,
adtl_chunk_size
)
adtl_chunk_data += label_data
#-=-=-=-#
# RIFF
# Calculate new chunk size excluding the first 8 bytes of "RIFF" and its size field
riff_chunk_size = sum(map(len, (rest_of_file, cue_chunk_data, adtl_chunk_data))) + 4
# Pack the new chunk header with updated size
new_riff_chunk_header = struct.pack(
"<4sI4s",
b"RIFF",
riff_chunk_size,
b"WAVE"
)
#-=-=-=-#
data = new_riff_chunk_header + rest_of_file + cue_chunk_data + adtl_chunk_data
with open(input_path, "wb") as output_path:
output_path.write(data)
return output_path
#-=-=-=-#
# Usage
add_wav_cue("sine.wav", "my marker name", 83)
add_wav_cue("sine.wav", "another marker", 228)