1 /*
2  * Copyright (c) 2008, 2013, 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.io.IOException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32 
33 import javax.sound.sampled.AudioFormat;
34 import javax.sound.sampled.AudioSystem;
35 import javax.sound.sampled.BooleanControl;
36 import javax.sound.sampled.Control;
37 import javax.sound.sampled.Control.Type;
38 import javax.sound.sampled.DataLine;
39 import javax.sound.sampled.FloatControl;
40 import javax.sound.sampled.LineEvent;
41 import javax.sound.sampled.LineListener;
42 
43 /**
44  * General software mixing line.
45  *
46  * @author Karl Helgason
47  */
48 public abstract class SoftMixingDataLine implements DataLine {
49 
50     public static final FloatControl.Type CHORUS_SEND = new FloatControl.Type(
51             "Chorus Send") {
52     };
53 
54     protected static final class AudioFloatInputStreamResampler extends
55             AudioFloatInputStream {
56 
57         private final AudioFloatInputStream ais;
58 
59         private final AudioFormat targetFormat;
60 
61         private float[] skipbuffer;
62 
63         private SoftAbstractResampler resampler;
64 
65         private final float[] pitch = new float[1];
66 
67         private final float[] ibuffer2;
68 
69         private final float[][] ibuffer;
70 
71         private float ibuffer_index = 0;
72 
73         private int ibuffer_len = 0;
74 
75         private int nrofchannels = 0;
76 
77         private float[][] cbuffer;
78 
79         private final int buffer_len = 512;
80 
81         private final int pad;
82 
83         private final int pad2;
84 
85         private final float[] ix = new float[1];
86 
87         private final int[] ox = new int[1];
88 
89         private float[][] mark_ibuffer = null;
90 
91         private float mark_ibuffer_index = 0;
92 
93         private int mark_ibuffer_len = 0;
94 
AudioFloatInputStreamResampler(AudioFloatInputStream ais, AudioFormat format)95         public AudioFloatInputStreamResampler(AudioFloatInputStream ais,
96                 AudioFormat format) {
97             this.ais = ais;
98             AudioFormat sourceFormat = ais.getFormat();
99             targetFormat = new AudioFormat(sourceFormat.getEncoding(), format
100                     .getSampleRate(), sourceFormat.getSampleSizeInBits(),
101                     sourceFormat.getChannels(), sourceFormat.getFrameSize(),
102                     format.getSampleRate(), sourceFormat.isBigEndian());
103             nrofchannels = targetFormat.getChannels();
104             Object interpolation = format.getProperty("interpolation");
105             if (interpolation != null && (interpolation instanceof String)) {
106                 String resamplerType = (String) interpolation;
107                 if (resamplerType.equalsIgnoreCase("point"))
108                     this.resampler = new SoftPointResampler();
109                 if (resamplerType.equalsIgnoreCase("linear"))
110                     this.resampler = new SoftLinearResampler2();
111                 if (resamplerType.equalsIgnoreCase("linear1"))
112                     this.resampler = new SoftLinearResampler();
113                 if (resamplerType.equalsIgnoreCase("linear2"))
114                     this.resampler = new SoftLinearResampler2();
115                 if (resamplerType.equalsIgnoreCase("cubic"))
116                     this.resampler = new SoftCubicResampler();
117                 if (resamplerType.equalsIgnoreCase("lanczos"))
118                     this.resampler = new SoftLanczosResampler();
119                 if (resamplerType.equalsIgnoreCase("sinc"))
120                     this.resampler = new SoftSincResampler();
121             }
122             if (resampler == null)
123                 resampler = new SoftLinearResampler2(); // new
124             // SoftLinearResampler2();
125             pitch[0] = sourceFormat.getSampleRate() / format.getSampleRate();
126             pad = resampler.getPadding();
127             pad2 = pad * 2;
128             ibuffer = new float[nrofchannels][buffer_len + pad2];
129             ibuffer2 = new float[nrofchannels * buffer_len];
130             ibuffer_index = buffer_len + pad;
131             ibuffer_len = buffer_len;
132         }
133 
134         @Override
available()135         public int available() throws IOException {
136             return 0;
137         }
138 
139         @Override
close()140         public void close() throws IOException {
141             ais.close();
142         }
143 
144         @Override
getFormat()145         public AudioFormat getFormat() {
146             return targetFormat;
147         }
148 
149         @Override
getFrameLength()150         public long getFrameLength() {
151             return AudioSystem.NOT_SPECIFIED; // ais.getFrameLength();
152         }
153 
154         @Override
mark(int readlimit)155         public void mark(int readlimit) {
156             ais.mark((int) (readlimit * pitch[0]));
157             mark_ibuffer_index = ibuffer_index;
158             mark_ibuffer_len = ibuffer_len;
159             if (mark_ibuffer == null) {
160                 mark_ibuffer = new float[ibuffer.length][ibuffer[0].length];
161             }
162             for (int c = 0; c < ibuffer.length; c++) {
163                 float[] from = ibuffer[c];
164                 float[] to = mark_ibuffer[c];
165                 for (int i = 0; i < to.length; i++) {
166                     to[i] = from[i];
167                 }
168             }
169         }
170 
171         @Override
markSupported()172         public boolean markSupported() {
173             return ais.markSupported();
174         }
175 
readNextBuffer()176         private void readNextBuffer() throws IOException {
177 
178             if (ibuffer_len == -1)
179                 return;
180 
181             for (int c = 0; c < nrofchannels; c++) {
182                 float[] buff = ibuffer[c];
183                 int buffer_len_pad = ibuffer_len + pad2;
184                 for (int i = ibuffer_len, ix = 0; i < buffer_len_pad; i++, ix++) {
185                     buff[ix] = buff[i];
186                 }
187             }
188 
189             ibuffer_index -= (ibuffer_len);
190 
191             ibuffer_len = ais.read(ibuffer2);
192             if (ibuffer_len >= 0) {
193                 while (ibuffer_len < ibuffer2.length) {
194                     int ret = ais.read(ibuffer2, ibuffer_len, ibuffer2.length
195                             - ibuffer_len);
196                     if (ret == -1)
197                         break;
198                     ibuffer_len += ret;
199                 }
200                 Arrays.fill(ibuffer2, ibuffer_len, ibuffer2.length, 0);
201                 ibuffer_len /= nrofchannels;
202             } else {
203                 Arrays.fill(ibuffer2, 0, ibuffer2.length, 0);
204             }
205 
206             int ibuffer2_len = ibuffer2.length;
207             for (int c = 0; c < nrofchannels; c++) {
208                 float[] buff = ibuffer[c];
209                 for (int i = c, ix = pad2; i < ibuffer2_len; i += nrofchannels, ix++) {
210                     buff[ix] = ibuffer2[i];
211                 }
212             }
213 
214         }
215 
216         @Override
read(float[] b, int off, int len)217         public int read(float[] b, int off, int len) throws IOException {
218 
219             if (cbuffer == null || cbuffer[0].length < len / nrofchannels) {
220                 cbuffer = new float[nrofchannels][len / nrofchannels];
221             }
222             if (ibuffer_len == -1)
223                 return -1;
224             if (len < 0)
225                 return 0;
226             int remain = len / nrofchannels;
227             int destPos = 0;
228             int in_end = ibuffer_len;
229             while (remain > 0) {
230                 if (ibuffer_len >= 0) {
231                     if (ibuffer_index >= (ibuffer_len + pad))
232                         readNextBuffer();
233                     in_end = ibuffer_len + pad;
234                 }
235 
236                 if (ibuffer_len < 0) {
237                     in_end = pad2;
238                     if (ibuffer_index >= in_end)
239                         break;
240                 }
241 
242                 if (ibuffer_index < 0)
243                     break;
244                 int preDestPos = destPos;
245                 for (int c = 0; c < nrofchannels; c++) {
246                     ix[0] = ibuffer_index;
247                     ox[0] = destPos;
248                     float[] buff = ibuffer[c];
249                     resampler.interpolate(buff, ix, in_end, pitch, 0,
250                             cbuffer[c], ox, len / nrofchannels);
251                 }
252                 ibuffer_index = ix[0];
253                 destPos = ox[0];
254                 remain -= destPos - preDestPos;
255             }
256             for (int c = 0; c < nrofchannels; c++) {
257                 int ix = 0;
258                 float[] buff = cbuffer[c];
259                 for (int i = c; i < b.length; i += nrofchannels) {
260                     b[i] = buff[ix++];
261                 }
262             }
263             return len - remain * nrofchannels;
264         }
265 
266         @Override
reset()267         public void reset() throws IOException {
268             ais.reset();
269             if (mark_ibuffer == null)
270                 return;
271             ibuffer_index = mark_ibuffer_index;
272             ibuffer_len = mark_ibuffer_len;
273             for (int c = 0; c < ibuffer.length; c++) {
274                 float[] from = mark_ibuffer[c];
275                 float[] to = ibuffer[c];
276                 for (int i = 0; i < to.length; i++) {
277                     to[i] = from[i];
278                 }
279             }
280 
281         }
282 
283         @Override
skip(long len)284         public long skip(long len) throws IOException {
285             if (len > 0)
286                 return 0;
287             if (skipbuffer == null)
288                 skipbuffer = new float[1024 * targetFormat.getFrameSize()];
289             float[] l_skipbuffer = skipbuffer;
290             long remain = len;
291             while (remain > 0) {
292                 int ret = read(l_skipbuffer, 0, (int) Math.min(remain,
293                         skipbuffer.length));
294                 if (ret < 0) {
295                     if (remain == len)
296                         return ret;
297                     break;
298                 }
299                 remain -= ret;
300             }
301             return len - remain;
302 
303         }
304 
305     }
306 
307     private final class Gain extends FloatControl {
308 
Gain()309         private Gain() {
310 
311             super(FloatControl.Type.MASTER_GAIN, -80f, 6.0206f, 80f / 128.0f,
312                     -1, 0.0f, "dB", "Minimum", "", "Maximum");
313         }
314 
315         @Override
setValue(float newValue)316         public void setValue(float newValue) {
317             super.setValue(newValue);
318             calcVolume();
319         }
320     }
321 
322     private final class Mute extends BooleanControl {
323 
Mute()324         private Mute() {
325             super(BooleanControl.Type.MUTE, false, "True", "False");
326         }
327 
328         @Override
setValue(boolean newValue)329         public void setValue(boolean newValue) {
330             super.setValue(newValue);
331             calcVolume();
332         }
333     }
334 
335     private final class ApplyReverb extends BooleanControl {
336 
ApplyReverb()337         private ApplyReverb() {
338             super(BooleanControl.Type.APPLY_REVERB, false, "True", "False");
339         }
340 
341         @Override
setValue(boolean newValue)342         public void setValue(boolean newValue) {
343             super.setValue(newValue);
344             calcVolume();
345         }
346 
347     }
348 
349     private final class Balance extends FloatControl {
350 
Balance()351         private Balance() {
352             super(FloatControl.Type.BALANCE, -1.0f, 1.0f, (1.0f / 128.0f), -1,
353                     0.0f, "", "Left", "Center", "Right");
354         }
355 
356         @Override
setValue(float newValue)357         public void setValue(float newValue) {
358             super.setValue(newValue);
359             calcVolume();
360         }
361 
362     }
363 
364     private final class Pan extends FloatControl {
365 
Pan()366         private Pan() {
367             super(FloatControl.Type.PAN, -1.0f, 1.0f, (1.0f / 128.0f), -1,
368                     0.0f, "", "Left", "Center", "Right");
369         }
370 
371         @Override
setValue(float newValue)372         public void setValue(float newValue) {
373             super.setValue(newValue);
374             balance_control.setValue(newValue);
375         }
376 
377         @Override
getValue()378         public float getValue() {
379             return balance_control.getValue();
380         }
381 
382     }
383 
384     private final class ReverbSend extends FloatControl {
385 
ReverbSend()386         private ReverbSend() {
387             super(FloatControl.Type.REVERB_SEND, -80f, 6.0206f, 80f / 128.0f,
388                     -1, -80f, "dB", "Minimum", "", "Maximum");
389         }
390 
391         @Override
setValue(float newValue)392         public void setValue(float newValue) {
393             super.setValue(newValue);
394             balance_control.setValue(newValue);
395         }
396 
397     }
398 
399     private final class ChorusSend extends FloatControl {
400 
ChorusSend()401         private ChorusSend() {
402             super(CHORUS_SEND, -80f, 6.0206f, 80f / 128.0f, -1, -80f, "dB",
403                     "Minimum", "", "Maximum");
404         }
405 
406         @Override
setValue(float newValue)407         public void setValue(float newValue) {
408             super.setValue(newValue);
409             balance_control.setValue(newValue);
410         }
411 
412     }
413 
414     private final Gain gain_control = new Gain();
415 
416     private final Mute mute_control = new Mute();
417 
418     private final Balance balance_control = new Balance();
419 
420     private final Pan pan_control = new Pan();
421 
422     private final ReverbSend reverbsend_control = new ReverbSend();
423 
424     private final ChorusSend chorussend_control = new ChorusSend();
425 
426     private final ApplyReverb apply_reverb = new ApplyReverb();
427 
428     private final Control[] controls;
429 
430     float leftgain = 1;
431 
432     float rightgain = 1;
433 
434     float eff1gain = 0;
435 
436     float eff2gain = 0;
437 
438     List<LineListener> listeners = new ArrayList<>();
439 
440     final Object control_mutex;
441 
442     SoftMixingMixer mixer;
443 
444     DataLine.Info info;
445 
processControlLogic()446     protected abstract void processControlLogic();
447 
processAudioLogic(SoftAudioBuffer[] buffers)448     protected abstract void processAudioLogic(SoftAudioBuffer[] buffers);
449 
SoftMixingDataLine(SoftMixingMixer mixer, DataLine.Info info)450     SoftMixingDataLine(SoftMixingMixer mixer, DataLine.Info info) {
451         this.mixer = mixer;
452         this.info = info;
453         this.control_mutex = mixer.control_mutex;
454 
455         controls = new Control[] { gain_control, mute_control, balance_control,
456                 pan_control, reverbsend_control, chorussend_control,
457                 apply_reverb };
458         calcVolume();
459     }
460 
calcVolume()461     final void calcVolume() {
462         synchronized (control_mutex) {
463             double gain = Math.pow(10.0, gain_control.getValue() / 20.0);
464             if (mute_control.getValue())
465                 gain = 0;
466             leftgain = (float) gain;
467             rightgain = (float) gain;
468             if (mixer.getFormat().getChannels() > 1) {
469                 // -1 = Left, 0 Center, 1 = Right
470                 double balance = balance_control.getValue();
471                 if (balance > 0)
472                     leftgain *= (1 - balance);
473                 else
474                     rightgain *= (1 + balance);
475 
476             }
477         }
478 
479         eff1gain = (float) Math.pow(10.0, reverbsend_control.getValue() / 20.0);
480         eff2gain = (float) Math.pow(10.0, chorussend_control.getValue() / 20.0);
481 
482         if (!apply_reverb.getValue()) {
483             eff1gain = 0;
484         }
485     }
486 
sendEvent(LineEvent event)487     final void sendEvent(LineEvent event) {
488         if (listeners.size() == 0)
489             return;
490         LineListener[] listener_array = listeners
491                 .toArray(new LineListener[listeners.size()]);
492         for (LineListener listener : listener_array) {
493             listener.update(event);
494         }
495     }
496 
497     @Override
addLineListener(LineListener listener)498     public final void addLineListener(LineListener listener) {
499         synchronized (control_mutex) {
500             listeners.add(listener);
501         }
502     }
503 
504     @Override
removeLineListener(LineListener listener)505     public final void removeLineListener(LineListener listener) {
506         synchronized (control_mutex) {
507             listeners.add(listener);
508         }
509     }
510 
511     @Override
getLineInfo()512     public final javax.sound.sampled.Line.Info getLineInfo() {
513         return info;
514     }
515 
516     @Override
getControl(Type control)517     public final Control getControl(Type control) {
518         if (control != null) {
519             for (int i = 0; i < controls.length; i++) {
520                 if (controls[i].getType() == control) {
521                     return controls[i];
522                 }
523             }
524         }
525         throw new IllegalArgumentException("Unsupported control type : "
526                 + control);
527     }
528 
529     @Override
getControls()530     public final Control[] getControls() {
531         return Arrays.copyOf(controls, controls.length);
532     }
533 
534     @Override
isControlSupported(Type control)535     public final boolean isControlSupported(Type control) {
536         if (control != null) {
537             for (int i = 0; i < controls.length; i++) {
538                 if (controls[i].getType() == control) {
539                     return true;
540                 }
541             }
542         }
543         return false;
544     }
545 }
546