Files

994 lines
37 KiB
Odin
Raw Permalink Normal View History

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"
import "core:mem"
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,
Negative_Coefficient_Bits_To_Shift,
2024-06-11 22:57:04 +02:00
}
/*
Option:
2024-06-19 04:08:54 +02:00
`.only_return_metadata`
Returns all metadata of a FLAC stream without decoding the audio data.
*/
2024-09-24 03:50:34 +02:00
Option :: enum u8 {
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 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,
}
Flac :: struct {
2024-06-15 03:39:53 +02:00
metadata: FlacMetadata,
// This holds the samples of the current frame.
// 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,
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,
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,
// A slice view of `Flac`'s `samples` field.
2024-09-24 03:50:34 +02:00
// Contains the frame's decoded samples.
// 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
}
if subframe_type == 0 { // CONSTANT
2024-06-11 22:57:04 +02:00
bits_to_read := int(bps) - wasted_bits
sample := i32(read_bits(r, uint(bits_to_read)) or_return)
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)
}
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 {
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
// negative samples ??
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
}
} 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
bits_to_read := int(bps) - wasted_bits
for s in 0..<predictor_order {
2024-06-11 22:57:04 +02:00
// NOTE: samples are signed so we do 2's complement
unencoded_warmup_sample := i32(read_bits(r, uint(bits_to_read)) or_return)
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 {
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
// 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 {
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 {
unencoded_coefficient := i32(read_bits(r, uint(coeff_precision)) or_return)
2024-06-11 22:57:04 +02:00
// Ditto
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 {
return nil, .Invalid_Residual_Coding_Method
2024-06-11 22:57:04 +02:00
}
partition_order := read_bits(r, 4) or_return
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
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 {
// 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
residual_sample_value := i32(read_bits(r, uint(unencoded_bps)) or_return)
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
}
predictor := predictor_before_shift >> u8(coeff_bits_to_shift)
2024-06-11 22:57:04 +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
//
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
}
remainder := int(read_bits(r, uint(rice_parameter)) or_return)
2024-06-11 22:57:04 +02:00
zigzag_encoded_value := quotient * pow2(rice_parameter) + remainder
2024-06-11 22:57:04 +02:00
// Unzigzag the residual sample values
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
}
predictor := predictor_before_shift >> u8(coeff_bits_to_shift)
2024-06-11 22:57:04 +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
}
}
}
} else if (subframe_type >> 3) & 1 == 1 && low_3_bits <= 4 { // FIXED
sample_i := 0
2024-06-11 22:57:04 +02:00
predictor_order := low_3_bits
bits_to_read := int(bps) - wasted_bits
2024-06-11 22:57:04 +02:00
// NOTE: samples can be negative
for sample in 0..<predictor_order {
unencoded_warmup_sample := i32(read_bits(r, uint(bits_to_read)) or_return)
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-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 {
return nil, .Invalid_Residual_Coding_Method
2024-06-11 22:57:04 +02:00
}
partition_order := read_bits(r, 4) or_return
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
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 {
// 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
}
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
residual_sample_value := i8(read_bits(r, uint(unencoded_bps)) or_return)
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
}
}
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 {
num_samples := 0
2024-06-11 22:57:04 +02:00
if partition_order == 0 {
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
//
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
}
remainder := int(read_bits(r, uint(rice_parameter)) or_return)
2024-06-11 22:57:04 +02:00
zigzag_encoded_value := quotient * pow2(rice_parameter) + remainder
2024-06-11 22:57:04 +02:00
// Unzigzag the residual sample values
residual_sample_value :=
zigzag_encoded_value % 2 == 0 ? zigzag_encoded_value / 2 : (zigzag_encoded_value + 1) / -2
sample_value := 0
2024-06-11 22:57:04 +02:00
// Restore sample values using the predictor and the residual values
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
}
sample_value = (predictor + residual_sample_value)
}
2024-06-14 03:58:36 +02:00
subframe_samples[sample_i + int(predictor_order)] = i32(sample_value)
sample_i += 1
2024-06-11 22:57:04 +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
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) {
// Undo Stereo Decorrelation
#partial switch channel_assignment {
case .STEREO_MID_SIDE:
for i in 0..<len(subframes[1]) {
// These MUST be cast to i64 because overflows can happen if we use i32 or u32
mid := i64(subframes[0][i]) << 1
side := i64(subframes[1][i])
// 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
}
left := i32((mid + side) >> 1)
right := (mid - side) >> 1
subframes[0][i] = left
subframes[1][i] = i32(right)
}
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
}
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
streaminfo_header := MetadataBlockHeader {
2024-06-11 22:57:04 +02:00
last_block = bool(header.streaminfo_header >> 31),
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
metadata_block_header := MetadataBlockHeader {
2024-06-11 22:57:04 +02:00
last_block = bool(md_block_hdr >> 31),
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))
//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
//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)
//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
}
//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
//fmt.println(track_index)
2024-06-11 22:57:04 +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
}
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
//
context.allocator = allocator
2024-06-19 04:08:54 +02:00
tee_r: io.Tee_Reader
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)
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
}
// 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.
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)
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
}
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
//
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 {
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.
// 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
file, open_err := os.open(filename)
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)
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
}