com.cloudgarden.audio
Class AudioSplitter

java.lang.Object
  |
  +--com.cloudgarden.audio.DefaultAudioObject
        |
        +--com.cloudgarden.audio.DefaultAudioSink
              |
              +--com.cloudgarden.audio.AudioSplitter
All Implemented Interfaces:
AudioObject, AudioSink, AudioSource

public class AudioSplitter
extends DefaultAudioSink
implements AudioSource

A class designed to allow a single AudioSource to supply the same audio data to multiple AudioSinks (useful, say, to broadcast speech to multiple remote clients). AudioPipes should be used to connect the sinks to other sinks (eg. AudioLineSinks) since they provide buffering and better speech quality.

Here is an example which splits synthesized speech to three AudioLineSinks and starts them at staggered intervals, so you can hear the three identical but separate streams.

  public static void main(String args[]) {
      try {
          synth = Central.createSynthesizer(new SynthesizerModeDesc(Locale.ENGLISH));
          CGAudioManager audioMan = (CGAudioManager)synth.getAudioManager();
          synth.allocate();
          synth.resume();

          SynthesizerProperties props = synth.getSynthesizerProperties();
          Voice v1 = new Voice("Microsoft Mary",Voice.GENDER_DONT_CARE,  Voice.AGE_DONT_CARE, "");
          props.setVoice(v1);

          synth.speakPlainText("ready",null);
          synth.waitEngineState(Synthesizer.QUEUE_EMPTY);

          SourceDataLine[] lines = new SourceDataLine[3];
          AudioPipe[] pipes = new AudioPipe[3];
          AudioFormat fmt;
          if(audioMan.canSetAudioFormat()) {
              fmt = new AudioFormat(22000,16,1,true,false);
              audioMan.setAudioFormat(fmt);
          }
          fmt = audioMan.getAudioFormat();
          System.out.println("Line format = "+fmt);

          int bps = (int)(2*fmt.getSampleRate());

          AudioSplitter splitter = new AudioSplitter();
          audioMan.setSink(splitter);
          splitter.startGetting();
          for(int i=0;i<3;i++) {
              DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt);
              if (!AudioSystem.isLineSupported(info)) {
                  System.out.println("line not supported "+info);
                  return;
              }
              lines[i] = (SourceDataLine) AudioSystem.getLine(info);
              pipes[i] = new AudioPipe();
              //buffer must be big enough to hold 2*1.0 secs worth of data
              //(the time between the first line starting and the third line starting)
              //at 22000*2 bytes/sec
              pipes[i].setBufferSize(2*bps);
              pipes[i].setPauseInput(true);
              pipes[i].setSink(new AudioLineSink(lines[i]));
              splitter.addSink(pipes[i]);
          }
          Thread rt = new Thread() {
              public void run() {
                  synth.speakPlainText("Hello world",null);
                  synth.speakPlainText("How are you this fine day?",null);
              }
          };
          rt.start();
          for(int i=0;i<3;i++) {
              lines[i].open();
              lines[i].start();
              pipes[i].startGetting();
              Thread.currentThread().sleep(1000);
          }
          synth.waitEngineState(Synthesizer.QUEUE_EMPTY);
          audioMan.closeOutput();

          //need to wait because output to stream occurs much faster than the
          //SourceDataLine plays it
          pipes[2].drain(); //wait for last line to finish

          //...and now speak to the default audio device

          audioMan.setSink(null);

          synth.speakPlainText("and now I'm all done",null);

          synth.waitEngineState(Synthesizer.QUEUE_EMPTY);

      } catch (Exception e) {
          e.printStackTrace();
      } finally {
          try {
              synth.deallocate();
              //wait till we are deallocated...
              synth.waitEngineState(Engine.DEALLOCATED);
          } catch(Exception e2) {
              e2.printStackTrace();
          }
      }
      //...before closing up shop
      System.exit(0);
 }


Fields inherited from class com.cloudgarden.audio.DefaultAudioSink
getThread, getting, source
 
Fields inherited from class com.cloudgarden.audio.DefaultAudioObject
contentType, exception, format, listeners, paused, pauseSync, running, waiting
 
Fields inherited from interface com.cloudgarden.audio.AudioObject
END_OF_DATA
 
Constructor Summary
AudioSplitter()
          Creates new SynthesizerAudioStream
 
Method Summary
 void addSink(AudioSink sink)
          Starts a thread which gets data from the source and writes (copies) it to all the sinks added using the addSink method.
 void drain()
          Blocks until END_OF_DATA is written to or read from this AudioObject.
 AudioSink getSink()
          This method does nothing - use getSinks instead
 int getSinkCount()
           
 java.util.Vector getSinks()
          Returns a Vector of all AudioSinks added using the addSInk method
 boolean isSending()
          Returns true if the thread started by the startSending method is still writing data to the AudioSink.
 int read(byte[] data, int len)
          Cannot call this method - use the startSending method instead.
 int read(byte[] data, int offset, int len)
          Cannot call this method - use the startSending method instead.
 boolean removeSink(AudioSink sink)
           
 void setBufferSize(int size)
           
 void setSink(AudioSink sink)
          This method does nothing - use the addSink method instead
 void startSending()
          This method should start a thread which repeatedly writes data to the AudioSink object which this object is connected to, until it writes data with length END_OF_DATA, or until the stopSending method is called.
 void stopSending()
           
 int write(byte[] data, int offset, int len)
          Writes to all the sinks added using the addSInk method
 
Methods inherited from class com.cloudgarden.audio.DefaultAudioSink
getSource, setAudioFormat, setContentType, setSource, write
 
Methods inherited from class com.cloudgarden.audio.DefaultAudioObject
addTransferListener, blockWhilePaused, blockWhileWaiting, bytesTransferred, canSetAudioFormat, getAudioFormat, getContentType, getLastException, isPaused, isWaiting, removeTransferListener, setPaused
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 
Methods inherited from interface com.cloudgarden.audio.AudioObject
addTransferListener, canSetAudioFormat, getAudioFormat, getContentType, isPaused, isWaiting, removeTransferListener, setAudioFormat, setContentType, setPaused
 

Constructor Detail

AudioSplitter

public AudioSplitter()
Creates new SynthesizerAudioStream
Method Detail

addSink

public void addSink(AudioSink sink)
             throws java.io.IOException
Starts a thread which gets data from the source and writes (copies) it to all the sinks added using the addSink method.

getSinkCount

public int getSinkCount()

removeSink

public boolean removeSink(AudioSink sink)

write

public int write(byte[] data,
                 int offset,
                 int len)
          throws java.io.IOException
Writes to all the sinks added using the addSInk method
See Also:
addSink(com.cloudgarden.audio.AudioSink)

startSending

public void startSending()
                  throws java.io.IOException
Description copied from interface: AudioSource
This method should start a thread which repeatedly writes data to the AudioSink object which this object is connected to, until it writes data with length END_OF_DATA, or until the stopSending method is called. If an error occurs while the thread is sending data it could be detected by calling the drain method.
Specified by:
startSending in interface AudioSource
Following copied from interface: com.cloudgarden.audio.AudioSource
Throws:
an - IOException if the source is unable to start sending data - for example, if an audio file is unable to be opened to supply the data to be sent.
See Also:
AudioSource.setSink(com.cloudgarden.audio.AudioSink), AudioObject.drain()

drain

public void drain()
           throws java.io.IOException
Description copied from interface: AudioObject
Blocks until END_OF_DATA is written to or read from this AudioObject. For sinks such as an AudioLineSink, or sources such as AudioLineSource should also drain the line.
Specified by:
drain in interface AudioObject
Overrides:
drain in class DefaultAudioObject

read

public int read(byte[] data,
                int offset,
                int len)
         throws java.io.IOException
Cannot call this method - use the startSending method instead.
Specified by:
read in interface AudioSource
Following copied from interface: com.cloudgarden.audio.AudioSource
See Also:
AudioSource.setSink(com.cloudgarden.audio.AudioSink), AudioSink#startGetting, AudioObject.setPaused(boolean)

read

public int read(byte[] data,
                int len)
         throws java.io.IOException
Cannot call this method - use the startSending method instead.
Specified by:
read in interface AudioSource

isSending

public boolean isSending()
Description copied from interface: AudioSource
Returns true if the thread started by the startSending method is still writing data to the AudioSink.
Specified by:
isSending in interface AudioSource
Following copied from interface: com.cloudgarden.audio.AudioSource
See Also:
AudioSource.startSending()

stopSending

public void stopSending()
Specified by:
stopSending in interface AudioSource
Following copied from interface: com.cloudgarden.audio.AudioSource
See Also:
AudioSource.startSending()

setSink

public void setSink(AudioSink sink)
This method does nothing - use the addSink method instead
Specified by:
setSink in interface AudioSource
See Also:
addSink(com.cloudgarden.audio.AudioSink)

getSink

public AudioSink getSink()
This method does nothing - use getSinks instead
Specified by:
getSink in interface AudioSource
See Also:
addSink(com.cloudgarden.audio.AudioSink)

getSinks

public java.util.Vector getSinks()
Returns a Vector of all AudioSinks added using the addSInk method

setBufferSize

public void setBufferSize(int size)