All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.beatofthedrum.alacdecoder.AlacUtils Maven / Gradle / Ivy

Go to download

Java implementation of Apple Lossless decoder supports both 16-bit and 24-bit Apple Lossless files

The newest version!
/*
** AlacUtils.java
**
** Copyright (c) 2011 Peter McQuillan
**
** All Rights Reserved.
**                       
** Distributed under the BSD Software License (see license.txt)  
**
*/
package com.beatofthedrum.alacdecoder;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.File;

public class AlacUtils
{

	public static AlacContext AlacOpenFileInput(AlacContext ac, String inputfilename)
	{
		try
		{
			return AlacOpenInput(ac, AlacInputStream.open(new RandomAccessFile(inputfilename, "r")));
		}
		catch (java.io.FileNotFoundException fe)
		{
			ac.error_message = new FileNotFoundException("Input file not found");
			ac.error = true;
			return (ac);
		}
	}

	public static AlacContext AlacOpenFileInput(AlacContext ac, File inputfile)
	{
		try
		{
			return AlacOpenInput(ac, AlacInputStream.open(new RandomAccessFile(inputfile, "r")));
		}
		catch (java.io.FileNotFoundException fe)
		{
			ac.error_message = new FileNotFoundException("Input file not found");
			ac.error = true;
			return (ac);
		}
	}

	public static AlacContext AlacOpenResourceInput(AlacContext ac, ClassLoader resourceLoader, String inputfilename)
	{
		try
		{
			return AlacOpenInput(ac, AlacInputStream.open(resourceLoader, inputfilename));
		}
		catch (IOException ioe)
		{
			ac.error_message = new IOException("Could not load resource");
			ac.error = true;
			return (ac);
		}
	}

	public static AlacContext AlacOpenStreamInput(AlacContext ac, InputStream input)
	{
		return AlacOpenInput(ac, AlacInputStream.open(input));
	}

    public static AlacContext AlacOpenInput(AlacContext ac, AlacInputStream input_stream)
    {
		int headerRead;
		QTMovieT qtmovie = new QTMovieT();
		DemuxResT demux_res = new DemuxResT();
		AlacFile alac;
		
		ac.error = false;
		
		ac.input_stream = input_stream;
		
		/* if qtmovie_read returns successfully, the stream is up to
		 * the movie data, which can be used directly by the decoder */
		headerRead = DemuxUtils.qtmovie_read(input_stream, qtmovie, demux_res);

		if (headerRead == 0)
		{
			ac.error = true;
			if (demux_res.format_read == 0)
			{
				String error_message = "Failed to load the QuickTime movie headers.";
				if (demux_res.format_read != 0)
					error_message = error_message + " File type: " + DemuxUtils.SplitFourCC(demux_res.format);
				ac.error_message = new AlacException(error_message);
			}
			else
			{
				ac.error_message = new IOException("Error while loading the QuickTime movie headers.");
			}
			return (ac);
		}
		else if (headerRead == 3)
		{
			ac.error_message = new AlacException("Error when seeking to start of music data");
			ac.error = true;
			return (ac);
		}
		
		/* initialise the sound converter */
		
		alac = AlacDecodeUtils.create_alac(demux_res.sample_size, demux_res.num_channels);

		AlacDecodeUtils.alac_set_info(alac, demux_res.codecdata);

		ac.demux_res = demux_res;
		ac.alac = alac;
		
		return (ac);
			
	}
	
	public static AlacContext AlacCloseInput(AlacContext ac)
	{
		if(null != ac.input_stream)
		{
			try
			{
				ac.input_stream.close();
			}
			catch(java.io.IOException ioe)
			{
				ac.error_message = new IOException("Error when closing file");
				ac.error = true;
			}
		}
		return ac;
	}
	
	// Heres where we extract the actual music data
	
	public static int AlacUnpackSamples(AlacContext ac, int[] pDestBuffer)
	{
        int sample_byte_size;
		SampleDuration sampleinfo = new SampleDuration();
        byte[] read_buffer = ac.read_buffer;
		int destBufferSize = 1024 *24 * 3; // 24kb buffer = 4096 frames = 1 alac sample (we support max 24bps)
		int outputBytes;
		MyStream inputStream = new MyStream();

		inputStream.stream = ac.input_stream;
		
		// if current_sample_block is beyond last block then finished
		
		if(ac.current_sample_block >= ac.demux_res.sample_byte_size.length)
		{
			return 0;
		}
		
		if (get_sample_info(ac.demux_res, ac.current_sample_block , sampleinfo) == 0)
		{
			// getting sample failed
				return 0;
		}

        sample_byte_size = sampleinfo.sample_byte_size;

		StreamUtils.stream_read(inputStream, sample_byte_size, read_buffer, 0);
		
		/* now fetch */
		outputBytes = destBufferSize;

		outputBytes = AlacDecodeUtils.decode_frame(ac.alac, read_buffer, pDestBuffer, outputBytes);
		
		ac.current_sample_block = ac.current_sample_block + 1;
		outputBytes -= ac.offset * AlacGetBytesPerSample(ac);
        System.arraycopy(pDestBuffer, ac.offset, pDestBuffer, 0, outputBytes);
        ac.offset = 0;
        return outputBytes;
	
	}
	

	// Returns the sample rate of the specified ALAC file

    public static int AlacGetSampleRate(AlacContext ac)
    {
        if ( null != ac && ac.demux_res.sample_rate != 0)
        {
            return ac.demux_res.sample_rate;
        }
        else
        {
            return (44100);
        }
    }
	
	public static int AlacGetNumChannels(AlacContext ac)
    {
        if ( null != ac && ac.demux_res.num_channels != 0)
        {
            return ac.demux_res.num_channels;
        }
        else
        {
            return 2;
        }
    }
	
	public static int AlacGetBitsPerSample(AlacContext ac)
    {
        if (null != ac && ac.demux_res.sample_size != 0)
        {
            return ac.demux_res.sample_size;
        }
        else
        {
            return 16;
        }
    }
	

	public static int AlacGetBytesPerSample(AlacContext ac)
    {
        if ( null != ac && ac.demux_res.sample_size != 0)
        {
            return (int)Math.ceil(ac.demux_res.sample_size/8.0);
        }
        else
        {
            return 2;
        }
    }
	
	
	// Get total number of samples contained in the Apple Lossless file, or -1 if unknown

    public static int AlacGetNumSamples(AlacContext ac)
    {
		/* calculate output size */
		int num_samples = 0;
		int thissample_duration;
		int thissample_bytesize = 0;
		SampleDuration sampleinfo = new SampleDuration();
		int i;
		boolean error_found = false;
		int retval = 0;
			
		for (i = 0; i < ac.demux_res.sample_byte_size.length; i++)
		{
			thissample_duration = 0;
			thissample_bytesize = 0;

			retval = get_sample_info(ac.demux_res, i, sampleinfo);
			
			if(retval == 0)
			{
				return (-1);
			}
			thissample_duration = sampleinfo.sample_duration;
			thissample_bytesize = sampleinfo.sample_byte_size;

			num_samples += thissample_duration;
		}
		
		return (num_samples);
	}
	

	static int get_sample_info(DemuxResT demux_res, int samplenum, SampleDuration sampleinfo)
	{
		int duration_index_accum = 0;
		int duration_cur_index = 0;

		if (samplenum >= demux_res.sample_byte_size.length)
		{
			System.err.println("sample " + samplenum + " does not exist ");
			return 0;
		}

		if (demux_res.num_time_to_samples == 0)		// was null
		{
			System.err.println("no time to samples");
			return 0;
		}
		while ((demux_res.time_to_sample[duration_cur_index].sample_count + duration_index_accum) <= samplenum)
		{
			duration_index_accum += demux_res.time_to_sample[duration_cur_index].sample_count;
			duration_cur_index++;
			if (duration_cur_index >= demux_res.num_time_to_samples)
			{
				System.err.println("sample " + samplenum + " does not have a duration");
				return 0;
			}
		}

		sampleinfo.sample_duration = demux_res.time_to_sample[duration_cur_index].sample_duration;
		sampleinfo.sample_byte_size = demux_res.sample_byte_size[samplenum];

		return 1;
	}

    /**
     * sets position in pcm samples
     * @param ac alac context
     * @param position position in pcm samples to go to
     */

    public static AlacContext AlacSetPosition(AlacContext ac, long position) {
        DemuxResT res = ac.demux_res;

        int current_position = 0;
        int current_sample = 0;
        SampleDuration sample_info = new SampleDuration();
        for (int i = 0; i < res.stsc.length; i++) {
            ChunkInfo chunkInfo = res.stsc[i];
            int last_chunk;

            if (i < res.stsc.length - 1) {
                last_chunk = res.stsc[i + 1].first_chunk;
            } else {
                last_chunk = res.stco.length;
            }

            for (int chunk = chunkInfo.first_chunk; chunk <= last_chunk; chunk ++) {
                int pos = res.stco[chunk - 1];
                int sample_count = chunkInfo.samples_per_chunk;
                while (sample_count > 0) {
                    int ret = get_sample_info(res, current_sample, sample_info);
                    if (ret == 0) {
						ac.error_message = new IOException("Error while reading sample info");
						ac.error = true;
						return ac;
					}
                    current_position += sample_info.sample_duration;
                    if (position < current_position) {
						try {
							ac.input_stream.seek(pos);
						} catch (IOException e) {
							ac.error_message = new IOException("Error when seeking");
							ac.error = true;
							return ac;
						}
						ac.current_sample_block = current_sample;
                        ac.offset =
                                (int) (position - (current_position - sample_info.sample_duration))
                                        * AlacGetNumChannels(ac);
                        return ac;
                    }
                    pos += sample_info.sample_byte_size;
                    current_sample++;
                    sample_count--;
                }
            }
        }
		return ac;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy