From d9bf52320afe653b84872e091a9d0b450e2e3b14 Mon Sep 17 00:00:00 2001 From: San Jacobs Date: Thu, 11 Dec 2025 01:23:46 +0100 Subject: Beginning wav file folder parse, need to replace file_list with job_list buf:[]u8 is completely temporary when parsing wav files now. When no file is specified, it spits out a header info template "info.txt" --- src/wav/wav.odin | 250 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 127 insertions(+), 123 deletions(-) (limited to 'src/wav') diff --git a/src/wav/wav.odin b/src/wav/wav.odin index f2cf85c..01fbad1 100644 --- a/src/wav/wav.odin +++ b/src/wav/wav.odin @@ -18,10 +18,7 @@ Wav :: struct { reported_size : u32, // Metadata - buf : []u8, channel_names : []string, - bext : []u8, - ixml : string, samples_since_midnight: u64, timecode : Timecode, tc_framerate : f32, @@ -56,38 +53,44 @@ main :: proc() { fmt.printf("\n\nf8 = %#v\n\n", f8) } -// TODO: Maybe split reading metadata into its own function call. - +/* +Reads in the wav file data, including metadata. +*/ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { file : Wav - file_ok : os.Error - file.handle, file_ok = os.open(path) + load_err : os.Error + file.handle, load_err = os.open(path) defer os.close(file.handle) defer file.handle = 0 - assert(file_ok == os.General_Error.None) + if load_err != os.General_Error.None { + fmt.eprintln("ERROR %v: Unable to load file \"%v\"", load_err, path) + return {}, false + } - file.buf = new([BUFFER_SIZE]u8)[:] - defer delete(file.buf) - os.read(file.handle, file.buf) + temp_buf := new([BUFFER_SIZE]u8)[:] + temp_bext : []u8 + temp_ixml : string + defer delete(temp_buf) + os.read(file.handle, temp_buf) head : int = 0 // RIFF header - fmt.println(string(file.buf[0:4])) - if string(file.buf[0:4]) != "RIFF" do return {}, false + fmt.println(string(temp_buf[0:4])) + if string(temp_buf[0:4]) != "RIFF" do return {}, false head += 4 // Size - file.reported_size = read_little_endian_u32(file.buf[head:head+4]) + file.reported_size = read_little_endian_u32(temp_buf[head:head+4]) fmt.println("Reported size:", file.reported_size) head += 4 - - // Confirming again that this is a wave file - fmt.println(string(file.buf[head:head+4])) - if string(file.buf[head:head+4]) != "WAVE" do return {}, false + + // Confirming again that this is a wave file + fmt.println(string(temp_buf[head:head+4])) + if string(temp_buf[head:head+4]) != "WAVE" do return {}, false head += 4 fmt.println("\nChunks:\n") @@ -95,10 +98,10 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { // Looping through chunks null_chunks := 0 for _ in 0.. (Wav, bool) { read_end := min(head+15000, data_end) switch chunk_id { case "iXML": - //print_data = string(file.buf[head:read_end]) - file.ixml = strings.clone(string(file.buf[head:data_end]), allocator=allocator) + temp_ixml = string(temp_buf[head:data_end]) + //print_data = temp_ixml null_chunks = 0 case "bext": - print_data = string(file.buf[head:read_end]) - file.bext = file.buf[head:data_end] + temp_bext = temp_buf[head:data_end] + //print_data = string(temp_bext) null_chunks = 0 case "fmt ": - file.format = Audio_Format(read_little_endian_u16(file.buf[head:])) + file.format = Audio_Format(read_little_endian_u16(temp_buf[head:])) fmt.println("Format:", file.format) head += 2 - file.channels = int(read_little_endian_u16(file.buf[head:])) + file.channels = int(read_little_endian_u16(temp_buf[head:])) fmt.println("Channels:", file.channels) head += 2 - file.sample_rate = int(read_little_endian_u32(file.buf[head:])) + file.sample_rate = int(read_little_endian_u32(temp_buf[head:])) fmt.println("Sample rate:", file.sample_rate) head += 4 @@ -133,7 +136,7 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { // and are implied by the other fields. head += 4 + 2 - file.bit_depth = int(read_little_endian_u16(file.buf[head:])) + file.bit_depth = int(read_little_endian_u16(temp_buf[head:])) fmt.println("Bit depth:", file.bit_depth) head += 2 head = data_end @@ -167,104 +170,15 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { - // bext parsing - if len(file.bext) > 0 { - - // Stripping null padding - end := len(file.bext) - 1 - for end >= 0 && file.bext[end] == 0 { - end -= 1 - } - file.bext = file.bext[:end] - - naming_channel := 0 - - description := string(file.bext[:256]) - for line in strings.split_lines(description) { - if strings.starts_with(line, "sTRK") || strings.starts_with(line, "zTRK") { - eq_index := strings.index(line, "=") - file.channel_names[naming_channel] = strings.clone(line[eq_index+1:], allocator=allocator) - naming_channel += 1 - } - if strings.starts_with(line[1:], "TAKE=") { - file.take, _ = strconv.parse_int(line[6:]) - } - if strings.starts_with(line[1:], "CIRCLED=") { - file.circled = line[9:] == "TRUE" - } - if strings.starts_with(line[1:], "SPEED=") { - value := line[7:] - num_end : int - type_start : int - for r, i in value { - if !strings.contains_rune("1234567890.", r) && num_end == 0 { - num_end = i - } - if r=='N' || r=='D' { - type_start = i - break - } - } - file.tc_framerate, _ = strconv.parse_f32(value[:num_end]) - file.tc_dropframe = value[type_start:] != "ND" - - } - // Only if ixml doesn't exist, so we don't allocate the note string twice. - if file.ixml == "" { - if strings.starts_with(line[1:], "NOTE=") { - v := line[6:] - if v != "" { - file.note = strings.clone(v, allocator=allocator) - } - } - } - } - head := 0 - fmt.printf("Description: \n%v\n", string(file.bext[head:256])) - head += 256 - fmt.printf("Originator: %v\n", string(file.bext[head:head+32])) - head += 32 - fmt.printf("Originator Reference: %v\n", string(file.bext[head:head+32])) - head += 32 - fmt.printf("Origination Date: %v\n", string(file.bext[head:head+10])) - head += 10 - fmt.printf("Origination Time: %v\n", string(file.bext[head:head+8])) - head += 8 - - file.samples_since_midnight = read_little_endian_u64(file.bext[head:head+8]) - - seconds_since_midnight := file.samples_since_midnight / u64(file.sample_rate) - file.timecode.hour = u8( seconds_since_midnight / 3600) - file.timecode.minute = u8((seconds_since_midnight % 3600) / 60) - file.timecode.second = u8( seconds_since_midnight % 60) - file.timecode.frame = f32( f64(file.samples_since_midnight % u64(file.sample_rate) ) * f64(file.tc_framerate) / f64(file.sample_rate)) - fmt.printf("Time Reference: %v (Samples since midnight, source of timecode)\n", file.samples_since_midnight) - fmt.printf(" %v seconds + %v samples\n", seconds_since_midnight, file.samples_since_midnight % u64(file.sample_rate)) - head += 8 - - fmt.printf("Version: %v\n", read_little_endian_u16(file.bext[head:head+2])) - head += 2 - fmt.printf("UMID Skipped.\n") - head += 64 - fmt.printf("Skipped reserved nothingness.\n") - head += 190 - fmt.printf("Coding history:\n%v\n", string(file.bext[head:])) - } - fmt.println() - - // just here to make some printing prettier - file.bext = nil - - // iXML Parsing - if file.ixml != "" { + if temp_ixml != "" { // Stripping null padding - end := len(file.ixml) - 1 - for end >= 0 && file.ixml[end] == 0 { + end := len(temp_ixml) - 1 + for end >= 0 && temp_ixml[end] == 0 { end -= 1 } - file.ixml = file.ixml[:end] + temp_ixml = temp_ixml[:end] naming_channel := 0 @@ -406,14 +320,104 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { parsed_ixml : ^xml.Document - parsed_ixml, _ = xml.parse(file.ixml, xml.Options{ + parsed_ixml, _ = xml.parse(temp_ixml, xml.Options{ flags={.Ignore_Unsupported}, expected_doctype = "", }) xml_recurse(parsed_ixml, 0, &file, &naming_channel, &interleave_set, allocator) } - file.buf = nil + // bext parsing + if len(temp_bext) > 0 { + + // Stripping null padding + end := len(temp_bext) - 1 + for end >= 0 && temp_bext[end] == 0 { + end -= 1 + } + temp_bext = temp_bext[:end] + + naming_channel := 0 + + description := string(temp_bext[:256]) + for line in strings.split_lines(description) { + + if file.channel_names[naming_channel] == "" && + (strings.starts_with(line, "sTRK") || strings.starts_with(line, "zTRK")) { + eq_index := strings.index(line, "=") + file.channel_names[naming_channel] = strings.clone(line[eq_index+1:], allocator=allocator) + naming_channel += 1 + } + if strings.starts_with(line[1:], "TAKE=") { + file.take, _ = strconv.parse_int(line[6:]) + } + if strings.starts_with(line[1:], "CIRCLED=") { + file.circled = line[9:] == "TRUE" + } + if strings.starts_with(line[1:], "SPEED=") { + value := line[7:] + num_end : int + type_start : int + for r, i in value { + if !strings.contains_rune("1234567890.", r) && num_end == 0 { + num_end = i + } + if r=='N' || r=='D' { + type_start = i + break + } + } + file.tc_framerate, _ = strconv.parse_f32(value[:num_end]) + file.tc_dropframe = value[type_start:] != "ND" + + } + // Only if ixml doesn't exist, so we don't allocate the note string twice. + if file.note == "" { + if strings.starts_with(line[1:], "NOTE=") { + v := line[6:] + if v != "" { + file.note = strings.clone(v, allocator=allocator) + } + } + } + } + head := 0 + fmt.printf("Description: \n%v\n", string(temp_bext[head:256])) + head += 256 + fmt.printf("Originator: %v\n", string(temp_bext[head:head+32])) + head += 32 + fmt.printf("Originator Reference: %v\n", string(temp_bext[head:head+32])) + head += 32 + fmt.printf("Origination Date: %v\n", string(temp_bext[head:head+10])) + head += 10 + fmt.printf("Origination Time: %v\n", string(temp_bext[head:head+8])) + head += 8 + + file.samples_since_midnight = read_little_endian_u64(temp_bext[head:head+8]) + + seconds_since_midnight := file.samples_since_midnight / u64(file.sample_rate) + file.timecode.hour = u8( seconds_since_midnight / 3600) + file.timecode.minute = u8((seconds_since_midnight % 3600) / 60) + file.timecode.second = u8( seconds_since_midnight % 60) + file.timecode.frame = f32( f64(file.samples_since_midnight % u64(file.sample_rate) ) * f64(file.tc_framerate) / f64(file.sample_rate)) + fmt.printf("Time Reference: %v (Samples since midnight, source of timecode)\n", file.samples_since_midnight) + fmt.printf(" %v seconds + %v samples\n", seconds_since_midnight, file.samples_since_midnight % u64(file.sample_rate)) + head += 8 + + fmt.printf("Version: %v\n", read_little_endian_u16(temp_bext[head:head+2])) + head += 2 + fmt.printf("UMID Skipped.\n") + head += 64 + fmt.printf("Skipped reserved nothingness.\n") + head += 190 + fmt.printf("Coding history:\n%v\n", string(temp_bext[head:])) + } + fmt.println() + + // just here to make some printing prettier + temp_bext = nil + + temp_buf = nil return file, true } -- cgit v1.2.1