mirror of
https://github.com/IllusionMan1212/flac-odin.git
synced 2026-10-24 18:12:29 +00:00
Compare commits
7
Commits
dfaca585fd
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a0741320d
|
||
|
|
44b4e52063
|
||
|
|
ba691f7470
|
||
|
|
c8b4c93276
|
||
|
|
81aa8d0f68
|
||
|
|
1142d83adb
|
||
|
|
f444efc5af
|
@@ -6,23 +6,19 @@ Files:-
|
||||
Playback issues in the example program that may relate to the decoder:-
|
||||
23 - Crunchy loud noise mixed with the actual audio data
|
||||
|
||||
uncommon/02 -> 6 channel audio might be incorrect (not a decoder issue afaik)
|
||||
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 -> 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)
|
||||
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:-
|
||||
[ ] Seeking to arbitrary samples
|
||||
- bufio `Reader` doesn't implement `.Seek` but the underlying `io.Stream` which is just a file DOES implement
|
||||
`.Seek` so I think bufio `Reader` should have Seeking capabilities too as long as the underlying Reader supports it.
|
||||
I could hack something together to (dis)prove this.
|
||||
[ ] Options
|
||||
- [x] Vorbis comment data
|
||||
- [ ] Cuesheet ?
|
||||
@@ -32,10 +28,13 @@ Things:-
|
||||
[ ] Lots of checks (look at the TODOs in flac.odin)
|
||||
[x] Memory optimizations
|
||||
[ ] Speed optimizations
|
||||
- Does this matter? So far I haven't found a FLAC file that lags while playing or stalls the audio device even without `-o:speed`
|
||||
[ ] Seeking
|
||||
[ ] Fuzzing to test the stability of the code
|
||||
[ ] Check if the use-after-free bug happens on Windows, and if so, if the address sanitizer will show us the full stacktrace
|
||||
[x] Check if the use-after-free bug happens on Windows, and if so, if the address sanitizer will show us the full stacktrace
|
||||
instead of skipping some procs.
|
||||
This was caused by `read_data()` and the tee reader writing into the buffer and us using some memory that was freed while the buffer
|
||||
resized. Solution is to use buffer data BEFORE calling `read_data()`
|
||||
|
||||
[ ] Encoder (low priority for now)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package flac_example
|
||||
package flac_linux_alsa_example
|
||||
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
@@ -33,15 +33,19 @@ main :: proc() {
|
||||
fmt.eprintln("Failed to alloc memory")
|
||||
return
|
||||
}
|
||||
allocator := virtual.arena_allocator(&arena)
|
||||
//allocator := virtual.arena_allocator(&arena)
|
||||
|
||||
track: mem.Tracking_Allocator
|
||||
mem.tracking_allocator_init(&track, context.allocator)
|
||||
defer mem.tracking_allocator_destroy(&track)
|
||||
context.allocator = mem.tracking_allocator(&track)
|
||||
|
||||
start := time.now()
|
||||
flac_data, reader, err := flac.load_from_file(os.args[1], allocator)
|
||||
flac_data, reader, err := flac.load_from_file(os.args[1], context.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)
|
||||
|
||||
@@ -51,13 +55,13 @@ main :: proc() {
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
sample_rate := flac_data.metadata.sample_rate
|
||||
// TODO: channel mapping is incorrect for anything with more than 2 channels.
|
||||
channels := flac_data.metadata.channels
|
||||
|
||||
md5_ctx: md5.Context
|
||||
md5.init(&md5_ctx)
|
||||
for {
|
||||
frame, err := flac.read_next_frame(reader, flac_data)
|
||||
frame, err := flac.decode_next_frame(reader, flac_data, context.allocator)
|
||||
if err != nil {
|
||||
if err == .EOF {
|
||||
break
|
||||
@@ -67,20 +71,24 @@ main :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
//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)
|
||||
// }
|
||||
//}
|
||||
if frame.channels != channels {
|
||||
// Finish playing the previous frame before changing the hw params.
|
||||
alsa.pcm_drain(handle)
|
||||
|
||||
channels = frame.channels
|
||||
alsa_err = alsa.pcm_set_params(handle, .S16_LE, .RW_INTERLEAVED, u32(channels), flac_data.metadata.sample_rate, 1, 500000)
|
||||
if alsa_err < 0 {
|
||||
fmt.eprintln("Playback open error:", alsa.strerror(alsa_err))
|
||||
os.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: resampling
|
||||
|
||||
// 8 bps audio
|
||||
//converted_samples := make([]u8, len(samples))
|
||||
//converted_samples := make([]u8, len(frame.samples))
|
||||
//defer delete(converted_samples)
|
||||
//for sample, i in samples {
|
||||
//for sample, i in frame.samples {
|
||||
// converted_samples[i] = u8(sample & 0xFF)
|
||||
//}
|
||||
|
||||
@@ -102,13 +110,6 @@ main :: proc() {
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
alsa_err = alsa.pcm_drain(handle)
|
||||
if alsa_err < 0 {
|
||||
@@ -116,6 +117,30 @@ main :: proc() {
|
||||
}
|
||||
alsa.pcm_close(handle)
|
||||
|
||||
end := time.now()
|
||||
|
||||
err = flac.md5sum(&md5_ctx, flac_data)
|
||||
if err == .MD5_Mismatch {
|
||||
fmt.printfln("Decoded audio data is not correct. Expected MD5: %v, Got: %v", flac_data.metadata.expected_md5, flac_data.metadata.calculated_md5)
|
||||
os.exit(1)
|
||||
}
|
||||
|
||||
elapsed := time.diff(start, end)
|
||||
fmt.println("Decoding took", elapsed)
|
||||
|
||||
flac.destroy(flac_data, reader, context.allocator)
|
||||
|
||||
for _, leak in track.allocation_map {
|
||||
fmt.printfln("%v leaked %m", leak.location, leak.size)
|
||||
}
|
||||
|
||||
for bad_free in track.bad_free_array {
|
||||
fmt.printfln("%v allocation %p was freed badly", bad_free.location, bad_free.memory)
|
||||
}
|
||||
|
||||
fmt.printfln("Peak Mem: %m", track.peak_memory_allocated)
|
||||
fmt.printfln("Total Mem: %m", track.total_memory_allocated)
|
||||
fmt.printfln("Current Mem: %m", track.current_memory_allocated)
|
||||
fmt.printfln("Total Allocs: %v, Total Frees: %v:", track.total_allocation_count, track.total_free_count)
|
||||
fmt.printfln("Total Mem Free'd: %m", track.total_memory_freed)
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
package flac
|
||||
|
||||
import "core:bytes"
|
||||
import "core:bufio"
|
||||
import "core:crypto/legacy/md5"
|
||||
import "core:fmt"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:time"
|
||||
|
||||
Error :: union {
|
||||
io.Error,
|
||||
@@ -69,8 +67,8 @@ PictureType :: enum u32 {
|
||||
|
||||
Flac :: struct {
|
||||
metadata: FlacMetadata,
|
||||
// This holds the samples up to the currently decoded frame. If all frames have
|
||||
// been decoded then this will hold all the decoded samples.
|
||||
// This holds the samples of the current frame.
|
||||
// TODO: Should this be deleted? every frame already holds its own samples data
|
||||
samples: [dynamic]i32,
|
||||
}
|
||||
|
||||
@@ -94,14 +92,15 @@ Picture :: struct {
|
||||
height: u32,
|
||||
depth: u32,
|
||||
num_colors: u32,
|
||||
data: []byte,
|
||||
data: []byte `fmt:"-"`,
|
||||
}
|
||||
|
||||
Frame :: struct {
|
||||
channels: u8,
|
||||
sample_rate: u32,
|
||||
// A slice of `Flac`'s `samples` field.
|
||||
// A slice view of `Flac`'s `samples` field.
|
||||
// Contains the frame's decoded samples.
|
||||
// DO NOT FREE.
|
||||
samples: []i32,
|
||||
}
|
||||
|
||||
@@ -430,12 +429,27 @@ correlate :: proc(subframes: [][]i32, channel_assignment: ChannelAssignment) {
|
||||
}
|
||||
|
||||
@(private)
|
||||
read_metadata :: proc(r: ^Reader, allocator := context.allocator) -> (flac: ^Flac, err: Error) {
|
||||
read_metadata :: proc(r: ^Reader, allocator := context.allocator) -> (out_flac: ^Flac, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
/* Held in a local rather than in the named result: an Odin `defer` cannot
|
||||
change a named result, so the unwind below is able to free this but not to
|
||||
nil it out for the caller. Assigning it only on the successful return means
|
||||
every error path hands back nil instead of a freed pointer.
|
||||
*/
|
||||
flac: ^Flac
|
||||
|
||||
header := read_data(r, FlacHeader) or_return
|
||||
if header.magic != FLAC_MAGIC {
|
||||
return nil, .Invalid_Signature
|
||||
}
|
||||
|
||||
if (header.streaminfo.min_block_size < 16 || header.streaminfo.min_block_size > 65535) ||
|
||||
(header.streaminfo.max_block_size < 16 || header.streaminfo.max_block_size > 65535) ||
|
||||
(header.streaminfo.min_block_size > header.streaminfo.max_block_size) {
|
||||
return nil, .Invalid_Block_Size
|
||||
}
|
||||
|
||||
sample_rate := header.streaminfo.sr_chan_bps_ts >> 44
|
||||
num_channel_minus_one := (header.streaminfo.sr_chan_bps_ts >> 41) & 7
|
||||
bits_per_sample_minus_one := (header.streaminfo.sr_chan_bps_ts >> 36) & 0x1F
|
||||
@@ -448,7 +462,7 @@ read_metadata :: proc(r: ^Reader, allocator := context.allocator) -> (flac: ^Fla
|
||||
}
|
||||
|
||||
if streaminfo_header.type != .STREAMINFO {
|
||||
return flac, .Missing_StreamInfo
|
||||
return nil, .Missing_StreamInfo
|
||||
}
|
||||
|
||||
if flac == nil {
|
||||
@@ -462,6 +476,30 @@ read_metadata :: proc(r: ^Reader, allocator := context.allocator) -> (flac: ^Fla
|
||||
flac.metadata.expected_md5 = header.streaminfo.md5
|
||||
|
||||
pictures := make([dynamic]Picture, 0, 2, allocator)
|
||||
|
||||
/* Everything allocated from here on hangs off `flac`, and a caller that gets
|
||||
an error back is handed a nil `flac` -- so it has nothing to pass to
|
||||
`destroy` and cannot clean any of this up itself. Unwind it here instead.
|
||||
Each block below therefore hands its allocations to `flac` or to `pictures`
|
||||
as soon as they exist, before the next read that could fail.
|
||||
*/
|
||||
defer if err != nil {
|
||||
for picture in pictures {
|
||||
delete(picture.mimetype)
|
||||
delete(picture.description)
|
||||
delete(picture.data)
|
||||
}
|
||||
delete(pictures)
|
||||
|
||||
for comment in flac.metadata.vorbis_comments {
|
||||
delete(comment)
|
||||
}
|
||||
delete(flac.metadata.vorbis_comments)
|
||||
delete(flac.metadata.vendor_name)
|
||||
|
||||
free(flac)
|
||||
}
|
||||
|
||||
last_block := streaminfo_header.last_block
|
||||
for !last_block {
|
||||
md_block_hdr := read_data(r, u32be) or_return
|
||||
@@ -501,26 +539,33 @@ read_metadata :: proc(r: ^Reader, allocator := context.allocator) -> (flac: ^Fla
|
||||
fmt.eprintfln("Expected size: %d, Got %d", metadata_block_header.length, actual_size)
|
||||
}
|
||||
case .VORBIS_COMMENT:
|
||||
// A stream may carry more than one of these; the last one wins,
|
||||
// so release the previous block rather than orphaning it.
|
||||
for comment in flac.metadata.vorbis_comments {
|
||||
delete(comment)
|
||||
}
|
||||
delete(flac.metadata.vorbis_comments)
|
||||
delete(flac.metadata.vendor_name)
|
||||
flac.metadata.vorbis_comments = nil
|
||||
flac.metadata.vendor_name = ""
|
||||
|
||||
vendor_name_len := read_data(r, u32le) or_return
|
||||
vendor_name_slice := make([]byte, vendor_name_len)
|
||||
flac.metadata.vendor_name = string(vendor_name_slice)
|
||||
read_slice(r, vendor_name_slice) or_return
|
||||
vendor_name := string(vendor_name_slice)
|
||||
user_comment_list_len := read_data(r, u32le) or_return
|
||||
|
||||
actual_size := 4 + vendor_name_len + 4
|
||||
|
||||
comments := make([]string, user_comment_list_len)
|
||||
flac.metadata.vorbis_comments = comments
|
||||
for i in 0..<user_comment_list_len {
|
||||
comment_len := read_data(r, u32le) or_return
|
||||
comment_slice := make([]byte, comment_len)
|
||||
comments[i] = string(comment_slice)
|
||||
read_slice(r, comment_slice) or_return
|
||||
comment := string(comment_slice)
|
||||
actual_size += 4 + comment_len
|
||||
|
||||
comments[i] = comment
|
||||
}
|
||||
flac.metadata.vorbis_comments = comments
|
||||
flac.metadata.vendor_name = vendor_name
|
||||
if metadata_block_header.length != u32(actual_size) {
|
||||
fmt.eprintln("Vorbis metadata header length mismatch!")
|
||||
fmt.eprintfln("Expected size: %d, Got %d", metadata_block_header.length, actual_size)
|
||||
@@ -532,7 +577,7 @@ read_metadata :: proc(r: ^Reader, allocator := context.allocator) -> (flac: ^Fla
|
||||
cuesheet := read_data(r, CueSheet) or_return
|
||||
if cuesheet.num_tracks < 1 {
|
||||
// TODO: should this be a warning instead of an error that halts the decoding?
|
||||
return flac, .Missing_Lead_Out_Track
|
||||
return nil, .Missing_Lead_Out_Track
|
||||
}
|
||||
//fmt.println(cuesheet)
|
||||
for i in 0..<cuesheet.num_tracks {
|
||||
@@ -546,15 +591,20 @@ read_metadata :: proc(r: ^Reader, allocator := context.allocator) -> (flac: ^Fla
|
||||
case .PICTURE:
|
||||
pic_type := PictureType(read_data(r, u32be) or_return)
|
||||
mime_len := read_data(r, u32be) or_return
|
||||
// These three are only reachable through `pictures` once the
|
||||
// append below runs, so they need their own unwind until then.
|
||||
mimetype_slice := make([]byte, mime_len)
|
||||
defer if err != nil {delete(mimetype_slice)}
|
||||
read_slice(r, mimetype_slice) or_return
|
||||
mimetype := string(mimetype_slice)
|
||||
desc_len := read_data(r, u32be) or_return
|
||||
desc_slice := make([]byte, desc_len)
|
||||
defer if err != nil {delete(desc_slice)}
|
||||
read_slice(r, desc_slice) or_return
|
||||
desc := string(desc_slice)
|
||||
metadata := read_data(r, PictureMetadata) or_return
|
||||
data := make([]byte, metadata.data_len)
|
||||
defer if err != nil {delete(data)}
|
||||
read_slice(r, data) or_return
|
||||
picture := Picture{
|
||||
type = pic_type,
|
||||
@@ -583,7 +633,7 @@ read_metadata :: proc(r: ^Reader, allocator := context.allocator) -> (flac: ^Fla
|
||||
return flac, nil
|
||||
}
|
||||
|
||||
read_next_frame :: proc(r: ^Reader, flac: ^Flac, allocator := context.allocator) -> (frame: Frame, err: Error) {
|
||||
decode_next_frame :: proc(r: ^Reader, flac: ^Flac, allocator := context.allocator) -> (frame: Frame, err: Error) {
|
||||
//
|
||||
// Frame Header
|
||||
//
|
||||
@@ -591,10 +641,6 @@ read_next_frame :: proc(r: ^Reader, flac: ^Flac, allocator := context.allocator)
|
||||
tee_r: io.Tee_Reader
|
||||
|
||||
crc_buffer: Buffer
|
||||
// NOTE: using the default heap allocator here causes a use-after-free bug on files subset/05, subset/06, and subset/25
|
||||
// No idea what the cause of the bug is.
|
||||
//
|
||||
// Resolved without understanding the root issue. See below code for a note on this.
|
||||
buffer_init_allocator(&crc_buffer, 0, 128)
|
||||
defer buffer_destroy(&crc_buffer)
|
||||
|
||||
@@ -656,6 +702,14 @@ read_next_frame :: proc(r: ^Reader, flac: ^Flac, allocator := context.allocator)
|
||||
return {}, .Invalid_Block_Size
|
||||
}
|
||||
|
||||
// TODO: Any frame but the last one MUST have a block size equal to or greater
|
||||
// than the minimum block size and MUST have a block size equal to or
|
||||
// less than the maximum block size. The last frame MUST have a block
|
||||
// size equal to or less than the maximum block size; it does not have
|
||||
// to comply to the minimum block size because the block size of that
|
||||
// frame must be able to accommodate the length of the audio data the
|
||||
// stream contains
|
||||
//
|
||||
//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.
|
||||
//}
|
||||
@@ -784,7 +838,7 @@ read_next_frame :: proc(r: ^Reader, flac: ^Flac, allocator := context.allocator)
|
||||
|
||||
correlate(subframes, channel_assignment)
|
||||
|
||||
start := len(flac.samples)
|
||||
clear(&flac.samples)
|
||||
|
||||
// Write the samples interleaved.
|
||||
for i in 0..<len(subframes[0]) {
|
||||
@@ -798,7 +852,7 @@ read_next_frame :: proc(r: ^Reader, flac: ^Flac, allocator := context.allocator)
|
||||
delete(subframe)
|
||||
}
|
||||
|
||||
frame.samples = flac.samples[start:]
|
||||
frame.samples = flac.samples[:]
|
||||
frame.channels = frame_channels
|
||||
frame.sample_rate = actual_sample_rate_in_Hz
|
||||
|
||||
@@ -806,13 +860,6 @@ read_next_frame :: proc(r: ^Reader, flac: ^Flac, allocator := context.allocator)
|
||||
// Frame Footer
|
||||
//
|
||||
crc16_bytes := buffer_to_bytes(&crc_buffer)
|
||||
// NOTE: The order of these two calls is important. If we're using the default heap allocator
|
||||
// for crc_buffer and we call read_data before calculate_crc16 then we run into a memory bug.
|
||||
// I'm still not sure the root cause of the bug but it disappears when they're in this order.
|
||||
//
|
||||
// In the below cases the bug doesn't happen and the order doesn't matter.
|
||||
// When using the temp allocator or an arena allocator.
|
||||
// When using the reader from the arguments instead of the tee reader.
|
||||
calculated_frame_crc := calculate_crc16(crc16_bytes)
|
||||
frame_crc := u16(read_data(r, u16be) or_return)
|
||||
|
||||
@@ -867,33 +914,47 @@ md5sum :: proc(md5_ctx: ^md5.Context, flac: ^Flac) -> Error {
|
||||
}
|
||||
|
||||
// `load_from_file` 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.
|
||||
// Calls to `decode_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 :: 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 {
|
||||
if open_err != nil {
|
||||
fmt.eprintln(open_err)
|
||||
return nil, {}, .Unable_To_Read_File
|
||||
return nil, nil, .Unable_To_Read_File
|
||||
}
|
||||
|
||||
br := new(bufio.Reader)
|
||||
bufio.reader_init(br, os.stream_from_handle(file))
|
||||
bufio.reader_init(br, os.to_stream(file))
|
||||
|
||||
r = new(Reader)
|
||||
r.r = bufio.reader_to_stream(br)
|
||||
r.buf = 0
|
||||
r.x = 0
|
||||
r.n = 0
|
||||
reader := new(Reader)
|
||||
reader.r = bufio.reader_to_stream(br)
|
||||
reader.buf = 0
|
||||
reader.x = 0
|
||||
reader.n = 0
|
||||
|
||||
flac = read_metadata(r) or_return
|
||||
/* Without this an unreadable file in a scanned library burns an fd and the
|
||||
reader's 4 KB buffer on every attempt -- the caller is handed nil for both
|
||||
results and so has no handle to close the file with.
|
||||
`reader` is a local for the same reason `read_metadata` keeps one: a defer
|
||||
cannot nil out the named result, so `r` is only assigned on success.
|
||||
*/
|
||||
defer if err != nil {
|
||||
bufio.reader_destroy(br)
|
||||
free(br)
|
||||
free(reader)
|
||||
os.close(file)
|
||||
}
|
||||
|
||||
return flac, r, nil
|
||||
flac = read_metadata(reader) or_return
|
||||
|
||||
return flac, reader, nil
|
||||
}
|
||||
|
||||
/* `destroy` cleans up and frees the memory allocated by all 3 loading functions.
|
||||
The returned reader from `load_from_file` MUST be passed to `destroy` in order to close the file handle.
|
||||
Both arguments may be nil, so it is safe to defer immediately after a load.
|
||||
---
|
||||
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.
|
||||
@@ -901,6 +962,9 @@ load_from_file :: proc(filename: string, allocator := context.allocator) -> (fla
|
||||
destroy :: proc(flac: ^Flac, r: ^Reader = nil, allocator := context.allocator) {
|
||||
context.allocator = allocator
|
||||
|
||||
// Tolerates a nil `flac` so a caller can `defer destroy(...)` straight after
|
||||
// a load without first checking whether the load succeeded.
|
||||
if flac != nil {
|
||||
delete(flac.samples)
|
||||
for picture in flac.metadata.pictures {
|
||||
delete(picture.mimetype)
|
||||
@@ -916,6 +980,7 @@ destroy :: proc(flac: ^Flac, r: ^Reader = nil, allocator := context.allocator) {
|
||||
delete(flac.metadata.vendor_name)
|
||||
|
||||
free(flac)
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
br := cast(^bufio.Reader)r.data
|
||||
|
||||
+7
-4
@@ -6,6 +6,7 @@ import "core:crypto/legacy/md5"
|
||||
FLAC_MAGIC :: 'f' << 24 | 'L' << 16 | 'a' << 8 | 'C'
|
||||
|
||||
//odinfmt: disable
|
||||
@(rodata)
|
||||
crc8_lookup := [256]u8 {
|
||||
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
|
||||
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
|
||||
@@ -41,6 +42,7 @@ crc8_lookup := [256]u8 {
|
||||
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3,
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
crc16_lookup := [256]u16 {
|
||||
0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
|
||||
0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
|
||||
@@ -210,10 +212,10 @@ CueSheet :: struct #packed {
|
||||
media_catalog_num: [128]byte,
|
||||
num_lead_in_samples: u64be,
|
||||
using _: bit_field u8 {
|
||||
reserved: u8 | 7,
|
||||
reserved_1: u8 | 7,
|
||||
compact_disc: bool | 1,
|
||||
},
|
||||
reserved: [258]byte,
|
||||
reserved_2: [258]byte,
|
||||
num_tracks: u8,
|
||||
}
|
||||
|
||||
@@ -223,11 +225,11 @@ CueSheetTrack :: struct #packed {
|
||||
track_num: u8,
|
||||
ISRC: [12]byte,
|
||||
using _: bit_field u8 {
|
||||
reserved: u8 | 6,
|
||||
reserved_1: u8 | 6,
|
||||
pre_emphasis: bool | 1,
|
||||
track_type: TrackType | 1,
|
||||
},
|
||||
reserved: [13]byte,
|
||||
reserved_2: [13]byte,
|
||||
num_track_index_points: u8,
|
||||
}
|
||||
|
||||
@@ -238,6 +240,7 @@ CueSheetTrackIndex :: struct #packed {
|
||||
reserved: [3]byte,
|
||||
}
|
||||
|
||||
@(optimization_mode="favor_size")
|
||||
decode_extended_utf8 :: proc(r: ^Reader) -> (decoded_num: u64, err: Error) {
|
||||
MASKX :: 0b0011_1111
|
||||
MASK2 :: 0b0001_1111
|
||||
|
||||
+4
-1
@@ -1,4 +1,3 @@
|
||||
#+private
|
||||
package flac
|
||||
|
||||
import "core:io"
|
||||
@@ -6,6 +5,8 @@ import "core:io"
|
||||
/* Basic byte and bit reader.
|
||||
Uses core:bytes reader under the hood with a few useful procedures for reading structs and other types.
|
||||
*/
|
||||
// Public because load_from_file hands one back; everything else in this file
|
||||
// stays package-private.
|
||||
Reader :: struct {
|
||||
using r: io.Stream,
|
||||
// Temporary read buffer.
|
||||
@@ -33,6 +34,7 @@ read_data :: #force_inline proc(r: ^Reader, $T: typeid) -> (res: T, err: io.Erro
|
||||
// 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
|
||||
@(private)
|
||||
read_bits :: proc(r: ^Reader, n: uint) -> (res: u64, err: io.Error) #no_bounds_check {
|
||||
if n == 0 {
|
||||
return 0, nil
|
||||
@@ -86,6 +88,7 @@ read_bits :: proc(r: ^Reader, n: uint) -> (res: u64, err: io.Error) #no_bounds_c
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@(private)
|
||||
align_to_byte :: proc(r: ^Reader) {
|
||||
r.x = 0
|
||||
r.n = 0
|
||||
|
||||
+90
-66
@@ -3,6 +3,7 @@ package flac_test
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:testing"
|
||||
import "core:crypto/legacy/md5"
|
||||
import "shared:flac"
|
||||
|
||||
@(test)
|
||||
@@ -12,7 +13,7 @@ test_blocksize_4096 :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -27,7 +28,7 @@ test_blocksize_4608 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Blocksize 4608 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -42,7 +43,7 @@ test_blocksize_16 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Blocksize 16 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -57,7 +58,7 @@ test_blocksize_192 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Blocksize 192 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -72,7 +73,7 @@ test_blocksize_254 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Blocksize 254 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -87,7 +88,7 @@ test_blocksize_512 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Blocksize 512 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -102,7 +103,7 @@ test_blocksize_725 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Blocksize 725 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -117,7 +118,7 @@ test_blocksize_1000 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Blocksize 1000 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -132,7 +133,7 @@ test_blocksize_1937 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Blocksize 1937 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -147,7 +148,7 @@ test_blocksize_2304 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Blocksize 2304 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -162,7 +163,7 @@ test_partition_order_8 :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Partition order 8 Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -177,7 +178,7 @@ test_qlp_precision_15_bit :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -192,7 +193,7 @@ test_qlp_precision_2_bit :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -207,7 +208,7 @@ wasted_bits :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Wasted bits Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -222,7 +223,7 @@ only_verbatim_subframes :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Only verbatim subframes Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -241,7 +242,7 @@ partition_order_8_containing_escaped_partitions :: proc(t: ^testing.T) {
|
||||
)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -256,7 +257,7 @@ all_fixed_orders :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("All fixed orders Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -271,7 +272,7 @@ precision_search :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Precision search Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -286,7 +287,7 @@ samplerate_35467Hz :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Samplerate 35467Hz Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -301,7 +302,7 @@ samplerate_39kHz :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Samplerate 39kHz Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -316,7 +317,7 @@ samplerate_22050Hz :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Samplerate 22050Hz Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -331,7 +332,7 @@ _12_bit_per_sample :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -346,7 +347,7 @@ _8_bit_per_sample :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -367,7 +368,7 @@ variable_blocksize_file_created_with_flake_revision_264 :: proc(t: ^testing.T) {
|
||||
)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -391,7 +392,7 @@ variable_blocksize_file_created_with_flake_revision_264_modified_to_create_small
|
||||
)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -412,7 +413,7 @@ variable_blocksize_file_created_with_CUETools_Flake_2_1_6 :: proc(t: ^testing.T)
|
||||
)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -433,7 +434,7 @@ old_format_variable_blocksize_file_created_with_Flake_0_11 :: proc(t: ^testing.T
|
||||
)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -448,7 +449,7 @@ high_resolution_audio_default_settings :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -463,7 +464,7 @@ high_resolution_audio_blocksize_16384 :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -478,7 +479,7 @@ high_resolution_audio_blocksize_13456 :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -499,7 +500,7 @@ high_resolution_audio_using_only_32nd_order_predictors :: proc(t: ^testing.T) {
|
||||
)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -520,7 +521,7 @@ high_resolution_audio_partition_order_8_containing_escaped_partitions :: proc(t:
|
||||
)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -535,7 +536,7 @@ samplerate_192kHz :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Samplerate 192kHz Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -554,7 +555,7 @@ samplerate_192kHz_using_only_32nd_order_predictors :: proc(t: ^testing.T) {
|
||||
)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -569,7 +570,7 @@ samplerate_134560Hz :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Samplerate 134560Hz Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -584,7 +585,7 @@ samplerate_384kHz :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Samplerate 384kHz Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -599,7 +600,7 @@ _20_bit_per_sample :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -614,7 +615,7 @@ _3_channels :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("3 channels Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -629,7 +630,7 @@ _4_channels :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("4 channels Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -644,7 +645,7 @@ _5_channels :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("5 channels Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -659,7 +660,7 @@ _6_channels :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("6 channels Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -674,7 +675,7 @@ _7_channels :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("7 channels Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -689,7 +690,7 @@ _8_channels :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("8 channels Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -710,7 +711,7 @@ _8_channel_surround_192kHz_24_bit_using_only_32nd_order_predictors :: proc(t: ^t
|
||||
)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -725,7 +726,7 @@ no_total_number_of_samples :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -740,7 +741,7 @@ no_min_max_framesize :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -755,7 +756,7 @@ only_streaminfo :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Only STREAMINFO Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -770,7 +771,7 @@ extreme_large_seektable :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Extremely large SEEKTABLE Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -785,7 +786,7 @@ extreme_large_padding :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Extremely large PADDING Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -800,7 +801,7 @@ extreme_large_picture :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Extremely large PICTURE Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -815,7 +816,7 @@ extreme_large_vorbiscomment :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Extremely large VORBISCOMMENT Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -830,7 +831,7 @@ extreme_large_application :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Extremely large APPLICATION Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -845,7 +846,7 @@ cuesheet_with_very_many_indexes :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -860,7 +861,7 @@ _1000x_repeating_vorbiscomment :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("1000x repeating VORBSICOMMENT Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -875,7 +876,7 @@ file_48_53_combined :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -890,7 +891,7 @@ jpg_picture :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("JPG PICTURE Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -905,7 +906,7 @@ png_picture :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("PNG PICTURE Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -920,7 +921,7 @@ gif_picture :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("GIF PICTURE Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -935,7 +936,7 @@ avif_picture :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("AVIF PICTURE Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -950,7 +951,7 @@ mono_audio :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Mono audio Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -965,7 +966,7 @@ predictor_overflow_check_16_bit :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -980,7 +981,7 @@ predictor_overflow_check_20_bit :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -995,7 +996,7 @@ predictor_overflow_check_24_bit :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -1010,7 +1011,7 @@ rice_partitions_with_escape_code_zero :: proc(t: ^testing.T) {
|
||||
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)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -1018,6 +1019,29 @@ rice_partitions_with_escape_code_zero :: proc(t: ^testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
uncommon_changing_samplerate :: proc(t: ^testing.T) {
|
||||
data, r, err := flac.load_from_file("flac-test-files/uncommon/01 - changing samplerate.flac")
|
||||
defer flac.destroy(data, r)
|
||||
testing.expect(t, err == nil, fmt.tprint("Uncommon changing samplerate Failed with error:", err))
|
||||
|
||||
md5_ctx: md5.Context
|
||||
md5.init(&md5_ctx)
|
||||
|
||||
for {
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
flac.md5hash(&md5_ctx, data.metadata.bits_per_sample, frame.samples)
|
||||
testing.expect(t, f_err == nil, fmt.tprintf("Decoding Uncommon changing samplerate frame Failed with error:", f_err))
|
||||
}
|
||||
|
||||
if data.metadata.expected_md5 != 0 {
|
||||
testing.expect(t, flac.md5sum(&md5_ctx, data) == nil, fmt.tprintf("Incorrect MD5 hash. Expected %v. Got %v", data.metadata.expected_md5, data.metadata.calculated_md5))
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
uncommon_32_bit_per_sample :: proc(t: ^testing.T) {
|
||||
data, r, err := flac.load_from_file("flac-test-files/uncommon/05 - 32bps audio.flac")
|
||||
@@ -1025,7 +1049,7 @@ uncommon_32_bit_per_sample :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Uncommon 32bps audio Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
@@ -1040,7 +1064,7 @@ uncommon_samplerate_768kHz :: proc(t: ^testing.T) {
|
||||
testing.expect(t, err == nil, fmt.tprint("Uncommon samplerate 768kHz Failed with error:", err))
|
||||
|
||||
for {
|
||||
frame, f_err := flac.read_next_frame(r, data)
|
||||
frame, f_err := flac.decode_next_frame(r, data)
|
||||
if f_err == .EOF {
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user