// moonflac-encoder — pure MoonBit FLAC encoder.
// Copyright 2026 Sakuraqwq1
// Licensed under the Apache License, Version 2.0.
// Encoder errors are explicit so callers can tell "your input is unsupported"
// apart from "your input is malformed".
///|
/// Why an encode request was rejected.
pub suberror EncodeError {
/// No samples were supplied.
Empty
/// The channel count is outside the 1..8 range FLAC allows.
BadChannels(Int32)
/// The bit depth is outside the 4..32 range FLAC allows.
BadBits(Int32)
/// The sample rate is not positive.
BadSampleRate(Int32)
/// The sample count is not a multiple of the channel count.
NotWholeFrames(Int32, Int32)
}
///|
/// Encode interleaved signed PCM as a native FLAC stream.
///
/// `samples` holds `channels` interleaved values per frame, each already scaled
/// to `bits_per_sample` two's complement range. The result is a complete
/// `.flac` file: `fLaC` marker, a STREAMINFO block carrying the PCM digest, and
/// fixed blocksize frames with per-frame CRC-8 and CRC-16.
pub fn encode(
samples : Array[Int64],
sample_rate : Int32,
channels : Int32,
bits_per_sample : Int32,
) -> Bytes raise EncodeError {
if channels < 1 || channels > 8 {
raise BadChannels(channels)
}
if bits_per_sample < 4 || bits_per_sample > 32 {
raise BadBits(bits_per_sample)
}
if sample_rate <= 0 {
raise BadSampleRate(sample_rate)
}
let length = samples.length()
if length == 0 {
raise Empty
}
if length % channels != 0 {
raise NotWholeFrames(length, channels)
}
let total_frames = length / channels
let block_size = block_size_for(total_frames)
let frame_count = (total_frames + block_size - 1) / block_size
let shortest = if frame_count == 1 {
total_frames
} else {
let tail = total_frames % block_size
if tail == 0 {
block_size
} else {
tail
}
}
let digest = @primitives.md5(raw_pcm(samples, bits_per_sample))
let out = @buffer.Buffer::Buffer(size_hint=total_frames * channels)
out.write_string_utf8("fLaC")
out.write_byte(0x80)
out.write_byte(0x00)
out.write_byte(0x00)
out.write_byte(34)
let info = @primitives.BitWriter::new()
info.write_bits(shortest.to_int64(), 16)
info.write_bits(block_size.to_int64(), 16)
info.write_bits(0L, 24)
info.write_bits(0L, 24)
info.write_bits(sample_rate.to_int64(), 20)
info.write_bits((channels - 1).to_int64(), 3)
info.write_bits((bits_per_sample - 1).to_int64(), 5)
info.write_bits(total_frames.to_int64(), 36)
let info_bytes = info.finish()
for index = 0; index < info_bytes.length(); index = index + 1 {
out.write_byte(info_bytes.at(index))
}
for index = 0; index < digest.length(); index = index + 1 {
out.write_byte(digest.at(index))
}
let channel_code = channels - 1
let mut frame = 0
let mut offset = 0
while offset < total_frames {
let count = if total_frames - offset < block_size {
total_frames - offset
} else {
block_size
}
let bytes = build_frame(
samples, channel_code, channels, offset, count, frame, sample_rate, bits_per_sample,
)
for index = 0; index < bytes.length(); index = index + 1 {
out.write_byte(bytes.at(index))
}
offset = offset + count
frame = frame + 1
}
out.to_bytes()
}
///|
/// Block size of every frame but possibly the last.
const BLOCK_SIZE : Int32 = 4096
///|
/// The block size used for every frame but possibly the last.
///
/// 4096 is the value the reference encoder defaults to and it keeps the frame
/// headers short because it is one of the sizes with a dedicated code.
fn block_size_for(total_frames : Int32) -> Int32 {
if total_frames < 4096 {
total_frames
} else {
4096
}
}
///|
/// Serialise `samples` as the byte string FLAC's STREAMINFO digest covers:
/// interleaved, signed, little endian, `ceil(bits/8)` bytes per value.
fn raw_pcm(samples : Array[Int64], bits : Int32) -> Bytes {
let width = (bits + 7) / 8
let out = @buffer.Buffer::Buffer(size_hint=samples.length() * width)
for index = 0; index < samples.length(); index = index + 1 {
let value = samples[index]
for byte = 0; byte < width; byte = byte + 1 {
out.write_byte(((value >> (byte * 8)) & 0xFFL).to_byte())
}
}
out.to_bytes()
}
///|
/// Block size code from the FLAC frame header table, plus the flag that says a
/// 16 bit size field has to follow.
fn block_size_code(count : Int32) -> (Int32, Bool) {
if count == 192 {
return (1, false)
}
if count == 576 {
return (2, false)
}
if count == 1152 {
return (3, false)
}
if count == 2304 {
return (4, false)
}
if count == 4608 {
return (5, false)
}
let mut power = 256
let mut code = 8
while power < 32768 {
if power == count {
return (code, false)
}
power = power * 2
code = code + 1
}
if count == 32768 {
return (15, false)
}
(7, true)
}
///|
/// Sample rate code from the FLAC frame header table. Zero means "take it from
/// STREAMINFO", which is valid for any rate not listed here.
fn sample_rate_code(rate : Int32) -> Int32 {
if rate == 88200 {
1
} else if rate == 176400 {
2
} else if rate == 192000 {
3
} else if rate == 8000 {
4
} else if rate == 16000 {
5
} else if rate == 22050 {
6
} else if rate == 24000 {
7
} else if rate == 32000 {
8
} else if rate == 44100 {
9
} else if rate == 48000 {
10
} else if rate == 96000 {
11
} else {
0
}
}
///|
/// Bit depth code from the FLAC frame header table; zero defers to STREAMINFO.
fn sample_size_code(bits : Int32) -> Int32 {
if bits == 8 {
1
} else if bits == 12 {
2
} else if bits == 16 {
4
} else if bits == 20 {
5
} else if bits == 24 {
6
} else {
0
}
}
///|
/// FLAC's UTF-8 style coding for frame or sample numbers (7 data bits per byte).
fn write_utf8_number(writer : @primitives.BitWriter, value : Int64) -> Unit {
let (length, prefix) = if value < 0x80L {
(1, 0x00)
} else if value < 0x800L {
(2, 0xC0)
} else if value < 0x10000L {
(3, 0xE0)
} else if value < 0x200000L {
(4, 0xF0)
} else if value < 0x4000000L {
(5, 0xF8)
} else if value < 0x80000000L {
(6, 0xFC)
} else {
(7, 0xFE)
}
let mut remaining = value
let bytes = Array::make(length, 0)
let mut index = length - 1
while index > 0 {
bytes[index] = 0x80 | (remaining & 0x3F).to_int()
remaining = remaining >> 6
index = index - 1
}
bytes[0] = prefix | remaining.to_int()
for byte in bytes {
writer.write_bits(byte.to_int64(), 8)
}
}
///|
/// Assemble one frame: header with CRC-8, the subframes, and the CRC-16 footer.
fn build_frame(
samples : Array[Int64],
channel_code : Int32,
channels : Int32,
offset : Int32,
count : Int32,
number : Int32,
sample_rate : Int32,
bits : Int32,
) -> Bytes {
let planes : Array[Array[Int64]] = []
for channel = 0; channel < channels; channel = channel + 1 {
let plane = Array::make(count, 0L)
for index = 0; index < count; index = index + 1 {
plane[index] = samples[(offset + index) * channels + channel]
}
planes.push(plane)
}
let mut final_code = channel_code
let mut payload = planes
let mut depths = Array::make(channels, bits)
// Each plane is planned exactly once and the written subframes reuse that
// plan, so nothing is costed or searched twice.
let mut plans : Array[Array[Int64]] = []
let costs : Array[Int64] = []
for channel = 0; channel < channels; channel = channel + 1 {
let plan = Array::make(35, 0L)
costs.push(plan_plane_subframe(planes[channel], bits, plan))
plans.push(plan)
}
if channels == 2 {
let left = planes[0]
let right = planes[1]
let side = Array::make(count, 0L)
let mid = Array::make(count, 0L)
for index = 0; index < count; index = index + 1 {
side[index] = left[index] - right[index]
mid[index] = (left[index] + right[index]) >> 1
}
let plan_left = plans[0]
let plan_right = plans[1]
let plan_side = Array::make(35, 0L)
let cost_side = plan_plane_subframe(side, bits + 1, plan_side)
let plan_mid = Array::make(35, 0L)
let cost_mid = plan_plane_subframe(mid, bits, plan_mid)
let independent = costs[0] + costs[1]
let left_side = costs[0] + cost_side
let right_side = cost_side + costs[1]
let mid_side = cost_mid + cost_side
let mut best = independent
if left_side < best {
best = left_side
final_code = 8
payload = [left, side]
depths = [bits, bits + 1]
plans = [plan_left, plan_side]
}
if right_side < best {
best = right_side
final_code = 9
payload = [side, right]
depths = [bits + 1, bits]
plans = [plan_side, plan_right]
}
if mid_side < best {
final_code = 10
payload = [mid, side]
depths = [bits, bits + 1]
plans = [plan_mid, plan_side]
}
}
let (size_code, wide_size) = block_size_code(count)
let rate_code = sample_rate_code(sample_rate)
let depth_code = sample_size_code(bits)
let header = @primitives.BitWriter::new()
header.write_bits(0x3FFEL, 14)
header.write_bit(0)
header.write_bit(0)
header.write_bits(size_code.to_int64(), 4)
header.write_bits(rate_code.to_int64(), 4)
header.write_bits(final_code.to_int64(), 4)
header.write_bits(depth_code.to_int64(), 3)
header.write_bit(0)
write_utf8_number(header, number.to_int64())
if wide_size {
header.write_bits((count - 1).to_int64(), 16)
}
if rate_code == 12 {
header.write_bits((sample_rate / 1000).to_int64(), 8)
} else if rate_code == 13 {
header.write_bits(sample_rate.to_int64(), 16)
} else if rate_code == 14 {
header.write_bits((sample_rate / 10).to_int64(), 16)
}
let header_bytes = header.finish()
let frame = @primitives.BitWriter::new()
frame.write_bytes(header_bytes)
frame.write_bits(@primitives.crc8(header_bytes).to_int64(), 8)
for channel = 0; channel < channels; channel = channel + 1 {
write_plane_subframe(
frame,
payload[channel],
depths[channel],
plans[channel],
)
}
let body = frame.finish()
let checksum = @primitives.crc16(body)
let out = @buffer.Buffer::Buffer(size_hint=body.length() + 2)
for index = 0; index < body.length(); index = index + 1 {
out.write_byte(body.at(index))
}
out.write_byte(((checksum >> 8) & 0xFF).to_byte())
out.write_byte((checksum & 0xFF).to_byte())
out.to_bytes()
}
///|
/// Write the subframe that `plan` chose for one channel.
///
/// The type code, the LPC coefficients and the Rice partition order all come
/// from the plan, so nothing is searched a second time here.
fn write_plane_subframe(
frame : @primitives.BitWriter,
plane : Array[Int64],
bits : Int32,
plan : Array[Int64],
) -> Unit {
let count = plane.length()
let code = plan[33].to_int()
frame.write_bit(0)
frame.write_bits(code.to_int64(), 6)
frame.write_bit(0)
if code == 0 {
frame.write_signed(plane[0], bits)
return
}
if code == 1 {
for value in plane {
frame.write_signed(value, bits)
}
return
}
if code < 32 {
let order = code - 8
for index = 0; index < order; index = index + 1 {
frame.write_signed(plane[index], bits)
}
let residual = fixed_residual(plane, order)
let partition = plan[0].to_int()
frame.write_bits(0L, 2)
frame.write_bits(partition.to_int64(), 4)
write_partitions(frame, residual, count, order, partition)
return
}
let order = code - 32 + 1
for index = 0; index < order; index = index + 1 {
frame.write_signed(plane[index], bits)
}
frame.write_bits(14L, 4)
let shift = plan[34].to_int()
frame.write_signed(plan[34], 5)
for index = 1; index <= order; index = index + 1 {
frame.write_signed(plan[index], 15)
}
let residual = lpc_residual(plane, order, plan, shift)
let partition = plan[0].to_int()
frame.write_bits(0L, 2)
frame.write_bits(partition.to_int64(), 4)
write_partitions(frame, residual, count, order, partition)
}
///|
/// Compute how `plane` would be coded without emitting anything.
///
/// Layout of `plan`: slot 0 holds the residual partition order of the chosen
/// FIXED or LPC candidate, slots 1..32 hold the quantised LPC coefficients
/// (index 1 is the first tap, so `lpc_residual` can read them directly), slot
/// 33 holds the subframe type code and slot 34 the LPC shift. The return value
/// is the exact bit cost. Stereo assignment selection calls this for every
/// candidate, so it must not touch a writer: that is what made encoding slow.
///
/// The FIXED orders and the LPC orders are first ranked with a single
/// partition Rice estimate, then the surviving FIXED and LPC candidates are
/// costed again with the full partition search: the partition gain differs
/// between candidates, so a proxy comparison alone can pick the wrong family.
/// The returned cost and the stored partition order always describe exactly
/// what `write_plane_subframe` emits.
fn plan_plane_subframe(
plane : Array[Int64],
bits : Int32,
plan : Array[Int64],
) -> Int64 {
let count = plane.length()
let first = plane[0]
let mut index = 1
let mut constant = true
while index < count && constant {
if plane[index] != first {
constant = false
}
index = index + 1
}
if constant {
plan[33] = 0L
plan[34] = 0L
return (8 + bits).to_int64()
}
let mut fixed_order = -1
let mut fixed_estimate = 0L
for order = 0; order <= 4; order = order + 1 {
if order < count {
let residual = fixed_residual(plane, order)
let (_parameter, rice_cost) = best_parameter_cost(
residual,
0,
residual.length(),
)
let cost = (8 + order * bits + 6 + 4).to_int64() + rice_cost
if fixed_order < 0 || cost < fixed_estimate {
fixed_estimate = cost
fixed_order = order
}
}
}
let choice = Array::make(4, 0L)
let coefficients = Array::make(33, 0L)
let lpc_estimate = lpc_search(plane, bits, choice, coefficients)
let verbatim_bits = (8 + count * bits).to_int64()
let mut best_code = 1
let mut best_bits = verbatim_bits
if fixed_order >= 0 {
let residual = fixed_residual(plane, fixed_order)
let partition = best_partition(residual, count, fixed_order)
let cost = (8 + fixed_order * bits + 6).to_int64() +
partition_cost(residual, count, fixed_order, partition)
if cost < best_bits {
best_bits = cost
best_code = 8 + fixed_order
plan[0] = partition.to_int64()
}
}
// Only take LPC when it is clearly better than storing the samples. On
// noise-like blocks LPC wins by a fraction of a percent, which is not worth
// the extra decoder work (and is the regime where the predictor is least
// trustworthy), so require a five percent margin.
if lpc_estimate >= 0L {
let order = choice[1].to_int()
let shift = choice[2].to_int()
let residual = lpc_residual(plane, order, coefficients, shift)
let partition = best_partition(residual, count, order)
let cost = (8 + order * bits + 4 + 5 + order * 15 + 6).to_int64() +
partition_cost(residual, count, order, partition)
if cost < best_bits && cost * 100L < verbatim_bits * 95L {
best_bits = cost
best_code = choice[0].to_int()
plan[0] = partition.to_int64()
plan[34] = choice[2]
for tap = 1; tap <= order; tap = tap + 1 {
plan[tap] = coefficients[tap]
}
}
}
plan[33] = best_code.to_int64()
best_bits
}
///|
/// Residual of the `order`-th fixed predictor (FLAC coefficients 1, 2 1, 3 3 1,
/// 4 6 4 1).
fn fixed_residual(plane : Array[Int64], order : Int32) -> Array[Int64] {
let length = plane.length() - order
let residual = Array::make(length, 0L)
for index = 0; index < length; index = index + 1 {
let position = order + index
let current = plane[position]
let mut prediction = 0L
if order == 1 {
prediction = plane[position - 1]
} else if order == 2 {
prediction = 2L * plane[position - 1] - plane[position - 2]
} else if order == 3 {
prediction = 3L * plane[position - 1] -
3L * plane[position - 2] +
plane[position - 3]
} else if order == 4 {
prediction = 4L * plane[position - 1] -
6L * plane[position - 2] +
4L * plane[position - 3] -
plane[position - 4]
}
residual[index] = current - prediction
}
residual
}
///|
/// Residual of an LPC predictor: `x[n] - ((sum q[k] x[n-k]) >> shift)`.
fn lpc_residual(
plane : Array[Int64],
order : Int32,
coefficients : Array[Int64],
shift : Int32,
) -> Array[Int64] {
let length = plane.length() - order
let residual = Array::make(length, 0L)
for index = 0; index < length; index = index + 1 {
let position = order + index
let mut sum = 0L
for tap = 1; tap <= order; tap = tap + 1 {
sum = sum + coefficients[tap] * plane[position - tap]
}
residual[index] = plane[position] - (sum >> shift)
}
residual
}
///|
/// Largest absolute predictor accumulator this coefficient set produces.
///
/// FLAC decoders may implement the predictor with 32 bit accumulators, so an
/// encoder must keep the sum inside that range. Estimating it from the sample
/// magnitude is not enough (the coefficients can be much larger than the
/// signal), so the real maximum is measured here and the candidate rejected
/// when it does not fit.
fn lpc_max_sum(
plane : Array[Int64],
order : Int32,
coefficients : Array[Int64],
) -> Int64 {
let count = plane.length()
let mut maximum = 0L
for index = order; index < count; index = index + 1 {
let mut sum = 0L
for tap = 1; tap <= order; tap = tap + 1 {
sum = sum + coefficients[tap] * plane[index - tap]
}
let magnitude = if sum < 0L { -sum } else { sum }
if magnitude > maximum {
maximum = magnitude
}
}
maximum
}
///|
/// Quantise one order's prediction coefficients into `out`, returning the right
/// shift the decoder has to apply.
fn quantise_into(
row : Array[Double],
precision : Int32,
shift_limit : Int32,
out : Array[Int64],
) -> Int32 {
let order = row.length() - 1
let ceiling = (1 << (precision - 1)).to_double()
let mut max_abs = 0.0
for index = 1; index <= order; index = index + 1 {
let magnitude = if row[index] < 0.0 { -row[index] } else { row[index] }
if magnitude > max_abs {
max_abs = magnitude
}
}
let mut shift = 0
if max_abs > 0.0 {
let mut scaled = max_abs
while scaled * 2.0 < ceiling && shift < 15 && shift < shift_limit {
scaled = scaled * 2.0
shift = shift + 1
}
}
let scale = (1 << shift).to_double()
let high = (1L << (precision - 1)) - 1L
let low = -(1L << (precision - 1))
for index = 1; index <= order; index = index + 1 {
let value = row[index] * scale
let rounded = if value >= 0.0 {
(value + 0.5).to_int().to_int64()
} else {
(value - 0.5).to_int().to_int64()
}
if rounded > high {
out[index] = high
} else if rounded < low {
out[index] = low
} else {
out[index] = rounded
}
}
shift
}
///|
/// Search the LPC orders and keep the cheapest one.
///
/// `choice` receives `[code, order, shift]` and `coefficients` receives the
/// quantised taps. Returns the bit cost, or -1 when LPC is not applicable (a
/// block too short to fit a warmup).
fn lpc_search(
plane : Array[Int64],
bits : Int32,
choice : Array[Int64],
coefficients : Array[Int64],
) -> Int64 {
let count = plane.length()
let mut max_order = count - 1
if max_order > 12 {
max_order = 12
}
if max_order < 1 {
return -1L
}
// Apodization matters far more than it looks. The plain autocorrelation
// method is ill conditioned for resonant signals (a pure tone gives
// 1 - a^2 ~ 0.004), so small estimation errors move the predictor away from
// the true poles and the residual grows by a factor of three. Windowing the
// block first restores the correct coefficients, which is what libFLAC does.
let windowed = Array::make(count, 0.0)
let denominator = if count > 1 { (count - 1).to_double() } else { 1.0 }
for index = 0; index < count; index = index + 1 {
let weight = 0.5 -
0.5 * @math.cos(6.283185307179586 * index.to_double() / denominator)
windowed[index] = plane[index].to_double() * weight
}
let mut mean = 0.0
for value in windowed {
mean = mean + value
}
mean = mean / count.to_double()
let ac = Array::make(max_order + 1, 0.0)
for lag = 0; lag <= max_order; lag = lag + 1 {
let mut sum = 0.0
for index = lag; index < count; index = index + 1 {
sum = sum + (windowed[index] - mean) * (windowed[index - lag] - mean)
}
ac[lag] = sum
}
let rows = Array::make(max_order + 1, Array::make(1, 0.0))
let errors = Array::make(max_order + 1, 0.0)
let a = Array::make(max_order + 1, 0.0)
let b = Array::make(max_order + 1, 0.0)
let mut energy = ac[0]
errors[0] = energy
for order = 1; order <= max_order; order = order + 1 {
let mut acc = ac[order]
for tap = 1; tap < order; tap = tap + 1 {
acc = acc + a[tap] * ac[order - tap]
}
let reflection = if energy > 0.0 { -acc / energy } else { 0.0 }
for tap = 1; tap < order; tap = tap + 1 {
b[tap] = a[tap] + reflection * a[order - tap]
}
b[order] = reflection
for tap = 1; tap <= order; tap = tap + 1 {
a[tap] = b[tap]
}
energy = energy * (1.0 - reflection * reflection)
let row = Array::make(order + 1, 0.0)
for tap = 1; tap <= order; tap = tap + 1 {
row[tap] = -a[tap]
}
rows[order] = row
errors[order] = energy
}
let mut candidate = 1
let mut best_estimate = -1.0
for order = 1; order <= max_order; order = order + 1 {
if errors[order] > 0.0 {
let estimate = count.to_double() * @math.log2(errors[order]) +
(order * (bits + 15) + 9).to_double()
if best_estimate < 0.0 || estimate < best_estimate {
best_estimate = estimate
candidate = order
}
}
}
let mut best_bits = -1L
let scratch = Array::make(33, 0L)
// The predictor sum has to stay inside 32 bits: order * max_coefficient *
// max_sample * 2^shift < 2^30. Without this bound a 17 bit side channel with
// 15 bit coefficients overflows, and 32 bit decoders (libsndfile among them)
// then decode a different signal.
for delta = -1; delta <= 1; delta = delta + 1 {
let order = candidate + delta
if order >= 1 && order <= max_order {
let row = rows[order]
// The exact accumulator check below decides whether a candidate is safe,
// so the quantiser is free to use the full 15 bit precision here.
let shift_limit = 15
let shift = quantise_into(row, 15, shift_limit, scratch)
let accumulator = lpc_max_sum(plane, order, scratch)
if accumulator < 1073741824L {
let residual = lpc_residual(plane, order, scratch, shift)
let (_parameter, rice_cost) = best_parameter_cost(
residual,
0,
residual.length(),
)
let cost = (8 + order * bits + 4 + 5 + order * 15).to_int64() +
rice_cost
if best_bits < 0L || cost < best_bits {
best_bits = cost
choice[0] = (32 + order - 1).to_int64()
choice[1] = order.to_int64()
choice[2] = shift.to_int64()
for tap = 1; tap <= order; tap = tap + 1 {
coefficients[tap] = scratch[tap]
}
}
}
}
}
best_bits
}
///|
/// Whether partition order `p` is legal for this block.
fn partition_valid(block_size : Int32, order : Int32, p : Int32) -> Bool {
let parts = 1 << p
block_size % parts == 0 && block_size >> p > order
}
///|
/// Rice cost of the whole residual split into `2^p` partitions, each with its
/// own parameter.
fn partition_cost(
residual : Array[Int64],
block_size : Int32,
order : Int32,
p : Int32,
) -> Int64 {
let parts = 1 << p
let size = block_size >> p
let mut offset = 0
let mut total = 0L
for index = 0; index < parts; index = index + 1 {
let length = if index == 0 { size - order } else { size }
if length > 0 {
let (_k, cost) = best_parameter_cost(residual, offset, length)
total = total + 4L + cost
offset = offset + length
}
}
total
}
///|
/// Pick the partition order that codes the residual in the fewest bits.
fn best_partition(
residual : Array[Int64],
block_size : Int32,
order : Int32,
) -> Int32 {
let mut best = 0
let mut best_cost = partition_cost(residual, block_size, order, 0)
for p = 1; p <= 8; p = p + 1 {
if partition_valid(block_size, order, p) {
let cost = partition_cost(residual, block_size, order, p)
if cost < best_cost {
best_cost = cost
best = p
}
}
}
best
}
///|
/// Rice code one partition.
fn write_rice_range(
frame : @primitives.BitWriter,
residual : Array[Int64],
start : Int32,
length : Int32,
k : Int32,
) -> Unit {
let mask = (1L << k) - 1L
let end = start + length
for index = start; index < end; index = index + 1 {
let folded = zigzag(residual[index])
frame.write_unary((folded >> k).to_int())
frame.write_bits(folded & mask, k)
}
}
///|
/// Write the partition table and the coded residual.
fn write_partitions(
frame : @primitives.BitWriter,
residual : Array[Int64],
block_size : Int32,
order : Int32,
p : Int32,
) -> Unit {
let parts = 1 << p
let size = block_size >> p
let mut offset = 0
for index = 0; index < parts; index = index + 1 {
let length = if index == 0 { size - order } else { size }
if length > 0 {
let (k, _cost) = best_parameter_cost(residual, offset, length)
frame.write_bits(k.to_int64(), 4)
write_rice_range(frame, residual, offset, length, k)
offset = offset + length
}
}
}
///|
/// Fold a signed residual into an unsigned one (FLAC's zig-zag rule).
fn zigzag(value : Int64) -> Int64 {
if value >= 0L {
value << 1
} else {
(-value << 1) - 1L
}
}
///|
/// Pick the Rice parameter for one partition and report its exact bit cost.
///
/// The mean folded value locates the right parameter closely, so one pass
/// computes it and a second pass costs the candidates around it — and the
/// escape parameter 14 — exactly, in one scan with one accumulator each. The
/// returned cost is what `write_rice_range` emits for the chosen parameter,
/// and costing, planning and writing all share this function, so they cannot
/// drift apart.
fn best_parameter_cost(
residual : Array[Int64],
start : Int32,
length : Int32,
) -> (Int32, Int64) {
if length <= 0 {
return (0, 0L)
}
let end = start + length
let mut sum = 0L
for index = start; index < end; index = index + 1 {
sum = sum + zigzag(residual[index])
}
let mean = sum / length.to_int64()
let mut base = 0
let mut probe = mean
while probe > 1L {
probe = probe >> 1
base = base + 1
}
if base > 14 {
base = 14
}
let low = if base > 0 { base - 1 } else { 0 }
let high = if base < 14 { base + 1 } else { 14 }
let mut sum_low = 0L
let mut sum_base = 0L
let mut sum_high = 0L
let mut sum_escape = 0L
for index = start; index < end; index = index + 1 {
let folded = zigzag(residual[index])
sum_low = sum_low + (folded >> low)
sum_base = sum_base + (folded >> base)
sum_high = sum_high + (folded >> high)
sum_escape = sum_escape + (folded >> 14)
}
let count = length.to_int64()
let mut best_k = 14
let mut best_bits = sum_escape + count * 15L
let cost_low = sum_low + count * (1L + low.to_int64())
if cost_low < best_bits {
best_bits = cost_low
best_k = low
}
let cost_base = sum_base + count * (1L + base.to_int64())
if cost_base < best_bits {
best_bits = cost_base
best_k = base
}
let cost_high = sum_high + count * (1L + high.to_int64())
if cost_high < best_bits {
best_bits = cost_high
best_k = high
}
(best_k, best_bits)
}