1
1
use crate :: error:: Result ;
2
2
use crate :: macros:: decode_err;
3
+ use crate :: probe:: ParsingMode ;
3
4
use crate :: properties:: FileProperties ;
4
5
5
6
use std:: convert:: TryInto ;
@@ -76,6 +77,7 @@ pub(super) fn read_properties<R>(
76
77
data : & mut R ,
77
78
stream_len : u64 ,
78
79
file_length : u64 ,
80
+ parse_mode : ParsingMode ,
79
81
) -> Result < ApeProperties >
80
82
where
81
83
R : Read + Seek ,
86
88
87
89
// Property reading differs between versions
88
90
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 )
90
92
} else {
91
- properties_lt_3980 ( data, version, stream_len, file_length)
93
+ properties_lt_3980 ( data, version, stream_len, file_length, parse_mode )
92
94
}
93
95
}
94
96
@@ -97,6 +99,7 @@ fn properties_gt_3980<R>(
97
99
version : u16 ,
98
100
stream_len : u64 ,
99
101
file_length : u64 ,
102
+ parse_mode : ParsingMode ,
100
103
) -> Result < ApeProperties >
101
104
where
102
105
R : Read + Seek ,
@@ -126,6 +129,9 @@ where
126
129
data. read_exact ( & mut header)
127
130
. map_err ( |_| decode_err ! ( Ape , "Not enough data left in reader to finish MAC header" ) ) ?;
128
131
132
+ let mut properties = ApeProperties :: default ( ) ;
133
+ properties. version = version;
134
+
129
135
// Skip the first 4 bytes of the header
130
136
// Compression type (2)
131
137
// Format flags (2)
@@ -135,72 +141,57 @@ where
135
141
let final_frame_blocks = header_read. read_u32 :: < LittleEndian > ( ) ?;
136
142
let total_frames = header_read. read_u32 :: < LittleEndian > ( ) ?;
137
143
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 > ( ) ?;
143
147
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
+ _ => { } ,
148
152
}
149
153
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,
153
156
file_length,
154
157
total_frames,
155
158
final_frame_blocks,
156
159
blocks_per_frame,
157
- sample_rate,
158
160
stream_len,
159
161
) ;
160
162
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)
170
164
}
171
165
172
166
fn properties_lt_3980 < R > (
173
167
data : & mut R ,
174
168
version : u16 ,
175
169
stream_len : u64 ,
176
170
file_length : u64 ,
171
+ parse_mode : ParsingMode ,
177
172
) -> Result < ApeProperties >
178
173
where
179
- R : Read + Seek ,
174
+ R : Read ,
180
175
{
181
176
// Versions < 3980 don't have a descriptor
182
177
let mut header = [ 0 ; 26 ] ;
183
178
data. read_exact ( & mut header)
184
179
. map_err ( |_| decode_err ! ( Ape , "Not enough data left in reader to finish MAC header" ) ) ?;
185
180
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 ;
188
183
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[ ..] ;
193
185
194
- let compression_level = header_first. read_u16 :: < LittleEndian > ( ) ?;
195
-
196
- let format_flags = header_first. read_u16 :: < LittleEndian > ( ) ?;
197
186
// 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
202
193
} else {
203
- 16
194
+ properties . bit_depth = 16
204
195
} ;
205
196
206
197
let blocks_per_frame = match version {
@@ -209,74 +200,68 @@ where
209
200
_ => 9216 ,
210
201
} ;
211
202
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 > ( ) ?;
217
205
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) ?;
219
211
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 > ( ) ?;
222
214
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
+ _ => { } ,
225
219
}
226
220
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,
230
223
file_length,
231
224
total_frames,
232
225
final_frame_blocks,
233
226
blocks_per_frame,
234
- sample_rate,
235
227
stream_len,
236
228
) ;
237
229
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 ( ( ) )
247
244
}
248
245
249
246
fn get_duration_bitrate (
247
+ properties : & mut ApeProperties ,
250
248
file_length : u64 ,
251
249
total_frames : u32 ,
252
250
final_frame_blocks : u32 ,
253
251
blocks_per_frame : u32 ,
254
- sample_rate : u32 ,
255
252
stream_len : u64 ,
256
- ) -> ( Duration , u32 , u32 ) {
253
+ ) {
257
254
let mut total_samples = u64:: from ( final_frame_blocks) ;
258
255
259
256
if total_samples > 1 {
260
257
total_samples += u64:: from ( blocks_per_frame) * u64:: from ( total_frames - 1 )
261
258
}
262
259
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 ) ;
268
262
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 ;
281
266
}
282
267
}
0 commit comments