Voice Over IP (VOIP) without RTP or JMF

I’ve used JMF on windows and was able to send audio via RTP.

I think it would be foolish to try and implement something of my own to simply send and receive audio. I only need audio to go between 2 people, with somesort of PTT or VOX for the sending.

The hoops to go through installing JMF on windows and linux, then using RTP seems a bit much.

Any one have ideas or samples they can provide? Is JMF my best hope?

Regards,
Aaron R>

JMF sucks. It is actually far easier to roll your own than to get it to work properly.

Check out the Ogg/Vorbis stuff… that should help you get most of the way.

Will do!

What about doing the capture though? Once I’ve got the data, I’m probably 1/3 the way there.

ie get data, transport it, play it.

Regards,
Aaron R>

You can use javax.sound for capturing audio.
I found JSpeex very useful and easy for great sounding compression.

See http://www.java-gaming.org/cgi-bin/JGNetForums/YaBB.cgi?board=Sound;action=display;num=1089138785 for the code to decode using JSpeex and play the audio.
The code for the other way around (capture - encode) is about the same. I can post that too if you like when I get home.

Thanks alot for the code! Yes, I would appreciate it if you posted the code for the other side as well.

Cheers!

Here it is:


import java.io.IOException;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;

import org.xiph.speex.SpeexEncoder;
import org.xiph.speex.spi.SpeexEncoding;


public class AudioInputThread extends Thread {
      
      
      private AudioFileFormat.Type      targetType;
      private AudioInputStream            audioInputStream;
      private AudioStreamListener            listener;
      private TargetDataLine                  targetLine;
      private boolean running = false;
      
      private SpeexEncoder encoder;

      
      public AudioInputThread(AudioStreamListener listener) {
            
            AudioFormat      audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000.0F, 16, 1, 2, 16000.0F, false);
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
            TargetDataLine targetDataLine = null;
            encoder = new SpeexEncoder();
            encoder.init(1, SpeexEncoding.SPEEX_Q8.getQuality(), 16000, 1);
            
            try {
                  
                  targetDataLine = (TargetDataLine) AudioSystem.getLine(info);
                  targetDataLine.open(audioFormat, 8192);
            } catch (LineUnavailableException e) {
                  
                  System.err.println("unable to get a recording line");
                  e.printStackTrace();
                  System.exit(1);
            }

            this.targetLine = targetDataLine;

            AudioFileFormat.Type targetType = AudioFileFormat.Type.WAVE;
            audioInputStream = new AudioInputStream(targetLine);

            this.listener = listener;
            
            start();
      }

      
      /**
       * Starts the recording. To accomplish this, (i) the line is started and
       * (ii) the thread is started.
       */
      public void start() {
            
            running = true;
            targetLine.start();
            super.start();
      }

      /**
       * Stops the recording.
       */
      private void closeLines() {
            
            targetLine.stop();
            targetLine.close();
            System.out.println("stopped");
      }
      
      
      public void stopInput() {
            running = false;
      }

      
      public void run() {
            
            byte[] buffer = new byte[640];
            
            while (running) {
                  
                  try {
                        
                        audioInputStream.read(buffer);
                        encoder.processData(buffer, 0, 640);
                        byte[] encoded = new byte[encoder.getProcessedDataByteSize()];
                        encoder.getProcessedData(encoded, 0);
                        listener.write(encoded);
                        
                  } catch (IOException e) {
                        e.printStackTrace();
                        running = false;
                  }
            }
            
            closeLines();
            System.out.println("Reading from mic stopped");
      }
}

It’s not the most efficient code (you can easily get rid of the constant creating of new arrays in run() to avoid some garbage)

Hey thanks! I’ll see about playing with it a bit. I won’t be using it in a project for awhile, but when the time comes I’ll be ready!

I saw the suggestion to use speex and am very interested.

I am trying to develop a VOIP application with more then 2 participants.
I am thinking about using speex, but this is only for coding (and decoding).
What if I want to stream.
I found an ietf draft to encapsulate JSpeex in rtp.
http://www.ietf.org/internet-drafts/draft-herlein-avt-rtp-speex-00.txt

Anybody implemented something like this yet?
or is there an alternative for RTP for streaming audio?

I need this urgently…

regards,
Frie

Can someone help me understand why my Mac is barfing on this?


           AudioFormat      audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000.0F, 16, 1, 2, 16000.0F, false);
           DataLine.Info info = new DataLine.Info(TargetDataLine.class, audioFormat);
           TargetDataLine targetDataLine = null;
           encoder = new SpeexEncoder();
           encoder.init(1, SpeexEncoding.SPEEX_Q8.getQuality(), 16000, 1);
           
           try {
                 
                 targetDataLine = (TargetDataLine) AudioSystem.getLine(info);

I get the following exception -

Exception in thread “main” java.lang.IllegalArgumentException: No line matching interface TargetDataLine supporting format PCM_SIGNED, 16000.0 Hz, 16 bit, mono, big-endian, audio data is supported.

I tried changing the endian flag on the audio format, but it doesn’t change anything. Any other ideas?

Cheers,
Dr. A>

I have had this working on the mac OS X 10.4.1 with Java 1.5.0


        try {
            // Initialise Sound System
            AudioFormat audioFormat = new AudioFormat(RATE, 16, 1, true, true);
            DataLine.Info info = new DataLine.Info(Clip.class, audioFormat);
            music = (Clip)AudioSystem.getLine(info);
            music.open(audioFormat, musicLoop, 0, musicLoop.length);
            music.loop(Clip.LOOP_CONTINUOUSLY);
            fire = (Clip)AudioSystem.getLine(info);
            fire.open(audioFormat, fireSound, 0, fireSound.length);
        } catch (Exception e) {
            e.printStackTrace(); // Display error, but keep going
        }

RATE = 16000f;
The differences are that I’m using the alternative constructor for AudioFormat, where I leave it to java to decide on the frame size and that I’m creating a Clip rather than a TargetDataLine. Both Clip and TargetDataLine are subclasses of DataLine.

I’d try the alternative constructor for AudioFormat first. If no joy, then there must be something unique to TargetDataLine, not inherited from DataLine which is causing the problem, in which case looking at the source might help.

/Edit Looking at this some more. TargetDataLine is a source, while Clip is a sink. This suggests the the microphone doesn’t support the same data formats as the sound synthesiser.

/Edit I think it’s the sample rate. Browsing the web suggests 11025, 22050 and 44100 (CD sampling rate) as possibles.
Alan

Alan -

Thanks for the info! So far it seems to be the solution. I’ll post any new specifics if they come up.

Cheers,
Dr. A>