Save This Page
Home » openjdk-7 » com.sun.media » sound » [javadoc | source]
    1   /*
    2    * Copyright (c) 2008, 2010, 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   package com.sun.media.sound;
   26   
   27   import java.io.IOException;
   28   import java.util.ArrayList;
   29   import java.util.List;
   30   
   31   import javax.sound.sampled.AudioFormat;
   32   import javax.sound.sampled.AudioInputStream;
   33   import javax.sound.sampled.AudioSystem;
   34   import javax.sound.sampled.Clip;
   35   import javax.sound.sampled.Control;
   36   import javax.sound.sampled.DataLine;
   37   import javax.sound.sampled.Line;
   38   import javax.sound.sampled.LineEvent;
   39   import javax.sound.sampled.LineListener;
   40   import javax.sound.sampled.LineUnavailableException;
   41   import javax.sound.sampled.Mixer;
   42   import javax.sound.sampled.SourceDataLine;
   43   import javax.sound.sampled.AudioFormat.Encoding;
   44   import javax.sound.sampled.Control.Type;
   45   
   46   /**
   47    * Software audio mixer
   48    *
   49    * @author Karl Helgason
   50    */
   51   public class SoftMixingMixer implements Mixer {
   52   
   53       private static class Info extends Mixer.Info {
   54           public Info() {
   55               super(INFO_NAME, INFO_VENDOR, INFO_DESCRIPTION, INFO_VERSION);
   56           }
   57       }
   58   
   59       protected static final String INFO_NAME = "Gervill Sound Mixer";
   60   
   61       protected static final String INFO_VENDOR = "OpenJDK Proposal";
   62   
   63       protected static final String INFO_DESCRIPTION = "Software Sound Mixer";
   64   
   65       protected static final String INFO_VERSION = "1.0";
   66   
   67       protected final static Mixer.Info info = new Info();
   68   
   69       protected Object control_mutex = this;
   70   
   71       protected boolean implicitOpen = false;
   72   
   73       private boolean open = false;
   74   
   75       private SoftMixingMainMixer mainmixer = null;
   76   
   77       private AudioFormat format = new AudioFormat(44100, 16, 2, true, false);
   78   
   79       private SourceDataLine sourceDataLine = null;
   80   
   81       private SoftAudioPusher pusher = null;
   82   
   83       private AudioInputStream pusher_stream = null;
   84   
   85       private float controlrate = 147f;
   86   
   87       private long latency = 100000; // 100 msec
   88   
   89       private boolean jitter_correction = false;
   90   
   91       private List<LineListener> listeners = new ArrayList<LineListener>();
   92   
   93       private javax.sound.sampled.Line.Info[] sourceLineInfo;
   94   
   95       public SoftMixingMixer() {
   96   
   97           sourceLineInfo = new javax.sound.sampled.Line.Info[2];
   98   
   99           ArrayList<AudioFormat> formats = new ArrayList<AudioFormat>();
  100           for (int channels = 1; channels <= 2; channels++) {
  101               formats.add(new AudioFormat(Encoding.PCM_SIGNED,
  102                       AudioSystem.NOT_SPECIFIED, 8, channels, channels,
  103                       AudioSystem.NOT_SPECIFIED, false));
  104               formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
  105                       AudioSystem.NOT_SPECIFIED, 8, channels, channels,
  106                       AudioSystem.NOT_SPECIFIED, false));
  107               for (int bits = 16; bits < 32; bits += 8) {
  108                   formats.add(new AudioFormat(Encoding.PCM_SIGNED,
  109                           AudioSystem.NOT_SPECIFIED, bits, channels, channels
  110                                   * bits / 8, AudioSystem.NOT_SPECIFIED, false));
  111                   formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
  112                           AudioSystem.NOT_SPECIFIED, bits, channels, channels
  113                                   * bits / 8, AudioSystem.NOT_SPECIFIED, false));
  114                   formats.add(new AudioFormat(Encoding.PCM_SIGNED,
  115                           AudioSystem.NOT_SPECIFIED, bits, channels, channels
  116                                   * bits / 8, AudioSystem.NOT_SPECIFIED, true));
  117                   formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
  118                           AudioSystem.NOT_SPECIFIED, bits, channels, channels
  119                                   * bits / 8, AudioSystem.NOT_SPECIFIED, true));
  120               }
  121               formats.add(new AudioFormat(Encoding.PCM_FLOAT,
  122                       AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
  123                       AudioSystem.NOT_SPECIFIED, false));
  124               formats.add(new AudioFormat(Encoding.PCM_FLOAT,
  125                       AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
  126                       AudioSystem.NOT_SPECIFIED, true));
  127               formats.add(new AudioFormat(Encoding.PCM_FLOAT,
  128                       AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
  129                       AudioSystem.NOT_SPECIFIED, false));
  130               formats.add(new AudioFormat(Encoding.PCM_FLOAT,
  131                       AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
  132                       AudioSystem.NOT_SPECIFIED, true));
  133           }
  134           AudioFormat[] formats_array = formats.toArray(new AudioFormat[formats
  135                   .size()]);
  136           sourceLineInfo[0] = new DataLine.Info(SourceDataLine.class,
  137                   formats_array, AudioSystem.NOT_SPECIFIED,
  138                   AudioSystem.NOT_SPECIFIED);
  139           sourceLineInfo[1] = new DataLine.Info(Clip.class, formats_array,
  140                   AudioSystem.NOT_SPECIFIED, AudioSystem.NOT_SPECIFIED);
  141       }
  142   
  143       public Line getLine(Line.Info info) throws LineUnavailableException {
  144   
  145           if (!isLineSupported(info))
  146               throw new IllegalArgumentException("Line unsupported: " + info);
  147   
  148           if ((info.getLineClass() == SourceDataLine.class)) {
  149               return new SoftMixingSourceDataLine(this, (DataLine.Info) info);
  150           }
  151           if ((info.getLineClass() == Clip.class)) {
  152               return new SoftMixingClip(this, (DataLine.Info) info);
  153           }
  154   
  155           throw new IllegalArgumentException("Line unsupported: " + info);
  156       }
  157   
  158       public int getMaxLines(Line.Info info) {
  159           if (info.getLineClass() == SourceDataLine.class)
  160               return AudioSystem.NOT_SPECIFIED;
  161           if (info.getLineClass() == Clip.class)
  162               return AudioSystem.NOT_SPECIFIED;
  163           return 0;
  164       }
  165   
  166       public javax.sound.sampled.Mixer.Info getMixerInfo() {
  167           return info;
  168       }
  169   
  170       public javax.sound.sampled.Line.Info[] getSourceLineInfo() {
  171           Line.Info[] localArray = new Line.Info[sourceLineInfo.length];
  172           System.arraycopy(sourceLineInfo, 0, localArray, 0,
  173                   sourceLineInfo.length);
  174           return localArray;
  175       }
  176   
  177       public javax.sound.sampled.Line.Info[] getSourceLineInfo(
  178               javax.sound.sampled.Line.Info info) {
  179           int i;
  180           ArrayList<javax.sound.sampled.Line.Info> infos = new ArrayList<javax.sound.sampled.Line.Info>();
  181   
  182           for (i = 0; i < sourceLineInfo.length; i++) {
  183               if (info.matches(sourceLineInfo[i])) {
  184                   infos.add(sourceLineInfo[i]);
  185               }
  186           }
  187           return infos.toArray(new Line.Info[infos.size()]);
  188       }
  189   
  190       public Line[] getSourceLines() {
  191   
  192           Line[] localLines;
  193   
  194           synchronized (control_mutex) {
  195   
  196               if (mainmixer == null)
  197                   return new Line[0];
  198               SoftMixingDataLine[] sourceLines = mainmixer.getOpenLines();
  199   
  200               localLines = new Line[sourceLines.length];
  201   
  202               for (int i = 0; i < localLines.length; i++) {
  203                   localLines[i] = sourceLines[i];
  204               }
  205           }
  206   
  207           return localLines;
  208       }
  209   
  210       public javax.sound.sampled.Line.Info[] getTargetLineInfo() {
  211           return new javax.sound.sampled.Line.Info[0];
  212       }
  213   
  214       public javax.sound.sampled.Line.Info[] getTargetLineInfo(
  215               javax.sound.sampled.Line.Info info) {
  216           return new javax.sound.sampled.Line.Info[0];
  217       }
  218   
  219       public Line[] getTargetLines() {
  220           return new Line[0];
  221       }
  222   
  223       public boolean isLineSupported(javax.sound.sampled.Line.Info info) {
  224           if (info != null) {
  225               for (int i = 0; i < sourceLineInfo.length; i++) {
  226                   if (info.matches(sourceLineInfo[i])) {
  227                       return true;
  228                   }
  229               }
  230           }
  231           return false;
  232       }
  233   
  234       public boolean isSynchronizationSupported(Line[] lines, boolean maintainSync) {
  235           return false;
  236       }
  237   
  238       public void synchronize(Line[] lines, boolean maintainSync) {
  239           throw new IllegalArgumentException(
  240                   "Synchronization not supported by this mixer.");
  241       }
  242   
  243       public void unsynchronize(Line[] lines) {
  244           throw new IllegalArgumentException(
  245                   "Synchronization not supported by this mixer.");
  246       }
  247   
  248       public void addLineListener(LineListener listener) {
  249           synchronized (control_mutex) {
  250               listeners.add(listener);
  251           }
  252       }
  253   
  254       private void sendEvent(LineEvent event) {
  255           if (listeners.size() == 0)
  256               return;
  257           LineListener[] listener_array = listeners
  258                   .toArray(new LineListener[listeners.size()]);
  259           for (LineListener listener : listener_array) {
  260               listener.update(event);
  261           }
  262       }
  263   
  264       public void close() {
  265           if (!isOpen())
  266               return;
  267   
  268           sendEvent(new LineEvent(this, LineEvent.Type.CLOSE,
  269                   AudioSystem.NOT_SPECIFIED));
  270   
  271           SoftAudioPusher pusher_to_be_closed = null;
  272           AudioInputStream pusher_stream_to_be_closed = null;
  273           synchronized (control_mutex) {
  274               if (pusher != null) {
  275                   pusher_to_be_closed = pusher;
  276                   pusher_stream_to_be_closed = pusher_stream;
  277                   pusher = null;
  278                   pusher_stream = null;
  279               }
  280           }
  281   
  282           if (pusher_to_be_closed != null) {
  283               // Pusher must not be closed synchronized against control_mutex
  284               // this may result in synchronized conflict between pusher and
  285               // current thread.
  286               pusher_to_be_closed.stop();
  287   
  288               try {
  289                   pusher_stream_to_be_closed.close();
  290               } catch (IOException e) {
  291                   e.printStackTrace();
  292               }
  293           }
  294   
  295           synchronized (control_mutex) {
  296   
  297               if (mainmixer != null)
  298                   mainmixer.close();
  299               open = false;
  300   
  301               if (sourceDataLine != null) {
  302                   sourceDataLine.drain();
  303                   sourceDataLine.close();
  304                   sourceDataLine = null;
  305               }
  306   
  307           }
  308   
  309       }
  310   
  311       public Control getControl(Type control) {
  312           throw new IllegalArgumentException("Unsupported control type : "
  313                   + control);
  314       }
  315   
  316       public Control[] getControls() {
  317           return new Control[0];
  318       }
  319   
  320       public javax.sound.sampled.Line.Info getLineInfo() {
  321           return new Line.Info(Mixer.class);
  322       }
  323   
  324       public boolean isControlSupported(Type control) {
  325           return false;
  326       }
  327   
  328       public boolean isOpen() {
  329           synchronized (control_mutex) {
  330               return open;
  331           }
  332       }
  333   
  334       public void open() throws LineUnavailableException {
  335           if (isOpen()) {
  336               implicitOpen = false;
  337               return;
  338           }
  339           open(null);
  340       }
  341   
  342       public void open(SourceDataLine line) throws LineUnavailableException {
  343           if (isOpen()) {
  344               implicitOpen = false;
  345               return;
  346           }
  347           synchronized (control_mutex) {
  348   
  349               try {
  350   
  351                   if (line != null)
  352                       format = line.getFormat();
  353   
  354                   AudioInputStream ais = openStream(getFormat());
  355   
  356                   if (line == null) {
  357                       synchronized (SoftMixingMixerProvider.mutex) {
  358                           SoftMixingMixerProvider.lockthread = Thread
  359                                   .currentThread();
  360                       }
  361   
  362                       try {
  363                           Mixer defaultmixer = AudioSystem.getMixer(null);
  364                           if (defaultmixer != null)
  365                           {
  366                               // Search for suitable line
  367   
  368                               DataLine.Info idealinfo = null;
  369                               AudioFormat idealformat = null;
  370   
  371                               Line.Info[] lineinfos = defaultmixer.getSourceLineInfo();
  372                               idealFound:
  373                               for (int i = 0; i < lineinfos.length; i++) {
  374                                   if(lineinfos[i].getLineClass() == SourceDataLine.class)
  375                                   {
  376                                       DataLine.Info info = (DataLine.Info)lineinfos[i];
  377                                       AudioFormat[] formats = info.getFormats();
  378                                       for (int j = 0; j < formats.length; j++) {
  379                                           AudioFormat format = formats[j];
  380                                           if(format.getChannels() == 2 ||
  381                                                   format.getChannels() == AudioSystem.NOT_SPECIFIED)
  382                                           if(format.getEncoding().equals(Encoding.PCM_SIGNED) ||
  383                                                   format.getEncoding().equals(Encoding.PCM_UNSIGNED))
  384                                           if(format.getSampleRate() == AudioSystem.NOT_SPECIFIED ||
  385                                                   format.getSampleRate() == 48000.0)
  386                                           if(format.getSampleSizeInBits() == AudioSystem.NOT_SPECIFIED ||
  387                                                   format.getSampleSizeInBits() == 16)
  388                                           {
  389                                               idealinfo = info;
  390                                               int ideal_channels = format.getChannels();
  391                                               boolean ideal_signed = format.getEncoding().equals(Encoding.PCM_SIGNED);
  392                                               float ideal_rate = format.getSampleRate();
  393                                               boolean ideal_endian = format.isBigEndian();
  394                                               int ideal_bits = format.getSampleSizeInBits();
  395                                               if(ideal_bits == AudioSystem.NOT_SPECIFIED) ideal_bits = 16;
  396                                               if(ideal_channels == AudioSystem.NOT_SPECIFIED) ideal_channels = 2;
  397                                               if(ideal_rate == AudioSystem.NOT_SPECIFIED) ideal_rate = 48000;
  398                                               idealformat = new AudioFormat(ideal_rate, ideal_bits,
  399                                                       ideal_channels, ideal_signed, ideal_endian);
  400                                               break idealFound;
  401                                           }
  402                                       }
  403                                   }
  404                               }
  405   
  406                               if(idealformat != null)
  407                               {
  408                                   format = idealformat;
  409                                   line = (SourceDataLine) defaultmixer.getLine(idealinfo);
  410                               }
  411                           }
  412   
  413                           if(line == null)
  414                               line = AudioSystem.getSourceDataLine(format);
  415                       } finally {
  416                           synchronized (SoftMixingMixerProvider.mutex) {
  417                               SoftMixingMixerProvider.lockthread = null;
  418                           }
  419                       }
  420   
  421                       if (line == null)
  422                           throw new IllegalArgumentException("No line matching "
  423                                   + info.toString() + " is supported.");
  424                   }
  425   
  426                   double latency = this.latency;
  427   
  428                   if (!line.isOpen()) {
  429                       int bufferSize = getFormat().getFrameSize()
  430                               * (int) (getFormat().getFrameRate() * (latency / 1000000f));
  431                       line.open(getFormat(), bufferSize);
  432   
  433                       // Remember that we opened that line
  434                       // so we can close again in SoftSynthesizer.close()
  435                       sourceDataLine = line;
  436                   }
  437                   if (!line.isActive())
  438                       line.start();
  439   
  440                   int controlbuffersize = 512;
  441                   try {
  442                       controlbuffersize = ais.available();
  443                   } catch (IOException e) {
  444                   }
  445   
  446                   // Tell mixer not fill read buffers fully.
  447                   // This lowers latency, and tells DataPusher
  448                   // to read in smaller amounts.
  449                   // mainmixer.readfully = false;
  450                   // pusher = new DataPusher(line, ais);
  451   
  452                   int buffersize = line.getBufferSize();
  453                   buffersize -= buffersize % controlbuffersize;
  454   
  455                   if (buffersize < 3 * controlbuffersize)
  456                       buffersize = 3 * controlbuffersize;
  457   
  458                   if (jitter_correction) {
  459                       ais = new SoftJitterCorrector(ais, buffersize,
  460                               controlbuffersize);
  461                   }
  462                   pusher = new SoftAudioPusher(line, ais, controlbuffersize);
  463                   pusher_stream = ais;
  464                   pusher.start();
  465   
  466               } catch (LineUnavailableException e) {
  467                   if (isOpen())
  468                       close();
  469                   throw new LineUnavailableException(e.toString());
  470               }
  471   
  472           }
  473       }
  474   
  475       public AudioInputStream openStream(AudioFormat targetFormat)
  476               throws LineUnavailableException {
  477   
  478           if (isOpen())
  479               throw new LineUnavailableException("Mixer is already open");
  480   
  481           synchronized (control_mutex) {
  482   
  483               open = true;
  484   
  485               implicitOpen = false;
  486   
  487               if (targetFormat != null)
  488                   format = targetFormat;
  489   
  490               mainmixer = new SoftMixingMainMixer(this);
  491   
  492               sendEvent(new LineEvent(this, LineEvent.Type.OPEN,
  493                       AudioSystem.NOT_SPECIFIED));
  494   
  495               return mainmixer.getInputStream();
  496   
  497           }
  498   
  499       }
  500   
  501       public void removeLineListener(LineListener listener) {
  502           synchronized (control_mutex) {
  503               listeners.remove(listener);
  504           }
  505       }
  506   
  507       public long getLatency() {
  508           synchronized (control_mutex) {
  509               return latency;
  510           }
  511       }
  512   
  513       public AudioFormat getFormat() {
  514           synchronized (control_mutex) {
  515               return format;
  516           }
  517       }
  518   
  519       protected float getControlRate() {
  520           return controlrate;
  521       }
  522   
  523       protected SoftMixingMainMixer getMainMixer() {
  524           if (!isOpen())
  525               return null;
  526           return mainmixer;
  527       }
  528   
  529   }

Save This Page
Home » openjdk-7 » com.sun.media » sound » [javadoc | source]