# HG changeset patch # User Omair Majid # Date 1222196575 14400 # Node ID c46f6e0e7959b60287bb000d80dbba0543dc3f9a # Parent 381f212496ebcb486c558ae890ef1bc286ffab72 2008-09-23 Omair Majid * src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Removed clipThreadStarted. Use clipThread.isAlive() etc. Removed unused clipSemaphore (ClipThread.run): Fixed to run without clipSemaphore. (close): Reset currentFrame and framesSinceOpen on close. (getFramePosition): Synchronized on framesSinceOpen. (loop): Replaced use of clipThreadStarted with clipThread.isAlive. (open): Initialize currentFrame, framesSinceOpen and endFrame. (stop): Remember which frame we stopped at. * src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java (open): Clean up properly on error. * src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java (write): On interrupt, return asap and mark the thread as being interrupted. * unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java (testFramePosition): Renamed to testFramePositionBeforeClose. (testFramePositionWithStartStop): New function. Checks that starting and stopping doesnt change the frame counts. (testFramePositionWithLoop): Checks that looping doesnt interfere with the frame position. * unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineRawTest.java (ThreadWriter.run): Removed useless TODO comment. * unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineTest.java (ThreadWriter.run): Removed useless TODO. (setUp): Initialize listeners. diff -r 381f212496eb -r c46f6e0e7959 src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java --- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Tue Sep 23 11:38:18 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Tue Sep 23 15:02:55 2008 -0400 @@ -38,7 +38,6 @@ package org.classpath.icedtea.pulseaudio; import java.io.IOException; -import java.util.concurrent.Semaphore; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -57,53 +56,57 @@ private float volume; // these are frame indices. so counted from 0 + // the current frame index private int currentFrame = 0; + + // total number of frames in this clip private int frameCount = 0; + + // the starting frame of the loop private int startFrame = 0; + // the ending frame of the loop private int endFrame = 0; + + // the total number of frames played since this line was opened private int framesSinceOpen = 0; public static final String DEFAULT_CLIP_NAME = "Clip"; private Object clipLock = new Object(); - private boolean clipThreadStarted; private int loopsLeft; - private Semaphore clipSemaphore = new Semaphore(1); + + // private Semaphore clipSemaphore = new Semaphore(1); private class ClipThread extends Thread { @Override public void run() { - clipThreadStarted = true; while (loopsLeft >= 0) { writeFrames(currentFrame, endFrame + 1); if (Thread.interrupted()) { // Thread.currentThread().interrupt(); - clipThreadStarted = false; + // System.out.println("returned from interrupted + // writeFrames"); break; } - try { - // if loop(0) has been called from the mainThread, - // wait until loopsLeft has been set - clipSemaphore.acquire(); - if (loopsLeft == 0) { - System.out.println("Reading to the end of the file"); - writeFrames(endFrame, getFrameLength()); - break; - } else { - synchronized (clipLock) { - currentFrame = startFrame; - loopsLeft--; - } + // if loop(0) has been called from the mainThread, + // wait until loopsLeft has been set + if (loopsLeft == 0) { + // System.out.println("Reading to the end of the file"); + // System.out.println("endFrame: " + endFrame); + writeFrames(endFrame, getFrameLength()); + break; + } else { + synchronized (clipLock) { + currentFrame = startFrame; + loopsLeft--; } - clipSemaphore.release(); - } catch (InterruptedException e) { - break; } } + // drain Operation operation; synchronized (eventLoop.threadLock) { @@ -145,6 +148,9 @@ try { eventLoop.threadLock.wait(); } catch (InterruptedException e) { + // System.out + // .println("interrupted while waiting for + // getWritableSize"); // clean up and return Thread.currentThread().interrupt(); stream.removeWriteListener(writeListener); @@ -160,16 +166,18 @@ framesToWrite * getFormat().getFrameSize()); remainingFrames -= framesToWrite; currentFrame += framesToWrite; + framesSinceOpen += framesToWrite; if (Thread.interrupted()) { Thread.currentThread().interrupt(); break; } // System.out.println("remaining frames" + remainingFrames); + // System.out.println("currentFrame: " + currentFrame); + // System.out.println("framesSinceOpen: " + framesSinceOpen); } } stream.removeWriteListener(writeListener); - } public PulseAudioClip(EventLoop eventLoop, AudioFormat[] formats, @@ -210,6 +218,9 @@ e.printStackTrace(); } + currentFrame = 0; + framesSinceOpen = 0; + PulseAudioMixer mixer = PulseAudioMixer.getInstance(); mixer.removeSourceLine(this); @@ -235,8 +246,7 @@ try { clipThread.join(); } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + // ignore } } @@ -280,8 +290,9 @@ if (!isOpen) { throw new IllegalStateException("Line not open"); } - - return (int) framesSinceOpen; + synchronized (clipLock) { + return (int) framesSinceOpen; + } } @Override @@ -324,7 +335,7 @@ System.out.println("Loop " + count + " called"); - if (clipThreadStarted && count != 0) { + if (clipThread.isAlive() && count != 0) { // Do nothing; behavior not specified by the Java API return; } @@ -358,7 +369,9 @@ this.data = new byte[bufferSize]; System.arraycopy(data, offset, this.data, 0, bufferSize); frameCount = bufferSize / format.getFrameSize(); - + currentFrame = 0; + framesSinceOpen = 0; + endFrame = frameCount - 1; PulseAudioVolumeControl volumeControl = new PulseAudioVolumeControl( this, eventLoop); PulseAudioMuteControl muteControl = new PulseAudioMuteControl(this, @@ -373,6 +386,20 @@ } + // FIXME This is not exposed by the Clip interface, but becomes available if + // using PulseAudioClip + // @Override + // public void open(AudioFormat format, int bufferSize) + // throws LineUnavailableException { + // throw new IllegalArgumentException("open(AudioFormat, int) on a Clip is + // not allowed"); + // } + + @Override + // public void open(AudioFormat format) throws LineUnavailableException { + // throw new IllegalArgumentException("open(AudioFormat) on a Clip is not + // allowed"); + // } public byte[] native_setVolume(float value) { return stream.native_setVolume(value); } @@ -428,7 +455,7 @@ } if (end == -1) { - end = frameCount; + end = frameCount - 1; } if (end < start) { @@ -496,7 +523,6 @@ e.printStackTrace(); } synchronized (clipLock) { - currentFrame = 0; loopsLeft = 0; } diff -r 381f212496eb -r c46f6e0e7959 src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java --- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java Tue Sep 23 11:38:18 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioDataLine.java Tue Sep 23 15:02:55 2008 -0400 @@ -160,7 +160,11 @@ } catch (LineUnavailableException e) { // error connecting to the server! - // FIXME clean up + stream.removePlaybackStartedListener(startedListener); + stream.removeUnderflowListener(stoppedListener); + stream.removeStateListener(openCloseListener); + stream.free(); + stream = null; throw e; } this.bufferSize = bufferSize; @@ -169,6 +173,7 @@ synchronized (eventLoop.threadLock) { if (stream.getState() != Stream.State.READY) { stream.disconnect(); + stream.free(); throw new LineUnavailableException( "unable to obtain a line"); } @@ -230,9 +235,9 @@ isStarted = false; } - // A BIG FIXME ! - /* + * TODO + * * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4791152 : * * a line is active in between calls to start() and stop(). In that sense, @@ -243,6 +248,8 @@ * underrun/overflow. * * + * HOWEVER, the javadocs say the opposite thing! + * */ public boolean isActive() { diff -r 381f212496eb -r c46f6e0e7959 src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java --- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java Tue Sep 23 11:38:18 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLine.java Tue Sep 23 15:02:55 2008 -0400 @@ -151,6 +151,8 @@ }; stream.addWriteListener(writeNotifier); + boolean interrupted = false; + while (remainingLength != 0) { synchronized (eventLoop.threadLock) { @@ -164,8 +166,8 @@ try { eventLoop.threadLock.wait(100); } catch (InterruptedException e) { - // FIXME - assert (false); + // ignore for now + interrupted = true; } } @@ -195,7 +197,9 @@ */ stream.removeWriteListener(writeNotifier); - + if (interrupted) { + Thread.currentThread().interrupt(); + } return sizeWritten; } diff -r 381f212496eb -r c46f6e0e7959 unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java --- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java Tue Sep 23 11:38:18 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java Tue Sep 23 15:02:55 2008 -0400 @@ -168,6 +168,8 @@ @Test public void testOpenEvent() throws LineUnavailableException, UnsupportedAudioFileException, IOException { + System.out + .println("This tests the OPEN event. You should see an OPEN on the next line"); Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); File soundFile = new File("testsounds/startup.wav"); AudioInputStream audioInputStream = AudioSystem @@ -327,8 +329,8 @@ } @Test - public void testLoop0InterruptsPlayback() throws LineUnavailableException, IOException, - UnsupportedAudioFileException { + public void testLoop0InterruptsPlayback() throws LineUnavailableException, + IOException, UnsupportedAudioFileException { Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); File soundFile = new File("testsounds/startup.wav"); AudioInputStream audioInputStream = AudioSystem @@ -410,7 +412,7 @@ } @Test - public void testFramePosition() throws LineUnavailableException, + public void testFramePositionBeforeClose() throws LineUnavailableException, UnsupportedAudioFileException, IOException { System.out .println("This tests if the Clip provides the correct frame position"); @@ -430,7 +432,89 @@ clip.close(); long expected = 136703; - long granularity = 100; + long granularity = 5; + System.out.println("Frames in " + fileName + ": " + expected); + System.out.println("Frame position in clip :" + pos); + Assert.assertTrue("Expected: " + expected + " got " + pos, Math + .abs(expected - pos) < granularity); + + } + + @Test + public void testFramePositionWithStartStop() + throws LineUnavailableException, UnsupportedAudioFileException, + IOException, InterruptedException { + + System.out + .println("This tests if the Clip provides the correct frame position"); + Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); + String fileName = "testsounds/logout.wav"; + File soundFile1 = new File(fileName); + AudioInputStream audioInputStream1 = AudioSystem + .getAudioInputStream(soundFile1); + clip.open(audioInputStream1); + + clip.start(); + + Thread.sleep(500); + clip.stop(); + Thread.sleep(5000); + clip.start(); + + clip.drain(); + + long pos = clip.getFramePosition(); + + clip.close(); + + long expected = 136703; + long granularity = 5; + System.out.println("Frames in " + fileName + ": " + expected); + System.out.println("Frame position in clip :" + pos); + Assert.assertTrue("Expected: " + expected + " got " + pos, Math + .abs(expected - pos) < granularity); + + } + + @Test + public void testFramePositionWithLoop() throws LineUnavailableException, + UnsupportedAudioFileException, IOException, InterruptedException { + System.out.println("This tests if the Clip provides the correct frame " + + "position with a bit of looping in the clip"); + Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); + String fileName = "testsounds/logout.wav"; + File soundFile1 = new File(fileName); + AudioInputStream audioInputStream1 = AudioSystem + .getAudioInputStream(soundFile1); + clip.open(audioInputStream1); + + clip.setLoopPoints(0, -1); + clip.loop(1); + Thread.sleep(500); + clip.stop(); + Thread.sleep(2000); + clip.start(); + Thread.sleep(100); + clip.stop(); + Thread.sleep(4000); + clip.start(); + Thread.sleep(100); + clip.stop(); + Thread.sleep(2000); + clip.start(); + Thread.sleep(100); + clip.stop(); + Thread.sleep(3000); + clip.start(); + + clip.drain(); + + long pos = clip.getFramePosition(); + + clip.close(); + + long expected = 136703 * 1; + long granularity = 5; System.out.println("Frames in " + fileName + ": " + expected); System.out.println("Frame position in clip :" + pos); Assert.assertTrue("Expected: " + expected + " got " + pos, Math @@ -451,6 +535,7 @@ .getAudioInputStream(soundFile1); clip.open(audioInputStream1); + clip.setLoopPoints(0, -1); clip.loop(1); clip.drain(); @@ -460,7 +545,7 @@ clip.close(); long expected = 136703 * 2; - long granularity = 100; + long granularity = 5; System.out.println("Frames in " + fileName + ": " + expected); System.out.println("Frame position in clip :" + pos); Assert.assertTrue("Expected: " + expected + " got " + pos, Math diff -r 381f212496eb -r c46f6e0e7959 unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineRawTest.java --- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineRawTest.java Tue Sep 23 11:38:18 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineRawTest.java Tue Sep 23 15:02:55 2008 -0400 @@ -113,7 +113,6 @@ } catch (LineUnavailableException e) { e.printStackTrace(); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } diff -r 381f212496eb -r c46f6e0e7959 unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineTest.java --- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineTest.java Tue Sep 23 11:38:18 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioSourceDataLineTest.java Tue Sep 23 15:02:55 2008 -0400 @@ -119,7 +119,6 @@ } catch (LineUnavailableException e) { e.printStackTrace(); } catch (IOException e) { - // TODO Auto-generated catch block e.printStackTrace(); } } @@ -152,7 +151,7 @@ stopped = 0; opened = 0; closed = 0; - + listenerCalled = 0; } @Test