changeset 82:10aa02b5a832

* src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java: superclass for TargetDataLine and SourceDataLine; moved open and close here * src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java: chnaged the methods written so far to use Stream.java
author iivan@town.yyz.redhat.com
date Fri, 15 Aug 2008 16:39:53 -0400
parents 543d5b1cef67
children 3f9cc08fdbc0
files ChangeLog build.xml src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java src/java/org/classpath/icedtea/pulseaudio/SimpleAudioRecorder.java src/java/org/classpath/icedtea/pulseaudio/Stream.java src/java/org/classpath/icedtea/pulseaudio/StreamEvent.java src/java/org/classpath/icedtea/pulseaudio/StreamListener.java src/native/org_classpath_icedtea_pulseaudio_Stream.c
diffstat 10 files changed, 172 insertions(+), 286 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Fri Aug 15 15:46:53 2008 -0400
+++ b/ChangeLog	Fri Aug 15 16:39:53 2008 -0400
@@ -1,3 +1,12 @@
+2008-08-13 Ioana Ivan  <iivan@redhat.com>
+
+        * src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java:
+	        superclass for TargetDataLine and SourceDataLine; moved open
+		and close here
+	* src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java:
+		chnaged the methods written so far to use Stream.java
+
+
 2008-08-13 Ioana Ivan  <iivan@redhat.com>
 
         * src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java:
--- a/build.xml	Fri Aug 15 15:46:53 2008 -0400
+++ b/build.xml	Fri Aug 15 16:39:53 2008 -0400
@@ -40,6 +40,7 @@
 			<class name="org.classpath.icedtea.pulseaudio.Stream"/>
 			<class name="org.classpath.icedtea.pulseaudio.PulseAudioTargetDataLine"/>
 			<class name="org.classpath.icedtea.pulseaudio.PulseAudioStreamVolumeControl"/>
+			<class name="org.classpath.icedtea.pulseaudio.PulseAudioDataLine"/>
 		</javah>
 	</target>
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java	Fri Aug 15 16:39:53 2008 -0400
@@ -0,0 +1,118 @@
+package org.classpath.icedtea.pulseaudio;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Line;
+import javax.sound.sampled.LineEvent;
+import javax.sound.sampled.LineListener;
+import javax.sound.sampled.LineUnavailableException;
+
+public abstract class PulseAudioDataLine implements Line {
+
+	protected static final int DEFAULT_BUFFER_SIZE = 1000;
+	protected static final String PULSEAUDIO_FORMAT_KEY = "PulseAudioFormatKey";
+
+	protected String streamName = "Java Stream";
+	
+	protected boolean isOpen = false;
+	protected AudioFormat[] supportedFormats = null;
+	protected AudioFormat currentFormat = null;
+	protected AudioFormat defaultFormat = null;
+	
+	
+	protected List<LineListener> lineListeners = new ArrayList<LineListener>();;
+	
+	protected EventLoop eventLoop = null;
+	protected Semaphore semaphore = new Semaphore(0);
+	protected Stream stream;
+	
+	
+	
+	public void open(AudioFormat format, int bufferSize)
+	throws LineUnavailableException {
+		if (isOpen) {
+			throw new IllegalStateException("Line is already open");
+		}
+
+		// ignore suggested buffer size
+
+		for (AudioFormat myFormat : supportedFormats) {
+			if (format.matches(myFormat)) {
+				stream = new Stream(eventLoop.getContextPointer(), streamName,
+						Stream.Format.valueOf((String) myFormat
+								.getProperty(PULSEAUDIO_FORMAT_KEY)),
+								(int) format.getSampleRate(), format.getChannels());
+				currentFormat = format;
+				isOpen = true;
+			}
+		}
+		// no matches found
+		if (!isOpen) {
+			throw new IllegalArgumentException("Invalid format");
+		}
+
+		Stream.StateListener openCloseListener = new Stream.StateListener() {
+
+			@Override
+			public void update() {
+				if (stream.getState() == Stream.State.READY) {
+					fireLineEvent(new LineEvent(PulseAudioDataLine.this,
+							LineEvent.Type.OPEN, AudioSystem.NOT_SPECIFIED));
+					semaphore.release();
+				} else if (stream.getState() == Stream.State.TERMINATED
+						|| stream.getState() == Stream.State.FAILED) {
+					fireLineEvent((new LineEvent(PulseAudioDataLine.this,
+							LineEvent.Type.CLOSE, AudioSystem.NOT_SPECIFIED)));
+					semaphore.release();
+				}
+			}
+
+		};
+
+		stream.addStateListener(openCloseListener);
+
+		synchronized (eventLoop.threadLock) {
+
+			connectLine();
+		}
+
+		try {
+			semaphore.acquire();
+		} catch (InterruptedException e) {
+			// throw new LineUnavailableException("unable to prepare
+			// stream");
+		}
+	}
+	
+	public void close() {
+		assert (isOpen);
+
+		synchronized (eventLoop.threadLock) {
+			stream.drain();
+			stream.disconnect();
+		}
+
+		try {
+			semaphore.acquire();
+		} catch (InterruptedException e) {
+			// throw new LineUnavailableException("unable to prepare
+			// stream");
+		}
+
+	}
+	
+	private void fireLineEvent(LineEvent e) {
+		for (LineListener lineListener : lineListeners) {
+			lineListener.update(e);
+		}
+	}
+
+	abstract void connectLine();
+
+	
+
+}
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Fri Aug 15 15:46:53 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java	Fri Aug 15 16:39:53 2008 -0400
@@ -38,37 +38,23 @@
 package org.classpath.icedtea.pulseaudio;
 
 import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Semaphore;
 
 import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.AudioSystem;
 import javax.sound.sampled.Control;
 import javax.sound.sampled.DataLine;
-import javax.sound.sampled.LineEvent;
 import javax.sound.sampled.LineListener;
 import javax.sound.sampled.LineUnavailableException;
 import javax.sound.sampled.SourceDataLine;
 import javax.sound.sampled.AudioFormat.Encoding;
 import javax.sound.sampled.Control.Type;
 
-public class PulseAudioSourceDataLine implements SourceDataLine {
-
-	private static final int DEFAULT_BUFFER_SIZE = 1000;
-	private static final String PULSEAUDIO_FORMAT_KEY = "PulseAudioFormatKey";
+public class PulseAudioSourceDataLine extends PulseAudioDataLine implements SourceDataLine {
 
-	private String streamName = "Java Stream";
 
-	private EventLoop eventLoop = null;
-
-	private boolean isOpen = false;
 	private boolean isPaused = false;
 
-	private AudioFormat[] supportedFormats = null;
-	private AudioFormat currentFormat = null;
-	private AudioFormat defaultFormat = null;
 
-	private List<LineListener> lineListeners;
 
 	private Control[] controls = null;
 	private PulseAudioStreamMuteControl muteControl;
@@ -76,10 +62,9 @@
 	private boolean muted;
 	private float volume;
 
-	private Semaphore semaphore = new Semaphore(0);
 	private long currentFramePosition = 0;
 
-	private Stream stream;
+	
 
 	public PulseAudioSourceDataLine(EventLoop eventLoop, AudioFormat[] formats,
 			AudioFormat defaultFormat) {
@@ -112,59 +97,8 @@
 
 	public void open(AudioFormat format, int bufferSize)
 			throws LineUnavailableException {
-		if (isOpen) {
-			throw new IllegalStateException("Line is already open");
-		}
-
-		// ignore suggested buffer size
-
-		for (AudioFormat myFormat : supportedFormats) {
-			if (format.matches(myFormat)) {
-				stream = new Stream(eventLoop.getContextPointer(), streamName,
-						Stream.Format.valueOf((String) myFormat
-								.getProperty(PULSEAUDIO_FORMAT_KEY)),
-						(int) format.getSampleRate(), format.getChannels());
-				currentFormat = format;
-				isOpen = true;
-			}
-		}
-		// no matches found
-		if (!isOpen) {
-			throw new IllegalArgumentException("Invalid format");
-		}
-
-		Stream.StateListener openCloseListener = new Stream.StateListener() {
-
-			@Override
-			public void update() {
-				if (stream.getState() == Stream.State.READY) {
-					fireLineEvent(new LineEvent(PulseAudioSourceDataLine.this,
-							LineEvent.Type.OPEN, AudioSystem.NOT_SPECIFIED));
-					semaphore.release();
-				} else if (stream.getState() == Stream.State.TERMINATED
-						|| stream.getState() == Stream.State.FAILED) {
-					fireLineEvent((new LineEvent(PulseAudioSourceDataLine.this,
-							LineEvent.Type.CLOSE, AudioSystem.NOT_SPECIFIED)));
-					semaphore.release();
-				}
-			}
-
-		};
-
-		stream.addStateListener(openCloseListener);
-
-		synchronized (eventLoop.threadLock) {
-
-			stream.connectForPlayback(null);
-		}
-
-		try {
-			semaphore.acquire();
-		} catch (InterruptedException e) {
-			// throw new LineUnavailableException("unable to prepare
-			// stream");
-		}
-
+	
+		super.open(format, bufferSize);
 		controls = new Control[2];
 		volumeControl = new PulseAudioStreamVolumeControl(this);
 		controls[0] = volumeControl;
@@ -172,6 +106,10 @@
 		controls[1] = muteControl;
 
 	}
+	
+	protected void connectLine() {
+		stream.connectForPlayback(null);
+	}
 
 	public void open(AudioFormat format) throws LineUnavailableException {
 		open(format, DEFAULT_BUFFER_SIZE);
@@ -279,22 +217,6 @@
 		}
 	};
 
-	public void close() {
-		assert (isOpen);
-
-		synchronized (eventLoop.threadLock) {
-			stream.drain();
-			stream.disconnect();
-		}
-
-		try {
-			semaphore.acquire();
-		} catch (InterruptedException e) {
-			// throw new LineUnavailableException("unable to prepare
-			// stream");
-		}
-
-	}
 
 	public int getBufferSize() {
 		// FIXME!
@@ -418,11 +340,6 @@
 
 	}
 
-	private void fireLineEvent(LineEvent e) {
-		for (LineListener lineListener : lineListeners) {
-			lineListener.update(e);
-		}
-	}
 
 	protected EventLoop getEventLoop() {
 		return this.eventLoop;
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Fri Aug 15 15:46:53 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioTargetDataLine.java	Fri Aug 15 16:39:53 2008 -0400
@@ -37,42 +37,20 @@
 
 package org.classpath.icedtea.pulseaudio;
 
-import java.io.File;
+
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Semaphore;
-
 import javax.sound.sampled.*;
 import javax.sound.sampled.AudioFormat.Encoding;
 import javax.sound.sampled.Control.Type;
-import javax.sound.sampled.Port.Info;
+
 
-public class PulseAudioTargetDataLine implements TargetDataLine {
+public class PulseAudioTargetDataLine extends PulseAudioDataLine implements TargetDataLine {
 
 	
 	
 	protected boolean isOpen = false;
 	protected boolean isPaused = false;
-
-	private AudioFormat[] supportedFormats = null;
-	private AudioFormat currentFormat = null;
-	private AudioFormat defaultFormat = null;
-
-	private List<LineListener> lineListeners = new LinkedList<LineListener>();
-	
-	private List<StreamListener> streamListeners = new ArrayList<StreamListener>();
-	
-	private String streamName = "Java Stream";
-	private static final int DEFAULT_BUFFER_SIZE = 1000;
-	private static final String PULSEAUDIO_FORMAT_KEY = "PulseAudioFormatKey";
-	
-	private EventLoop eventLoop = null;
-	
-	private Semaphore semaphore = new Semaphore(0);
 	
 	@SuppressWarnings("unused")
 	private long streamPointer;
@@ -88,16 +66,9 @@
 		}
 	}
 	
-	private native void native_open(long contextPointer, String streamName,
-			String encoding, int sampleRate, int channels, int bufferSize);
-	
-	private native void native_start();
 	
 	private native int native_get_readable_size();
 	
-	private native void native_close();
-	
-	private native int native_read(byte[] array, int remaininglength, int position);
 	
 	public PulseAudioTargetDataLine(EventLoop eventLoop, AudioFormat[] formats, AudioFormat defaultFormat) {
 		supportedFormats = formats;
@@ -108,64 +79,8 @@
 
 	}
 
-	public void open(AudioFormat format, int bufferSize)
-			throws LineUnavailableException {
-		System.out.println("OPEn CALLED");
-		if (isOpen) {
-			throw new IllegalStateException("Line is already open");
-		}
-
-		// ignore suggested buffer size
-
-		for (AudioFormat myFormat : supportedFormats) {
-			if (format.matches(myFormat)) {
-				native_open(eventLoop.getContextPointer(), streamName,
-						(String) myFormat.getProperty(PULSEAUDIO_FORMAT_KEY),
-						(int) format.getSampleRate(), format.getChannels(),
-						bufferSize);
-				currentFormat = format;
-				isOpen = true;
-			}
-		}
-		// no matches found
-		if (!isOpen) {
-			throw new IllegalArgumentException("Invalid format");
-		}
-
-		StreamListener openCloseListener = new StreamListener() {
-
-			@Override
-			public void update(StreamEvent e) {
-				if (e.getType() == StreamEvent.Type.READY) {
-					fireLineEvent(new LineEvent(PulseAudioTargetDataLine.this,
-							LineEvent.Type.OPEN, AudioSystem.NOT_SPECIFIED));
-					System.out.println("IN HERE");
-					semaphore.release();
-				} else if (e.getType() == StreamEvent.Type.TERMINATED
-						|| e.getType() == StreamEvent.Type.FAILED) {
-					fireLineEvent((new LineEvent(PulseAudioTargetDataLine.this,
-							LineEvent.Type.CLOSE, AudioSystem.NOT_SPECIFIED)));
-					semaphore.release();
-				}
-			}
-
-		};
-
-		addStreamListener(openCloseListener);
-
-
-		
-		synchronized (eventLoop.threadLock) {
-
-			native_start();
-		}
-
-		try {
-			semaphore.acquire();
-		} catch (InterruptedException e) {
-			// throw new LineUnavailableException("unable to prepare
-			// stream");
-		}
+	protected void connectLine() {
+		stream.connectForRecording(null);
 	}
 
 	public void open(AudioFormat format) throws LineUnavailableException {
@@ -203,19 +118,16 @@
 
 		int position = offset;
 		int remainingLength = length;
-		int availableSize;
-
 		int sizeRead = 0;
 
 		while (remainingLength != 0) {
 
 			synchronized (eventLoop.threadLock) {
-				availableSize = available();
-				int toRead = native_read(data, remainingLength,  position);
+				int bytesRead = stream.read(data, remainingLength, position);
 
-				sizeRead += toRead;
-				position += toRead;
-				remainingLength -= toRead;
+				sizeRead += bytesRead;
+				position += bytesRead;
+				remainingLength -= bytesRead;
 				
 
 			}
@@ -289,25 +201,6 @@
 		}
 	}
 
-	public void close() {
-		assert (isOpen);
-
-
-		synchronized (eventLoop.threadLock) {
-			native_close();
-		}
-
-		try {
-			semaphore.acquire();
-			System.out.println("stream closed");
-		} catch (InterruptedException e) {
-			// throw new LineUnavailableException("unable to prepare
-			// stream");
-		}
-
-	}
-
-	private native void closeStream();
 
 	public int getBufferSize() {
 		// TODO Auto-generated method stub
@@ -365,57 +258,11 @@
 	public boolean isControlSupported(Type control) {
 		// TODO Auto-generated method stub
 		return false;
+		
 	}
 	
-	public void update(int status) {
-		synchronized (eventLoop.threadLock) {
-			switch (status) {
-			case 0:
-				fireStreamEvent(new StreamEvent(StreamEvent.Type.UNCONNECTED));
-				break;
-			case 1:
-				fireStreamEvent(new StreamEvent(StreamEvent.Type.CREATING));
-				break;
-			case 2:
-				fireStreamEvent(new StreamEvent(StreamEvent.Type.READY));
-				break;
-			case 3:
-				fireStreamEvent(new StreamEvent(StreamEvent.Type.FAILED));
-				break;
-			case 4:
-				fireStreamEvent(new StreamEvent(StreamEvent.Type.TERMINATED));
-				break;
-			default:
-				assert ("not supposed to happen".indexOf("false") >= 0);
-			}
-		}
-	}
 
-	private void fireLineEvent(LineEvent e) {
-		for (LineListener lineListener : lineListeners) {
-			lineListener.update(e);
-		}
-	}
+	
 
-	private void fireStreamEvent(StreamEvent e) {
-		synchronized (streamListeners) {
-			for (StreamListener streamListener : streamListeners) {
-				streamListener.update(e);
-			}
-		}
-	}
-	
-	
-	private void addStreamListener(StreamListener listener) {
-		synchronized (streamListeners) {
-			this.streamListeners.add(listener);
-		}
-	}
-
-	private void removeStreamListener(StreamListener listener) {
-		synchronized (streamListeners) {
-			this.streamListeners.remove(listener);
-		}
-	}
 
 }
\ No newline at end of file
--- a/src/java/org/classpath/icedtea/pulseaudio/SimpleAudioRecorder.java	Fri Aug 15 15:46:53 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/SimpleAudioRecorder.java	Fri Aug 15 16:39:53 2008 -0400
@@ -1,3 +1,4 @@
+
 package org.classpath.icedtea.pulseaudio;
 
 /*
--- a/src/java/org/classpath/icedtea/pulseaudio/Stream.java	Fri Aug 15 15:46:53 2008 -0400
+++ b/src/java/org/classpath/icedtea/pulseaudio/Stream.java	Fri Aug 15 16:39:53 2008 -0400
@@ -96,6 +96,8 @@
 
 	private native int native_pa_stream_write(byte[] data, int offset,
 			int length);
+	
+	private native int native_pa_stream_read(byte[] array, int length, int position);
 
 	/*
 	 * private native int native_pa_stream_peek (pa_stream *p, const void
@@ -366,6 +368,10 @@
 	public int write(byte[] data, int offset, int length) {
 		return native_pa_stream_write(data, offset, length);
 	}
+	
+	public int read(byte[] array, int length, int position) {
+		return native_pa_stream_read(array, length, position);
+	}
 
 	/**
 	 * Read the next fragment from the buffer (for recording).
--- a/src/java/org/classpath/icedtea/pulseaudio/StreamEvent.java	Fri Aug 15 15:46:53 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-package org.classpath.icedtea.pulseaudio;
-
-@Deprecated
-public class StreamEvent {
-
-	public enum Type {
-		UNCONNECTED, CREATING, READY, FAILED, TERMINATED
-	}
-	
-	private Type type;
-	
-	public StreamEvent(Type type) {
-		this.type = type;
-	}
-	
-	public Type getType() {
-		return this.type;
-	}
-	
-}
--- a/src/java/org/classpath/icedtea/pulseaudio/StreamListener.java	Fri Aug 15 15:46:53 2008 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-package org.classpath.icedtea.pulseaudio;
-
-@Deprecated
-public interface StreamListener {
-
-	public void update(StreamEvent e);
-
-}
--- a/src/native/org_classpath_icedtea_pulseaudio_Stream.c	Fri Aug 15 15:46:53 2008 -0400
+++ b/src/native/org_classpath_icedtea_pulseaudio_Stream.c	Fri Aug 15 16:39:53 2008 -0400
@@ -45,7 +45,7 @@
 	}
 }
 
-static void stream_read_callback(pa_stream *stream, size_t length,
+/*static void stream_read_callback(pa_stream *stream, size_t length,
 		void *userdata) {
 	printf("stream_read_callback called\n");
 
@@ -61,7 +61,7 @@
 		callJavaVoidMethod(pulse_thread_env, context->obj, "readCallback");
 	}
 
-}
+}*/
 
 static void stream_overflow_callback(pa_stream *stream, void *userdata) {
 	printf("stream_overflow_callback called\n");
@@ -231,7 +231,7 @@
 
 	pa_stream_set_state_callback (stream, stream_state_callback, j_context);
 	pa_stream_set_write_callback (stream, stream_write_callback, j_context);
-	pa_stream_set_read_callback (stream, stream_read_callback, j_context);
+	//pa_stream_set_read_callback (stream, stream_read_callback, j_context);
 	pa_stream_set_overflow_callback (stream, stream_overflow_callback, j_context);
 	pa_stream_set_underflow_callback (stream, stream_underflow_callback, j_context);
 	//	pa_stream_set_started_callback (stream, stream_started_callback, j_context);
@@ -398,6 +398,21 @@
 	return value;
 }
 
+JNIEXPORT int JNICALL Java_org_classpath_icedtea_pulseaudio_Stream_native_1pa_1stream_1read
+  (JNIEnv *env, jobject obj, jbyteArray array, jint length,  jint offset) {
+	pa_stream *stream = getJavaPointer(env, obj, "streamPointer");
+	const void *read_data = NULL;
+	size_t  read_length = 0;
+	pa_stream_peek(stream, &read_data, &read_length);
+	if (length < read_length) {
+		read_length = length;
+	}
+
+	(*env)->SetByteArrayRegion(env, array, offset, read_length, read_data);
+	pa_stream_drop(stream);
+	return read_length;
+}
+
 /*
  * Class:     org_classpath_icedtea_pulseaudio_Stream
  * Method:    native_pa_stream_writable_size