2024-06-11 22:57:04 +02:00
|
|
|
package flac
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
import "core:bufio"
|
2024-06-11 22:57:04 +02:00
|
|
|
import "core:crypto/legacy/md5"
|
|
|
|
|
import "core:fmt"
|
|
|
|
|
import "core:io"
|
2024-06-15 01:29:48 +02:00
|
|
|
import "core:mem"
|
2026-09-11 01:25:54 +02:00
|
|
|
import "core:os"
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
Error :: union {
|
|
|
|
|
io.Error,
|
|
|
|
|
FlacError,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
FlacError :: enum {
|
|
|
|
|
Unable_To_Read_File,
|
|
|
|
|
Invalid_Signature,
|
|
|
|
|
Invalid_Sample_Rate,
|
|
|
|
|
Missing_StreamInfo,
|
|
|
|
|
Missing_Lead_Out_Track,
|
|
|
|
|
Invalid_Coefficient_Precision,
|
|
|
|
|
CRC_Mismatch,
|
|
|
|
|
MD5_Mismatch,
|
2024-09-24 03:50:34 +02:00
|
|
|
Unsupported_BPS, // TODO: Remove when implemented
|
|
|
|
|
Unencoded_BPS_For_Fixed_Subframe_Is_Unsupported, // TODO: Remove when implemented
|
2024-06-11 22:57:04 +02:00
|
|
|
Invalid_Residual_Coding_Method,
|
|
|
|
|
Invalid_Block_Size,
|
|
|
|
|
Bits_Per_Second_Mismatch,
|
2024-06-14 00:18:03 +02:00
|
|
|
Negative_Coefficient_Bits_To_Shift,
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
/*
|
|
|
|
|
Option:
|
2024-06-19 04:08:54 +02:00
|
|
|
`.only_return_metadata`
|
2024-06-15 01:29:48 +02:00
|
|
|
Returns all metadata of a FLAC stream without decoding the audio data.
|
|
|
|
|
*/
|
|
|
|
|
|
2024-09-24 03:50:34 +02:00
|
|
|
Option :: enum u8 {
|
2024-06-15 01:29:48 +02:00
|
|
|
only_return_metadata,
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-09-24 03:50:34 +02:00
|
|
|
Options :: bit_set[Option; u8]
|
2024-06-15 01:29:48 +02:00
|
|
|
|
2024-06-15 03:39:53 +02:00
|
|
|
PictureType :: enum u32 {
|
2024-06-11 22:57:04 +02:00
|
|
|
OTHER,
|
|
|
|
|
FILE_ICON_32x32_PNG,
|
|
|
|
|
OTHER_FILE_ICON,
|
|
|
|
|
COVER_FRONT,
|
|
|
|
|
COVER_BACK,
|
|
|
|
|
LEAFLET,
|
|
|
|
|
MEDIA,
|
|
|
|
|
LEAD_ARTIST,
|
|
|
|
|
ARTIST,
|
|
|
|
|
CONDUCTOR,
|
|
|
|
|
BAND,
|
|
|
|
|
COMPOSER,
|
|
|
|
|
LYRICIST,
|
|
|
|
|
RECORDING_LOCATION,
|
|
|
|
|
DURING_RECORDING,
|
|
|
|
|
DURING_PERFORMANCE,
|
|
|
|
|
VIDEO_SCREEN_CAPTURE,
|
|
|
|
|
BRIGHT_COLORED_FISH,
|
|
|
|
|
ILLUSTRATION,
|
|
|
|
|
ARTIST_LOGO,
|
|
|
|
|
PUBLISHER_LOGO,
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
Flac :: struct {
|
2024-06-15 03:39:53 +02:00
|
|
|
metadata: FlacMetadata,
|
2025-01-27 23:57:57 +02:00
|
|
|
// This holds the samples of the current frame.
|
2026-09-11 01:25:54 +02:00
|
|
|
// TODO: Should this be deleted? every frame already holds its own samples data
|
2024-06-19 04:08:54 +02:00
|
|
|
samples: [dynamic]i32,
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-15 03:39:53 +02:00
|
|
|
FlacMetadata :: struct {
|
|
|
|
|
total_samples: u64,
|
2024-06-15 01:29:48 +02:00
|
|
|
bits_per_sample: u8,
|
|
|
|
|
channels: u8,
|
|
|
|
|
sample_rate: u32,
|
2024-06-15 03:39:53 +02:00
|
|
|
expected_md5: [16]byte,
|
|
|
|
|
calculated_md5: [16]byte,
|
|
|
|
|
pictures: []Picture,
|
2024-06-19 04:08:54 +02:00
|
|
|
vendor_name: string,
|
|
|
|
|
vorbis_comments: []string,
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-15 03:39:53 +02:00
|
|
|
Picture :: struct {
|
|
|
|
|
type: PictureType,
|
|
|
|
|
mimetype: string,
|
|
|
|
|
description: string,
|
|
|
|
|
width: u32,
|
|
|
|
|
height: u32,
|
|
|
|
|
depth: u32,
|
|
|
|
|
num_colors: u32,
|
2025-01-27 23:57:57 +02:00
|
|
|
data: []byte `fmt:"-"`,
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
Frame :: struct {
|
|
|
|
|
channels: u8,
|
|
|
|
|
sample_rate: u32,
|
2025-01-27 23:57:57 +02:00
|
|
|
// A slice view of `Flac`'s `samples` field.
|
2024-09-24 03:50:34 +02:00
|
|
|
// Contains the frame's decoded samples.
|
2025-01-27 23:57:57 +02:00
|
|
|
// DO NOT FREE.
|
2024-06-19 04:08:54 +02:00
|
|
|
samples: []i32,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@(private)
|
2024-06-14 03:58:36 +02:00
|
|
|
decode_subframe :: proc(r: ^Reader, bps: u8, block_size: u16) -> (samples: []i32, err: Error) #no_bounds_check {
|
|
|
|
|
subframe_samples := make([]i32, block_size)
|
2024-06-11 22:57:04 +02:00
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
data := read_bits(r, 8) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
subframe_type := (data & 0x7E) >> 1
|
|
|
|
|
low_3_bits := subframe_type & 7
|
|
|
|
|
has_wasted_bits := data & 1 == 1
|
|
|
|
|
wasted_bits := 0
|
|
|
|
|
|
|
|
|
|
if has_wasted_bits {
|
2024-06-19 04:08:54 +02:00
|
|
|
for (read_bits(r, 1) or_return) != 1 {
|
2024-06-11 22:57:04 +02:00
|
|
|
wasted_bits += 1
|
|
|
|
|
}
|
|
|
|
|
wasted_bits += 1
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
if subframe_type == 0 { // CONSTANT
|
2024-06-11 22:57:04 +02:00
|
|
|
bits_to_read := int(bps) - wasted_bits
|
2024-06-15 01:29:48 +02:00
|
|
|
sample := i32(read_bits(r, uint(bits_to_read)) or_return)
|
2024-06-14 00:18:03 +02:00
|
|
|
if sample > 0 && (sample >> (uint(bits_to_read) - 1)) & 1 == 1 {
|
2024-06-14 03:58:36 +02:00
|
|
|
sample = -(((~sample) & (i32(pow2(bits_to_read)) - 1)) + 1)
|
2024-06-14 00:18:03 +02:00
|
|
|
}
|
2024-06-15 01:29:48 +02:00
|
|
|
copy(subframe_samples, []i32{sample})
|
|
|
|
|
} else if subframe_type == 1 { // VERBATIM
|
2024-06-11 22:57:04 +02:00
|
|
|
// TODO: this will break if bits per sample is anything BUT 16
|
|
|
|
|
if bps < 16 || bps > 17 {
|
2024-06-12 20:09:09 +02:00
|
|
|
return nil, .Unsupported_BPS
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
for sample in 0..<block_size {
|
|
|
|
|
bits_to_read := int(bps) - wasted_bits
|
|
|
|
|
|
2024-06-14 00:18:03 +02:00
|
|
|
// negative samples ??
|
2024-06-15 01:29:48 +02:00
|
|
|
unencoded_subblock := i32((read_bits(r, uint(bits_to_read)) or_return))
|
2024-06-11 22:57:04 +02:00
|
|
|
|
2024-06-14 03:58:36 +02:00
|
|
|
subframe_samples[sample] = unencoded_subblock
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
2024-06-15 01:29:48 +02:00
|
|
|
} else if (subframe_type >> 5) & 1 == 1 { // LPC
|
2024-06-11 22:57:04 +02:00
|
|
|
sample_i := 0
|
|
|
|
|
predictor_order := (subframe_type & 0x1F) + 1
|
|
|
|
|
|
2024-06-12 20:09:09 +02:00
|
|
|
bits_to_read := int(bps) - wasted_bits
|
2024-06-14 00:18:03 +02:00
|
|
|
for s in 0..<predictor_order {
|
2024-06-11 22:57:04 +02:00
|
|
|
// NOTE: samples are signed so we do 2's complement
|
2024-06-15 01:29:48 +02:00
|
|
|
unencoded_warmup_sample := i32(read_bits(r, uint(bits_to_read)) or_return)
|
2024-06-14 00:18:03 +02:00
|
|
|
if unencoded_warmup_sample > 0 && (unencoded_warmup_sample >> (uint(bits_to_read) - 1)) & 1 == 1 {
|
2024-06-14 03:58:36 +02:00
|
|
|
unencoded_warmup_sample = -(((~unencoded_warmup_sample) & (i32(pow2(bits_to_read)) - 1)) + 1)
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-14 03:58:36 +02:00
|
|
|
subframe_samples[s] = unencoded_warmup_sample
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
coeff_precision_minus_one := u8(read_bits(r, 4) or_return)
|
|
|
|
|
if coeff_precision_minus_one == 0xF {
|
2024-06-12 20:09:09 +02:00
|
|
|
return nil, .Invalid_Coefficient_Precision
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
coeff_precision := coeff_precision_minus_one + 1
|
|
|
|
|
|
|
|
|
|
coeff_bits_to_shift := i8(read_bits(r, 5) or_return)
|
|
|
|
|
// NOTE: This is signed 2's complement so we check the MSB
|
2024-06-15 01:29:48 +02:00
|
|
|
// NOTE: This is apparently supposed to never be a negative number according to the (new?) spec.
|
2024-06-14 03:58:36 +02:00
|
|
|
// TODO: For now we just error if the shift value is negative. I have to find test cases to be sure.
|
2024-06-11 22:57:04 +02:00
|
|
|
if (coeff_bits_to_shift >> (5 - 1)) & 1 == 1 {
|
2024-06-14 00:18:03 +02:00
|
|
|
return nil, .Negative_Coefficient_Bits_To_Shift
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
coefficients := make([]i32, predictor_order)
|
2024-06-15 03:39:53 +02:00
|
|
|
defer delete(coefficients)
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
for i in 0..<predictor_order {
|
2024-06-15 01:29:48 +02:00
|
|
|
unencoded_coefficient := i32(read_bits(r, uint(coeff_precision)) or_return)
|
2024-06-11 22:57:04 +02:00
|
|
|
// Ditto
|
2024-06-14 00:18:03 +02:00
|
|
|
if unencoded_coefficient > 0 && (unencoded_coefficient >> (coeff_precision - 1)) & 1 == 1 {
|
2024-06-14 03:58:36 +02:00
|
|
|
unencoded_coefficient = -(((~unencoded_coefficient) & (i32(pow2(coeff_precision)) - 1)) + 1)
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
coefficients[i] = unencoded_coefficient
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
residual_coding_method := ResidualCodingMethod(read_bits(r, 2) or_return)
|
|
|
|
|
if residual_coding_method != .RICE && residual_coding_method != .RICE2 {
|
2024-06-12 20:09:09 +02:00
|
|
|
return nil, .Invalid_Residual_Coding_Method
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
partition_order := read_bits(r, 4) or_return
|
2024-06-15 01:29:48 +02:00
|
|
|
partitions := pow2(partition_order)
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
lpc_partitions_loop: for i in 0..<partitions {
|
|
|
|
|
rice_parameter := read_bits(r, residual_coding_method == .RICE ? 4 : 5) or_return
|
2024-06-15 01:29:48 +02:00
|
|
|
if (residual_coding_method == .RICE && rice_parameter == 15) ||
|
|
|
|
|
(residual_coding_method == .RICE2 && rice_parameter == 31) {
|
2024-06-11 22:57:04 +02:00
|
|
|
unencoded_bps := u8(read_bits(r, 5) or_return)
|
|
|
|
|
|
|
|
|
|
num_samples := 0
|
|
|
|
|
if partition_order == 0 {
|
|
|
|
|
num_samples = int(block_size - u16(predictor_order))
|
|
|
|
|
} else if i != 0 {
|
2024-06-14 03:58:36 +02:00
|
|
|
num_samples = int(block_size / u16(pow2(partition_order)))
|
2024-06-11 22:57:04 +02:00
|
|
|
} else {
|
2024-06-14 03:58:36 +02:00
|
|
|
num_samples = int((block_size / u16(pow2(partition_order))) - u16(predictor_order))
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if unencoded_bps == 0 {
|
2024-06-15 01:29:48 +02:00
|
|
|
// Since we initialize a slice to zeros we just have to skip over those zeroed samples in the slice.
|
|
|
|
|
sample_i += num_samples
|
2024-06-11 22:57:04 +02:00
|
|
|
continue lpc_partitions_loop
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for s in 0..<num_samples {
|
|
|
|
|
// NOTE: This is signed 2's complement so we check the MSB
|
2024-06-15 01:29:48 +02:00
|
|
|
residual_sample_value := i32(read_bits(r, uint(unencoded_bps)) or_return)
|
2024-06-14 00:18:03 +02:00
|
|
|
if residual_sample_value > 0 && (residual_sample_value >> (unencoded_bps - 1)) & 1 == 1 {
|
2024-06-14 03:58:36 +02:00
|
|
|
residual_sample_value = -(((~residual_sample_value) & (i32(pow2(unencoded_bps)) - 1)) + 1)
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Restore sample values using the predictor and the residual values
|
|
|
|
|
predictor_before_shift := 0
|
|
|
|
|
c := 0
|
|
|
|
|
#reverse for coefficient in coefficients {
|
|
|
|
|
predictor_before_shift += int(coefficient) * int(subframe_samples[sample_i + c])
|
|
|
|
|
c += 1
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
predictor := predictor_before_shift >> u8(coeff_bits_to_shift)
|
2024-06-11 22:57:04 +02:00
|
|
|
|
2024-06-14 00:18:03 +02:00
|
|
|
sample := i32(predictor + int(residual_sample_value))
|
2024-06-14 03:58:36 +02:00
|
|
|
subframe_samples[sample_i + int(predictor_order)] = sample
|
2024-06-11 22:57:04 +02:00
|
|
|
sample_i += 1
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
num_samples := 0
|
|
|
|
|
if partition_order == 0 {
|
|
|
|
|
num_samples = int(block_size - u16(predictor_order))
|
|
|
|
|
} else if i != 0 {
|
2024-06-14 03:58:36 +02:00
|
|
|
num_samples = int(block_size / u16(pow2(partition_order)))
|
2024-06-11 22:57:04 +02:00
|
|
|
} else {
|
2024-06-14 03:58:36 +02:00
|
|
|
num_samples = int((block_size / u16(pow2(partition_order))) - u16(predictor_order))
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Undo the RICE coding
|
|
|
|
|
//
|
2024-06-12 20:09:09 +02:00
|
|
|
for s in 0..<num_samples {
|
2024-06-11 22:57:04 +02:00
|
|
|
quotient := 0
|
|
|
|
|
// Read bits until we encounter a 1 (also called unary encoding). That's our quotient.
|
2024-06-19 04:08:54 +02:00
|
|
|
for (read_bits(r, 1) or_return) != 1 {
|
2024-06-11 22:57:04 +02:00
|
|
|
quotient += 1
|
|
|
|
|
}
|
2024-06-15 01:29:48 +02:00
|
|
|
remainder := int(read_bits(r, uint(rice_parameter)) or_return)
|
2024-06-11 22:57:04 +02:00
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
zigzag_encoded_value := quotient * pow2(rice_parameter) + remainder
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
// Unzigzag the residual sample values
|
2024-06-15 01:29:48 +02:00
|
|
|
residual_sample_value :=
|
|
|
|
|
zigzag_encoded_value % 2 == 0 ? zigzag_encoded_value / 2 : (zigzag_encoded_value + 1) / -2
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
// Restore sample values using the predictor and the residual values
|
|
|
|
|
predictor_before_shift := 0
|
|
|
|
|
c := 0
|
|
|
|
|
#reverse for coefficient in coefficients {
|
|
|
|
|
predictor_before_shift += int(coefficient) * int(subframe_samples[sample_i + c])
|
|
|
|
|
c += 1
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
predictor := predictor_before_shift >> u8(coeff_bits_to_shift)
|
2024-06-11 22:57:04 +02:00
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
sample := predictor + residual_sample_value
|
2024-06-14 03:58:36 +02:00
|
|
|
subframe_samples[sample_i + int(predictor_order)] = i32(sample)
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
sample_i += 1
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-15 01:29:48 +02:00
|
|
|
} else if (subframe_type >> 3) & 1 == 1 && low_3_bits <= 4 { // FIXED
|
2024-06-12 20:09:09 +02:00
|
|
|
sample_i := 0
|
2024-06-11 22:57:04 +02:00
|
|
|
predictor_order := low_3_bits
|
2024-06-15 01:29:48 +02:00
|
|
|
|
2024-06-12 20:09:09 +02:00
|
|
|
bits_to_read := int(bps) - wasted_bits
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
// NOTE: samples can be negative
|
2024-06-12 20:09:09 +02:00
|
|
|
for sample in 0..<predictor_order {
|
2024-06-15 01:29:48 +02:00
|
|
|
unencoded_warmup_sample := i32(read_bits(r, uint(bits_to_read)) or_return)
|
2024-06-14 00:18:03 +02:00
|
|
|
if unencoded_warmup_sample > 0 && (unencoded_warmup_sample >> (uint(bits_to_read) - 1)) & 1 == 1 {
|
2024-06-14 03:58:36 +02:00
|
|
|
unencoded_warmup_sample = -(((~unencoded_warmup_sample) & (i32(pow2(bits_to_read)) - 1)) + 1)
|
2024-06-12 20:09:09 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-14 03:58:36 +02:00
|
|
|
subframe_samples[sample] = unencoded_warmup_sample
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
residual_coding_method := ResidualCodingMethod(read_bits(r, 2) or_return)
|
|
|
|
|
if residual_coding_method != .RICE && residual_coding_method != .RICE2 {
|
2024-06-12 20:09:09 +02:00
|
|
|
return nil, .Invalid_Residual_Coding_Method
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
partition_order := read_bits(r, 4) or_return
|
2024-06-15 01:29:48 +02:00
|
|
|
partitions := pow2(partition_order)
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
fixed_partitions_loop: for i in 0..<partitions {
|
|
|
|
|
rice_parameter := read_bits(r, residual_coding_method == .RICE ? 4 : 5) or_return
|
2024-06-15 01:29:48 +02:00
|
|
|
if (residual_coding_method == .RICE && rice_parameter == 15) ||
|
|
|
|
|
(residual_coding_method == .RICE2 && rice_parameter == 31) {
|
2024-06-11 22:57:04 +02:00
|
|
|
unencoded_bps := u8(read_bits(r, 5) or_return)
|
|
|
|
|
|
|
|
|
|
num_samples := 0
|
|
|
|
|
if partition_order == 0 {
|
|
|
|
|
num_samples = int(block_size - u16(predictor_order))
|
|
|
|
|
} else if i != 0 {
|
2024-06-14 03:58:36 +02:00
|
|
|
num_samples = int(block_size / u16(pow2(partition_order)))
|
2024-06-11 22:57:04 +02:00
|
|
|
} else {
|
2024-06-14 03:58:36 +02:00
|
|
|
num_samples = int((block_size / u16(pow2(partition_order))) - u16(predictor_order))
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if unencoded_bps == 0 {
|
2024-06-15 01:29:48 +02:00
|
|
|
// Since we initialize a slice to zeros we just have to skip over those zeroed samples in the slice.
|
|
|
|
|
sample_i += num_samples
|
2024-06-11 22:57:04 +02:00
|
|
|
continue fixed_partitions_loop
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 20:09:09 +02:00
|
|
|
for s in 0..<num_samples {
|
2024-06-11 22:57:04 +02:00
|
|
|
// NOTE: This is signed 2's complement so we check the MSB
|
2024-06-15 01:29:48 +02:00
|
|
|
residual_sample_value := i8(read_bits(r, uint(unencoded_bps)) or_return)
|
2024-06-14 00:18:03 +02:00
|
|
|
if residual_sample_value > 0 && (residual_sample_value >> (unencoded_bps - 1)) & 1 == 1 {
|
2024-06-14 03:58:36 +02:00
|
|
|
residual_sample_value = -(((~residual_sample_value) & (i8(pow2(unencoded_bps)) - 1)) + 1)
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-12 20:09:09 +02:00
|
|
|
return nil, .Unencoded_BPS_For_Fixed_Subframe_Is_Unsupported
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
// TODO: calculate the actual sample value by using the residual AND the previous sample value
|
|
|
|
|
// TODO: add the samples to something ??
|
|
|
|
|
} else {
|
2024-06-12 20:09:09 +02:00
|
|
|
num_samples := 0
|
2024-06-11 22:57:04 +02:00
|
|
|
if partition_order == 0 {
|
2024-06-12 20:09:09 +02:00
|
|
|
num_samples = int(block_size - u16(predictor_order))
|
2024-06-11 22:57:04 +02:00
|
|
|
} else if i != 0 {
|
2024-06-14 03:58:36 +02:00
|
|
|
num_samples = int(block_size / u16(pow2(partition_order)))
|
2024-06-11 22:57:04 +02:00
|
|
|
} else {
|
2024-06-14 03:58:36 +02:00
|
|
|
num_samples = int((block_size / u16(pow2(partition_order))) - u16(predictor_order))
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Undo the RICE coding
|
|
|
|
|
//
|
2024-06-12 20:09:09 +02:00
|
|
|
for s in 0..<num_samples {
|
2024-06-11 22:57:04 +02:00
|
|
|
quotient := 0
|
|
|
|
|
// Read bits until we encounter a 1 (also called unary encoding). That's our quotient.
|
2024-06-19 04:08:54 +02:00
|
|
|
for (read_bits(r, 1) or_return) != 1 {
|
2024-06-11 22:57:04 +02:00
|
|
|
quotient += 1
|
|
|
|
|
}
|
2024-06-15 01:29:48 +02:00
|
|
|
remainder := int(read_bits(r, uint(rice_parameter)) or_return)
|
2024-06-11 22:57:04 +02:00
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
zigzag_encoded_value := quotient * pow2(rice_parameter) + remainder
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
// Unzigzag the residual sample values
|
2024-06-15 01:29:48 +02:00
|
|
|
residual_sample_value :=
|
|
|
|
|
zigzag_encoded_value % 2 == 0 ? zigzag_encoded_value / 2 : (zigzag_encoded_value + 1) / -2
|
2024-06-12 20:09:09 +02:00
|
|
|
|
|
|
|
|
sample_value := 0
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
// Restore sample values using the predictor and the residual values
|
2024-06-12 20:09:09 +02:00
|
|
|
if predictor_order == 0 {
|
|
|
|
|
sample_value = residual_sample_value
|
|
|
|
|
} else {
|
|
|
|
|
predictor := 0
|
|
|
|
|
c := 0
|
|
|
|
|
#reverse for coefficient in fixed_coefficients[predictor_order - 1] {
|
|
|
|
|
predictor += int(coefficient) * int(subframe_samples[sample_i + c])
|
|
|
|
|
c += 1
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-14 00:18:03 +02:00
|
|
|
sample_value = (predictor + residual_sample_value)
|
2024-06-12 20:09:09 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-14 03:58:36 +02:00
|
|
|
subframe_samples[sample_i + int(predictor_order)] = i32(sample_value)
|
2024-06-12 20:09:09 +02:00
|
|
|
|
|
|
|
|
sample_i += 1
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-14 00:18:03 +02:00
|
|
|
// The shifting has to happen after predicting all the sample values.
|
|
|
|
|
for &sample in subframe_samples {
|
|
|
|
|
sample <<= uint(wasted_bits)
|
|
|
|
|
}
|
2024-06-11 22:57:04 +02:00
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
return subframe_samples, nil
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
@(private)
|
|
|
|
|
correlate :: proc(subframes: [][]i32, channel_assignment: ChannelAssignment) {
|
2024-06-12 20:09:09 +02:00
|
|
|
// Undo Stereo Decorrelation
|
|
|
|
|
#partial switch channel_assignment {
|
|
|
|
|
case .STEREO_MID_SIDE:
|
|
|
|
|
for i in 0..<len(subframes[1]) {
|
2024-06-15 01:29:48 +02:00
|
|
|
// These MUST be cast to i64 because overflows can happen if we use i32 or u32
|
2024-06-14 00:18:03 +02:00
|
|
|
mid := i64(subframes[0][i]) << 1
|
|
|
|
|
side := i64(subframes[1][i])
|
2024-06-12 20:09:09 +02:00
|
|
|
// Since side is a signed integer here. We check for oddity by looking at the LSB instead of doing a check for 1 and -1
|
|
|
|
|
if side & 1 == 1 {
|
|
|
|
|
mid += 1
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-14 00:18:03 +02:00
|
|
|
left := i32((mid + side) >> 1)
|
2024-06-12 20:09:09 +02:00
|
|
|
right := (mid - side) >> 1
|
|
|
|
|
|
|
|
|
|
subframes[0][i] = left
|
2024-06-14 00:18:03 +02:00
|
|
|
subframes[1][i] = i32(right)
|
2024-06-12 20:09:09 +02:00
|
|
|
}
|
|
|
|
|
case .STEREO_SIDE_RIGHT:
|
|
|
|
|
for i in 0..<len(subframes[0]) {
|
|
|
|
|
side := subframes[0][i]
|
|
|
|
|
right := subframes[1][i]
|
|
|
|
|
subframes[0][i] = side + right
|
|
|
|
|
}
|
|
|
|
|
case .STEREO_LEFT_SIDE:
|
|
|
|
|
for i in 0..<len(subframes[1]) {
|
|
|
|
|
left := subframes[0][i]
|
|
|
|
|
side := subframes[1][i]
|
|
|
|
|
subframes[1][i] = left - side
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
@(private)
|
2026-09-11 15:12:53 +02:00
|
|
|
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
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
header := read_data(r, FlacHeader) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
if header.magic != FLAC_MAGIC {
|
2024-06-15 03:39:53 +02:00
|
|
|
return nil, .Invalid_Signature
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-27 23:57:57 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 22:57:04 +02:00
|
|
|
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
|
|
|
|
|
total_samples := header.streaminfo.sr_chan_bps_ts & 0xFFFFFFFFF
|
|
|
|
|
|
2024-06-15 01:29:48 +02:00
|
|
|
streaminfo_header := MetadataBlockHeader {
|
2024-06-11 22:57:04 +02:00
|
|
|
last_block = bool(header.streaminfo_header >> 31),
|
2024-06-15 01:29:48 +02:00
|
|
|
type = BlockType((header.streaminfo_header >> 24) & 0x7F),
|
|
|
|
|
length = u32(header.streaminfo_header & 0xFFFFFF),
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if streaminfo_header.type != .STREAMINFO {
|
2026-09-11 15:12:53 +02:00
|
|
|
return nil, .Missing_StreamInfo
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-15 03:39:53 +02:00
|
|
|
if flac == nil {
|
|
|
|
|
flac = new(Flac)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
flac.metadata.channels = u8(num_channel_minus_one + 1)
|
|
|
|
|
flac.metadata.sample_rate = u32(sample_rate)
|
|
|
|
|
flac.metadata.bits_per_sample = u8(bits_per_sample_minus_one + 1)
|
|
|
|
|
flac.metadata.total_samples = u64(total_samples)
|
|
|
|
|
flac.metadata.expected_md5 = header.streaminfo.md5
|
|
|
|
|
|
|
|
|
|
pictures := make([dynamic]Picture, 0, 2, allocator)
|
2026-09-11 15:12:53 +02:00
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-11 22:57:04 +02:00
|
|
|
last_block := streaminfo_header.last_block
|
|
|
|
|
for !last_block {
|
2024-06-19 04:08:54 +02:00
|
|
|
md_block_hdr := read_data(r, u32be) or_return
|
2024-06-15 01:29:48 +02:00
|
|
|
metadata_block_header := MetadataBlockHeader {
|
2024-06-11 22:57:04 +02:00
|
|
|
last_block = bool(md_block_hdr >> 31),
|
2024-06-15 01:29:48 +02:00
|
|
|
type = BlockType((md_block_hdr >> 24) & 0x7F),
|
|
|
|
|
length = u32(md_block_hdr & 0xFFFFFF),
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-09-24 03:50:34 +02:00
|
|
|
last_block = metadata_block_header.last_block
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
switch metadata_block_header.type {
|
|
|
|
|
case .STREAMINFO:
|
|
|
|
|
// Do nothing, we already parse the mandatory streaminfo metadata
|
|
|
|
|
case .PADDING:
|
2024-09-24 06:36:33 +02:00
|
|
|
reader := cast(^bufio.Reader)r.data
|
|
|
|
|
skipped := bufio.reader_discard(reader, int(metadata_block_header.length)) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
case .APPLICATION:
|
|
|
|
|
//app_id := read_slice(&r, 4) or_return
|
|
|
|
|
//fmt.println("Found application with ID:", string(app_id))
|
2024-06-14 00:18:03 +02:00
|
|
|
//fmt.printfln("Skipping %d bytes of application metadata block", metadata_block_header.length)
|
2024-06-11 22:57:04 +02:00
|
|
|
// TODO: handle the application data
|
2024-09-24 06:36:33 +02:00
|
|
|
reader := cast(^bufio.Reader)r.data
|
|
|
|
|
skipped := bufio.reader_discard(reader, int(metadata_block_header.length)) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
case .SEEKTABLE:
|
|
|
|
|
seekpoints := metadata_block_header.length / 18
|
2024-06-14 00:18:03 +02:00
|
|
|
//fmt.printfln("we have %d seekpoints", seekpoints)
|
2024-06-19 04:08:54 +02:00
|
|
|
actual_size := 0
|
2024-06-11 22:57:04 +02:00
|
|
|
for i in 0..<seekpoints {
|
2024-06-19 04:08:54 +02:00
|
|
|
_ = read_data(r, Seekpoint) or_return
|
|
|
|
|
actual_size += size_of(Seekpoint)
|
2024-06-14 00:18:03 +02:00
|
|
|
//fmt.println(seekpoint)
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
2024-06-19 04:08:54 +02:00
|
|
|
|
|
|
|
|
if metadata_block_header.length != u32(actual_size) {
|
|
|
|
|
fmt.eprintln("Seektable metadata header length mismatch!")
|
|
|
|
|
fmt.eprintfln("Expected size: %d, Got %d", metadata_block_header.length, actual_size)
|
|
|
|
|
}
|
2024-06-11 22:57:04 +02:00
|
|
|
case .VORBIS_COMMENT:
|
2026-09-11 15:12:53 +02:00
|
|
|
// 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 = ""
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
vendor_name_len := read_data(r, u32le) or_return
|
|
|
|
|
vendor_name_slice := make([]byte, vendor_name_len)
|
2026-09-11 15:12:53 +02:00
|
|
|
flac.metadata.vendor_name = string(vendor_name_slice)
|
2024-06-19 04:08:54 +02:00
|
|
|
read_slice(r, vendor_name_slice) or_return
|
|
|
|
|
user_comment_list_len := read_data(r, u32le) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
|
|
|
|
|
actual_size := 4 + vendor_name_len + 4
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
comments := make([]string, user_comment_list_len)
|
2026-09-11 15:12:53 +02:00
|
|
|
flac.metadata.vorbis_comments = comments
|
2024-06-11 22:57:04 +02:00
|
|
|
for i in 0..<user_comment_list_len {
|
2024-06-19 04:08:54 +02:00
|
|
|
comment_len := read_data(r, u32le) or_return
|
|
|
|
|
comment_slice := make([]byte, comment_len)
|
2026-09-11 15:12:53 +02:00
|
|
|
comments[i] = string(comment_slice)
|
2024-06-19 04:08:54 +02:00
|
|
|
read_slice(r, comment_slice) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
actual_size += 4 + comment_len
|
|
|
|
|
}
|
|
|
|
|
if metadata_block_header.length != u32(actual_size) {
|
2024-06-19 04:08:54 +02:00
|
|
|
fmt.eprintln("Vorbis metadata header length mismatch!")
|
|
|
|
|
fmt.eprintfln("Expected size: %d, Got %d", metadata_block_header.length, actual_size)
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
// TODO: framing bit??
|
|
|
|
|
case .CUESHEET:
|
|
|
|
|
// TODO: save this to a struct
|
|
|
|
|
// + there's some CD-DA stuff that we need to consider I think
|
2024-06-19 04:08:54 +02:00
|
|
|
cuesheet := read_data(r, CueSheet) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
if cuesheet.num_tracks < 1 {
|
|
|
|
|
// TODO: should this be a warning instead of an error that halts the decoding?
|
2026-09-11 15:12:53 +02:00
|
|
|
return nil, .Missing_Lead_Out_Track
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
2024-06-14 00:18:03 +02:00
|
|
|
//fmt.println(cuesheet)
|
2024-06-11 22:57:04 +02:00
|
|
|
for i in 0..<cuesheet.num_tracks {
|
2024-06-19 04:08:54 +02:00
|
|
|
track := read_data(r, CueSheetTrack) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
for i in 0..<track.num_track_index_points {
|
2024-06-19 04:08:54 +02:00
|
|
|
_ = read_data(r, CueSheetTrackIndex) or_return
|
2024-06-14 00:18:03 +02:00
|
|
|
//fmt.println(track_index)
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
2024-06-14 00:18:03 +02:00
|
|
|
//fmt.println(track)
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
case .PICTURE:
|
2024-06-19 04:08:54 +02:00
|
|
|
pic_type := PictureType(read_data(r, u32be) or_return)
|
|
|
|
|
mime_len := read_data(r, u32be) or_return
|
2026-09-11 15:12:53 +02:00
|
|
|
// These three are only reachable through `pictures` once the
|
|
|
|
|
// append below runs, so they need their own unwind until then.
|
2024-06-19 04:08:54 +02:00
|
|
|
mimetype_slice := make([]byte, mime_len)
|
2026-09-11 15:12:53 +02:00
|
|
|
defer if err != nil {delete(mimetype_slice)}
|
2024-06-19 04:08:54 +02:00
|
|
|
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)
|
2026-09-11 15:12:53 +02:00
|
|
|
defer if err != nil {delete(desc_slice)}
|
2024-06-19 04:08:54 +02:00
|
|
|
read_slice(r, desc_slice) or_return
|
|
|
|
|
desc := string(desc_slice)
|
|
|
|
|
metadata := read_data(r, PictureMetadata) or_return
|
|
|
|
|
data := make([]byte, metadata.data_len)
|
2026-09-11 15:12:53 +02:00
|
|
|
defer if err != nil {delete(data)}
|
2024-06-19 04:08:54 +02:00
|
|
|
read_slice(r, data) or_return
|
2024-06-15 03:39:53 +02:00
|
|
|
picture := Picture{
|
|
|
|
|
type = pic_type,
|
|
|
|
|
mimetype = mimetype,
|
|
|
|
|
description = desc,
|
|
|
|
|
width = u32(metadata.width),
|
|
|
|
|
height = u32(metadata.height),
|
|
|
|
|
depth = u32(metadata.depth),
|
|
|
|
|
num_colors = u32(metadata.num_colors),
|
|
|
|
|
data = data,
|
|
|
|
|
}
|
|
|
|
|
append(&pictures, picture)
|
2024-06-11 22:57:04 +02:00
|
|
|
case .INVALID:
|
|
|
|
|
fmt.eprintln("Invalid block type")
|
2024-09-24 06:36:33 +02:00
|
|
|
reader := cast(^bufio.Reader)r.data
|
|
|
|
|
skipped := bufio.reader_discard(reader, int(metadata_block_header.length)) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
case:
|
|
|
|
|
fmt.printfln("Skipping %d bytes of Unknown metadata block", metadata_block_header.length)
|
2024-09-24 06:36:33 +02:00
|
|
|
reader := cast(^bufio.Reader)r.data
|
|
|
|
|
skipped := bufio.reader_discard(reader, int(metadata_block_header.length)) or_return
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
flac.metadata.pictures = pictures[:]
|
|
|
|
|
|
|
|
|
|
return flac, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-27 23:57:57 +02:00
|
|
|
decode_next_frame :: proc(r: ^Reader, flac: ^Flac, allocator := context.allocator) -> (frame: Frame, err: Error) {
|
2024-06-19 04:08:54 +02:00
|
|
|
//
|
|
|
|
|
// Frame Header
|
|
|
|
|
//
|
2024-06-20 05:00:35 +02:00
|
|
|
context.allocator = allocator
|
2024-06-19 04:08:54 +02:00
|
|
|
tee_r: io.Tee_Reader
|
2024-06-20 05:00:35 +02:00
|
|
|
|
|
|
|
|
crc_buffer: Buffer
|
2024-07-25 04:07:53 +02:00
|
|
|
buffer_init_allocator(&crc_buffer, 0, 128)
|
2024-09-24 03:50:34 +02:00
|
|
|
defer buffer_destroy(&crc_buffer)
|
2024-06-20 05:00:35 +02:00
|
|
|
|
|
|
|
|
crc := buffer_to_stream(&crc_buffer)
|
2024-06-19 04:08:54 +02:00
|
|
|
r := &Reader{
|
2024-09-24 03:50:34 +02:00
|
|
|
// TODO: tee reader's docs say we should call io.destroy() when done with it. But the tee reader
|
|
|
|
|
// doesn't implement the .Destroy method so it does absolutely nothing ?? Should ask about this in the discord.
|
2024-06-19 04:08:54 +02:00
|
|
|
r = io.tee_reader_init(&tee_r, r, crc),
|
|
|
|
|
buf = r.buf,
|
|
|
|
|
x = r.x,
|
|
|
|
|
n = r.n,
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
data := read_data(r, u16be) or_return
|
2024-06-15 03:39:53 +02:00
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-27 23:57:57 +02:00
|
|
|
// 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
|
|
|
|
|
//
|
2024-06-19 04:08:54 +02:00
|
|
|
//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.
|
|
|
|
|
|
2024-06-20 05:00:35 +02:00
|
|
|
crc8_bytes := buffer_to_bytes(&crc_buffer)
|
2024-06-19 04:08:54 +02:00
|
|
|
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..<frame_channels {
|
|
|
|
|
frame_bps := bps
|
|
|
|
|
// For side channels we increase the bps by 1
|
|
|
|
|
// Left, mid and right channels don't need an extra bit.
|
|
|
|
|
// ref: https://github.com/ietf-wg-cellar/flac-specification/blob/master/rfc_backmatter.md#first-audio-frame
|
|
|
|
|
if (channel_assignment == .STEREO_SIDE_RIGHT && i == 0) ||
|
|
|
|
|
((channel_assignment == .STEREO_LEFT_SIDE || channel_assignment == .STEREO_MID_SIDE) && i == 1) {
|
|
|
|
|
frame_bps += 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subframes[i] = decode_subframe(r, frame_bps, auto_cast actual_block_size) or_return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
align_to_byte(r)
|
|
|
|
|
|
|
|
|
|
correlate(subframes, channel_assignment)
|
|
|
|
|
|
2025-01-27 23:57:57 +02:00
|
|
|
clear(&flac.samples)
|
2024-09-24 03:50:34 +02:00
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
// Write the samples interleaved.
|
|
|
|
|
for i in 0..<len(subframes[0]) {
|
2024-09-24 03:50:34 +02:00
|
|
|
for subframe in subframes {
|
2024-06-19 04:08:54 +02:00
|
|
|
sample := subframe[i]
|
2024-09-24 03:50:34 +02:00
|
|
|
append(&flac.samples, sample)
|
2024-06-19 04:08:54 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for subframe in subframes {
|
|
|
|
|
delete(subframe)
|
2024-06-11 22:57:04 +02:00
|
|
|
}
|
|
|
|
|
|
2025-01-27 23:57:57 +02:00
|
|
|
frame.samples = flac.samples[:]
|
2024-06-19 04:08:54 +02:00
|
|
|
frame.channels = frame_channels
|
|
|
|
|
frame.sample_rate = actual_sample_rate_in_Hz
|
|
|
|
|
|
2024-06-11 22:57:04 +02:00
|
|
|
//
|
2024-06-19 04:08:54 +02:00
|
|
|
// Frame Footer
|
2024-06-11 22:57:04 +02:00
|
|
|
//
|
2024-06-20 05:00:35 +02:00
|
|
|
crc16_bytes := buffer_to_bytes(&crc_buffer)
|
2024-06-19 04:08:54 +02:00
|
|
|
calculated_frame_crc := calculate_crc16(crc16_bytes)
|
2024-09-24 03:50:34 +02:00
|
|
|
frame_crc := u16(read_data(r, u16be) or_return)
|
2024-06-19 04:08:54 +02:00
|
|
|
|
|
|
|
|
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 frame, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-24 03:50:34 +02:00
|
|
|
// `md5hash` updates the MD5 context with the hash of the provided samples.
|
|
|
|
|
// Should be called for every decoded frame with its BPS and samples.
|
2024-06-19 04:08:54 +02:00
|
|
|
md5hash :: proc(md5_ctx: ^md5.Context, bps: u8, samples: []i32) {
|
2024-09-24 03:50:34 +02:00
|
|
|
assert(1 <= bps && bps <= 32)
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
for sample in samples {
|
|
|
|
|
switch bps {
|
|
|
|
|
case 1..=8:
|
|
|
|
|
md5.update(md5_ctx, {u8(sample)})
|
|
|
|
|
case 9..=16:
|
|
|
|
|
first_byte := u8(sample & 0xFF)
|
|
|
|
|
second_byte := u8((sample >> 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:
|
2024-09-24 03:50:34 +02:00
|
|
|
unreachable()
|
2024-06-19 04:08:54 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-24 03:50:34 +02:00
|
|
|
// `md5sum` writes the final MD5 checksum to the `flac` struct's `calculated_md5` field
|
|
|
|
|
// and returns a MD5_Mismatch error in the case of a mismatch or nil.
|
2024-06-19 04:08:54 +02:00
|
|
|
md5sum :: proc(md5_ctx: ^md5.Context, flac: ^Flac) -> Error {
|
2024-06-20 05:00:35 +02:00
|
|
|
calculated_md5 := [16]byte{}
|
2024-06-19 04:08:54 +02:00
|
|
|
md5.final(md5_ctx, calculated_md5[:])
|
2024-06-11 22:57:04 +02:00
|
|
|
|
2024-06-15 03:39:53 +02:00
|
|
|
flac.metadata.calculated_md5 = calculated_md5
|
|
|
|
|
|
2024-06-19 04:08:54 +02:00
|
|
|
if mem.compare(calculated_md5[:], flac.metadata.expected_md5[:]) != 0 {
|
|
|
|
|
return .MD5_Mismatch
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-24 06:36:33 +02:00
|
|
|
// `load_from_file` initializes a buffered reader for the file and reads all the metadata blocks.
|
2026-09-11 01:25:54 +02:00
|
|
|
// Calls to `decode_next_frame()` MUST be made to decode the audio data.
|
2024-06-19 04:08:54 +02:00
|
|
|
// This is the recommended way to read most FLAC files as it has a minimal memory overhead.
|
2024-09-24 06:36:33 +02:00
|
|
|
load_from_file :: proc(filename: string, allocator := context.allocator) -> (flac: ^Flac, r: ^Reader, err: Error) {
|
2024-06-19 04:08:54 +02:00
|
|
|
context.allocator = allocator
|
|
|
|
|
|
2026-09-11 01:25:54 +02:00
|
|
|
file, open_err := os.open(filename)
|
2025-01-27 23:57:57 +02:00
|
|
|
if open_err != nil {
|
2024-06-19 04:08:54 +02:00
|
|
|
fmt.eprintln(open_err)
|
2026-09-11 15:12:53 +02:00
|
|
|
return nil, nil, .Unable_To_Read_File
|
2024-06-19 04:08:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
br := new(bufio.Reader)
|
2026-09-11 01:25:54 +02:00
|
|
|
bufio.reader_init(br, os.to_stream(file))
|
2024-06-19 04:08:54 +02:00
|
|
|
|
2026-09-11 15:12:53 +02:00
|
|
|
reader := new(Reader)
|
|
|
|
|
reader.r = bufio.reader_to_stream(br)
|
|
|
|
|
reader.buf = 0
|
|
|
|
|
reader.x = 0
|
|
|
|
|
reader.n = 0
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
|
}
|
2024-06-19 04:08:54 +02:00
|
|
|
|
2026-09-11 15:12:53 +02:00
|
|
|
flac = read_metadata(reader) or_return
|
2024-06-19 04:08:54 +02:00
|
|
|
|
2026-09-11 15:12:53 +02:00
|
|
|
return flac, reader, nil
|
2024-06-19 04:08:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* `destroy` cleans up and frees the memory allocated by all 3 loading functions.
|
2024-09-24 06:36:33 +02:00
|
|
|
The returned reader from `load_from_file` MUST be passed to `destroy` in order to close the file handle.
|
2026-09-11 15:12:53 +02:00
|
|
|
Both arguments may be nil, so it is safe to defer immediately after a load.
|
2024-06-19 04:08:54 +02:00
|
|
|
---
|
|
|
|
|
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
|
|
|
|
|
|
2026-09-11 15:12:53 +02:00
|
|
|
// 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)
|
|
|
|
|
delete(picture.data)
|
|
|
|
|
delete(picture.description)
|
|
|
|
|
}
|
|
|
|
|
delete(flac.metadata.pictures)
|
2024-06-19 04:08:54 +02:00
|
|
|
|
2026-09-11 15:12:53 +02:00
|
|
|
for comment in flac.metadata.vorbis_comments {
|
|
|
|
|
delete(comment)
|
|
|
|
|
}
|
|
|
|
|
delete(flac.metadata.vorbis_comments)
|
|
|
|
|
delete(flac.metadata.vendor_name)
|
2024-06-19 04:08:54 +02:00
|
|
|
|
2026-09-11 15:12:53 +02:00
|
|
|
free(flac)
|
|
|
|
|
}
|
2024-06-19 04:08:54 +02:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
}
|
2024-06-15 03:39:53 +02:00
|
|
|
}
|