Skip to main content

shine_rs/
encoder.rs

1//! MP3 encoder implementation
2//!
3//! This module implements the main MP3 encoding functions exactly as defined
4//! in shine's layer3.c. It provides the primary interface for MP3 encoding
5//! including initialization, configuration, and encoding operations.
6
7use crate::bitstream::BitstreamWriter;
8use crate::error::{EncodingError, EncodingResult};
9use crate::tables::{BITRATES, SAMPLERATES};
10use crate::types::{ShineGlobalConfig, ShineSideInfo, GRANULE_SIZE};
11
12/// Buffer size for bitstream (matches shine BUFFER_SIZE)
13/// (ref/shine/src/lib/bitstream.h:19)
14const BUFFER_SIZE: i32 = 4096;
15
16/// MPEG version constants (matches shine's mpeg_versions enum)
17/// (ref/shine/src/lib/layer3.h:10)
18pub const MPEG_I: i32 = 3;
19pub const MPEG_II: i32 = 2;
20pub const MPEG_25: i32 = 0;
21
22/// MPEG layer constants (matches shine's mpeg_layers enum)
23/// (ref/shine/src/lib/layer3.h:13)
24pub const LAYER_III: i32 = 1;
25
26/// Emphasis constants (matches shine's emph enum)
27/// (ref/shine/src/lib/layer3.h:25)
28pub const NONE: i32 = 0;
29
30/// Granules per frame for different MPEG versions (matches shine's granules_per_frame)
31/// (ref/shine/src/lib/layer3.c:9-14)
32static GRANULES_PER_FRAME: [i32; 4] = [
33    1,  // MPEG 2.5
34    -1, // Reserved
35    1,  // MPEG II
36    2,  // MPEG I
37];
38
39/// Public wave configuration (matches shine_wave_t)
40/// (ref/shine/src/lib/layer3.h:16-19)
41#[repr(C)]
42#[derive(Debug, Clone)]
43pub struct ShineWave {
44    pub channels: i32,
45    pub samplerate: i32,
46}
47
48impl Default for ShineWave {
49    fn default() -> Self {
50        Self {
51            channels: 2,
52            samplerate: 44100,
53        }
54    }
55}
56
57/// Public MPEG configuration (matches shine_mpeg_t)
58/// (ref/shine/src/lib/layer3.h:21-34)
59#[repr(C)]
60#[derive(Debug, Clone)]
61pub struct ShineMpeg {
62    pub mode: i32,
63    pub bitr: i32,
64    pub emph: i32,
65    pub copyright: i32,
66    pub original: i32,
67}
68
69impl Default for ShineMpeg {
70    fn default() -> Self {
71        let mut mpeg = Self {
72            mode: 0,
73            bitr: 128,
74            emph: NONE,
75            copyright: 0,
76            original: 1,
77        };
78        shine_set_config_mpeg_defaults(&mut mpeg);
79        mpeg
80    }
81}
82
83/// Public configuration structure (matches shine_config_t)
84/// (ref/shine/src/lib/layer3.h:36-38)
85#[repr(C)]
86#[derive(Debug, Clone, Default)]
87pub struct ShineConfig {
88    pub wave: ShineWave,
89    pub mpeg: ShineMpeg,
90}
91
92/// Set default values for important vars (matches shine_set_config_mpeg_defaults)
93/// (ref/shine/src/lib/layer3.c:16-21)
94pub fn shine_set_config_mpeg_defaults(mpeg: &mut ShineMpeg) {
95    mpeg.bitr = 128;
96    mpeg.emph = NONE;
97    mpeg.copyright = 0;
98    mpeg.original = 1;
99}
100
101/// Pick mpeg version according to samplerate index (matches shine_mpeg_version)
102/// (ref/shine/src/lib/layer3.c:23-33)
103pub fn shine_mpeg_version(samplerate_index: i32) -> i32 {
104    if samplerate_index < 3 {
105        // First 3 samplerates are for MPEG-I
106        MPEG_I
107    } else if samplerate_index < 6 {
108        // Then it's MPEG-II
109        MPEG_II
110    } else {
111        // Finally, MPEG-2.5
112        MPEG_25
113    }
114}
115
116/// Find samplerate index (matches shine_find_samplerate_index)
117/// (ref/shine/src/lib/layer3.c:35-43)
118pub fn shine_find_samplerate_index(freq: i32) -> i32 {
119    SAMPLERATES
120        .iter()
121        .position(|&rate| rate == freq)
122        .map(|i| i as i32)
123        .unwrap_or(-1)
124}
125
126/// Find bitrate index (matches shine_find_bitrate_index)
127/// (ref/shine/src/lib/layer3.c:45-53)
128pub fn shine_find_bitrate_index(bitr: i32, mpeg_version: i32) -> i32 {
129    BITRATES
130        .iter()
131        .position(|&rates| rates[mpeg_version as usize] == bitr)
132        .map(|i| i as i32)
133        .unwrap_or(-1)
134}
135
136/// Check configuration validity (matches shine_check_config)
137/// (ref/shine/src/lib/layer3.c:55-69)
138pub fn shine_check_config(freq: i32, bitr: i32) -> i32 {
139    let samplerate_index = shine_find_samplerate_index(freq);
140    if samplerate_index < 0 {
141        return -1;
142    }
143
144    let mpeg_version = shine_mpeg_version(samplerate_index);
145
146    let bitrate_index = shine_find_bitrate_index(bitr, mpeg_version);
147    if bitrate_index < 0 {
148        return -1;
149    }
150
151    mpeg_version
152}
153
154/// Get samples per pass (matches shine_samples_per_pass)
155/// (ref/shine/src/lib/layer3.c:71-73)
156pub fn shine_samples_per_pass(config: &ShineGlobalConfig) -> i32 {
157    config.mpeg.granules_per_frame * GRANULE_SIZE as i32
158}
159
160/// Compute default encoding values (matches shine_initialise)
161/// (ref/shine/src/lib/layer3.c:75-134)
162pub fn shine_initialise(pub_config: &ShineConfig) -> EncodingResult<Box<ShineGlobalConfig>> {
163    if shine_check_config(pub_config.wave.samplerate, pub_config.mpeg.bitr) < 0 {
164        return Err(EncodingError::ValidationError(
165            "Invalid configuration".to_string(),
166        ));
167    }
168
169    let mut config = Box::new(ShineGlobalConfig::default());
170
171    // Initialize submodules
172    crate::subband::shine_subband_initialise(&mut config.subband);
173    crate::mdct::shine_mdct_initialise(&mut config);
174    crate::quantization::shine_loop_initialise(&mut config);
175
176    // Copy public config
177    config.wave.channels = pub_config.wave.channels;
178    config.wave.samplerate = pub_config.wave.samplerate;
179    config.mpeg.mode = pub_config.mpeg.mode;
180    config.mpeg.bitr = pub_config.mpeg.bitr;
181    config.mpeg.emph = pub_config.mpeg.emph;
182    config.mpeg.copyright = pub_config.mpeg.copyright;
183    config.mpeg.original = pub_config.mpeg.original;
184
185    // Set default values
186    config.resv_max = 0;
187    config.resv_size = 0;
188    config.mpeg.layer = LAYER_III;
189    config.mpeg.crc = 0;
190    config.mpeg.ext = 0;
191    config.mpeg.mode_ext = 0;
192    config.mpeg.bits_per_slot = 8;
193
194    config.mpeg.samplerate_index = shine_find_samplerate_index(config.wave.samplerate);
195    config.mpeg.version = shine_mpeg_version(config.mpeg.samplerate_index);
196    config.mpeg.bitrate_index = shine_find_bitrate_index(config.mpeg.bitr, config.mpeg.version);
197    config.mpeg.granules_per_frame = GRANULES_PER_FRAME[config.mpeg.version as usize];
198
199    // Figure average number of 'slots' per frame
200    let avg_slots_per_frame = (config.mpeg.granules_per_frame as f64 * GRANULE_SIZE as f64
201        / config.wave.samplerate as f64)
202        * (1000.0 * config.mpeg.bitr as f64 / config.mpeg.bits_per_slot as f64);
203
204    config.mpeg.whole_slots_per_frame = avg_slots_per_frame as i32;
205
206    config.mpeg.frac_slots_per_frame =
207        avg_slots_per_frame - config.mpeg.whole_slots_per_frame as f64;
208    config.mpeg.slot_lag = -config.mpeg.frac_slots_per_frame;
209
210    if config.mpeg.frac_slots_per_frame == 0.0 {
211        config.mpeg.padding = 0;
212    }
213
214    config.bs = BitstreamWriter::new(BUFFER_SIZE);
215
216    // Clear side info (matches memset in shine)
217    config.side_info = ShineSideInfo::default();
218
219    // Determine the mean bitrate for main data
220    if config.mpeg.granules_per_frame == 2 {
221        // MPEG 1
222        config.sideinfo_len = 8 * if config.wave.channels == 1 {
223            4 + 17
224        } else {
225            4 + 32
226        };
227    } else {
228        // MPEG 2
229        config.sideinfo_len = 8 * if config.wave.channels == 1 {
230            4 + 9
231        } else {
232            4 + 17
233        };
234    }
235
236    Ok(config)
237}
238
239/// Internal encoding function (matches shine_encode_buffer_internal)
240/// (ref/shine/src/lib/layer3.c:136-158)
241fn shine_encode_buffer_internal(
242    config: &mut ShineGlobalConfig,
243    stride: i32,
244) -> EncodingResult<(&[u8], usize)> {
245    #[cfg(feature = "diagnostics")]
246    let frame_num = crate::get_next_frame_number();
247
248    // Start frame data collection
249    #[cfg(feature = "diagnostics")]
250    crate::diagnostics::start_frame_collection(frame_num);
251
252    // Dynamic padding calculation (matches shine exactly)
253    if config.mpeg.frac_slots_per_frame != 0.0 {
254        config.mpeg.padding = if config.mpeg.slot_lag <= (config.mpeg.frac_slots_per_frame - 1.0) {
255            1
256        } else {
257            0
258        };
259        config.mpeg.slot_lag += config.mpeg.padding as f64 - config.mpeg.frac_slots_per_frame;
260    }
261
262    config.mpeg.bits_per_frame = 8 * (config.mpeg.whole_slots_per_frame + config.mpeg.padding);
263    config.mean_bits =
264        (config.mpeg.bits_per_frame - config.sideinfo_len) / config.mpeg.granules_per_frame;
265
266    // Apply mdct to the polyphase output
267    crate::mdct::shine_mdct_sub(config, stride);
268
269    // Bit and noise allocation
270    crate::quantization::shine_iteration_loop(config);
271
272    // Write the frame to the bitstream
273    crate::bitstream::format_bitstream(config)?;
274
275    // Return data exactly as shine does: return current data_position and reset it
276    let written = config.bs.data_position as usize;
277    config.bs.data_position = 0;
278
279    // Print key parameters for verification (debug mode only)
280    #[cfg(feature = "diagnostics")]
281    {
282        // Silent - no debug output
283    }
284
285    // Record bitstream data for test collection
286    #[cfg(feature = "diagnostics")]
287    crate::diagnostics::record_bitstream_data(
288        config.mpeg.padding,
289        config.mpeg.bits_per_frame,
290        written,
291        config.mpeg.slot_lag,
292    );
293
294    Ok((&config.bs.data[..written], written))
295}
296
297/// Encode buffer with separate channel arrays (matches shine_encode_buffer)
298/// (ref/shine/src/lib/layer3.c:160-167)
299pub fn shine_encode_buffer<'a>(
300    config: &'a mut ShineGlobalConfig,
301    data: &[*const i16],
302) -> EncodingResult<(&'a [u8], usize)> {
303    config.buffer[0] = data[0] as *mut i16;
304    if config.wave.channels == 2 {
305        config.buffer[1] = data[1] as *mut i16;
306    }
307
308    shine_encode_buffer_internal(config, 1)
309}
310
311/// Encode buffer with interleaved channels (matches shine_encode_buffer_interleaved)
312/// (ref/shine/src/lib/layer3.c:169-176)
313///
314/// # Safety
315///
316/// This function is unsafe because it accepts a raw pointer to PCM data.
317/// The caller must ensure that:
318/// - `data` points to valid PCM samples
319/// - The data contains at least `GRANULE_SIZE * channels` samples
320/// - The data remains valid for the duration of the function call
321/// - The pointer is properly aligned for i16 access
322pub unsafe fn shine_encode_buffer_interleaved(
323    config: &mut ShineGlobalConfig,
324    data: *const i16,
325) -> EncodingResult<(&[u8], usize)> {
326    config.buffer[0] = data as *mut i16;
327    if config.wave.channels == 2 {
328        config.buffer[1] = data.offset(1) as *mut i16;
329    }
330
331    shine_encode_buffer_internal(config, config.wave.channels)
332}
333
334/// Flush remaining data (matches shine_flush)
335/// (ref/shine/src/lib/layer3.c:178-183)
336pub fn shine_flush(config: &mut ShineGlobalConfig) -> (&[u8], usize) {
337    // Shine's flush function simply returns current data_position without any bitstream flush
338    // *written = config->bs.data_position;
339    // config->bs.data_position = 0;
340    let written = config.bs.data_position as usize;
341    config.bs.data_position = 0;
342
343    (&config.bs.data[..written], written)
344}
345
346/// Close encoder and free resources (matches shine_close)
347/// (ref/shine/src/lib/layer3.c:185-188)
348pub fn shine_close(_config: Box<ShineGlobalConfig>) {
349    // shine_close_bit_stream(&config->bs);
350    // free(config);
351    // In Rust, the Box will be automatically dropped
352}