├── DecoderDemo.java ├── README ├── com └── beatofthedrum │ └── alacdecoder │ ├── AlacContext.java │ ├── AlacDecodeUtils.java │ ├── AlacFile.java │ ├── AlacInputStream.java │ ├── AlacUtils.java │ ├── ChunkInfo.java │ ├── DecodeResult.java │ ├── Defines.java │ ├── DemuxResT.java │ ├── DemuxUtils.java │ ├── LeadingZeros.java │ ├── MyStream.java │ ├── QTMovieT.java │ ├── SampleDuration.java │ ├── SampleInfo.java │ ├── StreamUtils.java │ └── WavWriter.java └── license.txt /DecoderDemo.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DecoderDemo.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | import com.beatofthedrum.alacdecoder.*; 12 | 13 | class DecoderDemo 14 | { 15 | 16 | static java.io.FileOutputStream output_stream; 17 | static int output_opened; 18 | 19 | static int write_wav_format = 1; 20 | 21 | static String input_file_n = ""; 22 | static String output_file_n = ""; 23 | 24 | 25 | // Reformat samples from longs in processor's native endian mode to 26 | // little-endian data with (possibly) less than 3 bytes / sample. 27 | 28 | public static byte[] format_samples(int bps, int[] src, int samcnt) 29 | { 30 | int temp = 0; 31 | int counter = 0; 32 | int counter2 = 0; 33 | byte[] dst = new byte[65536]; 34 | 35 | switch (bps) 36 | { 37 | case 1: 38 | while (samcnt > 0) 39 | { 40 | dst[counter] = (byte)(0x00FF & (src[counter] + 128)); 41 | counter++; 42 | samcnt--; 43 | } 44 | break; 45 | 46 | case 2: 47 | while (samcnt > 0) 48 | { 49 | temp = src[counter2]; 50 | dst[counter] = (byte)temp; 51 | counter++; 52 | dst[counter] = (byte)(temp >>> 8); 53 | counter++; 54 | counter2++; 55 | samcnt = samcnt - 2; 56 | } 57 | break; 58 | 59 | case 3: 60 | while (samcnt > 0) 61 | { 62 | dst[counter] = (byte)src[counter2]; 63 | counter++; 64 | counter2++; 65 | samcnt--; 66 | } 67 | break; 68 | } 69 | 70 | return dst; 71 | } 72 | 73 | 74 | static void setup_environment(int argc, String[] argv) 75 | { 76 | int i = argc; 77 | 78 | int escaped = 0; 79 | 80 | if (argc < 2) 81 | usage(); 82 | 83 | int arg_idx = 0; 84 | // loop through command-line arguments 85 | while (arg_idx < argc) 86 | { 87 | if (argv[arg_idx].startsWith("-")) 88 | { 89 | if (argv[arg_idx].startsWith("-r") || argv[arg_idx].startsWith("-R")) 90 | { 91 | // raw PCM output 92 | write_wav_format = 0; 93 | } 94 | } 95 | else if (input_file_n.length() == 0) 96 | { 97 | input_file_n = argv[arg_idx]; 98 | } 99 | else if (output_file_n.length() == 0) 100 | { 101 | output_file_n = argv[arg_idx]; 102 | } 103 | else 104 | { 105 | System.out.println("extra unknown argument: " + argv[arg_idx]); 106 | usage(); 107 | } 108 | arg_idx++; 109 | } 110 | 111 | if (input_file_n.length() == 0 || output_file_n.length() == 0 ) 112 | usage(); 113 | 114 | } 115 | 116 | static void GetBuffer(AlacContext ac) 117 | { 118 | int destBufferSize = 1024 *24 * 3; // 24kb buffer = 4096 frames = 1 alac sample (we support max 24bps) 119 | byte[] pcmBuffer = new byte[65536]; 120 | int total_unpacked_bytes = 0; 121 | int bytes_unpacked; 122 | 123 | int[] pDestBuffer = new int[destBufferSize]; 124 | 125 | int bps = AlacUtils.AlacGetBytesPerSample(ac); 126 | 127 | 128 | while (true) 129 | { 130 | bytes_unpacked = AlacUtils.AlacUnpackSamples(ac, pDestBuffer); 131 | 132 | total_unpacked_bytes += bytes_unpacked; 133 | 134 | if (bytes_unpacked > 0) 135 | { 136 | pcmBuffer = format_samples(bps, pDestBuffer, bytes_unpacked); 137 | try 138 | { 139 | output_stream.write(pcmBuffer, 0, bytes_unpacked); 140 | } 141 | catch(java.io.IOException ioe) 142 | { 143 | System.err.println("Error writing data to output file. Error: " + ioe); 144 | } 145 | } 146 | 147 | if (bytes_unpacked == 0) 148 | break; 149 | } // end of while 150 | 151 | 152 | } 153 | 154 | 155 | static void usage() 156 | { 157 | System.out.println("Usage: alac [options] inputfile outputfile"); 158 | System.out.println("Decompresses the ALAC file specified"); 159 | System.out.println("Options:"); 160 | System.out.println(" -r write output as raw PCM data. Default"); 161 | System.out.println(" is in WAV format."); 162 | System.out.println(""); 163 | System.out.println("This port of the code is (c) Peter McQuillan 2011"); 164 | System.out.println("Original software is (c) 2005 David Hammerton"); 165 | System.exit(1); 166 | } 167 | 168 | public static void main(String [] args) 169 | { 170 | AlacContext ac = new AlacContext(); 171 | int output_size; 172 | int total_samples; 173 | int sample_rate; 174 | int num_channels; 175 | int byteps; 176 | int bitps; 177 | 178 | output_opened = 0; 179 | 180 | setup_environment(args.length, args); // checks all the parameters passed on command line 181 | 182 | try 183 | { 184 | output_stream = new java.io.FileOutputStream(output_file_n); 185 | output_opened = 1; 186 | } 187 | catch(java.io.IOException ioe) 188 | { 189 | System.out.println("Cannot open output file: " + output_file_n + " : Error : " + ioe); 190 | output_opened = 0; 191 | System.exit(1); 192 | } 193 | 194 | ac = AlacUtils.AlacOpenFileInput(input_file_n); 195 | 196 | if (ac.error) 197 | { 198 | System.err.println("Sorry an error has occured"); 199 | System.err.println(ac.error_message); 200 | System.exit(1); 201 | } 202 | 203 | num_channels = AlacUtils.AlacGetNumChannels(ac); 204 | 205 | System.out.println("The Apple Lossless file has " + num_channels + " channels"); 206 | 207 | total_samples = AlacUtils.AlacGetNumSamples(ac); 208 | 209 | System.out.println("The Apple Lossless file has " + total_samples + " samples"); 210 | 211 | byteps = AlacUtils.AlacGetBytesPerSample(ac); 212 | 213 | System.out.println("The Apple Lossless file has " + byteps + " bytes per sample"); 214 | 215 | sample_rate = AlacUtils.AlacGetSampleRate(ac); 216 | 217 | bitps = AlacUtils.AlacGetBitsPerSample(ac); 218 | 219 | 220 | /* write wav output headers */ 221 | if (write_wav_format != 0) 222 | { 223 | WavWriter.wavwriter_writeheaders(output_stream, (total_samples * byteps * num_channels), num_channels, sample_rate, byteps, bitps); 224 | } 225 | 226 | /* will convert the entire buffer */ 227 | GetBuffer(ac); 228 | 229 | AlacUtils.AlacCloseFile(ac); 230 | 231 | if (output_opened != 0) 232 | { 233 | try 234 | { 235 | output_stream.close(); 236 | } 237 | catch(java.io.IOException ioe) 238 | { 239 | } 240 | } 241 | } 242 | } 243 | 244 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////// 2 | // Java Implementation of Apple Lossless Decoder // 3 | // Copyright (c) 2011-2014 Peter McQuillan // 4 | // All Rights Reserved. // 5 | // Distributed under the BSD Software License (see license.txt) // 6 | //////////////////////////////////////////////////////////////////////////// 7 | 8 | This package contains a Java implementation of an Apple Lossless decoder. 9 | It is ported from v0.2.0 of the Apple Lossless decoder written by David Hammerton. 10 | This code supports both 16-bit and 24-bit Apple Lossless files. 11 | 12 | It is packaged with a demo command-line program that accepts a 13 | Apple Lossless audio file as input and output a RIFF wav file. 14 | 15 | The Java source code files can be compiled to class files very simply by going 16 | to the directory where you have downloaded the .java files and running 17 | 18 | javac *.java 19 | 20 | To run the demo program, use the following command 21 | 22 | java DecoderDemo 23 | 24 | where input.m4a is the name of the Apple Lossless file you wish to decode to a WAV file. 25 | 26 | This code is ported from v0.2.0 of the Apple Lossless decoder written by David Hammerton. 27 | However there are also some extra changes, for example: 28 | 29 | * The original code to read the hdlr atom was capable of generating a minus value seek 30 | after reading strlen - this causes problems if there is poor or non-existent seeking 31 | support 32 | * The stream handling code is now written so that it keeps track of where in the input 33 | stream it is - this is needed for handling the case where the mdat atom comes before the 34 | moov atom (and you have poor/non-existent seeking support) 35 | * The stsz atom handling code assumed variable sample sizes, it now also handles fixed 36 | sample sizes. 37 | 38 | 39 | Thanks to Denis Tulskiy for the contributions he made to the code. 40 | 41 | Please direct any questions or comments to beatofthedrum@gmail.com 42 | -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/AlacContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** AlacContext.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | 12 | package com.beatofthedrum.alacdecoder; 13 | 14 | public class AlacContext 15 | { 16 | DemuxResT demux_res = new DemuxResT(); 17 | AlacFile alac = new AlacFile(); 18 | AlacInputStream input_stream; 19 | int current_sample_block = 0; 20 | int offset; 21 | public boolean error; 22 | public String error_message = ""; 23 | byte[] read_buffer = new byte[1024 *80]; // sample big enough to hold any input for a single alac frame 24 | } -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/AlacDecodeUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** AlacDecodeUtils.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | package com.beatofthedrum.alacdecoder; 12 | 13 | class AlacDecodeUtils 14 | { 15 | 16 | public static void alac_set_info(AlacFile alac, int[] inputbuffer) 17 | { 18 | int ptrIndex = 0; 19 | ptrIndex += 4; // size 20 | ptrIndex += 4; // frma 21 | ptrIndex += 4; // alac 22 | ptrIndex += 4; // size 23 | ptrIndex += 4; // alac 24 | 25 | ptrIndex += 4; // 0 ? 26 | 27 | alac.setinfo_max_samples_per_frame = ((inputbuffer[ptrIndex] << 24) + (inputbuffer[ptrIndex+1] << 16) + (inputbuffer[ptrIndex+2] << 8) + inputbuffer[ptrIndex+3]); // buffer size / 2 ? 28 | ptrIndex += 4; 29 | alac.setinfo_7a = inputbuffer[ptrIndex]; 30 | ptrIndex += 1; 31 | alac.setinfo_sample_size = inputbuffer[ptrIndex]; 32 | ptrIndex += 1; 33 | alac.setinfo_rice_historymult = (inputbuffer[ptrIndex] & 0xff); 34 | ptrIndex += 1; 35 | alac.setinfo_rice_initialhistory = (inputbuffer[ptrIndex] & 0xff); 36 | ptrIndex += 1; 37 | alac.setinfo_rice_kmodifier = (inputbuffer[ptrIndex] & 0xff); 38 | ptrIndex += 1; 39 | alac.setinfo_7f = inputbuffer[ptrIndex]; 40 | ptrIndex += 1; 41 | alac.setinfo_80 = (inputbuffer[ptrIndex] << 8) + inputbuffer[ptrIndex+1]; 42 | ptrIndex += 2; 43 | alac.setinfo_82 = ((inputbuffer[ptrIndex] << 24) + (inputbuffer[ptrIndex+1] << 16) + (inputbuffer[ptrIndex+2] << 8) + inputbuffer[ptrIndex+3]); 44 | ptrIndex += 4; 45 | alac.setinfo_86 = ((inputbuffer[ptrIndex] << 24) + (inputbuffer[ptrIndex+1] << 16) + (inputbuffer[ptrIndex+2] << 8) + inputbuffer[ptrIndex+3]); 46 | ptrIndex += 4; 47 | alac.setinfo_8a_rate = ((inputbuffer[ptrIndex] << 24) + (inputbuffer[ptrIndex+1] << 16) + (inputbuffer[ptrIndex+2] << 8) + inputbuffer[ptrIndex+3]); 48 | ptrIndex += 4; 49 | 50 | } 51 | 52 | /* stream reading */ 53 | 54 | /* supports reading 1 to 16 bits, in big endian format */ 55 | static int readbits_16(AlacFile alac, int bits ) 56 | { 57 | int result = 0; 58 | int new_accumulator = 0; 59 | int part1 = 0; 60 | int part2 = 0; 61 | int part3 =0; 62 | 63 | part1 = (alac.input_buffer[alac.ibIdx] & 0xff); 64 | part2 = (alac.input_buffer[alac.ibIdx + 1] & 0xff); 65 | part3 = (alac.input_buffer[alac.ibIdx + 2] & 0xff); 66 | 67 | result = ((part1 << 16) | (part2 << 8) | part3); 68 | 69 | /* shift left by the number of bits we've already read, 70 | * so that the top 'n' bits of the 24 bits we read will 71 | * be the return bits */ 72 | result = result << alac.input_buffer_bitaccumulator; 73 | 74 | result = result & 0x00ffffff; 75 | 76 | /* and then only want the top 'n' bits from that, where 77 | * n is 'bits' */ 78 | result = result >> (24 - bits); 79 | 80 | new_accumulator = (alac.input_buffer_bitaccumulator + bits); 81 | 82 | /* increase the buffer pointer if we've read over n bytes. */ 83 | alac.ibIdx += (new_accumulator >> 3); 84 | 85 | /* and the remainder goes back into the bit accumulator */ 86 | alac.input_buffer_bitaccumulator = (new_accumulator & 7); 87 | 88 | return result; 89 | } 90 | 91 | /* supports reading 1 to 32 bits, in big endian format */ 92 | static int readbits(AlacFile alac, int bits ) 93 | { 94 | int result = 0; 95 | 96 | if (bits > 16) 97 | { 98 | bits -= 16; 99 | 100 | result = readbits_16(alac, 16) << bits; 101 | } 102 | 103 | result |= readbits_16(alac, bits); 104 | 105 | return result; 106 | } 107 | 108 | /* reads a single bit */ 109 | static int readbit(AlacFile alac) 110 | { 111 | int result = 0; 112 | int new_accumulator = 0; 113 | int part1 = 0; 114 | 115 | part1 = (alac.input_buffer[alac.ibIdx] & 0xff); 116 | 117 | result = part1; 118 | 119 | result = result << alac.input_buffer_bitaccumulator; 120 | 121 | result = result >> 7 & 1; 122 | 123 | new_accumulator = (alac.input_buffer_bitaccumulator + 1); 124 | 125 | alac.ibIdx += new_accumulator / 8; 126 | 127 | alac.input_buffer_bitaccumulator = (new_accumulator % 8); 128 | 129 | return result; 130 | } 131 | 132 | static void unreadbits(AlacFile alac, int bits ) 133 | { 134 | int new_accumulator = (alac.input_buffer_bitaccumulator - bits); 135 | 136 | alac.ibIdx += (new_accumulator >> 3); 137 | 138 | alac.input_buffer_bitaccumulator = (new_accumulator & 7); 139 | if (alac.input_buffer_bitaccumulator < 0) 140 | alac.input_buffer_bitaccumulator *= -1; 141 | } 142 | 143 | static LeadingZeros count_leading_zeros_extra(int curbyte, int output, LeadingZeros lz) 144 | { 145 | 146 | if ((curbyte & 0xf0)==0) 147 | { 148 | output += 4; 149 | } 150 | else 151 | curbyte = curbyte >> 4; 152 | 153 | if ((curbyte & 0x8) != 0) 154 | { 155 | lz.output = output; 156 | lz.curbyte = curbyte; 157 | return lz; 158 | } 159 | if ((curbyte & 0x4) != 0) 160 | { 161 | lz.output = output + 1; 162 | lz.curbyte = curbyte; 163 | return lz; 164 | } 165 | if ((curbyte & 0x2) != 0) 166 | { 167 | lz.output = output + 2; 168 | lz.curbyte = curbyte; 169 | return lz; 170 | } 171 | if ((curbyte & 0x1) != 0) 172 | { 173 | lz.output = output + 3; 174 | lz.curbyte = curbyte; 175 | return lz; 176 | } 177 | 178 | /* shouldn't get here: */ 179 | 180 | lz.output = output + 4; 181 | lz.curbyte = curbyte; 182 | return lz; 183 | 184 | } 185 | static int count_leading_zeros(int input, LeadingZeros lz) 186 | { 187 | int output = 0; 188 | int curbyte = 0; 189 | 190 | curbyte = input >> 24; 191 | if (curbyte != 0) 192 | { 193 | count_leading_zeros_extra(curbyte, output, lz); 194 | output = lz.output; 195 | curbyte = lz.curbyte; 196 | return output; 197 | } 198 | output += 8; 199 | 200 | curbyte = input >> 16; 201 | if ((curbyte & 0xFF) != 0) 202 | { 203 | count_leading_zeros_extra(curbyte, output, lz); 204 | output = lz.output; 205 | curbyte = lz.curbyte; 206 | 207 | return output; 208 | } 209 | output += 8; 210 | 211 | curbyte = input >> 8; 212 | if ((curbyte & 0xFF) != 0) 213 | { 214 | count_leading_zeros_extra(curbyte, output, lz); 215 | output = lz.output; 216 | curbyte = lz.curbyte; 217 | 218 | return output; 219 | } 220 | output += 8; 221 | 222 | curbyte = input; 223 | if ((curbyte & 0xFF) != 0) 224 | { 225 | count_leading_zeros_extra(curbyte, output, lz); 226 | output = lz.output; 227 | curbyte = lz.curbyte; 228 | 229 | return output; 230 | } 231 | output += 8; 232 | 233 | return output; 234 | } 235 | 236 | public static int entropy_decode_value(AlacFile alac, int readSampleSize , int k , int rice_kmodifier_mask ) 237 | { 238 | int x = 0; // decoded value 239 | 240 | // read x, number of 1s before 0 represent the rice value. 241 | while (x <= Defines.RICE_THRESHOLD && readbit(alac) != 0) 242 | { 243 | x++; 244 | } 245 | 246 | if (x > Defines.RICE_THRESHOLD) 247 | { 248 | // read the number from the bit stream (raw value) 249 | int value = 0; 250 | 251 | value = readbits(alac, readSampleSize); 252 | 253 | // mask value 254 | value &= ((0xffffffff) >> (32 - readSampleSize)); 255 | 256 | x = value; 257 | } 258 | else 259 | { 260 | if (k != 1) 261 | { 262 | int extraBits = readbits(alac, k); 263 | 264 | x *= (((1 << k) - 1) & rice_kmodifier_mask); 265 | 266 | if (extraBits > 1) 267 | x += extraBits - 1; 268 | else 269 | unreadbits(alac, 1); 270 | } 271 | } 272 | 273 | return x; 274 | } 275 | 276 | public static void entropy_rice_decode(AlacFile alac, int[] outputBuffer, int outputSize , int readSampleSize , int rice_initialhistory , int rice_kmodifier , int rice_historymult , int rice_kmodifier_mask ) 277 | { 278 | int history = rice_initialhistory; 279 | int outputCount = 0; 280 | int signModifier = 0; 281 | 282 | while(outputCount < outputSize) 283 | { 284 | int decodedValue = 0; 285 | int finalValue = 0; 286 | int k = 0; 287 | 288 | k = 31 - rice_kmodifier - count_leading_zeros((history >> 9) + 3, alac.lz); 289 | 290 | if (k < 0) 291 | k += rice_kmodifier; 292 | else 293 | k = rice_kmodifier; 294 | 295 | // note: don't use rice_kmodifier_mask here (set mask to 0xFFFFFFFF) 296 | decodedValue = entropy_decode_value(alac, readSampleSize, k, 0xFFFFFFFF); 297 | 298 | decodedValue += signModifier; 299 | finalValue = ((decodedValue + 1) / 2); // inc by 1 and shift out sign bit 300 | if ((decodedValue & 1) != 0) // the sign is stored in the low bit 301 | finalValue *= -1; 302 | 303 | outputBuffer[outputCount] = finalValue; 304 | 305 | signModifier = 0; 306 | 307 | // update history 308 | history += (decodedValue * rice_historymult) - ((history * rice_historymult) >> 9); 309 | 310 | if (decodedValue > 0xFFFF) 311 | history = 0xFFFF; 312 | 313 | // special case, for compressed blocks of 0 314 | if ((history < 128) && (outputCount + 1 < outputSize)) 315 | { 316 | int blockSize = 0; 317 | 318 | signModifier = 1; 319 | 320 | k = count_leading_zeros(history, alac.lz) + ((history + 16) / 64) - 24; 321 | 322 | // note: blockSize is always 16bit 323 | blockSize = entropy_decode_value(alac, 16, k, rice_kmodifier_mask); 324 | 325 | // got blockSize 0s 326 | if (blockSize > 0) 327 | { 328 | int countSize = 0; 329 | countSize = blockSize; 330 | for (int j = 0; j < countSize; j++) 331 | { 332 | outputBuffer[outputCount + 1 + j] = 0; 333 | } 334 | outputCount += blockSize; 335 | } 336 | 337 | if (blockSize > 0xFFFF) 338 | signModifier = 0; 339 | 340 | history = 0; 341 | } 342 | 343 | outputCount++; 344 | } 345 | } 346 | 347 | static int[] predictor_decompress_fir_adapt(int[] error_buffer, int output_size , int readsamplesize , int[] predictor_coef_table, int predictor_coef_num , int predictor_quantitization ) 348 | { 349 | int buffer_out_idx = 0; 350 | int[] buffer_out; 351 | int bitsmove = 0; 352 | 353 | /* first sample always copies */ 354 | buffer_out = error_buffer; 355 | 356 | if (predictor_coef_num == 0) 357 | { 358 | if (output_size <= 1) 359 | return(buffer_out); 360 | int sizeToCopy = 0; 361 | sizeToCopy = (output_size-1) * 4; 362 | System.arraycopy(error_buffer, 1, buffer_out, 1, sizeToCopy); 363 | return(buffer_out); 364 | } 365 | 366 | if (predictor_coef_num == 0x1f) // 11111 - max value of predictor_coef_num 367 | { 368 | /* second-best case scenario for fir decompression, 369 | * error describes a small difference from the previous sample only 370 | */ 371 | if (output_size <= 1) 372 | return(buffer_out); 373 | 374 | for (int i = 0; i < (output_size - 1); i++) 375 | { 376 | int prev_value = 0; 377 | int error_value = 0; 378 | 379 | prev_value = buffer_out[i]; 380 | error_value = error_buffer[i+1]; 381 | 382 | bitsmove = 32 - readsamplesize; 383 | buffer_out[i+1] = (((prev_value + error_value) << bitsmove) >> bitsmove); 384 | } 385 | return(buffer_out); 386 | } 387 | 388 | /* read warm-up samples */ 389 | if (predictor_coef_num > 0) 390 | { 391 | for (int i = 0; i < predictor_coef_num; i++) 392 | { 393 | int val = 0; 394 | 395 | val = buffer_out[i] + error_buffer[i+1]; 396 | 397 | bitsmove = 32 - readsamplesize; 398 | 399 | val = ((val << bitsmove) >> bitsmove); 400 | 401 | buffer_out[i+1] = val; 402 | } 403 | } 404 | 405 | /* general case */ 406 | if (predictor_coef_num > 0) 407 | { 408 | buffer_out_idx = 0; 409 | for (int i = predictor_coef_num + 1; i < output_size; i++) 410 | { 411 | int j ; 412 | int sum = 0; 413 | int outval ; 414 | int error_val = error_buffer[i]; 415 | 416 | for (j = 0; j < predictor_coef_num; j++) 417 | { 418 | sum += (buffer_out[buffer_out_idx + predictor_coef_num-j] - buffer_out[buffer_out_idx]) * predictor_coef_table[j]; 419 | } 420 | 421 | outval = (1 << (predictor_quantitization-1)) + sum; 422 | outval = outval >> predictor_quantitization; 423 | outval = outval + buffer_out[buffer_out_idx] + error_val; 424 | bitsmove = 32 - readsamplesize; 425 | 426 | outval = ((outval << bitsmove) >> bitsmove); 427 | 428 | buffer_out[buffer_out_idx+predictor_coef_num+1] = outval; 429 | 430 | if (error_val > 0) 431 | { 432 | int predictor_num = predictor_coef_num - 1; 433 | 434 | while (predictor_num >= 0 && error_val > 0) 435 | { 436 | int val = buffer_out[buffer_out_idx] - buffer_out[buffer_out_idx + predictor_coef_num - predictor_num]; 437 | int sign = ((val < 0) ? (-1) : ((val > 0) ? (1) : (0))); 438 | 439 | predictor_coef_table[predictor_num] -= sign; 440 | 441 | val *= sign; // absolute value 442 | 443 | error_val -= ((val >> predictor_quantitization) * (predictor_coef_num - predictor_num)); 444 | 445 | predictor_num--; 446 | } 447 | } 448 | else if (error_val < 0) 449 | { 450 | int predictor_num = predictor_coef_num - 1; 451 | 452 | while (predictor_num >= 0 && error_val < 0) 453 | { 454 | int val = buffer_out[buffer_out_idx] - buffer_out[buffer_out_idx + predictor_coef_num - predictor_num]; 455 | int sign = - ((val < 0) ? (-1) : ((val > 0) ? (1) : (0))); 456 | 457 | predictor_coef_table[predictor_num] -= sign; 458 | 459 | val *= sign; // neg value 460 | 461 | error_val -= ((val >> predictor_quantitization) * (predictor_coef_num - predictor_num)); 462 | 463 | predictor_num--; 464 | } 465 | } 466 | 467 | buffer_out_idx++; 468 | } 469 | } 470 | return(buffer_out); 471 | } 472 | 473 | 474 | public static void deinterlace_16(int[] buffer_a, int[] buffer_b, int[] buffer_out, int numchannels , int numsamples , int interlacing_shift , int interlacing_leftweight ) 475 | { 476 | 477 | if (numsamples <= 0) 478 | return; 479 | 480 | /* weighted interlacing */ 481 | if (0 != interlacing_leftweight) 482 | { 483 | for (int i = 0; i < numsamples; i++) 484 | { 485 | int difference = 0; 486 | int midright = 0; 487 | int left = 0; 488 | int right = 0; 489 | 490 | midright = buffer_a[i]; 491 | difference = buffer_b[i]; 492 | 493 | right = (midright - ((difference * interlacing_leftweight) >> interlacing_shift)); 494 | left = (right + difference); 495 | 496 | /* output is always little endian */ 497 | 498 | buffer_out[i *numchannels] = left; 499 | buffer_out[i *numchannels + 1] = right; 500 | } 501 | 502 | return; 503 | } 504 | 505 | /* otherwise basic interlacing took place */ 506 | for (int i = 0; i < numsamples; i++) 507 | { 508 | int left = 0; 509 | int right = 0; 510 | 511 | left = buffer_a[i]; 512 | right = buffer_b[i]; 513 | 514 | /* output is always little endian */ 515 | 516 | buffer_out[i *numchannels] = left; 517 | buffer_out[i *numchannels + 1] = right; 518 | } 519 | } 520 | 521 | 522 | public static void deinterlace_24(int[] buffer_a, int[] buffer_b, int uncompressed_bytes , int[] uncompressed_bytes_buffer_a, int[] uncompressed_bytes_buffer_b, int[] buffer_out, int numchannels , int numsamples , int interlacing_shift , int interlacing_leftweight ) 523 | { 524 | if (numsamples <= 0) 525 | return; 526 | 527 | /* weighted interlacing */ 528 | if (interlacing_leftweight != 0) 529 | { 530 | for (int i = 0; i < numsamples; i++) 531 | { 532 | int difference = 0; 533 | int midright = 0; 534 | int left = 0; 535 | int right = 0; 536 | 537 | midright = buffer_a[i]; 538 | difference = buffer_b[i]; 539 | 540 | right = midright - ((difference * interlacing_leftweight) >> interlacing_shift); 541 | left = right + difference; 542 | 543 | if (uncompressed_bytes != 0) 544 | { 545 | int mask = ~(0xFFFFFFFF << (uncompressed_bytes * 8)); 546 | left <<= (uncompressed_bytes * 8); 547 | right <<= (uncompressed_bytes * 8); 548 | 549 | left = left | (uncompressed_bytes_buffer_a[i] & mask); 550 | right = right | (uncompressed_bytes_buffer_b[i] & mask); 551 | } 552 | 553 | buffer_out[i * numchannels * 3] = (left & 0xFF); 554 | buffer_out[i * numchannels * 3 + 1] = ((left >> 8) & 0xFF); 555 | buffer_out[i * numchannels * 3 + 2] = ((left >> 16) & 0xFF); 556 | 557 | buffer_out[i * numchannels * 3 + 3] = (right & 0xFF); 558 | buffer_out[i * numchannels * 3 + 4] = ((right >> 8) & 0xFF); 559 | buffer_out[i * numchannels * 3 + 5] = ((right >> 16) & 0xFF); 560 | } 561 | 562 | return; 563 | } 564 | 565 | /* otherwise basic interlacing took place */ 566 | for (int i = 0; i < numsamples; i++) 567 | { 568 | int left = 0; 569 | int right = 0; 570 | 571 | left = buffer_a[i]; 572 | right = buffer_b[i]; 573 | 574 | if (uncompressed_bytes != 0) 575 | { 576 | int mask = ~(0xFFFFFFFF << (uncompressed_bytes * 8)); 577 | left <<= (uncompressed_bytes * 8); 578 | right <<= (uncompressed_bytes * 8); 579 | 580 | left = left | (uncompressed_bytes_buffer_a[i] & mask); 581 | right = right | (uncompressed_bytes_buffer_b[i] & mask); 582 | } 583 | 584 | buffer_out[i * numchannels * 3] = (left & 0xFF); 585 | buffer_out[i * numchannels * 3 + 1] = ((left >> 8) & 0xFF); 586 | buffer_out[i * numchannels * 3 + 2] = ((left >> 16) & 0xFF); 587 | 588 | buffer_out[i * numchannels * 3 + 3] = (right & 0xFF); 589 | buffer_out[i * numchannels * 3 + 4] = ((right >> 8) & 0xFF); 590 | buffer_out[i * numchannels * 3 + 5] = ((right >> 16) & 0xFF); 591 | 592 | } 593 | 594 | } 595 | 596 | 597 | public static int decode_frame(AlacFile alac, byte[] inbuffer, int[] outbuffer, int outputsize ) 598 | { 599 | int channels ; 600 | int outputsamples = alac.setinfo_max_samples_per_frame; 601 | 602 | /* setup the stream */ 603 | alac.input_buffer = inbuffer; 604 | alac.input_buffer_bitaccumulator = 0; 605 | alac.ibIdx = 0; 606 | 607 | 608 | channels = readbits(alac, 3); 609 | 610 | outputsize = outputsamples * alac.bytespersample; 611 | 612 | if(channels == 0) // 1 channel 613 | { 614 | int hassize ; 615 | int isnotcompressed ; 616 | int readsamplesize ; 617 | 618 | int uncompressed_bytes ; 619 | int ricemodifier ; 620 | 621 | int tempPred = 0; 622 | 623 | /* 2^result = something to do with output waiting. 624 | * perhaps matters if we read > 1 frame in a pass? 625 | */ 626 | readbits(alac, 4); 627 | 628 | readbits(alac, 12); // unknown, skip 12 bits 629 | 630 | hassize = readbits(alac, 1); // the output sample size is stored soon 631 | 632 | uncompressed_bytes = readbits(alac, 2); // number of bytes in the (compressed) stream that are not compressed 633 | 634 | isnotcompressed = readbits(alac, 1); // whether the frame is compressed 635 | 636 | if (hassize != 0) 637 | { 638 | /* now read the number of samples, 639 | * as a 32bit integer */ 640 | outputsamples = readbits(alac, 32); 641 | outputsize = outputsamples * alac.bytespersample; 642 | } 643 | 644 | readsamplesize = alac.setinfo_sample_size - (uncompressed_bytes * 8); 645 | 646 | if (isnotcompressed == 0) 647 | { // so it is compressed 648 | int[] predictor_coef_table = alac.predictor_coef_table; 649 | int predictor_coef_num ; 650 | int prediction_type ; 651 | int prediction_quantitization ; 652 | int i ; 653 | 654 | /* skip 16 bits, not sure what they are. seem to be used in 655 | * two channel case */ 656 | readbits(alac, 8); 657 | readbits(alac, 8); 658 | 659 | prediction_type = readbits(alac, 4); 660 | prediction_quantitization = readbits(alac, 4); 661 | 662 | ricemodifier = readbits(alac, 3); 663 | predictor_coef_num = readbits(alac, 5); 664 | 665 | /* read the predictor table */ 666 | 667 | for (i = 0; i < predictor_coef_num; i++) 668 | { 669 | tempPred = readbits(alac,16); 670 | if(tempPred > 32767) 671 | { 672 | // the predictor coef table values are only 16 bit signed 673 | tempPred = tempPred - 65536; 674 | } 675 | 676 | predictor_coef_table[i] = tempPred; 677 | } 678 | 679 | if (uncompressed_bytes != 0) 680 | { 681 | for (i = 0; i < outputsamples; i++) 682 | { 683 | alac.uncompressed_bytes_buffer_a[i] = readbits(alac, uncompressed_bytes * 8); 684 | } 685 | } 686 | 687 | 688 | entropy_rice_decode(alac, alac.predicterror_buffer_a, outputsamples, readsamplesize, alac.setinfo_rice_initialhistory, alac.setinfo_rice_kmodifier, ricemodifier * (alac.setinfo_rice_historymult / 4), (1 << alac.setinfo_rice_kmodifier) - 1); 689 | 690 | if (prediction_type == 0) 691 | { // adaptive fir 692 | alac.outputsamples_buffer_a = predictor_decompress_fir_adapt(alac.predicterror_buffer_a, outputsamples, readsamplesize, predictor_coef_table, predictor_coef_num, prediction_quantitization); 693 | } 694 | else 695 | { 696 | System.err.println("FIXME: unhandled predicition type: " +prediction_type); 697 | 698 | /* i think the only other prediction type (or perhaps this is just a 699 | * boolean?) runs adaptive fir twice.. like: 700 | * predictor_decompress_fir_adapt(predictor_error, tempout, ...) 701 | * predictor_decompress_fir_adapt(predictor_error, outputsamples ...) 702 | * little strange.. 703 | */ 704 | } 705 | 706 | } 707 | else 708 | { // not compressed, easy case 709 | if (alac.setinfo_sample_size <= 16) 710 | { 711 | int bitsmove = 0; 712 | for (int i = 0; i < outputsamples; i++) 713 | { 714 | int audiobits = readbits(alac, alac.setinfo_sample_size); 715 | bitsmove = 32 - alac.setinfo_sample_size; 716 | 717 | audiobits = ((audiobits << bitsmove) >> bitsmove); 718 | 719 | alac.outputsamples_buffer_a[i] = audiobits; 720 | } 721 | } 722 | else 723 | { 724 | int x ; 725 | int m = 1 << (24 -1); 726 | for (int i = 0; i < outputsamples; i++) 727 | { 728 | int audiobits ; 729 | 730 | audiobits = readbits(alac, 16); 731 | /* special case of sign extension.. 732 | * as we'll be ORing the low 16bits into this */ 733 | audiobits = audiobits << (alac.setinfo_sample_size - 16); 734 | audiobits = audiobits | readbits(alac, alac.setinfo_sample_size - 16); 735 | x = audiobits & ((1 << 24) - 1); 736 | audiobits = (x ^ m) - m; // sign extend 24 bits 737 | 738 | alac.outputsamples_buffer_a[i] = audiobits; 739 | } 740 | } 741 | uncompressed_bytes = 0; // always 0 for uncompressed 742 | } 743 | 744 | switch(alac.setinfo_sample_size) 745 | { 746 | case 16: 747 | { 748 | 749 | for (int i = 0; i < outputsamples; i++) 750 | { 751 | int sample = alac.outputsamples_buffer_a[i]; 752 | outbuffer[i * alac.numchannels] = sample; 753 | 754 | /* 755 | ** We have to handle the case where the data is actually mono, but the stsd atom says it has 2 channels 756 | ** in this case we create a stereo file where one of the channels is silent. If mono and 1 channel this value 757 | ** will be overwritten in the next iteration 758 | */ 759 | 760 | outbuffer[(i * alac.numchannels) + 1] = 0; 761 | } 762 | break; 763 | } 764 | case 24: 765 | { 766 | for (int i = 0; i < outputsamples; i++) 767 | { 768 | int sample = alac.outputsamples_buffer_a[i]; 769 | 770 | if (uncompressed_bytes != 0) 771 | { 772 | int mask = 0; 773 | sample = sample << (uncompressed_bytes * 8); 774 | mask = ~(0xFFFFFFFF << (uncompressed_bytes * 8)); 775 | sample = sample | (alac.uncompressed_bytes_buffer_a[i] & mask); 776 | } 777 | 778 | outbuffer[i * alac.numchannels * 3] = ((sample) & 0xFF); 779 | outbuffer[i * alac.numchannels * 3 + 1] = ((sample >> 8) & 0xFF); 780 | outbuffer[i * alac.numchannels * 3 + 2] = ((sample >> 16) & 0xFF); 781 | 782 | /* 783 | ** We have to handle the case where the data is actually mono, but the stsd atom says it has 2 channels 784 | ** in this case we create a stereo file where one of the channels is silent. If mono and 1 channel this value 785 | ** will be overwritten in the next iteration 786 | */ 787 | 788 | outbuffer[i * alac.numchannels * 3 + 3] = 0; 789 | outbuffer[i * alac.numchannels * 3 + 4] = 0; 790 | outbuffer[i * alac.numchannels * 3 + 5] = 0; 791 | 792 | } 793 | break; 794 | } 795 | case 20: 796 | case 32: 797 | System.err.println("FIXME: unimplemented sample size " + alac.setinfo_sample_size); 798 | default: 799 | 800 | } 801 | } 802 | else if(channels == 1) // 2 channels 803 | { 804 | int hassize ; 805 | int isnotcompressed ; 806 | int readsamplesize ; 807 | 808 | int uncompressed_bytes ; 809 | 810 | int interlacing_shift ; 811 | int interlacing_leftweight ; 812 | 813 | /* 2^result = something to do with output waiting. 814 | * perhaps matters if we read > 1 frame in a pass? 815 | */ 816 | readbits(alac, 4); 817 | 818 | readbits(alac, 12); // unknown, skip 12 bits 819 | 820 | hassize = readbits(alac, 1); // the output sample size is stored soon 821 | 822 | uncompressed_bytes = readbits(alac, 2); // the number of bytes in the (compressed) stream that are not compressed 823 | 824 | isnotcompressed = readbits(alac, 1); // whether the frame is compressed 825 | 826 | if (hassize != 0) 827 | { 828 | /* now read the number of samples, 829 | * as a 32bit integer */ 830 | outputsamples = readbits(alac, 32); 831 | outputsize = outputsamples * alac.bytespersample; 832 | } 833 | 834 | readsamplesize = alac.setinfo_sample_size - (uncompressed_bytes * 8) + 1; 835 | 836 | if (isnotcompressed == 0) 837 | { // compressed 838 | int[] predictor_coef_table_a = alac.predictor_coef_table_a; 839 | int predictor_coef_num_a ; 840 | int prediction_type_a ; 841 | int prediction_quantitization_a ; 842 | int ricemodifier_a ; 843 | 844 | int[] predictor_coef_table_b = alac.predictor_coef_table_b; 845 | int predictor_coef_num_b ; 846 | int prediction_type_b ; 847 | int prediction_quantitization_b ; 848 | int ricemodifier_b ; 849 | 850 | int tempPred = 0; 851 | 852 | interlacing_shift = readbits(alac, 8); 853 | interlacing_leftweight = readbits(alac, 8); 854 | 855 | /******** channel 1 ***********/ 856 | prediction_type_a = readbits(alac, 4); 857 | prediction_quantitization_a = readbits(alac, 4); 858 | 859 | ricemodifier_a = readbits(alac, 3); 860 | predictor_coef_num_a = readbits(alac, 5); 861 | 862 | /* read the predictor table */ 863 | 864 | for (int i = 0; i < predictor_coef_num_a; i++) 865 | { 866 | tempPred = readbits(alac,16); 867 | if(tempPred > 32767) 868 | { 869 | // the predictor coef table values are only 16 bit signed 870 | tempPred = tempPred - 65536; 871 | } 872 | predictor_coef_table_a[i] = tempPred; 873 | } 874 | 875 | /******** channel 2 *********/ 876 | prediction_type_b = readbits(alac, 4); 877 | prediction_quantitization_b = readbits(alac, 4); 878 | 879 | ricemodifier_b = readbits(alac, 3); 880 | predictor_coef_num_b = readbits(alac, 5); 881 | 882 | /* read the predictor table */ 883 | 884 | for (int i = 0; i < predictor_coef_num_b; i++) 885 | { 886 | tempPred = readbits(alac,16); 887 | if(tempPred > 32767) 888 | { 889 | // the predictor coef table values are only 16 bit signed 890 | tempPred = tempPred - 65536; 891 | } 892 | predictor_coef_table_b[i] = tempPred; 893 | } 894 | 895 | /*********************/ 896 | if (uncompressed_bytes != 0) 897 | { // see mono case 898 | for (int i = 0; i < outputsamples; i++) 899 | { 900 | alac.uncompressed_bytes_buffer_a[i] = readbits(alac, uncompressed_bytes * 8); 901 | alac.uncompressed_bytes_buffer_b[i] = readbits(alac, uncompressed_bytes * 8); 902 | } 903 | } 904 | 905 | /* channel 1 */ 906 | 907 | entropy_rice_decode(alac, alac.predicterror_buffer_a, outputsamples, readsamplesize, alac.setinfo_rice_initialhistory, alac.setinfo_rice_kmodifier, ricemodifier_a * (alac.setinfo_rice_historymult / 4), (1 << alac.setinfo_rice_kmodifier) - 1); 908 | 909 | if (prediction_type_a == 0) 910 | { // adaptive fir 911 | 912 | alac.outputsamples_buffer_a = predictor_decompress_fir_adapt(alac.predicterror_buffer_a, outputsamples, readsamplesize, predictor_coef_table_a, predictor_coef_num_a, prediction_quantitization_a); 913 | 914 | } 915 | else 916 | { // see mono case 917 | System.err.println("FIXME: unhandled predicition type: " + prediction_type_a); 918 | } 919 | 920 | /* channel 2 */ 921 | entropy_rice_decode(alac, alac.predicterror_buffer_b, outputsamples, readsamplesize, alac.setinfo_rice_initialhistory, alac.setinfo_rice_kmodifier, ricemodifier_b * (alac.setinfo_rice_historymult / 4), (1 << alac.setinfo_rice_kmodifier) - 1); 922 | 923 | if (prediction_type_b == 0) 924 | { // adaptive fir 925 | alac.outputsamples_buffer_b = predictor_decompress_fir_adapt(alac.predicterror_buffer_b, outputsamples, readsamplesize, predictor_coef_table_b, predictor_coef_num_b, prediction_quantitization_b); 926 | } 927 | else 928 | { 929 | System.err.println("FIXME: unhandled predicition type: " + prediction_type_b); 930 | } 931 | } 932 | else 933 | { // not compressed, easy case 934 | if (alac.setinfo_sample_size <= 16) 935 | { 936 | int bitsmove ; 937 | 938 | for (int i = 0; i < outputsamples; i++) 939 | { 940 | int audiobits_a ; 941 | int audiobits_b ; 942 | 943 | audiobits_a = readbits(alac, alac.setinfo_sample_size); 944 | audiobits_b = readbits(alac, alac.setinfo_sample_size); 945 | 946 | bitsmove = 32 - alac.setinfo_sample_size; 947 | 948 | audiobits_a = ((audiobits_a << bitsmove) >> bitsmove); 949 | audiobits_b = ((audiobits_b << bitsmove) >> bitsmove); 950 | 951 | alac.outputsamples_buffer_a[i] = audiobits_a; 952 | alac.outputsamples_buffer_b[i] = audiobits_b; 953 | } 954 | } 955 | else 956 | { 957 | int x ; 958 | int m = 1 << (24 -1); 959 | 960 | for (int i = 0; i < outputsamples; i++) 961 | { 962 | int audiobits_a ; 963 | int audiobits_b ; 964 | 965 | audiobits_a = readbits(alac, 16); 966 | audiobits_a = audiobits_a << (alac.setinfo_sample_size - 16); 967 | audiobits_a = audiobits_a | readbits(alac, alac.setinfo_sample_size - 16); 968 | x = audiobits_a & ((1 << 24) - 1); 969 | audiobits_a = (x ^ m) - m; // sign extend 24 bits 970 | 971 | audiobits_b = readbits(alac, 16); 972 | audiobits_b = audiobits_b << (alac.setinfo_sample_size - 16); 973 | audiobits_b = audiobits_b | readbits(alac, alac.setinfo_sample_size - 16); 974 | x = audiobits_b & ((1 << 24) - 1); 975 | audiobits_b = (x ^ m) - m; // sign extend 24 bits 976 | 977 | alac.outputsamples_buffer_a[i] = audiobits_a; 978 | alac.outputsamples_buffer_b[i] = audiobits_b; 979 | } 980 | } 981 | uncompressed_bytes = 0; // always 0 for uncompressed 982 | interlacing_shift = 0; 983 | interlacing_leftweight = 0; 984 | } 985 | 986 | switch(alac.setinfo_sample_size) 987 | { 988 | case 16: 989 | { 990 | deinterlace_16(alac.outputsamples_buffer_a, alac.outputsamples_buffer_b, outbuffer, alac.numchannels, outputsamples, interlacing_shift, interlacing_leftweight); 991 | break; 992 | } 993 | case 24: 994 | { 995 | deinterlace_24(alac.outputsamples_buffer_a, alac.outputsamples_buffer_b, uncompressed_bytes, alac.uncompressed_bytes_buffer_a, alac.uncompressed_bytes_buffer_b, outbuffer, alac.numchannels, outputsamples, interlacing_shift, interlacing_leftweight); 996 | break; 997 | } 998 | case 20: 999 | case 32: 1000 | System.err.println("FIXME: unimplemented sample size " + alac.setinfo_sample_size); 1001 | 1002 | default: 1003 | 1004 | } 1005 | } 1006 | return outputsize; 1007 | } 1008 | 1009 | public static AlacFile create_alac(int samplesize , int numchannels ) 1010 | { 1011 | AlacFile newfile = new AlacFile(); 1012 | 1013 | newfile.samplesize = samplesize; 1014 | newfile.numchannels = numchannels; 1015 | newfile.bytespersample = (samplesize / 8) * numchannels; 1016 | 1017 | return newfile; 1018 | } 1019 | } 1020 | 1021 | -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/AlacFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** AlacFile.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | package com.beatofthedrum.alacdecoder; 12 | 13 | class AlacFile 14 | { 15 | 16 | byte input_buffer[]; 17 | int ibIdx = 0; 18 | int input_buffer_bitaccumulator = 0; /* used so we can do arbitary 19 | bit reads */ 20 | 21 | int samplesize = 0; 22 | int numchannels = 0; 23 | int bytespersample = 0; 24 | 25 | LeadingZeros lz = new LeadingZeros(); 26 | 27 | 28 | private int buffer_size = 16384; 29 | /* buffers */ 30 | int predicterror_buffer_a[] = new int[buffer_size]; 31 | int predicterror_buffer_b[] = new int[buffer_size]; 32 | 33 | int outputsamples_buffer_a[] = new int[buffer_size]; 34 | int outputsamples_buffer_b[] = new int[buffer_size]; 35 | 36 | int uncompressed_bytes_buffer_a[] = new int[buffer_size]; 37 | int uncompressed_bytes_buffer_b[] = new int[buffer_size]; 38 | 39 | 40 | 41 | /* stuff from setinfo */ 42 | int setinfo_max_samples_per_frame = 0; // 0x1000 = 4096 43 | /* max samples per frame? */ 44 | 45 | int setinfo_7a = 0; // 0x00 46 | int setinfo_sample_size = 0; // 0x10 47 | int setinfo_rice_historymult = 0; // 0x28 48 | int setinfo_rice_initialhistory = 0; // 0x0a 49 | int setinfo_rice_kmodifier = 0; // 0x0e 50 | int setinfo_7f = 0; // 0x02 51 | int setinfo_80 = 0; // 0x00ff 52 | int setinfo_82 = 0; // 0x000020e7 53 | /* max sample size?? */ 54 | int setinfo_86 = 0; // 0x00069fe4 55 | /* bit rate (avarge)?? */ 56 | int setinfo_8a_rate = 0; // 0x0000ac44 57 | /* end setinfo stuff */ 58 | 59 | public int[] predictor_coef_table = new int[1024]; 60 | public int[] predictor_coef_table_a = new int[1024]; 61 | public int[] predictor_coef_table_b = new int[1024]; 62 | } -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/AlacInputStream.java: -------------------------------------------------------------------------------- 1 | package com.beatofthedrum.alacdecoder; 2 | 3 | import java.io.DataInputStream; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | 8 | /** 9 | * Author: Denis Tulskiy 10 | * Date: 4/7/11 11 | */ 12 | public class AlacInputStream extends DataInputStream { 13 | /** 14 | * Creates a DataInputStream that uses the specified 15 | * underlying InputStream. 16 | * 17 | * @param in the specified input stream 18 | */ 19 | public AlacInputStream(InputStream in) { 20 | super(in); 21 | } 22 | 23 | public void seek(long pos) { 24 | if (in instanceof FileInputStream) { 25 | try { 26 | ((FileInputStream) in).getChannel().position(pos); 27 | } catch (IOException e) { 28 | e.printStackTrace(); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/AlacUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** AlacUtils.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | package com.beatofthedrum.alacdecoder; 12 | 13 | public class AlacUtils 14 | { 15 | public static AlacContext AlacOpenFileInput(String inputfilename) 16 | { 17 | int headerRead; 18 | QTMovieT qtmovie = new QTMovieT(); 19 | DemuxResT demux_res = new DemuxResT(); 20 | AlacContext ac = new AlacContext(); 21 | AlacInputStream input_stream; 22 | AlacFile alac; 23 | 24 | ac.error = false; 25 | 26 | try 27 | { 28 | java.io.FileInputStream fistream; 29 | fistream = new java.io.FileInputStream(inputfilename); 30 | input_stream = new AlacInputStream(fistream); 31 | } 32 | catch (java.io.FileNotFoundException fe) 33 | { 34 | ac.error_message = "Input file not found"; 35 | ac.error = true; 36 | return (ac); 37 | } 38 | 39 | ac.input_stream = input_stream; 40 | 41 | /* if qtmovie_read returns successfully, the stream is up to 42 | * the movie data, which can be used directly by the decoder */ 43 | headerRead = DemuxUtils.qtmovie_read(input_stream, qtmovie, demux_res); 44 | 45 | if (headerRead == 0) 46 | { 47 | ac.error = true; 48 | if (demux_res.format_read == 0) 49 | { 50 | ac.error_message = "Failed to load the QuickTime movie headers."; 51 | if (demux_res.format_read != 0) 52 | ac.error_message = ac.error_message + " File type: " + DemuxUtils.SplitFourCC(demux_res.format); 53 | } 54 | else 55 | { 56 | ac.error_message = "Error while loading the QuickTime movie headers."; 57 | } 58 | return (ac); 59 | } 60 | else if(headerRead == 3) 61 | { 62 | /* 63 | ** This section is used when the stream system being used doesn't support seeking 64 | ** We have kept track within the file where we need to go to, we close the file and 65 | ** skip bytes to go directly to that point 66 | */ 67 | 68 | try 69 | { 70 | ac.input_stream.close(); 71 | } 72 | catch(java.io.IOException ioe) 73 | { 74 | ac.error_message = "Error when seeking to start of music data"; 75 | ac.error = true; 76 | return (ac); 77 | } 78 | 79 | try 80 | { 81 | java.io.FileInputStream fistream; 82 | fistream = new java.io.FileInputStream(inputfilename); 83 | input_stream = new AlacInputStream(fistream); 84 | ac.input_stream = input_stream; 85 | 86 | qtmovie.qtstream.stream = input_stream; 87 | qtmovie.qtstream.currentPos = 0; 88 | StreamUtils.stream_skip(qtmovie.qtstream, qtmovie.saved_mdat_pos); 89 | } 90 | catch (java.io.FileNotFoundException fe) 91 | { 92 | ac.error_message = "Input file not found"; 93 | ac.error = true; 94 | return (ac); 95 | } 96 | } 97 | 98 | /* initialise the sound converter */ 99 | 100 | alac = AlacDecodeUtils.create_alac(demux_res.sample_size, demux_res.num_channels); 101 | 102 | AlacDecodeUtils.alac_set_info(alac, demux_res.codecdata); 103 | 104 | ac.demux_res = demux_res; 105 | ac.alac = alac; 106 | 107 | return (ac); 108 | 109 | } 110 | 111 | public static void AlacCloseFile(AlacContext ac) 112 | { 113 | if(null != ac.input_stream) 114 | { 115 | try 116 | { 117 | ac.input_stream.close(); 118 | } 119 | catch(java.io.IOException ioe) 120 | { 121 | } 122 | } 123 | } 124 | 125 | // Heres where we extract the actual music data 126 | 127 | public static int AlacUnpackSamples(AlacContext ac, int[] pDestBuffer) 128 | { 129 | int sample_byte_size; 130 | SampleDuration sampleinfo = new SampleDuration(); 131 | byte[] read_buffer = ac.read_buffer; 132 | int destBufferSize = 1024 *24 * 3; // 24kb buffer = 4096 frames = 1 alac sample (we support max 24bps) 133 | int outputBytes; 134 | MyStream inputStream = new MyStream(); 135 | 136 | inputStream.stream = ac.input_stream; 137 | 138 | // if current_sample_block is beyond last block then finished 139 | 140 | if(ac.current_sample_block >= ac.demux_res.sample_byte_size.length) 141 | { 142 | return 0; 143 | } 144 | 145 | if (get_sample_info(ac.demux_res, ac.current_sample_block , sampleinfo) == 0) 146 | { 147 | // getting sample failed 148 | return 0; 149 | } 150 | 151 | sample_byte_size = sampleinfo.sample_byte_size; 152 | 153 | StreamUtils.stream_read(inputStream, sample_byte_size, read_buffer, 0); 154 | 155 | /* now fetch */ 156 | outputBytes = destBufferSize; 157 | 158 | outputBytes = AlacDecodeUtils.decode_frame(ac.alac, read_buffer, pDestBuffer, outputBytes); 159 | 160 | ac.current_sample_block = ac.current_sample_block + 1; 161 | outputBytes -= ac.offset * AlacGetBytesPerSample(ac); 162 | System.arraycopy(pDestBuffer, ac.offset, pDestBuffer, 0, outputBytes); 163 | ac.offset = 0; 164 | return outputBytes; 165 | 166 | } 167 | 168 | 169 | // Returns the sample rate of the specified ALAC file 170 | 171 | public static int AlacGetSampleRate(AlacContext ac) 172 | { 173 | if ( null != ac && ac.demux_res.sample_rate != 0) 174 | { 175 | return ac.demux_res.sample_rate; 176 | } 177 | else 178 | { 179 | return (44100); 180 | } 181 | } 182 | 183 | public static int AlacGetNumChannels(AlacContext ac) 184 | { 185 | if ( null != ac && ac.demux_res.num_channels != 0) 186 | { 187 | return ac.demux_res.num_channels; 188 | } 189 | else 190 | { 191 | return 2; 192 | } 193 | } 194 | 195 | public static int AlacGetBitsPerSample(AlacContext ac) 196 | { 197 | if (null != ac && ac.demux_res.sample_size != 0) 198 | { 199 | return ac.demux_res.sample_size; 200 | } 201 | else 202 | { 203 | return 16; 204 | } 205 | } 206 | 207 | 208 | public static int AlacGetBytesPerSample(AlacContext ac) 209 | { 210 | if ( null != ac && ac.demux_res.sample_size != 0) 211 | { 212 | return (int)Math.ceil(ac.demux_res.sample_size/8); 213 | } 214 | else 215 | { 216 | return 2; 217 | } 218 | } 219 | 220 | 221 | // Get total number of samples contained in the Apple Lossless file, or -1 if unknown 222 | 223 | public static int AlacGetNumSamples(AlacContext ac) 224 | { 225 | /* calculate output size */ 226 | int num_samples = 0; 227 | int thissample_duration; 228 | int thissample_bytesize = 0; 229 | SampleDuration sampleinfo = new SampleDuration(); 230 | int i; 231 | boolean error_found = false; 232 | int retval = 0; 233 | 234 | for (i = 0; i < ac.demux_res.sample_byte_size.length; i++) 235 | { 236 | thissample_duration = 0; 237 | thissample_bytesize = 0; 238 | 239 | retval = get_sample_info(ac.demux_res, i, sampleinfo); 240 | 241 | if(retval == 0) 242 | { 243 | return (-1); 244 | } 245 | thissample_duration = sampleinfo.sample_duration; 246 | thissample_bytesize = sampleinfo.sample_byte_size; 247 | 248 | num_samples += thissample_duration; 249 | } 250 | 251 | return (num_samples); 252 | } 253 | 254 | 255 | static int get_sample_info(DemuxResT demux_res, int samplenum, SampleDuration sampleinfo) 256 | { 257 | int duration_index_accum = 0; 258 | int duration_cur_index = 0; 259 | 260 | if (samplenum >= demux_res.sample_byte_size.length) 261 | { 262 | System.err.println("sample " + samplenum + " does not exist "); 263 | return 0; 264 | } 265 | 266 | if (demux_res.num_time_to_samples == 0) // was null 267 | { 268 | System.err.println("no time to samples"); 269 | return 0; 270 | } 271 | while ((demux_res.time_to_sample[duration_cur_index].sample_count + duration_index_accum) <= samplenum) 272 | { 273 | duration_index_accum += demux_res.time_to_sample[duration_cur_index].sample_count; 274 | duration_cur_index++; 275 | if (duration_cur_index >= demux_res.num_time_to_samples) 276 | { 277 | System.err.println("sample " + samplenum + " does not have a duration"); 278 | return 0; 279 | } 280 | } 281 | 282 | sampleinfo.sample_duration = demux_res.time_to_sample[duration_cur_index].sample_duration; 283 | sampleinfo.sample_byte_size = demux_res.sample_byte_size[samplenum]; 284 | 285 | return 1; 286 | } 287 | 288 | /** 289 | * sets position in pcm samples 290 | * @param ac alac context 291 | * @param position position in pcm samples to go to 292 | */ 293 | 294 | public static void AlacSetPosition(AlacContext ac, long position) { 295 | DemuxResT res = ac.demux_res; 296 | 297 | int current_position = 0; 298 | int current_sample = 0; 299 | SampleDuration sample_info = new SampleDuration(); 300 | for (int i = 0; i < res.stsc.length; i++) { 301 | ChunkInfo chunkInfo = res.stsc[i]; 302 | int last_chunk; 303 | 304 | if (i < res.stsc.length - 1) { 305 | last_chunk = res.stsc[i + 1].first_chunk; 306 | } else { 307 | last_chunk = res.stco.length; 308 | } 309 | 310 | for (int chunk = chunkInfo.first_chunk; chunk <= last_chunk; chunk++) { 311 | int pos = res.stco[chunk - 1]; 312 | int sample_count = chunkInfo.samples_per_chunk; 313 | while (sample_count > 0) { 314 | int ret = get_sample_info(res, current_sample, sample_info); 315 | if (ret == 0) return; 316 | current_position += sample_info.sample_duration; 317 | if (position < current_position) { 318 | ac.input_stream.seek(pos); 319 | ac.current_sample_block = current_sample; 320 | ac.offset = 321 | (int) (position - (current_position - sample_info.sample_duration)) 322 | * AlacGetNumChannels(ac); 323 | return; 324 | } 325 | pos += sample_info.sample_byte_size; 326 | current_sample++; 327 | sample_count--; 328 | } 329 | } 330 | } 331 | } 332 | } -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/ChunkInfo.java: -------------------------------------------------------------------------------- 1 | package com.beatofthedrum.alacdecoder; 2 | 3 | /** 4 | * Author: Denis Tulskiy 5 | * Date: 4/9/11 6 | */ 7 | public class ChunkInfo { 8 | int first_chunk; 9 | int samples_per_chunk; 10 | int sample_desc_index; 11 | } 12 | -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/DecodeResult.java: -------------------------------------------------------------------------------- 1 | package com.beatofthedrum.alacdecoder; 2 | 3 | /** 4 | * Author: Denis Tulskiy 5 | * Date: 4/9/11 6 | */ 7 | public class DecodeResult { 8 | public int bytesUnpacked; 9 | public int offset; 10 | } 11 | -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/Defines.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** Defines.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | 12 | package com.beatofthedrum.alacdecoder; 13 | 14 | class Defines 15 | { 16 | static int RICE_THRESHOLD = 8; 17 | } -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/DemuxResT.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DemuxResT.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | package com.beatofthedrum.alacdecoder; 12 | 13 | class DemuxResT 14 | { 15 | public int format_read; 16 | 17 | public int num_channels; 18 | public int sample_size; 19 | public int sample_rate; 20 | public int format; 21 | public int[] buf = new int[1024*80]; 22 | 23 | public SampleInfo[] time_to_sample = new SampleInfo[16]; 24 | public int num_time_to_samples; 25 | 26 | public int[] sample_byte_size; 27 | 28 | public int codecdata_len; 29 | 30 | public int[] codecdata = new int[1024]; 31 | 32 | public int[] stco; 33 | public ChunkInfo[] stsc; 34 | 35 | public int mdat_len; 36 | 37 | public DemuxResT() 38 | { 39 | // not sure how many of these I need, so make 16 40 | for (int i = 0; i < 16; i++) 41 | { 42 | time_to_sample[i] = new SampleInfo(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/DemuxUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** DemuxUtils.java 3 | ** 4 | ** Copyright (c) 2011-2014 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | 12 | package com.beatofthedrum.alacdecoder; 13 | 14 | 15 | class DemuxUtils 16 | { 17 | public static int MakeFourCC(int ch0, int ch1, int ch2, int ch3) 18 | { 19 | return ( ((ch0) << 24)| ((ch1) << 16)| ((ch2) << 8) | ((ch3)) ); 20 | } 21 | 22 | public static int MakeFourCC32(int ch0, int ch1, int ch2, int ch3) 23 | { 24 | int retval = 0; 25 | int tmp = ch0; 26 | 27 | retval = tmp << 24; 28 | 29 | tmp = ch1; 30 | 31 | retval = retval | (tmp << 16); 32 | tmp = ch2; 33 | 34 | retval = retval | (tmp << 8); 35 | tmp = ch3; 36 | 37 | retval = retval | tmp; 38 | 39 | return (retval); 40 | } 41 | 42 | public static String SplitFourCC(int code) 43 | { 44 | String retstr; 45 | char c1; 46 | char c2; 47 | char c3; 48 | char c4; 49 | 50 | c1 = (char)((code >> 24) & 0xFF); 51 | c2 = (char)((code >> 16) & 0xFF); 52 | c3 = (char)((code >> 8) & 0xFF); 53 | c4 = (char)(code & 0xFF); 54 | retstr = c1 + " " + c2 + " " + c3 + " " + c4; 55 | 56 | return retstr; 57 | 58 | } 59 | 60 | 61 | public static int qtmovie_read(java.io.DataInputStream file, QTMovieT qtmovie, DemuxResT demux_res) 62 | { 63 | int found_moov = 0; 64 | int found_mdat = 0; 65 | 66 | /* construct the stream */ 67 | qtmovie.qtstream.stream = file; 68 | 69 | qtmovie.res = demux_res; 70 | 71 | // reset demux_res TODO 72 | 73 | /* read the chunks */ 74 | while (true) 75 | { 76 | int chunk_len; 77 | int chunk_id = 0; 78 | 79 | try 80 | { 81 | chunk_len = StreamUtils.stream_read_uint32(qtmovie.qtstream); 82 | } 83 | catch (Exception e) 84 | { 85 | System.err.println("(top) error reading chunk_len - possibly number too large"); 86 | chunk_len = 1; 87 | } 88 | 89 | if (StreamUtils.stream_eof(qtmovie.qtstream) != 0) 90 | { 91 | return 0; 92 | } 93 | 94 | if (chunk_len == 1) 95 | { 96 | System.err.println("need 64bit support"); 97 | return 0; 98 | } 99 | chunk_id = StreamUtils.stream_read_uint32(qtmovie.qtstream); 100 | 101 | if(chunk_id == MakeFourCC32(102,116,121,112)) // fourcc equals ftyp 102 | { 103 | read_chunk_ftyp(qtmovie, chunk_len); 104 | } 105 | else if(chunk_id == MakeFourCC32(109,111,111,118) ) // fourcc equals moov 106 | { 107 | if (read_chunk_moov(qtmovie, chunk_len) == 0) 108 | return 0; // failed to read moov, can't do anything 109 | if (found_mdat != 0) 110 | { 111 | return set_saved_mdat(qtmovie); 112 | } 113 | found_moov = 1; 114 | } 115 | /* if we hit mdat before we've found moov, record the position 116 | * and move on. We can then come back to mdat later. 117 | * This presumes the stream supports seeking backwards. 118 | */ 119 | else if(chunk_id == MakeFourCC32(109,100,97,116)) // fourcc equals mdat 120 | { 121 | int not_found_moov = 0; 122 | if(found_moov==0) 123 | not_found_moov = 1; 124 | read_chunk_mdat(qtmovie, chunk_len, not_found_moov); 125 | if (found_moov != 0) 126 | { 127 | return 1; 128 | } 129 | found_mdat = 1; 130 | } 131 | /* these following atoms can be skipped !!!! */ 132 | else if(chunk_id == MakeFourCC32(102,114,101,101)) // fourcc equals free 133 | { 134 | StreamUtils.stream_skip(qtmovie.qtstream, chunk_len - 8); // FIXME not 8 135 | } 136 | else if(chunk_id == MakeFourCC32(106,117,110,107)) // fourcc equals junk 137 | { 138 | StreamUtils.stream_skip(qtmovie.qtstream, chunk_len - 8); // FIXME not 8 139 | } 140 | else 141 | { 142 | System.err.println("(top) unknown chunk id: " + SplitFourCC(chunk_id)); 143 | return 0; 144 | } 145 | } 146 | } 147 | 148 | 149 | /* chunk handlers */ 150 | static void read_chunk_ftyp(QTMovieT qtmovie, int chunk_len) 151 | { 152 | int type = 0; 153 | int minor_ver = 0; 154 | int size_remaining = chunk_len - 8; // FIXME: can't hardcode 8, size may be 64bit 155 | 156 | type = StreamUtils.stream_read_uint32(qtmovie.qtstream); 157 | size_remaining-=4; 158 | 159 | if(type != MakeFourCC32(77,52,65,32) ) // "M4A " ascii values 160 | { 161 | System.err.println("not M4A file"); 162 | return; 163 | } 164 | minor_ver = StreamUtils.stream_read_uint32(qtmovie.qtstream); 165 | size_remaining-=4; 166 | 167 | /* compatible brands */ 168 | while (size_remaining != 0) 169 | { 170 | /* unused */ 171 | /*fourcc_t cbrand =*/ 172 | StreamUtils.stream_read_uint32(qtmovie.qtstream); 173 | size_remaining-=4; 174 | } 175 | } 176 | 177 | static void read_chunk_tkhd(QTMovieT qtmovie, int chunk_len) 178 | { 179 | /* don't need anything from here atm, skip */ 180 | int size_remaining = chunk_len - 8; // FIXME WRONG 181 | 182 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 183 | } 184 | 185 | static void read_chunk_mdhd(QTMovieT qtmovie, int chunk_len) 186 | { 187 | /* don't need anything from here atm, skip */ 188 | int size_remaining = chunk_len - 8; // FIXME WRONG 189 | 190 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 191 | } 192 | 193 | static void read_chunk_edts(QTMovieT qtmovie, int chunk_len) 194 | { 195 | /* don't need anything from here atm, skip */ 196 | int size_remaining = chunk_len - 8; // FIXME WRONG 197 | 198 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 199 | } 200 | 201 | static void read_chunk_elst(QTMovieT qtmovie, int chunk_len) 202 | { 203 | /* don't need anything from here atm, skip */ 204 | int size_remaining = chunk_len - 8; // FIXME WRONG 205 | 206 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 207 | } 208 | 209 | /* media handler inside mdia */ 210 | static void read_chunk_hdlr(QTMovieT qtmovie, int chunk_len) 211 | { 212 | int comptype = 0; 213 | int compsubtype = 0; 214 | int size_remaining = chunk_len - 8; // FIXME WRONG 215 | 216 | int strlen; 217 | 218 | /* version */ 219 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 220 | size_remaining -= 1; 221 | /* flags */ 222 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 223 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 224 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 225 | size_remaining -= 3; 226 | 227 | /* component type */ 228 | comptype = StreamUtils.stream_read_uint32(qtmovie.qtstream); 229 | compsubtype = StreamUtils.stream_read_uint32(qtmovie.qtstream); 230 | size_remaining -= 8; 231 | 232 | /* component manufacturer */ 233 | StreamUtils.stream_read_uint32(qtmovie.qtstream); 234 | size_remaining -= 4; 235 | 236 | /* flags */ 237 | StreamUtils.stream_read_uint32(qtmovie.qtstream); 238 | StreamUtils.stream_read_uint32(qtmovie.qtstream); 239 | size_remaining -= 8; 240 | 241 | /* name */ 242 | strlen = StreamUtils.stream_read_uint8(qtmovie.qtstream); 243 | 244 | /* 245 | ** rewrote this to handle case where we actually read more than required 246 | ** so here we work out how much we need to read first 247 | */ 248 | 249 | size_remaining -= 1; 250 | 251 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 252 | } 253 | 254 | static int read_chunk_stsd(QTMovieT qtmovie, int chunk_len) 255 | { 256 | int i; 257 | int numentries = 0; 258 | int size_remaining = chunk_len - 8; // FIXME WRONG 259 | 260 | /* version */ 261 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 262 | size_remaining -= 1; 263 | /* flags */ 264 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 265 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 266 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 267 | size_remaining -= 3; 268 | 269 | try 270 | { 271 | numentries = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 272 | } 273 | catch (Exception e) 274 | { 275 | System.err.println("(read_chunk_stsd) error reading numentries - possibly number too large"); 276 | numentries = 0; 277 | } 278 | 279 | 280 | size_remaining -= 4; 281 | 282 | if (numentries != 1) 283 | { 284 | System.err.println("only expecting one entry in sample description atom!"); 285 | return 0; 286 | } 287 | 288 | for (i = 0; i < numentries; i++) 289 | { 290 | /* parse the alac atom contained within the stsd atom */ 291 | int entry_size; 292 | int version; 293 | 294 | int entry_remaining; 295 | 296 | entry_size = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 297 | qtmovie.res.format = StreamUtils.stream_read_uint32(qtmovie.qtstream); 298 | entry_remaining = entry_size; 299 | entry_remaining -= 8; 300 | 301 | if(qtmovie.res.format != MakeFourCC32(97,108,97,99) ) // "alac" ascii values 302 | { 303 | System.err.println("(read_chunk_stsd) error reading description atom - expecting alac, got " + SplitFourCC(qtmovie.res.format)); 304 | return 0; 305 | } 306 | 307 | /* sound info: */ 308 | 309 | StreamUtils.stream_skip(qtmovie.qtstream, 6); // reserved 310 | entry_remaining -= 6; 311 | 312 | version = StreamUtils.stream_read_uint16(qtmovie.qtstream); 313 | 314 | if (version != 1) 315 | System.err.println("unknown version??"); 316 | entry_remaining -= 2; 317 | 318 | /* revision level */ 319 | StreamUtils.stream_read_uint16(qtmovie.qtstream); 320 | /* vendor */ 321 | StreamUtils.stream_read_uint32(qtmovie.qtstream); 322 | entry_remaining -= 6; 323 | 324 | /* EH?? spec doesn't say theres an extra 16 bits here.. but there is! */ 325 | StreamUtils.stream_read_uint16(qtmovie.qtstream); 326 | entry_remaining -= 2; 327 | 328 | /* skip 4 - this is the top level num of channels and bits per sample */ 329 | StreamUtils.stream_skip(qtmovie.qtstream, 4); 330 | entry_remaining -= 4; 331 | 332 | /* compression id */ 333 | StreamUtils.stream_read_uint16(qtmovie.qtstream); 334 | /* packet size */ 335 | StreamUtils.stream_read_uint16(qtmovie.qtstream); 336 | entry_remaining -= 4; 337 | 338 | /* skip 4 - this is the top level sample rate */ 339 | StreamUtils.stream_skip(qtmovie.qtstream, 4); 340 | entry_remaining -= 4; 341 | 342 | /* remaining is codec data */ 343 | 344 | /* 12 = audio format atom, 8 = padding */ 345 | qtmovie.res.codecdata_len = entry_remaining + 12 + 8; 346 | 347 | if(qtmovie.res.codecdata_len > qtmovie.res.codecdata.length) 348 | { 349 | System.err.println("(read_chunk_stsd) unexpected codec data length read from atom " + qtmovie.res.codecdata_len); 350 | return 0; 351 | } 352 | 353 | for (int count = 0; count < qtmovie.res.codecdata_len; count++) 354 | { 355 | qtmovie.res.codecdata[count] = 0; 356 | } 357 | 358 | /* audio format atom */ 359 | qtmovie.res.codecdata[0] = 0x0c000000; 360 | qtmovie.res.codecdata[1] = MakeFourCC(97,109,114,102); // "amrf" ascii values 361 | qtmovie.res.codecdata[2] = MakeFourCC(99,97,108,97); // "cala" ascii values 362 | 363 | StreamUtils.stream_read(qtmovie.qtstream, entry_remaining, qtmovie.res.codecdata, 12); // codecdata buffer should be +12 364 | entry_remaining -= entry_remaining; 365 | 366 | /* We need to read the bits per sample, number of channels and sample rate from the codec data i.e. the alac atom within 367 | ** the stsd atom the 'alac' atom contains a number of pieces of information which we can skip just now, its processed later 368 | ** in the alac_set_info() method. This atom contains the following information 369 | ** 370 | ** samples_per_frame 371 | ** compatible version 372 | ** bits per sample 373 | ** history multiplier 374 | ** initial history 375 | ** maximum K 376 | ** channels 377 | ** max run 378 | ** max coded frame size 379 | ** bitrate 380 | ** sample rate 381 | */ 382 | int ptrIndex = 29; // position of bits per sample 383 | 384 | qtmovie.res.sample_size = (qtmovie.res.codecdata[ptrIndex] & 0xff); 385 | 386 | ptrIndex = 33; // position of num of channels 387 | 388 | qtmovie.res.num_channels = (qtmovie.res.codecdata[ptrIndex] & 0xff); 389 | 390 | ptrIndex = 44; // position of sample rate within codec data buffer 391 | 392 | qtmovie.res.sample_rate = (((qtmovie.res.codecdata[ptrIndex] & 0xff) << 24) | ((qtmovie.res.codecdata[ptrIndex+1] & 0xff) << 16) | ((qtmovie.res.codecdata[ptrIndex+2] & 0xff) << 8) | (qtmovie.res.codecdata[ptrIndex+3] & 0xff)); 393 | 394 | if (entry_remaining != 0) // was comparing to null 395 | StreamUtils.stream_skip(qtmovie.qtstream, entry_remaining); 396 | 397 | qtmovie.res.format_read = 1; 398 | if(qtmovie.res.format != MakeFourCC32(97,108,97,99) ) // "alac" ascii values 399 | { 400 | return 0; 401 | } 402 | } 403 | 404 | return 1; 405 | } 406 | 407 | static void read_chunk_stts(QTMovieT qtmovie, int chunk_len) 408 | { 409 | int i; 410 | int numentries = 0; 411 | int size_remaining = chunk_len - 8; // FIXME WRONG 412 | 413 | /* version */ 414 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 415 | size_remaining -= 1; 416 | /* flags */ 417 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 418 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 419 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 420 | size_remaining -= 3; 421 | 422 | try 423 | { 424 | numentries = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 425 | } 426 | catch (Exception e) 427 | { 428 | System.err.println("(read_chunk_stts) error reading numentries - possibly number too large"); 429 | numentries = 0; 430 | } 431 | 432 | size_remaining -= 4; 433 | 434 | qtmovie.res.num_time_to_samples = numentries; 435 | 436 | for (i = 0; i < numentries; i++) 437 | { 438 | qtmovie.res.time_to_sample[i].sample_count = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 439 | qtmovie.res.time_to_sample[i].sample_duration = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 440 | size_remaining -= 8; 441 | } 442 | 443 | if (size_remaining != 0) 444 | { 445 | System.err.println("(read_chunk_stts) size remaining?"); 446 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 447 | } 448 | } 449 | 450 | static void read_chunk_stsz(QTMovieT qtmovie, int chunk_len) 451 | { 452 | int i; 453 | int numentries = 0; 454 | int uniform_size = 0; 455 | int size_remaining = chunk_len - 8; // FIXME WRONG 456 | 457 | /* version */ 458 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 459 | size_remaining -= 1; 460 | /* flags */ 461 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 462 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 463 | StreamUtils.stream_read_uint8(qtmovie.qtstream); 464 | size_remaining -= 3; 465 | 466 | /* default sample size */ 467 | uniform_size = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 468 | if (uniform_size != 0) 469 | { 470 | /* 471 | ** Normally files have intiable sample sizes, this handles the case where 472 | ** they are all the same size 473 | */ 474 | 475 | int uniform_num = 0; 476 | 477 | uniform_num = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 478 | 479 | qtmovie.res.sample_byte_size = new int[uniform_num]; 480 | 481 | for (i = 0; i < uniform_num; i++) 482 | { 483 | qtmovie.res.sample_byte_size[i] = uniform_size; 484 | } 485 | size_remaining -= 4; 486 | return; 487 | } 488 | size_remaining -= 4; 489 | 490 | try 491 | { 492 | numentries = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 493 | } 494 | catch (Exception e) 495 | { 496 | System.err.println("(read_chunk_stsz) error reading numentries - possibly number too large"); 497 | numentries = 0; 498 | } 499 | 500 | size_remaining -= 4; 501 | 502 | qtmovie.res.sample_byte_size = new int[numentries]; 503 | 504 | for (i = 0; i < numentries; i++) 505 | { 506 | qtmovie.res.sample_byte_size[i] = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 507 | 508 | size_remaining -= 4; 509 | } 510 | 511 | if (size_remaining != 0) 512 | { 513 | System.err.println("(read_chunk_stsz) size remaining?"); 514 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 515 | } 516 | } 517 | 518 | static int read_chunk_stbl(QTMovieT qtmovie, int chunk_len) 519 | { 520 | int size_remaining = chunk_len - 8; // FIXME WRONG 521 | 522 | while (size_remaining != 0) 523 | { 524 | int sub_chunk_len; 525 | int sub_chunk_id = 0; 526 | 527 | try 528 | { 529 | sub_chunk_len = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 530 | } 531 | catch (Exception e) 532 | { 533 | System.err.println("(read_chunk_stbl) error reading sub_chunk_len - possibly number too large"); 534 | sub_chunk_len = 0; 535 | } 536 | 537 | if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) 538 | { 539 | System.err.println("strange size for chunk inside stbl " + sub_chunk_len + " (remaining: " + size_remaining + ")"); 540 | return 0; 541 | } 542 | 543 | sub_chunk_id = StreamUtils.stream_read_uint32(qtmovie.qtstream); 544 | 545 | if(sub_chunk_id == MakeFourCC32(115,116,115,100) ) // fourcc equals stsd 546 | { 547 | if (read_chunk_stsd(qtmovie, sub_chunk_len) == 0) 548 | return 0; 549 | } 550 | else if(sub_chunk_id == MakeFourCC32(115,116,116,115) ) // fourcc equals stts 551 | { 552 | read_chunk_stts(qtmovie, sub_chunk_len); 553 | } 554 | else if(sub_chunk_id == MakeFourCC32(115,116,115,122) ) // fourcc equals stsz 555 | { 556 | read_chunk_stsz(qtmovie, sub_chunk_len); 557 | } 558 | else if(sub_chunk_id == MakeFourCC32(115,116,115,99) ) // fourcc equals stsc 559 | { 560 | read_chunk_stsc(qtmovie, sub_chunk_len); 561 | } 562 | else if(sub_chunk_id == MakeFourCC32(115,116,99,111) ) // fourcc equals stco 563 | { 564 | read_chunk_stco(qtmovie, sub_chunk_len); 565 | } 566 | else 567 | { 568 | System.err.println("(stbl) unknown chunk id: " + SplitFourCC(sub_chunk_id)); 569 | return 0; 570 | } 571 | 572 | size_remaining -= sub_chunk_len; 573 | } 574 | 575 | return 1; 576 | } 577 | 578 | /* 579 | * chunk to offset box 580 | */ 581 | private static void read_chunk_stco(QTMovieT qtmovie, int sub_chunk_len) { 582 | //skip header and size 583 | MyStream stream = qtmovie.qtstream; 584 | StreamUtils.stream_skip(stream, 4); 585 | 586 | int num_entries = StreamUtils.stream_read_uint32(stream); 587 | 588 | qtmovie.res.stco = new int[num_entries]; 589 | for (int i = 0; i < num_entries; i++) { 590 | qtmovie.res.stco[i] = StreamUtils.stream_read_uint32(stream); 591 | } 592 | } 593 | 594 | /* 595 | * sample to chunk box 596 | */ 597 | private static void read_chunk_stsc(QTMovieT qtmovie, int sub_chunk_len) { 598 | //skip header and size 599 | MyStream stream = qtmovie.qtstream; 600 | //skip version and other junk 601 | StreamUtils.stream_skip(stream, 4); 602 | int num_entries = StreamUtils.stream_read_uint32(stream); 603 | qtmovie.res.stsc = new ChunkInfo[num_entries]; 604 | for (int i = 0; i < num_entries; i++) { 605 | ChunkInfo entry = new ChunkInfo(); 606 | entry.first_chunk = StreamUtils.stream_read_uint32(stream); 607 | entry.samples_per_chunk = StreamUtils.stream_read_uint32(stream); 608 | entry.sample_desc_index = StreamUtils.stream_read_uint32(stream); 609 | qtmovie.res.stsc[i] = entry; 610 | } 611 | } 612 | 613 | static int read_chunk_minf(QTMovieT qtmovie, int chunk_len) 614 | { 615 | int dinf_size; 616 | int stbl_size; 617 | int size_remaining = chunk_len - 8; // FIXME WRONG 618 | int media_info_size; 619 | 620 | /**** SOUND HEADER CHUNK ****/ 621 | 622 | try 623 | { 624 | media_info_size = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 625 | } 626 | catch (Exception e) 627 | { 628 | System.err.println("(read_chunk_minf) error reading media_info_size - possibly number too large"); 629 | media_info_size = 0; 630 | } 631 | 632 | if (media_info_size != 16) 633 | { 634 | System.err.println("unexpected size in media info\n"); 635 | return 0; 636 | } 637 | if (StreamUtils.stream_read_uint32(qtmovie.qtstream) != MakeFourCC32(115,109,104,100)) // "smhd" ascii values 638 | { 639 | System.err.println("not a sound header! can't handle this."); 640 | return 0; 641 | } 642 | /* now skip the rest */ 643 | StreamUtils.stream_skip(qtmovie.qtstream, 16 - 8); 644 | size_remaining -= 16; 645 | /****/ 646 | 647 | /**** DINF CHUNK ****/ 648 | 649 | try 650 | { 651 | dinf_size = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 652 | } 653 | catch (Exception e) 654 | { 655 | System.err.println("(read_chunk_minf) error reading dinf_size - possibly number too large"); 656 | dinf_size = 0; 657 | } 658 | 659 | if (StreamUtils.stream_read_uint32(qtmovie.qtstream) != MakeFourCC32(100,105,110,102)) // "dinf" ascii values 660 | { 661 | System.err.println("expected dinf, didn't get it."); 662 | return 0; 663 | } 664 | /* skip it */ 665 | StreamUtils.stream_skip(qtmovie.qtstream, dinf_size - 8); 666 | size_remaining -= dinf_size; 667 | /****/ 668 | 669 | 670 | /**** SAMPLE TABLE ****/ 671 | try 672 | { 673 | stbl_size = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 674 | } 675 | catch (Exception e) 676 | { 677 | System.err.println("(read_chunk_minf) error reading stbl_size - possibly number too large"); 678 | stbl_size = 0; 679 | } 680 | 681 | if (StreamUtils.stream_read_uint32(qtmovie.qtstream) != MakeFourCC32(115,116,98,108)) // "stbl" ascii values 682 | { 683 | System.err.println("expected stbl, didn't get it."); 684 | return 0; 685 | } 686 | if (read_chunk_stbl(qtmovie, stbl_size) == 0) 687 | return 0; 688 | size_remaining -= stbl_size; 689 | 690 | if (size_remaining != 0) 691 | { 692 | System.err.println("(read_chunk_minf) - size remaining?"); 693 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 694 | } 695 | 696 | return 1; 697 | } 698 | 699 | static int read_chunk_mdia(QTMovieT qtmovie, int chunk_len) 700 | { 701 | int size_remaining = chunk_len - 8; // FIXME WRONG 702 | 703 | while (size_remaining != 0) 704 | { 705 | int sub_chunk_len; 706 | int sub_chunk_id = 0; 707 | 708 | try 709 | { 710 | sub_chunk_len = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 711 | } 712 | catch (Exception e) 713 | { 714 | System.err.println("(read_chunk_mdia) error reading sub_chunk_len - possibly number too large"); 715 | sub_chunk_len = 0; 716 | } 717 | 718 | if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) 719 | { 720 | System.err.println("strange size for chunk inside mdia\n"); 721 | return 0; 722 | } 723 | 724 | sub_chunk_id = StreamUtils.stream_read_uint32(qtmovie.qtstream); 725 | 726 | if(sub_chunk_id == MakeFourCC32(109,100,104,100) ) // fourcc equals mdhd 727 | { 728 | read_chunk_mdhd(qtmovie, sub_chunk_len); 729 | } 730 | else if(sub_chunk_id == MakeFourCC32(104,100,108,114) ) // fourcc equals hdlr 731 | { 732 | read_chunk_hdlr(qtmovie, sub_chunk_len); 733 | } 734 | else if(sub_chunk_id == MakeFourCC32(109,105,110,102) ) // fourcc equals minf 735 | { 736 | if (read_chunk_minf(qtmovie, sub_chunk_len) == 0) 737 | return 0; 738 | } 739 | else 740 | { 741 | System.err.println("(mdia) unknown chunk id: " + SplitFourCC(sub_chunk_id)); 742 | return 0; 743 | } 744 | 745 | size_remaining -= sub_chunk_len; 746 | } 747 | 748 | return 1; 749 | } 750 | 751 | /* 'trak' - a movie track - contains other atoms */ 752 | static int read_chunk_trak(QTMovieT qtmovie, int chunk_len) 753 | { 754 | int size_remaining = chunk_len - 8; // FIXME WRONG 755 | 756 | while (size_remaining != 0) 757 | { 758 | int sub_chunk_len; 759 | int sub_chunk_id = 0; 760 | 761 | try 762 | { 763 | sub_chunk_len = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 764 | } 765 | catch (Exception e) 766 | { 767 | System.err.println("(read_chunk_trak) error reading sub_chunk_len - possibly number too large"); 768 | sub_chunk_len = 0; 769 | } 770 | 771 | if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) 772 | { 773 | System.err.println("strange size for chunk inside trak"); 774 | return 0; 775 | } 776 | 777 | sub_chunk_id = StreamUtils.stream_read_uint32(qtmovie.qtstream); 778 | 779 | if(sub_chunk_id == MakeFourCC32(116,107,104,100) ) // fourcc equals tkhd 780 | { 781 | read_chunk_tkhd(qtmovie, sub_chunk_len); 782 | } 783 | else if(sub_chunk_id == MakeFourCC32(109,100,105,97) ) // fourcc equals mdia 784 | { 785 | if (read_chunk_mdia(qtmovie, sub_chunk_len) == 0) 786 | return 0; 787 | } 788 | else if(sub_chunk_id == MakeFourCC32(101,100,116,115) ) // fourcc equals edts 789 | { 790 | read_chunk_edts(qtmovie, sub_chunk_len); 791 | } 792 | else 793 | { 794 | System.err.println("(trak) unknown chunk id: " + SplitFourCC(sub_chunk_id)); 795 | return 0; 796 | } 797 | 798 | size_remaining -= sub_chunk_len; 799 | } 800 | 801 | return 1; 802 | } 803 | 804 | /* 'mvhd' movie header atom */ 805 | static void read_chunk_mvhd(QTMovieT qtmovie, int chunk_len) 806 | { 807 | /* don't need anything from here atm, skip */ 808 | int size_remaining = chunk_len - 8; // FIXME WRONG 809 | 810 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 811 | } 812 | 813 | /* 'udta' user data.. contains tag info */ 814 | static void read_chunk_udta(QTMovieT qtmovie, int chunk_len) 815 | { 816 | /* don't need anything from here atm, skip */ 817 | int size_remaining = chunk_len - 8; // FIXME WRONG 818 | 819 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 820 | } 821 | 822 | /* 'iods' */ 823 | static void read_chunk_iods(QTMovieT qtmovie, int chunk_len) 824 | { 825 | /* don't need anything from here atm, skip */ 826 | int size_remaining = chunk_len - 8; // FIXME WRONG 827 | 828 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 829 | } 830 | 831 | /* 'moov' movie atom - contains other atoms */ 832 | static int read_chunk_moov(QTMovieT qtmovie, int chunk_len) 833 | { 834 | int size_remaining = chunk_len - 8; // FIXME WRONG 835 | 836 | while (size_remaining != 0) 837 | { 838 | int sub_chunk_len; 839 | int sub_chunk_id = 0; 840 | 841 | try 842 | { 843 | sub_chunk_len = (StreamUtils.stream_read_uint32(qtmovie.qtstream)); 844 | } 845 | catch (Exception e) 846 | { 847 | System.err.println("(read_chunk_moov) error reading sub_chunk_len - possibly number too large"); 848 | sub_chunk_len = 0; 849 | } 850 | 851 | if (sub_chunk_len <= 1 || sub_chunk_len > size_remaining) 852 | { 853 | System.err.println("strange size for chunk inside moov"); 854 | return 0; 855 | } 856 | 857 | sub_chunk_id = StreamUtils.stream_read_uint32(qtmovie.qtstream); 858 | 859 | if(sub_chunk_id == MakeFourCC32(109,118,104,100) ) // fourcc equals mvhd 860 | { 861 | read_chunk_mvhd(qtmovie, sub_chunk_len); 862 | } 863 | else if(sub_chunk_id == MakeFourCC32(116,114,97,107) ) // fourcc equals trak 864 | { 865 | if (read_chunk_trak(qtmovie, sub_chunk_len) == 0) 866 | return 0; 867 | } 868 | else if(sub_chunk_id == MakeFourCC32(117,100,116,97) ) // fourcc equals udta 869 | { 870 | read_chunk_udta(qtmovie, sub_chunk_len); 871 | } 872 | else if(sub_chunk_id == MakeFourCC32(101,108,115,116) ) // fourcc equals elst 873 | { 874 | read_chunk_elst(qtmovie, sub_chunk_len); 875 | } 876 | else if(sub_chunk_id == MakeFourCC32(105,111,100,115) ) // fourcc equals iods 877 | { 878 | read_chunk_iods(qtmovie, sub_chunk_len); 879 | } 880 | else if(sub_chunk_id == MakeFourCC32(102,114,101,101)) // fourcc equals free 881 | { 882 | StreamUtils.stream_skip(qtmovie.qtstream, sub_chunk_len - 8); // FIXME not 8 883 | } 884 | else 885 | { 886 | System.err.println("(moov) unknown chunk id: " + SplitFourCC(sub_chunk_id)); 887 | return 0; 888 | } 889 | 890 | size_remaining -= sub_chunk_len; 891 | } 892 | 893 | return 1; 894 | } 895 | 896 | static void read_chunk_mdat(QTMovieT qtmovie, int chunk_len, int skip_mdat) 897 | { 898 | int size_remaining = chunk_len - 8; // FIXME WRONG 899 | 900 | if (size_remaining == 0) 901 | return; 902 | 903 | qtmovie.res.mdat_len = size_remaining; 904 | if (skip_mdat != 0) 905 | { 906 | qtmovie.saved_mdat_pos = StreamUtils.stream_tell(qtmovie.qtstream); 907 | 908 | StreamUtils.stream_skip(qtmovie.qtstream, size_remaining); 909 | } 910 | } 911 | 912 | static int set_saved_mdat(QTMovieT qtmovie) 913 | { 914 | // returns as follows 915 | // 1 - all ok 916 | // 2 - do not have valid saved mdat pos 917 | // 3 - have valid saved mdat pos, but cannot seek there - need to close/reopen stream 918 | 919 | if (qtmovie.saved_mdat_pos == -1) 920 | { 921 | System.err.println("stream contains mdat before moov but is not seekable"); 922 | return 2; 923 | } 924 | 925 | if (StreamUtils.stream_setpos(qtmovie.qtstream, qtmovie.saved_mdat_pos) != 0) 926 | { 927 | return 3; 928 | } 929 | 930 | return 1; 931 | } 932 | } 933 | 934 | 935 | -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/LeadingZeros.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** LeadingZeros.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | 12 | package com.beatofthedrum.alacdecoder; 13 | 14 | class LeadingZeros 15 | { 16 | public int curbyte = 0; 17 | public int output = 0; 18 | } -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/MyStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** MyStream.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | 12 | package com.beatofthedrum.alacdecoder; 13 | 14 | class MyStream 15 | { 16 | public java.io.DataInputStream stream; 17 | public int currentPos = 0; 18 | public byte[] read_buf = new byte[8]; 19 | } -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/QTMovieT.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** QTMovieT.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | 12 | package com.beatofthedrum.alacdecoder; 13 | 14 | class QTMovieT 15 | { 16 | public MyStream qtstream; 17 | public DemuxResT res; 18 | public int saved_mdat_pos; 19 | 20 | public QTMovieT() 21 | { 22 | saved_mdat_pos = 0; 23 | qtstream = new MyStream(); 24 | } 25 | } -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/SampleDuration.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** SampleDuration.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | 12 | package com.beatofthedrum.alacdecoder; 13 | 14 | class SampleDuration 15 | { 16 | int sample_byte_size = 0; 17 | int sample_duration = 0; 18 | } 19 | -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/SampleInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** SampleInfo.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | 12 | package com.beatofthedrum.alacdecoder; 13 | 14 | class SampleInfo 15 | { 16 | public int sample_count = 0; 17 | public int sample_duration = 0; 18 | } -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/StreamUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** StreamUtils.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | 12 | package com.beatofthedrum.alacdecoder; 13 | 14 | class StreamUtils 15 | { 16 | public static void stream_read(MyStream mystream, int size, int[] buf, int startPos) { 17 | byte[] byteBuf = new byte[size]; 18 | int bytes_read = stream_read(mystream, size, byteBuf, 0); 19 | for(int i=0; i < bytes_read; i++) { 20 | buf[startPos + i] = byteBuf[i]; 21 | } 22 | } 23 | 24 | public static int stream_read(MyStream mystream, int size, byte[] buf, int startPos) 25 | { 26 | int bytes_read = 0; 27 | 28 | try 29 | { 30 | bytes_read = mystream.stream.read(buf, startPos, size); 31 | } 32 | catch (Exception err) 33 | { 34 | System.err.println("stream_read: exception thrown: " + err); 35 | } 36 | mystream.currentPos = mystream.currentPos + bytes_read; 37 | return bytes_read; 38 | } 39 | 40 | public static int stream_read_uint32(MyStream mystream) 41 | { 42 | int v = 0; 43 | int tmp = 0; 44 | byte[] bytebuf = mystream.read_buf; 45 | int bytes_read = 0; 46 | 47 | try 48 | { 49 | bytes_read = mystream.stream.read(bytebuf, 0, 4); 50 | mystream.currentPos = mystream.currentPos + bytes_read; 51 | tmp = (bytebuf[0] & 0xff); 52 | 53 | v = tmp << 24; 54 | tmp = (bytebuf[1] & 0xff); 55 | 56 | v = v | (tmp << 16); 57 | tmp = (bytebuf[2] & 0xff); 58 | 59 | v = v | (tmp << 8); 60 | 61 | tmp = (bytebuf[3] & 0xff); 62 | v = v | tmp; 63 | 64 | } 65 | catch (Exception err) 66 | { 67 | System.err.println("stream_read_uint32: exception thrown: " + err); 68 | } 69 | 70 | return v; 71 | } 72 | 73 | public static int stream_read_int16(MyStream mystream) 74 | { 75 | int v = 0; 76 | try 77 | { 78 | v = mystream.stream.readShort(); 79 | mystream.currentPos = mystream.currentPos + 2; 80 | } 81 | catch (Exception err) 82 | { 83 | } 84 | 85 | return v; 86 | } 87 | public static int stream_read_uint16(MyStream mystream) 88 | { 89 | int v = 0; 90 | int tmp = 0; 91 | byte[] bytebuf = mystream.read_buf; 92 | int bytes_read = 0; 93 | 94 | try 95 | { 96 | bytes_read = mystream.stream.read(bytebuf, 0, 2); 97 | mystream.currentPos = mystream.currentPos + bytes_read; 98 | tmp = (bytebuf[0] & 0xff); 99 | v = tmp << 8; 100 | tmp = (bytebuf[1] & 0xff); 101 | 102 | v = v | tmp; 103 | } 104 | catch (Exception e) 105 | { 106 | } 107 | 108 | return v; 109 | } 110 | 111 | public static int stream_read_uint8(MyStream mystream) 112 | { 113 | int v = 0; 114 | int bytes_read = 0; 115 | byte[] bytebuf = mystream.read_buf; 116 | 117 | try 118 | { 119 | bytes_read = mystream.stream.read(bytebuf, 0, 1); 120 | v = (bytebuf[0] & 0xff); 121 | mystream.currentPos = mystream.currentPos + 1; 122 | } 123 | catch (Exception e) 124 | { 125 | } 126 | 127 | return v; 128 | } 129 | 130 | public static void stream_skip(MyStream mystream, int skip) 131 | { 132 | int toskip = skip; 133 | int bytes_read = 0; 134 | 135 | if(toskip < 0) 136 | { 137 | System.err.println("stream_skip: request to seek backwards in stream - not supported, sorry"); 138 | return; 139 | } 140 | 141 | try 142 | { 143 | bytes_read = mystream.stream.skipBytes(toskip); 144 | mystream.currentPos = mystream.currentPos + bytes_read; 145 | } 146 | catch (java.io.IOException ioe) 147 | { 148 | } 149 | } 150 | 151 | public static int stream_eof(MyStream mystream) 152 | { 153 | // TODO 154 | 155 | return 0; 156 | } 157 | 158 | public static int stream_tell(MyStream mystream) 159 | { 160 | return (mystream.currentPos); 161 | } 162 | public static int stream_setpos(MyStream mystream, int pos) 163 | { 164 | return -1; 165 | } 166 | 167 | } 168 | 169 | -------------------------------------------------------------------------------- /com/beatofthedrum/alacdecoder/WavWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | ** WavWriter.java 3 | ** 4 | ** Copyright (c) 2011 Peter McQuillan 5 | ** 6 | ** All Rights Reserved. 7 | ** 8 | ** Distributed under the BSD Software License (see license.txt) 9 | ** 10 | */ 11 | package com.beatofthedrum.alacdecoder; 12 | 13 | public class WavWriter 14 | { 15 | static void write_uint32(java.io.FileOutputStream f, int v) 16 | { 17 | byte[] outputBytes = new byte[4]; 18 | 19 | outputBytes[3] = (byte) (v >>> 24); 20 | outputBytes[2] = (byte) (v >>> 16); 21 | outputBytes[1] = (byte) (v >>> 8); 22 | outputBytes[0] = (byte) (v); 23 | try 24 | { 25 | f.write(outputBytes, 0, 4 ); 26 | } 27 | catch(java.io.IOException ioe) 28 | { 29 | } 30 | } 31 | 32 | static void write_uint16(java.io.FileOutputStream f, int v) 33 | { 34 | byte[] outputBytes = new byte[2]; 35 | 36 | outputBytes[1] = (byte) (v >>> 8); 37 | outputBytes[0] = (byte) (v); 38 | try 39 | { 40 | f.write(outputBytes, 0, 2 ); 41 | } 42 | catch(java.io.IOException ioe) 43 | { 44 | } 45 | } 46 | 47 | public static void wavwriter_writeheaders(java.io.FileOutputStream f, int datasize, int numchannels, int samplerate, int bytespersample, int bitspersample) 48 | { 49 | byte[] buffAsBytes = new byte[4]; 50 | 51 | /* write RIFF header */ 52 | buffAsBytes[0]=82; 53 | buffAsBytes[1]=73; 54 | buffAsBytes[2]=70; 55 | buffAsBytes[3]=70; // "RIFF" ascii values 56 | 57 | try 58 | { 59 | f.write(buffAsBytes, 0, 4 ); 60 | } 61 | catch(java.io.IOException ioe) 62 | { 63 | } 64 | 65 | write_uint32(f, (36 + datasize)); 66 | buffAsBytes[0]=87; 67 | buffAsBytes[1]=65; 68 | buffAsBytes[2]=86; 69 | buffAsBytes[3]=69; // "WAVE" ascii values 70 | 71 | try 72 | { 73 | f.write(buffAsBytes, 0, 4 ); 74 | } 75 | catch(java.io.IOException ioe) 76 | { 77 | } 78 | 79 | /* write fmt header */ 80 | buffAsBytes[0]=102; 81 | buffAsBytes[1]=109; 82 | buffAsBytes[2]=116; 83 | buffAsBytes[3]=32; // "fmt " ascii values 84 | 85 | try 86 | { 87 | f.write(buffAsBytes, 0, 4 ); 88 | } 89 | catch(java.io.IOException ioe) 90 | { 91 | } 92 | 93 | write_uint32(f, 16); 94 | write_uint16(f, 1); // PCM data 95 | write_uint16(f, numchannels); 96 | write_uint32(f, samplerate); 97 | write_uint32(f, (samplerate * numchannels * bytespersample)); // byterate 98 | write_uint16(f, (numchannels * bytespersample )); 99 | write_uint16(f, bitspersample); 100 | 101 | /* write data header */ 102 | buffAsBytes[0]=100; 103 | buffAsBytes[1]=97; 104 | buffAsBytes[2]=116; 105 | buffAsBytes[3]=97; // "data" ascii values 106 | 107 | try 108 | { 109 | f.write(buffAsBytes, 0, 4 ); 110 | } 111 | catch(java.io.IOException ioe) 112 | { 113 | } 114 | 115 | write_uint32(f, datasize); 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2014 Peter McQuillan 2 | 3 | Based on the ALAC decoder - Copyright (c) 2005 David Hammerton 4 | 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 met: 9 | 10 | * Redistributions of source code must retain the above copyright notice, 11 | this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | * The name of the author may not be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR 22 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | --------------------------------------------------------------------------------