diff --git a/.gitignore b/.gitignore index 6f1bb6d..36a11e1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,4 @@ +.vscode/ ols.json odinfmt.json -example/ -example/!*.odin -tests/test-files/LPC_example.flac -tests/test-files/fixed_example.flac -tests/test-files/verbatim_example.flac +tests/*.flac diff --git a/TODO.md b/TODO.md index 7d4e130..9fb4d0e 100644 --- a/TODO.md +++ b/TODO.md @@ -2,11 +2,14 @@ Files:- 27 -> This one uses the old format of specifying a variable block strategy. Look into it. The way to know the blocking strategy for this old format FLACs is to see if min_block_size and max_block_size are different, if they are, then the blocking strategy is VARIABLE, if not, then it's FIXED. -44 -> High memory and CPU usage. needs optimization. -uncommon/01 -> This one decodes fine but the expected MD5 is 0x00 * 16 which doesn't match the calculated MD5. not sure what to do about this. -uncommon/02 -> No MD5 -uncommon/03 -> No MD5 +Playback issues in the example program that may relate to the decoder:- +05 - CRC fails for a frame. The gotten CRC is different each time, expected CRC is also different sometimes. +06 - Same as above +23 - Crunchy loud noise mixed with the actual audio data +25 - Same CRC problem +It's worth noting that if we get rid of the CRC checks the files play just fine. + uncommon/04 -> We get a bits per sample mismatch uncommon/10 -> We error with Invalid Signature. I assume for these two I should look for the frame sync bits. But maybe my decoder should reject these. idk. uncommon/11 -> We error with Invalid Signature @@ -14,19 +17,24 @@ uncommon/11 -> We error with Invalid Signature faulty/01 -> This one works when it shouldn't (Wrong max blocksize) faulty/02 -> This one works when it shouldn't (Wrong max framesize) faulty/04 -> This one works when it shouldn't. This one has a wrong number of channels (whatever that means) + Each frame has to be played by itself, otherwise plays sped up. faulty/05 -> This one works when it shouldn't. (Wrong number of samples) + This should be fine to decode and play. faulty/08 -> This one works when it shouldn't. (Blocksize 65536, exceeds the allowed max of 65535) -faulty/09 -> This one works when it shouldn't. (Blocksize 1, below the allowed min of 16 (last frame(subframe?) is allowed to have less than 16)) + We should error here. We segfault currently. +faulty/09 -> This one works when it shouldn't. (Blocksize 1, below the allowed min of 16 (only the last frame is allowed to have less than 16)) + We should error here. We play correctly currently. +faulty/11 -> We return an error now, but we should just warn and decode the audio fine. Things:- [ ] Options - - [ ] Vorbis comment data + - [x] Vorbis comment data - [ ] Cuesheet ? - [ ] Seektable ? - [x] Picture data -[ ] File streaming API? io.Stream?? +[x] File streaming API? io.Stream?? [ ] Lots of checks (look at the TODOs in flac.odin) -[ ] Memory optimizations +[x] Memory optimizations [ ] Speed optimizations [ ] Seeking [ ] Fuzzing to test the stability of the code diff --git a/example/main.odin b/example/main.odin deleted file mode 100644 index c79eafe..0000000 --- a/example/main.odin +++ /dev/null @@ -1,36 +0,0 @@ -package flac_example - -import "core:fmt" -import "core:mem" -import "core:mem/virtual" -import "core:os" -import "core:time" - -import "../" - -main :: proc() { - if len(os.args) < 2 { - fmt.eprintln("Please provide a path to a flac file.") - os.exit(1) - } - - arena: virtual.Arena - arena_err := virtual.arena_init_static(&arena, mem.Megabyte * 8) - if arena_err != nil { - fmt.eprintln("Failed to alloc memory") - return - } - allocator := virtual.arena_allocator(&arena) - - start := time.now() - flac_data, err := flac.load_from_file(os.args[1], allocator = allocator) - if err != nil { - fmt.eprintln(err) - os.exit(1) - } - - end := time.now() - - elapsed := time.diff(start, end) - fmt.println("Decoding took", elapsed) -} diff --git a/examples/buffered/main.odin b/examples/buffered/main.odin new file mode 100644 index 0000000..283ccd4 --- /dev/null +++ b/examples/buffered/main.odin @@ -0,0 +1,189 @@ +package flac_example + +import "core:fmt" +import "core:mem" +import "core:mem/virtual" +import "core:crypto/legacy/md5" +import "core:os" +import "core:time" + +import "shared:flac" +import "shared:alsa" + +DEVICE :: "default" + +main :: proc() { + if len(os.args) < 2 { + fmt.eprintln("Please provide a path to a flac file.") + os.exit(1) + } + + arena: virtual.Arena + handle: ^alsa.pcm_t + + alsa_err := alsa.pcm_open(&handle, DEVICE, .PLAYBACK, .BLOCK) + if alsa_err < 0 { + fmt.eprintln("Failed to initialize audio device: ", alsa.strerror(alsa_err)) + os.exit(1) + } + + arena_err := virtual.arena_init_growing(&arena, mem.Megabyte * 8) + if arena_err != nil { + fmt.eprintln("Failed to alloc memory") + return + } + allocator := virtual.arena_allocator(&arena) + + start := time.now() + flac_data, reader, err := flac.load_from_file_buffered(os.args[1], allocator) + if err != nil { + fmt.println("Error while reading flac file:", err) + os.exit(1) + } + defer flac.destroy(flac_data, reader, allocator) + + fmt.println(flac_data) + + alsa_err = alsa.pcm_set_params(handle, .S16_LE, .RW_INTERLEAVED, u32(flac_data.metadata.channels), flac_data.metadata.sample_rate, 1, 500000) + if alsa_err < 0 { + fmt.eprintln("Playback open error:", alsa.strerror(alsa_err)) + os.exit(1) + } + + sample_rate := flac_data.metadata.sample_rate + channels := flac_data.metadata.channels + + //md5_ctx: md5.Context + //md5.init(&md5_ctx) + for { + frame, err := flac.read_next_frame(reader, flac_data) + + defer delete(frame.samples) + if err != nil { + if err == .EOF { + break + } else { + fmt.println("Error while decoding frame:", err) + os.exit(1) + } + } + + //if frame.sample_rate != sample_rate || frame.channels != channels { + // sample_rate = frame.sample_rate + // channels = frame.channels + // alsa_err = alsa.pcm_set_params(handle, .S16_LE, .RW_INTERLEAVED, u32(channels), sample_rate, 1, 0) + // if alsa_err < 0 { + // fmt.eprintln("Playback open error:", alsa.strerror(alsa_err)) + // os.exit(1) + // } + //} + + //converted_samples := make([]u8, len(samples)) + //defer delete(converted_samples) + //for sample, i in samples { + // converted_samples[i] = u8(sample & 0xFF) + //} + + converted_samples := make([]i16, len(frame.samples)) + defer delete(converted_samples) + for sample, i in frame.samples { + new_sample := sample & 0xFF + new_sample |= ((sample >> 8) & 0xFF) << 8 + converted_samples[i] = i16(new_sample) + } + + frames := alsa.pcm_writei(handle, raw_data(converted_samples), u64(len(frame.samples)) / u64(frame.channels)) + if frames < 0 { + fmt.eprintln("Error writing to PCM device:", alsa.strerror(auto_cast frames)) + break + } + fmt.printfln("Played %d frames", frames) + + //flac.md5hash(&md5_ctx, flac_data.metadata.bits_per_sample, samples) + } + end := time.now() + + //err = flac.md5sum(&md5_ctx, flac_data) + //if err == .MD5_Mismatch { + // fmt.println("Decoded audio data is not correct.") + // os.exit(1) + //} + + alsa_err = alsa.pcm_drain(handle) + if alsa_err < 0 { + fmt.eprintln("pcm_drain failed with:", alsa.strerror(alsa_err)) + } + alsa.pcm_close(handle) + + elapsed := time.diff(start, end) + fmt.println("Decoding took", elapsed) +} + +old_main :: proc() { + if len(os.args) < 2 { + fmt.eprintln("Please provide a path to a flac file.") + os.exit(1) + } + + handle: ^alsa.pcm_t + + arena: virtual.Arena + arena_err := virtual.arena_init_growing(&arena, mem.Megabyte * 8) + if arena_err != nil { + fmt.eprintln("Failed to alloc memory") + return + } + allocator := virtual.arena_allocator(&arena) + + alsa_err := alsa.pcm_open(&handle, DEVICE, .PLAYBACK, .BLOCK) + if alsa_err < 0 { + fmt.eprintln("Failed to initialize audio device: ", alsa.strerror(alsa_err)) + os.exit(1) + } + + start := time.now() + flac_data, err := flac.load_from_file(os.args[1], allocator = allocator) + if err != nil { + fmt.eprintln(err) + if err == .MD5_Mismatch { + fmt.printfln("Expected: %v, got: %v", flac_data.metadata.expected_md5, flac_data.metadata.calculated_md5) + } + os.exit(1) + } + defer flac.destroy(flac_data, allocator = allocator) + end := time.now() + + fmt.println(flac_data.metadata) + + alsa_err = alsa.pcm_set_params(handle, .S16_LE, .RW_INTERLEAVED, u32(flac_data.metadata.channels), flac_data.metadata.sample_rate, 1, 500000) + if alsa_err < 0 { + fmt.eprintln("Playback open error:", alsa.strerror(alsa_err)) + os.exit(1) + } + + converted_samples := make([]i16, len(flac_data.samples)) + + for sample, i in flac_data.samples { + new_sample := sample & 0xFF + new_sample |= ((sample >> 8) & 0xFF) << 8 + converted_samples[i] = i16(new_sample) + } + + for { + frames := alsa.pcm_writei(handle, raw_data(converted_samples), u64(len(flac_data.samples)) / u64(flac_data.metadata.channels)) + if frames < 0 { + fmt.eprintln("Error writing to PCM device:", alsa.strerror(auto_cast frames)) + break + } + fmt.printfln("Played %d frames", frames) + } + + alsa_err = alsa.pcm_drain(handle) + if alsa_err < 0 { + fmt.eprintln("pcm_drain failed with:", alsa.strerror(alsa_err)) + } + alsa.pcm_close(handle) + + elapsed := time.diff(start, end) + fmt.println("Decoding took", elapsed) +} diff --git a/examples/in_memory/main.odin b/examples/in_memory/main.odin new file mode 100644 index 0000000..e7a593b --- /dev/null +++ b/examples/in_memory/main.odin @@ -0,0 +1,81 @@ +package main + +import "core:os" +import "core:fmt" +import "core:mem" +import "core:mem/virtual" +import "core:time" + +import "shared:alsa" +import "shared:flac" + +DEVICE :: "default" + +main :: proc() { + if len(os.args) < 2 { + fmt.eprintln("Please provide a path to a flac file.") + os.exit(1) + } + + handle: ^alsa.pcm_t + + arena: virtual.Arena + arena_err := virtual.arena_init_growing(&arena, mem.Megabyte * 8) + if arena_err != nil { + fmt.eprintln("Failed to alloc memory") + return + } + allocator := virtual.arena_allocator(&arena) + + alsa_err := alsa.pcm_open(&handle, DEVICE, .PLAYBACK, .BLOCK) + if alsa_err < 0 { + fmt.eprintln("Failed to initialize audio device: ", alsa.strerror(alsa_err)) + os.exit(1) + } + + start := time.now() + flac_data, err := flac.load_from_file(os.args[1], allocator = allocator) + if err != nil { + fmt.eprintln(err) + if err == .MD5_Mismatch { + fmt.printfln("Expected: %v, got: %v", flac_data.metadata.expected_md5, flac_data.metadata.calculated_md5) + } + os.exit(1) + } + defer flac.destroy(flac_data, allocator = allocator) + end := time.now() + + fmt.println(flac_data.metadata) + + alsa_err = alsa.pcm_set_params(handle, .S16_LE, .RW_INTERLEAVED, u32(flac_data.metadata.channels), flac_data.metadata.sample_rate, 1, 500000) + if alsa_err < 0 { + fmt.eprintln("Playback open error:", alsa.strerror(alsa_err)) + os.exit(1) + } + + converted_samples := make([]i16, len(flac_data.samples)) + + for sample, i in flac_data.samples { + new_sample := sample & 0xFF + new_sample |= ((sample >> 8) & 0xFF) << 8 + converted_samples[i] = i16(new_sample) + } + + for { + frames := alsa.pcm_writei(handle, raw_data(converted_samples), u64(len(flac_data.samples)) / u64(flac_data.metadata.channels)) + if frames < 0 { + fmt.eprintln("Error writing to PCM device:", alsa.strerror(auto_cast frames)) + break + } + fmt.printfln("Played %d frames", frames) + } + + alsa_err = alsa.pcm_drain(handle) + if alsa_err < 0 { + fmt.eprintln("pcm_drain failed with:", alsa.strerror(alsa_err)) + } + alsa.pcm_close(handle) + + elapsed := time.diff(start, end) + fmt.println("Decoding took", elapsed) +} diff --git a/flac.odin b/flac.odin index 6811936..baf0a87 100644 --- a/flac.odin +++ b/flac.odin @@ -1,6 +1,7 @@ package flac import "core:bytes" +import "core:bufio" import "core:crypto/legacy/md5" import "core:fmt" import "core:io" @@ -20,7 +21,6 @@ FlacError :: enum { Missing_StreamInfo, Missing_Lead_Out_Track, Invalid_Coefficient_Precision, - Metadata_Block_Length_Mismatch, CRC_Mismatch, MD5_Mismatch, Unsupported_BPS, // Remove when implemented @@ -36,17 +36,12 @@ Option: `.skip_md5_check` Skips checking the MD5 hash of the decoded sample data. Offers a slight performance increase but will not error if the data is incorrect. - `.only_return_picture_data` - Returns the embedded picture metadata and encoded pixel data (if available) - `.only_return_metadata` includes this. - `.only_return_metadata` + `.only_return_metadata` Returns all metadata of a FLAC stream without decoding the audio data. */ -// TODO: Implement these Option :: enum { skip_md5_check, - only_return_picture_data, only_return_metadata, } @@ -77,9 +72,8 @@ PictureType :: enum u32 { } Flac :: struct { - // TODO: Other stuff metadata: FlacMetadata, - samples: []i32, + samples: [dynamic]i32, } FlacMetadata :: struct { @@ -90,6 +84,8 @@ FlacMetadata :: struct { expected_md5: [16]byte, calculated_md5: [16]byte, pictures: []Picture, + vendor_name: string, + vorbis_comments: []string, } Picture :: struct { @@ -103,14 +99,17 @@ Picture :: struct { data: []byte, } +Frame :: struct { + channels: u8, + sample_rate: u32, + samples: []i32, +} + +@(private) decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32, err: Error) #no_bounds_check { - // 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([]i32, block_size) - data := read_byte(r) or_return + data := read_bits(r, 8) or_return subframe_type := (data & 0x7E) >> 1 low_3_bits := subframe_type & 7 @@ -118,7 +117,7 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 wasted_bits := 0 if has_wasted_bits { - for (read_bit(r) or_return) != 1 { + for (read_bits(r, 1) or_return) != 1 { wasted_bits += 1 } wasted_bits += 1 @@ -251,7 +250,7 @@ decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32 for s in 0.. (samples: []i32 for s in 0.. (samples: []i32 return subframe_samples, nil } -// TODO: remove -frame_num := 0 - +@(private) decode_frame :: proc( r: ^Reader, flac: ^Flac, + md5_ctx: ^md5.Context, + options: Options, ) -> ( err: Error, ) #no_bounds_check { // // Frame Header // - frame_beginning := r.i - - //fmt.println("STARTING FRAME", frame_num) - //fmt.println("FRAME START AT OFFSET:", r.i) + tee_r: io.Tee_Reader + crc_buffer: bytes.Buffer + bytes.buffer_init_allocator(&crc_buffer, 0, 128) + defer bytes.buffer_destroy(&crc_buffer) + crc := bytes.buffer_to_stream(&crc_buffer) + r := &Reader{ + r = io.tee_reader_init(&tee_r, r, crc), + buf = r.buf, + x = r.x, + n = r.n, + } data := read_data(r, u16be) or_return @@ -451,7 +457,7 @@ decode_frame :: proc( case 2..=5: actual_block_size = 576 * auto_cast (pow2(block_size - 2)) case 6: - actual_block_size = auto_cast (read_byte(r) or_return) + 1 + actual_block_size = auto_cast (read_bits(r, 8) or_return) + 1 case 7: actual_block_size = (read_data(r, u16be) or_return) + 1 case 8..=15: @@ -491,7 +497,7 @@ decode_frame :: proc( case ._96kHz: actual_sample_rate_in_Hz = 96000 case .USE_8_BITS_IN_kHz_FROM_HEADER_END: - actual_sample_rate_in_Hz = auto_cast (read_byte(r) or_return) * 1000 + actual_sample_rate_in_Hz = auto_cast (read_bits(r, 8) or_return) * 1000 case .USE_16_BITS_IN_Hz_FROM_HEADER_END: actual_sample_rate_in_Hz = auto_cast (read_data(r, u16be) or_return) case .USE_16_BITS_IN_TENS_OF_Hz_FROM_HEADER_END: @@ -504,16 +510,9 @@ decode_frame :: proc( // TODO: sample rate MUST NOT be 0 if the subframe contains audio. // a sample rate of 0 MAY be used when non-audio is represented. - frame_header_end := r.i - - frame_header_crc := read_byte(r) or_return - calculated_header_crc := calculate_crc8(r.s[frame_beginning:frame_header_end]) - - //fmt.printfln("block size bits: 0b%b", block_size) - //fmt.printfln("actual block size: %d sample(s)", actual_block_size) - //fmt.println("sample rate bits:", sample_rate) - //fmt.printfln("actual sample rate: %dHz", actual_sample_rate_in_Hz) - //fmt.println("channel assignment:", channel_assignment) + crc8_bytes := bytes.buffer_to_bytes(&crc_buffer) + frame_header_crc := u8(read_bits(r, 8) or_return) + calculated_header_crc := calculate_crc8(crc8_bytes) if frame_header_crc != calculated_header_crc { fmt.printfln("Expected frame header CRC: 0x%X, got: 0x%X", frame_header_crc, calculated_header_crc) @@ -576,7 +575,6 @@ decode_frame :: proc( // so many questions... } - // TODO: remember to clean this up subframes := make([][]i32, frame_channels) defer delete(subframes) for i in 0..> 8) & 0xFF) + md5.update(md5_ctx, {first_byte, second_byte}) + case 17..=24: + first_byte := u8(sample & 0xFF) + second_byte := u8((sample >> 8) & 0xFF) + third_byte := u8((sample >> 16) & 0xFF) + md5.update(md5_ctx, {first_byte, second_byte, third_byte}) + case 25..=32: + data := transmute([4]u8)sample + md5.update(md5_ctx, data[:]) + case: + fmt.println("INVALID BPS") + os.exit(1) + } + } + } + } + + for subframe in subframes { + delete(subframe) + } + + // + // Frame Footer + // + crc16_bytes := bytes.buffer_to_bytes(&crc_buffer) + frame_crc := u16(read_data(r, u16be) or_return) + calculated_frame_crc := calculate_crc16(crc16_bytes) + + if frame_crc != calculated_frame_crc { + fmt.eprintfln("Expected frame CRC 0x%X, got 0x%X", frame_crc, calculated_frame_crc) + return .CRC_Mismatch + } + + return nil +} + +@(private) +correlate :: proc(subframes: [][]i32, channel_assignment: ChannelAssignment) { // Undo Stereo Decorrelation #partial switch channel_assignment { case .STEREO_MID_SIDE: @@ -625,67 +676,11 @@ decode_frame :: proc( subframes[1][i] = left - side } } - - // TODO: Maybe this is slow if we have a lot of samples?? - // The cases are ranges because streaminfo can define unusual bits per sample - for i in 0..> 8) & 0xFF) - md5.update(&md5_ctx, {first_byte, second_byte}) - case 17..=24: - first_byte := u8(sample & 0xFF) - second_byte := u8((sample >> 8) & 0xFF) - third_byte := u8((sample >> 16) & 0xFF) - md5.update(&md5_ctx, {first_byte, second_byte, third_byte}) - case 25..=32: - data := transmute([4]u8)sample - md5.update(&md5_ctx, data[:]) - case: - fmt.println("INVALID BPS") - os.exit(1) - } - } - } - - // TODO: We need a way to copy (or better yet just point to) the decoded sample data - // We'll probably either copy each subframe's samples somehow, or have a dynamic array and append to that. - // Both are slow-ish but I can't think of a faster way that just works. - // This doesn't work btw, just for my future self to not think this is fine. - for subframe in subframes { - copy(flac.samples, subframe) - } - - // - // Frame Footer - // - frame_end := r.i - frame_crc := read_data(r, u16be) or_return - calculated_frame_crc := calculate_crc16(r.s[frame_beginning:frame_end]) - - if auto_cast frame_crc != calculated_frame_crc { - fmt.eprintfln("Expected frame CRC 0x%X, got 0x%X", frame_crc, calculated_frame_crc) - return .CRC_Mismatch - } - - frame_num += 1 - - return nil } -load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (flac: ^Flac, err: Error) { - r: Reader - bytes.reader_init(&r, data) - - md5.init(&md5_ctx) - defer md5.reset(&md5_ctx) - - header := read_data(&r, FlacHeader) or_return +@(private) +read_metadata :: proc(r: ^Reader, buffered := false, allocator := context.allocator) -> (flac: ^Flac, err: Error) { + header := read_data(r, FlacHeader) or_return if header.magic != FLAC_MAGIC { return nil, .Invalid_Signature } @@ -718,7 +713,7 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context pictures := make([dynamic]Picture, 0, 2, allocator) last_block := streaminfo_header.last_block for !last_block { - md_block_hdr := read_data(&r, u32be) or_return + md_block_hdr := read_data(r, u32be) or_return metadata_block_header := MetadataBlockHeader { last_block = bool(md_block_hdr >> 31), type = BlockType((md_block_hdr >> 24) & 0x7F), @@ -731,70 +726,95 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context case .STREAMINFO: // Do nothing, we already parse the mandatory streaminfo metadata case .PADDING: - //fmt.printfln("Skipping %d bytes of padding data", metadata_block_header.length) - bytes.reader_seek(&r, auto_cast metadata_block_header.length, .Current) + if buffered { + reader := cast(^bufio.Reader)r.data + skipped := bufio.reader_discard(reader, int(metadata_block_header.length)) or_return + } else { + reader := cast(^bytes.Reader)r.data + skipped := bytes.reader_seek(reader, i64(metadata_block_header.length), .Current) or_return + } case .APPLICATION: //app_id := read_slice(&r, 4) or_return //fmt.println("Found application with ID:", string(app_id)) //fmt.printfln("Skipping %d bytes of application metadata block", metadata_block_header.length) - bytes.reader_seek(&r, auto_cast metadata_block_header.length, .Current) // TODO: handle the application data + if buffered { + reader := cast(^bufio.Reader)r.data + skipped := bufio.reader_discard(reader, int(metadata_block_header.length)) or_return + } else { + reader := cast(^bytes.Reader)r.data + skipped := bytes.reader_seek(reader, i64(metadata_block_header.length), .Current) or_return + } case .SEEKTABLE: - // TODO: check for size mismatch? seekpoints := metadata_block_header.length / 18 //fmt.printfln("we have %d seekpoints", seekpoints) + actual_size := 0 for i in 0.. (frame: Frame, err: Error) { + // + // Frame Header + // + tee_r: io.Tee_Reader + crc_buffer: bytes.Buffer + bytes.buffer_init_allocator(&crc_buffer, 0, 128) + defer bytes.buffer_destroy(&crc_buffer) + crc := bytes.buffer_to_stream(&crc_buffer) + r := &Reader{ + r = io.tee_reader_init(&tee_r, r, crc), + buf = r.buf, + x = r.x, + n = r.n, } - flac.metadata.pictures = pictures[:] + data := read_data(r, u16be) or_return + + sync_code := data >> 2 + blocking_strategy := BlockingStrategy(data & 1) + + data = read_data(r, u16be) or_return + block_size := data >> 12 + sample_rate := SampleRate((data >> 8) & 0xF) + channel_assignment := ChannelAssignment((data >> 4) & 0xF) + sample_size := SampleSize((data >> 1) & 7) + + coded_num := decode_extended_utf8(r) or_return + if blocking_strategy == .VARIABLE { + // Sample number of first sample + // MUST NOT be larger than 36 bits unencoded or 7 bytes encoded. + // MUST be equal to the number of samples preceding the current frame. Otherwise seeking is not possible. + // TODO: also write a check for the number of samples so we'll probably keep track of how many samples we decoded + // maybe the number of decoded samples can be inferred by the number of frames ?? or just use the length of + // the decoded_samples slice. + if coded_num > 0x1000000000 { + // TODO: Sample number can't be bigger than 36 bits + } + } else { + // Frame number + // MUST NOT be larger than 31 bits unencoded or 6 bytes encoded. + // MUST be equal to the number of frames preceding the current frame. Otherwise seeking is not possible. + // TODO: also write a check for the number of frames so we'll probably keep track of how many frames we decoded. + if coded_num > 0x80000000 { + // TODO: Frame number cannot be bigger than 31 bits + } + } + + actual_block_size: u16be + switch block_size { + case 1: + actual_block_size = 192 + case 2..=5: + actual_block_size = 576 * auto_cast (pow2(block_size - 2)) + case 6: + actual_block_size = auto_cast (read_bits(r, 8) or_return) + 1 + case 7: + actual_block_size = (read_data(r, u16be) or_return) + 1 + case 8..=15: + actual_block_size = 256 * auto_cast (pow2(block_size - 8)) + case: + return {}, .Invalid_Block_Size + } + + //if actual_block_size < 15 && !last_frame { + // TODO: block sizes less than 16 are only valid for the last frame and MUST NOT be used for any other frame. + //} + + actual_sample_rate_in_Hz: u32 + switch sample_rate { + case .USE_STREAMINFO: + actual_sample_rate_in_Hz = flac.metadata.sample_rate + case ._88_2kHz: + actual_sample_rate_in_Hz = 88200 + case ._176_4kHz: + actual_sample_rate_in_Hz = 176400 + case ._192kHz: + actual_sample_rate_in_Hz = 192000 + case ._8kHz: + actual_sample_rate_in_Hz = 8000 + case ._16kHz: + actual_sample_rate_in_Hz = 16000 + case ._22_05kHz: + actual_sample_rate_in_Hz = 22005 + case ._24kHz: + actual_sample_rate_in_Hz = 24000 + case ._32kHz: + actual_sample_rate_in_Hz = 32000 + case ._44_1kHz: + actual_sample_rate_in_Hz = 44100 + case ._48kHz: + actual_sample_rate_in_Hz = 48000 + case ._96kHz: + actual_sample_rate_in_Hz = 96000 + case .USE_8_BITS_IN_kHz_FROM_HEADER_END: + actual_sample_rate_in_Hz = auto_cast (read_bits(r, 8) or_return) * 1000 + case .USE_16_BITS_IN_Hz_FROM_HEADER_END: + actual_sample_rate_in_Hz = auto_cast (read_data(r, u16be) or_return) + case .USE_16_BITS_IN_TENS_OF_Hz_FROM_HEADER_END: + actual_sample_rate_in_Hz = auto_cast (read_data(r, u16be) or_return) * 10 + case .INVALID: fallthrough + case: + return {}, .Invalid_Sample_Rate + } + + // TODO: sample rate MUST NOT be 0 if the subframe contains audio. + // a sample rate of 0 MAY be used when non-audio is represented. + + crc8_bytes := bytes.buffer_to_bytes(&crc_buffer) + frame_header_crc := u8(read_bits(r, 8) or_return) + calculated_header_crc := calculate_crc8(crc8_bytes) + + if frame_header_crc != calculated_header_crc { + fmt.printfln("Expected frame header CRC: 0x%X, got: 0x%X", frame_header_crc, calculated_header_crc) + return {}, .CRC_Mismatch + } + + // + // Subframes + // + bps: u8 + switch sample_size { + case .USE_STREAMINFO: + bps = flac.metadata.bits_per_sample + case ._8BPS: + bps = 8 + case ._12BPS: + bps = 12 + case ._16BPS: + bps = 16 + case ._20BPS: + bps = 20 + case ._24BPS: + bps = 24 + case ._32BPS: + bps = 32 + } + + if bps != flac.metadata.bits_per_sample { + return {}, .Bits_Per_Second_Mismatch + } + + frame_channels := flac.metadata.channels + switch channel_assignment { + case .MONO: + frame_channels = 1 + case ._3CHANNELS: + frame_channels = 3 + case ._4CHANNELS: + frame_channels = 4 + case ._5CHANNELS: + frame_channels = 5 + case ._6CHANNELS: + frame_channels = 6 + case ._7CHANNELS: + frame_channels = 7 + case ._8CHANNELS: + frame_channels = 8 + case ._2CHANNELS: fallthrough + case .STEREO_LEFT_SIDE: fallthrough + case .STEREO_SIDE_RIGHT: fallthrough + case .STEREO_MID_SIDE: + frame_channels = 2 + } + + if frame_channels != flac.metadata.channels { + // TODO: warning or error? + // we have a faulty file that says it has 5 channels but subframes all say they got 1 + // we also have an two uncommon files with increasing and decreasing number of channels + // uncommon are supposed to be unusual but still valid FLAC files. why is that first one faulty then??? + // so many questions... + } + + subframes := make([][]i32, frame_channels) + defer delete(subframes) + for i in 0..> 8) & 0xFF) + md5.update(md5_ctx, {first_byte, second_byte}) + case 17..=24: + first_byte := u8(sample & 0xFF) + second_byte := u8((sample >> 8) & 0xFF) + third_byte := u8((sample >> 16) & 0xFF) + md5.update(md5_ctx, {first_byte, second_byte, third_byte}) + case 25..=32: + data := transmute([4]u8)sample + md5.update(md5_ctx, data[:]) + case: + fmt.println("INVALID BPS") + os.exit(1) + } + } +} + +md5sum :: proc(md5_ctx: ^md5.Context, flac: ^Flac) -> Error { + calculated_md5: [16]byte + md5.final(md5_ctx, calculated_md5[:]) + + flac.metadata.calculated_md5 = calculated_md5 + + if mem.compare(calculated_md5[:], flac.metadata.expected_md5[:]) != 0 { + return .MD5_Mismatch + } + + return nil +} + +// `load_from_bytes` takes a byte slice and initializes a bitreader for it. +// The data is expected to be a full flac stream. +load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (flac: ^Flac, err: Error) { + context.allocator = allocator + + byte_r: bytes.Reader + bytes.reader_init(&byte_r, data) + + r := Reader{bytes.reader_to_stream(&byte_r), 0, 0, 0} + + flac = read_metadata(&r) or_return + if .only_return_metadata in options { + return flac, nil + } + + md5_ctx: md5.Context + md5.init(&md5_ctx) + defer md5.reset(&md5_ctx) + + for { + err = decode_frame(&r, flac, &md5_ctx, options) + if err != nil { + if err == .EOF { + break + } else { + return flac, err + } + } + } if err != .EOF { return flac, err } - // - // Validate the decoded audio data's md5 - // - calculated_md5: [16]byte - md5.final(&md5_ctx, calculated_md5[:]) - - flac.metadata.calculated_md5 = calculated_md5 - - if mem.compare(calculated_md5[:], header.streaminfo.md5[:]) != 0 { - return flac, .MD5_Mismatch + if .skip_md5_check not_in options { + md5sum(&md5_ctx, flac) or_return } return flac, nil } +// `load_from_file` reads the entire file to memory and operates on the returned byte slice. +// Only use this with small files. load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (flac: ^Flac, err: Error) { context.allocator = allocator @@ -849,22 +1163,71 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont defer delete(data) if ok { - return load_from_bytes(data) + return load_from_bytes(data, options) } else { return nil, .Unable_To_Read_File } } -destroy :: proc(flac: ^Flac) { - // TODO: clean up the allocations - delete(flac.samples) - // TODO: I think this data gets deleted when the data from os.read_entire_file is deleted. - // Double check that and if it's true then make sure to copy this data. - // Copying small strings is fine but copying the image pixel data might be slow. - //for picture in flac.metadata.pictures { - // picture.mimetype - // picture.data - // picture.description - //} - delete(flac.metadata.pictures) + +// `load_from_file_buffered` initializes a buffered reader for the file and reads all the metadata blocks. +// Calls to `read_next_frame()` MUST be made to decode the audio data. +// This is the recommended way to read most FLAC files as it has a minimal memory overhead. +load_from_file_buffered :: proc(filename: string, allocator := context.allocator) -> (flac: ^Flac, r: ^Reader, err: Error) { + context.allocator = allocator + + file, open_err := os.open(filename) + if open_err != os.ERROR_NONE { + fmt.eprintln(open_err) + return nil, {}, .Unable_To_Read_File + } + + br := new(bufio.Reader) + bufio.reader_init(br, os.stream_from_handle(file)) + + r = new(Reader) + r.r = bufio.reader_to_stream(br) + r.buf = 0 + r.x = 0 + r.n = 0 + + flac = read_metadata(r, true) or_return + + return flac, r, nil +} + +/* `destroy` cleans up and frees the memory allocated by all 3 loading functions. + The returned reader from `load_from_file_buffered` MUST be passed to `destroy` in order to close the file handle. + `load_from_file` and `load_from_bytes` may pass nil as the reader. + --- + The allocator that was used when loading the flac file MUST be passed to `destroy` in order to properly free + the memory, otherwise a bad free may occur and crash the program. +*/ +destroy :: proc(flac: ^Flac, r: ^Reader = nil, allocator := context.allocator) { + context.allocator = allocator + + delete(flac.samples) + for picture in flac.metadata.pictures { + delete(picture.mimetype) + delete(picture.data) + delete(picture.description) + } + delete(flac.metadata.pictures) + + for comment in flac.metadata.vorbis_comments { + delete(comment) + } + delete(flac.metadata.vorbis_comments) + delete(flac.metadata.vendor_name) + + free(flac) + + if r != nil { + br := cast(^bufio.Reader)r.data + closer, ok := io.to_closer(br.rd) + io.close(closer) + bufio.reader_destroy(br) + free(br) + free(r) + } } diff --git a/internal.odin b/internal.odin index 470478c..dce93ed 100644 --- a/internal.odin +++ b/internal.odin @@ -79,11 +79,6 @@ crc16_lookup := [256]u16 { fixed_coefficients := [4][]i32{{1}, {2, -1}, {3, -3, 1}, {4, -6, 4, -1}} -// TODO: no idea if I want to keep this global and a thread_local -// or if I should just init it in load_from_bytes -@(thread_local) -md5_ctx: md5.Context - BlockType :: enum u16be { STREAMINFO, PADDING, @@ -190,13 +185,13 @@ StreamInfoBlock :: struct #packed { max_frame_size: u32be | 24, }, /* - bit_field u64be { + bit_field u64be { sample_rate: u32be | 20, num_channel_minus_one: u8 | 3, bits_per_sample_minus_one: u8 | 5, total_samples: u64be | 36, } - */ + */ sr_chan_bps_ts: u64be, md5: [16]byte, } @@ -253,7 +248,7 @@ decode_extended_utf8 :: proc(r: ^Reader) -> (decoded_num: u64, err: Error) { decoded_num = u64(0) - first_byte := read_byte(r) or_return + first_byte := read_bits(r, 8) or_return utf8_sequence_len := 0 for i: uint = 7; i > 0; i -= 1 { @@ -269,7 +264,7 @@ decode_extended_utf8 :: proc(r: ^Reader) -> (decoded_num: u64, err: Error) { octets := make([dynamic]byte, 0, 8) defer delete(octets) for i := utf8_sequence_len - 1; i > 0; i -= 1 { - append(&octets, read_byte(r) or_return) + append(&octets, byte(read_bits(r, 8) or_return)) } switch utf8_sequence_len { diff --git a/reader.odin b/reader.odin index 89e0c1d..074f62c 100644 --- a/reader.odin +++ b/reader.odin @@ -8,119 +8,85 @@ import "core:io" 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: u8, + using r: io.Stream, + // Temporary read buffer. + buf: [8]u8, + // Between 0 and 7 buffered bits since previous read operations. + x: u8, + // The number of buffered bits in x. + n: uint, } -read_slice :: #force_inline proc(r: ^Reader, size: int) -> (res: []u8, err: io.Error) { - #no_bounds_check { - if r.i + i64(size) <= i64(len(r.s)) { - res = r.s[r.i:r.i + i64(size)] - r.i += i64(size) - return res, .None - } - } +read_slice :: #force_inline proc(r: ^Reader, buf: []u8) -> io.Error { + io.read_full(r^, buf) or_return - if i64(len(r.s)) == r.i { - return []u8{}, .EOF - } else { - return []u8{}, .Short_Buffer - } + return nil } read_data :: #force_inline proc(r: ^Reader, $T: typeid) -> (res: T, err: io.Error) { - b := read_slice(r, size_of(T)) or_return + b: [size_of(T)]byte + read_slice(r, b[:]) or_return + return (^T)(&b[0])^, nil } -read_bit :: proc(r: ^Reader) -> (bit: byte, err: Error) #no_bounds_check { - if r.i >= i64(len(r.s)) { - return 0, .EOF +// Read reads and returns the next n bits, at most 64. It buffers bits up to the +// next byte boundary. +// Borrowed from https://github.com/mewkiz/flac +read_bits :: proc(r: ^Reader, n: uint) -> (res: u64, err: io.Error) #no_bounds_check { + if n == 0 { + return 0, nil } - bits := 8 - (r.bits_read + 1) - b := (r.s[r.i] >> bits) & 1 - r.bits_read += 1 + n := n - if r.bits_read == 8 { - r.i += 1 - r.bits_read = 0 - } - - return b, nil -} - -read_bits :: proc(r: ^Reader, n: uint) -> (bits: u64, err: Error) #no_bounds_check { - res: u64 - - // Fast path: if we can read all bits within the current byte. - if r.bits_read + u8(n) <= 8 { - res = (u64(r.s[r.i]) >> (8 - r.bits_read - u8(n))) & ((1 << n) - 1) - r.bits_read += u8(n) - if r.bits_read == 8 { - r.bits_read = 0 - r.i += 1 + // Read buffered bits. + if r.n > 0 { + switch { + case r.n == n: + r.n = 0 + return u64(r.x), nil + case r.n > n: + r.n -= n + mask := ~u8(0) << r.n + res = u64(r.x&mask) >> r.n + r.x &~= mask + return res, nil } - return res, nil + n -= r.n + res = u64(r.x) + r.n = 0 } - // General case: reading across multiple bytes. - rem_bits := n - if r.bits_read > 0 { - // Read the remaining bits in the current byte. - bits_in_byte := 8 - r.bits_read - res = u64(r.s[r.i]) & ((1 << bits_in_byte) - 1) - rem_bits -= uint(bits_in_byte) - r.bits_read = 0 - r.i += 1 + // Fill the temporary buffer. + bytes := n / 8 + bits := n % 8 + if bits > 0 { + bytes += 1 } + io.read_full(r, r.buf[:bytes]) or_return - // Read full bytes. - for rem_bits >= 8 { - res = (res << 8) | u64(r.s[r.i]) - rem_bits -= 8 - r.i += 1 + // Read bits from the temporary buffer. + for b in r.buf[:bytes-1] { + res <<= 8 + res |= u64(b) } - - // Read remaining bits in the final byte. - if rem_bits > 0 { - res = (res << rem_bits) | (u64(r.s[r.i]) >> (8 - rem_bits)) - r.bits_read += u8(rem_bits) + b := r.buf[bytes-1] + if bits > 0 { + res <<= bits + r.n = 8 - bits + mask := ~u8(0) << r.n + res |= u64(b&mask) >> r.n + r.x = b & ~mask + } else { + res <<= 8 + res |= u64(b) } return res, nil } -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. - 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 - } - return b, nil -} - -align_to_byte :: proc(r: ^Reader) -> Error { - if r.bits_read == 0 { - return nil - } - if r.i >= i64(len(r.s)) { - return .EOF - } - - // Align by skipping the remaining bits in the byte - r.bits_read = 0 - r.i += 1 - - return nil +align_to_byte :: proc(r: ^Reader) { + r.x = 0 + r.n = 0 } diff --git a/tests/flac_buffered_test.odin b/tests/flac_buffered_test.odin new file mode 100644 index 0000000..fbeb1c1 --- /dev/null +++ b/tests/flac_buffered_test.odin @@ -0,0 +1,1117 @@ +package flac_test + +import "core:fmt" +import "core:os" +import "core:testing" +import "shared:flac" + +@(test) +test_blocksize_4096_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/01 - blocksize 4096.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Read Blocksize 4096 metadata Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 4096 frame Failed with error:", f_err)) + } +} + +@(test) +test_blocksize_4608_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/02 - blocksize 4608.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Blocksize 4608 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 4608 frame Failed with error:", f_err)) + } +} + +@(test) +test_blocksize_16_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/03 - blocksize 16.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Blocksize 16 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 16 frame Failed with error:", f_err)) + } +} + +@(test) +test_blocksize_192_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/04 - blocksize 192.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Blocksize 192 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 192 frame Failed with error:", f_err)) + } +} + +@(test) +test_blocksize_254_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/05 - blocksize 254.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Blocksize 254 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 254 frame Failed with error:", f_err)) + } +} + +@(test) +test_blocksize_512_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/06 - blocksize 512.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Blocksize 512 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 512 frame Failed with error:", f_err)) + } +} + +@(test) +test_blocksize_725_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/07 - blocksize 725.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Blocksize 725 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 725 frame Failed with error:", f_err)) + } +} + +@(test) +test_blocksize_1000_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/08 - blocksize 1000.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Blocksize 1000 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 1000 frame Failed with error:", f_err)) + } +} + +@(test) +test_blocksize_1937_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/09 - blocksize 1937.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Blocksize 1937 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 1937 frame Failed with error:", f_err)) + } +} + +@(test) +test_blocksize_2304_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/10 - blocksize 2304.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Blocksize 2304 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Blocksize 2304 frame Failed with error:", f_err)) + } +} + +@(test) +test_partition_order_8_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/11 - partition order 8.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Partition order 8 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Partition order 8 frame Failed with error:", f_err)) + } +} + +@(test) +test_qlp_precision_15_bit_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/12 - qlp precision 15 bit.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("qlp precision 15 bit Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding qlp precision 15 bit frame Failed with error:", f_err)) + } +} + +@(test) +test_qlp_precision_2_bit_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/13 - qlp precision 2 bit.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("qlp precision 2 bit Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding qlp precision 2 bit frame Failed with error:", f_err)) + } +} + +@(test) +wasted_bits_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/14 - wasted bits.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Wasted bits Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Wasted bits frame Failed with error:", f_err)) + } +} + +@(test) +only_verbatim_subframes_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/15 - only verbatim subframes.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Only verbatim subframes Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Only verbatim subframes frame Failed with error:", f_err)) + } +} + +@(test) +partition_order_8_containing_escaped_partitions_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/16 - partition order 8 containing escaped partitions.flac") + defer flac.destroy(data, r) + testing.expect( + t, + err == nil, + fmt.tprint("Partition order 8 containing escaped partitions Failed with error:", err), + ) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Partition order 8 containing escaped partitions frame Failed with error:", f_err)) + } +} + +@(test) +all_fixed_orders_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/17 - all fixed orders.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("All fixed orders Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding All fixed orders frame Failed with error:", f_err)) + } +} + +@(test) +precision_search_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/18 - precision search.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Precision search Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Precision search frame Failed with error:", f_err)) + } +} + +@(test) +samplerate_35467Hz_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/19 - samplerate 35467Hz.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Samplerate 35467Hz Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Samplerate 35467Hz frame Failed with error:", f_err)) + } +} + +@(test) +samplerate_39kHz_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/20 - samplerate 39kHz.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Samplerate 39kHz Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Samplerate 39kHz frame Failed with error:", f_err)) + } +} + +@(test) +samplerate_22050Hz_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/21 - samplerate 22050Hz.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Samplerate 22050Hz Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Samplerate 22050Hz frame Failed with error:", f_err)) + } +} + +@(test) +_12_bit_per_sample_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/22 - 12 bit per sample.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("12 bit per sample Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 12 bit per sample frame Failed with error:", f_err)) + } +} + +@(test) +_8_bit_per_sample_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/23 - 8 bit per sample.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("8 bit per sample Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 8 bit per sample frame Failed with error:", f_err)) + } +} + +@(test) +variable_blocksize_file_created_with_flake_revision_264_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered( + "flac-test-files/subset/24 - variable blocksize file created with flake revision 264.flac", + ) + defer flac.destroy(data, r) + testing.expect( + t, + err == nil, + fmt.tprint("Variable blocksize file created with flake revision 264 Failed with error:", err), + ) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Variable blocksize file created with flake revision 264 frame Failed with error:", f_err)) + } +} + +@(test) +variable_blocksize_file_created_with_flake_revision_264_modified_to_create_smaller_blocks_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered( + "flac-test-files/subset/25 - variable blocksize file created with flake revision 264, modified to create smaller blocks.flac", + ) + defer flac.destroy(data, r) + testing.expect( + t, + err == nil, + fmt.tprint( + "Variable blocksize file created with flake revision 264 modified to create smaller blocks Failed with error:", + err, + ), + ) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Variable blocksize file created with flake revision 264 modified to create smaller blocks frame Failed with error:", f_err)) + } +} + +@(test) +variable_blocksize_file_created_with_CUETools_Flake_2_1_6_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered( + "flac-test-files/subset/26 - variable blocksize file created with CUETools.Flake 2.1.6.flac", + ) + defer flac.destroy(data, r) + testing.expect( + t, + err == nil, + fmt.tprint("Variable blocksize file created with CUETools.Flake 2.1.6 Failed with error:", err), + ) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Variable blocksize file created with CUETools Flake 2.1.6 frame Failed with error:", f_err)) + } +} + +@(test) +old_format_variable_blocksize_file_created_with_Flake_0_11_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered( + "flac-test-files/subset/27 - old format variable blocksize file created with Flake 0.11.flac", + ) + defer flac.destroy(data, r) + testing.expect( + t, + err == nil, + fmt.tprint("Old format variable blocksize file created with Flake 0.11 Failed with error:", err), + ) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Old format variable blocksize file created with CUETools Flake 0.11 frame Failed with error:", f_err)) + } +} + +@(test) +high_resolution_audio_default_settings_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered ("flac-test-files/subset/28 - high resolution audio, default settings.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("High resolution audio, default settings Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding High resolution audio, default settings frame Failed with error:", f_err)) + } +} + +@(test) +high_resolution_audio_blocksize_16384_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/29 - high resolution audio, blocksize 16384.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("High resolution audio, blocksize 16384 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding High resolution audio, blocksize 16384 frame Failed with error:", f_err)) + } +} + +@(test) +high_resolution_audio_blocksize_13456_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/30 - high resolution audio, blocksize 13456.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("High resolution audio, blocksize 13456 Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding High resolution audio, blocksize 13456 frame Failed with error:", f_err)) + } +} + +@(test) +high_resolution_audio_using_only_32nd_order_predictors_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered( + "flac-test-files/subset/31 - high resolution audio, using only 32nd order predictors.flac", + ) + defer flac.destroy(data, r) + testing.expect( + t, + err == nil, + fmt.tprint("High resolution audio, using only 32nd order predictors Failed with error:", err), + ) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding High resolution audio, using only 32nd predictors frame Failed with error:", f_err)) + } +} + +@(test) +high_resolution_audio_partition_order_8_containing_escaped_partitions_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered( + "flac-test-files/subset/32 - high resolution audio, partition order 8 containing escaped partitions.flac", + ) + defer flac.destroy(data, r) + testing.expect( + t, + err == nil, + fmt.tprint("High resolution audio, partition order 8 containing escaped partitions Failed with error:", err), + ) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding High resolution audio, partition order 8 containing escaped partitions frame Failed with error:", f_err)) + } +} + +@(test) +samplerate_192kHz_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/33 - samplerate 192kHz.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Samplerate 192kHz Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Samplerate 192kHz frame Failed with error:", f_err)) + } +} + +@(test) +samplerate_192kHz_using_only_32nd_order_predictors_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/34 - samplerate 192kHz, using only 32nd order predictors.flac") + defer flac.destroy(data, r) + testing.expect( + t, + err == nil, + fmt.tprint("Samplerate 192kHz, using only 32nd order predictors Failed with error:", err), + ) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Samplerate 192kHz, using only 32nd predictors frame Failed with error:", f_err)) + } +} + +@(test) +samplerate_134560Hz_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/35 - samplerate 134560Hz.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Samplerate 134560Hz Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Samplerate 134560Hz frame Failed with error:", f_err)) + } +} + +@(test) +samplerate_384kHz_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/36 - samplerate 384kHz.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Samplerate 384kHz Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Samplerate 384kHz frame Failed with error:", f_err)) + } +} + +@(test) +_20_bit_per_sample_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/37 - 20 bit per sample.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("20 bit per sample Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 20 bit per sample frame Failed with error:", f_err)) + } +} + +@(test) +_3_channels_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/38 - 3 channels (3.0).flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("3 channels Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 3 channels frame Failed with error:", f_err)) + } +} + +@(test) +_4_channels_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/39 - 4 channels (4.0).flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("4 channels Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 4 channels frame Failed with error:", f_err)) + } +} + +@(test) +_5_channels_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/40 - 5 channels (5.0).flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("5 channels Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 5 channels frame Failed with error:", f_err)) + } +} + +@(test) +_6_channels_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/41 - 6 channels (5.1).flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("6 channels Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 6 channels frame Failed with error:", f_err)) + } +} + +@(test) +_7_channels_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/42 - 7 channels (6.1).flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("7 channels Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 7 channels frame Failed with error:", f_err)) + } +} + +@(test) +_8_channels_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/43 - 8 channels (7.1).flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("8 channels Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 8 channels frame Failed with error:", f_err)) + } +} + +@(test) +_8_channel_surround_192kHz_24_bit_using_only_32nd_order_predictors_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered( + "flac-test-files/subset/44 - 8-channel surround, 192kHz, 24 bit, using only 32nd order predictors.flac", + ) + defer flac.destroy(data, r) + testing.expect( + t, + err == nil, + fmt.tprint("8-channel surround 192kHz 24 bit using only 32nd order predictors Failed with error:", err), + ) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 8 channel surround, 192kHz, 24 bit, using only 32nd order predictors frame Failed with error:", f_err)) + } +} + +@(test) +no_total_number_of_samples_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/45 - no total number of samples set.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("No total number of samples Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding No total number of samples frame Failed with error:", f_err)) + } +} + +@(test) +no_min_max_framesize_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/46 - no min-max framesize set.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("No min-max framesize Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding No min max framesize frame Failed with error:", f_err)) + } +} + +@(test) +only_streaminfo_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/47 - only STREAMINFO.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Only STREAMINFO Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Only streaminfo frame Failed with error:", f_err)) + } +} + +@(test) +extreme_large_seektable_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/48 - Extremely large SEEKTABLE.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Extremely large SEEKTABLE Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Extremely large seektable frame Failed with error:", f_err)) + } +} + +@(test) +extreme_large_padding_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/49 - Extremely large PADDING.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Extremely large PADDING Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Extremely large padding frame Failed with error:", f_err)) + } +} + +@(test) +extreme_large_picture_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/50 - Extremely large PICTURE.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Extremely large PICTURE Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Extremely large picture frame Failed with error:", f_err)) + } +} + +@(test) +extreme_large_vorbiscomment_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/51 - Extremely large VORBISCOMMENT.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Extremely large VORBISCOMMENT Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Extremely large vorbis comment frame Failed with error:", f_err)) + } +} + +@(test) +extreme_large_application_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/52 - Extremely large APPLICATION.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Extremely large APPLICATION Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Extremely large application frame Failed with error:", f_err)) + } +} + +@(test) +cuesheet_with_very_many_indexes_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/53 - CUESHEET with very many indexes.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("CUESHEET with very many indexes Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Cuesheet with very many indexes frame Failed with error:", f_err)) + } +} + +@(test) +_1000x_repeating_vorbiscomment_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/54 - 1000x repeating VORBISCOMMENT.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("1000x repeating VORBSICOMMENT Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding 1000x repeating vorbis comment frame Failed with error:", f_err)) + } +} + +@(test) +file_48_53_combined_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/55 - file 48-53 combined.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("File 48-53 combined Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding file 48-53 combined frame Failed with error:", f_err)) + } +} + +@(test) +jpg_picture_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/56 - JPG PICTURE.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("JPG PICTURE Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding JPG picture frame Failed with error:", f_err)) + } +} + +@(test) +png_picture_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/57 - PNG PICTURE.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("PNG PICTURE Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding PNG picture frame Failed with error:", f_err)) + } +} + +@(test) +gif_picture_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/58 - GIF PICTURE.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("GIF PICTURE Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding GIF picture frame Failed with error:", f_err)) + } +} + +@(test) +avif_picture_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/59 - AVIF PICTURE.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("AVIF PICTURE Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding AVIF picture frame Failed with error:", f_err)) + } +} + +@(test) +mono_audio_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/60 - mono audio.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Mono audio Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Mono audio frame Failed with error:", f_err)) + } +} + +@(test) +predictor_overflow_check_16_bit_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/61 - predictor overflow check, 16-bit.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Predictor overflow check, 16-bit Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Predictor overflow check 16 bit frame Failed with error:", f_err)) + } +} + +@(test) +predictor_overflow_check_20_bit_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/62 - predictor overflow check, 20-bit.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Predictor overflow check, 20-bit Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Predictor overflow check 20 bit frame Failed with error:", f_err)) + } +} + +@(test) +predictor_overflow_check_24_bit_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/63 - predictor overflow check, 24-bit.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Predictor overflow check, 24-bit Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Predictor overflow check 24 bit frame Failed with error:", f_err)) + } +} + +@(test) +rice_partitions_with_escape_code_zero_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/subset/64 - rice partitions with escape code zero.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Rice partitions with escape code zero Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Rice partitions with escape code zero frame Failed with error:", f_err)) + } +} + +@(test) +uncommon_32_bit_per_sample_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/uncommon/05 - 32bps audio.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Uncommon 32bps audio Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Uncommon 32 bit per sample frame Failed with error:", f_err)) + } +} + +@(test) +uncommon_samplerate_768kHz_buffered :: proc(t: ^testing.T) { + data, r, err := flac.load_from_file_buffered("flac-test-files/uncommon/06 - samplerate 768kHz.flac") + defer flac.destroy(data, r) + testing.expect(t, err == nil, fmt.tprint("Uncommon samplerate 768kHz Failed with error:", err)) + + for { + frame, f_err := flac.read_next_frame(r, data) + defer delete(frame.samples) + if f_err == .EOF { + break + } + testing.expect(t, f_err == nil, fmt.tprint("Decoding Uncommon samplerate 768kHz frame Failed with error:", f_err)) + } +} + +// TODO: diff --git a/tests/flac_test.odin b/tests/flac_in_memory_test.odin similarity index 100% rename from tests/flac_test.odin rename to tests/flac_in_memory_test.odin