diff options
Diffstat (limited to 'src/wav/wav.odin')
| -rw-r--r-- | src/wav/wav.odin | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/src/wav/wav.odin b/src/wav/wav.odin new file mode 100644 index 0000000..04cb0cf --- /dev/null +++ b/src/wav/wav.odin @@ -0,0 +1,140 @@ +package wav + +import "core:fmt" +import "core:math" +import "core:strings" +import "core:os" + +Wav :: struct { + path : string, + handle : os.Handle, + buf : []u8, + reported_size : u32, + bext : string, + ixml : string, + format : Audio_Format, + channels : int, + channel_names : []string, + sample_rate : int, + bit_depth : int, +} +Audio_Format :: enum { + PCM = 1, + FLOAT = 3, +} + +BUFFER_SIZE :: 1<<15 + +main :: proc() { + sweden, sweden_ok := read_wav("test/sweden.wav", context.temp_allocator) + fmt.printf("\n\nsweden = %#v\n\n", sweden) + enok, enok_ok := read_wav("test/ENOKS-BIRHTDAYT02.WAV", context.temp_allocator) + fmt.printf("\n\nenok = %#v\n\n", enok) +} + + +read_wav :: proc(path : string, allocator:=context.allocator) -> (Wav, bool) { + file : Wav + + file_ok : os.Error + file.handle, file_ok = os.open(path) + defer os.close(file.handle) + assert(file_ok == os.General_Error.None) + + file.buf = new([BUFFER_SIZE]u8)[:] + defer delete(file.buf) + + os.read(file.handle, file.buf) + + + head : int = 0 + + // RIFF header + fmt.println(string(file.buf[0:4])) + if string(file.buf[0:4]) != "RIFF" do return {}, false + head += 4 + + // Size + file.reported_size = read_little_endian_u32(file.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 + head += 4 + + fmt.println("\nChunks:\n") + + // Looping through chunks + for _ in 0..<BUFFER_SIZE { + chunk_id := string(file.buf[head:head+4]) + head += 4 + chunk_size := int(read_little_endian_u32(file.buf[head:head+4])) + head += 4 + fmt.println(chunk_id,"\n-------------------------------------") + + if head+chunk_size < BUFFER_SIZE { + print_data : string + data_end := head+chunk_size + 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) + case "bext": + print_data = string(file.buf[head:read_end]) + file.bext = strings.clone(string(file.buf[head:data_end]), allocator=allocator) + case "fmt ": + file.format = Audio_Format(read_little_endian_u16(file.buf[head:])) + head += 2 + file.channels = int(read_little_endian_u16(file.buf[head:])) + head += 2 + file.sample_rate = int(read_little_endian_u32(file.buf[head:])) + head += 4 + + // Skipping byte rate and block align. + // These two legacy fields are only ever redundant or wrong, + // and are implied by the other fields. + head += 4 + 2 + + file.bit_depth = int(read_little_endian_u16(file.buf[head:])) + head += 2 + } + fmt.println(print_data, "\n") + } else { + break + } + + + head += chunk_size + } + + file.channel_names = make([]string, file.channels) + + naming_channel := 0 + for line in strings.split_lines(file.bext) { + if strings.starts_with(line, "sTRK") { + eq_index := strings.index(line, "=") + file.channel_names[naming_channel] = strings.clone(line[eq_index+1:], allocator=allocator) + naming_channel += 1 + } + } + + delete(file.buf) + file.buf = nil + return file, true +} + + +read_little_endian_u32 :: proc(x : []u8) -> u32 { + return u32(x[0]) | + u32(x[1]) << 8 | + u32(x[2]) << 16 | + u32(x[3]) << 24 +} + +read_little_endian_u16 :: proc(x : []u8) -> u16 { + return u16(x[0]) | + u16(x[1]) << 8 +}
\ No newline at end of file |