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.Vector;
29 
30 import javax.sound.sampled.Control;
31 import javax.sound.sampled.Line;
32 import javax.sound.sampled.LineUnavailableException;
33 import javax.sound.sampled.Mixer;
34 
35 /**
36  * Abstract Mixer.  Implements Mixer (with abstract methods) and specifies
37  * some other common methods for use by our implementation.
38  *
39  * @author Kara Kytle
40  */
41 //$$fb 2002-07-26: let AbstractMixer be an AbstractLine and NOT an AbstractDataLine!
42 abstract class AbstractMixer extends AbstractLine implements Mixer {
43 
44     //  STATIC VARIABLES
45     protected static final int PCM  = 0;
46     protected static final int ULAW = 1;
47     protected static final int ALAW = 2;
48 
49 
50     // IMMUTABLE PROPERTIES
51 
52     /**
53      * Info object describing this mixer.
54      */
55     private final Mixer.Info mixerInfo;
56 
57     /**
58      * source lines provided by this mixer
59      */
60     protected Line.Info[] sourceLineInfo;
61 
62     /**
63      * target lines provided by this mixer
64      */
65     protected Line.Info[] targetLineInfo;
66 
67     /**
68      * if any line of this mixer is started
69      */
70     private boolean started = false;
71 
72     /**
73      * if this mixer had been opened manually with open()
74      * If it was, then it won't be closed automatically,
75      * only when close() is called manually.
76      */
77     private boolean manuallyOpened = false;
78 
79     // STATE VARIABLES
80 
81     /**
82      * Source lines (ports) currently open.
83      */
84     private final Vector<Line> sourceLines = new Vector<>();
85 
86     /**
87      * Target lines currently open.
88      */
89     private final Vector<Line> targetLines = new Vector<>();
90 
91     /**
92      * Constructs a new AbstractMixer.
93      * @param mixerInfo the mixer with which this line is associated
94      * @param controls set of supported controls
95      */
AbstractMixer(Mixer.Info mixerInfo, Control[] controls, Line.Info[] sourceLineInfo, Line.Info[] targetLineInfo)96     protected AbstractMixer(Mixer.Info mixerInfo,
97                             Control[] controls,
98                             Line.Info[] sourceLineInfo,
99                             Line.Info[] targetLineInfo) {
100 
101         // Line.Info, AbstractMixer, Control[]
102         super(new Line.Info(Mixer.class), null, controls);
103 
104         // setup the line part
105         this.mixer = this;
106         if (controls == null) {
107             controls = new Control[0];
108         }
109 
110         // setup the mixer part
111         this.mixerInfo = mixerInfo;
112         this.sourceLineInfo = sourceLineInfo;
113         this.targetLineInfo = targetLineInfo;
114     }
115 
116     // MIXER METHODS
117 
118     @Override
getMixerInfo()119     public final Mixer.Info getMixerInfo() {
120         return mixerInfo;
121     }
122 
123     @Override
getSourceLineInfo()124     public final Line.Info[] getSourceLineInfo() {
125         Line.Info[] localArray = new Line.Info[sourceLineInfo.length];
126         System.arraycopy(sourceLineInfo, 0, localArray, 0, sourceLineInfo.length);
127         return localArray;
128     }
129 
130     @Override
getTargetLineInfo()131     public final Line.Info[] getTargetLineInfo() {
132         Line.Info[] localArray = new Line.Info[targetLineInfo.length];
133         System.arraycopy(targetLineInfo, 0, localArray, 0, targetLineInfo.length);
134         return localArray;
135     }
136 
137     @Override
getSourceLineInfo(Line.Info info)138     public final Line.Info[] getSourceLineInfo(Line.Info info) {
139 
140         int i;
141         Vector<Line.Info> vec = new Vector<>();
142 
143         for (i = 0; i < sourceLineInfo.length; i++) {
144 
145             if (info.matches(sourceLineInfo[i])) {
146                 vec.addElement(sourceLineInfo[i]);
147             }
148         }
149 
150         Line.Info[] returnedArray = new Line.Info[vec.size()];
151         for (i = 0; i < returnedArray.length; i++) {
152             returnedArray[i] = vec.elementAt(i);
153         }
154 
155         return returnedArray;
156     }
157 
158     @Override
getTargetLineInfo(Line.Info info)159     public final Line.Info[] getTargetLineInfo(Line.Info info) {
160 
161         int i;
162         Vector<Line.Info> vec = new Vector<>();
163 
164         for (i = 0; i < targetLineInfo.length; i++) {
165 
166             if (info.matches(targetLineInfo[i])) {
167                 vec.addElement(targetLineInfo[i]);
168             }
169         }
170 
171         Line.Info[] returnedArray = new Line.Info[vec.size()];
172         for (i = 0; i < returnedArray.length; i++) {
173             returnedArray[i] = vec.elementAt(i);
174         }
175 
176         return returnedArray;
177     }
178 
179     @Override
isLineSupported(Line.Info info)180     public final boolean isLineSupported(Line.Info info) {
181 
182         int i;
183 
184         for (i = 0; i < sourceLineInfo.length; i++) {
185 
186             if (info.matches(sourceLineInfo[i])) {
187                 return true;
188             }
189         }
190 
191         for (i = 0; i < targetLineInfo.length; i++) {
192 
193             if (info.matches(targetLineInfo[i])) {
194                 return true;
195             }
196         }
197 
198         return false;
199     }
200 
201     @Override
getLine(Line.Info info)202     public abstract Line getLine(Line.Info info) throws LineUnavailableException;
203 
204     @Override
getMaxLines(Line.Info info)205     public abstract int getMaxLines(Line.Info info);
206 
implOpen()207     protected abstract void implOpen() throws LineUnavailableException;
implStart()208     protected abstract void implStart();
implStop()209     protected abstract void implStop();
implClose()210     protected abstract void implClose();
211 
212     @Override
getSourceLines()213     public final Line[] getSourceLines() {
214 
215         Line[] localLines;
216 
217         synchronized(sourceLines) {
218 
219             localLines = new Line[sourceLines.size()];
220 
221             for (int i = 0; i < localLines.length; i++) {
222                 localLines[i] = sourceLines.elementAt(i);
223             }
224         }
225 
226         return localLines;
227     }
228 
229     @Override
getTargetLines()230     public final Line[] getTargetLines() {
231 
232         Line[] localLines;
233 
234         synchronized(targetLines) {
235 
236             localLines = new Line[targetLines.size()];
237 
238             for (int i = 0; i < localLines.length; i++) {
239                 localLines[i] = targetLines.elementAt(i);
240             }
241         }
242 
243         return localLines;
244     }
245 
246     /**
247      * Default implementation always throws an exception.
248      */
249     @Override
synchronize(Line[] lines, boolean maintainSync)250     public final void synchronize(Line[] lines, boolean maintainSync) {
251         throw new IllegalArgumentException("Synchronization not supported by this mixer.");
252     }
253 
254     /**
255      * Default implementation always throws an exception.
256      */
257     @Override
unsynchronize(Line[] lines)258     public final void unsynchronize(Line[] lines) {
259         throw new IllegalArgumentException("Synchronization not supported by this mixer.");
260     }
261 
262     /**
263      * Default implementation always returns false.
264      */
265     @Override
isSynchronizationSupported(Line[] lines, boolean maintainSync)266     public final boolean isSynchronizationSupported(Line[] lines,
267                                                     boolean maintainSync) {
268         return false;
269     }
270 
271     // OVERRIDES OF ABSTRACT DATA LINE METHODS
272 
273     /**
274      * This implementation tries to open the mixer with its current format and buffer size settings.
275      */
276     @Override
open()277     public final synchronized void open() throws LineUnavailableException {
278         open(true);
279     }
280 
281     /**
282      * This implementation tries to open the mixer with its current format and buffer size settings.
283      */
open(boolean manual)284     final synchronized void open(boolean manual) throws LineUnavailableException {
285         if (!isOpen()) {
286             implOpen();
287             // if the mixer is not currently open, set open to true and send event
288             setOpen(true);
289             if (manual) {
290                 manuallyOpened = true;
291             }
292         }
293     }
294 
295     // METHOD FOR INTERNAL IMPLEMENTATION USE
296 
297     /**
298      * The default implementation of this method just determines whether
299      * this line is a source or target line, calls open(no-arg) on the
300      * mixer, and adds the line to the appropriate vector.
301      * The mixer may be opened at a format different than the line's
302      * format if it is a DataLine.
303      */
open(Line line)304     final synchronized void open(Line line) throws LineUnavailableException {
305         // $$kk: 06.11.99: ignore ourselves for now
306         if (this.equals(line)) {
307             return;
308         }
309 
310         // source line?
311         if (isSourceLine(line.getLineInfo())) {
312             if (! sourceLines.contains(line) ) {
313                 // call the no-arg open method for the mixer; it should open at its
314                 // default format if it is not open yet
315                 open(false);
316 
317                 // we opened successfully! add the line to the list
318                 sourceLines.addElement(line);
319             }
320         } else {
321             // target line?
322             if(isTargetLine(line.getLineInfo())) {
323                 if (! targetLines.contains(line) ) {
324                     // call the no-arg open method for the mixer; it should open at its
325                     // default format if it is not open yet
326                     open(false);
327 
328                     // we opened successfully!  add the line to the list
329                     targetLines.addElement(line);
330                 }
331             } else {
332                 if (Printer.err) Printer.err("Unknown line received for AbstractMixer.open(Line): " + line);
333             }
334         }
335     }
336 
337     /**
338      * Removes this line from the list of open source lines and
339      * open target lines, if it exists in either.
340      * If the list is now empty, closes the mixer.
341      */
close(Line line)342     final synchronized void close(Line line) {
343         // $$kk: 06.11.99: ignore ourselves for now
344         if (this.equals(line)) {
345             return;
346         }
347 
348         sourceLines.removeElement(line);
349         targetLines.removeElement(line);
350 
351         if (sourceLines.isEmpty() && targetLines.isEmpty() && !manuallyOpened) {
352             close();
353         }
354     }
355 
356     /**
357      * Close all lines and then close this mixer.
358      */
359     @Override
close()360     public final synchronized void close() {
361         if (isOpen()) {
362             // close all source lines
363             Line[] localLines = getSourceLines();
364             for (int i = 0; i<localLines.length; i++) {
365                 localLines[i].close();
366             }
367 
368             // close all target lines
369             localLines = getTargetLines();
370             for (int i = 0; i<localLines.length; i++) {
371                 localLines[i].close();
372             }
373 
374             implClose();
375 
376             // set the open state to false and send events
377             setOpen(false);
378         }
379         manuallyOpened = false;
380     }
381 
382     /**
383      * Starts the mixer.
384      */
start(Line line)385     final synchronized void start(Line line) {
386         // $$kk: 06.11.99: ignore ourselves for now
387         if (this.equals(line)) {
388             return;
389         }
390 
391         // we just start the mixer regardless of anything else here.
392         if (!started) {
393             implStart();
394             started = true;
395         }
396     }
397 
398     /**
399      * Stops the mixer if this was the last running line.
400      */
stop(Line line)401     final synchronized void stop(Line line) {
402         // $$kk: 06.11.99: ignore ourselves for now
403         if (this.equals(line)) {
404             return;
405         }
406 
407         @SuppressWarnings("unchecked")
408         Vector<Line> localSourceLines = (Vector<Line>)sourceLines.clone();
409         for (int i = 0; i < localSourceLines.size(); i++) {
410 
411             // if any other open line is running, return
412 
413             // this covers clips and source data lines
414             if (localSourceLines.elementAt(i) instanceof AbstractDataLine) {
415                 AbstractDataLine sourceLine = (AbstractDataLine)localSourceLines.elementAt(i);
416                 if ( sourceLine.isStartedRunning() && (!sourceLine.equals(line)) ) {
417                     return;
418                 }
419             }
420         }
421 
422         @SuppressWarnings("unchecked")
423         Vector<Line> localTargetLines = (Vector<Line>)targetLines.clone();
424         for (int i = 0; i < localTargetLines.size(); i++) {
425 
426             // if any other open line is running, return
427             // this covers target data lines
428             if (localTargetLines.elementAt(i) instanceof AbstractDataLine) {
429                 AbstractDataLine targetLine = (AbstractDataLine)localTargetLines.elementAt(i);
430                 if ( targetLine.isStartedRunning() && (!targetLine.equals(line)) ) {
431                     return;
432                 }
433             }
434         }
435 
436         // otherwise, stop
437         started = false;
438         implStop();
439     }
440 
441     /**
442      * Determines whether this is a source line for this mixer.
443      * Right now this just checks whether it's supported, but should
444      * check whether it actually belongs to this mixer....
445      */
isSourceLine(Line.Info info)446     final boolean isSourceLine(Line.Info info) {
447 
448         for (int i = 0; i < sourceLineInfo.length; i++) {
449             if (info.matches(sourceLineInfo[i])) {
450                 return true;
451             }
452         }
453 
454         return false;
455     }
456 
457     /**
458      * Determines whether this is a target line for this mixer.
459      * Right now this just checks whether it's supported, but should
460      * check whether it actually belongs to this mixer....
461      */
isTargetLine(Line.Info info)462     final boolean isTargetLine(Line.Info info) {
463 
464         for (int i = 0; i < targetLineInfo.length; i++) {
465             if (info.matches(targetLineInfo[i])) {
466                 return true;
467             }
468         }
469 
470         return false;
471     }
472 
473     /**
474      * Returns the first complete Line.Info object it finds that
475      * matches the one specified, or null if no matching Line.Info
476      * object is found.
477      */
getLineInfo(Line.Info info)478     final Line.Info getLineInfo(Line.Info info) {
479         if (info == null) {
480             return null;
481         }
482         // $$kk: 05.31.99: need to change this so that
483         // the format and buffer size get set in the
484         // returned info object for data lines??
485         for (int i = 0; i < sourceLineInfo.length; i++) {
486             if (info.matches(sourceLineInfo[i])) {
487                 return sourceLineInfo[i];
488             }
489         }
490 
491         for (int i = 0; i < targetLineInfo.length; i++) {
492             if (info.matches(targetLineInfo[i])) {
493                 return targetLineInfo[i];
494             }
495         }
496         return null;
497     }
498 }
499