592 lines
21 KiB
C
592 lines
21 KiB
C
#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 <cstdlib>
|
|
#include <cstring>
|
|
|
|
// 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;
|
|
}
|