File Coverage

src/mpc.c
Criterion Covered Total %
statement 147 165 89.0
branch 48 66 72.7
condition n/a
subroutine n/a
pod n/a
total 195 231 84.4


line stmt bran cond sub pod time code
1             /*
2             * Original Copyright:
3             *
4             Copyright (c) 2005, The Musepack Development Team
5             All rights reserved.
6              
7             Redistribution and use in source and binary forms, with or without
8             modification, are permitted provided that the following conditions are
9             met:
10              
11             * Redistributions of source code must retain the above copyright
12             notice, this list of conditions and the following disclaimer.
13              
14             * Redistributions in binary form must reproduce the above
15             copyright notice, this list of conditions and the following
16             disclaimer in the documentation and/or other materials provided
17             with the distribution.
18              
19             * Neither the name of the The Musepack Development Team nor the
20             names of its contributors may be used to endorse or promote
21             products derived from this software without specific prior
22             written permission.
23              
24             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25             "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26             LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27             A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28             OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29             SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30             LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31             DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32             THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33             (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34             OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35             */
36              
37             #include "mpc.h"
38              
39             #define MPC_BLOCK_SIZE 1024
40             #define MPC_OLD_GAIN_REF 64.82
41              
42             const int32_t samplefreqs[4] = { 44100, 48000, 37800, 32000 };
43              
44             // profile is 0...15, where 7...13 is used
45             static const char *
46 4           _mpc_profile_string(uint32_t profile)
47             {
48             static const char na[] = "n.a.";
49             static const char *names[] = {
50             na,
51             "Unstable/Experimental",
52             na,
53             na,
54             na,
55             "below Telephone (q=0)",
56             "below Telephone (q=1)",
57             "Telephone (q=2)",
58             "Thumb (q=3)",
59             "Radio (q=4)",
60             "Standard (q=5)",
61             "Extreme (q=6)",
62             "Insane (q=7)",
63             "BrainDead (q=8)",
64             "above BrainDead (q=9)",
65             "above BrainDead (q=10)"
66             };
67              
68 4 50         return profile >= sizeof(names) / sizeof(*names) ? na : names[profile];
69             }
70              
71             unsigned int
72 6           _mpc_bits_get_size(Buffer *buf, uint64_t *p_size)
73             {
74             unsigned char tmp;
75 6           uint64_t size = 0;
76 6           unsigned int ret = 0;
77              
78             do {
79 9           tmp = buffer_get_char(buf);
80 9           size = (size << 7) | (tmp & 0x7F);
81 9           ret++;
82 9 100         } while((tmp & 0x80));
83              
84 6           *p_size = size;
85 6           return ret;
86             }
87              
88             static void
89 4           _mpc_get_encoder_string(mpc_streaminfo* si)
90             {
91 4           int ver = si->encoder_version;
92 4 100         if (si->stream_version >= 8)
93 1           ver = (si->encoder_version >> 24) * 100 + ((si->encoder_version >> 16) & 0xFF);
94 4 100         if (ver <= 116) {
95 3 100         if (ver == 0) {
96 1           sprintf(si->encoder, "Buschmann 1.7.0...9, Klemm 0.90...1.05");
97             } else {
98 2           switch (ver % 10) {
99             case 0:
100 0           sprintf(si->encoder, "Release %u.%u", ver / 100,
101 0           ver / 10 % 10);
102 0           break;
103             case 2: case 4: case 6: case 8:
104 0           sprintf(si->encoder, "Beta %u.%02u", ver / 100,
105             ver % 100);
106 0           break;
107             default:
108 2           sprintf(si->encoder, "--Alpha-- %u.%02u",
109             ver / 100, ver % 100);
110 3           break;
111             }
112             }
113             } else {
114 1           int major = si->encoder_version >> 24;
115 1           int minor = (si->encoder_version >> 16) & 0xFF;
116 1           int build = (si->encoder_version >> 8) & 0xFF;
117 1           char * tmp = "--Stable--";
118              
119 1 50         if (minor & 1)
120 0           tmp = "--Unstable--";
121              
122 1           sprintf(si->encoder, "%s %u.%u.%u", tmp, major, minor, build);
123             }
124 4           }
125              
126             static int32_t
127 1           _mpc_read_header_sv8(mpc_streaminfo *si)
128             {
129             unsigned char blocktype[2];
130 1           unsigned char *bptr = buffer_ptr(si->buf);
131             uint64_t size;
132              
133 4 50         while ( memcmp(bptr, "AP", 2) != 0 ) { // scan all blocks until audio
134 4           memcpy(blocktype, bptr, 2);
135 4           buffer_consume(si->buf, 2);
136              
137 4           _mpc_bits_get_size(si->buf, &size);
138 4           size -= 3;
139              
140             DEBUG_TRACE("%c%c block, size %llu\n", blocktype[0], blocktype[1], size);
141              
142 4 50         if ( !_check_buf(si->infile, si->buf, size, MPC_BLOCK_SIZE) ) {
143 0           return -1;
144             }
145              
146 4 100         if (memcmp(blocktype, "SH", 2) == 0) {
147             // Skip CRC
148 1           buffer_consume(si->buf, 4);
149              
150 1           si->stream_version = buffer_get_char(si->buf);
151 1           _mpc_bits_get_size(si->buf, &si->pcm_samples);
152 1           _mpc_bits_get_size(si->buf, &si->beg_silence);
153              
154 1           si->is_true_gapless = 1;
155              
156 1           bptr = buffer_ptr(si->buf);
157 1           si->sample_freq = samplefreqs[ (bptr[0] & 0xE0) >> 5 ];
158 1           si->max_band = (bptr[0] & 0x1F) + 1;
159 1           si->channels = ( (bptr[1] & 0xF0) >> 4 ) + 1;
160 1           si->ms = (bptr[1] & 0x8) >> 3;
161 1           si->block_pwr = (bptr[1] & 0x7) * 2;
162 1           buffer_consume(si->buf, 2);
163             }
164 3 100         else if (memcmp(blocktype, "RG", 2) == 0) {
165             // Check version
166 1 50         if ( buffer_get_char(si->buf) != 1 ) {
167             // Skip
168 0           buffer_consume(si->buf, size - 1);
169             }
170             else {
171 1           si->gain_title = buffer_get_short(si->buf);
172 1           si->peak_title = buffer_get_short(si->buf);
173 1           si->gain_album = buffer_get_short(si->buf);
174 1           si->peak_album = buffer_get_short(si->buf);
175             }
176             }
177 2 100         else if (memcmp(blocktype, "EI", 2) == 0) {
178 1           bptr = buffer_ptr(si->buf);
179              
180 1           si->fprofile = ((bptr[0] & 0xFE) >> 1) / 8.;
181 1           si->profile_name = _mpc_profile_string((uint32_t)si->fprofile);
182 1           buffer_consume(si->buf, 1);
183              
184 1           si->encoder_version = buffer_get_char(si->buf) << 24; // major
185 1           si->encoder_version |= buffer_get_char(si->buf) << 16; // minor
186 1           si->encoder_version |= buffer_get_char(si->buf) << 8; // build
187             DEBUG_TRACE("ver: %d\n", si->encoder_version);
188              
189 1           _mpc_get_encoder_string(si);
190             }
191             else {
192 1           break;
193             }
194              
195 3           bptr = buffer_ptr(si->buf);
196             }
197              
198 1           return 0;
199             }
200              
201             static int32_t
202 3           _mpc_read_header_sv7(mpc_streaminfo *si)
203             {
204             unsigned char *bptr;
205              
206             // Update (si->stream_version);
207 3 50         if (si->stream_version > 0x71) {
208 0           return 0;
209             }
210              
211 3           si->bitrate = 0;
212 3           si->frames = buffer_get_int_le(si->buf);
213              
214 3           bptr = buffer_ptr(si->buf);
215 3           si->is = (bptr[3] >> 7) & 0x1;
216 3           si->ms = (bptr[3] >> 6) & 0x1;
217 3           si->max_band = bptr[3] & 0x3F;
218              
219 3           si->block_size = 1;
220 3           si->profile = (bptr[2] >> 4) & 0xF;
221 3           si->profile_name = _mpc_profile_string(si->profile);
222             // skip Link
223 3           si->sample_freq = samplefreqs[bptr[2] & 0x3];
224             // skip MaxLevel
225 3           buffer_consume(si->buf, 4);
226              
227 3           si->peak_title = buffer_get_short_le(si->buf);
228 3           si->gain_title = buffer_get_short_le(si->buf);
229              
230 3           si->peak_album = buffer_get_short_le(si->buf);
231 3           si->gain_album = buffer_get_short_le(si->buf);
232              
233             // convert gain info
234 3 100         if (si->gain_title != 0) {
235 1           int tmp = (int)((MPC_OLD_GAIN_REF - (int16_t)si->gain_title / 100.) * 256. + .5);
236 1 50         if (tmp >= (1 << 16) || tmp < 0) tmp = 0;
    50          
237 1           si->gain_title = (int16_t)tmp;
238             }
239              
240 3 100         if (si->gain_album != 0) {
241 1           int tmp = (int)((MPC_OLD_GAIN_REF - (int16_t)si->gain_album / 100.) * 256. + .5);
242 1 50         if (tmp >= (1 << 16) || tmp < 0) tmp = 0;
    50          
243 1           si->gain_album = (int16_t)tmp;
244             }
245              
246 3 100         if (si->peak_title != 0)
247 1           si->peak_title = (uint16_t) (log10(si->peak_title) * 20 * 256 + .5);
248              
249 3 100         if (si->peak_album != 0)
250 1           si->peak_album = (uint16_t) (log10(si->peak_album) * 20 * 256 + .5);
251              
252 3           bptr = buffer_ptr(si->buf);
253 3           si->is_true_gapless = (bptr[3] >> 7) & 0x1;
254 3           si->last_frame_samples = ((bptr[3] >> 1) & 0x7F) | ((bptr[2] >> 4) & 0xF); // true gapless: valid samples for last frame
255 3           buffer_consume(si->buf, 4);
256              
257 3           bptr = buffer_ptr(si->buf);
258 3           si->encoder_version = bptr[3];
259 3           si->channels = 2;
260              
261 3           _mpc_get_encoder_string(si);
262              
263 3           return 0;
264             }
265              
266             static int
267 4           get_mpcfileinfo(PerlIO *infile, char *file, HV *info)
268             {
269             Buffer buf;
270 4           int32_t ret = 0;
271             unsigned char *bptr;
272              
273             mpc_streaminfo *si;
274              
275 4           Newz(0, si, sizeof(mpc_streaminfo), mpc_streaminfo);
276 4           buffer_init(&buf, MPC_BLOCK_SIZE);
277              
278 4           si->buf = &buf;
279 4           si->infile = infile;
280              
281             // get header position
282 4 50         if ((si->header_position = skip_id3v2(infile)) < 0) {
283 0           PerlIO_printf(PerlIO_stderr(), "Musepack: [Couldn't skip ID3v2]: %s\n", file);
284 0           goto out;
285             }
286              
287             // seek to first byte of mpc data
288 4 50         if (PerlIO_seek(infile, si->header_position, SEEK_SET) < 0) {
289 0           PerlIO_printf(PerlIO_stderr(), "Musepack: [Couldn't seek to offset %d]: %s\n", si->header_position, file);
290 0           goto out;
291             }
292              
293 4 50         if ( !_check_buf(infile, &buf, 128, MPC_BLOCK_SIZE) ) {
294 0           goto out;
295             }
296              
297 4 50         if (PerlIO_seek(infile, si->header_position + 6 * 4, SEEK_SET) < 0) {
298 0           PerlIO_printf(PerlIO_stderr(), "Musepack: [Couldn't seek to offset %d + (6*4)]: %s\n", si->header_position, file);
299 0           goto out;
300             }
301              
302 4           si->tag_offset = PerlIO_tell(infile);
303              
304 4           si->total_file_length = _file_size(infile);
305              
306 4           bptr = buffer_ptr(&buf);
307              
308 4 100         if (memcmp(bptr, "MP+", 3) == 0) {
309 3           buffer_consume(&buf, 3);
310 3           si->stream_version = buffer_get_char(&buf);
311              
312 3 50         if ((si->stream_version & 15) == 7) {
313             DEBUG_TRACE("parsing MPC SV7 header\n");
314 3           ret = _mpc_read_header_sv7(si);
315             }
316              
317             }
318 1 50         else if (memcmp(bptr, "MPCK", 4) == 0) {
319 1           buffer_consume(&buf, 4);
320              
321             DEBUG_TRACE("parsing MPC SV8 header\n");
322 1           ret = _mpc_read_header_sv8(si);
323             }
324             else {
325 0           PerlIO_printf(PerlIO_stderr(), "Not a Musepack SV7 or SV8 file: %s\n", file);
326 0           goto out;
327             }
328              
329             // estimation, exact value needs too much time
330 4 100         if ( !si->pcm_samples )
331 3           si->pcm_samples = 1152 * si->frames - 576;
332              
333 4 50         if (ret == 0) {
334 4           double total_seconds = (double)( (si->pcm_samples * 1.0) / si->sample_freq);
335              
336 4           my_hv_store(info, "stream_version", newSVuv(si->stream_version));
337 4           my_hv_store(info, "samplerate", newSViv(si->sample_freq));
338 4           my_hv_store(info, "channels", newSViv(si->channels));
339 4           my_hv_store(info, "song_length_ms", newSVuv(total_seconds * 1000));
340 4           my_hv_store(info, "bitrate", newSVuv(8 * (double)(si->total_file_length - si->tag_offset) / total_seconds));
341              
342 4           my_hv_store(info, "audio_offset", newSVuv(si->tag_offset));
343 4           my_hv_store(info, "audio_size", newSVuv(si->total_file_length - si->tag_offset));
344 4           my_hv_store(info, "file_size", newSVuv(si->total_file_length));
345 4           my_hv_store(info, "encoder", newSVpv(si->encoder, 0));
346              
347 4 50         if (si->profile_name)
348 4           my_hv_store(info, "profile", newSVpv(si->profile_name, 0));
349              
350 4           my_hv_store(info, "gapless", newSViv(si->is_true_gapless));
351 4 100         my_hv_store(info, "track_gain", newSVpvf("%2.2f dB", si->gain_title == 0 ? 0 : MPC_OLD_GAIN_REF - si->gain_title / 256.0));
352 4 100         my_hv_store(info, "album_gain", newSVpvf("%2.2f dB", si->gain_album == 0 ? 0 : MPC_OLD_GAIN_REF - si->gain_album / 256.0));
353             }
354              
355             out:
356 4           Safefree(si);
357 4           buffer_free(&buf);
358              
359 4           return ret;
360             }