1313 */
1414public class LinesHolder {
1515 private static final Logger LOGGER = Logger .getLogger (LinesHolder .class );
16- private final Map <Mixer , List <Line >> openLines = new HashMap <>();
17- private final Object waitLineLock = new Object ();
16+ private final Map <Mixer , Line > openLines = new HashMap <>();
1817
1918 LinesHolder () {
2019 }
@@ -29,7 +28,7 @@ private static List<Mixer> findSupportingMixersFor(@NotNull Line.Info info) thro
2928 }
3029
3130 if (mixers .isEmpty ())
32- throw new MixerException (String .format ("Couldn't find a suitable mixer, line : %s, available: %s" , info , Arrays .toString (AudioSystem .getMixerInfo ())));
31+ throw new MixerException (String .format ("Couldn't find a suitable mixer, openLine : %s, available: %s" , info , Arrays .toString (AudioSystem .getMixerInfo ())));
3332 else
3433 return mixers ;
3534 }
@@ -53,74 +52,29 @@ private static Mixer findMixer(@NotNull List<Mixer> mixers, @Nullable String[] k
5352 return list .get (0 );
5453 }
5554
56- @ NotNull
57- public LineWrapper getLine (@ NotNull Mixer mixer , @ NotNull DataLine .Info info ) throws LineUnavailableException {
58- Line line = getLineOrNull (mixer , info );
59- if (line == null ) return new LineWrapper (mixer , info );
60- else return new LineWrapper ((SourceDataLine ) line );
61- }
62-
63- @ Nullable
64- private Line getLineOrNull (@ NotNull Mixer mixer , @ NotNull DataLine .Info info ) throws LineUnavailableException {
65- List <Line > lines ;
66- synchronized (openLines ) {
67- lines = openLines .computeIfAbsent (mixer , k -> new ArrayList <>());
68- if (lines .isEmpty ()) {
69- Line line = mixer .getLine (info );
70- lines .add (line );
71- LOGGER .debug (String .format ("Got first line from mixer '%s'" , mixer .getMixerInfo ().getName ()));
72- return line ;
73- }
55+ private static boolean isCompatible (@ NotNull DataLine .Info line , @ NotNull DataLine .Info other ) {
56+ for (AudioFormat format : other .getFormats ()) {
57+ if (!line .isFormatSupported (format ))
58+ return false ;
7459 }
7560
76- int max = mixer .getMaxLines (info );
77- LOGGER .debug (String .format ("Mixer '%s' has %d lines in use, max: %d" , mixer .getMixerInfo ().getName (), lines .size (), max ));
78-
79- if (max == AudioSystem .NOT_SPECIFIED ) {
80- Line line = mixer .getLine (info );
81- lines .add (line );
82- return line ;
83- } else {
84- int count = 0 ;
85- for (Line line : lines ) {
86- if (line .getLineInfo ().matches (info ))
87- count ++;
88- }
61+ return true ;
62+ }
8963
90- if (max < count ) {
91- Line line = mixer .getLine (info );
92- lines .add (line );
93- return line ;
94- } else {
95- return null ;
96- }
97- }
64+ @ NotNull
65+ public LineWrapper getLine (@ NotNull Mixer mixer , @ NotNull DataLine .Info info ) {
66+ return new LineWrapper (mixer , info );
9867 }
9968
10069 @ NotNull
101- public LineWrapper getLineFor (@ NotNull Player .Configuration conf , @ NotNull AudioFormat format ) throws LineUnavailableException , MixerException {
70+ public LineWrapper getLineFor (@ NotNull Player .Configuration conf , @ NotNull AudioFormat format ) throws MixerException {
10271 DataLine .Info info = new DataLine .Info (SourceDataLine .class , format , AudioSystem .NOT_SPECIFIED );
10372 List <Mixer > mixers = findSupportingMixersFor (info );
10473 if (conf .logAvailableMixers ()) LOGGER .info ("Available mixers: " + Utils .mixersToString (mixers ));
10574 Mixer mixer = findMixer (mixers , conf .mixerSearchKeywords ());
106- LOGGER .info (String .format ("Mixer for playback '%s', maxLines: %d" , mixer .getMixerInfo ().getName (), mixer .getMaxLines (info )));
10775 return getLine (mixer , info );
10876 }
10977
110- private void lineClosed (@ NotNull Line line ) {
111- synchronized (openLines ) {
112- for (List <Line > lines : openLines .values ()) {
113- if (lines .remove (line )) {
114- LOGGER .debug (String .format ("Removed closed line, remaining: %d" , lines .size ()));
115- synchronized (waitLineLock ) {
116- waitLineLock .notifyAll ();
117- return ;
118- }
119- }
120- }
121- }
122- }
123-
12478 public static class MixerException extends Exception {
12579 MixerException (String message ) {
12680 super (message );
@@ -130,47 +84,69 @@ public static class MixerException extends Exception {
13084 public class LineWrapper {
13185 private Mixer mixer ;
13286 private DataLine .Info info ;
133- private SourceDataLine line ;
87+ private SourceDataLine openLine ;
13488
13589 private LineWrapper (@ NotNull Mixer mixer , @ NotNull DataLine .Info info ) {
13690 this .mixer = mixer ;
13791 this .info = info ;
13892 }
13993
140- private LineWrapper (@ NotNull SourceDataLine line ) {
141- this .line = line ;
94+ public void write (byte [] buffer , int from , int to ) {
95+ if (openLine == null ) throw new IllegalStateException ();
96+ openLine .write (buffer , from , to );
14297 }
14398
144- @ NotNull
145- public SourceDataLine waitAndOpen (@ NotNull AudioFormat format ) throws LineUnavailableException {
146- if (line != null ) {
147- line .open (format );
148- return line ;
149- }
150-
151- if (info == null || mixer == null ) throw new IllegalStateException ("Line and info are both null!" );
99+ public void open (@ NotNull AudioFormat format ) throws LineUnavailableException , InterruptedException {
100+ Line line = openLines .get (mixer );
101+ if (line != null && isCompatible ((DataLine .Info ) line .getLineInfo (), info )) {
102+ openLine = (SourceDataLine ) line ;
103+ synchronized (openLine ) {
104+ openLine .wait ();
105+ }
152106
153- while (line == null ) {
154- synchronized (waitLineLock ) {
155- try {
156- waitLineLock .wait ();
157- line = (SourceDataLine ) getLineOrNull (mixer , info );
158- } catch (InterruptedException ex ) {
159- LOGGER .fatal ("Interrupted while waiting for line! Retrying." , ex );
107+ LOGGER .trace (String .format ("Reused line for mixer '%s'." , mixer .getMixerInfo ().getName ()));
108+ } else {
109+ if (line != null ) {
110+ synchronized (line ) {
111+ line .wait ();
112+ line .close ();
160113 }
161114 }
162- }
163115
164- line .open (format );
165- LOGGER .trace (String .format ("Line opened for mixer '%s'." , mixer .getMixerInfo ().getName ()));
166- return line ;
116+ openLine = (SourceDataLine ) mixer .getLine (info );
117+ openLines .put (mixer , openLine );
118+ openLine .open (format );
119+ LOGGER .trace (String .format ("New line opened for mixer '%s'." , mixer .getMixerInfo ().getName ()));
120+ }
167121 }
168122
169123 public void close () {
170- if (line != null ) {
171- line .close ();
172- lineClosed (line );
124+ if (openLine != null ) {
125+ synchronized (openLine ) {
126+ openLine .notifyAll ();
127+ }
173128 }
174129 }
130+
131+ public void stop () {
132+ if (openLine == null ) throw new IllegalStateException ();
133+ openLine .stop ();
134+ }
135+
136+ public void start () {
137+ if (openLine == null ) throw new IllegalStateException ();
138+ openLine .start ();
139+ }
140+
141+ public boolean isControlSupported (@ NotNull Control .Type type ) {
142+ if (openLine == null ) throw new IllegalStateException ();
143+ return openLine .isControlSupported (type );
144+ }
145+
146+ @ NotNull
147+ public Control getControl (@ NotNull Control .Type type ) {
148+ if (openLine == null ) throw new IllegalStateException ();
149+ return openLine .getControl (type );
150+ }
175151 }
176152}
0 commit comments