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
26package com.sun.media.sound;
27
28import java.io.IOException;
29import java.io.InputStream;
30import java.util.ArrayList;
31import java.util.List;
32
33import javax.sound.sampled.AudioInputStream;
34import javax.sound.sampled.AudioSystem;
35
36/**
37 * Main mixer for SoftMixingMixer.
38 *
39 * @author Karl Helgason
40 */
41public final class SoftMixingMainMixer {
42
43    public static final int CHANNEL_LEFT = 0;
44
45    public static final int CHANNEL_RIGHT = 1;
46
47    public static final int CHANNEL_EFFECT1 = 2;
48
49    public static final int CHANNEL_EFFECT2 = 3;
50
51    public static final int CHANNEL_EFFECT3 = 4;
52
53    public static final int CHANNEL_EFFECT4 = 5;
54
55    public static final int CHANNEL_LEFT_DRY = 10;
56
57    public static final int CHANNEL_RIGHT_DRY = 11;
58
59    public static final int CHANNEL_SCRATCH1 = 12;
60
61    public static final int CHANNEL_SCRATCH2 = 13;
62
63    public static final int CHANNEL_CHANNELMIXER_LEFT = 14;
64
65    public static final int CHANNEL_CHANNELMIXER_RIGHT = 15;
66
67    private final SoftMixingMixer mixer;
68
69    private final AudioInputStream ais;
70
71    private final SoftAudioBuffer[] buffers;
72
73    private final SoftAudioProcessor reverb;
74
75    private final SoftAudioProcessor chorus;
76
77    private final SoftAudioProcessor agc;
78
79    private final int nrofchannels;
80
81    private final Object control_mutex;
82
83    private final List<SoftMixingDataLine> openLinesList = new ArrayList<>();
84
85    private SoftMixingDataLine[] openLines = new SoftMixingDataLine[0];
86
87    public AudioInputStream getInputStream() {
88        return ais;
89    }
90
91    void processAudioBuffers() {
92        for (int i = 0; i < buffers.length; i++) {
93            buffers[i].clear();
94        }
95
96        SoftMixingDataLine[] openLines;
97        synchronized (control_mutex) {
98            openLines = this.openLines;
99            for (int i = 0; i < openLines.length; i++) {
100                openLines[i].processControlLogic();
101            }
102            chorus.processControlLogic();
103            reverb.processControlLogic();
104            agc.processControlLogic();
105        }
106        for (int i = 0; i < openLines.length; i++) {
107            openLines[i].processAudioLogic(buffers);
108        }
109
110        chorus.processAudio();
111        reverb.processAudio();
112
113        agc.processAudio();
114
115    }
116
117    public SoftMixingMainMixer(SoftMixingMixer mixer) {
118        this.mixer = mixer;
119
120        nrofchannels = mixer.getFormat().getChannels();
121
122        int buffersize = (int) (mixer.getFormat().getSampleRate() / mixer
123                .getControlRate());
124
125        control_mutex = mixer.control_mutex;
126        buffers = new SoftAudioBuffer[16];
127        for (int i = 0; i < buffers.length; i++) {
128            buffers[i] = new SoftAudioBuffer(buffersize, mixer.getFormat());
129
130        }
131
132        reverb = new SoftReverb();
133        chorus = new SoftChorus();
134        agc = new SoftLimiter();
135
136        float samplerate = mixer.getFormat().getSampleRate();
137        float controlrate = mixer.getControlRate();
138        reverb.init(samplerate, controlrate);
139        chorus.init(samplerate, controlrate);
140        agc.init(samplerate, controlrate);
141
142        reverb.setMixMode(true);
143        chorus.setMixMode(true);
144        agc.setMixMode(false);
145
146        chorus.setInput(0, buffers[CHANNEL_EFFECT2]);
147        chorus.setOutput(0, buffers[CHANNEL_LEFT]);
148        if (nrofchannels != 1)
149            chorus.setOutput(1, buffers[CHANNEL_RIGHT]);
150        chorus.setOutput(2, buffers[CHANNEL_EFFECT1]);
151
152        reverb.setInput(0, buffers[CHANNEL_EFFECT1]);
153        reverb.setOutput(0, buffers[CHANNEL_LEFT]);
154        if (nrofchannels != 1)
155            reverb.setOutput(1, buffers[CHANNEL_RIGHT]);
156
157        agc.setInput(0, buffers[CHANNEL_LEFT]);
158        if (nrofchannels != 1)
159            agc.setInput(1, buffers[CHANNEL_RIGHT]);
160        agc.setOutput(0, buffers[CHANNEL_LEFT]);
161        if (nrofchannels != 1)
162            agc.setOutput(1, buffers[CHANNEL_RIGHT]);
163
164        InputStream in = new InputStream() {
165
166            private final SoftAudioBuffer[] buffers = SoftMixingMainMixer.this.buffers;
167
168            private final int nrofchannels = SoftMixingMainMixer.this.mixer
169                    .getFormat().getChannels();
170
171            private final int buffersize = buffers[0].getSize();
172
173            private final byte[] bbuffer = new byte[buffersize
174                    * (SoftMixingMainMixer.this.mixer.getFormat()
175                            .getSampleSizeInBits() / 8) * nrofchannels];
176
177            private int bbuffer_pos = 0;
178
179            private final byte[] single = new byte[1];
180
181            public void fillBuffer() {
182                processAudioBuffers();
183                for (int i = 0; i < nrofchannels; i++)
184                    buffers[i].get(bbuffer, i);
185                bbuffer_pos = 0;
186            }
187
188            @Override
189            public int read(byte[] b, int off, int len) {
190                int bbuffer_len = bbuffer.length;
191                int offlen = off + len;
192                byte[] bbuffer = this.bbuffer;
193                while (off < offlen)
194                    if (available() == 0)
195                        fillBuffer();
196                    else {
197                        int bbuffer_pos = this.bbuffer_pos;
198                        while (off < offlen && bbuffer_pos < bbuffer_len)
199                            b[off++] = bbuffer[bbuffer_pos++];
200                        this.bbuffer_pos = bbuffer_pos;
201                    }
202                return len;
203            }
204
205            @Override
206            public int read() throws IOException {
207                int ret = read(single);
208                if (ret == -1)
209                    return -1;
210                return single[0] & 0xFF;
211            }
212
213            @Override
214            public int available() {
215                return bbuffer.length - bbuffer_pos;
216            }
217
218            @Override
219            public void close() {
220                SoftMixingMainMixer.this.mixer.close();
221            }
222
223        };
224
225        ais = new AudioInputStream(in, mixer.getFormat(),
226                AudioSystem.NOT_SPECIFIED);
227
228    }
229
230    public void openLine(SoftMixingDataLine line) {
231        synchronized (control_mutex) {
232            openLinesList.add(line);
233            openLines = openLinesList
234                    .toArray(new SoftMixingDataLine[openLinesList.size()]);
235        }
236    }
237
238    public void closeLine(SoftMixingDataLine line) {
239        synchronized (control_mutex) {
240            openLinesList.remove(line);
241            openLines = openLinesList
242                    .toArray(new SoftMixingDataLine[openLinesList.size()]);
243            if (openLines.length == 0)
244                if (mixer.implicitOpen)
245                    mixer.close();
246        }
247    }
248
249    public SoftMixingDataLine[] getOpenLines() {
250        synchronized (control_mutex) {
251            return openLines;
252        }
253    }
254
255    public void close() {
256        SoftMixingDataLine[] openLines = this.openLines;
257        for (int i = 0; i < openLines.length; i++) {
258            openLines[i].close();
259        }
260    }
261}
262