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::error::{EncodingError, EncodingResult};
8use crate::types::{ShineGlobalConfig, ShineSideInfo, GRANULE_SIZE};
9use crate::tables::{SAMPLERATES, BITRATES};
10use crate::bitstream::BitstreamWriter;
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
48/// Public MPEG configuration (matches shine_mpeg_t)
49/// (ref/shine/src/lib/layer3.h:21-34)
50#[repr(C)]
51#[derive(Debug, Clone)]
52pub struct ShineMpeg {
53    pub mode: i32,
54    pub bitr: i32,
55    pub emph: i32,
56    pub copyright: i32,
57    pub original: i32,
58}
59
60/// Public configuration structure (matches shine_config_t)
61/// (ref/shine/src/lib/layer3.h:36-38)
62#[repr(C)]
63#[derive(Debug, Clone)]
64pub struct ShineConfig {
65    pub wave: ShineWave,
66    pub mpeg: ShineMpeg,
67}
68
69/// Set default values for important vars (matches shine_set_config_mpeg_defaults)
70/// (ref/shine/src/lib/layer3.c:16-21)
71pub fn shine_set_config_mpeg_defaults(mpeg: &mut ShineMpeg) {
72    mpeg.bitr = 128;
73    mpeg.emph = NONE;
74    mpeg.copyright = 0;
75    mpeg.original = 1;
76}
77
78/// Pick mpeg version according to samplerate index (matches shine_mpeg_version)
79/// (ref/shine/src/lib/layer3.c:23-33)
80pub fn shine_mpeg_version(samplerate_index: i32) -> i32 {
81    if samplerate_index < 3 {
82        // First 3 samplerates are for MPEG-I
83        MPEG_I
84    } else if samplerate_index < 6 {
85        // Then it's MPEG-II
86        MPEG_II
87    } else {
88        // Finally, MPEG-2.5
89        MPEG_25
90    }
91}
92
93/// Find samplerate index (matches shine_find_samplerate_index)
94/// (ref/shine/src/lib/layer3.c:35-43)
95pub fn shine_find_samplerate_index(freq: i32) -> i32 {
96    for i in 0..9 {
97        if freq == SAMPLERATES[i] {
98            return i as i32;
99        }
100    }
101    -1 // error - not a valid samplerate for encoder
102}
103
104/// Find bitrate index (matches shine_find_bitrate_index)
105/// (ref/shine/src/lib/layer3.c:45-53)
106pub fn shine_find_bitrate_index(bitr: i32, mpeg_version: i32) -> i32 {
107    for i in 0..16 {
108        if bitr == BITRATES[i][mpeg_version as usize] {
109            return i as i32;
110        }
111    }
112    -1 // error - not a valid bitrate for encoder
113}
114
115/// Check configuration validity (matches shine_check_config)
116/// (ref/shine/src/lib/layer3.c:55-69)
117pub fn shine_check_config(freq: i32, bitr: i32) -> i32 {
118    let samplerate_index = shine_find_samplerate_index(freq);
119    if samplerate_index < 0 {
120        return -1;
121    }
122
123    let mpeg_version = shine_mpeg_version(samplerate_index);
124
125    let bitrate_index = shine_find_bitrate_index(bitr, mpeg_version);
126    if bitrate_index < 0 {
127        return -1;
128    }
129
130    mpeg_version
131}
132
133/// Get samples per pass (matches shine_samples_per_pass)
134/// (ref/shine/src/lib/layer3.c:71-73)
135pub fn shine_samples_per_pass(config: &ShineGlobalConfig) -> i32 {
136    config.mpeg.granules_per_frame * GRANULE_SIZE as i32
137}
138
139/// Compute default encoding values (matches shine_initialise)
140/// (ref/shine/src/lib/layer3.c:75-134)
141pub fn shine_initialise(pub_config: &ShineConfig) -> EncodingResult<Box<ShineGlobalConfig>> {
142    if shine_check_config(pub_config.wave.samplerate, pub_config.mpeg.bitr) < 0 {
143        return Err(EncodingError::ValidationError("Invalid configuration".to_string()));
144    }
145
146    let mut config = Box::new(ShineGlobalConfig::default());
147
148    // Initialize submodules
149    crate::subband::shine_subband_initialise(&mut config.subband);
150    crate::mdct::shine_mdct_initialise(&mut config);
151    crate::quantization::shine_loop_initialise(&mut config);
152
153    // Copy public config
154    config.wave.channels = pub_config.wave.channels;
155    config.wave.samplerate = pub_config.wave.samplerate;
156    config.mpeg.mode = pub_config.mpeg.mode;
157    config.mpeg.bitr = pub_config.mpeg.bitr;
158    config.mpeg.emph = pub_config.mpeg.emph;
159    config.mpeg.copyright = pub_config.mpeg.copyright;
160    config.mpeg.original = pub_config.mpeg.original;
161
162    // Set default values
163    config.resv_max = 0;
164    config.resv_size = 0;
165    config.mpeg.layer = LAYER_III;
166    config.mpeg.crc = 0;
167    config.mpeg.ext = 0;
168    config.mpeg.mode_ext = 0;
169    config.mpeg.bits_per_slot = 8;
170
171    config.mpeg.samplerate_index = shine_find_samplerate_index(config.wave.samplerate);
172    config.mpeg.version = shine_mpeg_version(config.mpeg.samplerate_index);
173    config.mpeg.bitrate_index = shine_find_bitrate_index(config.mpeg.bitr, config.mpeg.version);
174    config.mpeg.granules_per_frame = GRANULES_PER_FRAME[config.mpeg.version as usize];
175
176    // Figure average number of 'slots' per frame
177    let avg_slots_per_frame = (config.mpeg.granules_per_frame as f64 * GRANULE_SIZE as f64 /
178                              config.wave.samplerate as f64) *
179                              (1000.0 * config.mpeg.bitr as f64 / config.mpeg.bits_per_slot as f64);
180
181    config.mpeg.whole_slots_per_frame = avg_slots_per_frame as i32;
182
183    config.mpeg.frac_slots_per_frame = avg_slots_per_frame - config.mpeg.whole_slots_per_frame as f64;
184    config.mpeg.slot_lag = -config.mpeg.frac_slots_per_frame;
185
186    if config.mpeg.frac_slots_per_frame == 0.0 {
187        config.mpeg.padding = 0;
188    }
189
190    config.bs = BitstreamWriter::new(BUFFER_SIZE);
191
192    // Clear side info (matches memset in shine)
193    config.side_info = ShineSideInfo::default();
194
195    // Determine the mean bitrate for main data
196    if config.mpeg.granules_per_frame == 2 {
197        // MPEG 1
198        config.sideinfo_len = 8 * if config.wave.channels == 1 { 4 + 17 } else { 4 + 32 };
199    } else {
200        // MPEG 2
201        config.sideinfo_len = 8 * if config.wave.channels == 1 { 4 + 9 } else { 4 + 17 };
202    }
203
204    Ok(config)
205}
206
207/// Internal encoding function (matches shine_encode_buffer_internal)
208/// (ref/shine/src/lib/layer3.c:136-158)
209fn shine_encode_buffer_internal(config: &mut ShineGlobalConfig, stride: i32) -> EncodingResult<(&[u8], usize)> {
210    #[cfg(any(debug_assertions, feature = "diagnostics"))]
211    let frame_num = crate::get_next_frame_number();
212
213    // Start frame data collection
214    #[cfg(feature = "diagnostics")]
215    crate::diagnostics_data::start_frame_collection(frame_num);
216
217    // Dynamic padding calculation (matches shine exactly)
218    if config.mpeg.frac_slots_per_frame != 0.0 {
219        config.mpeg.padding = if config.mpeg.slot_lag <= (config.mpeg.frac_slots_per_frame - 1.0) { 1 } else { 0 };
220        config.mpeg.slot_lag += config.mpeg.padding as f64 - config.mpeg.frac_slots_per_frame;
221    }
222
223    config.mpeg.bits_per_frame = 8 * (config.mpeg.whole_slots_per_frame + config.mpeg.padding);
224    config.mean_bits = (config.mpeg.bits_per_frame - config.sideinfo_len) / config.mpeg.granules_per_frame;
225
226    // Apply mdct to the polyphase output
227    crate::mdct::shine_mdct_sub(config, stride);
228
229    // Bit and noise allocation
230    crate::quantization::shine_iteration_loop(config);
231
232    // Write the frame to the bitstream
233    crate::bitstream::format_bitstream(config)?;
234
235    // Return data exactly as shine does: return current data_position and reset it
236    let written = config.bs.data_position as usize;
237    config.bs.data_position = 0;
238
239    // Print key parameters for verification (debug mode only)
240    #[cfg(any(debug_assertions, feature = "diagnostics"))]
241    {
242        use log::debug;
243        debug!("[Frame {}] pad={}, bits={}, written={}, slot_lag={:.6}",
244                 frame_num, config.mpeg.padding, config.mpeg.bits_per_frame, written, config.mpeg.slot_lag);
245    }
246
247    // Record bitstream data for test collection
248    #[cfg(feature = "diagnostics")]
249    crate::diagnostics_data::record_bitstream_data(
250        config.mpeg.padding,
251        config.mpeg.bits_per_frame,
252        written,
253        config.mpeg.slot_lag
254    );
255
256    // Stop after specified frames for debugging (debug mode only)
257    #[cfg(any(debug_assertions, feature = "diagnostics"))]
258    {
259        // Check for frame limit from environment variable or default to unlimited
260        if let Ok(max_frames_str) = std::env::var("RUST_MP3_MAX_FRAMES") {
261            if let Ok(max_frames) = max_frames_str.parse::<i32>() {
262                if frame_num > max_frames {
263                    #[cfg(any(debug_assertions, feature = "diagnostics"))]
264                    {
265                        use log::info;
266                        info!("[RUST] Stopping after {} frames for comparison", max_frames);
267                    }
268                    // Return a special error to indicate we should stop encoding but still write the file
269                    return Err(EncodingError::StopAfterFrames);
270                }
271            }
272        }
273    }
274
275    Ok((&config.bs.data[..written], written))
276}
277
278/// Encode buffer with separate channel arrays (matches shine_encode_buffer)
279/// (ref/shine/src/lib/layer3.c:160-167)
280pub fn shine_encode_buffer<'a>(config: &'a mut ShineGlobalConfig, data: &[*const i16]) -> EncodingResult<(&'a [u8], usize)> {
281    config.buffer[0] = data[0] as *mut i16;
282    if config.wave.channels == 2 {
283        config.buffer[1] = data[1] as *mut i16;
284    }
285
286    shine_encode_buffer_internal(config, 1)
287}
288
289/// Encode buffer with interleaved channels (matches shine_encode_buffer_interleaved)
290/// (ref/shine/src/lib/layer3.c:169-176)
291pub unsafe fn shine_encode_buffer_interleaved(config: &mut ShineGlobalConfig, data: *const i16) -> EncodingResult<(&[u8], usize)> {
292    config.buffer[0] = data as *mut i16;
293    if config.wave.channels == 2 {
294        config.buffer[1] = data.offset(1) as *mut i16;
295    }
296
297    shine_encode_buffer_internal(config, config.wave.channels)
298}
299
300/// Flush remaining data (matches shine_flush)
301/// (ref/shine/src/lib/layer3.c:178-183)
302pub fn shine_flush(config: &mut ShineGlobalConfig) -> (&[u8], usize) {
303    // Shine's flush function simply returns current data_position without any bitstream flush
304    // *written = config->bs.data_position;
305    // config->bs.data_position = 0;
306    let written = config.bs.data_position as usize;
307    config.bs.data_position = 0;
308
309    (&config.bs.data[..written], written)
310}
311
312/// Close encoder and free resources (matches shine_close)
313/// (ref/shine/src/lib/layer3.c:185-188)
314pub fn shine_close(_config: Box<ShineGlobalConfig>) {
315    // shine_close_bit_stream(&config->bs);
316    // free(config);
317    // In Rust, the Box will be automatically dropped
318}