1 /* 2 * Copyright (c) 1999, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.media.sound; 27 28 import java.util.Map; 29 import java.util.Vector; 30 import java.util.WeakHashMap; 31 32 import javax.sound.sampled.AudioSystem; 33 import javax.sound.sampled.Control; 34 import javax.sound.sampled.Line; 35 import javax.sound.sampled.LineEvent; 36 import javax.sound.sampled.LineListener; 37 import javax.sound.sampled.LineUnavailableException; 38 39 /** 40 * AbstractLine 41 * 42 * @author Kara Kytle 43 */ 44 abstract class AbstractLine implements Line { 45 46 protected final Line.Info info; 47 protected Control[] controls; 48 AbstractMixer mixer; 49 private volatile boolean open; 50 private final Vector<Object> listeners = new Vector<>(); 51 52 /** 53 * Contains event dispatcher per thread group. 54 */ 55 private static final Map<ThreadGroup, EventDispatcher> dispatchers = 56 new WeakHashMap<>(); 57 58 /** 59 * Constructs a new AbstractLine. 60 * @param mixer the mixer with which this line is associated 61 * @param controls set of supported controls 62 */ AbstractLine(Line.Info info, AbstractMixer mixer, Control[] controls)63 protected AbstractLine(Line.Info info, AbstractMixer mixer, Control[] controls) { 64 65 if (controls == null) { 66 controls = new Control[0]; 67 } 68 69 this.info = info; 70 this.mixer = mixer; 71 this.controls = controls; 72 } 73 74 // LINE METHODS 75 76 @Override getLineInfo()77 public final Line.Info getLineInfo() { 78 return info; 79 } 80 81 @Override isOpen()82 public final boolean isOpen() { 83 return open; 84 } 85 86 @Override addLineListener(LineListener listener)87 public final void addLineListener(LineListener listener) { 88 synchronized(listeners) { 89 if ( ! (listeners.contains(listener)) ) { 90 listeners.addElement(listener); 91 } 92 } 93 } 94 95 /** 96 * Removes an audio listener. 97 * @param listener listener to remove 98 */ 99 @Override removeLineListener(LineListener listener)100 public final void removeLineListener(LineListener listener) { 101 listeners.removeElement(listener); 102 } 103 104 /** 105 * Obtains the set of controls supported by the 106 * line. If no controls are supported, returns an 107 * array of length 0. 108 * @return control set 109 */ 110 @Override getControls()111 public final Control[] getControls() { 112 Control[] returnedArray = new Control[controls.length]; 113 114 for (int i = 0; i < controls.length; i++) { 115 returnedArray[i] = controls[i]; 116 } 117 118 return returnedArray; 119 } 120 121 @Override isControlSupported(Control.Type controlType)122 public final boolean isControlSupported(Control.Type controlType) { 123 // protect against a NullPointerException 124 if (controlType == null) { 125 return false; 126 } 127 128 for (int i = 0; i < controls.length; i++) { 129 if (controlType == controls[i].getType()) { 130 return true; 131 } 132 } 133 134 return false; 135 } 136 137 @Override getControl(Control.Type controlType)138 public final Control getControl(Control.Type controlType) { 139 // protect against a NullPointerException 140 if (controlType != null) { 141 142 for (int i = 0; i < controls.length; i++) { 143 if (controlType == controls[i].getType()) { 144 return controls[i]; 145 } 146 } 147 } 148 149 throw new IllegalArgumentException("Unsupported control type: " + controlType); 150 } 151 152 // HELPER METHODS 153 154 /** 155 * This method sets the open state and generates 156 * events if it changes. 157 */ setOpen(boolean open)158 final void setOpen(boolean open) { 159 boolean sendEvents = false; 160 long position = getLongFramePosition(); 161 162 synchronized (this) { 163 if (this.open != open) { 164 this.open = open; 165 sendEvents = true; 166 } 167 } 168 169 if (sendEvents) { 170 if (open) { 171 sendEvents(new LineEvent(this, LineEvent.Type.OPEN, position)); 172 } else { 173 sendEvents(new LineEvent(this, LineEvent.Type.CLOSE, position)); 174 } 175 } 176 } 177 178 /** 179 * Send line events. 180 */ sendEvents(LineEvent event)181 final void sendEvents(LineEvent event) { 182 getEventDispatcher().sendAudioEvents(event, listeners); 183 } 184 185 /** 186 * This is an error in the API: getFramePosition 187 * should return a long value. At CD quality, 188 * the int value wraps around after 13 hours. 189 */ getFramePosition()190 public final int getFramePosition() { 191 return (int) getLongFramePosition(); 192 } 193 194 /** 195 * Return the frame position in a long value 196 * This implementation returns AudioSystem.NOT_SPECIFIED. 197 */ getLongFramePosition()198 public long getLongFramePosition() { 199 return AudioSystem.NOT_SPECIFIED; 200 } 201 202 // $$kk: 06.03.99: returns the mixer used in construction. 203 // this is a hold-over from when there was a public method like 204 // this on line and should be fixed!! getMixer()205 final AbstractMixer getMixer() { 206 return mixer; 207 } 208 getEventDispatcher()209 final EventDispatcher getEventDispatcher() { 210 // create and start the global event thread 211 //TODO need a way to stop this thread when the engine is done 212 final ThreadGroup tg = Thread.currentThread().getThreadGroup(); 213 synchronized (dispatchers) { 214 EventDispatcher eventDispatcher = dispatchers.get(tg); 215 if (eventDispatcher == null) { 216 eventDispatcher = new EventDispatcher(); 217 dispatchers.put(tg, eventDispatcher); 218 eventDispatcher.start(); 219 } 220 return eventDispatcher; 221 } 222 } 223 224 @Override open()225 public abstract void open() throws LineUnavailableException; 226 @Override close()227 public abstract void close(); 228 } 229