001package org.intellimate.izou.system.sound; 002 003import org.intellimate.izou.addon.AddOnModel; 004import org.intellimate.izou.main.Main; 005 006import javax.sound.sampled.*; 007import java.io.IOException; 008 009/** 010 * the delegation to Clip and SourceDataLine. 011 * @author LeanderK 012 * @version 1.0 013 */ 014public class IzouSoundLineClipAndSDLine extends IzouSoundDataLine implements Clip, SourceDataLine { 015 private final Clip clip; 016 private final SourceDataLine sourceDataLine; 017 018 public IzouSoundLineClipAndSDLine(Clip clip, SourceDataLine sourceDataLine, Main main, boolean isPermanent, AddOnModel addOnModel) { 019 super(clip, main, isPermanent, addOnModel); 020 this.clip = clip; 021 this.sourceDataLine = sourceDataLine; 022 } 023 024 /** 025 * Opens the line with the specified format and suggested buffer size, 026 * causing the line to acquire any required 027 * system resources and become operational. 028 * <p> 029 * The buffer size is specified in bytes, but must represent an integral 030 * number of sample frames. Invoking this method with a requested buffer 031 * size that does not meet this requirement may result in an 032 * IllegalArgumentException. The actual buffer size for the open line may 033 * differ from the requested buffer size. The value actually set may be 034 * queried by subsequently calling <code>{@link DataLine#getBufferSize}</code>. 035 * <p> 036 * If this operation succeeds, the line is marked as open, and an 037 * <code>{@link LineEvent.Type#OPEN OPEN}</code> event is dispatched to the 038 * line's listeners. 039 * <p> 040 * Invoking this method on a line which is already open is illegal 041 * and may result in an <code>IllegalStateException</code>. 042 * <p> 043 * Note that some lines, once closed, cannot be reopened. Attempts 044 * to reopen such a line will always result in a 045 * <code>LineUnavailableException</code>. 046 * 047 * @param format the desired audio format 048 * @param bufferSize the desired buffer size 049 * @throws LineUnavailableException if the line cannot be 050 * opened due to resource restrictions 051 * @throws IllegalArgumentException if the buffer size does not represent 052 * an integral number of sample frames, 053 * or if <code>format</code> is not fully specified or invalid 054 * @throws IllegalStateException if the line is already open 055 * @throws SecurityException if the line cannot be 056 * opened due to security restrictions 057 * 058 * @see #open(AudioFormat) 059 * @see Line#open 060 * @see Line#close 061 * @see Line#isOpen 062 * @see LineEvent 063 */ 064 @Override 065 public void open(AudioFormat format, int bufferSize) throws LineUnavailableException { 066 opening(); 067 sourceDataLine.open(format, bufferSize); 068 } 069 070 /** 071 * Opens the line with the specified format, causing the line to acquire any 072 * required system resources and become operational. 073 * 074 * <p> 075 * The implementation chooses a buffer size, which is measured in bytes but 076 * which encompasses an integral number of sample frames. The buffer size 077 * that the system has chosen may be queried by subsequently calling 078 * <code>{@link DataLine#getBufferSize}</code>. 079 * <p> 080 * If this operation succeeds, the line is marked as open, and an 081 * <code>{@link LineEvent.Type#OPEN OPEN}</code> event is dispatched to the 082 * line's listeners. 083 * <p> 084 * Invoking this method on a line which is already open is illegal 085 * and may result in an <code>IllegalStateException</code>. 086 * <p> 087 * Note that some lines, once closed, cannot be reopened. Attempts 088 * to reopen such a line will always result in a 089 * <code>LineUnavailableException</code>. 090 * 091 * @param format the desired audio format 092 * @throws LineUnavailableException if the line cannot be 093 * opened due to resource restrictions 094 * @throws IllegalArgumentException if <code>format</code> 095 * is not fully specified or invalid 096 * @throws IllegalStateException if the line is already open 097 * @throws SecurityException if the line cannot be 098 * opened due to security restrictions 099 * 100 * @see #open(AudioFormat, int) 101 * @see Line#open 102 * @see Line#close 103 * @see Line#isOpen 104 * @see LineEvent 105 */ 106 @Override 107 public void open(AudioFormat format) throws LineUnavailableException { 108 opening(); 109 sourceDataLine.open(format); 110 } 111 112 /** 113 * Writes audio data to the mixer via this source data line. The requested 114 * number of bytes of data are read from the specified array, 115 * starting at the given offset into the array, and written to the data 116 * line's buffer. If the caller attempts to write more data than can 117 * currently be written (see <code>{@link DataLine#available available}</code>), 118 * this method blocks until the requested amount of data has been written. 119 * This applies even if the requested amount of data to write is greater 120 * than the data line's buffer size. However, if the data line is closed, 121 * stopped, or flushed before the requested amount has been written, 122 * the method no longer blocks, but returns the number of bytes 123 * written thus far. 124 * <p> 125 * The number of bytes that can be written without blocking can be ascertained 126 * using the <code>{@link DataLine#available available}</code> method of the 127 * <code>DataLine</code> interface. (While it is guaranteed that 128 * this number of bytes can be written without blocking, there is no guarantee 129 * that attempts to write additional data will block.) 130 * <p> 131 * The number of bytes to write must represent an integral number of 132 * sample frames, such that: 133 * <br> 134 * <center><code>[ bytes written ] % [frame size in bytes ] == 0</code></center> 135 * <br> 136 * The return value will always meet this requirement. A request to write a 137 * number of bytes representing a non-integral number of sample frames cannot 138 * be fulfilled and may result in an <code>IllegalArgumentException</code>. 139 * 140 * @param b a byte array containing data to be written to the data line 141 * @param off the offset from the beginning of the array, in bytes 142 * @param len the length, in bytes, of the valid data in the array 143 * (in other words, the requested amount of data to write, in bytes) 144 * @return the number of bytes actually written 145 * @throws IllegalArgumentException if the requested number of bytes does 146 * not represent an integral number of sample frames, 147 * or if <code>len</code> is negative 148 * @throws ArrayIndexOutOfBoundsException if <code>off</code> is negative, 149 * or <code>off+len</code> is greater than the length of the array 150 * <code>b</code>. 151 * 152 * @see TargetDataLine#read 153 * @see DataLine#available 154 */ 155 @Override 156 public int write(byte[] b, int off, int len) { 157 if (isMutable) { 158 return sourceDataLine.write(b, off, len); 159 } else { 160 if (isMutedFromSystem) { 161 byte[] newArr = new byte[b.length]; 162 return sourceDataLine.write(newArr, off, len); 163 } else { 164 return sourceDataLine.write(b, off, len); 165 } 166 } 167 } 168 169 /** 170 * Opens the clip, meaning that it should acquire any required 171 * system resources and become operational. The clip is opened 172 * with the format and audio data indicated. 173 * If this operation succeeds, the line is marked as open and an 174 * <code>{@link LineEvent.Type#OPEN OPEN}</code> event is dispatched 175 * to the line's listeners. 176 * <p> 177 * Invoking this method on a line which is already open is illegal 178 * and may result in an IllegalStateException. 179 * <p> 180 * Note that some lines, once closed, cannot be reopened. Attempts 181 * to reopen such a line will always result in a 182 * <code>{@link LineUnavailableException}</code>. 183 * 184 * @param format the format of the supplied audio data 185 * @param data a byte array containing audio data to load into the clip 186 * @param offset the point at which to start copying, expressed in 187 * <em>bytes</em> from the beginning of the array 188 * @param bufferSize the number of <em>bytes</em> 189 * of data to load into the clip from the array. 190 * @throws LineUnavailableException if the line cannot be 191 * opened due to resource restrictions 192 * @throws IllegalArgumentException if the buffer size does not represent 193 * an integral number of sample frames, 194 * or if <code>format</code> is not fully specified or invalid 195 * @throws IllegalStateException if the line is already open 196 * @throws SecurityException if the line cannot be 197 * opened due to security restrictions 198 * 199 * @see #close 200 * @see #isOpen 201 * @see LineListener 202 */ 203 @Override 204 public void open(AudioFormat format, byte[] data, int offset, int bufferSize) throws LineUnavailableException { 205 opening(); 206 clip.open(format, data, offset, bufferSize); 207 } 208 209 /** 210 * Opens the clip with the format and audio data present in the provided audio 211 * input stream. Opening a clip means that it should acquire any required 212 * system resources and become operational. If this operation 213 * input stream. If this operation 214 * succeeds, the line is marked open and an 215 * <code>{@link LineEvent.Type#OPEN OPEN}</code> event is dispatched 216 * to the line's listeners. 217 * <p> 218 * Invoking this method on a line which is already open is illegal 219 * and may result in an IllegalStateException. 220 * <p> 221 * Note that some lines, once closed, cannot be reopened. Attempts 222 * to reopen such a line will always result in a 223 * <code>{@link LineUnavailableException}</code>. 224 * 225 * @param stream an audio input stream from which audio data will be read into 226 * the clip 227 * @throws LineUnavailableException if the line cannot be 228 * opened due to resource restrictions 229 * @throws IOException if an I/O exception occurs during reading of 230 * the stream 231 * @throws IllegalArgumentException if the stream's audio format 232 * is not fully specified or invalid 233 * @throws IllegalStateException if the line is already open 234 * @throws SecurityException if the line cannot be 235 * opened due to security restrictions 236 * 237 * @see #close 238 * @see #isOpen 239 * @see LineListener 240 */ 241 @Override 242 public void open(AudioInputStream stream) throws LineUnavailableException, IOException { 243 opening(); 244 clip.open(stream); 245 } 246 247 /** 248 * Obtains the media length in sample frames. 249 * @return the media length, expressed in sample frames, 250 * or <code>AudioSystem.NOT_SPECIFIED</code> if the line is not open. 251 * @see AudioSystem#NOT_SPECIFIED 252 */ 253 @Override 254 public int getFrameLength() { 255 return clip.getFrameLength(); 256 } 257 258 /** 259 * Obtains the media duration in microseconds 260 * @return the media duration, expressed in microseconds, 261 * or <code>AudioSystem.NOT_SPECIFIED</code> if the line is not open. 262 * @see AudioSystem#NOT_SPECIFIED 263 */ 264 @Override 265 public long getMicrosecondLength() { 266 return clip.getMicrosecondLength(); 267 } 268 269 /** 270 * Sets the media position in sample frames. The position is zero-based; 271 * the first frame is frame number zero. When the clip begins playing the 272 * next time, it will start by playing the frame at this position. 273 * <p> 274 * To obtain the current position in sample frames, use the 275 * <code>{@link DataLine#getFramePosition getFramePosition}</code> 276 * method of <code>DataLine</code>. 277 * 278 * @param frames the desired new media position, expressed in sample frames 279 */ 280 @Override 281 public void setFramePosition(int frames) { 282 clip.setFramePosition(frames); 283 } 284 285 /** 286 * Sets the media position in microseconds. When the clip begins playing the 287 * next time, it will start at this position. 288 * The level of precision is not guaranteed. For example, an implementation 289 * might calculate the microsecond position from the current frame position 290 * and the audio sample frame rate. The precision in microseconds would 291 * then be limited to the number of microseconds per sample frame. 292 * <p> 293 * To obtain the current position in microseconds, use the 294 * <code>{@link DataLine#getMicrosecondPosition getMicrosecondPosition}</code> 295 * method of <code>DataLine</code>. 296 * 297 * @param microseconds the desired new media position, expressed in microseconds 298 */ 299 @Override 300 public void setMicrosecondPosition(long microseconds) { 301 clip.setMicrosecondPosition(microseconds); 302 } 303 304 /** 305 * Sets the first and last sample frames that will be played in 306 * the loop. The ending point must be greater than 307 * or equal to the starting point, and both must fall within the 308 * the size of the loaded media. A value of 0 for the starting 309 * point means the beginning of the loaded media. Similarly, a value of -1 310 * for the ending point indicates the last frame of the media. 311 * @param start the loop's starting position, in sample frames (zero-based) 312 * @param end the loop's ending position, in sample frames (zero-based), or 313 * -1 to indicate the final frame 314 * @throws IllegalArgumentException if the requested 315 * loop points cannot be set, usually because one or both falls outside 316 * the media's duration or because the ending point is 317 * before the starting point 318 */ 319 @Override 320 public void setLoopPoints(int start, int end) { 321 clip.setLoopPoints(start, end); 322 } 323 324 /** 325 * Starts looping playback from the current position. Playback will 326 * continue to the loop's end point, then loop back to the loop start point 327 * <code>count</code> times, and finally continue playback to the end of 328 * the clip. 329 * <p> 330 * If the current position when this method is invoked is greater than the 331 * loop end point, playback simply continues to the 332 * end of the clip without looping. 333 * <p> 334 * A <code>count</code> value of 0 indicates that any current looping should 335 * cease and playback should continue to the end of the clip. The behavior 336 * is undefined when this method is invoked with any other value during a 337 * loop operation. 338 * <p> 339 * If playback is stopped during looping, the current loop status is 340 * cleared; the behavior of subsequent loop and start requests is not 341 * affected by an interrupted loop operation. 342 * 343 * @param count the number of times playback should loop back from the 344 * loop's end position to the loop's start position, or 345 * <code>{@link #LOOP_CONTINUOUSLY}</code> to indicate that looping should 346 * continue until interrupted 347 */ 348 @Override 349 public void loop(int count) { 350 clip.loop(count); 351 } 352}