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 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 135 public int available() throws IOException { 136 return 0; 137 } 138 139 @Override 140 public void close() throws IOException { 141 ais.close(); 142 } 143 144 @Override 145 public AudioFormat getFormat() { 146 return targetFormat; 147 } 148 149 @Override 150 public long getFrameLength() { 151 return AudioSystem.NOT_SPECIFIED; // ais.getFrameLength(); 152 } 153 154 @Override 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 172 public boolean markSupported() { 173 return ais.markSupported(); 174 } 175 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 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 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 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 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 316 public void setValue(float newValue) { 317 super.setValue(newValue); 318 calcVolume(); 319 } 320 } 321 322 private final class Mute extends BooleanControl { 323 324 private Mute() { 325 super(BooleanControl.Type.MUTE, false, "True", "False"); 326 } 327 328 @Override 329 public void setValue(boolean newValue) { 330 super.setValue(newValue); 331 calcVolume(); 332 } 333 } 334 335 private final class ApplyReverb extends BooleanControl { 336 337 private ApplyReverb() { 338 super(BooleanControl.Type.APPLY_REVERB, false, "True", "False"); 339 } 340 341 @Override 342 public void setValue(boolean newValue) { 343 super.setValue(newValue); 344 calcVolume(); 345 } 346 347 } 348 349 private final class Balance extends FloatControl { 350 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 357 public void setValue(float newValue) { 358 super.setValue(newValue); 359 calcVolume(); 360 } 361 362 } 363 364 private final class Pan extends FloatControl { 365 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 372 public void setValue(float newValue) { 373 super.setValue(newValue); 374 balance_control.setValue(newValue); 375 } 376 377 @Override 378 public float getValue() { 379 return balance_control.getValue(); 380 } 381 382 } 383 384 private final class ReverbSend extends FloatControl { 385 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 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 401 private ChorusSend() { 402 super(CHORUS_SEND, -80f, 6.0206f, 80f / 128.0f, -1, -80f, "dB", 403 "Minimum", "", "Maximum"); 404 } 405 406 @Override 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 446 protected abstract void processControlLogic(); 447 448 protected abstract void processAudioLogic(SoftAudioBuffer[] buffers); 449 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 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 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 498 public final void addLineListener(LineListener listener) { 499 synchronized (control_mutex) { 500 listeners.add(listener); 501 } 502 } 503 504 @Override 505 public final void removeLineListener(LineListener listener) { 506 synchronized (control_mutex) { 507 listeners.add(listener); 508 } 509 } 510 511 @Override 512 public final javax.sound.sampled.Line.Info getLineInfo() { 513 return info; 514 } 515 516 @Override 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 530 public final Control[] getControls() { 531 return Arrays.copyOf(controls, controls.length); 532 } 533 534 @Override 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 }