diff --git a/flac.odin b/flac.odin index 132d8aa..4f7f700 100644 --- a/flac.odin +++ b/flac.odin @@ -9,6 +9,7 @@ import "core:slice" import "core:io" import "core:os" import "core:time" +import "core:prof/spall" @(private) FLAC_MAGIC :: 'f' << 24 | 'L' << 16 | 'a' << 8 | 'C' @@ -252,6 +253,15 @@ StreamInfoBlock :: struct #packed { md5: [16]byte, } +#assert(size_of(PictureMetadata) == 20) +PictureMetadata :: struct { + width: u32be, + height: u32be, + depth: u32be, + num_colors: u32be, + data_len: u32be, +} + #assert(size_of(CueSheet) == 396) CueSheet :: struct #packed { media_catalog_num: [128]byte, @@ -285,13 +295,13 @@ CueSheetTrackIndex :: struct #packed { reserved: [3]byte, } -decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32, err: Error) { +decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32, err: Error) #no_bounds_check { subframe_type_str := "" // TODO: temp allocator?? so we copy the slice of data into the _actual_ sample buffer // Maybe even a static arena with a size of 32MB by default (and configurable using #config) or something // that we then use to allocate from (better perf is the reason because allocating on the heap for every subframe // is probably noticibly slower) - subframe_samples := make([dynamic]i32, 0, block_size) + subframe_samples := make([]i32, block_size) //fmt.println("SUBFRAME START AT OFFSET:", r.i) //fmt.println("SUBFRAME offset bits", r.bits_read_in_byte) @@ -322,10 +332,11 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 bits_to_read := int(bps) - wasted_bits sample := i32(read_bits(r, bits_to_read) or_return) if sample > 0 && (sample >> (uint(bits_to_read) - 1)) & 1 == 1 { - sample = -(((~sample) & (i32(math.pow2_f32(bits_to_read)) - 1)) + 1) + sample = -(((~sample) & (i32(pow2(bits_to_read)) - 1)) + 1) } for s in 0.. (samples: []i32 //unencoded_subblock_arr := transmute([2]byte)unencoded_subblock //fmt.printfln("arr: %X", unencoded_subblock_arr) //fmt.printfln("arr as u16: %X", transmute(u16)unencoded_subblock_arr) - append(&subframe_samples, unencoded_subblock) - //append(&decoded_samples, ..unencoded_subblock_arr[:]) + //append(&subframe_samples, unencoded_subblock) + subframe_samples[sample] = unencoded_subblock } } else if (subframe_type >> 5) & 1 == 1 { // LPC //fmt.println("SUBFRAME: LPC") @@ -368,13 +379,14 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 unencoded_warmup_sample := i32(read_bits(r, bits_to_read) or_return) // fmt.println("warmup sample:", unencoded_warmup_sample) if unencoded_warmup_sample > 0 && (unencoded_warmup_sample >> (uint(bits_to_read) - 1)) & 1 == 1 { - unencoded_warmup_sample = -(((~unencoded_warmup_sample) & (i32(math.pow2_f32(bits_to_read)) - 1)) + 1) + unencoded_warmup_sample = -(((~unencoded_warmup_sample) & (i32(pow2(bits_to_read)) - 1)) + 1) } //fmt.printfln("unencoded warmup sample: 0b%b", unencoded_warmup_sample) //fmt.println("unencoded warmup sample:", unencoded_warmup_sample) //fmt.printfln("unencoded warmup sample: 0x%X", unencoded_warmup_sample) - append(&subframe_samples, unencoded_warmup_sample) + //append(&subframe_samples, unencoded_warmup_sample) + subframe_samples[s] = unencoded_warmup_sample } coeff_precision_minus_one := u8(read_bits(r, 4) or_return) @@ -386,7 +398,7 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 coeff_bits_to_shift := i8(read_bits(r, 5) or_return) // NOTE: This is signed 2's complement so we check the MSB // NOTE: This is apparently supposed to never be a negative number according to the spec. - // TODO: For now we just error if the shift value is negative. I have to find test cases to be sure. + // TODO: For now we just error if the shift value is negative. I have to find test cases to be sure. if (coeff_bits_to_shift >> (5 - 1)) & 1 == 1 { //fmt.println("BITS TO SHIFT IS NEGATIVE") //coeff_bits_to_shift = -(((~coeff_bits_to_shift) & i8(math.pow2_f32(5) - 1)) + 1) @@ -402,7 +414,7 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 unencoded_coefficient := i32(read_bits(r, int(coeff_precision)) or_return) // Ditto if unencoded_coefficient > 0 && (unencoded_coefficient >> (coeff_precision - 1)) & 1 == 1 { - unencoded_coefficient = -(((~unencoded_coefficient) & (i32(math.pow2_f32(coeff_precision)) - 1)) + 1) + unencoded_coefficient = -(((~unencoded_coefficient) & (i32(pow2(coeff_precision)) - 1)) + 1) } coefficients[i] = unencoded_coefficient //fmt.println("unencoded_coefficient", unencoded_coefficient) @@ -416,7 +428,7 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 partition_order := read_bits(r, 4) or_return //fmt.println("parition order", partition_order) - partitions := int(math.pow2_f32(partition_order)) + partitions := int(pow2(partition_order)) //fmt.println("partitions:", partitions) //fmt.println("block_size:", block_size) @@ -430,14 +442,15 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 if partition_order == 0 { num_samples = int(block_size - u16(predictor_order)) } else if i != 0 { - num_samples = int(block_size / u16(math.pow2_f32(partition_order))) + num_samples = int(block_size / u16(pow2(partition_order))) } else { - num_samples = int((block_size / u16(math.pow2_f32(partition_order))) - u16(predictor_order)) + num_samples = int((block_size / u16(pow2(partition_order))) - u16(predictor_order)) } if unencoded_bps == 0 { for sample in 0.. (samples: []i32 // NOTE: This is signed 2's complement so we check the MSB residual_sample_value := i32(read_bits(r, int(unencoded_bps)) or_return) if residual_sample_value > 0 && (residual_sample_value >> (unencoded_bps - 1)) & 1 == 1 { - residual_sample_value = -(((~residual_sample_value) & (i32(math.pow2_f32(unencoded_bps)) - 1)) + 1) + residual_sample_value = -(((~residual_sample_value) & (i32(pow2(unencoded_bps)) - 1)) + 1) } // Restore sample values using the predictor and the residual values @@ -472,7 +485,8 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 sample := i32(predictor + int(residual_sample_value)) //sample := (i32(predictor) + residual_sample_value) - append(&subframe_samples, sample) + //append(&subframe_samples, sample) + subframe_samples[sample_i + int(predictor_order)] = sample //fmt.printfln(" residual[%d]=%d", sample_i, residual_sample_value) //fmt.printfln(" sample[%d]=%d", sample_i, sample) sample_i += 1 @@ -484,16 +498,15 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 if partition_order == 0 { num_samples = int(block_size - u16(predictor_order)) } else if i != 0 { - num_samples = int(block_size / u16(math.pow2_f32(partition_order))) + num_samples = int(block_size / u16(pow2(partition_order))) } else { - num_samples = int((block_size / u16(math.pow2_f32(partition_order))) - u16(predictor_order)) + num_samples = int((block_size / u16(pow2(partition_order))) - u16(predictor_order)) } //fmt.printfln("reading %d samples for residual", num_samples) // // Undo the RICE coding // - previous_sample_value := 0 for s in 0.. (samples: []i32 //fmt.println("quotient:", quotient) //fmt.println("remainder:", remainder) - zigzag_encoded_value := quotient * int(math.pow2_f32(rice_parameter)) + remainder + zigzag_encoded_value := quotient * int(pow2(rice_parameter)) + remainder // Unzigzag the residual sample values residual_sample_value := zigzag_encoded_value % 2 == 0 ? zigzag_encoded_value / 2 : (zigzag_encoded_value + 1) / -2 @@ -529,7 +542,8 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 //fmt.printfln(" predictor[%d]=%d", sample_i, predictor) sample := (predictor + residual_sample_value) - append(&subframe_samples, i32(sample)) + //append(&subframe_samples, i32(sample)) + subframe_samples[sample_i + int(predictor_order)] = i32(sample) //fmt.printfln(" residual[%d]=%d", sample_i, residual_sample_value) //fmt.printfln(" sample[%d]=%d", sample_i, sample) @@ -559,12 +573,13 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 for sample in 0.. 0 && (unencoded_warmup_sample >> (uint(bits_to_read) - 1)) & 1 == 1 { - unencoded_warmup_sample = -(((~unencoded_warmup_sample) & (i32(math.pow2_f32(bits_to_read)) - 1)) + 1) + unencoded_warmup_sample = -(((~unencoded_warmup_sample) & (i32(pow2(bits_to_read)) - 1)) + 1) } //fmt.println("unencoded warmup sample:", unencoded_warmup_sample) //fmt.printfln("unencoded warmup sample: 0x%X", unencoded_warmup_sample) - append(&subframe_samples, unencoded_warmup_sample) + //append(&subframe_samples, unencoded_warmup_sample) + subframe_samples[sample] = unencoded_warmup_sample } residual_coding_method := ResidualCodingMethod(read_bits(r, 2) or_return) @@ -575,7 +590,7 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 partition_order := read_bits(r, 4) or_return //fmt.println("parition order", partition_order) - partitions := int(math.pow2_f32(partition_order)) + partitions := int(pow2(partition_order)) //fmt.println("partitions:", partitions) //fmt.println("block_size:", block_size) @@ -589,15 +604,16 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 if partition_order == 0 { num_samples = int(block_size - u16(predictor_order)) } else if i != 0 { - num_samples = int(block_size / u16(math.pow2_f32(partition_order))) + num_samples = int(block_size / u16(pow2(partition_order))) } else { - num_samples = int((block_size / u16(math.pow2_f32(partition_order))) - u16(predictor_order)) + num_samples = int((block_size / u16(pow2(partition_order))) - u16(predictor_order)) } if unencoded_bps == 0 { for sample in 0.. (samples: []i32 // NOTE: This is signed 2's complement so we check the MSB residual_sample_value := i8(read_bits(r, int(unencoded_bps)) or_return) if residual_sample_value > 0 && (residual_sample_value >> (unencoded_bps - 1)) & 1 == 1 { - residual_sample_value = -(((~residual_sample_value) & (i8(math.pow2_f32(unencoded_bps)) - 1)) + 1) + residual_sample_value = -(((~residual_sample_value) & (i8(pow2(unencoded_bps)) - 1)) + 1) } //fmt.printfln(" residual[%d]=%d", sample_i, residual_sample_value) @@ -625,9 +641,9 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 if partition_order == 0 { num_samples = int(block_size - u16(predictor_order)) } else if i != 0 { - num_samples = int(block_size / u16(math.pow2_f32(partition_order))) + num_samples = int(block_size / u16(pow2(partition_order))) } else { - num_samples = int((block_size / u16(math.pow2_f32(partition_order))) - u16(predictor_order)) + num_samples = int((block_size / u16(pow2(partition_order))) - u16(predictor_order)) } //fmt.printfln("reading %d samples for residual", samples) @@ -645,7 +661,7 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 //fmt.println("quotient:", quotient) //fmt.println("remainder:", remainder) - zigzag_encoded_value := quotient * int(math.pow2_f32(rice_parameter)) + remainder + zigzag_encoded_value := quotient * int(pow2(rice_parameter)) + remainder // Unzigzag the residual sample values residual_sample_value := zigzag_encoded_value % 2 == 0 ? zigzag_encoded_value / 2 : (zigzag_encoded_value + 1) / -2 @@ -672,7 +688,8 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 //sample_value := residual_sample_value + (predictor_order == 0 ? 0 : int(subframe_samples[sample_i])) - append(&subframe_samples, i32(sample_value)) + //append(&subframe_samples, i32(sample_value)) + subframe_samples[sample_i + int(predictor_order)] = i32(sample_value) sample_i += 1 @@ -695,7 +712,7 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 // TODO: remove frame_num := 0 -decode_frame :: proc(r: ^Reader, streaminfo_bps: u8, streaminfo_sample_rate: u32, channels: u8) -> (err: Error) { +decode_frame :: proc(r: ^Reader, streaminfo_bps: u8, streaminfo_sample_rate: u32, channels: u8) -> (err: Error) #no_bounds_check { // // Frame Header // @@ -742,13 +759,13 @@ decode_frame :: proc(r: ^Reader, streaminfo_bps: u8, streaminfo_sample_rate: u32 case 1: actual_block_size = 192 case 2..=5: - actual_block_size = 576 * auto_cast (math.pow2_f32(block_size - 2)) + actual_block_size = 576 * auto_cast (pow2(block_size - 2)) case 6: actual_block_size = auto_cast (read_byte(r) or_return) + 1 case 7: actual_block_size = (read_data(r, u16be) or_return) + 1 case 8..=15: - actual_block_size = 256 * auto_cast (math.pow2_f32(block_size - 8)) + actual_block_size = 256 * auto_cast (pow2(block_size - 8)) case: return .Invalid_Block_Size } @@ -945,29 +962,27 @@ decode_frame :: proc(r: ^Reader, streaminfo_bps: u8, streaminfo_sample_rate: u32 switch bps { case 1..=8: data := []u8{u8(sample)} - os.write(output_file, data) + //os.write(output_file, data) md5.update(&md5_ctx, data) case 9..=16: first_byte := i8(sample & 0xFF) second_byte := i8((sample >> 8) & 0xFF) - data := slice.reinterpret([]u8, []i8{first_byte, second_byte}) - os.write(output_file, data) + data := []u8{u8(first_byte), u8(second_byte)} + //data := slice.reinterpret([]u8, []i8{first_byte, second_byte}) + //os.write(output_file, data) md5.update(&md5_ctx, data) case 17..=24: first_byte := i8(sample & 0xFF) second_byte := i8((sample >> 8) & 0xFF) third_byte := i8((sample >> 16) & 0xFF) - data := slice.reinterpret([]u8, []i8{first_byte, second_byte, third_byte}) - os.write(output_file, data) + data := []u8{u8(first_byte), u8(second_byte), u8(third_byte)} + //data := slice.reinterpret([]u8, []i8{first_byte, second_byte, third_byte}) + //os.write(output_file, data) md5.update(&md5_ctx, data) case 25..=32: - first_byte := i8(sample & 0xFF) - second_byte := i8((sample >> 8) & 0xFF) - third_byte := i8((sample >> 16) & 0xFF) - fourth_byte := i8((sample >> 24) & 0xFF) - data := slice.reinterpret([]u8, []i8{first_byte, second_byte, third_byte, fourth_byte}) - os.write(output_file, data) - md5.update(&md5_ctx, data) + data := transmute([4]u8)sample + //os.write(output_file, data[:]) + md5.update(&md5_ctx, data[:]) case: fmt.println("INVALID BPS") os.exit(1) @@ -1192,17 +1207,17 @@ load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (err: E } case .PICTURE: // TODO: save the pic data into a struct - // TODO: maybe put all of these into a struct so we do one big read? pic_type := read_data(&r, PictureType) or_return mime_len := read_data(&r, u32be) or_return mimetype := string(read_slice(&r, auto_cast mime_len) or_return) desc_len := read_data(&r, u32be) or_return desc := string(read_slice(&r, auto_cast desc_len) or_return) - width := read_data(&r, u32be) or_return - height := read_data(&r, u32be) or_return - depth := read_data(&r, u32be) or_return - num_colors := read_data(&r, u32be) or_return - data_len := read_data(&r, u32be) or_return + metadata := read_data(&r, PictureMetadata) or_return + width := metadata.width + height := metadata.height + depth := metadata.depth + num_colors := metadata.num_colors + data_len := metadata.data_len data := read_slice(&r, auto_cast data_len) or_return //fmt.println(pic_type) //fmt.println(mimetype) @@ -1250,8 +1265,8 @@ load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (err: E load_from_file :: proc(filename: string, allocator := context.allocator) -> (err: Error) { context.allocator = allocator - output_file, _ = os.open("output.raw", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0o664) - defer os.close(output_file) + //output_file, _ = os.open("output.raw", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0o664) + //defer os.close(output_file) data, ok := os.read_entire_file(filename) defer delete(data) diff --git a/reader.odin b/reader.odin index 10819ea..3661a10 100644 --- a/reader.odin +++ b/reader.odin @@ -3,14 +3,13 @@ package flac import "core:bytes" import "core:io" -import "core:math" /* Basic byte reader with support for reading bits. Uses core:bytes reader under the hood with a few useful procedures for reading structs and other types. */ Reader :: struct { using _: bytes.Reader, - bits_read_in_byte: u8, + bits_read: u8, } read_slice :: #force_inline proc(r: ^Reader, size: int) -> (res: []u8, err: io.Error) { @@ -37,43 +36,95 @@ read_data :: #force_inline proc(r: ^Reader, $T: typeid) -> (res: T, err: io.Erro return (^T)(&b[0])^, nil } -read_bit :: #force_inline proc(r: ^Reader) -> (bit: byte, err: Error) { +pow2 :: #force_inline proc(#any_int exp: uint) -> int { + return 1 << exp +} + +read_bit :: proc(r: ^Reader) -> (bit: byte, err: Error) #no_bounds_check { if r.i >= i64(len(r.s)) { return 0, .EOF } - byte := r.s[r.i] - // Get the bit that's after however many bits read starting from the MSB in the byte - bits := 8 - (r.bits_read_in_byte + 1) - b := (r.s[r.i:r.i+1][0] & auto_cast math.pow2_f32(bits)) >> bits - r.bits_read_in_byte += 1 + bits := 8 - (r.bits_read + 1) + b := (r.s[r.i] >> bits) & 1 + r.bits_read += 1 - if r.bits_read_in_byte == 8 { + if r.bits_read == 8 { r.i += 1 - r.bits_read_in_byte = 0 + r.bits_read = 0 } return b, nil } -read_bits :: proc(r: ^Reader, n: int) -> (bits: u64, err: Error) { - x: u64 = 0 +//read_bits :: proc(r: ^Reader, n: int) -> (bits: u64, err: Error) { +// x: u64 = 0 +// +// for i in 0 ..< n { +// x = 2 * x + auto_cast read_bit(r) or_return +// } +// +// return x, nil +//} - for i in 0 ..< n { - x = 2 * x + auto_cast read_bit(r) or_return - } +// TODO: optimize this even more +read_bits :: proc(r: ^Reader, n: int) -> (bits: u64, err: Error) #no_bounds_check { - return x, nil + //if n % 8 == 0 { + + //} + + + //if r.bits_remaining < u8(n) { + // // we need to read into the next byte too and possibly more bytes + //} else if r.bits_remaining == u8(n) { + // r.bits_remaining = 0 + // r.i += 1 + //} else { + + //} + + n := n + for n > 0 { + if r.i >= i64(len(r.s)) { + return 0, .EOF + } + + to_read := min(n, 8-int(r.bits_read)) + mask := (1 << u8(to_read)) - 1 + shift := 8 - int(r.bits_read) - to_read + + // Extract bits from the current byte + bitsPart := u64(r.s[r.i] >> u8(shift)) & u64(mask) + bits |= bitsPart << u64(n - to_read) + + // Update counters + r.bits_read += u8(to_read) + n -= to_read + + // Move to the next byte if all bits in the current byte have been read + if r.bits_read == 8 { + r.i += 1 + r.bits_read = 0 + } + } + + return bits, nil } -read_byte :: proc(r: ^Reader) -> (b: byte, err: Error) { +read_byte :: proc(r: ^Reader) -> (b: byte, err: Error) #no_bounds_check { if r.i >= i64(len(r.s)) { return 0, .EOF } // Continue reading from the current byte if it has unread bits. // Needed for the subframes. - if r.bits_read_in_byte != 0 { - b = u8(read_bits(r, 8) or_return) + if r.bits_read != 0 { + cur_byte := r.s[r.i] + next_byte := r.s[r.i + 1] + rem_bits := 8 - r.bits_read + rem_bits_mask: u8 = (1 << rem_bits) - 1 + b = ((cur_byte & rem_bits_mask) << r.bits_read) | (next_byte >> rem_bits) + r.i += 1 } else { b = r.s[r.i] r.i += 1 @@ -82,7 +133,7 @@ read_byte :: proc(r: ^Reader) -> (b: byte, err: Error) { } align_to_byte :: proc(r: ^Reader) -> Error { - if r.bits_read_in_byte == 0 { + if r.bits_read == 0 { return nil } if r.i >= i64(len(r.s)) { @@ -90,7 +141,7 @@ align_to_byte :: proc(r: ^Reader) -> Error { } // Align by skipping the remaining bits in the byte - r.bits_read_in_byte = 0 + r.bits_read = 0 r.i += 1 return nil