#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); // MS stereo decision is deferred until after MDCT energy analysis. // mode=1 (joint) allows switching M/S on or off per frame. // mode=0 (stereo) would force L/R always. mode=3 (mono). int mode = (nch == 1) ? 3 : 1; int mode_ext = 0; // set after MDCT energy analysis // 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; // Phase 1: compute MDCT + psy for ALL granules before bit allocation. // We need PE from both granules to weight the bit budget. float mdct_all[2][2][576]; // [granule][channel][576 MDCT lines] float saved_xmin[2][2][MP3ENC_PSY_SFB_MAX]; float saved_pe[2] = { 0.0f, 0.0f }; // Accumulators for MS stereo decision float energy_mid = 0.0f; float energy_side = 0.0f; for (int gr = 0; gr < 2; gr++) { int pcm_offset = gr * 576; // filterbank + MDCT for all channels for (int ch = 0; ch < nch; ch++) { const float * ch_pcm = pcm + ch * 1152 + pcm_offset; 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]; } } } mp3enc_mdct_granule(enc->sb_prev[ch], enc->sb_cur[ch], mdct_all[gr][ch]); memcpy(enc->sb_prev[ch], enc->sb_cur[ch], sizeof(enc->sb_cur[ch])); } // MS stereo energy analysis (before transform, on L/R data). // Accumulate mid and side energy across both granules to decide // whether M/S coding is beneficial for this frame. // M/S wins when L and R are correlated (side energy is small). if (nch == 2) { for (int i = 0; i < enc->lowpass_line && i < 576; i++) { float l = mdct_all[gr][0][i]; float r = mdct_all[gr][1][i]; float m = l + r; float s = l - r; energy_mid += m * m; energy_side += s * s; } } } // MS stereo decision: use M/S when channels are correlated enough // that the side channel is cheap to encode. FhG "almost always uses // ms_stereo" (GPSYCHO docs). We use it unless side energy dominates, // which means the channels are very different (rare for music). bool use_ms = false; if (nch == 2) { // Use M/S unless side channel has more energy than mid. // This is generous -- almost always enables M/S (like FhG). use_ms = (energy_side < energy_mid * 1.2f) || (energy_mid < 1e-20f); mode_ext = use_ms ? 2 : 0; hdr.mode_ext = mode_ext; } // Now apply M/S transform + lowpass + psy for both granules for (int gr = 0; gr < 2; gr++) { // MS stereo transform if (use_ms) { static const float ms_scale = 0.7071067811865476f; // 1/sqrt(2) for (int i = 0; i < 576; i++) { float l = mdct_all[gr][0][i]; float r = mdct_all[gr][1][i]; mdct_all[gr][0][i] = (l + r) * ms_scale; mdct_all[gr][1][i] = (l - r) * ms_scale; } } // adaptive lowpass for (int ch = 0; ch < nch; ch++) { for (int i = enc->lowpass_line; i < 576; i++) { mdct_all[gr][ch][i] = 0.0f; } } // psy: compute masking thresholds and perceptual entropy for (int ch = 0; ch < nch; ch++) { enc->psy.compute(mdct_all[gr][ch], sfb_long, enc->sr_index, ch); memcpy(saved_xmin[gr][ch], enc->psy.xmin, sizeof(enc->psy.xmin)); saved_pe[gr] += enc->psy.pe; } } // Phase 2: PE-weighted bit allocation + quantization. // Give more bits to granules with higher perceptual entropy (more complex // signal). ISO 11172-3 computes PE but then ignores it for allocation; // LAME uses it to drive the bit reservoir. We weight the per-granule // budget proportionally to PE, clamped to avoid starving either granule. int gr_budget[2]; float pe_sum = saved_pe[0] + saved_pe[1]; if (pe_sum > 1e-6f) { float frac0 = saved_pe[0] / pe_sum; gr_budget[0] = (int) ((float) total_md_bits * frac0); gr_budget[1] = total_md_bits - gr_budget[0]; // clamp: neither granule gets less than 20% or more than 80% int lo = total_md_bits / 5; int hi = total_md_bits * 4 / 5; for (int gr = 0; gr < 2; gr++) { if (gr_budget[gr] < lo) { gr_budget[gr] = lo; } if (gr_budget[gr] > hi) { gr_budget[gr] = hi; } } // re-normalize after clamp int clamped_sum = gr_budget[0] + gr_budget[1]; int bits_to_spread = total_md_bits - clamped_sum; gr_budget[0] += bits_to_spread / 2; gr_budget[1] += bits_to_spread - bits_to_spread / 2; } else { gr_budget[0] = total_md_bits / 2; gr_budget[1] = total_md_bits - gr_budget[0]; } int ix[2][2][576]; int total_bits_used = 0; int intra_resv = 0; for (int gr = 0; gr < 2; gr++) { int max_bits = gr_budget[gr] + 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; // quantize each channel with psy thresholds int gr_bits_used = 0; for (int ch = 0; ch < nch; ch++) { int bits = mp3enc_outer_loop(mdct_all[gr][ch], ix[gr][ch], si.gr[gr][ch], saved_xmin[gr][ch], bits_per_ch, sfb_long, enc->sr_index, gr, si.scfsi[ch]); gr_bits_used += bits; } // intra-frame savings: unused bits carry to next granule intra_resv += gr_budget[gr] - 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; }