mirror of
https://github.com/IllusionMan1212/flac-odin.git
synced 2026-10-24 02:12:29 +00:00
initial. still lots more to be done
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
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
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[submodule "tests/flac-test-files"]
|
||||||
|
path = tests/flac-test-files
|
||||||
|
url = https://github.com/ietf-wg-cellar/flac-test-files
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# flac-odin
|
||||||
|
|
||||||
|
FLAC decoder library written in Odin (WIP)
|
||||||
|
|
||||||
|
## LICENSE
|
||||||
|
No license has been decided on yet. A suitable license will be chosen once the project is near "completion".
|
||||||
|
|
||||||
|
Copyright 2024 IllusionMan1212. All rights reserved.
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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.
|
||||||
|
61 -> I suspect the predictor DOES overflow here. check it
|
||||||
|
62 -> I suspect the predictor DOES overflow here. check it
|
||||||
|
63 -> I suspect the predictor DOES overflow here. check it
|
||||||
|
|
||||||
|
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 -> We fail a CRC check
|
||||||
|
uncommon/03 -> We fail so hard we end up reading an invalid residual coding method
|
||||||
|
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
|
||||||
|
|
||||||
|
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 -> We fail on Invalid_Coefficient_Precision. This one has a wrong number of channels (whatever that means)
|
||||||
|
faulty/05 -> This one works when it shouldn't. (Wrong number of samples)
|
||||||
|
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 is allowed to have less than 16))
|
||||||
|
|
||||||
|
Things:-
|
||||||
|
[ ] Stereo Decorrelation
|
||||||
|
[ ] Lots of checks (look at the TODOs in flac.odin)
|
||||||
|
[ ] MD5 checksums (depends on stereo decorrelation)
|
||||||
|
- [ ] VERBATIM subframe
|
||||||
|
- [ ] CONSTANT subframe
|
||||||
|
- [ ] FIXED subframe
|
||||||
|
- [ ] LPC subframe
|
||||||
|
[ ] Seeking
|
||||||
|
[x] RICE2
|
||||||
|
[x] Add check for mismatched bits per sample
|
||||||
|
[x] Unencoded BPS is zero
|
||||||
|
[x] CONSTANT subframe
|
||||||
|
|
||||||
|
[ ] Encoder (low priority for now)
|
||||||
|
|
||||||
|
Security:-
|
||||||
|
Security concerns will be considered after making the decoder work correctly and robustly.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package flac_example
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:os"
|
||||||
|
import "core:time"
|
||||||
|
|
||||||
|
import "../"
|
||||||
|
|
||||||
|
main :: proc() {
|
||||||
|
start := time.now()
|
||||||
|
if len(os.args) < 2 {
|
||||||
|
fmt.eprintln("Please provide a path to a flac file.")
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
err := flac.load_from_file(os.args[1])
|
||||||
|
if err != nil {
|
||||||
|
fmt.eprintln(err)
|
||||||
|
os.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
end := time.now()
|
||||||
|
|
||||||
|
elapsed := time.diff(start, end)
|
||||||
|
fmt.println("Decoding took", elapsed)
|
||||||
|
}
|
||||||
+97
@@ -0,0 +1,97 @@
|
|||||||
|
//+private
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i64(len(r.s)) == r.i {
|
||||||
|
return []u8{}, .EOF
|
||||||
|
} else {
|
||||||
|
return []u8{}, .Short_Buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make sure this takes into account the bits in bits_read_in_byte
|
||||||
|
// Although this is probably not needed since the only times we're not alinged to a byte is when finishing a subframe
|
||||||
|
// All frames are supposed to end byte-aligned.
|
||||||
|
read_data :: #force_inline proc(r: ^Reader, $T: typeid) -> (res: T, err: io.Error) {
|
||||||
|
b := read_slice(r, size_of(T)) or_return
|
||||||
|
return (^T)(&b[0])^, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
read_bit :: #force_inline proc(r: ^Reader) -> (bit: byte, err: Error) {
|
||||||
|
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
|
||||||
|
|
||||||
|
if r.bits_read_in_byte == 8 {
|
||||||
|
r.i += 1
|
||||||
|
r.bits_read_in_byte = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
read_bits :: proc(r: ^Reader, n: int) -> (bits: i64, err: Error) {
|
||||||
|
x: i64 = 0
|
||||||
|
|
||||||
|
for i in 0 ..< n {
|
||||||
|
x = 2 * x + auto_cast read_bit(r) or_return
|
||||||
|
}
|
||||||
|
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
read_byte :: proc(r: ^Reader) -> (b: byte, err: Error) {
|
||||||
|
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)
|
||||||
|
} else {
|
||||||
|
b = r.s[r.i]
|
||||||
|
r.i += 1
|
||||||
|
}
|
||||||
|
return b, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
align_to_byte :: proc(r: ^Reader) -> Error {
|
||||||
|
if r.bits_read_in_byte == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if r.i >= i64(len(r.s)) {
|
||||||
|
return .EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Align by skipping the remaining bits in the byte
|
||||||
|
r.bits_read_in_byte = 0
|
||||||
|
r.i += 1
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
Submodule
+1
Submodule tests/flac-test-files added at aa7b0c6cf3
@@ -0,0 +1,3 @@
|
|||||||
|
package flac_test
|
||||||
|
|
||||||
|
// TODO:
|
||||||
Reference in New Issue
Block a user