/* * Copyright (C) 2012-2016 Julien Bonjean <julien@bonjean.info> * * This file is part of Beluga Player. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU 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 General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package info.bonjean.beluga.player; import info.bonjean.beluga.connection.BelugaHTTPClient; import info.bonjean.beluga.connection.CachedInputStream; import info.bonjean.beluga.exception.CommunicationException; import info.bonjean.beluga.exception.InternalException; import java.io.IOException; import java.net.MalformedURLException; import javazoom.jl.decoder.Bitstream; import javazoom.jl.decoder.BitstreamException; import javazoom.jl.decoder.Decoder; import javazoom.jl.decoder.Header; import javazoom.jl.decoder.JavaLayerException; import javazoom.jl.decoder.SampleBuffer; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Julien Bonjean <julien@bonjean.info> * */ public class MP3Player { private static final Logger log = LoggerFactory.getLogger(MP3Player.class); private Bitstream bitstream; private CachedInputStream cachedInputStream; private Decoder decoder; private AudioDevice audioDeviceManager; private boolean close = true; private boolean pause = false; private long duration; private int bitrate; private HttpGet streamRequest; public MP3Player() { } public void loadSong(String url) throws JavaLayerException, MalformedURLException, IOException, CommunicationException, InternalException { streamRequest = new HttpGet(url); HttpResponse httpResponse = BelugaHTTPClient.getInstance().requestGetStream(streamRequest); cachedInputStream = new CachedInputStream(httpResponse.getEntity().getContent()); bitstream = new Bitstream(cachedInputStream); // get the first frame header to get bitrate Header frame = bitstream.readFrame(); if (frame == null) { cleanResources(); throw new IOException("noAudioStream"); } bitrate = frame.bitrate(); bitstream.unreadFrame(); // get file size from HTTP headers long songSize = Long.parseLong(httpResponse.getFirstHeader("Content-Length").getValue()); // calculate the duration duration = (songSize * 1000) / (bitrate / 8); // set the decoder decoder = new Decoder(); // decode the first frame to initialize the decoder decoder.decodeFrame(frame, bitstream); // init environment variables close = true; pause = false; } public void play(boolean dummy) throws JavaLayerException, InternalException { // create audio device audioDeviceManager = dummy ? new DummyAudioDevice(decoder) : new SimpleAudioDevice(decoder); close = false; try { while (decodeFrame()) { while (pause) { try { Thread.sleep(100); } catch (InterruptedException e) { break; } } } } catch (JavaLayerException e) { throw e; } finally { close = true; cleanResources(); } } private void cleanResources() { // cleanup audio stack if (audioDeviceManager != null) audioDeviceManager.close(); // cleanup bitstream try { if (bitstream != null) bitstream.close(); } catch (BitstreamException e) { log.debug(e.getMessage()); } if (streamRequest != null) { // shut down the underlying connection and remove it from the // connection pool streamRequest.abort(); streamRequest = null; } } public void stop() { close = true; pause = false; } public void pause() { pause = !pause; } public long getCachePosition() { try { return ((cachedInputStream.available() + bitstream.getPosition()) * 1000) / (bitrate / 8); } catch (IOException e) { log.debug(e.getMessage()); return 0; } } public long getPosition() { return (bitstream.getPosition() * 1000) / (bitrate / 8); } protected boolean decodeFrame() throws JavaLayerException { if (close) return false; try { Header h = bitstream.readFrame(); if (h == null) return false; SampleBuffer output = (SampleBuffer) decoder.decodeFrame(h, bitstream); audioDeviceManager.write(output); bitstream.closeFrame(); } catch (RuntimeException ex) { throw new JavaLayerException("Exception decoding audio frame", ex); } return true; } public long getDuration() { return duration; } public boolean isPaused() { return pause; } public int getBitrate() { return bitrate; } public boolean isActive() { return !close; } }