From cb7ef81fd339199c69eccd93105c13d2a1f41f71 Mon Sep 17 00:00:00 2001 From: San Jacobs Date: Thu, 11 Dec 2025 20:44:02 +0100 Subject: We can now generate reports straight from the WAVs! No CSV needed! --- src/wav/wav.odin | 103 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 59 insertions(+), 44 deletions(-) (limited to 'src/wav/wav.odin') diff --git a/src/wav/wav.odin b/src/wav/wav.odin index 01fbad1..41667b5 100644 --- a/src/wav/wav.odin +++ b/src/wav/wav.odin @@ -5,22 +5,25 @@ import "core:math" import "core:strings" import "core:strconv" import "core:os" -import "core:encoding/xml" +import "xml" Wav :: struct { // Basic data path : string, - handle : os.Handle, format : Audio_Format, channels : int, sample_rate : int, bit_depth : int, reported_size : u32, + // Internals + handle : os.Handle, + // Metadata + date : Date, channel_names : []string, samples_since_midnight: u64, - timecode : Timecode, + timecode : Timecode, // Derived from samples_since_midnight tc_framerate : f32, tc_dropframe : bool, ubits : [8]u8, @@ -32,7 +35,7 @@ Wav :: struct { circled : bool, } Audio_Format :: enum { - PCM = 1, + INT = 1, FLOAT = 3, } Timecode :: struct { @@ -41,23 +44,29 @@ Timecode :: struct { second : u8, frame : f32, } +Date :: struct { + year, month, day : int, +} +VERBOSE :: false BUFFER_SIZE :: 1<<15 main :: proc() { - enok, enok_ok := read_wav("test/ENOKS-BIRHTDAYT02.WAV", context.temp_allocator) - fmt.printf("\n\nenok = %#v\n\n", enok) - prins, prins_ok := read_wav("test/KRONPRINS01T01.wav", context.temp_allocator) - fmt.printf("\n\nprins = %#v\n\n", prins) - f8, f8_ok := read_wav("test/F8-SL098-T001.WAV", context.temp_allocator) - fmt.printf("\n\nf8 = %#v\n\n", f8) + // Test + enok, enok_ok := read("test/WAVs/ENOKS-BIRHTDAYT02.WAV", context.temp_allocator) + when VERBOSE do fmt.printf("\n\nenok = %#v\n\n", enok) + prins, prins_ok := read("test/WAVs/KRONPRINS01T01.wav", context.temp_allocator) + when VERBOSE do fmt.printf("\n\nprins = %#v\n\n", prins) + f8, f8_ok := read("test/WAVs/F8-SL098-T001.WAV", context.temp_allocator) + when VERBOSE do fmt.printf("\n\nf8 = %#v\n\n", f8) } /* -Reads in the wav file data, including metadata. +Reads in the wav file metadata, without loading the sound data into ram. */ -read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { +read :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) #optional_ok { file : Wav + file.path = path load_err : os.Error file.handle, load_err = os.open(path) @@ -79,21 +88,21 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { head : int = 0 // RIFF header - fmt.println(string(temp_buf[0:4])) + when VERBOSE do 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(temp_buf[head:head+4]) - fmt.println("Reported size:", file.reported_size) + when VERBOSE do fmt.println("Reported size:", file.reported_size) head += 4 // Confirming again that this is a wave file - fmt.println(string(temp_buf[head:head+4])) + when VERBOSE do 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") + when VERBOSE do fmt.println("\nChunks:\n") // Looping through chunks null_chunks := 0 @@ -103,7 +112,7 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { head += 4 chunk_size := int(read_little_endian_u32(temp_buf[head:head+4])) head += 4 - fmt.println(chunk_id, chunk_size,"\n-------------------------------------") + when VERBOSE do fmt.println(chunk_id, chunk_size,"\n-------------------------------------") data_reached := false next_chunk_start := head + chunk_size @@ -122,13 +131,13 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { null_chunks = 0 case "fmt ": file.format = Audio_Format(read_little_endian_u16(temp_buf[head:])) - fmt.println("Format:", file.format) + when VERBOSE do fmt.println("Format:", file.format) head += 2 file.channels = int(read_little_endian_u16(temp_buf[head:])) - fmt.println("Channels:", file.channels) + when VERBOSE do fmt.println("Channels:", file.channels) head += 2 file.sample_rate = int(read_little_endian_u32(temp_buf[head:])) - fmt.println("Sample rate:", file.sample_rate) + when VERBOSE do fmt.println("Sample rate:", file.sample_rate) head += 4 // Skipping byte rate and block align. @@ -137,7 +146,7 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { head += 4 + 2 file.bit_depth = int(read_little_endian_u16(temp_buf[head:])) - fmt.println("Bit depth:", file.bit_depth) + when VERBOSE do fmt.println("Bit depth:", file.bit_depth) head += 2 head = data_end null_chunks = 0 @@ -148,9 +157,9 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { null_chunks += 1 } } - fmt.println(print_data, "\n") + when VERBOSE do fmt.println(print_data, "\n") } else { - fmt.println("End of buffer reached.") + when VERBOSE do fmt.println("End of buffer reached.") break } @@ -158,11 +167,11 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { head = next_chunk_start if null_chunks > 3 { - fmt.println("Got more than 3 null chunks in a row. Quitting parse.") + when VERBOSE do fmt.println("Got more than 3 null chunks in a row. Quitting parse.") break } if data_reached { - fmt.println("Data reached.") + when VERBOSE do fmt.println("Data reached.") } } @@ -202,13 +211,13 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { } } - fmt.printf("\n") - tab(indent) + when VERBOSE do fmt.printf("\n") + when VERBOSE do tab(indent) element := doc.elements[element_id] if element.kind == .Element { - fmt.printf("<%v>", element.ident) + when VERBOSE do fmt.printf("<%v>", element.ident) if len(element.value) > 0 { value := element.value[0] @@ -301,18 +310,18 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { for value in element.value { switch v in value { case string: - fmt.printf(": %v", v) + when VERBOSE do fmt.printf(": %v", v) case xml.Element_ID: xml_recurse(doc, v, file, naming_channel, interleave_set, allocator, indent + 1) } } for attr in element.attribs { - tab(indent + 1) - fmt.printf("[Attr] %v: %v\n", attr.key, attr.val) + when VERBOSE do tab(indent + 1) + when VERBOSE do fmt.printf("[Attr] %v: %v\n", attr.key, attr.val) } } else if element.kind == .Comment { - fmt.printf("[COMMENT] %v\n", element.value) + when VERBOSE do fmt.printf("[COMMENT] %v\n", element.value) } return @@ -382,15 +391,21 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { } } head := 0 - fmt.printf("Description: \n%v\n", string(temp_bext[head:256])) + when VERBOSE do fmt.printf("Description: \n%v\n", string(temp_bext[head:256])) head += 256 - fmt.printf("Originator: %v\n", string(temp_bext[head:head+32])) + when VERBOSE do 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])) + when VERBOSE do 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])) + date := string(temp_bext[head:head+10]) + when VERBOSE do fmt.printf("Origination Date: %v\n", date) + date_splits := strings.split(date, "-") + file.date.year, _ = strconv.parse_int(date_splits[0]) + file.date.month, _ = strconv.parse_int(date_splits[1]) + file.date.day, _ = strconv.parse_int(date_splits[2]) + delete(date_splits) head += 10 - fmt.printf("Origination Time: %v\n", string(temp_bext[head:head+8])) + when VERBOSE do 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]) @@ -400,19 +415,19 @@ read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { 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)) + when VERBOSE do fmt.printf("Time Reference: %v (Samples since midnight, source of timecode)\n", file.samples_since_midnight) + when VERBOSE do 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])) + when VERBOSE do fmt.printf("Version: %v\n", read_little_endian_u16(temp_bext[head:head+2])) head += 2 - fmt.printf("UMID Skipped.\n") + when VERBOSE do fmt.printf("UMID Skipped.\n") head += 64 - fmt.printf("Skipped reserved nothingness.\n") + when VERBOSE do fmt.printf("Skipped reserved nothingness.\n") head += 190 - fmt.printf("Coding history:\n%v\n", string(temp_bext[head:])) + when VERBOSE do fmt.printf("Coding history:\n%v\n", string(temp_bext[head:])) } - fmt.println() + when VERBOSE do fmt.println() // just here to make some printing prettier temp_bext = nil -- cgit v1.2.1