| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | /* | 
| 2 |  |  |  |  |  |  | * This program is free software; you can redistribute it and/or modify | 
| 3 |  |  |  |  |  |  | * it under the terms of the GNU General Public License as published by | 
| 4 |  |  |  |  |  |  | * the Free Software Foundation; either version 2 of the License, or | 
| 5 |  |  |  |  |  |  | * (at your option) any later version. | 
| 6 |  |  |  |  |  |  | * | 
| 7 |  |  |  |  |  |  | * This program is distributed in the hope that it will be useful, | 
| 8 |  |  |  |  |  |  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 9 |  |  |  |  |  |  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 10 |  |  |  |  |  |  | * GNU General Public License for more details. | 
| 11 |  |  |  |  |  |  | * | 
| 12 |  |  |  |  |  |  | * You should have received a copy of the GNU General Public License | 
| 13 |  |  |  |  |  |  | * along with this program; if not, write to the Free Software | 
| 14 |  |  |  |  |  |  | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
| 15 |  |  |  |  |  |  | */ | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | #include "wavpack.h" | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | static int | 
| 20 | 10 |  |  |  |  |  | get_wavpack_info(PerlIO *infile, char *file, HV *info) | 
| 21 |  |  |  |  |  |  | { | 
| 22 | 10 |  |  |  |  |  | wvpinfo *wvp = _wavpack_parse(infile, file, info, 0); | 
| 23 |  |  |  |  |  |  |  | 
| 24 | 10 |  |  |  |  |  | Safefree(wvp); | 
| 25 |  |  |  |  |  |  |  | 
| 26 | 10 |  |  |  |  |  | return 0; | 
| 27 |  |  |  |  |  |  | } | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | wvpinfo * | 
| 30 | 10 |  |  |  |  |  | _wavpack_parse(PerlIO *infile, char *file, HV *info, uint8_t seeking) | 
| 31 |  |  |  |  |  |  | { | 
| 32 | 10 |  |  |  |  |  | int err = 0; | 
| 33 | 10 |  |  |  |  |  | int done = 0; | 
| 34 |  |  |  |  |  |  | u_char *bptr; | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | wvpinfo *wvp; | 
| 37 | 10 |  |  |  |  |  | Newz(0, wvp, sizeof(wvpinfo), wvpinfo); | 
| 38 | 10 |  |  |  |  |  | Newz(0, wvp->buf, sizeof(Buffer), Buffer); | 
| 39 | 10 |  |  |  |  |  | Newz(0, wvp->header, sizeof(WavpackHeader), WavpackHeader); | 
| 40 |  |  |  |  |  |  |  | 
| 41 | 10 |  |  |  |  |  | wvp->infile         = infile; | 
| 42 | 10 |  |  |  |  |  | wvp->file           = file; | 
| 43 | 10 |  |  |  |  |  | wvp->info           = info; | 
| 44 | 10 |  |  |  |  |  | wvp->file_offset    = 0; | 
| 45 | 10 |  |  |  |  |  | wvp->audio_offset   = 0; | 
| 46 | 10 |  |  |  |  |  | wvp->seeking        = seeking ? 1 : 0; | 
| 47 |  |  |  |  |  |  |  | 
| 48 | 10 |  |  |  |  |  | buffer_init(wvp->buf, WAVPACK_BLOCK_SIZE); | 
| 49 |  |  |  |  |  |  |  | 
| 50 | 10 |  |  |  |  |  | wvp->file_size = _file_size(infile); | 
| 51 | 10 |  |  |  |  |  | my_hv_store( info, "file_size", newSVuv(wvp->file_size) ); | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | // Loop through each wvpk block until we find a good one | 
| 54 | 19 | 100 |  |  |  |  | while (!done) { | 
| 55 | 11 | 50 |  |  |  |  | if ( !_check_buf(infile, wvp->buf, 32, WAVPACK_BLOCK_SIZE) ) { | 
| 56 | 0 |  |  |  |  |  | err = -1; | 
| 57 | 0 |  |  |  |  |  | goto out; | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 | 11 |  |  |  |  |  | bptr = buffer_ptr(wvp->buf); | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | // If first byte is 'R', assume old version | 
| 63 | 11 | 100 |  |  |  |  | if ( bptr[0] == 'R' ) { | 
| 64 | 2 | 50 |  |  |  |  | if ( !_wavpack_parse_old(wvp) ) { | 
| 65 | 0 |  |  |  |  |  | err = -1; | 
| 66 | 0 |  |  |  |  |  | goto out; | 
| 67 |  |  |  |  |  |  | } | 
| 68 |  |  |  |  |  |  |  | 
| 69 | 2 |  |  |  |  |  | break; | 
| 70 |  |  |  |  |  |  | } | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | // May need to read past some junk before wvpk header | 
| 73 | 30729 | 100 |  |  |  |  | while ( bptr[0] != 'w' || bptr[1] != 'v' || bptr[2] != 'p' || bptr[3] != 'k' ) { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 74 | 30720 |  |  |  |  |  | buffer_consume(wvp->buf, 1); | 
| 75 |  |  |  |  |  |  |  | 
| 76 | 30720 |  |  |  |  |  | wvp->audio_offset++; | 
| 77 |  |  |  |  |  |  |  | 
| 78 | 30720 | 100 |  |  |  |  | if ( !buffer_len(wvp->buf) ) { | 
| 79 | 7 | 50 |  |  |  |  | if ( !_check_buf(infile, wvp->buf, 32, WAVPACK_BLOCK_SIZE) ) { | 
| 80 | 0 |  |  |  |  |  | PerlIO_printf(PerlIO_stderr(), "Unable to find a valid WavPack block in file: %s\n", file); | 
| 81 | 0 |  |  |  |  |  | err = -1; | 
| 82 | 0 |  |  |  |  |  | goto out; | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  | } | 
| 85 |  |  |  |  |  |  |  | 
| 86 | 30720 |  |  |  |  |  | bptr = buffer_ptr(wvp->buf); | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 9 | 100 |  |  |  |  | if ( _wavpack_parse_block(wvp) ) { | 
| 90 | 8 |  |  |  |  |  | done = 1; | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  | } | 
| 93 |  |  |  |  |  |  |  | 
| 94 | 10 |  |  |  |  |  | my_hv_store( info, "audio_offset", newSVuv(wvp->audio_offset) ); | 
| 95 | 10 |  |  |  |  |  | my_hv_store( info, "audio_size", newSVuv(wvp->file_size - wvp->audio_offset) ); | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | out: | 
| 98 | 10 |  |  |  |  |  | buffer_free(wvp->buf); | 
| 99 | 10 |  |  |  |  |  | Safefree(wvp->buf); | 
| 100 | 10 |  |  |  |  |  | Safefree(wvp->header); | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 10 |  |  |  |  |  | return wvp; | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | int | 
| 106 | 9 |  |  |  |  |  | _wavpack_parse_block(wvpinfo *wvp) | 
| 107 |  |  |  |  |  |  | { | 
| 108 |  |  |  |  |  |  | unsigned char *bptr; | 
| 109 |  |  |  |  |  |  | uint16_t remaining; | 
| 110 |  |  |  |  |  |  |  | 
| 111 | 9 |  |  |  |  |  | bptr = buffer_ptr(wvp->buf); | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | // Verify wvpk signature | 
| 114 | 9 | 50 |  |  |  |  | if ( bptr[0] != 'w' || bptr[1] != 'v' || bptr[2] != 'p' || bptr[3] != 'k' ) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | DEBUG_TRACE("Invalid wvpk header at %llu\n", wvp->file_offset); | 
| 116 | 0 |  |  |  |  |  | return 1; | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  |  | 
| 119 | 9 |  |  |  |  |  | buffer_consume(wvp->buf, 4); | 
| 120 |  |  |  |  |  |  |  | 
| 121 | 9 |  |  |  |  |  | wvp->header->ckSize        = buffer_get_int_le(wvp->buf); | 
| 122 | 9 |  |  |  |  |  | wvp->header->version       = buffer_get_short_le(wvp->buf); | 
| 123 | 9 |  |  |  |  |  | wvp->header->track_no      = buffer_get_char(wvp->buf); | 
| 124 | 9 |  |  |  |  |  | wvp->header->index_no      = buffer_get_char(wvp->buf); | 
| 125 | 9 |  |  |  |  |  | wvp->header->total_samples = buffer_get_int_le(wvp->buf); | 
| 126 | 9 |  |  |  |  |  | wvp->header->block_index   = buffer_get_int_le(wvp->buf); | 
| 127 | 9 |  |  |  |  |  | wvp->header->block_samples = buffer_get_int_le(wvp->buf); | 
| 128 | 9 |  |  |  |  |  | wvp->header->flags         = buffer_get_int_le(wvp->buf); | 
| 129 | 9 |  |  |  |  |  | wvp->header->crc           = buffer_get_int_le(wvp->buf); | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | DEBUG_TRACE("wvpk header @ %llu:\n", wvp->file_offset); | 
| 132 |  |  |  |  |  |  | DEBUG_TRACE("  size: %u\n", wvp->header->ckSize); | 
| 133 |  |  |  |  |  |  | DEBUG_TRACE("  version: 0x%x\n", wvp->header->version); | 
| 134 |  |  |  |  |  |  | DEBUG_TRACE("  track_no: 0x%x\n", wvp->header->track_no); | 
| 135 |  |  |  |  |  |  | DEBUG_TRACE("  index_no: 0x%x\n", wvp->header->index_no); | 
| 136 |  |  |  |  |  |  | DEBUG_TRACE("  total_samples: %u\n", wvp->header->total_samples); | 
| 137 |  |  |  |  |  |  | DEBUG_TRACE("  block_index: %u\n", wvp->header->block_index); | 
| 138 |  |  |  |  |  |  | DEBUG_TRACE("  block_samples: %u\n", wvp->header->block_samples); | 
| 139 |  |  |  |  |  |  | DEBUG_TRACE("  flags: 0x%x\n", wvp->header->flags); | 
| 140 |  |  |  |  |  |  | DEBUG_TRACE("  crc: 0x%x\n", wvp->header->crc); | 
| 141 |  |  |  |  |  |  |  | 
| 142 | 9 |  |  |  |  |  | wvp->file_offset += 32; | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 9 |  |  |  |  |  | my_hv_store( wvp->info, "encoder_version", newSVuv(wvp->header->version) ); | 
| 145 |  |  |  |  |  |  |  | 
| 146 | 9 | 50 |  |  |  |  | if (wvp->header->version < 0x4) { | 
| 147 |  |  |  |  |  |  | // XXX old version and not handled by 'R' check above for old version | 
| 148 | 0 |  |  |  |  |  | PerlIO_printf(PerlIO_stderr(), "Unsupported old WavPack version: 0x%x\n", wvp->header->version); | 
| 149 | 0 |  |  |  |  |  | return 1; | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | // Read data from flags | 
| 153 | 9 |  |  |  |  |  | my_hv_store( wvp->info, "bits_per_sample", newSVuv( 8 * ((wvp->header->flags & 0x3) + 1) ) ); | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | // Encoding mode | 
| 156 | 9 | 100 |  |  |  |  | my_hv_store( wvp->info, (wvp->header->flags & 0x8) ? "hybrid" : "lossless", newSVuv(1) ); | 
|  |  | 100 |  |  |  |  |  | 
| 157 |  |  |  |  |  |  |  | 
| 158 |  |  |  |  |  |  | { | 
| 159 |  |  |  |  |  |  | // samplerate, may be overridden by a later ID_SAMPLE_RATE metadata block | 
| 160 | 9 |  |  |  |  |  | uint32_t samplerate_index = (wvp->header->flags & 0x7800000) >> 23; | 
| 161 | 9 | 100 |  |  |  |  | if ( samplerate_index < 0xF ) { | 
| 162 | 8 |  |  |  |  |  | my_hv_store( wvp->info, "samplerate", newSVuv( wavpack_sample_rates[samplerate_index] ) ); | 
| 163 |  |  |  |  |  |  | } | 
| 164 |  |  |  |  |  |  | else { | 
| 165 |  |  |  |  |  |  | // Default to 44.1 just in case | 
| 166 | 1 |  |  |  |  |  | my_hv_store( wvp->info, "samplerate", newSVuv(44100) ); | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  | } | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | // Channels, may be overridden by a later ID_CHANNEL_INFO metadata block | 
| 171 | 9 | 50 |  |  |  |  | my_hv_store( wvp->info, "channels", newSVuv( (wvp->header->flags & 0x4) ? 1 : 2 ) ); | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | // Parse metadata sub-blocks | 
| 174 | 9 |  |  |  |  |  | remaining = wvp->header->ckSize - 24; // ckSize is 8 less than the block size | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | // If block_samples is 0, we need to skip to the next block | 
| 177 | 9 | 100 |  |  |  |  | if ( !wvp->header->block_samples ) { | 
| 178 | 1 |  |  |  |  |  | wvp->file_offset += remaining; | 
| 179 | 1 |  |  |  |  |  | _wavpack_skip(wvp, remaining); | 
| 180 | 1 |  |  |  |  |  | return 0; | 
| 181 |  |  |  |  |  |  | } | 
| 182 |  |  |  |  |  |  |  | 
| 183 | 54 | 100 |  |  |  |  | while (remaining > 0) { | 
| 184 |  |  |  |  |  |  | // Read sub-block header (2-4 bytes) | 
| 185 |  |  |  |  |  |  | unsigned char id; | 
| 186 |  |  |  |  |  |  | uint32_t size; | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | DEBUG_TRACE("remaining: %d\n", remaining); | 
| 189 |  |  |  |  |  |  |  | 
| 190 | 53 | 50 |  |  |  |  | if ( !_check_buf(wvp->infile, wvp->buf, 4, WAVPACK_BLOCK_SIZE) ) { | 
| 191 | 0 |  |  |  |  |  | return 0; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 | 53 |  |  |  |  |  | id = buffer_get_char(wvp->buf); | 
| 195 | 53 |  |  |  |  |  | remaining--; | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | // Size is in words | 
| 198 | 53 | 100 |  |  |  |  | if (id & ID_LARGE) { | 
| 199 |  |  |  |  |  |  | // 24-bit large size | 
| 200 | 7 |  |  |  |  |  | id &= ~ID_LARGE; | 
| 201 | 7 |  |  |  |  |  | size = buffer_get_int24_le(wvp->buf) << 1; | 
| 202 | 7 |  |  |  |  |  | remaining -= 3; | 
| 203 |  |  |  |  |  |  | DEBUG_TRACE("  ID_LARGE, changed to %x\n", id); | 
| 204 |  |  |  |  |  |  | } | 
| 205 |  |  |  |  |  |  | else { | 
| 206 |  |  |  |  |  |  | // 8-bit size | 
| 207 | 46 |  |  |  |  |  | size = buffer_get_char(wvp->buf) << 1; | 
| 208 | 46 |  |  |  |  |  | remaining--; | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 | 53 | 100 |  |  |  |  | if (id & ID_ODD_SIZE) { | 
| 212 | 13 |  |  |  |  |  | id &= ~ID_ODD_SIZE; | 
| 213 | 13 |  |  |  |  |  | size--; | 
| 214 |  |  |  |  |  |  | DEBUG_TRACE("  ID_ODD_SIZE, changed to %x\n", id); | 
| 215 |  |  |  |  |  |  | } | 
| 216 |  |  |  |  |  |  |  | 
| 217 | 53 | 100 |  |  |  |  | if ( id == ID_WV_BITSTREAM || !size ) { | 
|  |  | 100 |  |  |  |  |  | 
| 218 |  |  |  |  |  |  | // Found the bitstream, don't read any farther | 
| 219 |  |  |  |  |  |  | DEBUG_TRACE("  Sub-Chunk: WV_BITSTREAM (size %u)\n", size); | 
| 220 |  |  |  |  |  |  | break; | 
| 221 |  |  |  |  |  |  | } | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | // We only care about 0x27 (ID_SAMPLE_RATE) and 0xd (ID_CHANNEL_INFO) | 
| 224 | 46 |  |  |  |  |  | switch (id) { | 
| 225 |  |  |  |  |  |  | case ID_SAMPLE_RATE: | 
| 226 |  |  |  |  |  |  | DEBUG_TRACE("  Sub-Chunk: ID_SAMPLE_RATE (size: %u)\n", size); | 
| 227 | 1 |  |  |  |  |  | _wavpack_parse_sample_rate(wvp, size); | 
| 228 | 1 |  |  |  |  |  | break; | 
| 229 |  |  |  |  |  |  |  | 
| 230 |  |  |  |  |  |  | case ID_CHANNEL_INFO: | 
| 231 |  |  |  |  |  |  | DEBUG_TRACE("  Sub-Chunk: ID_CHANNEL_INFO (size: %u)\n", size); | 
| 232 | 1 |  |  |  |  |  | _wavpack_parse_channel_info(wvp, size); | 
| 233 | 1 |  |  |  |  |  | break; | 
| 234 |  |  |  |  |  |  |  | 
| 235 |  |  |  |  |  |  | case ID_DSD_BLOCK: | 
| 236 |  |  |  |  |  |  | DEBUG_TRACE("  Sub-Chunk: ID_DSD_BLOCK (size: %u)\n", size); | 
| 237 | 1 |  |  |  |  |  | _wavpack_parse_dsd_block(wvp, size); | 
| 238 | 1 |  |  |  |  |  | break; | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | default: | 
| 241 |  |  |  |  |  |  | // Skip it | 
| 242 |  |  |  |  |  |  | DEBUG_TRACE("  Sub-Chunk: %x (size: %u) (skipped)\n", id, size); | 
| 243 | 43 |  |  |  |  |  | _wavpack_skip(wvp, size); | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  |  | 
| 246 | 46 |  |  |  |  |  | remaining -= size; | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  | // If size was odd, skip a byte | 
| 249 | 46 | 100 |  |  |  |  | if (size & 0x1) { | 
| 250 | 13 | 50 |  |  |  |  | if ( buffer_len(wvp->buf) ) { | 
| 251 | 13 |  |  |  |  |  | buffer_consume(wvp->buf, 1); | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  | else { | 
| 254 | 0 |  |  |  |  |  | _wavpack_skip(wvp, 1); | 
| 255 |  |  |  |  |  |  | } | 
| 256 |  |  |  |  |  |  |  | 
| 257 | 13 |  |  |  |  |  | remaining--; | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  | } | 
| 260 |  |  |  |  |  |  |  | 
| 261 |  |  |  |  |  |  | // Calculate bitrate | 
| 262 | 8 | 50 |  |  |  |  | if ( wvp->header->total_samples && wvp->file_size > 0 ) { | 
|  |  | 50 |  |  |  |  |  | 
| 263 | 8 |  |  |  |  |  | SV **samplerate = my_hv_fetch( wvp->info, "samplerate" ); | 
| 264 | 8 | 50 |  |  |  |  | if (samplerate != NULL) { | 
| 265 |  |  |  |  |  |  | uint32_t song_length_ms; | 
| 266 |  |  |  |  |  |  |  | 
| 267 | 8 | 100 |  |  |  |  | if (wvp->header->flags & 0x80000000) | 
| 268 | 1 |  |  |  |  |  | wvp->header->total_samples *= 8; // DSD | 
| 269 |  |  |  |  |  |  |  | 
| 270 | 8 | 50 |  |  |  |  | song_length_ms = ((wvp->header->total_samples * 1.0) / SvIV(*samplerate)) * 1000; | 
| 271 |  |  |  |  |  |  |  | 
| 272 | 8 |  |  |  |  |  | my_hv_store( wvp->info, "song_length_ms", newSVuv(song_length_ms) ); | 
| 273 | 8 |  |  |  |  |  | my_hv_store( wvp->info, "bitrate", newSVuv( _bitrate(wvp->file_size - wvp->audio_offset, song_length_ms) ) ); | 
| 274 | 8 |  |  |  |  |  | my_hv_store( wvp->info, "total_samples", newSVuv(wvp->header->total_samples) ); | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  |  | 
| 278 | 8 |  |  |  |  |  | return 1; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  | int | 
| 282 | 1 |  |  |  |  |  | _wavpack_parse_sample_rate(wvpinfo *wvp, uint32_t size) | 
| 283 |  |  |  |  |  |  | { | 
| 284 | 1 |  |  |  |  |  | uint32_t samplerate = buffer_get_int24_le(wvp->buf); | 
| 285 |  |  |  |  |  |  |  | 
| 286 | 1 |  |  |  |  |  | my_hv_store( wvp->info, "samplerate", newSVuv(samplerate) ); | 
| 287 |  |  |  |  |  |  |  | 
| 288 | 1 |  |  |  |  |  | return 1; | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  |  | 
| 291 |  |  |  |  |  |  | int | 
| 292 | 1 |  |  |  |  |  | _wavpack_parse_channel_info(wvpinfo *wvp, uint32_t size) | 
| 293 |  |  |  |  |  |  | { | 
| 294 |  |  |  |  |  |  | uint32_t channels; | 
| 295 | 1 |  |  |  |  |  | unsigned char *bptr = buffer_ptr(wvp->buf); | 
| 296 |  |  |  |  |  |  |  | 
| 297 | 1 | 50 |  |  |  |  | if (size == 6) { | 
| 298 | 0 |  |  |  |  |  | channels = (bptr[0] | ((bptr[2] & 0xf) << 8)) + 1; | 
| 299 |  |  |  |  |  |  | } | 
| 300 |  |  |  |  |  |  | else { | 
| 301 | 1 |  |  |  |  |  | channels = bptr[0]; | 
| 302 |  |  |  |  |  |  | } | 
| 303 |  |  |  |  |  |  |  | 
| 304 | 1 |  |  |  |  |  | my_hv_store( wvp->info, "channels", newSVuv(channels) ); | 
| 305 |  |  |  |  |  |  |  | 
| 306 | 1 |  |  |  |  |  | buffer_consume(wvp->buf, size); | 
| 307 |  |  |  |  |  |  |  | 
| 308 | 1 |  |  |  |  |  | return 1; | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | int | 
| 312 | 1 |  |  |  |  |  | _wavpack_parse_dsd_block(wvpinfo *wvp, uint32_t size) | 
| 313 |  |  |  |  |  |  | { | 
| 314 | 1 | 50 |  |  |  |  | if (wvp->header->flags & 0x80000000) { | 
| 315 | 1 |  |  |  |  |  | unsigned char *bptr = buffer_ptr(wvp->buf); | 
| 316 | 1 |  |  |  |  |  | uint32_t samplerate_index = (wvp->header->flags & 0x7800000) >> 23; | 
| 317 |  |  |  |  |  |  | uint32_t samplerate; | 
| 318 | 1 | 50 |  |  |  |  | if (samplerate_index < 0xF) | 
| 319 | 1 |  |  |  |  |  | samplerate = wavpack_sample_rates[samplerate_index] * (1 << bptr[0]) * 8; | 
| 320 |  |  |  |  |  |  | else | 
| 321 | 0 |  |  |  |  |  | samplerate = 64 * 44100; // Default to DSD64 just in case | 
| 322 |  |  |  |  |  |  |  | 
| 323 | 1 |  |  |  |  |  | my_hv_store( wvp->info, "samplerate", newSVuv(samplerate) ); | 
| 324 | 1 |  |  |  |  |  | my_hv_store( wvp->info, "bits_per_sample", newSVuv(1) ); | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 | 1 |  |  |  |  |  | _wavpack_skip(wvp, size); | 
| 328 |  |  |  |  |  |  |  | 
| 329 | 1 |  |  |  |  |  | return 1; | 
| 330 |  |  |  |  |  |  | } | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | void | 
| 333 | 45 |  |  |  |  |  | _wavpack_skip(wvpinfo *wvp, uint32_t size) | 
| 334 |  |  |  |  |  |  | { | 
| 335 | 45 | 50 |  |  |  |  | if ( buffer_len(wvp->buf) >= size ) { | 
| 336 |  |  |  |  |  |  | //buffer_dump(mp4->buf, size); | 
| 337 | 45 |  |  |  |  |  | buffer_consume(wvp->buf, size); | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | DEBUG_TRACE("  skipped buffer data size %d\n", size); | 
| 340 |  |  |  |  |  |  | } | 
| 341 |  |  |  |  |  |  | else { | 
| 342 | 0 |  |  |  |  |  | PerlIO_seek(wvp->infile, size - buffer_len(wvp->buf), SEEK_CUR); | 
| 343 | 0 |  |  |  |  |  | buffer_clear(wvp->buf); | 
| 344 |  |  |  |  |  |  |  | 
| 345 |  |  |  |  |  |  | DEBUG_TRACE("  seeked past %d bytes to %d\n", size, (int)PerlIO_tell(wvp->infile)); | 
| 346 |  |  |  |  |  |  | } | 
| 347 | 45 |  |  |  |  |  | } | 
| 348 |  |  |  |  |  |  |  | 
| 349 |  |  |  |  |  |  | int | 
| 350 | 2 |  |  |  |  |  | _wavpack_parse_old(wvpinfo *wvp) | 
| 351 |  |  |  |  |  |  | { | 
| 352 | 2 |  |  |  |  |  | int ret = 1; | 
| 353 |  |  |  |  |  |  | char chunk_id[5]; | 
| 354 |  |  |  |  |  |  | uint32_t chunk_size; | 
| 355 |  |  |  |  |  |  | WavpackHeader3 wphdr; | 
| 356 |  |  |  |  |  |  | WaveHeader3 wavhdr; | 
| 357 |  |  |  |  |  |  | unsigned char *bptr; | 
| 358 |  |  |  |  |  |  | uint32_t total_samples; | 
| 359 |  |  |  |  |  |  | uint32_t song_length_ms; | 
| 360 |  |  |  |  |  |  |  | 
| 361 | 2 |  |  |  |  |  | Zero(&wavhdr, sizeof(wavhdr), char); | 
| 362 | 2 |  |  |  |  |  | Zero(&wphdr, sizeof(wphdr), char); | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | DEBUG_TRACE("Parsing old WavPack version\n"); | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | // Verify RIFF header | 
| 367 | 2 | 50 |  |  |  |  | if ( strncmp( (char *)buffer_ptr(wvp->buf), "RIFF", 4 ) ) { | 
| 368 | 0 |  |  |  |  |  | PerlIO_printf(PerlIO_stderr(), "Invalid WavPack file: missing RIFF header: %s\n", wvp->file); | 
| 369 | 0 |  |  |  |  |  | ret = 0; | 
| 370 | 0 |  |  |  |  |  | goto out; | 
| 371 |  |  |  |  |  |  | } | 
| 372 |  |  |  |  |  |  |  | 
| 373 | 2 |  |  |  |  |  | buffer_consume(wvp->buf, 4); | 
| 374 |  |  |  |  |  |  |  | 
| 375 | 2 |  |  |  |  |  | chunk_size = buffer_get_int_le(wvp->buf); | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | // Check format | 
| 378 | 2 | 50 |  |  |  |  | if ( strncmp( (char *)buffer_ptr(wvp->buf), "WAVE", 4 ) ) { | 
| 379 | 0 |  |  |  |  |  | PerlIO_printf(PerlIO_stderr(), "Invalid WavPack file: missing WAVE header: %s\n", wvp->file); | 
| 380 | 0 |  |  |  |  |  | ret = 0; | 
| 381 | 0 |  |  |  |  |  | goto out; | 
| 382 |  |  |  |  |  |  | } | 
| 383 |  |  |  |  |  |  |  | 
| 384 | 2 |  |  |  |  |  | buffer_consume(wvp->buf, 4); | 
| 385 |  |  |  |  |  |  |  | 
| 386 | 2 |  |  |  |  |  | wvp->file_offset += 12; | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | // Verify we have at least 8 bytes | 
| 389 | 2 | 50 |  |  |  |  | if ( !_check_buf(wvp->infile, wvp->buf, 8, WAVPACK_BLOCK_SIZE) ) { | 
| 390 | 0 |  |  |  |  |  | ret = 0; | 
| 391 | 0 |  |  |  |  |  | goto out; | 
| 392 |  |  |  |  |  |  | } | 
| 393 |  |  |  |  |  |  |  | 
| 394 |  |  |  |  |  |  | // loop through all chunks, read fmt, and break at data | 
| 395 | 4 | 50 |  |  |  |  | while ( buffer_len(wvp->buf) >= 8 ) { | 
| 396 | 4 |  |  |  |  |  | strncpy( chunk_id, (char *)buffer_ptr(wvp->buf), 4 ); | 
| 397 | 4 |  |  |  |  |  | chunk_id[4] = '\0'; | 
| 398 | 4 |  |  |  |  |  | buffer_consume(wvp->buf, 4); | 
| 399 |  |  |  |  |  |  |  | 
| 400 | 4 |  |  |  |  |  | chunk_size = buffer_get_int_le(wvp->buf); | 
| 401 |  |  |  |  |  |  |  | 
| 402 | 4 |  |  |  |  |  | wvp->file_offset += 8; | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | // Adjust for padding | 
| 405 | 4 | 50 |  |  |  |  | if ( chunk_size % 2 ) { | 
| 406 | 0 |  |  |  |  |  | chunk_size++; | 
| 407 |  |  |  |  |  |  | } | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | DEBUG_TRACE("  %s size %d\n", chunk_id, chunk_size); | 
| 410 |  |  |  |  |  |  |  | 
| 411 | 4 | 100 |  |  |  |  | if ( !strcmp( chunk_id, "data" ) ) { | 
| 412 | 2 |  |  |  |  |  | break; | 
| 413 |  |  |  |  |  |  | } | 
| 414 |  |  |  |  |  |  |  | 
| 415 | 2 |  |  |  |  |  | wvp->file_offset += chunk_size; | 
| 416 |  |  |  |  |  |  |  | 
| 417 | 2 | 50 |  |  |  |  | if ( !strcmp( chunk_id, "fmt " ) ) { | 
| 418 | 2 | 50 |  |  |  |  | if ( !_check_buf(wvp->infile, wvp->buf, chunk_size, WAV_BLOCK_SIZE) ) { | 
| 419 | 0 |  |  |  |  |  | ret = 0; | 
| 420 | 0 |  |  |  |  |  | goto out; | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  |  | 
| 423 | 2 | 50 |  |  |  |  | if (chunk_size < sizeof(wavhdr)) { | 
| 424 | 0 |  |  |  |  |  | ret = 0; | 
| 425 | 0 |  |  |  |  |  | goto out; | 
| 426 |  |  |  |  |  |  | } | 
| 427 |  |  |  |  |  |  |  | 
| 428 |  |  |  |  |  |  | // Read wav header | 
| 429 | 2 |  |  |  |  |  | wavhdr.FormatTag      = buffer_get_short_le(wvp->buf); | 
| 430 | 2 |  |  |  |  |  | wavhdr.NumChannels    = buffer_get_short_le(wvp->buf); | 
| 431 | 2 |  |  |  |  |  | wavhdr.SampleRate     = buffer_get_int_le(wvp->buf); | 
| 432 | 2 |  |  |  |  |  | wavhdr.BytesPerSecond = buffer_get_int_le(wvp->buf); | 
| 433 | 2 |  |  |  |  |  | wavhdr.BlockAlign     = buffer_get_short_le(wvp->buf); | 
| 434 | 2 |  |  |  |  |  | wavhdr.BitsPerSample  = buffer_get_short_le(wvp->buf); | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | // Skip rest of fmt chunk if necessary | 
| 437 | 2 | 50 |  |  |  |  | if (chunk_size > 16) { | 
| 438 | 2 |  |  |  |  |  | _wavpack_skip(wvp, chunk_size - 16); | 
| 439 |  |  |  |  |  |  | } | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  | else { | 
| 442 |  |  |  |  |  |  | // Skip it | 
| 443 | 0 |  |  |  |  |  | _wavpack_skip(wvp, chunk_size); | 
| 444 |  |  |  |  |  |  | } | 
| 445 |  |  |  |  |  |  |  | 
| 446 |  |  |  |  |  |  | // Verify we have at least 8 bytes | 
| 447 | 2 | 50 |  |  |  |  | if ( !_check_buf(wvp->infile, wvp->buf, 8, WAVPACK_BLOCK_SIZE) ) { | 
| 448 | 0 |  |  |  |  |  | ret = 0; | 
| 449 | 0 |  |  |  |  |  | goto out; | 
| 450 |  |  |  |  |  |  | } | 
| 451 |  |  |  |  |  |  | } | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | // Verify wav header, this code comes from unpack3.c | 
| 454 | 2 | 50 |  |  |  |  | if ( | 
| 455 | 2 | 50 |  |  |  |  | wavhdr.FormatTag != 1 || !wavhdr.NumChannels || wavhdr.NumChannels > 2 || | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 456 | 2 | 50 |  |  |  |  | !wavhdr.SampleRate || wavhdr.BitsPerSample < 16 || wavhdr.BitsPerSample > 24 || | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 457 | 2 | 50 |  |  |  |  | wavhdr.BlockAlign / wavhdr.NumChannels > 3 || wavhdr.BlockAlign % wavhdr.NumChannels || | 
|  |  | 50 |  |  |  |  |  | 
| 458 | 2 |  |  |  |  |  | wavhdr.BlockAlign / wavhdr.NumChannels < (wavhdr.BitsPerSample + 7) / 8 | 
| 459 |  |  |  |  |  |  | ) { | 
| 460 | 0 |  |  |  |  |  | ret = 0; | 
| 461 | 0 |  |  |  |  |  | goto out; | 
| 462 |  |  |  |  |  |  | } | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | // chunk_size here is the size of the data chunk | 
| 465 | 2 | 50 |  |  |  |  | total_samples = chunk_size / wavhdr.NumChannels / ((wavhdr.BitsPerSample > 16) ? 3 : 2); | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | // read WavpackHeader3 (differs for each version) | 
| 468 | 2 |  |  |  |  |  | bptr = buffer_ptr(wvp->buf); | 
| 469 | 2 | 50 |  |  |  |  | if ( bptr[0] != 'w' || bptr[1] != 'v' || bptr[2] != 'p' || bptr[3] != 'k' ) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 470 | 0 |  |  |  |  |  | PerlIO_printf(PerlIO_stderr(), "Invalid WavPack file: missing wvpk header: %s\n", wvp->file); | 
| 471 | 0 |  |  |  |  |  | ret = 0; | 
| 472 | 0 |  |  |  |  |  | goto out; | 
| 473 |  |  |  |  |  |  | } | 
| 474 |  |  |  |  |  |  |  | 
| 475 | 2 |  |  |  |  |  | buffer_consume(wvp->buf, 4); | 
| 476 |  |  |  |  |  |  |  | 
| 477 | 2 |  |  |  |  |  | wphdr.ckSize  = buffer_get_int_le(wvp->buf); | 
| 478 | 2 |  |  |  |  |  | wphdr.version = buffer_get_short_le(wvp->buf); | 
| 479 |  |  |  |  |  |  |  | 
| 480 | 2 | 50 |  |  |  |  | if (wphdr.version >= 2) { | 
| 481 | 2 |  |  |  |  |  | wphdr.bits = buffer_get_short_le(wvp->buf); | 
| 482 |  |  |  |  |  |  | } | 
| 483 |  |  |  |  |  |  |  | 
| 484 | 2 | 100 |  |  |  |  | if (wphdr.version == 3) { | 
| 485 | 1 |  |  |  |  |  | wphdr.flags         = buffer_get_short_le(wvp->buf); | 
| 486 | 1 |  |  |  |  |  | wphdr.shift         = buffer_get_short_le(wvp->buf); | 
| 487 | 1 |  |  |  |  |  | wphdr.total_samples = buffer_get_int_le(wvp->buf); | 
| 488 |  |  |  |  |  |  |  | 
| 489 | 1 |  |  |  |  |  | total_samples = wphdr.total_samples; | 
| 490 |  |  |  |  |  |  | } | 
| 491 |  |  |  |  |  |  |  | 
| 492 |  |  |  |  |  |  | DEBUG_TRACE("wvpk header @ %llu:\n", wvp->file_offset); | 
| 493 |  |  |  |  |  |  | DEBUG_TRACE("  size: %u\n", wphdr.ckSize); | 
| 494 |  |  |  |  |  |  | DEBUG_TRACE("  version: %d\n", wphdr.version); | 
| 495 |  |  |  |  |  |  | DEBUG_TRACE("  bits: 0x%x\n", wphdr.bits); | 
| 496 |  |  |  |  |  |  | DEBUG_TRACE("  flags: 0x%x\n", wphdr.flags); | 
| 497 |  |  |  |  |  |  | DEBUG_TRACE("  shift: 0x%x\n", wphdr.shift); | 
| 498 |  |  |  |  |  |  | DEBUG_TRACE("  total_samples: %d\n", wphdr.total_samples); | 
| 499 |  |  |  |  |  |  |  | 
| 500 | 2 |  |  |  |  |  | my_hv_store( wvp->info, "encoder_version", newSVuv(wphdr.version) ); | 
| 501 | 2 |  |  |  |  |  | my_hv_store( wvp->info, "bits_per_sample", newSVuv(wavhdr.BitsPerSample) ); | 
| 502 | 2 |  |  |  |  |  | my_hv_store( wvp->info, "channels", newSVuv(wavhdr.NumChannels) ); | 
| 503 | 2 |  |  |  |  |  | my_hv_store( wvp->info, "samplerate", newSVuv(wavhdr.SampleRate) ); | 
| 504 | 2 |  |  |  |  |  | my_hv_store( wvp->info, "total_samples", newSVuv(total_samples) ); | 
| 505 |  |  |  |  |  |  |  | 
| 506 | 2 |  |  |  |  |  | song_length_ms = ((total_samples * 1.0) / wavhdr.SampleRate) * 1000; | 
| 507 | 2 |  |  |  |  |  | my_hv_store( wvp->info, "song_length_ms", newSVuv(song_length_ms) ); | 
| 508 | 2 |  |  |  |  |  | my_hv_store( wvp->info, "bitrate", newSVuv( _bitrate(wvp->file_size - wvp->audio_offset, song_length_ms) ) ); | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | out: | 
| 511 | 2 |  |  |  |  |  | return ret; | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  |  |