diff --git a/TODO.md b/TODO.md index 9fb4d0e..d598aec 100644 --- a/TODO.md +++ b/TODO.md @@ -4,11 +4,7 @@ Files:- if they are, then the blocking strategy is VARIABLE, if not, then it's FIXED. 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. diff --git a/buffer.odin b/buffer.odin new file mode 100644 index 0000000..6dc4bf0 --- /dev/null +++ b/buffer.odin @@ -0,0 +1,48 @@ +package flac + +import "core:io" + +// Buffer is a very minimal writable buffer based on bytes.Buffer +Buffer :: struct { + buf: [dynamic]byte, +} + +buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator, loc := #caller_location) { + b.buf = make([dynamic]byte, len, cap, allocator, loc) +} + +buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) { + return append(&b.buf, ..p), nil +} + +buffer_destroy :: proc(b: ^Buffer) { + delete(b.buf) +} + +buffer_to_bytes :: proc(b: ^Buffer) -> []byte { + return b.buf[:] +} + +buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) { + s.data = b + s.procedure = _buffer_proc + return +} + +@(private) +_buffer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { + b := (^Buffer)(stream_data) + #partial switch mode { + case .Write: + return io._i64_err(buffer_write(b, p)) + case .Size: + n = i64(cap(b.buf)) + return + case .Destroy: + buffer_destroy(b) + return + case .Query: + return io.query_utility({.Write, .Size, .Destroy}) + } + return 0, .Empty +} diff --git a/examples/buffered/main.odin b/examples/buffered/main.odin index 6637b99..a642797 100644 --- a/examples/buffered/main.odin +++ b/examples/buffered/main.odin @@ -17,6 +17,7 @@ main :: proc() { fmt.eprintln("Please provide a path to a flac file.") os.exit(1) } + defer delete(os.args) arena: virtual.Arena handle: ^alsa.pcm_t @@ -53,12 +54,10 @@ main :: proc() { sample_rate := flac_data.metadata.sample_rate channels := flac_data.metadata.channels - //md5_ctx: md5.Context - //md5.init(&md5_ctx) + md5_ctx: md5.Context + md5.init(&md5_ctx) for { - frame, err := flac.read_next_frame(reader, flac_data) - - defer delete(frame.samples) + frame, err := flac.read_next_frame(reader, flac_data, allocator) if err != nil { if err == .EOF { break @@ -99,15 +98,15 @@ main :: proc() { } fmt.printfln("Played %d frames", frames) - //flac.md5hash(&md5_ctx, flac_data.metadata.bits_per_sample, samples) + flac.md5hash(&md5_ctx, flac_data.metadata.bits_per_sample, frame.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) - //} + 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 { diff --git a/examples/in_memory/main.odin b/examples/in_memory/main.odin index e7a593b..9ead040 100644 --- a/examples/in_memory/main.odin +++ b/examples/in_memory/main.odin @@ -16,6 +16,7 @@ main :: proc() { fmt.eprintln("Please provide a path to a flac file.") os.exit(1) } + defer delete(os.args) handle: ^alsa.pcm_t @@ -61,14 +62,12 @@ main :: proc() { 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) + 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)) + return } + fmt.printfln("Played %d frames", frames) alsa_err = alsa.pcm_drain(handle) if alsa_err < 0 { diff --git a/flac.odin b/flac.odin index baf0a87..eebf985 100644 --- a/flac.odin +++ b/flac.odin @@ -852,15 +852,17 @@ read_metadata :: proc(r: ^Reader, buffered := false, allocator := context.alloca return flac, nil } -read_next_frame :: proc(r: ^Reader, flac: ^Flac) -> (frame: Frame, err: Error) { +read_next_frame :: proc(r: ^Reader, flac: ^Flac, allocator := context.allocator) -> (frame: Frame, err: Error) { // // Frame Header // + context.allocator = allocator 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) + + crc_buffer: Buffer + buffer_init_allocator(&crc_buffer, 0, 128, context.temp_allocator) + + crc := buffer_to_stream(&crc_buffer) r := &Reader{ r = io.tee_reader_init(&tee_r, r, crc), buf = r.buf, @@ -960,7 +962,7 @@ read_next_frame :: proc(r: ^Reader, flac: ^Flac) -> (frame: Frame, err: Error) { // 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) + crc8_bytes := buffer_to_bytes(&crc_buffer) frame_header_crc := u8(read_bits(r, 8) or_return) calculated_header_crc := calculate_crc8(crc8_bytes) @@ -1064,7 +1066,7 @@ read_next_frame :: proc(r: ^Reader, flac: ^Flac) -> (frame: Frame, err: Error) { // // Frame Footer // - crc16_bytes := bytes.buffer_to_bytes(&crc_buffer) + crc16_bytes := buffer_to_bytes(&crc_buffer) frame_crc := u16(read_data(r, u16be) or_return) calculated_frame_crc := calculate_crc16(crc16_bytes) @@ -1101,7 +1103,7 @@ md5hash :: proc(md5_ctx: ^md5.Context, bps: u8, samples: []i32) { } md5sum :: proc(md5_ctx: ^md5.Context, flac: ^Flac) -> Error { - calculated_md5: [16]byte + calculated_md5 := [16]byte{} md5.final(md5_ctx, calculated_md5[:]) flac.metadata.calculated_md5 = calculated_md5