Package org.vorbis.spi.sampled.file

Source Code of org.vorbis.spi.sampled.file.VorbisAudioFileReader

/*
* Copyright (C) 2008 JavaZOOM
*               2013 Trilarion
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.vorbis.spi.sampled.file;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;
import org.sound.SoundException;
import org.sound.TDebug;
import org.sound.sampled.file.TAudioFileReader;
import org.vorbis.jcraft.jogg.Packet;
import org.vorbis.jcraft.jogg.Page;
import org.vorbis.jcraft.jogg.StreamState;
import org.vorbis.jcraft.jogg.SyncState;
import org.vorbis.jcraft.jorbis.Block;
import org.vorbis.jcraft.jorbis.Comment;
import org.vorbis.jcraft.jorbis.DspState;
import org.vorbis.jcraft.jorbis.Info;
import org.vorbis.jcraft.jorbis.VorbisFile;

/**
* This class implements the AudioFileReader class and provides an Ogg Vorbis
* file reader for use with the Java Sound Service Provider Interface.
*/
public class VorbisAudioFileReader extends TAudioFileReader {

    private SyncState oggSyncState_ = null;
    private StreamState oggStreamState_ = null;
    private Page oggPage_ = null;
    private Packet oggPacket_ = null;
    private Info vorbisInfo = null;
    private Comment vorbisComment = null;
    private DspState vorbisDspState = null;
    private Block vorbisBlock = null;
    private int bufferMultiple_ = 4;
    private int bufferSize_ = bufferMultiple_ * 256 * 2;
    private byte[] buffer = null;
    private int bytes = 0;
    private int index = 0;
    private InputStream oggBitStream_ = null;
    private static final int INITAL_READ_LENGTH = 64000;
    private static final int MARK_LIMIT = INITAL_READ_LENGTH + 1;

    /**
     *
     */
    public VorbisAudioFileReader() {
        super(MARK_LIMIT, true);
    }

    /**
     * Return the AudioFileFormat from the given file.
     */
    @Override
    public AudioFileFormat getAudioFileFormat(File file) throws UnsupportedAudioFileException, IOException {
        if (TDebug.TraceAudioFileReader) {
            TDebug.out("getAudioFileFormat(File file)");
        }

        try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
            inputStream.mark(MARK_LIMIT);
            AudioFileFormat aff = getAudioFileFormat(inputStream);
            inputStream.reset();
            // Get Vorbis file info such as length in seconds.
            VorbisFile vf = new VorbisFile(file.getAbsolutePath());
            return getAudioFileFormat(inputStream, (int) file.length(), (int) Math.round((vf.time_total(-1)) * 1000));
        } catch (SoundException e) {
            throw new IOException(e.getMessage());
        }
    }

    /**
     * Return the AudioFileFormat from the given URL.
     */
    @Override
    public AudioFileFormat getAudioFileFormat(URL url) throws UnsupportedAudioFileException, IOException {
        if (TDebug.TraceAudioFileReader) {
            TDebug.out("getAudioFileFormat(URL url)");
        }
        InputStream inputStream = url.openStream();
        try {
            return getAudioFileFormat(inputStream);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * Return the AudioFileFormat from the given InputStream.
     */
    @Override
    public AudioFileFormat getAudioFileFormat(InputStream inputStream) throws UnsupportedAudioFileException, IOException {
        if (TDebug.TraceAudioFileReader) {
            TDebug.out("getAudioFileFormat(InputStream inputStream)");
        }
        try {
            if (!inputStream.markSupported()) {
                inputStream = new BufferedInputStream(inputStream);
            }
            inputStream.mark(MARK_LIMIT);
            return getAudioFileFormat(inputStream, AudioSystem.NOT_SPECIFIED, AudioSystem.NOT_SPECIFIED);
        } finally {
            inputStream.reset();
        }
    }

    /**
     * Return the AudioFileFormat from the given InputStream and length in
     * bytes.
     */
    @Override
    public AudioFileFormat getAudioFileFormat(InputStream inputStream, long medialength) throws UnsupportedAudioFileException, IOException {
        return getAudioFileFormat(inputStream, (int) medialength, AudioSystem.NOT_SPECIFIED);
    }

    /**
     * Return the AudioFileFormat from the given InputStream, length in bytes
     * and length in milliseconds.
     */
    protected AudioFileFormat getAudioFileFormat(InputStream bitStream, int mediaLength, int totalms) throws UnsupportedAudioFileException, IOException {
        HashMap aff_properties = new HashMap();
        HashMap af_properties = new HashMap();
        if (totalms == AudioSystem.NOT_SPECIFIED) {
            totalms = 0;
        }
        if (totalms <= 0) {
            totalms = 0;
        } else {
            aff_properties.put("duration", totalms * 1000);
        }
        oggBitStream_ = bitStream;
        init_jorbis();
        index = 0;
        try {
            readHeaders(aff_properties, af_properties);
        } catch (IOException ioe) {
            if (TDebug.TraceAudioFileReader) {
                TDebug.out(ioe.getMessage());
            }
            throw new UnsupportedAudioFileException(ioe.getMessage());
        }

        String dmp = vorbisInfo.toString();
        if (TDebug.TraceAudioFileReader) {
            TDebug.out(dmp);
        }
        int ind = dmp.lastIndexOf("bitrate:");
        int minbitrate = -1;
        int nominalbitrate = -1;
        int maxbitrate = -1;
        if (ind != -1) {
            dmp = dmp.substring(ind + 8, dmp.length());
            StringTokenizer st = new StringTokenizer(dmp, ",");
            if (st.hasMoreTokens()) {
                minbitrate = Integer.parseInt(st.nextToken());
            }
            if (st.hasMoreTokens()) {
                nominalbitrate = Integer.parseInt(st.nextToken());
            }
            if (st.hasMoreTokens()) {
                maxbitrate = Integer.parseInt(st.nextToken());
            }
        }
        if (nominalbitrate > 0) {
            af_properties.put("bitrate", nominalbitrate);
        }
        af_properties.put("vbr", true);

        if (minbitrate > 0) {
            aff_properties.put("ogg.bitrate.min.bps", minbitrate);
        }
        if (maxbitrate > 0) {
            aff_properties.put("ogg.bitrate.max.bps", maxbitrate);
        }
        if (nominalbitrate > 0) {
            aff_properties.put("ogg.bitrate.nominal.bps", nominalbitrate);
        }
        if (vorbisInfo.channels > 0) {
            aff_properties.put("ogg.channels", vorbisInfo.channels);
        }
        if (vorbisInfo.rate > 0) {
            aff_properties.put("ogg.frequency.hz", vorbisInfo.rate);
        }
        if (mediaLength > 0) {
            aff_properties.put("ogg.length.bytes", mediaLength);
        }
        aff_properties.put("ogg.version", vorbisInfo.version);

        //AudioFormat.Encoding encoding = VorbisEncoding.VORBISENC;
        //AudioFormat format = new VorbisAudioFormat(encoding, vorbisInfo.rate, AudioSystem.NOT_SPECIFIED, vorbisInfo.channels, AudioSystem.NOT_SPECIFIED, AudioSystem.NOT_SPECIFIED, true,af_properties);

        // Patch from MS to ensure more SPI compatibility ...
        float frameRate = -1;
        if (nominalbitrate > 0) {
            frameRate = nominalbitrate / 8;
        } else if (minbitrate > 0) {
            frameRate = minbitrate / 8;
        }

        AudioFormat.Encoding encoding = VorbisEncoding.VORBISENC;
        // New Patch from MS:
        AudioFormat format = new VorbisAudioFormat(encoding, vorbisInfo.rate, AudioSystem.NOT_SPECIFIED, vorbisInfo.channels, 1, frameRate, false, af_properties);
        // Patch end

        return new VorbisAudioFileFormat(VorbisFileFormatType.OGG, format, AudioSystem.NOT_SPECIFIED, mediaLength, aff_properties);
    }

    /**
     * Return the AudioInputStream from the given InputStream.
     */
    @Override
    public AudioInputStream getAudioInputStream(InputStream inputStream) throws UnsupportedAudioFileException, IOException {
        if (TDebug.TraceAudioFileReader) {
            TDebug.out("getAudioInputStream(InputStream inputStream)");
        }
        return getAudioInputStream(inputStream, AudioSystem.NOT_SPECIFIED, AudioSystem.NOT_SPECIFIED);
    }

    /**
     * Return the AudioInputStream from the given InputStream.
     */
    public AudioInputStream getAudioInputStream(InputStream inputStream, int medialength, int totalms) throws UnsupportedAudioFileException, IOException {
        if (TDebug.TraceAudioFileReader) {
            TDebug.out("getAudioInputStream(InputStream inputStreamint medialength, int totalms)");
        }
        try {
            if (!inputStream.markSupported()) {
                inputStream = new BufferedInputStream(inputStream);
            }
            inputStream.mark(MARK_LIMIT);
            AudioFileFormat audioFileFormat = getAudioFileFormat(inputStream, medialength, totalms);
            inputStream.reset();
            return new AudioInputStream(inputStream, audioFileFormat.getFormat(), audioFileFormat.getFrameLength());
        } catch (UnsupportedAudioFileException | IOException e) {
            inputStream.reset();
            throw e;
        }
    }

    /**
     * Return the AudioInputStream from the given File.
     */
    @Override
    public AudioInputStream getAudioInputStream(File file) throws UnsupportedAudioFileException, IOException {
        if (TDebug.TraceAudioFileReader) {
            TDebug.out("getAudioInputStream(File file)");
        }
        InputStream inputStream = new FileInputStream(file);
        try {
            return getAudioInputStream(inputStream);
        } catch (UnsupportedAudioFileException | IOException e) {
            if (inputStream != null) {
                inputStream.close();
            }
            throw e;
        }
    }

    /**
     * Return the AudioInputStream from the given URL.
     */
    @Override
    public AudioInputStream getAudioInputStream(URL url) throws UnsupportedAudioFileException, IOException {
        if (TDebug.TraceAudioFileReader) {
            TDebug.out("getAudioInputStream(URL url)");
        }
        InputStream inputStream = url.openStream();
        try {
            return getAudioInputStream(inputStream);
        } catch (UnsupportedAudioFileException | IOException e) {
            if (inputStream != null) {
                inputStream.close();
            }
            throw e;
        }
    }

    /**
     * Reads headers and comments.
     */
    private void readHeaders(HashMap aff_properties, HashMap af_properties) throws IOException {
        if (TDebug.TraceAudioConverter) {
            TDebug.out("readHeaders(");
        }
        index = oggSyncState_.buffer(bufferSize_);
        buffer = oggSyncState_.data;
        bytes = readFromStream(buffer, index, bufferSize_);
        if (bytes == -1) {
            if (TDebug.TraceAudioConverter) {
                TDebug.out("Cannot get any data from selected Ogg bitstream.");
            }
            throw new IOException("Cannot get any data from selected Ogg bitstream.");
        }
        oggSyncState_.wrote(bytes);
        if (oggSyncState_.pageout(oggPage_) != 1) {
            if (bytes < bufferSize_) {
                throw new IOException("EOF");
            }
            if (TDebug.TraceAudioConverter) {
                TDebug.out("Input does not appear to be an Ogg bitstream.");
            }
            throw new IOException("Input does not appear to be an Ogg bitstream.");
        }
        oggStreamState_.init(oggPage_.serialno());
        vorbisInfo.init();
        vorbisComment.init();
        aff_properties.put("ogg.serial", oggPage_.serialno());
        if (oggStreamState_.pagein(oggPage_) < 0) {
            // error; stream version mismatch perhaps
            if (TDebug.TraceAudioConverter) {
                TDebug.out("Error reading first page of Ogg bitstream data.");
            }
            throw new IOException("Error reading first page of Ogg bitstream data.");
        }
        if (oggStreamState_.packetout(oggPacket_) != 1) {
            // no page? must not be vorbis
            if (TDebug.TraceAudioConverter) {
                TDebug.out("Error reading initial header packet.");
            }
            throw new IOException("Error reading initial header packet.");
        }
        if (vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_) < 0) {
            // error case; not a vorbis header
            if (TDebug.TraceAudioConverter) {
                TDebug.out("This Ogg bitstream does not contain Vorbis audio data.");
            }
            throw new IOException("This Ogg bitstream does not contain Vorbis audio data.");
        }
        int i = 0;
        while (i < 2) {
            while (i < 2) {
                int result = oggSyncState_.pageout(oggPage_);
                if (result == 0) {
                    break;
                } // Need more data
                if (result == 1) {
                    oggStreamState_.pagein(oggPage_);
                    while (i < 2) {
                        result = oggStreamState_.packetout(oggPacket_);
                        if (result == 0) {
                            break;
                        }
                        if (result == -1) {
                            if (TDebug.TraceAudioConverter) {
                                TDebug.out("Corrupt secondary header.  Exiting.");
                            }
                            throw new IOException("Corrupt secondary header.  Exiting.");
                        }
                        vorbisInfo.synthesis_headerin(vorbisComment, oggPacket_);
                        i++;
                    }
                }
            }
            index = oggSyncState_.buffer(bufferSize_);
            buffer = oggSyncState_.data;
            bytes = readFromStream(buffer, index, bufferSize_);
            if (bytes == -1) {
                break;
            }
            if (bytes == 0 && i < 2) {
                if (TDebug.TraceAudioConverter) {
                    TDebug.out("End of file before finding all Vorbis headers!");
                }
                throw new IOException("End of file before finding all Vorbis  headers!");
            }
            oggSyncState_.wrote(bytes);
        }
        // Read Ogg Vorbis comments.
        byte[][] ptr = vorbisComment.user_comments;
        String currComment;
        int c = 0;
        for (int j = 0; j < ptr.length; j++) {
            if (ptr[j] == null) {
                break;
            }
            currComment = (new String(ptr[j], 0, ptr[j].length - 1, "UTF-8")).trim();
            if (TDebug.TraceAudioConverter) {
                TDebug.out(currComment);
            }
            if (currComment.toLowerCase().startsWith("artist")) {
                aff_properties.put("author", currComment.substring(7));
            } else if (currComment.toLowerCase().startsWith("title")) {
                aff_properties.put("title", currComment.substring(6));
            } else if (currComment.toLowerCase().startsWith("album")) {
                aff_properties.put("album", currComment.substring(6));
            } else if (currComment.toLowerCase().startsWith("date")) {
                aff_properties.put("date", currComment.substring(5));
            } else if (currComment.toLowerCase().startsWith("copyright")) {
                aff_properties.put("copyright", currComment.substring(10));
            } else if (currComment.toLowerCase().startsWith("comment")) {
                aff_properties.put("comment", currComment.substring(8));
            } else if (currComment.toLowerCase().startsWith("genre")) {
                aff_properties.put("ogg.comment.genre", currComment.substring(6));
            } else if (currComment.toLowerCase().startsWith("tracknumber")) {
                aff_properties.put("ogg.comment.track", currComment.substring(12));
            } else {
                c++;
                aff_properties.put("ogg.comment.ext." + c, currComment);
            }
            aff_properties.put("ogg.comment.encodedby", new String(vorbisComment.vendor, 0, vorbisComment.vendor.length - 1));
        }
    }

    /**
     * Reads from the oggBitStream_ a specified number of Bytes(bufferSize_)
     * worth starting at index and puts them in the specified buffer[].
     *
     * @return the number of bytes read or -1 if error.
     */
    private int readFromStream(byte[] buffer, int index, int bufferSize_) {
        int bytes;
        try {
            bytes = oggBitStream_.read(buffer, index, bufferSize_);
        } catch (Exception e) {
            if (TDebug.TraceAudioFileReader) {
                TDebug.out("Cannot Read Selected Song");
            }
            bytes = -1;
        }
        return bytes;
    }

    /**
     * Initializes all the jOrbis and jOgg vars that are used for song playback.
     */
    private void init_jorbis() {
        oggSyncState_ = new SyncState();
        oggStreamState_ = new StreamState();
        oggPage_ = new Page();
        oggPacket_ = new Packet();
        vorbisInfo = new Info();
        vorbisComment = new Comment();
        vorbisDspState = new DspState();
        vorbisBlock = new Block(vorbisDspState);
        buffer = null;
        bytes = 0;
        oggSyncState_.init();
    }
    private static final Logger LOG = Logger.getLogger(VorbisAudioFileReader.class.getName());
}
TOP

Related Classes of org.vorbis.spi.sampled.file.VorbisAudioFileReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.