1/* 2 * Copyright (c) 2009-2010 jMonkeyEngine 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33package com.jme3.audio; 34 35import com.jme3.util.NativeObject; 36import java.io.Closeable; 37import java.io.IOException; 38import java.io.InputStream; 39import java.util.logging.Level; 40import java.util.logging.Logger; 41 42/** 43 * <code>AudioStream</code> is an implementation of AudioData that 44 * acquires the audio from an InputStream. Audio can be streamed 45 * from network, hard drive etc. It is assumed the data coming 46 * from the input stream is uncompressed. 47 * 48 * @author Kirill Vainer 49 */ 50public class AudioStream extends AudioData implements Closeable{ 51 52 private final static Logger logger = Logger.getLogger(AudioStream.class.getName()); 53 protected InputStream in; 54 protected float duration = -1f; 55 protected boolean open = false; 56 protected int[] ids; 57 58 public AudioStream(){ 59 super(); 60 } 61 62 protected AudioStream(int[] ids){ 63 // Pass some dummy ID so handle 64 // doesn't get created. 65 super(-1); 66 // This is what gets destroyed in reality 67 this.ids = ids; 68 } 69 70 public void updateData(InputStream in, float duration){ 71 if (id != -1 || this.in != null) 72 throw new IllegalStateException("Data already set!"); 73 74 this.in = in; 75 this.duration = duration; 76 open = true; 77 } 78 79 /** 80 * Reads samples from the stream. The format of the data 81 * depends on the getSampleRate(), getChannels(), getBitsPerSample() 82 * values. 83 * 84 * @param buf Buffer where to read the samples 85 * @param offset The offset in the buffer where to read samples 86 * @param length The length inside the buffer where to read samples 87 * @return number of bytes read. 88 */ 89 public int readSamples(byte[] buf, int offset, int length){ 90 if (!open) 91 return -1; 92 93 try{ 94 return in.read(buf, offset, length); 95 }catch (IOException ex){ 96 return -1; 97 } 98 } 99 100 /** 101 * Reads samples from the stream. 102 * 103 * @see AudioStream#readSamples(byte[], int, int) 104 * @param buf Buffer where to read the samples 105 * @return number of bytes read. 106 */ 107 public int readSamples(byte[] buf){ 108 return readSamples(buf, 0, buf.length); 109 } 110 111 public float getDuration(){ 112 return duration; 113 } 114 115 @Override 116 public int getId(){ 117 throw new RuntimeException("Don't use getId() on streams"); 118 } 119 120 @Override 121 public void setId(int id){ 122 throw new RuntimeException("Don't use setId() on streams"); 123 } 124 125 public void initIds(int count){ 126 ids = new int[count]; 127 } 128 129 public int getId(int index){ 130 return ids[index]; 131 } 132 133 public void setId(int index, int id){ 134 ids[index] = id; 135 } 136 137 public int[] getIds(){ 138 return ids; 139 } 140 141 public void setIds(int[] ids){ 142 this.ids = ids; 143 } 144 145 @Override 146 public DataType getDataType() { 147 return DataType.Stream; 148 } 149 150 @Override 151 public void resetObject() { 152 id = -1; 153 ids = null; 154 setUpdateNeeded(); 155 } 156 157 @Override 158 public void deleteObject(Object rendererObject) { 159 // It seems that the audio renderer is already doing a good 160 // job at deleting audio streams when they finish playing. 161// ((AudioRenderer)rendererObject).deleteAudioData(this); 162 } 163 164 @Override 165 public NativeObject createDestructableClone() { 166 return new AudioStream(ids); 167 } 168 169 /** 170 * @return Whether the stream is open or not. Reading from a closed 171 * stream will always return eof. 172 */ 173 public boolean isOpen(){ 174 return open; 175 } 176 177 /** 178 * Closes the stream, releasing all data relating to it. Reading 179 * from the stream will return eof. 180 * @throws IOException 181 */ 182 public void close() { 183 if (in != null && open){ 184 try{ 185 in.close(); 186 }catch (IOException ex){ 187 } 188 open = false; 189 }else{ 190 throw new RuntimeException("AudioStream is already closed!"); 191 } 192 } 193 194 195 public void setTime(float time){ 196 if(in instanceof SeekableStream){ 197 ((SeekableStream)in).setTime(time); 198 }else{ 199 logger.log(Level.WARNING,"Cannot use setTime on a stream that is not seekable. You must load the file with the streamCache option set to true"); 200 } 201 } 202 203 204} 205