11use crate :: error:: Result ;
22use crate :: macros:: decode_err;
3+ use crate :: probe:: ParsingMode ;
34use crate :: properties:: FileProperties ;
45
56use std:: convert:: TryInto ;
@@ -76,6 +77,7 @@ pub(super) fn read_properties<R>(
7677 data : & mut R ,
7778 stream_len : u64 ,
7879 file_length : u64 ,
80+ parse_mode : ParsingMode ,
7981) -> Result < ApeProperties >
8082where
8183 R : Read + Seek ,
8688
8789 // Property reading differs between versions
8890 if version >= 3980 {
89- properties_gt_3980 ( data, version, stream_len, file_length)
91+ properties_gt_3980 ( data, version, stream_len, file_length, parse_mode )
9092 } else {
91- properties_lt_3980 ( data, version, stream_len, file_length)
93+ properties_lt_3980 ( data, version, stream_len, file_length, parse_mode )
9294 }
9395}
9496
@@ -97,6 +99,7 @@ fn properties_gt_3980<R>(
9799 version : u16 ,
98100 stream_len : u64 ,
99101 file_length : u64 ,
102+ parse_mode : ParsingMode ,
100103) -> Result < ApeProperties >
101104where
102105 R : Read + Seek ,
@@ -126,6 +129,9 @@ where
126129 data. read_exact ( & mut header)
127130 . map_err ( |_| decode_err ! ( Ape , "Not enough data left in reader to finish MAC header" ) ) ?;
128131
132+ let mut properties = ApeProperties :: default ( ) ;
133+ properties. version = version;
134+
129135 // Skip the first 4 bytes of the header
130136 // Compression type (2)
131137 // Format flags (2)
@@ -135,72 +141,57 @@ where
135141 let final_frame_blocks = header_read. read_u32 :: < LittleEndian > ( ) ?;
136142 let total_frames = header_read. read_u32 :: < LittleEndian > ( ) ?;
137143
138- if total_frames == 0 {
139- decode_err ! ( @BAIL Ape , "File contains no frames" ) ;
140- }
141-
142- let bits_per_sample = header_read. read_u16 :: < LittleEndian > ( ) ?;
144+ properties. bit_depth = header_read. read_u16 :: < LittleEndian > ( ) ? as u8 ;
145+ properties. channels = header_read. read_u16 :: < LittleEndian > ( ) ? as u8 ;
146+ properties. sample_rate = header_read. read_u32 :: < LittleEndian > ( ) ?;
143147
144- let channels = header_read . read_u16 :: < LittleEndian > ( ) ? ;
145-
146- if ! ( 1 ..= 32 ) . contains ( & channels ) {
147- decode_err ! ( @ BAIL Ape , "File has an invalid channel count (must be between 1 and 32 inclusive)" ) ;
148+ match verify ( total_frames , properties . channels ) {
149+ Err ( e ) if parse_mode == ParsingMode :: Strict => return Err ( e ) ,
150+ Err ( _ ) => return Ok ( properties ) ,
151+ _ => { } ,
148152 }
149153
150- let sample_rate = header_read. read_u32 :: < LittleEndian > ( ) ?;
151-
152- let ( duration, overall_bitrate, audio_bitrate) = get_duration_bitrate (
154+ get_duration_bitrate (
155+ & mut properties,
153156 file_length,
154157 total_frames,
155158 final_frame_blocks,
156159 blocks_per_frame,
157- sample_rate,
158160 stream_len,
159161 ) ;
160162
161- Ok ( ApeProperties {
162- version,
163- duration,
164- overall_bitrate,
165- audio_bitrate,
166- sample_rate,
167- bit_depth : bits_per_sample as u8 ,
168- channels : channels as u8 ,
169- } )
163+ Ok ( properties)
170164}
171165
172166fn properties_lt_3980 < R > (
173167 data : & mut R ,
174168 version : u16 ,
175169 stream_len : u64 ,
176170 file_length : u64 ,
171+ parse_mode : ParsingMode ,
177172) -> Result < ApeProperties >
178173where
179- R : Read + Seek ,
174+ R : Read ,
180175{
181176 // Versions < 3980 don't have a descriptor
182177 let mut header = [ 0 ; 26 ] ;
183178 data. read_exact ( & mut header)
184179 . map_err ( |_| decode_err ! ( Ape , "Not enough data left in reader to finish MAC header" ) ) ?;
185180
186- // We don't need all the header data, so just make 2 slices
187- let header_first = & mut & header [ .. 8 ] ;
181+ let mut properties = ApeProperties :: default ( ) ;
182+ properties . version = version ;
188183
189- // Skipping 8 bytes
190- // WAV header length (4)
191- // WAV tail length (4)
192- let header_second = & mut & header[ 18 ..] ;
184+ let header_reader = & mut & header[ ..] ;
193185
194- let compression_level = header_first. read_u16 :: < LittleEndian > ( ) ?;
195-
196- let format_flags = header_first. read_u16 :: < LittleEndian > ( ) ?;
197186 // https://github.com/fernandotcl/monkeys-audio/blob/5fe956c7e67c13daa80518a4cc7001e9fa185297/src/MACLib/MACLib.h#L74
198- let bit_depth = if ( format_flags & 0b1 ) == 1 {
199- 8
200- } else if ( format_flags & 0b100 ) == 4 {
201- 24
187+ let compression_level = header_reader. read_u16 :: < LittleEndian > ( ) ?;
188+ let format_flags = header_reader. read_u16 :: < LittleEndian > ( ) ?;
189+ if ( format_flags & 0b1 ) == 1 {
190+ properties. bit_depth = 8
191+ } else if ( format_flags & 0b1000 ) == 8 {
192+ properties. bit_depth = 24
202193 } else {
203- 16
194+ properties . bit_depth = 16
204195 } ;
205196
206197 let blocks_per_frame = match version {
@@ -209,74 +200,68 @@ where
209200 _ => 9216 ,
210201 } ;
211202
212- let channels = header_first. read_u16 :: < LittleEndian > ( ) ?;
213-
214- if !( 1 ..=32 ) . contains ( & channels) {
215- decode_err ! ( @BAIL Ape , "File has an invalid channel count (must be between 1 and 32 inclusive)" ) ;
216- }
203+ properties. channels = header_reader. read_u16 :: < LittleEndian > ( ) ? as u8 ;
204+ properties. sample_rate = header_reader. read_u32 :: < LittleEndian > ( ) ?;
217205
218- let sample_rate = header_first. read_u32 :: < LittleEndian > ( ) ?;
206+ // Skipping 8 bytes
207+ // WAV header length (4)
208+ // WAV tail length (4)
209+ let mut _skip = [ 0 ; 8 ] ;
210+ header_reader. read_exact ( & mut _skip) ?;
219211
220- // Move on the second part of header
221- let total_frames = header_second . read_u32 :: < LittleEndian > ( ) ?;
212+ let total_frames = header_reader . read_u32 :: < LittleEndian > ( ) ? ;
213+ let final_frame_blocks = header_reader . read_u32 :: < LittleEndian > ( ) ?;
222214
223- if total_frames == 0 {
224- decode_err ! ( @BAIL Ape , "File contains no frames" ) ;
215+ match verify ( total_frames, properties. channels ) {
216+ Err ( e) if parse_mode == ParsingMode :: Strict => return Err ( e) ,
217+ Err ( _) => return Ok ( properties) ,
218+ _ => { } ,
225219 }
226220
227- let final_frame_blocks = data. read_u32 :: < LittleEndian > ( ) ?;
228-
229- let ( duration, overall_bitrate, audio_bitrate) = get_duration_bitrate (
221+ get_duration_bitrate (
222+ & mut properties,
230223 file_length,
231224 total_frames,
232225 final_frame_blocks,
233226 blocks_per_frame,
234- sample_rate,
235227 stream_len,
236228 ) ;
237229
238- Ok ( ApeProperties {
239- version,
240- duration,
241- overall_bitrate,
242- audio_bitrate,
243- sample_rate,
244- bit_depth,
245- channels : channels as u8 ,
246- } )
230+ Ok ( properties)
231+ }
232+
233+ /// Verifies the channel count falls within the bounds of the spec, and we have some audio frames to work with.
234+ fn verify ( total_frames : u32 , channels : u8 ) -> Result < ( ) > {
235+ if !( 1 ..=32 ) . contains ( & channels) {
236+ decode_err ! ( @BAIL Ape , "File has an invalid channel count (must be between 1 and 32 inclusive)" ) ;
237+ }
238+
239+ if total_frames == 0 {
240+ decode_err ! ( @BAIL Ape , "File contains no frames" ) ;
241+ }
242+
243+ Ok ( ( ) )
247244}
248245
249246fn get_duration_bitrate (
247+ properties : & mut ApeProperties ,
250248 file_length : u64 ,
251249 total_frames : u32 ,
252250 final_frame_blocks : u32 ,
253251 blocks_per_frame : u32 ,
254- sample_rate : u32 ,
255252 stream_len : u64 ,
256- ) -> ( Duration , u32 , u32 ) {
253+ ) {
257254 let mut total_samples = u64:: from ( final_frame_blocks) ;
258255
259256 if total_samples > 1 {
260257 total_samples += u64:: from ( blocks_per_frame) * u64:: from ( total_frames - 1 )
261258 }
262259
263- let mut overall_bitrate = 0 ;
264- let mut audio_bitrate = 0 ;
265-
266- if sample_rate > 0 {
267- let length = ( total_samples * 1000 ) / u64:: from ( sample_rate) ;
260+ if properties. sample_rate > 0 {
261+ let length = ( total_samples as f64 * 1000.0 ) / f64:: from ( properties. sample_rate ) ;
268262
269- if length > 0 {
270- overall_bitrate = crate :: div_ceil ( file_length * 8 , length) as u32 ;
271- audio_bitrate = crate :: div_ceil ( stream_len * 8 , length) as u32 ;
272- }
273-
274- (
275- Duration :: from_millis ( length) ,
276- overall_bitrate,
277- audio_bitrate,
278- )
279- } else {
280- ( Duration :: ZERO , overall_bitrate, audio_bitrate)
263+ properties. duration = Duration :: from_millis ( ( length + 0.5 ) as u64 ) ;
264+ properties. audio_bitrate = ( ( stream_len as f64 ) * 8.0 / length + 0.5 ) as u32 ;
265+ properties. overall_bitrate = ( ( file_length as f64 ) * 8.0 / length + 0.5 ) as u32 ;
281266 }
282267}
0 commit comments