File Coverage

src/mac.c
Criterion Covered Total %
statement 54 86 62.7
branch 10 26 38.4
condition n/a
subroutine n/a
pod n/a
total 64 112 57.1


line stmt bran cond sub pod time code
1             #include "mac.h"
2              
3             static int
4 2           get_macfileinfo(PerlIO *infile, char *file, HV *info)
5             {
6             Buffer header;
7             char *bptr;
8 2           int32_t ret = 0;
9             int32_t header_end;
10              
11             mac_streaminfo *si;
12 2           Newz(0, si, sizeof(mac_streaminfo), mac_streaminfo);
13              
14             /*
15             There are two possible variations here.
16             1. There's an ID3V2 tag present at the beginning of the file
17             2. There's an APE tag present at the beginning of the file
18             (deprecated, but still possible)
19             For each type of tag, check for existence and then skip it before
20             looking for the MPC header
21             */
22 2 50         if ((header_end = skip_id3v2(infile)) < 0) {
23 0           PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't skip ID3v2]: %s\n", file);
24 0           Safefree(si);
25 0           return -1;
26             }
27              
28             // seek to first byte of MAC data
29 2 50         if (PerlIO_seek(infile, header_end, SEEK_SET) < 0) {
30 0           PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't seek to offset %d]: %s\n", header_end, file);
31 0           Safefree(si);
32 0           return -1;
33             }
34              
35             // Offset + MAC. Does this need the space as well, to be +4 ?
36 2           si->audio_start_offset = PerlIO_tell(infile) + 3;
37              
38             // Skip the APETAGEX if it exists.
39 2           buffer_init(&header, APE_HEADER_LEN);
40              
41 2 50         if (!_check_buf(infile, &header, APE_HEADER_LEN, APE_HEADER_LEN)) {
42 0           PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't read tag header]: %s\n", file);
43 0           goto out;
44             }
45              
46 2           bptr = buffer_ptr(&header);
47              
48 2 50         if (memcmp(bptr, "APETAGEX", 8) == 0) {
49             // Skip the ape tag structure
50             // XXXX - need to test this code path.
51 0           buffer_get_int_le(&header);
52 0           PerlIO_seek(infile, buffer_get_int_le(&header), SEEK_CUR);
53              
54             } else {
55             // set the pointer back to original location
56 2           PerlIO_seek(infile, -APE_HEADER_LEN, SEEK_CUR);
57             }
58              
59 2           buffer_clear(&header);
60              
61 2 50         if (!_check_buf(infile, &header, 32, 32)) {
62 0           PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't read stream header]: %s\n", file);
63 0           goto out;
64             }
65              
66 2           bptr = buffer_ptr(&header);
67              
68 2 50         if (memcmp(bptr, "MAC ", 4) != 0) {
69 0           PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't couldn't find stream header]: %s\n", file);
70 0           goto out;
71             }
72              
73 2           buffer_consume(&header, 4);
74 2           si->version = buffer_get_short_le(&header);
75              
76 2 50         if (si->version < 3980) {
77 0           uint16_t compression_id = buffer_get_short_le(&header);
78 0 0         if (compression_id % 1000) {
79 0           si->compression = "";
80             }
81             else {
82 0           si->compression = mac_profile_names[ compression_id / 1000 ];
83             }
84              
85 0 0         if (!_check_buf(infile, &header, MAC_397_HEADER_LEN, MAC_397_HEADER_LEN)) {
86 0           PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't read < 3.98 stream header]: %s\n", file);
87 0           goto out;
88             }
89              
90 0           buffer_consume(&header, 2); // flags
91              
92 0           si->channels = buffer_get_short_le(&header);
93              
94 0           si->sample_rate = buffer_get_int_le(&header);
95              
96 0           buffer_consume(&header, 4); // header size
97 0           buffer_consume(&header, 4); // terminating data bytes
98              
99 0           si->total_frames = buffer_get_int_le(&header);
100 0           si->final_frame = buffer_get_int_le(&header);
101 0 0         si->blocks_per_frame = si->version >= 3950 ? (73728 * 4) : 73728;
102              
103             } else {
104             unsigned char md5[16];
105             uint16_t profile;
106              
107 2 50         if (!_check_buf(infile, &header, MAC_398_HEADER_LEN, MAC_398_HEADER_LEN)) {
108 0           PerlIO_printf(PerlIO_stderr(), "MAC: [Couldn't read > 3.98 stream header]: %s\n", file);
109 0           goto out;
110             }
111              
112 2           buffer_consume(&header, 2);
113              
114             // unused.
115 2           buffer_get_int_le(&header); // desc bytes
116 2           buffer_get_int_le(&header); // header bytes
117 2           buffer_get_int_le(&header); // seek table bytes
118 2           buffer_get_int_le(&header); // header data bytes
119 2           buffer_get_int_le(&header); // ape frame data bytes
120 2           buffer_get_int_le(&header); // ape frame data bytes high
121 2           buffer_get_int_le(&header); // terminating data bytes
122 2           buffer_get(&header, &md5, sizeof(md5));
123              
124             // Header block
125 2           profile = buffer_get_short_le(&header);
126 2 50         if (profile % 1000) {
127 0           si->compression = "";
128             }
129             else {
130 2           si->compression = mac_profile_names[ profile / 1000 ];
131             }
132              
133 2           buffer_get_short_le(&header); // flags
134              
135 2           si->blocks_per_frame = buffer_get_int_le(&header);
136 2           si->final_frame = buffer_get_int_le(&header);
137 2           si->total_frames = buffer_get_int_le(&header);
138 2           si->bits = buffer_get_short_le(&header);
139 2           si->channels = buffer_get_short_le(&header);
140 2           si->sample_rate = buffer_get_int_le(&header);
141             }
142              
143 2           si->file_size = _file_size(infile);
144              
145 2 50         if (si->sample_rate) {
146 2           double total_samples = (double)(((si->blocks_per_frame * (si->total_frames - 1)) + si->final_frame));
147 2           uint32_t total_ms = (total_samples * 1000) / si->sample_rate;
148              
149 2           my_hv_store(info, "samplerate", newSViv(si->sample_rate));
150 2           my_hv_store(info, "channels", newSViv(si->channels));
151 2           my_hv_store(info, "song_length_ms", newSVuv(total_ms));
152 2           my_hv_store(info, "bitrate", newSVuv( _bitrate(si->file_size - si->audio_start_offset, total_ms) ));
153              
154 2           my_hv_store(info, "file_size", newSVnv(si->file_size));
155 2           my_hv_store(info, "audio_offset", newSVuv(si->audio_start_offset));
156 2           my_hv_store(info, "audio_size", newSVuv(si->file_size - si->audio_start_offset));
157 2           my_hv_store(info, "compression", newSVpv(si->compression, 0));
158 2           my_hv_store(info, "version", newSVpvf( "%0.2f", si->version * 1.0 / 1000 ) );
159             }
160              
161             out:
162 2           buffer_free(&header);
163 2           Safefree(si);
164              
165 2           return ret;
166             }