diff --git a/embd_res/kcpp_musicui.embd b/embd_res/kcpp_musicui.embd
index ede27e9d0..0d5710c4c 100644
--- a/embd_res/kcpp_musicui.embd
+++ b/embd_res/kcpp_musicui.embd
@@ -195,18 +195,19 @@ input[type="checkbox"] {
@@ -323,6 +324,7 @@ function getFormData(){
if(v!=="") {data[id]=isNaN(v)?v:Number(v);}
});
data["stereo"] = (document.getElementById("stereo").checked ? true : false);
+ data["use_mp3"] = (document.getElementById("use_mp3").checked ? true : false);
data["gen_codes"] = (document.getElementById("gen_codes").checked ? true : false);
data["rewrite_caption"] = (document.getElementById("rewrite_caption").checked ? true : false);
return data;
diff --git a/expose.h b/expose.h
index cfa7e052a..9d36fcd77 100644
--- a/expose.h
+++ b/expose.h
@@ -346,7 +346,8 @@ struct music_load_model_inputs
struct music_generation_inputs
{
const bool is_planner_mode = false; //if true, generate codes, else, generate diffusion music
- const bool stereo = false;
+ const bool stereo = false; //only for wav
+ const bool use_mp3 = false;
const bool gen_codes = false;
const bool rewrite_caption = true;
const char * input_json = nullptr;
diff --git a/koboldcpp.py b/koboldcpp.py
index 36efc4b18..1e1029740 100755
--- a/koboldcpp.py
+++ b/koboldcpp.py
@@ -461,6 +461,7 @@ class music_load_model_inputs(ctypes.Structure):
class music_generation_inputs(ctypes.Structure):
_fields_ = [("is_planner_mode", ctypes.c_bool),
("stereo", ctypes.c_bool),
+ ("use_mp3", ctypes.c_bool),
("gen_codes", ctypes.c_bool),
("rewrite_caption", ctypes.c_bool),
("input_json", ctypes.c_char_p)]
@@ -2484,7 +2485,8 @@ def music_generate_codes(genparams):
input_json = json.dumps(genparams)
inputs = music_generation_inputs()
inputs.is_planner_mode = True
- inputs.stereo = genparams.get('stereo', False)
+ inputs.stereo = genparams.get('stereo', True)
+ inputs.use_mp3 = genparams.get('use_mp3', True)
inputs.gen_codes = genparams.get('gen_codes', False)
inputs.rewrite_caption = genparams.get('rewrite_caption', True)
inputs.input_json = input_json.encode("UTF-8")
@@ -2501,6 +2503,7 @@ def music_generate_audio(genparams):
inputs = music_generation_inputs()
inputs.is_planner_mode = False
inputs.stereo = genparams.get('stereo', True)
+ inputs.use_mp3 = genparams.get('use_mp3', True)
inputs.gen_codes = genparams.get('gen_codes', False)
inputs.rewrite_caption = genparams.get('rewrite_caption', True)
inputs.input_json = input_json.encode("UTF-8")
diff --git a/otherarch/acestep/dit-vae.cpp b/otherarch/acestep/dit-vae.cpp
index ea8cc280f..2e4b3e1dd 100644
--- a/otherarch/acestep/dit-vae.cpp
+++ b/otherarch/acestep/dit-vae.cpp
@@ -976,24 +976,20 @@ std::string acestep_generate_audio(const music_generation_inputs inputs)
}
}
- // std::string opath = "egghenlo.wav";
- // if (write_wav(opath.c_str(), audio.data(), T_audio, 48000)) {
- // fprintf(stderr, "[VAE Batch%d] Wrote %s: %d samples (%.2fs @ 48kHz stereo)\n",
- // b, opath.c_str(), T_audio, (float)T_audio / 48000.0f);
- // } else {
- // fprintf(stderr, "[VAE Batch%d] FATAL: failed to write %s\n", b, opath.c_str());
- // }
-
// output wav
float muslen = (float)T_audio / 48000.0f;
std::string finalb64;
- if(inputs.stereo)
- {
- finalb64 = save_stereo_wav16_base64(audio,T_audio,48000);
+ if (inputs.use_mp3) {
+ fprintf(stderr, "[Save Audio] Converting to Mp3...\n",muslen);
+ finalb64 = save_stereo_mp3_base64(audio, T_audio, 48000);
+ } else if (inputs.stereo) {
+ fprintf(stderr, "[Save Audio] Save as Stereo WAV...\n",muslen);
+ finalb64 = save_stereo_wav16_base64(audio, T_audio, 48000);
} else {
- std::vector mono = mix_planar_stereo_to_mono(audio.data(), T_audio);
- std::vector resampled_buf = resample_wav(1,mono,48000,32000);
- finalb64 = save_wav16_base64(resampled_buf, 32000);
+ fprintf(stderr, "[Save Audio] Save as Mono WAV...\n",muslen);
+ std::vector mono = mix_planar_stereo_to_mono(audio.data(), T_audio);
+ std::vector resampled_buf = resample_wav(1, mono, 48000, 32000);
+ finalb64 = save_wav16_base64(resampled_buf, 32000);
}
if(acestep_dit_lowvram)
diff --git a/otherarch/acestep/mp3/gen_huff_enc.c b/otherarch/acestep/mp3/gen_huff_enc.c
new file mode 100644
index 000000000..b2334bc3e
--- /dev/null
+++ b/otherarch/acestep/mp3/gen_huff_enc.c
@@ -0,0 +1,184 @@
+// gen_final.c: correct Huffman encoder table generator
+// The key insight: leaf>>8 is only the FINAL flush.
+// Total bits = sum of all intermediate flushes + leaf>>8.
+// Only the first peek (5 bits) is NOT flushed if we get a direct hit.
+
+#include
+#include
+#include
+#include "tabs_data.h"
+
+static const int tmax[] = {
+ 0, 1, 2, 2, 0, 3, 3, 5, 5, 5, 7, 7, 7, 15, 0, 15,
+ 15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15
+};
+
+typedef struct { uint32_t code; uint8_t len; } henc_t;
+static henc_t enc[16][16];
+
+// Walk the minimp3 tree and return {leaf, total_bits_consumed}
+// Returns -1 if cannot decode
+static int walk_tree(const int16_t *codebook, uint32_t bits, int nbits,
+ int *total_consumed) {
+ uint32_t cache = (nbits < 32) ? (bits << (32 - nbits)) : bits;
+ int w = 5;
+ int leaf = codebook[cache >> (32 - w)];
+ int flushed = 0; // bits flushed so far
+
+ if (leaf >= 0) {
+ // Direct hit: only flush leaf>>8 bits (subset of the initial 5 peeked)
+ *total_consumed = leaf >> 8;
+ return leaf;
+ }
+
+ // Tree traversal: flush the initial 5 bits, then follow nodes
+ cache <<= w;
+ flushed = w;
+
+ while (leaf < 0) {
+ w = leaf & 7;
+ if (w == 0) return -1;
+ int peek = (int)(cache >> (32 - w));
+ int idx = peek - (leaf >> 3);
+ leaf = codebook[idx];
+
+ if (leaf < 0) {
+ // Not a leaf yet, flush these w bits and continue
+ cache <<= w;
+ flushed += w;
+ if (flushed > 30) return -1;
+ }
+ }
+
+ // leaf is positive: flush leaf>>8 more bits
+ *total_consumed = flushed + (leaf >> 8);
+ return leaf;
+}
+
+static void build_pair_table(int tab_num) {
+ int mx = tmax[tab_num];
+ memset(enc, 0, sizeof(enc));
+ const int16_t *codebook = tabs + tabindex[tab_num];
+
+ for (int len = 1; len <= 19; len++) {
+ for (uint32_t code = 0; code < (1u << len); code++) {
+ int total = 0;
+ int leaf = walk_tree(codebook, code, len, &total);
+ if (leaf < 0) continue;
+ if (total != len) continue;
+ int x = (leaf >> 4) & 0xF;
+ int y = leaf & 0xF;
+ if (x > mx || y > mx) continue;
+ if (enc[x][y].len == 0) {
+ enc[x][y].code = code;
+ enc[x][y].len = (uint8_t)len;
+ }
+ }
+ }
+}
+
+int main(void) {
+ // Verify completeness first
+ int ok = 1;
+ for (int t = 1; t < 32; t++) {
+ if (t == 0 || t == 4 || t == 14) continue;
+ int mx = tmax[t]; if (mx == 0) continue;
+ build_pair_table(t);
+ int dim = mx + 1, found = 0;
+ for (int x = 0; x < dim; x++)
+ for (int y = 0; y < dim; y++)
+ if (enc[x][y].len > 0) found++;
+ int expected = dim * dim;
+ if (found != expected) {
+ fprintf(stderr, "Table %2d: %d/%d INCOMPLETE\n", t, found, expected);
+ ok = 0;
+ } else {
+ fprintf(stderr, "Table %2d: %d/%d OK\n", t, found, expected);
+ }
+ }
+ if (!ok) {
+ fprintf(stderr, "WARNING: some tables incomplete\n");
+ }
+
+ // Generate output
+ printf("// mp3 Huffman encoder lookup tables\n");
+ printf("// Generated from minimp3 (CC0). ISO 11172-3 Table B.7.\n");
+ printf("// code = MSB first bit pattern. len = number of bits.\n");
+ printf("// Append sign bits for nonzero |x| and |y|.\n");
+ printf("// Values >= 15 use linbits extension.\n\n");
+
+ printf("static const uint8_t mp3enc_linbits[32] = {\n");
+ printf(" 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,\n");
+ printf(" 1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13\n};\n\n");
+
+ for (int t = 1; t < 32; t++) {
+ if (t == 0 || t == 4 || t == 14) continue;
+ int mx = tmax[t]; if (mx == 0) continue;
+ // Skip duplicate trees
+ int skip = 0;
+ for (int j = 1; j < t; j++) {
+ if (j == 4 || j == 14) continue;
+ if (tabindex[t] == tabindex[j] && tmax[t] == tmax[j]) {
+ printf("// table %d: same tree as table %d (linbits=%d)\n\n", t, j, g_linbits[t]);
+ skip = 1; break;
+ }
+ }
+ if (skip) continue;
+
+ build_pair_table(t);
+ int dim = mx + 1;
+ printf("static const uint16_t mp3enc_hcode_%d[%d][%d] = {\n", t, dim, dim);
+ for (int x = 0; x < dim; x++) {
+ printf(" {");
+ for (int y = 0; y < dim; y++) {
+ printf("0x%x", enc[x][y].code);
+ if (y < dim-1) printf(",");
+ }
+ printf("}%s\n", x < dim-1 ? "," : "");
+ }
+ printf("};\nstatic const uint8_t mp3enc_hlen_%d[%d][%d] = {\n", t, dim, dim);
+ for (int x = 0; x < dim; x++) {
+ printf(" {");
+ for (int y = 0; y < dim; y++) {
+ printf("%d", enc[x][y].len);
+ if (y < dim-1) printf(",");
+ }
+ printf("}%s\n", x < dim-1 ? "," : "");
+ }
+ printf("};\n\n");
+ }
+
+ // Count1 tables
+ for (int tbl = 0; tbl < 2; tbl++) {
+ const uint8_t *cb = tbl ? tab33 : tab32;
+ printf("// count1 table %c (count1table_select=%d)\n", 'A'+tbl, tbl);
+ printf("// index = v*8 + w*4 + x*2 + y\n");
+ uint32_t codes[16]={0}; uint8_t lens[16]={0};
+ for (int len = 1; len <= 10; len++) {
+ for (uint32_t code = 0; code < (1u<>28);
+ int leaf = cb[peek4];
+ int consumed;
+ if (leaf & 8) { consumed = leaf & 7; }
+ else {
+ int extra = leaf & 3, offset = leaf >> 3;
+ cache <<= 4;
+ int peek2 = (int)(cache >> (32-extra));
+ leaf = cb[offset + peek2];
+ consumed = leaf & 7;
+ }
+ if (consumed != len) continue;
+ int v=(leaf>>7)&1, w=(leaf>>6)&1, x=(leaf>>5)&1, y=(leaf>>4)&1;
+ int idx = v*8+w*4+x*2+y;
+ if (lens[idx]==0) { codes[idx]=code; lens[idx]=(uint8_t)consumed; }
+ }
+ }
+ printf("static const uint8_t mp3enc_count1%c_code[16] = {\n ", 'a'+tbl);
+ for (int i=0;i<16;i++) { printf("%d",codes[i]); if(i<15) printf(","); if(i==7) printf("\n "); }
+ printf("\n};\nstatic const uint8_t mp3enc_count1%c_len[16] = {\n ", 'a'+tbl);
+ for (int i=0;i<16;i++) { printf("%d",lens[i]); if(i<15) printf(","); if(i==7) printf("\n "); }
+ printf("\n};\n\n");
+ }
+ return 0;
+}
diff --git a/otherarch/acestep/mp3/mp3enc-bits.h b/otherarch/acestep/mp3/mp3enc-bits.h
new file mode 100644
index 000000000..1e58fcbe4
--- /dev/null
+++ b/otherarch/acestep/mp3/mp3enc-bits.h
@@ -0,0 +1,220 @@
+#pragma once
+// mp3enc-bits.h
+// Bitstream writer, frame header, side information packer.
+// Part of mp3enc. MIT license.
+
+#include
+#include
+
+// Bitstream writer: accumulates bits MSB first into a byte buffer.
+// The encoder writes frame header, side info, and Huffman data through this.
+struct mp3enc_bs {
+ uint8_t * buf; // output buffer (caller owned)
+ int capacity; // total bytes available
+ int byte_pos; // current byte offset
+ int bit_pos; // bits used in current byte (0..7, 0 = empty)
+
+ void init(uint8_t * dst, int cap) {
+ buf = dst;
+ capacity = cap;
+ byte_pos = 0;
+ bit_pos = 0;
+ memset(dst, 0, cap);
+ }
+
+ // Write n bits (1..32) from val, MSB first.
+ void put(uint32_t val, int n) {
+ for (int i = n - 1; i >= 0; i--) {
+ buf[byte_pos] |= (uint8_t) (((val >> i) & 1) << (7 - bit_pos));
+ bit_pos++;
+ if (bit_pos == 8) {
+ bit_pos = 0;
+ byte_pos++;
+ }
+ }
+ }
+
+ // Total bits written so far
+ int total_bits() const { return byte_pos * 8 + bit_pos; }
+
+ // Byte align (pad with zeros)
+ void align() {
+ if (bit_pos > 0) {
+ byte_pos++;
+ bit_pos = 0;
+ }
+ }
+};
+
+// Frame header: 4 bytes, fixed format for MPEG1 Layer III.
+// ISO 11172-3, clause 2.4.2.3
+struct mp3enc_header {
+ int bitrate_kbps; // from mp3enc_bitrate_kbps[]
+ int samplerate; // 44100, 48000, or 32000
+ int mode; // 0=stereo, 1=joint, 2=dual, 3=mono
+ int mode_ext; // for joint stereo: bit0=intensity, bit1=ms
+ int padding; // 0 or 1
+
+ // Compute bitrate_index from kbps
+ int bitrate_index() const {
+ // Match against the Layer III table
+ static const int br[] = { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };
+ for (int i = 1; i < 15; i++) {
+ if (br[i] == bitrate_kbps) {
+ return i;
+ }
+ }
+ return 0; // free format
+ }
+
+ // Compute sampling_frequency field
+ int sr_index() const {
+ if (samplerate == 44100) {
+ return 0;
+ }
+ if (samplerate == 48000) {
+ return 1;
+ }
+ if (samplerate == 32000) {
+ return 2;
+ }
+ return 0;
+ }
+
+ // Frame size in bytes (including header)
+ int frame_bytes() const { return 144 * bitrate_kbps * 1000 / samplerate + padding; }
+
+ // Write 4 byte header to bitstream
+ void write(mp3enc_bs & bs) const {
+ bs.put(0xFFF, 12); // syncword
+ bs.put(1, 1); // ID = MPEG1
+ bs.put(1, 2); // layer = III (01)
+ bs.put(1, 1); // protection_bit = 1 (no CRC)
+ bs.put(bitrate_index(), 4); // bitrate_index
+ bs.put(sr_index(), 2); // sampling_frequency
+ bs.put(padding, 1); // padding_bit
+ bs.put(0, 1); // private_bit
+ bs.put(mode, 2); // mode
+ bs.put(mode_ext, 2); // mode_extension
+ bs.put(0, 1); // copyright
+ bs.put(1, 1); // original
+ bs.put(0, 2); // emphasis = none
+ }
+};
+
+// Granule side information for one channel.
+// ISO 11172-3, clause 2.4.1.7
+struct mp3enc_granule_info {
+ int part2_3_length; // total bits: scalefactors + Huffman data
+ int big_values; // number of pairs in big_values region
+ int global_gain; // quantizer step size (0..255)
+ int scalefac_compress; // index into slen table (0..15)
+ int block_type; // 0=normal, 1=start, 2=short, 3=stop
+ int mixed_block_flag; // 1 if lower bands use long windows
+ int table_select[3]; // Huffman table for each region
+ int subblock_gain[3]; // gain offset per short window
+ int region0_count; // sfb count in region 0
+ int region1_count; // sfb count in region 1
+ int preflag; // high frequency boost
+ int scalefac_scale; // 0 = sqrt(2) step, 1 = 2 step
+ int count1table_select; // 0 = table A, 1 = table B
+
+ // Scale factors (filled by quantization loop)
+ int scalefac_l[21]; // long block scalefactors
+ int scalefac_s[12][3]; // short block scalefactors
+};
+
+// Side information for one frame.
+// Stereo: 32 bytes, mono: 17 bytes.
+struct mp3enc_side_info {
+ int main_data_begin; // bit reservoir backpointer (bytes)
+ int scfsi[2][4]; // scalefactor selection info per channel
+ mp3enc_granule_info gr[2][2]; // [granule][channel]
+
+ // Write side info to bitstream (after header).
+ // nch = 1 for mono, 2 for stereo/joint/dual
+ void write(mp3enc_bs & bs, int nch) const {
+ bs.put(main_data_begin, 9);
+
+ // private bits
+ if (nch == 1) {
+ bs.put(0, 5);
+ } else {
+ bs.put(0, 3);
+ }
+
+ // scfsi
+ for (int ch = 0; ch < nch; ch++) {
+ for (int band = 0; band < 4; band++) {
+ bs.put(scfsi[ch][band], 1);
+ }
+ }
+
+ // per granule, per channel
+ for (int g = 0; g < 2; g++) {
+ for (int ch = 0; ch < nch; ch++) {
+ const mp3enc_granule_info & gi = gr[g][ch];
+ bs.put(gi.part2_3_length, 12);
+ bs.put(gi.big_values, 9);
+ bs.put(gi.global_gain, 8);
+ bs.put(gi.scalefac_compress, 4);
+
+ int window_switching = (gi.block_type != 0) ? 1 : 0;
+ bs.put(window_switching, 1);
+
+ if (window_switching) {
+ bs.put(gi.block_type, 2);
+ bs.put(gi.mixed_block_flag, 1);
+ for (int r = 0; r < 2; r++) {
+ bs.put(gi.table_select[r], 5);
+ }
+ for (int w = 0; w < 3; w++) {
+ bs.put(gi.subblock_gain[w], 3);
+ }
+ } else {
+ for (int r = 0; r < 3; r++) {
+ bs.put(gi.table_select[r], 5);
+ }
+ bs.put(gi.region0_count, 4);
+ bs.put(gi.region1_count, 3);
+ }
+
+ bs.put(gi.preflag, 1);
+ bs.put(gi.scalefac_scale, 1);
+ bs.put(gi.count1table_select, 1);
+ }
+ }
+ }
+};
+
+// Write scalefactors for one granule/channel into main_data.
+// Returns number of bits written.
+static int mp3enc_write_scalefactors(mp3enc_bs & bs,
+ const mp3enc_granule_info & gi,
+ int gr,
+ int nch_unused,
+ const int scfsi[4]) {
+ (void) nch_unused;
+ int slen1 = mp3enc_slen[0][gi.scalefac_compress];
+ int slen2 = mp3enc_slen[1][gi.scalefac_compress];
+ int bits = 0;
+
+ // Long blocks: 21 scalefactor bands, split by scfsi
+ // bands 0..5 (scfsi band 0)
+ // bands 6..10 (scfsi band 1)
+ // bands 11..15 (scfsi band 2)
+ // bands 16..20 (scfsi band 3)
+ static const int band_start[4] = { 0, 6, 11, 16 };
+ static const int band_end[4] = { 6, 11, 16, 21 };
+
+ for (int b = 0; b < 4; b++) {
+ if (gr == 0 || scfsi[b] == 0) {
+ int slen = (b < 2) ? slen1 : slen2;
+ for (int sfb = band_start[b]; sfb < band_end[b]; sfb++) {
+ bs.put(gi.scalefac_l[sfb], slen);
+ bits += slen;
+ }
+ }
+ }
+ return bits;
+}
diff --git a/otherarch/acestep/mp3/mp3enc-filter.h b/otherarch/acestep/mp3/mp3enc-filter.h
new file mode 100644
index 000000000..0518018bc
--- /dev/null
+++ b/otherarch/acestep/mp3/mp3enc-filter.h
@@ -0,0 +1,69 @@
+#pragma once
+// mp3enc-filter.h
+// Polyphase analysis filterbank: 32 new PCM samples in, 32 subband samples out.
+// ISO 11172-3 clause 2.4.3.2 (encoder side, Annex C).
+// Part of mp3enc. MIT license.
+
+#include
+#include
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+// The analysis filterbank state for one channel.
+// Holds the 512 sample FIFO and produces 32 subband outputs per call.
+struct mp3enc_filter {
+ float buf[512]; // circular buffer of past PCM samples
+ int off; // current write offset into buf
+
+ void init() {
+ memset(buf, 0, sizeof(buf));
+ off = 0;
+ }
+
+ // Feed 32 new PCM samples, produce 32 subband samples.
+ // pcm: pointer to 32 float samples (mono, this channel only)
+ // sb_out: output array of 32 subband values
+ //
+ // Algorithm (ISO 11172-3 Annex C, encoding process):
+ // 1. Shift 32 new samples into the 512 sample FIFO
+ // 2. Window: multiply by 512 Ci coefficients (mp3enc_enwindow)
+ // 3. Partial sum: 512 windowed values -> 64 values
+ // 4. Matrixing: 64 values -> 32 subband samples via DCT
+ void process(const float * pcm, float * sb_out) {
+ // Step 1: shift new samples into the buffer.
+ // The newest sample goes at buf[off], oldest at buf[off+31].
+ // We store them in reverse order so windowing is a straight multiply.
+ off = (off - 32) & 511;
+ for (int i = 0; i < 32; i++) {
+ buf[(off + i) & 511] = pcm[31 - i];
+ }
+
+ // Step 2 + 3: window and partial sum.
+ // z[i] = sum over j=0..7 of: buf[(off + i + 64*j) & 511] * Ci[i + 64*j]
+ // This produces 64 values from the 512 windowed samples.
+ float z[64];
+ for (int i = 0; i < 64; i++) {
+ float sum = 0.0f;
+ for (int j = 0; j < 8; j++) {
+ int buf_idx = (off + i + 64 * j) & 511;
+ int win_idx = i + 64 * j;
+ sum += buf[buf_idx] * mp3enc_enwindow[win_idx];
+ }
+ z[i] = sum;
+ }
+
+ // Step 4: matrixing (ISO 11172-3, Annex C, analysis filter).
+ // sb_out[k] = sum over i=0..63 of: z[i] * cos((2*k + 1) * (16 - i) * PI / 64)
+ // for k = 0..31 (subbands)
+ // Formula verified against ISO 11172-3 Table C.1
+ for (int k = 0; k < 32; k++) {
+ float sum = 0.0f;
+ for (int i = 0; i < 64; i++) {
+ float angle = (float) M_PI * (float) (2 * k + 1) * (float) (16 - i) / 64.0f;
+ sum += z[i] * cosf(angle);
+ }
+ sb_out[k] = sum;
+ }
+ }
+};
diff --git a/otherarch/acestep/mp3/mp3enc-huff.h b/otherarch/acestep/mp3/mp3enc-huff.h
new file mode 100644
index 000000000..203b7794b
--- /dev/null
+++ b/otherarch/acestep/mp3/mp3enc-huff.h
@@ -0,0 +1,357 @@
+#pragma once
+// mp3enc-huff.h
+// Huffman encoding of quantized spectral values.
+// ISO 11172-3 clause 2.4.2.7.
+// Part of mp3enc. MIT license.
+
+#include
+
+// Count trailing zero pairs in quantized spectrum.
+// Returns number of pairs of zeros from the end.
+static int mp3enc_count_rzero(const int * ix, int n) {
+ int i = n - 2;
+ while (i >= 0 && ix[i] == 0 && ix[i + 1] == 0) {
+ i -= 2;
+ }
+ return (n - i - 2) / 2;
+}
+
+// Count quadruples with |val| <= 1 after big_values region.
+// start: index right after big_values*2
+// end: index right before rzero
+// Returns number of quadruples.
+static int mp3enc_count_count1(const int * ix, int start, int end) {
+ int count = 0;
+ int i = end - 4;
+ while (i >= start) {
+ if (abs(ix[i]) <= 1 && abs(ix[i + 1]) <= 1 && abs(ix[i + 2]) <= 1 && abs(ix[i + 3]) <= 1) {
+ count++;
+ i -= 4;
+ } else {
+ break;
+ }
+ }
+ return count;
+}
+
+// Compute bits needed to encode a pair (x, y) with a given table.
+// Returns total bits (Huffman code + linbits + sign bits).
+static int mp3enc_pair_bits(int table, int x, int y) {
+ if (table == 0) {
+ return 0; // table 0 = all zeros, no bits emitted
+ }
+ int ax = abs(x);
+ int ay = abs(y);
+ int bits = 0;
+
+ // Clamp to 15 for Huffman lookup; excess goes to linbits
+ int cx = (ax < 15) ? ax : 15;
+ int cy = (ay < 15) ? ay : 15;
+ int lb = mp3enc_linbits[table];
+
+ // Get Huffman code length.
+ // Tables share trees; we select the right code/len arrays based on table number.
+ // For phase 1, we use tables 1, 2, 5, 6, 7, 10, 13, 15 (the common choices).
+ int hlen = 0;
+ switch (table) {
+ case 1:
+ hlen = mp3enc_hlen_1[cy][cx];
+ break;
+ case 2:
+ hlen = mp3enc_hlen_2[cy][cx];
+ break;
+ case 3:
+ hlen = mp3enc_hlen_3[cy][cx];
+ break;
+ case 5:
+ hlen = mp3enc_hlen_5[cy][cx];
+ break;
+ case 6:
+ hlen = mp3enc_hlen_6[cy][cx];
+ break;
+ case 7:
+ hlen = mp3enc_hlen_7[cy][cx];
+ break;
+ case 8:
+ hlen = mp3enc_hlen_8[cy][cx];
+ break;
+ case 9:
+ hlen = mp3enc_hlen_9[cy][cx];
+ break;
+ case 10:
+ hlen = mp3enc_hlen_10[cy][cx];
+ break;
+ case 11:
+ hlen = mp3enc_hlen_11[cy][cx];
+ break;
+ case 12:
+ hlen = mp3enc_hlen_12[cy][cx];
+ break;
+ case 13:
+ hlen = mp3enc_hlen_13[cy][cx];
+ break;
+ case 15:
+ hlen = mp3enc_hlen_15[cy][cx];
+ break;
+ // 16..23 use table 16 tree; 24..31 use table 24 tree
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ hlen = mp3enc_hlen_16[cy][cx];
+ break;
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ hlen = mp3enc_hlen_24[cy][cx];
+ break;
+ default:
+ return 9999; // invalid table
+ }
+
+ if (hlen == 0 && (cx || cy)) {
+ return 9999; // no valid code
+ }
+
+ bits = hlen;
+ if (ax >= 15) {
+ bits += lb; // linbits for x
+ }
+ if (ay >= 15) {
+ bits += lb; // linbits for y
+ }
+ if (ax > 0) {
+ bits += 1; // sign bit for x
+ }
+ if (ay > 0) {
+ bits += 1; // sign bit for y
+ }
+ return bits;
+}
+
+// Write a Huffman coded pair to the bitstream.
+static void mp3enc_write_pair(mp3enc_bs & bs, int table, int x, int y) {
+ if (table == 0) {
+ return; // table 0 = all zeros, nothing to write
+ }
+ int ax = abs(x);
+ int ay = abs(y);
+ int cx = (ax < 15) ? ax : 15;
+ int cy = (ay < 15) ? ay : 15;
+ int lb = mp3enc_linbits[table];
+
+ // Get Huffman code and length
+ uint16_t code = 0;
+ int len = 0;
+ switch (table) {
+ case 1:
+ code = mp3enc_hcode_1[cy][cx];
+ len = mp3enc_hlen_1[cy][cx];
+ break;
+ case 2:
+ code = mp3enc_hcode_2[cy][cx];
+ len = mp3enc_hlen_2[cy][cx];
+ break;
+ case 3:
+ code = mp3enc_hcode_3[cy][cx];
+ len = mp3enc_hlen_3[cy][cx];
+ break;
+ case 5:
+ code = mp3enc_hcode_5[cy][cx];
+ len = mp3enc_hlen_5[cy][cx];
+ break;
+ case 6:
+ code = mp3enc_hcode_6[cy][cx];
+ len = mp3enc_hlen_6[cy][cx];
+ break;
+ case 7:
+ code = mp3enc_hcode_7[cy][cx];
+ len = mp3enc_hlen_7[cy][cx];
+ break;
+ case 8:
+ code = mp3enc_hcode_8[cy][cx];
+ len = mp3enc_hlen_8[cy][cx];
+ break;
+ case 9:
+ code = mp3enc_hcode_9[cy][cx];
+ len = mp3enc_hlen_9[cy][cx];
+ break;
+ case 10:
+ code = mp3enc_hcode_10[cy][cx];
+ len = mp3enc_hlen_10[cy][cx];
+ break;
+ case 11:
+ code = mp3enc_hcode_11[cy][cx];
+ len = mp3enc_hlen_11[cy][cx];
+ break;
+ case 12:
+ code = mp3enc_hcode_12[cy][cx];
+ len = mp3enc_hlen_12[cy][cx];
+ break;
+ case 13:
+ code = mp3enc_hcode_13[cy][cx];
+ len = mp3enc_hlen_13[cy][cx];
+ break;
+ case 15:
+ code = mp3enc_hcode_15[cy][cx];
+ len = mp3enc_hlen_15[cy][cx];
+ break;
+ case 16:
+ case 17:
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ code = mp3enc_hcode_16[cy][cx];
+ len = mp3enc_hlen_16[cy][cx];
+ break;
+ case 24:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ code = mp3enc_hcode_24[cy][cx];
+ len = mp3enc_hlen_24[cy][cx];
+ break;
+ }
+
+ bs.put(code, len);
+ if (cx == 15 && lb > 0) {
+ bs.put(ax - 15, lb);
+ }
+ if (ax > 0) {
+ bs.put(x < 0 ? 1 : 0, 1);
+ }
+ if (cy == 15 && lb > 0) {
+ bs.put(ay - 15, lb);
+ }
+ if (ay > 0) {
+ bs.put(y < 0 ? 1 : 0, 1);
+ }
+}
+
+// Write count1 region (quadruples of {-1, 0, 1}).
+static void mp3enc_write_count1(mp3enc_bs & bs, const int * ix, int start, int count, int table_sel) {
+ const uint8_t * ctab_code = table_sel ? mp3enc_count1b_code : mp3enc_count1a_code;
+ const uint8_t * ctab_len = table_sel ? mp3enc_count1b_len : mp3enc_count1a_len;
+
+ for (int q = 0; q < count; q++) {
+ int i = start + q * 4;
+ int v = abs(ix[i]);
+ int w = abs(ix[i + 1]);
+ int x = abs(ix[i + 2]);
+ int y = abs(ix[i + 3]);
+ int idx = v * 8 + w * 4 + x * 2 + y;
+
+ bs.put(ctab_code[idx], ctab_len[idx]);
+ if (v) {
+ bs.put(ix[i] < 0 ? 1 : 0, 1);
+ }
+ if (w) {
+ bs.put(ix[i + 1] < 0 ? 1 : 0, 1);
+ }
+ if (x) {
+ bs.put(ix[i + 2] < 0 ? 1 : 0, 1);
+ }
+ if (y) {
+ bs.put(ix[i + 3] < 0 ? 1 : 0, 1);
+ }
+ }
+}
+
+// Choose the best Huffman table for a region of pairs.
+// Tries all candidate tables and returns the one with fewest bits.
+static int mp3enc_choose_table(const int * ix, int start, int count) {
+ if (count <= 0) {
+ return 0;
+ }
+
+ // Find max absolute value in this region
+ int maxval = 0;
+ for (int i = start; i < start + count * 2; i++) {
+ int a = abs(ix[i]);
+ if (a > maxval) {
+ maxval = a;
+ }
+ }
+
+ if (maxval == 0) {
+ return 0;
+ }
+
+ // Candidate tables grouped by max entry:
+ // max 1: tables 1
+ // max 2: tables 2, 3
+ // max 3: tables 5, 6
+ // max 5: tables 7, 8, 9
+ // max 7: tables 10, 11, 12
+ // max 15: tables 13, 15
+ // max >15: tables 16..31 (with linbits)
+ static const int candidates[][8] = {
+ { 1, 0 }, // maxval = 1
+ { 2, 3, 0 }, // maxval = 2
+ { 5, 6, 0 }, // maxval = 3
+ { 7, 8, 9, 0 }, // maxval = 4..5
+ { 7, 8, 9, 0 }, // (same)
+ { 10, 11, 12, 0 }, // maxval = 6..7
+ { 10, 11, 12, 0 }, // (same)
+ { 13, 15, 0 }, // maxval = 8..15
+ };
+ // For values > 15, need linbits tables
+ static const int linbit_tables[] = { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0 };
+
+ const int * cands;
+ if (maxval <= 1) {
+ cands = candidates[0];
+ } else if (maxval <= 2) {
+ cands = candidates[1];
+ } else if (maxval <= 3) {
+ cands = candidates[2];
+ } else if (maxval <= 5) {
+ cands = candidates[3];
+ } else if (maxval <= 7) {
+ cands = candidates[5];
+ } else if (maxval <= 15) {
+ cands = candidates[7];
+ } else {
+ cands = linbit_tables;
+ }
+
+ int best_table = cands[0];
+ int best_bits = 999999;
+
+ for (int c = 0; cands[c] != 0; c++) {
+ int t = cands[c];
+ // For linbits tables, skip if linbits too small for our max value
+ if (maxval > 15 && mp3enc_linbits[t] > 0) {
+ int max_encodable = 15 + (1 << mp3enc_linbits[t]) - 1;
+ if (maxval > max_encodable) {
+ continue;
+ }
+ }
+
+ int total = 0;
+ for (int p = 0; p < count; p++) {
+ total += mp3enc_pair_bits(t, ix[start + p * 2], ix[start + p * 2 + 1]);
+ }
+ if (total < best_bits) {
+ best_bits = total;
+ best_table = t;
+ }
+ }
+ return best_table;
+}
diff --git a/otherarch/acestep/mp3/mp3enc-mdct.h b/otherarch/acestep/mp3/mp3enc-mdct.h
new file mode 100644
index 000000000..538e34eb0
--- /dev/null
+++ b/otherarch/acestep/mp3/mp3enc-mdct.h
@@ -0,0 +1,64 @@
+#pragma once
+// mp3enc-mdct.h
+// Forward MDCT for the MP3 encoder: 36 subband samples -> 18 frequency lines.
+// Window and MDCT are combined into a single step (ISO 11172-3 Annex C).
+// Part of mp3enc. MIT license.
+
+#include
+#include
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+// Forward MDCT-36 for long blocks.
+// in[36] = prev[18] + cur[18] (raw subband samples)
+// out[18] = MDCT frequency coefficients
+//
+// Formula (ISO 11172-3 Annex C):
+// out[k] = (1/9) * sum(n=0..35) in[n] * sin(pi/36*(n+0.5)) * cos(pi/72*(2n+19)*(2k+1))
+static void mp3enc_mdct36(const float * in, float * out) {
+ for (int k = 0; k < 18; k++) {
+ float sum = 0.0f;
+ for (int n = 0; n < 36; n++) {
+ float w = sinf((float) M_PI / 36.0f * ((float) n + 0.5f));
+ float c = cosf((float) M_PI / 72.0f * (float) (2 * n + 19) * (float) (2 * k + 1));
+ sum += in[n] * w * c;
+ }
+ out[k] = sum * (1.0f / 9.0f);
+ }
+}
+
+// Alias reduction butterfly between adjacent subbands.
+// Applied after MDCT, before quantization.
+// ISO 11172-3 Table B.9 coefficients (from minimp3 CC0).
+//
+// For each pair of adjacent bands (band, band+1):
+// mdct[band][17-i] and mdct[band+1][i] are butterflied with cs/ca.
+static void mp3enc_alias_reduce(float * mdct_out) {
+ for (int band = 1; band < 32; band++) {
+ float * a = mdct_out + (band - 1) * 18; // previous band
+ float * b = mdct_out + band * 18; // current band
+ for (int i = 0; i < 8; i++) {
+ float u = a[17 - i];
+ float d = b[i];
+ a[17 - i] = u * mp3enc_cs[i] - d * mp3enc_ca[i];
+ b[i] = d * mp3enc_cs[i] + u * mp3enc_ca[i];
+ }
+ }
+}
+
+// Process all 32 subbands for one granule (long blocks only).
+// sb_samples layout: prev_gr[32][18] and cur_gr[32][18] (band major).
+// mdct_out[576]: output frequency lines (32 subbands * 18 lines)
+static void mp3enc_mdct_granule(const float sb_prev[32][18], const float sb_cur[32][18], float * mdct_out) {
+ for (int band = 0; band < 32; band++) {
+ float mdct_in[36];
+ for (int k = 0; k < 18; k++) {
+ mdct_in[k] = sb_prev[band][k];
+ mdct_in[k + 18] = sb_cur[band][k];
+ }
+ mp3enc_mdct36(mdct_in, mdct_out + band * 18);
+ }
+
+ mp3enc_alias_reduce(mdct_out);
+}
diff --git a/otherarch/acestep/mp3/mp3enc-psy.h b/otherarch/acestep/mp3/mp3enc-psy.h
new file mode 100644
index 000000000..fd1926285
--- /dev/null
+++ b/otherarch/acestep/mp3/mp3enc-psy.h
@@ -0,0 +1,304 @@
+#pragma once
+
+// mp3enc-psy.h
+// Psychoacoustic model for MP3 encoding.
+// Computes masking thresholds per scalefactor band from MDCT coefficients.
+//
+// Based on ISO 11172-3 Annex D principles:
+// - Absolute threshold of hearing (ATH)
+// - Bark-domain asymmetric spreading function (Schroeder)
+// - Tonal vs noise masking detection (spectral flatness)
+// - Variable masking offset: tonal -14.5 dB, noise -5.5 dB
+//
+// Constants from ISO 11172-3 Annex D and Zwicker (1961).
+// MIT license.
+
+#include
+
+// Number of scalefactor bands for long blocks
+#define MP3ENC_PSY_SFB_MAX 21
+
+// Absolute threshold of hearing in dB SPL.
+// ISO formula: ATH(f) = 3.64*(f/1000)^-0.8 - 6.5*exp(-0.6*(f/1000-3.3)^2) + 1e-3*(f/1000)^4
+// Minimum clamped at -20 dB to avoid numerical issues.
+static inline float mp3enc_ath_db(float freq_hz) {
+ if (freq_hz < 10.0f) {
+ freq_hz = 10.0f;
+ }
+ float fk = freq_hz * 0.001f;
+ float ath = 3.64f * powf(fk, -0.8f) - 6.5f * expf(-0.6f * (fk - 3.3f) * (fk - 3.3f)) + 0.001f * fk * fk * fk * fk;
+ if (ath < -20.0f) {
+ ath = -20.0f;
+ }
+ return ath;
+}
+
+// Convert frequency in Hz to Bark scale.
+// Traunmuller (1990) approximation, accurate to ~0.05 Bark.
+static inline float mp3enc_hz_to_bark(float f) {
+ if (f < 1.0f) {
+ f = 1.0f;
+ }
+ return 13.0f * atanf(0.00076f * f) + 3.5f * atanf((f / 7500.0f) * (f / 7500.0f));
+}
+
+// Schroeder spreading function in dB.
+// dz = bark distance from masker to maskee (positive = maskee above masker).
+// This models the asymmetric excitation pattern of the basilar membrane:
+// steep below the masker (~27 dB/Bark), shallow above (~10-25 dB/Bark).
+// From Schroeder, Atal, Hall (1979).
+static inline float mp3enc_spreading_db(float dz) {
+ float t = dz + 0.474f;
+ return 15.81f + 7.5f * t - 17.5f * sqrtf(1.0f + t * t);
+}
+
+// Psychoacoustic model state.
+struct mp3enc_psy {
+ // Output: allowed distortion energy per SFB
+ float xmin[MP3ENC_PSY_SFB_MAX];
+
+ // Output: perceptual entropy for this granule/channel (ISO 11172-3 Annex D).
+ // Higher PE = more complex signal = needs more bits.
+ float pe;
+
+ // Forward masking: previous granule's masking energy (per channel)
+ float prev_mask[2][MP3ENC_PSY_SFB_MAX];
+
+ // Pre-echo control: 2 previous granules of spread energy (per channel).
+ // Used to prevent masking threshold from rising too fast on transients
+ // (ISO 11172-3 Annex D, l3psy.c lines 610-616).
+ float nb_1[2][MP3ENC_PSY_SFB_MAX]; // previous granule spread energy
+ float nb_2[2][MP3ENC_PSY_SFB_MAX]; // 2 granules ago spread energy
+
+ // Precomputed per-SFB data (set once per sample rate)
+ float ath_energy[3][MP3ENC_PSY_SFB_MAX]; // [sr_index][sfb]: ATH in linear power
+ float sfb_bark[3][MP3ENC_PSY_SFB_MAX]; // [sr_index][sfb]: center freq in Bark
+ bool ath_valid;
+
+ void init() {
+ ath_valid = false;
+ pe = 0.0f;
+ memset(xmin, 0, sizeof(xmin));
+ memset(prev_mask, 0, sizeof(prev_mask));
+ memset(nb_1, 0, sizeof(nb_1));
+ memset(nb_2, 0, sizeof(nb_2));
+ }
+
+ // Precompute ATH energy and Bark positions per SFB.
+ // Must be called once before compute().
+ void init_ath(int sr_index, const uint8_t * sfb_table, int sample_rate) {
+ // MDCT has 576 lines covering 0 to samplerate/2.
+ // Each line represents a frequency bin of width samplerate / (2*576).
+ float freq_per_line = (float) sample_rate / (2.0f * 576.0f);
+
+ int pos = 0;
+ for (int sfb = 0; sfb < MP3ENC_PSY_SFB_MAX; sfb++) {
+ int width = sfb_table[sfb];
+ if (width == 0) {
+ ath_energy[sr_index][sfb] = 1e-20f;
+ sfb_bark[sr_index][sfb] = 0.0f;
+ continue;
+ }
+
+ // Center frequency of this SFB
+ float center_freq = ((float) pos + (float) width * 0.5f) * freq_per_line;
+ sfb_bark[sr_index][sfb] = mp3enc_hz_to_bark(center_freq);
+
+ // ATH: minimum of all lines in the band (most permissive)
+ float ath_min_db = 200.0f;
+ for (int j = 0; j < width; j++) {
+ float freq = ((float) (pos + j) + 0.5f) * freq_per_line;
+ float db = mp3enc_ath_db(freq);
+ if (db < ath_min_db) {
+ ath_min_db = db;
+ }
+ }
+
+ // Convert dB SPL to linear power, scaled by band width.
+ // Reference at 120 dB SPL (tuned empirically). A higher reference
+ // makes the ATH floor less dominant relative to the spreading
+ // function, so bits are spent on perceptual masking rather than
+ // fighting the absolute hearing threshold in quiet passages.
+ float ath_linear = powf(10.0f, (ath_min_db - 120.0f) * 0.1f) * (float) width;
+ ath_energy[sr_index][sfb] = ath_linear;
+
+ pos += width;
+ }
+ ath_valid = true;
+ }
+
+ // Compute masking thresholds for one granule/channel.
+ //
+ // Algorithm:
+ // 1. Compute energy per SFB from MDCT coefficients
+ // 2. Estimate tonality per SFB (spectral flatness measure)
+ // 3. Compute masking offset per SFB based on tonality
+ // 4. Apply Bark-domain spreading function
+ // 5. Combine spread masking with ATH
+ //
+ // mdct: 576 MDCT coefficients (after MS stereo if applicable)
+ // sfb_table: SFB widths for this sample rate
+ // sr_index: sample rate index
+ void compute(const float * mdct, const uint8_t * sfb_table, int sr_index, int ch = 0) {
+ float energy[MP3ENC_PSY_SFB_MAX];
+ float tonality[MP3ENC_PSY_SFB_MAX];
+
+ // Step 1: compute energy per SFB
+ int pos = 0;
+ for (int sfb = 0; sfb < MP3ENC_PSY_SFB_MAX; sfb++) {
+ int width = sfb_table[sfb];
+ float e = 0.0f;
+ for (int j = 0; j < width; j++) {
+ float x = mdct[pos + j];
+ e += x * x;
+ }
+ energy[sfb] = e;
+ pos += width;
+ }
+
+ // Step 2: estimate tonality per SFB using spectral flatness measure (SFM).
+ // SFM = geometric_mean(power) / arithmetic_mean(power)
+ // SFM = 1.0 for flat noise, SFM -> 0 for a single tone.
+ // We use log domain to avoid overflow: log(geometric_mean) = mean(log(power)).
+ pos = 0;
+ for (int sfb = 0; sfb < MP3ENC_PSY_SFB_MAX; sfb++) {
+ int width = sfb_table[sfb];
+ if (width == 0 || energy[sfb] < 1e-20f) {
+ tonality[sfb] = 0.5f; // default: assume mixed
+ pos += width;
+ continue;
+ }
+
+ float log_sum = 0.0f;
+ float arith = 0.0f;
+ int n_active = 0;
+ for (int j = 0; j < width; j++) {
+ float p = mdct[pos + j] * mdct[pos + j];
+ if (p > 1e-20f) {
+ log_sum += logf(p);
+ n_active++;
+ }
+ arith += p;
+ }
+
+ if (n_active < 2) {
+ // Single line or silence: treat as tonal
+ tonality[sfb] = 0.0f;
+ } else {
+ float geom_log = log_sum / (float) n_active;
+ float arith_mean = arith / (float) n_active;
+ // SFM in log domain: log(geom/arith) = geom_log - log(arith)
+ float sfm_log = geom_log - logf(arith_mean);
+ // sfm_log is <= 0. For flat spectrum sfm_log ~ 0, for tonal sfm_log << 0.
+ // Map to tonality index alpha in [0,1]:
+ // alpha = min(sfm_log / log(0.01), 1.0)
+ // log(0.01) = -4.605; so if sfm_log < -4.6 we consider it fully tonal.
+ float alpha = sfm_log / -4.605f;
+ if (alpha < 0.0f) {
+ alpha = 0.0f;
+ }
+ if (alpha > 1.0f) {
+ alpha = 1.0f;
+ }
+ tonality[sfb] = alpha; // 0 = noise, 1 = tonal
+ }
+ pos += width;
+ }
+
+ // Step 3: compute masking offset per SFB.
+ // TMN/NMT from ISO 11172-3, relaxed empirically for 128kbps.
+ // TMN=13.0 (tonal masking noise), NMT=4.5 (noise masking tone).
+ float offset_linear[MP3ENC_PSY_SFB_MAX];
+ for (int sfb = 0; sfb < MP3ENC_PSY_SFB_MAX; sfb++) {
+ float alpha = tonality[sfb];
+ float offset_db = alpha * 13.0f + (1.0f - alpha) * 4.5f;
+ offset_linear[sfb] = powf(10.0f, -offset_db * 0.1f);
+ }
+
+ // Step 4: Bark-domain spreading function.
+ // For each target SFB, sum the spread contributions from all source SFBs.
+ // The spreading function is asymmetric: steep below, shallow above.
+ float spread_energy[MP3ENC_PSY_SFB_MAX];
+ for (int j = 0; j < MP3ENC_PSY_SFB_MAX; j++) {
+ float sum = 0.0f;
+ float bark_j = sfb_bark[sr_index][j];
+
+ for (int i = 0; i < MP3ENC_PSY_SFB_MAX; i++) {
+ if (energy[i] < 1e-20f) {
+ continue;
+ }
+
+ float bark_i = sfb_bark[sr_index][i];
+ float dz_raw = bark_j - bark_i;
+
+ // Asymmetric spreading (ISO 11172-3 psy model 2, L3para_read).
+ // Upward masking (dz > 0): gentle slope, low freqs mask highs well.
+ // Downward masking (dz < 0): steep slope, highs mask lows poorly.
+ float dz = (dz_raw >= 0.0f) ? dz_raw * 1.35f : dz_raw * 2.7f;
+
+ // Spreading function value in dB
+ float sf_db = mp3enc_spreading_db(dz);
+
+ // Only apply if spreading is above -60 dB (optimization)
+ if (sf_db < -60.0f) {
+ continue;
+ }
+
+ float sf_linear = powf(10.0f, sf_db * 0.1f);
+ sum += energy[i] * sf_linear;
+ }
+ spread_energy[j] = sum;
+ }
+
+ // Step 5: combine spreading + offset, then apply pre-echo control.
+ // Pre-echo (ISO 11172-3 Annex D): prevent threshold from rising too fast
+ // on transients. Clamp current nb by 2x previous and 16x two-back.
+ // xmin = max(ath, min(nb, 2*nb_1, 16*nb_2))
+ for (int sfb = 0; sfb < MP3ENC_PSY_SFB_MAX; sfb++) {
+ float nb = spread_energy[sfb] * offset_linear[sfb];
+
+ // Pre-echo clamp: use history to limit sudden threshold rises
+ float clamped = nb;
+ float lim1 = 2.0f * nb_1[ch][sfb];
+ float lim2 = 16.0f * nb_2[ch][sfb];
+ if (lim1 > 0.0f && lim1 < clamped) {
+ clamped = lim1;
+ }
+ if (lim2 > 0.0f && lim2 < clamped) {
+ clamped = lim2;
+ }
+
+ // Update history (store UN-clamped nb for future reference)
+ nb_2[ch][sfb] = nb_1[ch][sfb];
+ nb_1[ch][sfb] = nb;
+
+ // ATH floor
+ float ath = ath_energy[sr_index][sfb];
+ xmin[sfb] = (clamped > ath) ? clamped : ath;
+ }
+
+ // Step 6: forward masking (temporal).
+ // A loud granule raises the masking threshold for the next granule.
+ // At 44.1kHz one granule = 13ms. Forward masking decays ~12dB over 13ms.
+ // Decay factor: 10^(-12/10) = ~0.063.
+ static const float fwd_decay = 0.063f;
+ for (int sfb = 0; sfb < MP3ENC_PSY_SFB_MAX; sfb++) {
+ float fwd = prev_mask[ch][sfb] * fwd_decay;
+ if (fwd > xmin[sfb]) {
+ xmin[sfb] = fwd;
+ }
+ // Save current mask (spread_energy, not xmin) for next granule
+ prev_mask[ch][sfb] = spread_energy[sfb];
+ }
+
+ // Step 7: perceptual entropy (ISO 11172-3 Annex D).
+ // PE = sum of width * log(energy/threshold) for bands where energy > threshold.
+ // Used by the bit reservoir to give more bits to complex granules.
+ pe = 0.0f;
+ for (int sfb = 0; sfb < MP3ENC_PSY_SFB_MAX; sfb++) {
+ if (energy[sfb] > xmin[sfb] && xmin[sfb] > 1e-20f) {
+ pe += (float) sfb_table[sfb] * logf(energy[sfb] / xmin[sfb]);
+ }
+ }
+ }
+};
diff --git a/otherarch/acestep/mp3/mp3enc-quant.h b/otherarch/acestep/mp3/mp3enc-quant.h
new file mode 100644
index 000000000..0d67242de
--- /dev/null
+++ b/otherarch/acestep/mp3/mp3enc-quant.h
@@ -0,0 +1,607 @@
+#pragma once
+// mp3enc-quant.h
+// Quantization: inner loop (global_gain search) + outer loop (scalefactor iteration).
+// ISO 11172-3 Annex C, encoding process.
+// Part of mp3enc. MIT license.
+
+#include
+#include
+
+// Quantize one MDCT coefficient using the MP3 power law quantizer.
+// xr = input MDCT value (float)
+// istep = 2^(-3/16 * (global_gain - 210))
+// Returns quantized integer (always >= 0; sign stored separately).
+static inline int mp3enc_quantize_value(float xr, float istep) {
+ float ax = fabsf(xr);
+ // ix = nint(ax^0.75 * istep)
+ float val = sqrtf(ax * sqrtf(ax)) * istep;
+ if (val > 8191.0f) {
+ return 8191;
+ }
+ int ix = (int) (val + 0.36f); // rounding bias for better SNR
+ return ix;
+}
+
+// Quantize 576 MDCT coefficients with per-band scalefactors.
+// The scalefactor amplifies each band's coefficients before quantization,
+// giving finer resolution to bands that need it.
+//
+// Decoder (minimp3) dequantization:
+// scf_shift = scalefac_scale + 1
+// band_gain = 2^(-(sf << scf_shift) / 4)
+// which gives 2^(-sf/2) for scalefac_scale=0, 2^(-sf) for scalefac_scale=1
+//
+// Encoder compensates: sfb_amp = 2^(ss * sf) where ss = 0.5 or 1.0
+static void mp3enc_quantize_sfb(const float * xr,
+ int * ix,
+ int global_gain,
+ const int * scalefac,
+ int scalefac_scale,
+ int preflag,
+ const uint8_t * sfb_table) {
+ float istep = powf(2.0f, -0.1875f * (float) (global_gain - 210));
+ float ss = scalefac_scale ? 1.0f : 0.5f;
+
+ int pos = 0;
+ for (int sfb = 0; sfb_table[sfb] != 0 && pos < 576; sfb++) {
+ int width = sfb_table[sfb];
+
+ int sf = scalefac[sfb] + (preflag ? mp3enc_pretab[sfb] : 0);
+ float sfb_amp = (sf > 0) ? powf(2.0f, ss * (float) sf) : 1.0f;
+
+ for (int j = 0; j < width && pos < 576; j++, pos++) {
+ float xr_adj = fabsf(xr[pos]) * sfb_amp;
+ float val = sqrtf(xr_adj * sqrtf(xr_adj)) * istep;
+ int q;
+ if (val > 8191.0f) {
+ q = 8191;
+ } else {
+ q = (int) (val + 0.36f);
+ }
+ ix[pos] = (xr[pos] >= 0.0f) ? q : -q;
+ }
+ }
+ while (pos < 576) {
+ ix[pos++] = 0;
+ }
+}
+
+// Simple quantize without scalefactors (Phase 1 compatible).
+// Kept for the initial global_gain search before outer loop kicks in.
+static void mp3enc_quantize(const float * xr, int * ix, int global_gain) {
+ float istep = powf(2.0f, -0.1875f * (float) (global_gain - 210));
+ for (int i = 0; i < 576; i++) {
+ int q = mp3enc_quantize_value(xr[i], istep);
+ ix[i] = (xr[i] >= 0.0f) ? q : -q;
+ }
+}
+
+// Compute quantization noise energy per SFB.
+// Noise = sum((xr[i] - dequant(ix[i]))^2) for each band.
+// Dequant matches minimp3: xr = |ix|^(4/3) * 2^((gg-210)/4) * 2^(-ss*sf)
+// where ss = 0.5 (scalefac_scale=0) or 1.0 (scalefac_scale=1).
+static void mp3enc_calc_noise(const float * xr,
+ const int * ix,
+ int global_gain,
+ const int * scalefac,
+ int scalefac_scale,
+ int preflag,
+ const uint8_t * sfb_table,
+ float * noise) {
+ float step = powf(2.0f, 0.25f * (float) (global_gain - 210));
+ float ss = scalefac_scale ? 1.0f : 0.5f;
+
+ int pos = 0;
+ for (int sfb = 0; sfb_table[sfb] != 0 && pos < 576; sfb++) {
+ int width = sfb_table[sfb];
+ int sf = scalefac[sfb] + (preflag ? mp3enc_pretab[sfb] : 0);
+ float sfb_gain = (sf > 0) ? powf(2.0f, -ss * (float) sf) : 1.0f;
+
+ float n = 0.0f;
+ for (int j = 0; j < width && pos < 576; j++, pos++) {
+ // Dequantize
+ int aix = abs(ix[pos]);
+ float dequant = (float) aix;
+ // |ix|^(4/3): use pow for accuracy
+ if (aix > 0) {
+ dequant = powf((float) aix, 4.0f / 3.0f) * step * sfb_gain;
+ } else {
+ dequant = 0.0f;
+ }
+ if (ix[pos] < 0) {
+ dequant = -dequant;
+ }
+ float diff = xr[pos] - dequant;
+ n += diff * diff;
+ }
+ noise[sfb] = n;
+ }
+}
+
+// Count total Huffman bits for 576 quantized values.
+// Also fills out granule info: big_values, table_select, count1, etc.
+// Returns total bits for Huffman data (not including scalefactors).
+static int mp3enc_count_bits(const int * ix, mp3enc_granule_info & gi, const uint8_t * sfb_table, int sr_index) {
+ (void) sr_index;
+
+ // Find the three regions: big_values, count1, rzero
+ int rzero_pairs = mp3enc_count_rzero(ix, 576);
+ int nz_end = 576 - rzero_pairs * 2;
+
+ // count1: quadruples with |val| <= 1, scanning from end of nonzero region
+ int c1_start = nz_end;
+ int c1_count = 0;
+ {
+ int i = nz_end - 4;
+ while (i >= 0 && abs(ix[i]) <= 1 && abs(ix[i + 1]) <= 1 && abs(ix[i + 2]) <= 1 && abs(ix[i + 3]) <= 1) {
+ c1_start = i;
+ c1_count++;
+ i -= 4;
+ }
+ }
+
+ gi.big_values = c1_start / 2;
+
+ int bv_end = c1_start; // end of big_values region (pair aligned)
+
+ // Region boundaries from SFB table.
+ int region_end[3] = { 0, 0, bv_end };
+
+ if (gi.block_type == 0) {
+ // Try a few region0_count values and pick the best.
+ int total_sfb = 0;
+ int sfb_acc[22] = {};
+ {
+ int acc = 0;
+ for (int sfb = 0; sfb < 22; sfb++) {
+ acc += sfb_table[sfb];
+ sfb_acc[sfb] = acc;
+ if (acc <= bv_end) {
+ total_sfb = sfb + 1;
+ }
+ }
+ }
+
+ int best_r0 = 7, best_r1 = 0, best_rbits = 999999;
+ for (int r0t = 5; r0t < 11 && r0t < total_sfb; r0t++) {
+ int r1t = total_sfb - r0t - 1;
+ if (r1t < 0) {
+ r1t = 0;
+ }
+ if (r1t > 7) {
+ r1t = 7;
+ }
+ int re0 = (sfb_acc[r0t] < bv_end) ? sfb_acc[r0t] : bv_end;
+ int sfb1 = r0t + r1t + 1;
+ if (sfb1 > 21) {
+ sfb1 = 21;
+ }
+ int re1 = (sfb_acc[sfb1] < bv_end) ? sfb_acc[sfb1] : bv_end;
+
+ int t0 = mp3enc_choose_table(ix, 0, re0 / 2);
+ int t1 = mp3enc_choose_table(ix, re0, (re1 - re0) / 2);
+ int t2 = mp3enc_choose_table(ix, re1, (bv_end - re1) / 2);
+ int rbits = 0;
+ for (int i = 0; i < re0; i += 2) {
+ rbits += mp3enc_pair_bits(t0, ix[i], ix[i + 1]);
+ }
+ for (int i = re0; i < re1; i += 2) {
+ rbits += mp3enc_pair_bits(t1, ix[i], ix[i + 1]);
+ }
+ for (int i = re1; i < bv_end; i += 2) {
+ rbits += mp3enc_pair_bits(t2, ix[i], ix[i + 1]);
+ }
+ if (rbits < best_rbits) {
+ best_rbits = rbits;
+ best_r0 = r0t;
+ best_r1 = r1t;
+ }
+ }
+ gi.region0_count = best_r0;
+ gi.region1_count = best_r1;
+
+ // Compute region end positions
+ {
+ int acc = 0;
+ for (int sfb = 0; sfb <= gi.region0_count; sfb++) {
+ acc += sfb_table[sfb];
+ }
+ region_end[0] = (acc < bv_end) ? acc : bv_end;
+ }
+ {
+ int acc = 0;
+ for (int sfb = 0; sfb <= gi.region0_count + gi.region1_count + 1; sfb++) {
+ acc += sfb_table[sfb];
+ }
+ region_end[1] = (acc < bv_end) ? acc : bv_end;
+ }
+ region_end[2] = bv_end;
+ }
+
+ // Choose Huffman tables for each region (3 regions for long blocks)
+ int n_regions = 3;
+ int total_bits = 0;
+ int prev_end = 0;
+
+ for (int r = 0; r < n_regions; r++) {
+ int pairs = (region_end[r] - prev_end) / 2;
+ gi.table_select[r] = mp3enc_choose_table(ix, prev_end, pairs);
+ for (int p = 0; p < pairs; p++) {
+ int i = prev_end + p * 2;
+ total_bits += mp3enc_pair_bits(gi.table_select[r], ix[i], ix[i + 1]);
+ }
+ prev_end = region_end[r];
+ }
+
+ // Count1 region: try both tables, pick the smaller
+ int c1_bits_a = 0, c1_bits_b = 0;
+ for (int q = 0; q < c1_count; q++) {
+ int i = c1_start + q * 4;
+ int v = abs(ix[i]), w = abs(ix[i + 1]), x = abs(ix[i + 2]), y = abs(ix[i + 3]);
+ int idx = v * 8 + w * 4 + x * 2 + y;
+ int signs = (v > 0) + (w > 0) + (x > 0) + (y > 0);
+ c1_bits_a += mp3enc_count1a_len[idx] + signs;
+ c1_bits_b += mp3enc_count1b_len[idx] + signs;
+ }
+
+ if (c1_bits_a <= c1_bits_b) {
+ gi.count1table_select = 0;
+ total_bits += c1_bits_a;
+ } else {
+ gi.count1table_select = 1;
+ total_bits += c1_bits_b;
+ }
+
+ return total_bits;
+}
+
+// Compute part2_length: number of bits for scalefactors.
+// Depends on scalefac_compress and which bands are transmitted.
+static int mp3enc_part2_length(const mp3enc_granule_info & gi, int gr, const int scfsi[4]) {
+ int slen1 = mp3enc_slen[0][gi.scalefac_compress];
+ int slen2 = mp3enc_slen[1][gi.scalefac_compress];
+ int bits = 0;
+
+ // Long blocks: 4 scfsi groups
+ static const int band_start[4] = { 0, 6, 11, 16 };
+ static const int band_end[4] = { 6, 11, 16, 21 };
+
+ for (int b = 0; b < 4; b++) {
+ if (gr == 0 || scfsi[b] == 0) {
+ int slen = (b < 2) ? slen1 : slen2;
+ int count = band_end[b] - band_start[b];
+ bits += count * slen;
+ }
+ }
+ return bits;
+}
+
+// Find the best scalefac_compress for the current scalefactors.
+// Returns the compress index (0..15) that can represent all scalefactors
+// with the fewest total bits.
+static int mp3enc_best_scalefac_compress(const int * scalefac_l) {
+ // Find max scalefactor in each group
+ int max1 = 0; // bands 0..10 (slen1)
+ int max2 = 0; // bands 11..20 (slen2)
+ for (int sfb = 0; sfb < 11; sfb++) {
+ if (scalefac_l[sfb] > max1) {
+ max1 = scalefac_l[sfb];
+ }
+ }
+ for (int sfb = 11; sfb < 21; sfb++) {
+ if (scalefac_l[sfb] > max2) {
+ max2 = scalefac_l[sfb];
+ }
+ }
+
+ // Try all 16 compress values, pick the one with fewest bits
+ // that can represent the max values
+ int best_compress = 0;
+ int best_bits = 999;
+
+ for (int c = 0; c < 16; c++) {
+ int s1 = mp3enc_slen[0][c];
+ int s2 = mp3enc_slen[1][c];
+ int max_val1 = (s1 > 0) ? ((1 << s1) - 1) : 0;
+ int max_val2 = (s2 > 0) ? ((1 << s2) - 1) : 0;
+
+ // Can this compress value represent our scalefactors?
+ if (max1 > max_val1 || max2 > max_val2) {
+ continue;
+ }
+
+ // Total bits for scalefactors (granule 0, no scfsi)
+ int bits = 11 * s1 + 10 * s2;
+ if (bits < best_bits) {
+ best_bits = bits;
+ best_compress = c;
+ }
+ }
+ return best_compress;
+}
+
+// Inner loop: find minimum global_gain where Huffman bits fit the budget.
+// Bit count is monotonically decreasing with global_gain (higher gain = coarser
+// quantization = fewer bits). We want the smallest gain where bits <= budget.
+//
+// When hint_gain >= 0 (from a previous inner_loop call in the same outer loop),
+// the optimal gain is typically within a few steps. We scan linearly from the
+// hint instead of doing a full binary search on [0, 255]. This cuts the typical
+// iteration count from 8 to 3-4.
+//
+// scalefac: per-band scalefactors (NULL for initial call before outer loop).
+// hint_gain: previous global_gain from last inner_loop call, or -1 for full search.
+static int mp3enc_inner_loop(const float * xr,
+ int * ix,
+ mp3enc_granule_info & gi,
+ int available_bits,
+ const uint8_t * sfb_table,
+ int sr_index,
+ const int * scalefac = nullptr,
+ int hint_gain = -1) {
+ // quantize + count_bits helper (avoids repeating the branch 5 times)
+ auto try_gain = [&](int g) -> int {
+ if (!scalefac || gi.scalefac_compress == 0) {
+ mp3enc_quantize(xr, ix, g);
+ } else {
+ mp3enc_quantize_sfb(xr, ix, g, scalefac, gi.scalefac_scale, gi.preflag, sfb_table);
+ }
+ for (int i = 0; i < 576; i++) {
+ if (abs(ix[i]) >= 8191) {
+ return available_bits + 1; // saturated
+ }
+ }
+ return mp3enc_count_bits(ix, gi, sfb_table, sr_index);
+ };
+
+ int best_gain = 210;
+ int best_bits = available_bits + 1;
+
+ if (hint_gain >= 0) {
+ // Linear scan from hint. Typical cost: 3-4 try_gain calls.
+ int bits = try_gain(hint_gain);
+
+ if (bits <= available_bits) {
+ // Hint fits. Scan downward to find the minimum valid gain.
+ best_gain = hint_gain;
+ best_bits = bits;
+ for (int g = hint_gain - 1; g >= 0 && g >= hint_gain - 10; g--) {
+ bits = try_gain(g);
+ if (bits > available_bits) {
+ break;
+ }
+ best_gain = g;
+ best_bits = bits;
+ }
+ } else {
+ // Hint doesn't fit. Scan upward to find the first valid gain.
+ bool found = false;
+ for (int g = hint_gain + 1; g <= 255 && g <= hint_gain + 20; g++) {
+ bits = try_gain(g);
+ if (bits <= available_bits) {
+ best_gain = g;
+ best_bits = bits;
+ found = true;
+ break;
+ }
+ }
+ // Fallback: if scan didn't find it (rare, e.g. scalefac_scale toggle),
+ // binary search on the remaining range.
+ if (!found) {
+ int lo = hint_gain + 21;
+ int hi = 255;
+ while (lo <= hi) {
+ int mid = (lo + hi) / 2;
+ bits = try_gain(mid);
+ if (bits <= available_bits) {
+ best_gain = mid;
+ best_bits = bits;
+ hi = mid - 1;
+ } else {
+ lo = mid + 1;
+ }
+ }
+ }
+ }
+ } else {
+ // No hint: full binary search on [0, 255].
+ int lo = 0, hi = 255;
+ while (lo <= hi) {
+ int mid = (lo + hi) / 2;
+ int bits = try_gain(mid);
+ if (bits <= available_bits) {
+ best_gain = mid;
+ best_bits = bits;
+ hi = mid - 1;
+ } else {
+ lo = mid + 1;
+ }
+ }
+ }
+
+ // Final quantization with the best gain
+ gi.global_gain = best_gain;
+ if (!scalefac || gi.scalefac_compress == 0) {
+ mp3enc_quantize(xr, ix, best_gain);
+ } else {
+ mp3enc_quantize_sfb(xr, ix, best_gain, scalefac, gi.scalefac_scale, gi.preflag, sfb_table);
+ }
+ best_bits = mp3enc_count_bits(ix, gi, sfb_table, sr_index);
+ return best_bits;
+}
+
+// Outer loop: iteratively adjust scalefactors to push quantization noise
+// below the masking thresholds computed by the psy model.
+//
+// Algorithm:
+// 1. Start with all scalefactors = 0, run inner loop
+// 2. Compute noise per SFB
+// 3. For each SFB where noise > xmin, bump its scalefactor
+// 4. Update scalefac_compress, recompute bit budget, re-run inner loop
+// 5. Repeat until noise is under control or we run out of iterations/bits
+//
+// xr: 576 MDCT coefficients
+// ix: 576 quantized output
+// gi: granule info (filled on return)
+// xmin: masking thresholds per SFB from psy model
+// available_bits: total bits for part2_3 (scalefactors + Huffman)
+// sfb_table: SFB widths
+// sr_index: sample rate index
+// gr: granule number (0 or 1)
+// scfsi: scfsi flags (for part2_length calculation)
+// Returns part2_3_length (scalefactor bits + Huffman bits).
+static int mp3enc_outer_loop(const float * xr,
+ int * ix,
+ mp3enc_granule_info & gi,
+ const float * xmin,
+ int available_bits,
+ const uint8_t * sfb_table,
+ int sr_index,
+ int gr,
+ const int scfsi[4]) {
+ // Initialize: no scalefactors
+ memset(&gi, 0, sizeof(gi));
+ gi.block_type = 0;
+
+ // Initial inner loop with flat quantization
+ int huff_bits = mp3enc_inner_loop(xr, ix, gi, available_bits, sfb_table, sr_index);
+ gi.part2_3_length = huff_bits;
+
+ // If no psy thresholds (all zero), skip outer loop
+ bool have_psy = false;
+ for (int sfb = 0; sfb < 21; sfb++) {
+ if (xmin[sfb] > 0.0f) {
+ have_psy = true;
+ break;
+ }
+ }
+ if (!have_psy) {
+ return gi.part2_3_length;
+ }
+
+ // Outer iteration loop (ISO 11172-3 Annex C.1.5.4.3).
+ // For each iteration:
+ // - compute distortion per SFB
+ // - bump scalefactor for EVERY band where noise > xmin
+ // - re-run inner loop with updated scalefactors
+ // - stop when all bands are under threshold or no bits left
+ //
+ // Max 25 passes: enough for convergence at all bitrates.
+ float noise[21];
+ int best_ix[576];
+ mp3enc_granule_info best_gi = gi;
+ int best_total = gi.part2_3_length;
+ int best_over = 21; // start pessimistic
+ memcpy(best_ix, ix, sizeof(best_ix));
+
+ for (int iter = 0; iter < 25; iter++) {
+ // Compute noise per SFB with current quantization
+ mp3enc_calc_noise(xr, ix, gi.global_gain, gi.scalefac_l, gi.scalefac_scale, gi.preflag, sfb_table, noise);
+
+ // Count bands over threshold
+ int over_count = 0;
+ for (int sfb = 0; sfb < 21; sfb++) {
+ if (xmin[sfb] > 0.0f && noise[sfb] > xmin[sfb]) {
+ over_count++;
+ }
+ }
+
+ // Track best result: prefer fewer over-threshold bands,
+ // then fewer total bits as tiebreaker.
+ if (over_count < best_over || (over_count == best_over && gi.part2_3_length < best_total)) {
+ best_gi = gi;
+ best_total = gi.part2_3_length;
+ best_over = over_count;
+ memcpy(best_ix, ix, sizeof(best_ix));
+ }
+
+ // If all bands are under threshold, we are done
+ if (over_count == 0) {
+ break;
+ }
+
+ // Bump scalefactor for EVERY band where noise > threshold.
+ // ISO outer loop: amplify all distorted bands by 1 step per iteration.
+ bool any_changed = false;
+ for (int sfb = 0; sfb < 21; sfb++) {
+ if (xmin[sfb] > 0.0f && noise[sfb] > xmin[sfb]) {
+ gi.scalefac_l[sfb]++;
+ any_changed = true;
+ }
+ }
+ if (!any_changed) {
+ break;
+ }
+
+ // Preflag: if HF bands need large scalefactors, enable preflag
+ // to get free amplification from the pretab table (ISO Table B.6).
+ // This saves bits: pretab adds 0-3 to HF scalefactors for free
+ // (encoded in a single bit rather than per-band bits).
+ if (!gi.preflag) {
+ int hf_need = 0;
+ for (int sfb = 11; sfb < 21; sfb++) {
+ if (gi.scalefac_l[sfb] >= 2 && mp3enc_pretab[sfb] > 0) {
+ hf_need++;
+ }
+ }
+ // Enable if at least 3 HF bands need boosting
+ if (hf_need >= 3) {
+ gi.preflag = 1;
+ for (int sfb = 0; sfb < 21; sfb++) {
+ gi.scalefac_l[sfb] -= mp3enc_pretab[sfb];
+ if (gi.scalefac_l[sfb] < 0) {
+ gi.scalefac_l[sfb] = 0;
+ }
+ }
+ }
+ }
+
+ // scalefac_scale: if any scalefactor exceeds 15 (4 bit max),
+ // double the step size. This halves all scalefactors but each
+ // step now represents sqrt(2) instead of 2^(1/4).
+ int max_sf = 0;
+ for (int sfb = 0; sfb < 21; sfb++) {
+ if (gi.scalefac_l[sfb] > max_sf) {
+ max_sf = gi.scalefac_l[sfb];
+ }
+ }
+ if (max_sf > 15 && !gi.scalefac_scale) {
+ gi.scalefac_scale = 1;
+ for (int sfb = 0; sfb < 21; sfb++) {
+ gi.scalefac_l[sfb] = (gi.scalefac_l[sfb] + 1) / 2;
+ }
+ }
+
+ // Clamp to 15
+ for (int sfb = 0; sfb < 21; sfb++) {
+ if (gi.scalefac_l[sfb] > 15) {
+ gi.scalefac_l[sfb] = 15;
+ }
+ }
+
+ // Update scalefac_compress and compute part2 bits
+ gi.scalefac_compress = mp3enc_best_scalefac_compress(gi.scalefac_l);
+ int part2 = mp3enc_part2_length(gi, gr, scfsi);
+ int huff_budget = available_bits - part2;
+ if (huff_budget < 0) {
+ break;
+ }
+
+ // Re-run inner loop
+ huff_bits = mp3enc_inner_loop(xr, ix, gi, huff_budget, sfb_table, sr_index, gi.scalefac_l, gi.global_gain);
+ int total = part2 + huff_bits;
+
+ if (total <= available_bits) {
+ gi.part2_3_length = total;
+ } else {
+ break;
+ }
+ }
+
+ // Restore best result
+ gi = best_gi;
+ memcpy(ix, best_ix, sizeof(best_ix));
+ gi.part2_3_length = best_total;
+
+ return gi.part2_3_length;
+}
diff --git a/otherarch/acestep/mp3/mp3enc-tables.h b/otherarch/acestep/mp3/mp3enc-tables.h
new file mode 100644
index 000000000..d2c902d5d
--- /dev/null
+++ b/otherarch/acestep/mp3/mp3enc-tables.h
@@ -0,0 +1,485 @@
+#pragma once
+// mp3enc-tables.h
+// Numerical constants for the MP3 encoder.
+// Extracted from minimp3 (CC0, github.com/lieff/minimp3).
+// Values match ISO 11172-3 Annex B+C.
+// MIT license.
+
+#include
+
+// Layer III bitrates in kbps, indexed by bitrate_index 0..14
+// Index 0 = free format, index 15 = forbidden
+static const int mp3enc_bitrate_kbps[15] = { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 };
+
+// MPEG1 sample rates, indexed by sampling_frequency field (2 bits)
+static const int mp3enc_samplerate[3] = { 44100, 48000, 32000 };
+
+// slen1 and slen2 from scalefac_compress (0..15)
+// slen1 = mp3enc_slen[0][i], slen2 = mp3enc_slen[1][i]
+static const uint8_t mp3enc_slen[2][16] = {
+ { 0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4 },
+ { 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3 }
+};
+
+// Pretab values added to scalefactors when preflag=1 (ISO Table B.6)
+static const uint8_t mp3enc_pretab[22] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0 };
+
+// SFB boundaries: long blocks (ISO Table B.8, from minimp3 CC0)
+// MPEG1 only: [0]=44100Hz, [1]=48000Hz, [2]=32000Hz
+// These correspond to minimp3 g_scf_long[5..7].
+static const uint8_t mp3enc_sfb_long[3][23] = {
+ { 4, 4, 4, 4, 4, 4, 6, 6, 8, 8, 10, 12, 16, 20, 24, 28, 34, 42, 50, 54, 76, 158, 0 },
+ { 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 10, 12, 16, 18, 22, 28, 34, 40, 46, 54, 54, 192, 0 },
+ { 4, 4, 4, 4, 4, 4, 6, 6, 8, 10, 12, 16, 20, 24, 30, 38, 46, 56, 68, 84, 102, 26, 0 }
+};
+
+// SFB boundaries: short blocks (ISO Table B.8, from minimp3 CC0)
+// [sr_index][band_width]
+// SFB boundaries: short blocks (ISO Table B.8, from minimp3 CC0)
+// MPEG1 only: [0]=44100Hz, [1]=48000Hz, [2]=32000Hz
+static const uint8_t mp3enc_sfb_short[3][40] = {
+ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10,
+ 10, 12, 12, 12, 14, 14, 14, 18, 18, 18, 22, 22, 22, 30, 30, 30, 56, 56, 56, 0 },
+ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 10, 10,
+ 10, 12, 12, 12, 14, 14, 14, 16, 16, 16, 20, 20, 20, 26, 26, 26, 66, 66, 66, 0 },
+ { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 6, 8, 8, 8, 12, 12,
+ 12, 16, 16, 16, 20, 20, 20, 26, 26, 26, 34, 34, 34, 42, 42, 42, 12, 12, 12, 0 }
+};
+
+// SFB boundaries: mixed blocks (ISO Table B.8, from minimp3 CC0)
+// [sr_index][band_width]
+static const uint8_t mp3enc_sfb_mixed[8][40] = {
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, 12, 14,
+ 14, 14, 18, 18, 18, 24, 24, 24, 30, 30, 30, 40, 40, 40, 18, 18, 18, 0 },
+ { 12, 12, 12, 4, 4, 4, 8, 8, 8, 12, 12, 12, 16, 16, 16, 20, 20, 20, 24, 24,
+ 24, 28, 28, 28, 36, 36, 36, 2, 2, 2, 2, 2, 2, 2, 2, 2, 26, 26, 26, 0 },
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 10, 10, 10, 14,
+ 14, 14, 18, 18, 18, 26, 26, 26, 32, 32, 32, 42, 42, 42, 18, 18, 18, 0 },
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, 12, 14,
+ 14, 14, 18, 18, 18, 24, 24, 24, 32, 32, 32, 44, 44, 44, 12, 12, 12, 0 },
+ { 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8, 8, 10, 10, 10, 12, 12, 12, 14,
+ 14, 14, 18, 18, 18, 24, 24, 24, 30, 30, 30, 40, 40, 40, 18, 18, 18, 0 },
+ { 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 4, 6, 6, 6, 8, 8, 8, 10, 10, 10,
+ 12, 12, 12, 14, 14, 14, 18, 18, 18, 22, 22, 22, 30, 30, 30, 56, 56, 56, 0 },
+ { 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 4, 6, 6, 6, 6, 6, 6, 10, 10, 10,
+ 12, 12, 12, 14, 14, 14, 16, 16, 16, 20, 20, 20, 26, 26, 26, 66, 66, 66, 0 },
+ { 4, 4, 4, 4, 4, 4, 6, 6, 4, 4, 4, 6, 6, 6, 8, 8, 8, 12, 12, 12,
+ 16, 16, 16, 20, 20, 20, 26, 26, 26, 34, 34, 34, 42, 42, 42, 12, 12, 12, 0 }
+};
+
+// Alias reduction butterfly coefficients (ISO Table B.9, from minimp3 CC0)
+// cs[i] = 1/sqrt(1 + ci^2), ca[i] = ci/sqrt(1 + ci^2)
+static const float mp3enc_cs[8] = { 0.85749293f, 0.88174200f, 0.94962865f, 0.98331459f,
+ 0.99551782f, 0.99916056f, 0.99989920f, 0.99999316f };
+static const float mp3enc_ca[8] = { 0.51449576f, 0.47173197f, 0.31337745f, 0.18191320f,
+ 0.09457419f, 0.04096558f, 0.01419856f, 0.00369997f };
+
+// MDCT window shapes for overlap add (from minimp3 CC0)
+// [0] = normal/start, [1] = stop block
+// First 9 values: sin(pi/36 * (i + 0.5)) for i=0..8
+// Last 9 values: sin(pi/36 * (i + 18.5)) for i=0..8
+static const float mp3enc_mdct_win[2][18] = {
+ { 0.99904822f, 0.99144486f, 0.97629601f, 0.95371695f, 0.92387953f, 0.88701083f, 0.84339145f, 0.79335334f,
+ 0.73727734f, 0.04361938f, 0.13052619f, 0.21643961f, 0.30070580f, 0.38268343f, 0.46174861f, 0.53729961f,
+ 0.60876143f, 0.67559021f },
+ { 1, 1, 1, 1, 1, 1, 0.99144486f, 0.92387953f, 0.79335334f, 0, 0, 0, 0, 0, 0, 0.13052619f, 0.38268343f, 0.60876143f }
+};
+
+// MDCT twiddle factors for 18 point and 6 point transforms (from minimp3 CC0)
+static const float mp3enc_twid9[18] = { 0.73727734f, 0.79335334f, 0.84339145f, 0.88701083f, 0.92387953f, 0.95371695f,
+ 0.97629601f, 0.99144486f, 0.99904822f, 0.67559021f, 0.60876143f, 0.53729961f,
+ 0.46174861f, 0.38268343f, 0.30070580f, 0.21643961f, 0.13052619f, 0.04361938f };
+static const float mp3enc_twid3[6] = { 0.79335334f, 0.92387953f, 0.99144486f, 0.60876143f, 0.38268343f, 0.13052619f };
+
+// Analysis filterbank window, 512 coefficients.
+// Ci coefficients for the polyphase analysis filter (ISO 11172-3 Table C.1).
+// Extracted from minimp3 (CC0).
+static const float mp3enc_enwindow[512] = {
+ 0.000000e+00f, -0.000000e+00f, -0.000000e+00f, -0.000000e+00f, -0.000000e+00f, -0.000000e+00f, -0.000000e+00f,
+ -1.000000e-06f, -1.000000e-06f, -1.000000e-06f, -1.000000e-06f, -1.000000e-06f, -1.000000e-06f, -2.000000e-06f,
+ -2.000000e-06f, -2.000000e-06f, -2.000000e-06f, -3.000000e-06f, -3.000000e-06f, -3.000000e-06f, -4.000000e-06f,
+ -4.000000e-06f, -5.000000e-06f, -5.000000e-06f, -6.000000e-06f, -7.000000e-06f, -8.000000e-06f, -8.000000e-06f,
+ -9.000000e-06f, -1.000000e-05f, -1.100000e-05f, -1.200000e-05f, -1.400000e-05f, -1.500000e-05f, -1.700000e-05f,
+ -1.800000e-05f, -2.000000e-05f, -2.100000e-05f, -2.300000e-05f, -2.500000e-05f, -2.800000e-05f, -3.000000e-05f,
+ -3.200000e-05f, -3.500000e-05f, -3.800000e-05f, -4.100000e-05f, -4.300000e-05f, -4.600000e-05f, -5.000000e-05f,
+ -5.300000e-05f, -5.600000e-05f, -6.000000e-05f, -6.300000e-05f, -6.600000e-05f, -7.000000e-05f, -7.300000e-05f,
+ -7.700000e-05f, -8.100000e-05f, -8.400000e-05f, -8.700000e-05f, -9.100000e-05f, -9.300000e-05f, -9.600000e-05f,
+ -9.900000e-05f, 1.020000e-04f, 1.040000e-04f, 1.060000e-04f, 1.070000e-04f, 1.080000e-04f, 1.090000e-04f,
+ 1.090000e-04f, 1.080000e-04f, 1.070000e-04f, 1.050000e-04f, 1.030000e-04f, 9.900000e-05f, 9.500000e-05f,
+ 9.000000e-05f, 8.400000e-05f, 7.800000e-05f, 7.000000e-05f, 6.100000e-05f, 5.100000e-05f, 4.000000e-05f,
+ 2.700000e-05f, 1.400000e-05f, -1.000000e-06f, -1.700000e-05f, -3.400000e-05f, -5.300000e-05f, -7.300000e-05f,
+ -9.400000e-05f, -1.160000e-04f, -1.400000e-04f, -1.650000e-04f, -1.910000e-04f, -2.190000e-04f, -2.470000e-04f,
+ -2.770000e-04f, -3.080000e-04f, -3.390000e-04f, -3.710000e-04f, -4.040000e-04f, -4.380000e-04f, -4.730000e-04f,
+ -5.070000e-04f, -5.420000e-04f, -5.770000e-04f, -6.120000e-04f, -6.470000e-04f, -6.810000e-04f, -7.140000e-04f,
+ -7.470000e-04f, -7.790000e-04f, -8.100000e-04f, -8.390000e-04f, -8.660000e-04f, -8.920000e-04f, -9.150000e-04f,
+ -9.360000e-04f, -9.540000e-04f, -9.690000e-04f, -9.810000e-04f, -9.890000e-04f, -9.940000e-04f, -9.950000e-04f,
+ -9.920000e-04f, -9.840000e-04f, 9.710000e-04f, 9.540000e-04f, 9.310000e-04f, 9.030000e-04f, 8.690000e-04f,
+ 8.290000e-04f, 7.840000e-04f, 7.320000e-04f, 6.740000e-04f, 6.100000e-04f, 5.390000e-04f, 4.630000e-04f,
+ 3.790000e-04f, 2.880000e-04f, 1.920000e-04f, 8.800000e-05f, -2.100000e-05f, -1.370000e-04f, -2.600000e-04f,
+ -3.880000e-04f, -5.220000e-04f, -6.620000e-04f, -8.070000e-04f, -9.570000e-04f, -1.111000e-03f, -1.270000e-03f,
+ -1.432000e-03f, -1.598000e-03f, -1.767000e-03f, -1.937000e-03f, -2.110000e-03f, -2.283000e-03f, -2.457000e-03f,
+ -2.631000e-03f, -2.803000e-03f, -2.974000e-03f, -3.142000e-03f, -3.307000e-03f, -3.467000e-03f, -3.623000e-03f,
+ -3.772000e-03f, -3.914000e-03f, -4.049000e-03f, -4.175000e-03f, -4.291000e-03f, -4.396000e-03f, -4.490000e-03f,
+ -4.570000e-03f, -4.638000e-03f, -4.691000e-03f, -4.728000e-03f, -4.749000e-03f, -4.752000e-03f, -4.737000e-03f,
+ -4.703000e-03f, -4.649000e-03f, -4.574000e-03f, -4.477000e-03f, -4.358000e-03f, -4.215000e-03f, -4.049000e-03f,
+ -3.859000e-03f, -3.643000e-03f, -3.402000e-03f, 3.135000e-03f, 2.841000e-03f, 2.522000e-03f, 2.175000e-03f,
+ 1.801000e-03f, 1.400000e-03f, 9.710000e-04f, 5.160000e-04f, 3.300000e-05f, -4.760000e-04f, -1.012000e-03f,
+ -1.574000e-03f, -2.162000e-03f, -2.774000e-03f, -3.411000e-03f, -4.072000e-03f, -4.756000e-03f, -5.462000e-03f,
+ -6.189000e-03f, -6.937000e-03f, -7.703000e-03f, -8.487000e-03f, -9.288000e-03f, -1.010400e-02f, -1.093300e-02f,
+ -1.177500e-02f, -1.262800e-02f, -1.348900e-02f, -1.435900e-02f, -1.523400e-02f, -1.611300e-02f, -1.699400e-02f,
+ -1.787600e-02f, -1.875700e-02f, -1.963400e-02f, -2.050700e-02f, -2.137200e-02f, -2.222900e-02f, -2.307400e-02f,
+ -2.390700e-02f, -2.472500e-02f, -2.552700e-02f, -2.631100e-02f, -2.707400e-02f, -2.781500e-02f, -2.853300e-02f,
+ -2.922500e-02f, -2.989000e-02f, -3.052700e-02f, -3.113300e-02f, -3.170700e-02f, -3.224800e-02f, -3.275500e-02f,
+ -3.322600e-02f, -3.366000e-02f, -3.405600e-02f, -3.441300e-02f, -3.473000e-02f, -3.500700e-02f, -3.524200e-02f,
+ -3.543500e-02f, -3.558600e-02f, -3.569400e-02f, -3.575900e-02f, 3.578100e-02f, 3.575900e-02f, 3.569400e-02f,
+ 3.558600e-02f, 3.543500e-02f, 3.524200e-02f, 3.500700e-02f, 3.473000e-02f, 3.441300e-02f, 3.405600e-02f,
+ 3.366000e-02f, 3.322600e-02f, 3.275500e-02f, 3.224800e-02f, 3.170700e-02f, 3.113300e-02f, 3.052700e-02f,
+ 2.989000e-02f, 2.922500e-02f, 2.853300e-02f, 2.781500e-02f, 2.707400e-02f, 2.631100e-02f, 2.552700e-02f,
+ 2.472500e-02f, 2.390700e-02f, 2.307400e-02f, 2.222900e-02f, 2.137200e-02f, 2.050700e-02f, 1.963400e-02f,
+ 1.875700e-02f, 1.787600e-02f, 1.699400e-02f, 1.611300e-02f, 1.523400e-02f, 1.435900e-02f, 1.348900e-02f,
+ 1.262800e-02f, 1.177500e-02f, 1.093300e-02f, 1.010400e-02f, 9.288000e-03f, 8.487000e-03f, 7.703000e-03f,
+ 6.937000e-03f, 6.189000e-03f, 5.462000e-03f, 4.756000e-03f, 4.072000e-03f, 3.411000e-03f, 2.774000e-03f,
+ 2.162000e-03f, 1.574000e-03f, 1.012000e-03f, 4.760000e-04f, -3.300000e-05f, -5.160000e-04f, -9.710000e-04f,
+ -1.400000e-03f, -1.801000e-03f, -2.175000e-03f, -2.522000e-03f, -2.841000e-03f, 3.135000e-03f, 3.402000e-03f,
+ 3.643000e-03f, 3.859000e-03f, 4.049000e-03f, 4.215000e-03f, 4.358000e-03f, 4.477000e-03f, 4.574000e-03f,
+ 4.649000e-03f, 4.703000e-03f, 4.737000e-03f, 4.752000e-03f, 4.749000e-03f, 4.728000e-03f, 4.691000e-03f,
+ 4.638000e-03f, 4.570000e-03f, 4.490000e-03f, 4.396000e-03f, 4.291000e-03f, 4.175000e-03f, 4.049000e-03f,
+ 3.914000e-03f, 3.772000e-03f, 3.623000e-03f, 3.467000e-03f, 3.307000e-03f, 3.142000e-03f, 2.974000e-03f,
+ 2.803000e-03f, 2.631000e-03f, 2.457000e-03f, 2.283000e-03f, 2.110000e-03f, 1.937000e-03f, 1.767000e-03f,
+ 1.598000e-03f, 1.432000e-03f, 1.270000e-03f, 1.111000e-03f, 9.570000e-04f, 8.070000e-04f, 6.620000e-04f,
+ 5.220000e-04f, 3.880000e-04f, 2.600000e-04f, 1.370000e-04f, 2.100000e-05f, -8.800000e-05f, -1.920000e-04f,
+ -2.880000e-04f, -3.790000e-04f, -4.630000e-04f, -5.390000e-04f, -6.100000e-04f, -6.740000e-04f, -7.320000e-04f,
+ -7.840000e-04f, -8.290000e-04f, -8.690000e-04f, -9.030000e-04f, -9.310000e-04f, -9.540000e-04f, 9.710000e-04f,
+ 9.840000e-04f, 9.920000e-04f, 9.950000e-04f, 9.940000e-04f, 9.890000e-04f, 9.810000e-04f, 9.690000e-04f,
+ 9.540000e-04f, 9.360000e-04f, 9.150000e-04f, 8.920000e-04f, 8.660000e-04f, 8.390000e-04f, 8.100000e-04f,
+ 7.790000e-04f, 7.470000e-04f, 7.140000e-04f, 6.810000e-04f, 6.470000e-04f, 6.120000e-04f, 5.770000e-04f,
+ 5.420000e-04f, 5.070000e-04f, 4.730000e-04f, 4.380000e-04f, 4.040000e-04f, 3.710000e-04f, 3.390000e-04f,
+ 3.080000e-04f, 2.770000e-04f, 2.470000e-04f, 2.190000e-04f, 1.910000e-04f, 1.650000e-04f, 1.400000e-04f,
+ 1.160000e-04f, 9.400000e-05f, 7.300000e-05f, 5.300000e-05f, 3.400000e-05f, 1.700000e-05f, 1.000000e-06f,
+ -1.400000e-05f, -2.700000e-05f, -4.000000e-05f, -5.100000e-05f, -6.100000e-05f, -7.000000e-05f, -7.800000e-05f,
+ -8.400000e-05f, -9.000000e-05f, -9.500000e-05f, -9.900000e-05f, -1.030000e-04f, -1.050000e-04f, -1.070000e-04f,
+ -1.080000e-04f, -1.090000e-04f, -1.090000e-04f, -1.080000e-04f, -1.070000e-04f, -1.060000e-04f, -1.040000e-04f,
+ 1.020000e-04f, 9.900000e-05f, 9.600000e-05f, 9.300000e-05f, 9.100000e-05f, 8.700000e-05f, 8.400000e-05f,
+ 8.100000e-05f, 7.700000e-05f, 7.300000e-05f, 7.000000e-05f, 6.600000e-05f, 6.300000e-05f, 6.000000e-05f,
+ 5.600000e-05f, 5.300000e-05f, 5.000000e-05f, 4.600000e-05f, 4.300000e-05f, 4.100000e-05f, 3.800000e-05f,
+ 3.500000e-05f, 3.200000e-05f, 3.000000e-05f, 2.800000e-05f, 2.500000e-05f, 2.300000e-05f, 2.100000e-05f,
+ 2.000000e-05f, 1.800000e-05f, 1.700000e-05f, 1.500000e-05f, 1.400000e-05f, 1.200000e-05f, 1.100000e-05f,
+ 1.000000e-05f, 9.000000e-06f, 8.000000e-06f, 8.000000e-06f, 7.000000e-06f, 6.000000e-06f, 5.000000e-06f,
+ 5.000000e-06f, 4.000000e-06f, 4.000000e-06f, 3.000000e-06f, 3.000000e-06f, 3.000000e-06f, 2.000000e-06f,
+ 2.000000e-06f, 2.000000e-06f, 2.000000e-06f, 1.000000e-06f, 1.000000e-06f, 1.000000e-06f, 1.000000e-06f,
+ 1.000000e-06f, 1.000000e-06f, 0.000000e+00f, 0.000000e+00f, 0.000000e+00f, 0.000000e+00f, 0.000000e+00f,
+ 0.000000e+00f
+};
+
+// Huffman encoder tables (generated from minimp3 CC0 decoder trees)
+// See gen_huff_enc.c for the generator source.
+static const uint8_t mp3enc_linbits[32] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 2, 3, 4, 6, 8, 10, 13, 4, 5, 6, 7, 8, 9, 11, 13 };
+static const uint16_t mp3enc_hcode_1[2][2] = {
+ { 0x1, 0x1 },
+ { 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_1[2][2] = {
+ { 1, 2 },
+ { 3, 3 }
+};
+static const uint16_t mp3enc_hcode_2[3][3] = {
+ { 0x1, 0x3, 0x3 },
+ { 0x2, 0x1, 0x2 },
+ { 0x1, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_2[3][3] = {
+ { 1, 3, 5 },
+ { 3, 3, 5 },
+ { 6, 5, 6 }
+};
+static const uint16_t mp3enc_hcode_3[3][3] = {
+ { 0x3, 0x1, 0x3 },
+ { 0x2, 0x1, 0x2 },
+ { 0x1, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_3[3][3] = {
+ { 2, 3, 5 },
+ { 2, 2, 5 },
+ { 6, 5, 6 }
+};
+static const uint16_t mp3enc_hcode_5[4][4] = {
+ { 0x1, 0x3, 0x7, 0x6 },
+ { 0x2, 0x1, 0x5, 0x1 },
+ { 0x6, 0x4, 0x7, 0x1 },
+ { 0x5, 0x4, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_5[4][4] = {
+ { 1, 3, 6, 7 },
+ { 3, 3, 6, 6 },
+ { 6, 6, 7, 7 },
+ { 7, 7, 8, 8 }
+};
+static const uint16_t mp3enc_hcode_6[4][4] = {
+ { 0x7, 0x6, 0x5, 0x3 },
+ { 0x3, 0x2, 0x4, 0x3 },
+ { 0x5, 0x3, 0x4, 0x2 },
+ { 0x1, 0x2, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_6[4][4] = {
+ { 3, 3, 4, 6 },
+ { 3, 2, 4, 5 },
+ { 5, 4, 5, 6 },
+ { 7, 5, 6, 7 }
+};
+static const uint16_t mp3enc_hcode_7[6][6] = {
+ { 0x1, 0x3, 0xb, 0xc, 0x7, 0x6 },
+ { 0x2, 0x3, 0x4, 0xb, 0x6, 0x4 },
+ { 0xa, 0x7, 0xd, 0x12, 0x9, 0x5 },
+ { 0x13, 0xa, 0x11, 0xf, 0xe, 0x3 },
+ { 0x10, 0x5, 0x8, 0xb, 0x3, 0x2 },
+ { 0xa, 0x3, 0x4, 0x2, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_7[6][6] = {
+ { 1, 3, 6, 7, 7, 8 },
+ { 3, 4, 5, 7, 7, 8 },
+ { 6, 6, 7, 8, 8, 9 },
+ { 8, 7, 8, 9, 9, 10 },
+ { 8, 7, 8, 9, 9, 10 },
+ { 9, 8, 9, 9, 10, 10 }
+};
+static const uint16_t mp3enc_hcode_8[6][6] = {
+ { 0x3, 0x5, 0x7, 0x13, 0xd, 0xc },
+ { 0x4, 0x1, 0x3, 0x11, 0x5, 0x4 },
+ { 0x6, 0x2, 0x5, 0xf, 0x8, 0x4 },
+ { 0x12, 0x10, 0xe, 0xd, 0xb, 0x1 },
+ { 0xc, 0x9, 0x7, 0xa, 0x5, 0x1 },
+ { 0x5, 0x3, 0x3, 0x4, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_8[6][6] = {
+ { 2, 3, 6, 8, 8, 9 },
+ { 3, 2, 4, 8, 7, 8 },
+ { 6, 4, 6, 8, 8, 9 },
+ { 8, 8, 8, 9, 9, 9 },
+ { 8, 8, 8, 9, 10, 11 },
+ { 9, 8, 9, 10, 10, 11 }
+};
+static const uint16_t mp3enc_hcode_9[6][6] = {
+ { 0x7, 0x6, 0x7, 0xf, 0xb, 0xe },
+ { 0x5, 0x4, 0x6, 0x6, 0x7, 0x4 },
+ { 0x9, 0x5, 0x8, 0x9, 0x9, 0x6 },
+ { 0xe, 0x5, 0x8, 0xa, 0x6, 0x2 },
+ { 0xf, 0x6, 0x8, 0x5, 0x4, 0x6 },
+ { 0x7, 0x7, 0x5, 0x1, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_9[6][6] = {
+ { 3, 3, 4, 6, 7, 8 },
+ { 3, 3, 4, 5, 6, 7 },
+ { 5, 4, 5, 6, 7, 8 },
+ { 6, 5, 6, 7, 7, 8 },
+ { 8, 6, 7, 7, 8, 9 },
+ { 9, 8, 8, 8, 9, 9 }
+};
+static const uint16_t mp3enc_hcode_10[8][8] = {
+ { 0x1, 0x3, 0xb, 0xe, 0x14, 0x1f, 0xe, 0x9 },
+ { 0x2, 0x3, 0x9, 0xd, 0x13, 0x16, 0xd, 0x8 },
+ { 0xa, 0x8, 0xf, 0x16, 0x21, 0x29, 0xa, 0x7 },
+ { 0x17, 0xc, 0x15, 0x22, 0x2f, 0x1a, 0xb, 0x8 },
+ { 0x23, 0x12, 0x20, 0x2e, 0x1b, 0x15, 0x10, 0x4 },
+ { 0x1e, 0x15, 0x28, 0x17, 0x16, 0x14, 0x6, 0x4 },
+ { 0xc, 0xc, 0x13, 0x12, 0x9, 0x5, 0x5, 0x2 },
+ { 0x11, 0x7, 0x6, 0x7, 0x3, 0x3, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_10[8][8] = {
+ { 1, 3, 6, 7, 8, 9, 8, 9 },
+ { 3, 4, 6, 7, 8, 9, 8, 8 },
+ { 6, 6, 7, 8, 9, 10, 9, 9 },
+ { 8, 7, 8, 9, 10, 10, 10, 10 },
+ { 9, 8, 9, 10, 10, 11, 10, 10 },
+ { 9, 9, 10, 10, 10, 11, 10, 11 },
+ { 9, 8, 9, 9, 10, 10, 11, 11 },
+ { 10, 8, 9, 10, 10, 11, 11, 11 }
+};
+static const uint16_t mp3enc_hcode_11[8][8] = {
+ { 0x3, 0x5, 0xb, 0x19, 0x23, 0x1c, 0xe, 0xb },
+ { 0x4, 0x3, 0x7, 0xb, 0x21, 0x1a, 0xc, 0x4 },
+ { 0xa, 0x4, 0xd, 0x13, 0x1f, 0x20, 0x9, 0x6 },
+ { 0x18, 0xa, 0x12, 0x3b, 0x3a, 0x13, 0xd, 0x6 },
+ { 0x22, 0x20, 0x1e, 0x1b, 0x1e, 0x11, 0xe, 0x6 },
+ { 0x21, 0x11, 0x1f, 0x12, 0x10, 0xf, 0x9, 0x3 },
+ { 0x15, 0xb, 0x14, 0xc, 0x7, 0x8, 0x4, 0x2 },
+ { 0xf, 0xa, 0x5, 0x5, 0x5, 0xe, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_11[8][8] = {
+ { 2, 3, 5, 7, 8, 8, 8, 8 },
+ { 3, 3, 5, 6, 8, 8, 7, 7 },
+ { 5, 4, 6, 7, 8, 9, 7, 8 },
+ { 7, 6, 7, 9, 9, 10, 8, 9 },
+ { 8, 8, 8, 8, 9, 10, 9, 10 },
+ { 9, 8, 9, 10, 10, 11, 10, 10 },
+ { 8, 7, 8, 8, 9, 10, 10, 10 },
+ { 9, 8, 8, 9, 10, 11, 10, 10 }
+};
+static const uint16_t mp3enc_hcode_12[8][8] = {
+ { 0x9, 0x7, 0x11, 0x11, 0x20, 0x28, 0x1b, 0x1b },
+ { 0x6, 0x5, 0x7, 0xa, 0xd, 0x11, 0xc, 0xc },
+ { 0x10, 0x6, 0xb, 0xf, 0x16, 0x1f, 0xb, 0x8 },
+ { 0x21, 0x9, 0xe, 0xc, 0x13, 0x1d, 0xf, 0xc },
+ { 0x29, 0x17, 0x15, 0x12, 0x12, 0x11, 0xa, 0x6 },
+ { 0x27, 0x10, 0x1e, 0x1c, 0x10, 0xd, 0x7, 0x3 },
+ { 0x26, 0x1a, 0xa, 0xe, 0x9, 0x4, 0x4, 0x1 },
+ { 0x1a, 0xb, 0x7, 0x5, 0x5, 0x2, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_12[8][8] = {
+ { 4, 3, 5, 6, 7, 8, 8, 9 },
+ { 3, 3, 4, 5, 6, 7, 7, 8 },
+ { 5, 4, 5, 6, 7, 8, 7, 8 },
+ { 7, 5, 6, 6, 7, 8, 8, 9 },
+ { 8, 7, 7, 7, 8, 8, 8, 9 },
+ { 9, 7, 8, 8, 8, 9, 9, 9 },
+ { 9, 8, 7, 8, 8, 8, 9, 9 },
+ { 9, 8, 8, 8, 9, 9, 10, 10 }
+};
+static const uint16_t mp3enc_hcode_13[16][16] = {
+ { 0x1, 0x3, 0xf, 0x16, 0x23, 0x3a, 0x2f, 0x48, 0x2b, 0x35, 0x23, 0x35, 0x22, 0x2d, 0x30, 0x10 },
+ { 0x5, 0x4, 0xd, 0x14, 0x10, 0x1b, 0x2d, 0x22, 0x14, 0x19, 0x21, 0x19, 0x20, 0x15, 0x17, 0xf },
+ { 0xe, 0xc, 0x17, 0x25, 0x3c, 0x32, 0x4e, 0x38, 0x1e, 0x29, 0x1f, 0x17, 0x1c, 0x22, 0x14, 0x11 },
+ { 0x15, 0x13, 0x24, 0x3d, 0x39, 0x60, 0x4a, 0x5f, 0x2c, 0x25, 0x39, 0x26, 0x27, 0x40, 0x27, 0x1b },
+ { 0x22, 0x1f, 0x3b, 0x38, 0x61, 0x4c, 0x73, 0x5c, 0x37, 0x2c, 0x2a, 0x46, 0x31, 0x38, 0x24, 0x19 },
+ { 0x33, 0x1a, 0x31, 0x4f, 0x4b, 0x46, 0x5e, 0x55, 0x4e, 0x3b, 0x52, 0x3c, 0x4b, 0x32, 0x23, 0x14 },
+ { 0x2e, 0x2c, 0x4d, 0x49, 0x72, 0x5d, 0x5a, 0x5b, 0x48, 0x36, 0x48, 0x33, 0x1e, 0x31, 0x35, 0x1d },
+ { 0x47, 0x21, 0x41, 0x40, 0x5b, 0x54, 0x4f, 0x5a, 0x57, 0x51, 0x50, 0x24, 0x34, 0x2d, 0x15, 0xb },
+ { 0x2a, 0x1f, 0x1d, 0x2b, 0x36, 0x4d, 0x45, 0x56, 0x4e, 0x42, 0x2f, 0x37, 0x30, 0x1f, 0x10, 0x11 },
+ { 0x34, 0x18, 0x28, 0x4c, 0x49, 0x3a, 0x53, 0x49, 0x3d, 0x4c, 0x3a, 0x1a, 0x28, 0x13, 0x17, 0xc },
+ { 0x44, 0x20, 0x1e, 0x38, 0x37, 0x4f, 0x47, 0x4d, 0x2e, 0x39, 0x37, 0x22, 0x34, 0xc, 0xd, 0x10 },
+ { 0x34, 0x18, 0x28, 0x25, 0x29, 0x1d, 0x32, 0x41, 0x36, 0x36, 0x15, 0x17, 0x1c, 0xf, 0xa, 0x8 },
+ { 0x43, 0x1f, 0x1b, 0x1a, 0x30, 0x4a, 0x3b, 0x33, 0x25, 0x25, 0x16, 0x1b, 0x12, 0xa, 0x6, 0x1 },
+ { 0x2c, 0x23, 0x21, 0x1f, 0x35, 0x31, 0x26, 0x2c, 0x1e, 0x12, 0x1a, 0xe, 0x11, 0x7, 0x1, 0x1 },
+ { 0x2b, 0x16, 0x2a, 0x19, 0x17, 0x29, 0x24, 0x2b, 0x14, 0x27, 0x26, 0x9, 0x9, 0x6, 0x4, 0x0 },
+ { 0x13, 0xe, 0x10, 0xe, 0x18, 0x11, 0xf, 0x2a, 0x10, 0xb, 0x16, 0x7, 0x5, 0x3, 0x2, 0x1 }
+};
+static const uint8_t mp3enc_hlen_13[16][16] = {
+ { 1, 3, 6, 7, 8, 9, 9, 10, 9, 10, 10, 11, 11, 12, 13, 12 },
+ { 4, 4, 6, 7, 7, 8, 9, 9, 8, 9, 10, 10, 11, 11, 12, 12 },
+ { 6, 6, 7, 8, 9, 9, 10, 10, 9, 10, 10, 10, 11, 12, 12, 13 },
+ { 7, 7, 8, 9, 9, 10, 10, 11, 10, 10, 11, 11, 12, 13, 13, 14 },
+ { 8, 8, 9, 9, 10, 10, 11, 11, 10, 11, 11, 12, 12, 13, 13, 14 },
+ { 9, 8, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 13, 13, 13, 14 },
+ { 9, 9, 10, 10, 11, 11, 11, 12, 11, 11, 12, 12, 12, 14, 15, 15 },
+ { 10, 9, 10, 10, 11, 11, 11, 12, 12, 13, 13, 12, 13, 14, 14, 14 },
+ { 9, 9, 9, 10, 10, 11, 11, 12, 12, 12, 12, 13, 14, 14, 14, 15 },
+ { 10, 9, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 17, 15 },
+ { 11, 10, 10, 11, 11, 12, 12, 13, 12, 13, 14, 13, 15, 14, 15, 16 },
+ { 11, 10, 11, 11, 12, 11, 12, 13, 13, 14, 13, 14, 15, 15, 15, 16 },
+ { 12, 11, 11, 11, 12, 13, 13, 13, 13, 14, 14, 15, 15, 16, 15, 19 },
+ { 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 16, 15, 17, 18 },
+ { 13, 12, 13, 13, 13, 14, 14, 16, 15, 16, 16, 15, 16, 16, 16, 19 },
+ { 13, 12, 13, 13, 14, 14, 14, 16, 15, 15, 17, 16, 16, 16, 16, 16 }
+};
+static const uint16_t mp3enc_hcode_15[16][16] = {
+ { 0x7, 0xd, 0x13, 0x1d, 0x34, 0x4d, 0x7d, 0x6d, 0x5a, 0x47, 0x6d, 0x56, 0x76, 0x5b, 0x7b, 0x47 },
+ { 0xc, 0x5, 0x11, 0x1c, 0x16, 0x25, 0x20, 0x35, 0x2b, 0x22, 0x35, 0x2a, 0x44, 0x2c, 0x3c, 0x25 },
+ { 0x12, 0x10, 0xf, 0x19, 0x2a, 0x23, 0x3c, 0x31, 0x29, 0x43, 0x33, 0x28, 0x1e, 0x27, 0x3a, 0x22 },
+ { 0x35, 0x1b, 0x18, 0x2b, 0x28, 0x42, 0x38, 0x5e, 0x4d, 0x3c, 0x2f, 0x25, 0x37, 0x26, 0x35, 0x1e },
+ { 0x2f, 0x2e, 0x29, 0x27, 0x43, 0x3a, 0x32, 0x58, 0x49, 0x3a, 0x5a, 0x46, 0x32, 0x22, 0x2f, 0x1c },
+ { 0x4c, 0x24, 0x22, 0x3f, 0x39, 0x34, 0x5c, 0x4b, 0x3f, 0x31, 0x52, 0x40, 0x2e, 0x3f, 0x2b, 0x14 },
+ { 0x7c, 0x3d, 0x3b, 0x37, 0x5f, 0x5b, 0x4e, 0x42, 0x38, 0x58, 0x3a, 0x34, 0x4a, 0x34, 0x20, 0x11 },
+ { 0x6c, 0x33, 0x30, 0x5d, 0x4f, 0x4a, 0x41, 0x7a, 0x5c, 0x4c, 0x39, 0x2b, 0x41, 0x2d, 0x16, 0x1a },
+ { 0x59, 0x2a, 0x28, 0x4c, 0x48, 0x3e, 0x37, 0x5b, 0x4d, 0x43, 0x30, 0x46, 0x31, 0x1f, 0x25, 0x15 },
+ { 0x7b, 0x46, 0x40, 0x3b, 0x39, 0x30, 0x57, 0x49, 0x42, 0x6a, 0x48, 0x37, 0x27, 0x34, 0x18, 0x10 },
+ { 0x6c, 0x34, 0x32, 0x5d, 0x59, 0x4f, 0x47, 0x38, 0x2f, 0x47, 0x39, 0x2a, 0x18, 0x1c, 0x11, 0xa },
+ { 0x77, 0x53, 0x4e, 0x48, 0x45, 0x3f, 0x33, 0x2a, 0x43, 0x36, 0x29, 0x19, 0x10, 0x13, 0xc, 0x6 },
+ { 0x6b, 0x41, 0x3e, 0x36, 0x31, 0x5a, 0x49, 0x40, 0x30, 0x26, 0x17, 0x1d, 0x16, 0xe, 0xf, 0x8 },
+ { 0x51, 0x29, 0x50, 0x4b, 0x42, 0x3e, 0x33, 0x2c, 0x35, 0x27, 0x1b, 0x12, 0xd, 0x8, 0xa, 0x6 },
+ { 0x7a, 0x3b, 0x38, 0x32, 0x2e, 0x28, 0x46, 0x15, 0x24, 0x17, 0x3e, 0xb, 0xe, 0x9, 0x2, 0x2 },
+ { 0x3f, 0x24, 0x21, 0x1d, 0x1b, 0x26, 0x1e, 0x19, 0x14, 0xf, 0x9, 0xb, 0x7, 0x3, 0x1, 0x0 }
+};
+static const uint8_t mp3enc_hlen_15[16][16] = {
+ { 3, 4, 5, 6, 7, 8, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12 },
+ { 4, 3, 5, 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 11, 11 },
+ { 5, 5, 5, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 11, 11 },
+ { 7, 6, 6, 7, 7, 8, 8, 9, 9, 9, 9, 9, 10, 10, 11, 11 },
+ { 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, 10, 11, 11 },
+ { 8, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11 },
+ { 9, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11, 11 },
+ { 9, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 12 },
+ { 9, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 12, 12 },
+ { 10, 9, 9, 9, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12 },
+ { 10, 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12 },
+ { 11, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12 },
+ { 11, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 13, 13 },
+ { 11, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 13, 13 },
+ { 12, 11, 11, 11, 11, 11, 12, 11, 12, 12, 13, 12, 13, 13, 12, 13 },
+ { 13, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13 }
+};
+static const uint16_t mp3enc_hcode_16[16][16] = {
+ { 0x1, 0x3, 0xf, 0x2d, 0x4b, 0x42, 0x6f, 0x62, 0x55, 0x9a, 0x8b, 0xf3, 0xca, 0x2eb, 0x179, 0xc },
+ { 0x5, 0x4, 0xd, 0x15, 0x24, 0x1e, 0x36, 0x30, 0x54, 0x4c, 0x81, 0x78, 0xe0, 0xd3, 0x171, 0xa },
+ { 0xe, 0xc, 0x17, 0x27, 0x44, 0x3b, 0x34, 0x5b, 0x51, 0x49, 0x43, 0x76, 0xde, 0xd2, 0x66, 0x7 },
+ { 0x2c, 0x14, 0x26, 0x45, 0x41, 0x38, 0x64, 0x58, 0x9f, 0x8d, 0x7d, 0x73, 0xda, 0xd0, 0xbb, 0xb },
+ { 0x4a, 0x23, 0x43, 0x40, 0x73, 0x66, 0xb8, 0xa5, 0x9c, 0x83, 0xf7, 0xe3, 0xd8, 0x172, 0x2d6, 0xa },
+ { 0x3f, 0x3e, 0x3a, 0x72, 0x65, 0xb9, 0xb2, 0x9d, 0x8f, 0x100, 0xe9, 0xdf, 0x185, 0x17b, 0x2d2, 0x11 },
+ { 0x6e, 0x35, 0x67, 0x63, 0xb3, 0xad, 0xa0, 0x94, 0x104, 0xf5, 0xe5, 0x18c, 0x182, 0x2de, 0x166, 0xb },
+ { 0x5d, 0x2f, 0x5a, 0x57, 0xa4, 0x109, 0x85, 0x105, 0xf9, 0x1aa, 0xdb, 0x2ea, 0x17d, 0x2d3, 0x2c7, 0x9 },
+ { 0xac, 0x53, 0xa1, 0x9e, 0x9b, 0x8e, 0x101, 0xf8, 0x1ab, 0x196, 0x189, 0x2e6, 0x16c, 0x2ca, 0x2c5, 0xd },
+ { 0x95, 0x4b, 0x48, 0x8c, 0x108, 0xfd, 0xf4, 0x197, 0x191, 0x18a, 0x2e7, 0x2e0, 0x378, 0x6c7, 0x362, 0xc },
+ { 0x8a, 0x44, 0x7f, 0xfc, 0xf6, 0xe8, 0xe4, 0x18d, 0x188, 0x180, 0x2e1, 0x2d1, 0x1bb, 0x373, 0x6c6, 0xa },
+ { 0xf2, 0x77, 0x75, 0xd4, 0xe2, 0x190, 0xd9, 0x174, 0x17f, 0x2df, 0x2d0, 0x2c8, 0x2c3, 0x36d, 0x367, 0x7 },
+ { 0xe1, 0xc9, 0x6e, 0xc7, 0x18b, 0x184, 0x181, 0x17c, 0x2d7, 0x167, 0x375, 0x2c2, 0x1b8, 0x36c, 0xd82, 0x5 },
+ { 0xc3, 0x6b, 0xd1, 0x183, 0x17e, 0x17a, 0x16e, 0x379, 0x2c9, 0x2c6, 0x372, 0xdf, 0x1b5, 0xd83, 0x366, 0x3 },
+ { 0x178, 0xcf, 0xce, 0x16d, 0x16a, 0x1bd, 0x2cb, 0x374, 0x2c4, 0x160, 0x1b7, 0x1b4, 0x6c0, 0x361, 0x1b2, 0x1 },
+ { 0x11, 0x9, 0x10, 0x1a, 0x9, 0x10, 0xa, 0x8, 0x7, 0xb, 0x4, 0x6, 0x4, 0x2, 0x0, 0x3 }
+};
+static const uint8_t mp3enc_hlen_16[16][16] = {
+ { 1, 3, 6, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 14, 13, 9 },
+ { 4, 4, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 8 },
+ { 6, 6, 7, 8, 9, 9, 9, 10, 10, 10, 10, 11, 12, 12, 11, 8 },
+ { 8, 7, 8, 9, 9, 9, 10, 10, 11, 11, 11, 11, 12, 12, 12, 9 },
+ { 9, 8, 9, 9, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 14, 9 },
+ { 9, 9, 9, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 13, 14, 10 },
+ { 10, 9, 10, 10, 11, 11, 11, 11, 12, 12, 12, 13, 13, 14, 13, 10 },
+ { 10, 9, 10, 10, 11, 12, 11, 12, 12, 13, 12, 14, 13, 14, 14, 10 },
+ { 11, 10, 11, 11, 11, 11, 12, 12, 13, 13, 13, 14, 13, 14, 14, 11 },
+ { 11, 10, 10, 11, 12, 12, 12, 13, 13, 13, 14, 14, 15, 16, 15, 11 },
+ { 11, 10, 11, 12, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 16, 11 },
+ { 12, 11, 11, 12, 12, 13, 12, 13, 13, 14, 14, 14, 14, 15, 15, 11 },
+ { 12, 12, 11, 12, 13, 13, 13, 13, 14, 13, 15, 14, 14, 15, 17, 11 },
+ { 12, 11, 12, 13, 13, 13, 13, 15, 14, 14, 15, 13, 14, 17, 15, 11 },
+ { 13, 12, 12, 13, 13, 14, 14, 15, 14, 13, 14, 14, 16, 15, 14, 11 },
+ { 9, 8, 9, 10, 9, 10, 10, 10, 10, 11, 10, 11, 11, 11, 11, 8 }
+};
+static const uint16_t mp3enc_hcode_24[16][16] = {
+ { 0xf, 0xe, 0x2f, 0x51, 0x93, 0x107, 0xf9, 0x1b3, 0x1ab, 0x14f, 0x29c, 0x28c, 0x288, 0x26c, 0x409, 0x2b },
+ { 0xd, 0xc, 0x16, 0x27, 0x48, 0x42, 0x7b, 0x73, 0xd4, 0xc7, 0xb8, 0x15a, 0x142, 0x12c, 0x118, 0x14 },
+ { 0x2e, 0x15, 0x29, 0x4b, 0x45, 0x81, 0x79, 0x6f, 0xd0, 0xc5, 0xb7, 0xab, 0x13c, 0x128, 0x116, 0x13 },
+ { 0x50, 0x26, 0x4a, 0x46, 0x87, 0x7e, 0x75, 0x6d, 0xcd, 0xbf, 0xb3, 0xa8, 0x138, 0x126, 0x112, 0x11 },
+ { 0x92, 0x47, 0x44, 0x86, 0x7f, 0x77, 0x71, 0xd3, 0xc9, 0xbd, 0xaf, 0xa4, 0x133, 0x120, 0x10b, 0xf },
+ { 0x106, 0x82, 0x80, 0x7d, 0x76, 0x72, 0xd7, 0xcb, 0xc1, 0xb5, 0x158, 0x13e, 0x12e, 0x11a, 0x108, 0xd },
+ { 0xf8, 0x7a, 0x78, 0x74, 0x70, 0xd6, 0xce, 0xc4, 0xba, 0xae, 0x14b, 0x135, 0x124, 0x111, 0x103, 0xb },
+ { 0x1b2, 0xd8, 0xdd, 0xdc, 0xd2, 0xca, 0xc3, 0xbb, 0xb1, 0x14d, 0x13a, 0x12b, 0x11c, 0x10a, 0x17e, 0x9 },
+ { 0x1aa, 0xd1, 0xcf, 0xcc, 0xc8, 0xc0, 0xb9, 0x161, 0xa9, 0x141, 0x130, 0x11f, 0x10d, 0x203, 0x17a, 0x7 },
+ { 0x29d, 0xc6, 0xc2, 0xbe, 0xbc, 0xb4, 0x15b, 0x14c, 0x140, 0x131, 0x122, 0x114, 0x105, 0x17c, 0x174, 0x6 },
+ { 0x28d, 0x147, 0xb6, 0xb2, 0x160, 0x155, 0x14a, 0x139, 0x12f, 0x121, 0x115, 0x107, 0x200, 0x176, 0x16f, 0x4 },
+ { 0x289, 0x159, 0x154, 0x145, 0x143, 0x13d, 0x134, 0x12a, 0x11e, 0x113, 0x212, 0x201, 0x178, 0x171, 0x16b, 0x7 },
+ { 0x26d, 0x13f, 0x13b, 0x137, 0x132, 0x12d, 0x123, 0x11b, 0x10c, 0x209, 0x17f, 0x177, 0x172, 0x16d, 0x168, 0x5 },
+ { 0x205, 0x129, 0x127, 0x125, 0x11d, 0x119, 0x110, 0x213, 0x202, 0x17b, 0x175, 0x170, 0x16c, 0x169, 0x166, 0x3 },
+ { 0x408, 0x117, 0x21d, 0x10f, 0x21c, 0x106, 0x208, 0x17d, 0x179, 0x173, 0x16e, 0x16a, 0x167, 0x165, 0x164, 0x1 },
+ { 0x58, 0x2a, 0x12, 0x10, 0xe, 0xc, 0xa, 0x11, 0x10, 0xb, 0xa, 0x6, 0x4, 0x2, 0x0, 0x3 }
+};
+static const uint8_t mp3enc_hlen_24[16][16] = {
+ { 4, 4, 6, 7, 8, 9, 9, 10, 10, 10, 11, 11, 11, 11, 12, 8 },
+ { 4, 4, 5, 6, 7, 7, 8, 8, 9, 9, 9, 10, 10, 10, 10, 7 },
+ { 6, 5, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 7 },
+ { 7, 6, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 7 },
+ { 8, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 7 },
+ { 9, 8, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 7 },
+ { 9, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 7 },
+ { 10, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 11, 7 },
+ { 10, 9, 9, 9, 9, 9, 9, 10, 9, 10, 10, 10, 10, 11, 11, 7 },
+ { 11, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 11, 11, 7 },
+ { 11, 10, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 7 },
+ { 11, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 8 },
+ { 11, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 8 },
+ { 11, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 8 },
+ { 12, 10, 11, 10, 11, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 8 },
+ { 9, 8, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 4 }
+};
+static const uint8_t mp3enc_count1a_code[16] = { 1, 5, 4, 5, 6, 5, 4, 4, 7, 3, 6, 0, 7, 2, 3, 1 };
+static const uint8_t mp3enc_count1a_len[16] = { 1, 4, 4, 5, 4, 6, 5, 6, 4, 5, 5, 6, 5, 6, 6, 6 };
+static const uint8_t mp3enc_count1b_code[16] = { 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
+static const uint8_t mp3enc_count1b_len[16] = { 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 };
diff --git a/otherarch/acestep/mp3/mp3enc.h b/otherarch/acestep/mp3/mp3enc.h
new file mode 100644
index 000000000..84987d4cd
--- /dev/null
+++ b/otherarch/acestep/mp3/mp3enc.h
@@ -0,0 +1,529 @@
+#pragma once
+// mp3enc.h
+// MPEG1 Layer III MP3 encoder, CBR, 32/44.1/48 kHz, mono/stereo.
+// MIT license.
+//
+// Usage:
+// mp3enc_t * enc = mp3enc_init(44100, 2, 128);
+// while (have_audio) {
+// const uint8_t * mp3 = mp3enc_encode(enc, pcm, samples, &size);
+// fwrite(mp3, 1, size, fp);
+// }
+// const uint8_t * mp3 = mp3enc_flush(enc, &size);
+// fwrite(mp3, 1, size, fp);
+// mp3enc_free(enc);
+//
+// PCM input: planar float [ch0: N samples] [ch1: N samples]
+// Output: raw MP3 frames (no ID3 tags)
+//
+// MIT license.
+
+// clang-format off
+// Order matters: tables.h defines constants used by all other headers.
+#include "mp3enc-tables.h"
+#include "mp3enc-bits.h"
+#include "mp3enc-filter.h"
+#include "mp3enc-huff.h"
+#include "mp3enc-mdct.h"
+#include "mp3enc-psy.h"
+#include "mp3enc-quant.h"
+// clang-format on
+
+#include
+#include
+
+// Encoder state.
+struct mp3enc_t {
+ // Config
+ int sample_rate;
+ int channels;
+ int bitrate_kbps;
+ int sr_index; // 0=44100, 1=48000, 2=32000
+
+ // Frame geometry
+ int frame_samples; // always 1152 for MPEG1 Layer III
+ int slots_per_frame; // frame size in bytes (varies with padding)
+
+ // Per channel state
+ mp3enc_filter filter[2]; // analysis filterbank
+ float sb_prev[2][32][18]; // subband overlap memory
+ float sb_cur[2][32][18]; // current granule subbands
+ mp3enc_psy psy; // psychoacoustic model
+
+ // Bit reservoir (ISO 11172-3, clause 2.4.2.7).
+ // Tracks unused bits from previous frames that can be borrowed.
+ int resv_size; // current reservoir size in bits
+ int resv_max; // max: 511 bytes * 8 = 4088 bits
+
+ // Adaptive lowpass: MDCT line index above which coefficients are zeroed.
+ // Saves bits at low bitrates by not encoding inaudible HF content.
+ int lowpass_line;
+
+ // PCM input buffer (accumulates until we have 1152 samples)
+ float * pcm_buf; // interleaved buffer: [ch0_1152][ch1_1152]
+ int pcm_fill; // samples accumulated per channel
+
+ // Output buffer (holds accumulated MP3 frames for one encode call)
+ uint8_t * out_buf;
+ int out_capacity;
+ int out_written;
+
+ // Scratch buffer for one frame (max ~1441 bytes at 320kbps/32kHz)
+ uint8_t frame_buf[2048];
+
+ // Main data scratch: written separately from header+sideinfo for reservoir
+ uint8_t md_scratch[2048];
+
+ // Pending frame: we delay output by one frame so we can write the next
+ // frame's main_data overflow into the current frame's unused tail.
+ // This is how the bit reservoir works (ISO 11172-3 clause 2.4.2.7).
+ uint8_t pending_frame[2048];
+ int pending_bytes; // size of pending frame (0 = no pending frame)
+ int pending_md_end; // byte offset where main_data ends in pending_frame
+
+ // Padding state (for 44100 Hz which needs alternating padding)
+ int pad_remainder;
+};
+
+// Initialize encoder.
+static mp3enc_t * mp3enc_init(int sample_rate, int channels, int bitrate_kbps) {
+ mp3enc_t * enc = (mp3enc_t *) calloc(1, sizeof(mp3enc_t));
+ if (!enc) {
+ return nullptr;
+ }
+
+ enc->sample_rate = sample_rate;
+ enc->channels = channels;
+ enc->bitrate_kbps = bitrate_kbps;
+ enc->frame_samples = 1152;
+
+ // Determine sample rate index
+ if (sample_rate == 44100) {
+ enc->sr_index = 0;
+ } else if (sample_rate == 48000) {
+ enc->sr_index = 1;
+ } else if (sample_rate == 32000) {
+ enc->sr_index = 2;
+ } else {
+ free(enc);
+ return nullptr;
+ }
+
+ // Init subsystems
+ for (int ch = 0; ch < channels; ch++) {
+ enc->filter[ch].init();
+ }
+ memset(enc->sb_prev, 0, sizeof(enc->sb_prev));
+ enc->psy.init();
+ enc->psy.init_ath(enc->sr_index, mp3enc_sfb_long[enc->sr_index], sample_rate);
+
+ // Bit reservoir: max 511 bytes (9 bit field main_data_begin)
+ enc->resv_size = 0;
+ enc->resv_max = 511 * 8;
+
+ // Adaptive lowpass: cut HF that wastes bits at low bitrates.
+ // Cutoff per total bitrate in Hz. Higher bitrates preserve more bandwidth.
+ {
+ static const struct {
+ int kbps;
+ int hz;
+ } lp_table[] = {
+ { 8, 2000 },
+ { 16, 3700 },
+ { 24, 3900 },
+ { 32, 5500 },
+ { 40, 7000 },
+ { 48, 7500 },
+ { 56, 10000 },
+ { 64, 11000 },
+ { 80, 13500 },
+ { 96, 15100 },
+ { 112, 15600 },
+ { 128, 17000 },
+ { 160, 17500 },
+ { 192, 18600 },
+ { 224, 19400 },
+ { 256, 19700 },
+ { 320, 20500 }
+ };
+
+ static const int lp_count = (int) (sizeof(lp_table) / sizeof(lp_table[0]));
+
+ // Find nearest bitrate in table
+ int best = 0;
+ int best_dist = 999;
+ for (int i = 0; i < lp_count; i++) {
+ int dist = abs(bitrate_kbps - lp_table[i].kbps);
+ if (dist < best_dist) {
+ best_dist = dist;
+ best = i;
+ }
+ }
+ float cutoff_hz = (float) lp_table[best].hz;
+
+ // MDCT line corresponding to cutoff: 576 lines cover 0..samplerate/2
+ float freq_per_line = (float) sample_rate / (2.0f * 576.0f);
+ enc->lowpass_line = (int) (cutoff_hz / freq_per_line);
+ if (enc->lowpass_line > 576) {
+ enc->lowpass_line = 576;
+ }
+ }
+
+ // Allocate buffers
+ enc->pcm_buf = (float *) calloc(1152 * channels, sizeof(float));
+ enc->pcm_fill = 0;
+
+ enc->out_capacity = 2048;
+ enc->out_buf = (uint8_t *) malloc(enc->out_capacity);
+ enc->out_written = 0;
+
+ enc->pad_remainder = 0;
+
+ // No pending frame at start
+ enc->pending_bytes = 0;
+ enc->pending_md_end = 0;
+
+ return enc;
+}
+
+// Free encoder.
+static void mp3enc_free(mp3enc_t * enc) {
+ if (!enc) {
+ return;
+ }
+ free(enc->pcm_buf);
+ free(enc->out_buf);
+ free(enc);
+}
+
+// Compute padding for this frame (needed at 44100 Hz).
+static int mp3enc_get_padding(mp3enc_t * enc) {
+ int dif = (144 * enc->bitrate_kbps * 1000) % enc->sample_rate;
+ enc->pad_remainder -= dif;
+ if (enc->pad_remainder < 0) {
+ enc->pad_remainder += enc->sample_rate;
+ return 1;
+ }
+ return 0;
+}
+
+// Encode one complete frame (1152 samples per channel).
+// pcm: planar float [ch0: 1152 samples][ch1: 1152 samples]
+// Returns number of bytes written to enc->frame_buf.
+static int mp3enc_encode_frame(mp3enc_t * enc, const float * pcm) {
+ int nch = enc->channels;
+ int padding = mp3enc_get_padding(enc);
+
+ // Use MS stereo for stereo input (joint stereo mode)
+ // mode=1 (joint), mode_ext=2 (MS on, intensity off)
+ int mode = (nch == 1) ? 3 : 1;
+ int mode_ext = (nch == 1) ? 0 : 2;
+
+ // Setup header
+ mp3enc_header hdr;
+ hdr.bitrate_kbps = enc->bitrate_kbps;
+ hdr.samplerate = enc->sample_rate;
+ hdr.mode = mode;
+ hdr.mode_ext = mode_ext;
+ hdr.padding = padding;
+
+ int frame_bytes = hdr.frame_bytes();
+ int side_info_bytes = (nch == 1) ? 17 : 32;
+ int main_data_bytes = frame_bytes - 4 - side_info_bytes;
+ int main_data_bits = main_data_bytes * 8;
+
+ // SFB tables for this sample rate
+ const uint8_t * sfb_long = mp3enc_sfb_long[enc->sr_index];
+
+ // Process 2 granules (each is 576 samples = 18 subband slots of 32 samples)
+ mp3enc_side_info si;
+ memset(&si, 0, sizeof(si));
+
+ // Cross-frame bit reservoir (ISO 11172-3 clause 2.4.2.7).
+ // Unused bytes at the end of the previous frame can be borrowed by this frame.
+ // The pending_frame holds the previous frame; its unused tail is the reservoir.
+ int resv_bytes = 0;
+ if (enc->pending_bytes > 0) {
+ resv_bytes = enc->pending_bytes - enc->pending_md_end;
+ if (resv_bytes < 0) {
+ resv_bytes = 0;
+ }
+ if (resv_bytes > 511) {
+ resv_bytes = 511;
+ }
+ }
+ si.main_data_begin = resv_bytes;
+
+ // Total main_data bits: this frame's area + reservoir from previous frame
+ int total_md_bits = main_data_bits + resv_bytes * 8;
+
+ // Mean bits per granule (from total budget)
+ int mean_bits = total_md_bits / 2;
+
+ int ix[2][2][576]; // [granule][channel][line]
+ float mdct_lr[2][576]; // MDCT output per channel before M/S transform
+
+ int total_bits_used = 0;
+ int intra_resv = 0; // intra-frame reservoir: bits saved by granule 0 for granule 1
+
+ for (int gr = 0; gr < 2; gr++) {
+ int pcm_offset = gr * 576;
+
+ // Step 1: filterbank + MDCT for all channels
+ for (int ch = 0; ch < nch; ch++) {
+ const float * ch_pcm = pcm + ch * 1152 + pcm_offset;
+
+ // Run analysis filterbank: 576 PCM samples = 18 calls of 32 samples
+ float sb_out[32];
+ for (int slot = 0; slot < 18; slot++) {
+ enc->filter[ch].process(ch_pcm + slot * 32, sb_out);
+ for (int sb = 0; sb < 32; sb++) {
+ enc->sb_cur[ch][sb][slot] = sb_out[sb];
+ }
+
+ // frequency inversion: negate odd subbands at odd time slots
+ if (slot & 1) {
+ for (int sb = 1; sb < 32; sb += 2) {
+ enc->sb_cur[ch][sb][slot] = -enc->sb_cur[ch][sb][slot];
+ }
+ }
+ }
+
+ // MDCT: transform subbands to 576 frequency lines
+ mp3enc_mdct_granule(enc->sb_prev[ch], enc->sb_cur[ch], mdct_lr[ch]);
+
+ // Save current subbands as previous for next granule
+ memcpy(enc->sb_prev[ch], enc->sb_cur[ch], sizeof(enc->sb_cur[ch]));
+ }
+
+ // Step 2: MS stereo transform
+ if (nch == 2) {
+ static const float ms_scale = 0.7071067811865476f; // 1/sqrt(2)
+ for (int i = 0; i < 576; i++) {
+ float l = mdct_lr[0][i];
+ float r = mdct_lr[1][i];
+ mdct_lr[0][i] = (l + r) * ms_scale;
+ mdct_lr[1][i] = (l - r) * ms_scale;
+ }
+ }
+
+ // Step 2b: adaptive lowpass
+ for (int ch = 0; ch < nch; ch++) {
+ for (int i = enc->lowpass_line; i < 576; i++) {
+ mdct_lr[ch][i] = 0.0f;
+ }
+ }
+
+ // Step 3: run psy for all channels before bit allocation
+ float saved_xmin[2][MP3ENC_PSY_SFB_MAX];
+ for (int ch = 0; ch < nch; ch++) {
+ enc->psy.compute(mdct_lr[ch], sfb_long, enc->sr_index, ch);
+ memcpy(saved_xmin[ch], enc->psy.xmin, sizeof(enc->psy.xmin));
+ }
+
+ // Step 4: bit allocation
+ int max_bits = mean_bits + intra_resv;
+
+ // Don't exceed remaining budget
+ int remaining_bits = total_md_bits - total_bits_used;
+ if (max_bits > remaining_bits) {
+ max_bits = remaining_bits;
+ }
+ if (max_bits > 4095 * nch) {
+ max_bits = 4095 * nch;
+ }
+
+ int bits_per_ch = max_bits / nch;
+
+ // Step 5: quantize each channel with saved thresholds
+ int gr_bits_used = 0;
+ for (int ch = 0; ch < nch; ch++) {
+ int bits = mp3enc_outer_loop(mdct_lr[ch], ix[gr][ch], si.gr[gr][ch], saved_xmin[ch], bits_per_ch, sfb_long,
+ enc->sr_index, gr, si.scfsi[ch]);
+ gr_bits_used += bits;
+ }
+
+ // Track intra-frame savings: bits not used by this granule
+ // become available for the next granule in this frame.
+ intra_resv += mean_bits - gr_bits_used;
+ if (intra_resv < 0) {
+ intra_resv = 0;
+ }
+ total_bits_used += gr_bits_used;
+ }
+
+ // Write header + side_info to frame_buf
+ mp3enc_bs hdr_bs;
+ hdr_bs.init(enc->frame_buf, sizeof(enc->frame_buf));
+ hdr.write(hdr_bs);
+ si.write(hdr_bs, nch);
+ int hdr_si_bytes = (hdr_bs.total_bits() + 7) / 8;
+
+ // Write main_data to md_scratch (separate buffer for reservoir assembly)
+ mp3enc_bs md_bs;
+ md_bs.init(enc->md_scratch, sizeof(enc->md_scratch));
+
+ for (int gr = 0; gr < 2; gr++) {
+ for (int ch = 0; ch < nch; ch++) {
+ const mp3enc_granule_info & gi = si.gr[gr][ch];
+
+ // Scalefactors
+ mp3enc_write_scalefactors(md_bs, gi, gr, nch, si.scfsi[ch]);
+
+ // Huffman data: big_values region (3 regions for long blocks)
+ int prev_end = 0;
+ for (int r = 0; r < 3; r++) {
+ int reg_end;
+ if (r == 0) {
+ int acc = 0;
+ for (int s = 0; s <= gi.region0_count; s++) {
+ acc += sfb_long[s];
+ }
+ reg_end = (acc < gi.big_values * 2) ? acc : gi.big_values * 2;
+ } else if (r == 1) {
+ int acc = 0;
+ for (int s = 0; s <= gi.region0_count + gi.region1_count + 1; s++) {
+ acc += sfb_long[s];
+ }
+ reg_end = (acc < gi.big_values * 2) ? acc : gi.big_values * 2;
+ } else {
+ reg_end = gi.big_values * 2;
+ }
+
+ int pairs = (reg_end - prev_end) / 2;
+ for (int p = 0; p < pairs; p++) {
+ int i = prev_end + p * 2;
+ mp3enc_write_pair(md_bs, gi.table_select[r], ix[gr][ch][i], ix[gr][ch][i + 1]);
+ }
+ prev_end = reg_end;
+ }
+
+ // Count1 region
+ int c1_start = gi.big_values * 2;
+ int nz_end = 576 - mp3enc_count_rzero(ix[gr][ch], 576) * 2;
+ int c1_count = (nz_end - c1_start) / 4;
+ mp3enc_write_count1(md_bs, ix[gr][ch], c1_start, c1_count, gi.count1table_select);
+ }
+ }
+ int md_bytes = (md_bs.total_bits() + 7) / 8;
+
+ // Assemble output: write overflow to pending frame, output it, save current as pending.
+ //
+ // The first resv_bytes of main_data go into the previous frame's unused tail.
+ // The rest goes into the current frame's main data area.
+ // This is how the decoder's main_data_begin backpointer works.
+ int output_bytes = 0;
+
+ if (enc->pending_bytes > 0) {
+ // Write main_data overflow into pending frame's unused tail
+ int overflow = (md_bytes < resv_bytes) ? md_bytes : resv_bytes;
+ if (overflow > 0) {
+ memcpy(enc->pending_frame + enc->pending_md_end, enc->md_scratch, overflow);
+ }
+
+ // Grow out_buf if needed
+ int need = enc->out_written + enc->pending_bytes + frame_bytes;
+ if (need > enc->out_capacity) {
+ enc->out_capacity = need * 2;
+ enc->out_buf = (uint8_t *) realloc(enc->out_buf, enc->out_capacity);
+ }
+
+ // Output the pending frame (now complete with overflow data)
+ memcpy(enc->out_buf + enc->out_written, enc->pending_frame, enc->pending_bytes);
+ enc->out_written += enc->pending_bytes;
+ output_bytes = enc->pending_bytes;
+ }
+
+ // Build current frame: header+sideinfo already in frame_buf, add main_data + padding
+ int md_in_frame = md_bytes - resv_bytes;
+ if (md_in_frame < 0) {
+ md_in_frame = 0;
+ }
+ int md_area = frame_bytes - hdr_si_bytes;
+
+ // Copy the portion of main_data that goes in this frame
+ if (md_in_frame > 0) {
+ memcpy(enc->frame_buf + hdr_si_bytes, enc->md_scratch + resv_bytes, md_in_frame);
+ }
+ // Zero-pad the unused tail (this becomes reservoir for the next frame)
+ if (md_in_frame < md_area) {
+ memset(enc->frame_buf + hdr_si_bytes + md_in_frame, 0, md_area - md_in_frame);
+ }
+
+ // Save current frame as pending (will be output when next frame is encoded)
+ memcpy(enc->pending_frame, enc->frame_buf, frame_bytes);
+ enc->pending_bytes = frame_bytes;
+ enc->pending_md_end = hdr_si_bytes + md_in_frame;
+
+ return output_bytes;
+}
+
+// Public API: encode PCM samples.
+// audio: planar float [ch0: n_samples][ch1: n_samples]
+// n_samples: number of samples per channel
+// out_size: receives the total MP3 bytes produced
+// Returns pointer to MP3 data (internal buffer, valid until next call).
+static const uint8_t * mp3enc_encode(mp3enc_t * enc, const float * audio, int n_samples, int * out_size) {
+ *out_size = 0;
+ int nch = enc->channels;
+ int consumed = 0;
+
+ // Reset output write position
+ enc->out_written = 0;
+
+ while (consumed < n_samples) {
+ // Fill PCM buffer up to 1152 samples per channel
+ int space = 1152 - enc->pcm_fill;
+ int avail = n_samples - consumed;
+ int copy = (avail < space) ? avail : space;
+
+ for (int ch = 0; ch < nch; ch++) {
+ memcpy(enc->pcm_buf + ch * 1152 + enc->pcm_fill, audio + ch * n_samples + consumed, copy * sizeof(float));
+ }
+ enc->pcm_fill += copy;
+ consumed += copy;
+
+ // When we have 1152 samples, encode a frame.
+ // encode_frame handles writing to out_buf (including pending frame output).
+ if (enc->pcm_fill == 1152) {
+ mp3enc_encode_frame(enc, enc->pcm_buf);
+ enc->pcm_fill = 0;
+ }
+ }
+
+ *out_size = enc->out_written;
+ return enc->out_buf;
+}
+
+// Flush remaining samples (zero pad to 1152, encode last frame).
+// Also outputs the final pending frame from the bit reservoir delay.
+static const uint8_t * mp3enc_flush(mp3enc_t * enc, int * out_size) {
+ *out_size = 0;
+ enc->out_written = 0;
+
+ if (enc->pcm_fill > 0) {
+ // Zero pad to 1152
+ int nch = enc->channels;
+ for (int ch = 0; ch < nch; ch++) {
+ memset(enc->pcm_buf + ch * 1152 + enc->pcm_fill, 0, (1152 - enc->pcm_fill) * sizeof(float));
+ }
+ enc->pcm_fill = 1152;
+ mp3enc_encode_frame(enc, enc->pcm_buf);
+ enc->pcm_fill = 0;
+ }
+
+ // Output the final pending frame (delayed by one frame for reservoir)
+ if (enc->pending_bytes > 0) {
+ int need = enc->out_written + enc->pending_bytes;
+ if (need > enc->out_capacity) {
+ enc->out_capacity = need * 2;
+ enc->out_buf = (uint8_t *) realloc(enc->out_buf, enc->out_capacity);
+ }
+ memcpy(enc->out_buf + enc->out_written, enc->pending_frame, enc->pending_bytes);
+ enc->out_written += enc->pending_bytes;
+ enc->pending_bytes = 0;
+ }
+
+ *out_size = enc->out_written;
+ return enc->out_buf;
+}
diff --git a/otherarch/acestep/mp3/tabs_data.h b/otherarch/acestep/mp3/tabs_data.h
new file mode 100644
index 000000000..0eaf2d948
--- /dev/null
+++ b/otherarch/acestep/mp3/tabs_data.h
@@ -0,0 +1,150 @@
+// Extracted from minimp3.h (CC0, github.com/lieff/minimp3)
+// ISO 11172-3 Annex B, Table B.7 Huffman trees
+
+static const int16_t tabs[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 785, 785, 785, 785, 784, 784, 784, 784, 513, 513, 513, 513, 513, 513, 513, 513,
+ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+ -255, 1313, 1298, 1282, 785, 785, 785, 785, 784, 784, 784, 784, 769, 769, 769, 769,
+ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+ 290, 288, -255, 1313, 1298, 1282, 769, 769, 769, 769, 529, 529, 529, 529, 529, 529,
+ 529, 529, 528, 528, 528, 528, 528, 528, 528, 528, 512, 512, 512, 512, 512, 512,
+ 512, 512, 290, 288, -253, -318, -351, -367, 785, 785, 785, 785, 784, 784, 784, 784,
+ 769, 769, 769, 769, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 819, 818, 547, 547, 275, 275, 275, 275, 561, 560, 515, 546,
+ 289, 274, 288, 258, -254, -287, 1329, 1299, 1314, 1312, 1057, 1057, 1042, 1042, 1026, 1026,
+ 784, 784, 784, 784, 529, 529, 529, 529, 529, 529, 529, 529, 769, 769, 769, 769,
+ 768, 768, 768, 768, 563, 560, 306, 306, 291, 259, -252, -413, -477, -542, 1298, -575,
+ 1041, 1041, 784, 784, 784, 784, 769, 769, 769, 769, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, -383, -399, 1107, 1092, 1106, 1061,
+ 849, 849, 789, 789, 1104, 1091, 773, 773, 1076, 1075, 341, 340, 325, 309, 834, 804,
+ 577, 577, 532, 532, 516, 516, 832, 818, 803, 816, 561, 561, 531, 531, 515, 546,
+ 289, 289, 288, 258, -252, -429, -493, -559, 1057, 1057, 1042, 1042, 529, 529, 529, 529,
+ 529, 529, 529, 529, 784, 784, 784, 784, 769, 769, 769, 769, 512, 512, 512, 512,
+ 512, 512, 512, 512, -382, 1077, -415, 1106, 1061, 1104, 849, 849, 789, 789, 1091, 1076,
+ 1029, 1075, 834, 834, 597, 581, 340, 340, 339, 324, 804, 833, 532, 532, 832, 772,
+ 818, 803, 817, 787, 816, 771, 290, 290, 290, 290, 288, 258, -253, -349, -414, -447,
+ -463, 1329, 1299, -479, 1314, 1312, 1057, 1057, 1042, 1042, 1026, 1026, 785, 785, 785, 785,
+ 784, 784, 784, 784, 769, 769, 769, 769, 768, 768, 768, 768, -319, 851, 821, -335,
+ 836, 850, 805, 849, 341, 340, 325, 336, 533, 533, 579, 579, 564, 564, 773, 832,
+ 578, 548, 563, 516, 321, 276, 306, 291, 304, 259, -251, -572, -733, -830, -863, -879,
+ 1041, 1041, 784, 784, 784, 784, 769, 769, 769, 769, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, -511, -527, -543, 1396, 1351, 1381,
+ 1366, 1395, 1335, 1380, -559, 1334, 1138, 1138, 1063, 1063, 1350, 1392, 1031, 1031, 1062, 1062,
+ 1364, 1363, 1120, 1120, 1333, 1348, 881, 881, 881, 881, 375, 374, 359, 373, 343, 358,
+ 341, 325, 791, 791, 1123, 1122, -703, 1105, 1045, -719, 865, 865, 790, 790, 774, 774,
+ 1104, 1029, 338, 293, 323, 308, -799, -815, 833, 788, 772, 818, 803, 816, 322, 292,
+ 307, 320, 561, 531, 515, 546, 289, 274, 288, 258, -251, -525, -605, -685, -765, -831,
+ -846, 1298, 1057, 1057, 1312, 1282, 785, 785, 785, 785, 784, 784, 784, 784, 769, 769,
+ 769, 769, 512, 512, 512, 512, 512, 512, 512, 512, 1399, 1398, 1383, 1367, 1382, 1396,
+ 1351, -511, 1381, 1366, 1139, 1139, 1079, 1079, 1124, 1124, 1364, 1349, 1363, 1333, 882, 882,
+ 882, 882, 807, 807, 807, 807, 1094, 1094, 1136, 1136, 373, 341, 535, 535, 881, 775,
+ 867, 822, 774, -591, 324, 338, -671, 849, 550, 550, 866, 864, 609, 609, 293, 336,
+ 534, 534, 789, 835, 773, -751, 834, 804, 308, 307, 833, 788, 832, 772, 562, 562,
+ 547, 547, 305, 275, 560, 515, 290, 290, -252, -397, -477, -557, -622, -653, -719, -735,
+ -750, 1329, 1299, 1314, 1057, 1057, 1042, 1042, 1312, 1282, 1024, 1024, 785, 785, 785, 785,
+ 784, 784, 784, 784, 769, 769, 769, 769, -383, 1127, 1141, 1111, 1126, 1140, 1095, 1110,
+ 869, 869, 883, 883, 1079, 1109, 882, 882, 375, 374, 807, 868, 838, 881, 791, -463,
+ 867, 822, 368, 263, 852, 837, 836, -543, 610, 610, 550, 550, 352, 336, 534, 534,
+ 865, 774, 851, 821, 850, 805, 593, 533, 579, 564, 773, 832, 578, 578, 548, 548,
+ 577, 577, 307, 276, 306, 291, 516, 560, 259, 259, -250, -2107, -2507, -2764, -2909, -2974,
+ -3007, -3023, 1041, 1041, 1040, 1040, 769, 769, 769, 769, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, -767, -1052, -1213, -1277, -1358, -1405,
+ -1469, -1535, -1550, -1582, -1614, -1647, -1662, -1694, -1726, -1759, -1774, -1807, -1822, -1854, -1886, 1565,
+ -1919, -1935, -1951, -1967, 1731, 1730, 1580, 1717, -1983, 1729, 1564, -1999, 1548, -2015, -2031, 1715,
+ 1595, -2047, 1714, -2063, 1610, -2079, 1609, -2095, 1323, 1323, 1457, 1457, 1307, 1307, 1712, 1547,
+ 1641, 1700, 1699, 1594, 1685, 1625, 1442, 1442, 1322, 1322, -780, -973, -910, 1279, 1278, 1277,
+ 1262, 1276, 1261, 1275, 1215, 1260, 1229, -959, 974, 974, 989, 989, -943, 735, 478, 478,
+ 495, 463, 506, 414, -1039, 1003, 958, 1017, 927, 942, 987, 957, 431, 476, 1272, 1167,
+ 1228, -1183, 1256, -1199, 895, 895, 941, 941, 1242, 1227, 1212, 1135, 1014, 1014, 490, 489,
+ 503, 487, 910, 1013, 985, 925, 863, 894, 970, 955, 1012, 847, -1343, 831, 755, 755,
+ 984, 909, 428, 366, 754, 559, -1391, 752, 486, 457, 924, 997, 698, 698, 983, 893,
+ 740, 740, 908, 877, 739, 739, 667, 667, 953, 938, 497, 287, 271, 271, 683, 606,
+ 590, 712, 726, 574, 302, 302, 738, 736, 481, 286, 526, 725, 605, 711, 636, 724,
+ 696, 651, 589, 681, 666, 710, 364, 467, 573, 695, 466, 466, 301, 465, 379, 379,
+ 709, 604, 665, 679, 316, 316, 634, 633, 436, 436, 464, 269, 424, 394, 452, 332,
+ 438, 363, 347, 408, 393, 448, 331, 422, 362, 407, 392, 421, 346, 406, 391, 376,
+ 375, 359, 1441, 1306, -2367, 1290, -2383, 1337, -2399, -2415, 1426, 1321, -2431, 1411, 1336, -2447,
+ -2463, -2479, 1169, 1169, 1049, 1049, 1424, 1289, 1412, 1352, 1319, -2495, 1154, 1154, 1064, 1064,
+ 1153, 1153, 416, 390, 360, 404, 403, 389, 344, 374, 373, 343, 358, 372, 327, 357,
+ 342, 311, 356, 326, 1395, 1394, 1137, 1137, 1047, 1047, 1365, 1392, 1287, 1379, 1334, 1364,
+ 1349, 1378, 1318, 1363, 792, 792, 792, 792, 1152, 1152, 1032, 1032, 1121, 1121, 1046, 1046,
+ 1120, 1120, 1030, 1030, -2895, 1106, 1061, 1104, 849, 849, 789, 789, 1091, 1076, 1029, 1090,
+ 1060, 1075, 833, 833, 309, 324, 532, 532, 832, 772, 818, 803, 561, 561, 531, 560,
+ 515, 546, 289, 274, 288, 258, -250, -1179, -1579, -1836, -1996, -2124, -2253, -2333, -2413, -2477,
+ -2542, -2574, -2607, -2622, -2655, 1314, 1313, 1298, 1312, 1282, 785, 785, 785, 785, 1040, 1040,
+ 1025, 1025, 768, 768, 768, 768, -766, -798, -830, -862, -895, -911, -927, -943, -959, -975,
+ -991, -1007, -1023, -1039, -1055, -1070, 1724, 1647, -1103, -1119, 1631, 1767, 1662, 1738, 1708, 1723,
+ -1135, 1780, 1615, 1779, 1599, 1677, 1646, 1778, 1583, -1151, 1777, 1567, 1737, 1692, 1765, 1722,
+ 1707, 1630, 1751, 1661, 1764, 1614, 1736, 1676, 1763, 1750, 1645, 1598, 1721, 1691, 1762, 1706,
+ 1582, 1761, 1566, -1167, 1749, 1629, 767, 766, 751, 765, 494, 494, 735, 764, 719, 749,
+ 734, 763, 447, 447, 748, 718, 477, 506, 431, 491, 446, 476, 461, 505, 415, 430,
+ 475, 445, 504, 399, 460, 489, 414, 503, 383, 474, 429, 459, 502, 502, 746, 752,
+ 488, 398, 501, 473, 413, 472, 486, 271, 480, 270, -1439, -1455, 1357, -1471, -1487, -1503,
+ 1341, 1325, -1519, 1489, 1463, 1403, 1309, -1535, 1372, 1448, 1418, 1476, 1356, 1462, 1387, -1551,
+ 1475, 1340, 1447, 1402, 1386, -1567, 1068, 1068, 1474, 1461, 455, 380, 468, 440, 395, 425,
+ 410, 454, 364, 467, 466, 464, 453, 269, 409, 448, 268, 432, 1371, 1473, 1432, 1417,
+ 1308, 1460, 1355, 1446, 1459, 1431, 1083, 1083, 1401, 1416, 1458, 1445, 1067, 1067, 1370, 1457,
+ 1051, 1051, 1291, 1430, 1385, 1444, 1354, 1415, 1400, 1443, 1082, 1082, 1173, 1113, 1186, 1066,
+ 1185, 1050, -1967, 1158, 1128, 1172, 1097, 1171, 1081, -1983, 1157, 1112, 416, 266, 375, 400,
+ 1170, 1142, 1127, 1065, 793, 793, 1169, 1033, 1156, 1096, 1141, 1111, 1155, 1080, 1126, 1140,
+ 898, 898, 808, 808, 897, 897, 792, 792, 1095, 1152, 1032, 1125, 1110, 1139, 1079, 1124,
+ 882, 807, 838, 881, 853, 791, -2319, 867, 368, 263, 822, 852, 837, 866, 806, 865,
+ -2399, 851, 352, 262, 534, 534, 821, 836, 594, 594, 549, 549, 593, 593, 533, 533,
+ 848, 773, 579, 579, 564, 578, 548, 563, 276, 276, 577, 576, 306, 291, 516, 560,
+ 305, 305, 275, 259, -251, -892, -2058, -2620, -2828, -2957, -3023, -3039, 1041, 1041, 1040, 1040,
+ 769, 769, 769, 769, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256,
+ 256, 256, 256, 256, -511, -527, -543, -559, 1530, -575, -591, 1528, 1527, 1407, 1526, 1391,
+ 1023, 1023, 1023, 1023, 1525, 1375, 1268, 1268, 1103, 1103, 1087, 1087, 1039, 1039, 1523, -604,
+ 815, 815, 815, 815, 510, 495, 509, 479, 508, 463, 507, 447, 431, 505, 415, 399,
+ -734, -782, 1262, -815, 1259, 1244, -831, 1258, 1228, -847, -863, 1196, -879, 1253, 987, 987,
+ 748, -767, 493, 493, 462, 477, 414, 414, 686, 669, 478, 446, 461, 445, 474, 429,
+ 487, 458, 412, 471, 1266, 1264, 1009, 1009, 799, 799, -1019, -1276, -1452, -1581, -1677, -1757,
+ -1821, -1886, -1933, -1997, 1257, 1257, 1483, 1468, 1512, 1422, 1497, 1406, 1467, 1496, 1421, 1510,
+ 1134, 1134, 1225, 1225, 1466, 1451, 1374, 1405, 1252, 1252, 1358, 1480, 1164, 1164, 1251, 1251,
+ 1238, 1238, 1389, 1465, -1407, 1054, 1101, -1423, 1207, -1439, 830, 830, 1248, 1038, 1237, 1117,
+ 1223, 1148, 1236, 1208, 411, 426, 395, 410, 379, 269, 1193, 1222, 1132, 1235, 1221, 1116,
+ 976, 976, 1192, 1162, 1177, 1220, 1131, 1191, 963, 963, -1647, 961, 780, -1663, 558, 558,
+ 994, 993, 437, 408, 393, 407, 829, 978, 813, 797, 947, -1743, 721, 721, 377, 392,
+ 844, 950, 828, 890, 706, 706, 812, 859, 796, 960, 948, 843, 934, 874, 571, 571,
+ -1919, 690, 555, 689, 421, 346, 539, 539, 944, 779, 918, 873, 932, 842, 903, 888,
+ 570, 570, 931, 917, 674, 674, -2575, 1562, -2591, 1609, -2607, 1654, 1322, 1322, 1441, 1441,
+ 1696, 1546, 1683, 1593, 1669, 1624, 1426, 1426, 1321, 1321, 1639, 1680, 1425, 1425, 1305, 1305,
+ 1545, 1668, 1608, 1623, 1667, 1592, 1638, 1666, 1320, 1320, 1652, 1607, 1409, 1409, 1304, 1304,
+ 1288, 1288, 1664, 1637, 1395, 1395, 1335, 1335, 1622, 1636, 1394, 1394, 1319, 1319, 1606, 1621,
+ 1392, 1392, 1137, 1137, 1137, 1137, 345, 390, 360, 375, 404, 373, 1047, -2751, -2767, -2783,
+ 1062, 1121, 1046, -2799, 1077, -2815, 1106, 1061, 789, 789, 1105, 1104, 263, 355, 310, 340,
+ 325, 354, 352, 262, 339, 324, 1091, 1076, 1029, 1090, 1060, 1075, 833, 833, 788, 788,
+ 1088, 1028, 818, 818, 803, 803, 561, 561, 531, 531, 816, 771, 546, 546, 289, 274,
+ 288, 258, -253, -317, -381, -446, -478, -509, 1279, 1279, -811, -1179, -1451, -1756, -1900, -2028,
+ -2189, -2253, -2333, -2414, -2445, -2511, -2526, 1313, 1298, -2559, 1041, 1041, 1040, 1040, 1025, 1025,
+ 1024, 1024, 1022, 1007, 1021, 991, 1020, 975, 1019, 959, 687, 687, 1018, 1017, 671, 671,
+ 655, 655, 1016, 1015, 639, 639, 758, 758, 623, 623, 757, 607, 756, 591, 755, 575,
+ 754, 559, 543, 543, 1009, 783, -575, -621, -685, -749, 496, -590, 750, 749, 734, 748,
+ 974, 989, 1003, 958, 988, 973, 1002, 942, 987, 957, 972, 1001, 926, 986, 941, 971,
+ 956, 1000, 910, 985, 925, 999, 894, 970, -1071, -1087, -1102, 1390, -1135, 1436, 1509, 1451,
+ 1374, -1151, 1405, 1358, 1480, 1420, -1167, 1507, 1494, 1389, 1342, 1465, 1435, 1450, 1326, 1505,
+ 1310, 1493, 1373, 1479, 1404, 1492, 1464, 1419, 428, 443, 472, 397, 736, 526, 464, 464,
+ 486, 457, 442, 471, 484, 482, 1357, 1449, 1434, 1478, 1388, 1491, 1341, 1490, 1325, 1489,
+ 1463, 1403, 1309, 1477, 1372, 1448, 1418, 1433, 1476, 1356, 1462, 1387, -1439, 1475, 1340, 1447,
+ 1402, 1474, 1324, 1461, 1371, 1473, 269, 448, 1432, 1417, 1308, 1460, -1711, 1459, -1727, 1441,
+ 1099, 1099, 1446, 1386, 1431, 1401, -1743, 1289, 1083, 1083, 1160, 1160, 1458, 1445, 1067, 1067,
+ 1370, 1457, 1307, 1430, 1129, 1129, 1098, 1098, 268, 432, 267, 416, 266, 400, -1887, 1144,
+ 1187, 1082, 1173, 1113, 1186, 1066, 1050, 1158, 1128, 1143, 1172, 1097, 1171, 1081, 420, 391,
+ 1157, 1112, 1170, 1142, 1127, 1065, 1169, 1049, 1156, 1096, 1141, 1111, 1155, 1080, 1126, 1154,
+ 1064, 1153, 1140, 1095, 1048, -2159, 1125, 1110, 1137, -2175, 823, 823, 1139, 1138, 807, 807,
+ 384, 264, 368, 263, 868, 838, 853, 791, 867, 822, 852, 837, 866, 806, 865, 790,
+ -2319, 851, 821, 836, 352, 262, 850, 805, 849, -2399, 533, 533, 835, 820, 336, 261,
+ 578, 548, 563, 577, 532, 532, 832, 772, 562, 562, 547, 547, 305, 275, 560, 515,
+ 290, 290, 288, 258
+};
+
+static const uint8_t tab32[] = { 130, 162, 193, 209, 44, 28, 76, 140, 9, 9, 9, 9, 9, 9,
+ 9, 9, 190, 254, 222, 238, 126, 94, 157, 157, 109, 61, 173, 205 };
+static const uint8_t tab33[] = { 252, 236, 220, 204, 188, 172, 156, 140, 124, 108, 92, 76, 60, 44, 28, 12 };
+static const int16_t tabindex[2 * 16] = { 0, 32, 64, 98, 0, 132, 180, 218, 292, 364, 426,
+ 538, 648, 746, 0, 1126, 1460, 1460, 1460, 1460, 1460, 1460,
+ 1460, 1460, 1842, 1842, 1842, 1842, 1842, 1842, 1842, 1842 };
+static const uint8_t g_linbits[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 2, 3, 4, 6, 8, 10, 13, 4, 5, 6, 7, 8, 9, 11, 13 };
diff --git a/otherarch/acestep/request.cpp b/otherarch/acestep/request.cpp
index 1a0225ccd..a3d0b3696 100644
--- a/otherarch/acestep/request.cpp
+++ b/otherarch/acestep/request.cpp
@@ -23,7 +23,7 @@ void request_init(AceRequest * r) {
r->task_type = "text2music";
r->seed = -1;
r->thinking = false;
- r->lm_temperature = 0.85f;
+ r->lm_temperature = 0.9f;
r->lm_cfg_scale = 3.0f;
r->lm_top_p = 0.9f;
r->lm_top_k = 50;
diff --git a/otherarch/utils.cpp b/otherarch/utils.cpp
index 80c072e58..b90f673eb 100644
--- a/otherarch/utils.cpp
+++ b/otherarch/utils.cpp
@@ -23,6 +23,8 @@
// #define MA_API static
#include "miniaudio/miniaudio.h"
+#include "acestep/mp3/mp3enc.h"
+
void utreplace(std::string & str, const std::string & needle, const std::string & replacement) {
size_t pos = 0;
while ((pos = str.find(needle, pos)) != std::string::npos) {
@@ -605,6 +607,54 @@ std::string save_stereo_wav16_base64(const std::vector & raw_audio, int T
return kcpp_base64_encode(wav_data);
}
+std::string save_stereo_mp3_base64(const std::vector & raw_audio,int T_audio,int sample_rate) {
+ const float * enc_audio = raw_audio.data();
+ int enc_T = T_audio;
+ int enc_sr = sample_rate;
+ std::vector resampled;
+
+ // resample to 44100 if sr is not a valid MPEG1 rate
+ if (sample_rate != 32000 && sample_rate != 44100 && sample_rate != 48000) {
+ resampled = resample_wav(2,raw_audio,sample_rate,44100);
+ enc_audio = resampled.data();
+ enc_sr = 44100;
+ }
+
+ const int kbps = 128;
+ mp3enc_t * enc = mp3enc_init(enc_sr, 2, kbps);
+ if (!enc) {
+ fprintf(stderr, "[Audio] mp3enc_init failed\n");
+ return "";
+ }
+
+ std::string mp3_data;
+ mp3_data.reserve(enc_T); // rough preallocation
+
+ int chunk = enc_sr;
+
+ // reusable buffer (replaces malloc inside loop)
+ std::vector buf((size_t)chunk * 2);
+
+ for (int pos = 0; pos < enc_T; pos += chunk) {
+ int n = (pos + chunk <= enc_T) ? chunk : (enc_T - pos);
+ // build planar chunk
+ memcpy(buf.data(), enc_audio + pos, (size_t)n * sizeof(float));
+ memcpy(buf.data() + n, enc_audio + enc_T + pos, (size_t)n * sizeof(float));
+ int out_size = 0;
+ const uint8_t * mp3 = mp3enc_encode(enc, buf.data(), n, &out_size);
+ if (out_size > 0) {
+ mp3_data.append((const char *) mp3, out_size);
+ }
+ }
+ int flush_size = 0;
+ const uint8_t * flush_data = mp3enc_flush(enc, &flush_size);
+ if (flush_size > 0) {
+ mp3_data.append((const char *) flush_data, flush_size);
+ }
+ mp3enc_free(enc);
+ return kcpp_base64_encode(mp3_data);
+}
+
//a very rudimentary all in one sampling function which has no dependencies
int32_t kcpp_quick_sample(float * logits, const int n_logits, const std::vector & last_n_tokens, float rep_pen, float top_p, int top_k, float temp, std::mt19937 & rng)
{
diff --git a/otherarch/utils.h b/otherarch/utils.h
index 8292eaa44..5863fa7ea 100644
--- a/otherarch/utils.h
+++ b/otherarch/utils.h
@@ -159,4 +159,5 @@ struct wav_ulaw_header {
std::string save_ulaw_wav8_base64(const std::vector &data, int sample_rate);
std::string save_wav16_base64(const std::vector &data, int sample_rate);
-std::string save_stereo_wav16_base64(const std::vector & raw_audio, int T_audio, int sample_rate);
\ No newline at end of file
+std::string save_stereo_wav16_base64(const std::vector & raw_audio, int T_audio, int sample_rate);
+std::string save_stereo_mp3_base64(const std::vector & raw_audio,int T_audio,int sample_rate);
\ No newline at end of file