changeset 29:b9836224602b

added open() and start() in SourceDataLine committer: Ioana Ivan <iivan@redhat.com>
author Ioana Ivan <iivan@redhat.com>
date Fri, 01 Aug 2008 10:59:19 -0400
parents f1a1eaa0f610
children f05acd4ee58b 3e7111680ba6
files src/org/openjdk/sound/PulseAudioMixer.java src/org/openjdk/sound/PulseAudioSourceDataLine.java src/org/openjdk/sound/StreamEvent.java src/org/openjdk/sound/StreamListener.java src/org_openjdk_sound_PulseAudioSourceDataLine.c
diffstat 5 files changed, 261 insertions(+), 25 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/openjdk/sound/PulseAudioMixer.java	Thu Jul 31 17:29:47 2008 -0400
+++ b/src/org/openjdk/sound/PulseAudioMixer.java	Fri Aug 01 10:59:19 2008 -0400
@@ -1,11 +1,14 @@
 package org.openjdk.sound;
 
+import java.io.File;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Semaphore;
 
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;
 import javax.sound.sampled.Control;
 import javax.sound.sampled.Line;
@@ -13,6 +16,7 @@
 import javax.sound.sampled.LineListener;
 import javax.sound.sampled.LineUnavailableException;
 import javax.sound.sampled.Mixer;
+import javax.sound.sampled.SourceDataLine;
 import javax.sound.sampled.Control.Type;
 
 public class PulseAudioMixer implements javax.sound.sampled.Mixer {
@@ -27,11 +31,11 @@
 
 	private boolean isOpen = false;
 
-	// private List<PulseAudioSourceDataLine> _sourceLines = null;
+	 private List<PulseAudioSourceDataLine> _sourceLines = new ArrayList();
 	// private List<PulseAudioTargetDataLine> _targetLines = null;
 
-	// private Line.Info _sourceDataLineInfo = new
-	// Line.Info(PulseAudioSourceDataLine.class);
+	private Line.Info _sourceDataLineInfo = new
+	 Line.Info(PulseAudioSourceDataLine.class);
 	// private Line.Info _targetDataLineInfo = new
 	// Line.Info(PulseAudioTargetDataLine.class);
 
@@ -58,13 +62,13 @@
 			throw new LineUnavailableException();
 		}
 
-		// if ( info.matches(_sourceDataLineInfo)) {
-		// PulseAudioSourceDataLine sourceLine = null;
+		if ( info.matches(_sourceDataLineInfo)) {
+		PulseAudioSourceDataLine sourceLine = null;
 		// // FIXME : THIS LINE HERE v
-		// // sourceLine = new PulseAudioSourceDataLine(eventLoop);
-		// _sourceLines.add(sourceLine);
-		// return sourceLine;
-		// }
+		 sourceLine = new PulseAudioSourceDataLine(eventLoop);
+		_sourceLines.add(sourceLine);
+		return sourceLine;
+		}
 
 		// if (info.matches(_targetDataLineInfo)) {
 		// PulseAudioTargetDataLine targetLine = new PulseAudioTargetDataLine();
@@ -343,8 +347,14 @@
 				.getMixer(selectedMixerInfo);
 
 		selectedMixer.open();
-
+		SourceDataLine line = (SourceDataLine)selectedMixer.getLine(new Line.Info(PulseAudioSourceDataLine.class));
+		File	soundFile = new File("/home/yyz/omajid/PulseAudio/main-loop/multi-stream/new.wav");
+		AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFile);
+		AudioFormat	audioFormat = audioInputStream.getFormat();
+		line.open(audioFormat);
+		line.start();
 		selectedMixer.close();
+		
 
 	}
 
--- a/src/org/openjdk/sound/PulseAudioSourceDataLine.java	Thu Jul 31 17:29:47 2008 -0400
+++ b/src/org/openjdk/sound/PulseAudioSourceDataLine.java	Fri Aug 01 10:59:19 2008 -0400
@@ -2,6 +2,8 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Semaphore;
 
 import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.Control;
@@ -13,19 +15,22 @@
 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 String streamName = "Java Stream";
+	private List<StreamListener> streamListeners = new ArrayList();
+
 	
 	private EventLoop eventLoop = null;
 
 	private boolean isOpen = false;
 	private boolean isPaused = false;
 
-	private final AudioFormat format = null;
+	private  AudioFormat format = null;
 	
 	private ArrayList<LineListener> listeners;
 
@@ -52,7 +57,7 @@
 	private native void native_close();
 
 
-	static {
+	/*static {
 		try {
 			String library = new java.io.File(".").getCanonicalPath()
 					+ java.io.File.separatorChar + "lib"
@@ -63,7 +68,7 @@
 		} catch (IOException e) {
 			assert ("Loading failed".endsWith("library"));
 		}
-	}
+	}*/
 
 	public PulseAudioSourceDataLine(EventLoop eventLoop) {
 		this.eventLoop = eventLoop;
@@ -90,14 +95,8 @@
 	}
 
 	public void open() throws LineUnavailableException {
-//		format = new AudioFormat(Encoding.PCM_SIGNED, 44100, 16, 2, );
-		
-//		synchronized (eventLoop.threadLock) {
-//			openStream("PCM_SIGNED", 44100, 16, 2, false, DEFAULT_BUFFER_SIZE);
-//
-//		}
-		
-		open(null);
+		format = new AudioFormat(44100, 16, 2, true, false);
+		open(format, DEFAULT_BUFFER_SIZE);
 		
 	}
 
@@ -121,8 +120,38 @@
 			native_resume();
 			isPaused = false;
 		} else {
-			native_start();
-		}
+			final Semaphore semaphore = new Semaphore(0);
+			
+			synchronized (eventLoop.threadLock) {
+			
+			this.addStreamListener(new StreamListener() {
+				@Override
+				public void update(StreamEvent e) {
+				 System.out.println(this.getClass().getName()
+				 + " waiting to stream to become ready");
+				 if (e.getType() == StreamEvent.Type.READY) {
+				 semaphore.release();
+				 }
+				 }
+			 });
+			
+			 System.out.println("about to open stream");
+			 native_start();
+			}
+			
+			
+			try {
+			 semaphore.acquire();
+			} catch (InterruptedException e) {
+				//throw new LineUnavailableException("unable to prepare stream");
+			}
+			System.out.println(this.getClass().getName() + "stream is ready");
+			}
+		
+			
+			
+			
+		
 
 		/*
 		 * for(LineListener l :listeners) { l.update(new LineEvent(this,
@@ -145,6 +174,10 @@
 	public void removeLineListener(LineListener listener) {
 		this.listeners.remove(listener);
 	}
+	
+	private void addStreamListener(StreamListener listener) {
+		this.streamListeners.add(listener);
+	}
 
 	public boolean isOpen() {
 		return isOpen;
@@ -235,5 +268,38 @@
 		// TODO Auto-generated method stub
 
 	}
+	
+    public void update(int status) {
+    	synchronized(eventLoop.threadLock) {
+        System.out.println(this.getClass().getCanonicalName() + ".update() called! status = " + status);
+        switch (status) {
+        case 0:
+                fireEvent(new StreamEvent(org.openjdk.sound.StreamEvent.Type.UNCONNECTED));
+                break;
+        case 1:
+                fireEvent(new StreamEvent(org.openjdk.sound.StreamEvent.Type.CREATING));
+                break;
+        case 2:
+                fireEvent(new StreamEvent(org.openjdk.sound.StreamEvent.Type.READY));
+                break;
+        case 3:
+                fireEvent(new StreamEvent(org.openjdk.sound.StreamEvent.Type.FAILED));
+                break;
+        case 4:
+                break;
+        default:
+                assert ("not supposed to happen".indexOf("false") >= 0);
+        }
+    	}
+    }
+    
+    private void fireEvent(StreamEvent e) {
+
+        for (StreamListener streamListener : streamListeners) {
+                streamListener.update(e);
+        }
+}
+
+
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openjdk/sound/StreamEvent.java	Fri Aug 01 10:59:19 2008 -0400
@@ -0,0 +1,20 @@
+package org.openjdk.sound;
+
+public class StreamEvent {
+
+        public static enum Type {
+                UNCONNECTED, CREATING, READY, FAILED, TERMINATED,
+        }
+
+
+        private Type type;
+
+        public StreamEvent(StreamEvent.Type type) {
+                this.type = type;
+        }
+
+        public Type getType() {
+                return this.type;
+        }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/org/openjdk/sound/StreamListener.java	Fri Aug 01 10:59:19 2008 -0400
@@ -0,0 +1,10 @@
+package org.openjdk.sound;
+
+import javax.sound.sampled.LineListener;
+
+public interface StreamListener{
+
+        public void update(StreamEvent e);
+
+}
+
--- a/src/org_openjdk_sound_PulseAudioSourceDataLine.c	Thu Jul 31 17:29:47 2008 -0400
+++ b/src/org_openjdk_sound_PulseAudioSourceDataLine.c	Fri Aug 01 10:59:19 2008 -0400
@@ -1,6 +1,90 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <jni.h>
+#include <pulse/pulseaudio.h>
 #include "org_openjdk_sound_PulseAudioSourceDataLine.h"
 
-#include <pulse/pulseaudio.h>
+void setJavaIntField(JNIEnv *env, jobject obj, void *ptr, char *fieldName) {
+	jclass cls = (*env)->GetObjectClass(env, obj);
+	jlong value = (int) ptr;
+	jfieldID fid =(*env)->GetFieldID(env, cls, fieldName, "I");
+	(*env)->SetIntField(env, obj, fid, value);
+	//CHECK No DeleteLocalReference Method?
+	//(*env)->DeleteLocalReference(cls);
+}
+
+void *getJavaIntField(JNIEnv *env, jobject obj, char *fieldName) {
+jclass cls = (*env)->GetObjectClass(env, obj);
+	jfieldID fid = (*env)->GetFieldID(env, cls, fieldName, "I");
+	jlong value = (*env)->GetIntField(env, obj, fid);
+	//(*env)->DeleteLocalReference(cls);
+	return (void *) value;
+}
+
+
+typedef struct java_context_t {
+	JNIEnv* env;
+	jobject obj;
+} java_context_t;
+
+
+/* defined in EventLoop.c */
+extern JNIEnv* pulse_thread_env;
+
+static void stream_drain_complete_callback(pa_stream* stream, int success,
+		void* userdata) {
+	assert(stream);
+	assert(success != 0);
+
+	pa_stream_disconnect(stream);
+}
+
+static void stream_state_change_callback(pa_stream* stream, void* userdata) {
+	assert(stream);
+	assert(userdata);
+
+	printf("entering stream_state_change_callback\n");
+
+	java_context_t* java_context = (java_context_t*)userdata;
+	JNIEnv* env;
+	
+	/* needed so we can create a stream from another thread
+	 */
+	if (pa_stream_get_state(stream) == PA_STREAM_CREATING) {
+		printf("java thread\n");
+		env = java_context->env;
+	} else {
+		env = pulse_thread_env;
+	}
+
+	jobject obj = java_context->obj;
+
+	printf("stream state changed to %d\n", pa_stream_get_state(stream));
+
+	/* Call the 'update' method in java
+	 * to handle all java-side events
+	 */
+	jclass cls = (*env)->GetObjectClass(env, obj);
+	if (cls == NULL) {
+		printf("unable to get class of object");
+		return;
+	}
+	jmethodID mid = (*env)->GetMethodID(env, cls, "update", "(I)V");
+	if (mid == NULL) {
+		printf("unable to get callback method\n");
+		return;
+
+	}
+	printf("calling update on java\n");
+	(*env)->CallVoidMethod(env, obj, mid, pa_stream_get_state(stream));
+
+	printf("returning form stream_state_change_callback\n");
+	return;
+
+}
+
+
 
 /*
  * Class:     org_openjdk_sound_PulseAudioSourceDataLine
@@ -8,8 +92,48 @@
  * Signature: (ILjava/lang/String;Ljava/lang/String;FIIZI)V
  */
 JNIEXPORT void JNICALL Java_org_openjdk_sound_PulseAudioSourceDataLine_native_1open
-(JNIEnv* env, jobject obj, jint arg, jstring arg1, jstring arg2, jfloat arg3, jint arg4, jint arg5, jboolean arg6, jint arg7) {
+(JNIEnv* env, jobject obj, jint contextPointer, jstring name, jstring encodingString, jfloat rate, jint size, jint channels, jboolean bigEndian, jint bufferSize) {
 
+	java_context_t* java_context = malloc(sizeof(java_context));
+	java_context->env = env;
+;
+	java_context->obj = (*env)->NewGlobalRef(env, obj);
+;
+	    
+    pa_sample_spec sample_spec;
+    
+    char *encoding = (*env)->GetStringUTFChars(env, encodingString, NULL);
+    
+    if( (strcmp(encoding, "PCM_UNSIGNED") == 0) && (size == 8)) {
+    	sample_spec.format = PA_SAMPLE_U8;
+    } else if( (strcmp(encoding, "ALAW") == 0) && (size == 8)) {
+    	sample_spec.format = PA_SAMPLE_ALAW;
+    } else if( (strcmp(encoding, "ULAW") == 0) && (size == 8)) {
+    	sample_spec.format = PA_SAMPLE_ULAW;
+    } else if ( (strcmp(encoding, "PCM_SIGNED") == 0) && (size == 16) && (bigEndian == 1)) {
+	sample_spec.format = PA_SAMPLE_S16BE;
+     } else if ( (strcmp(encoding, "PCM_SIGNED") == 0) && (size == 16) && (bigEndian == 0)) {
+	sample_spec.format = PA_SAMPLE_S16LE;
+     } else if ( (strcmp(encoding, "PCM_SIGNED") == 0) && (size == 32) && (bigEndian == 1)) {
+	sample_spec.format = PA_SAMPLE_S32BE;
+     } else if ( (strcmp(encoding, "PCM_SIGNED") == 0) && (size == 32) && (bigEndian == 0)) {
+	sample_spec.format = PA_SAMPLE_S32LE;
+     } else {
+     	printf("error in open");
+     	//TO DO: Invalid format :throw Exception;
+     }
+    
+    sample_spec.rate = rate;
+    sample_spec.channels = channels;
+
+    
+    pa_stream *stream = pa_stream_new(contextPointer, "default stream", &sample_spec, NULL);
+
+    pa_stream_set_state_callback(stream, stream_state_change_callback, java_context);
+  
+
+
+    setJavaIntField(env, obj, stream, "streamPointer");    
 }
 
 /*
@@ -60,6 +184,12 @@
  */
 JNIEXPORT void JNICALL Java_org_openjdk_sound_PulseAudioSourceDataLine_native_1start
 (JNIEnv *env, jobject obj) {
+	printf("start called\n");
+  
+    pa_stream *stream = getJavaIntField(env, obj, "streamPointer");
+
+    pa_stream_connect_playback(stream, NULL, NULL, 0, NULL, NULL);
+
 
 }