aboutsummaryrefslogtreecommitdiff
path: root/src/wav/wav.odin
diff options
context:
space:
mode:
authorSan Jacobs2025-12-11 20:44:02 +0100
committerSan Jacobs2025-12-11 20:44:02 +0100
commitcb7ef81fd339199c69eccd93105c13d2a1f41f71 (patch)
tree7109109bd690e91ff74ef2c09605e5626c1d9179 /src/wav/wav.odin
parent00121d7c14a3bfa03c5eeb6c28b5edf060baf029 (diff)
downloadbetter-report-cb7ef81fd339199c69eccd93105c13d2a1f41f71.tar.gz
better-report-cb7ef81fd339199c69eccd93105c13d2a1f41f71.tar.bz2
better-report-cb7ef81fd339199c69eccd93105c13d2a1f41f71.zip
We can now generate reports straight from the WAVs! No CSV needed!
Diffstat (limited to 'src/wav/wav.odin')
-rw-r--r--src/wav/wav.odin103
1 files changed, 59 insertions, 44 deletions
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