Mercurial > hg > pulseaudio
changeset 95:23c52715cfb2
added start, stop and loop in Clip
author | iivan@town.yyz.redhat.com |
---|---|
date | Wed, 20 Aug 2008 14:27:45 -0400 |
parents | c1154939ba8b |
children | 9a4c1d255bc6 |
files | src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java |
diffstat | 3 files changed, 215 insertions(+), 51 deletions(-) [+] |
line wrap: on
line diff
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Wed Aug 20 13:34:24 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioClip.java Wed Aug 20 14:27:45 2008 -0400 @@ -38,8 +38,10 @@ package org.classpath.icedtea.pulseaudio; import java.io.IOException; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.concurrent.Semaphore; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -51,7 +53,7 @@ import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Control.Type; -public class PulseAudioClip implements Clip { +public class PulseAudioClip extends PulseAudioDataLine implements Clip { private byte[] data = null; @@ -59,45 +61,121 @@ private int bufferSize = 0; // these are frame indices. so counted from 0 - private long currentFrame = 0; + private int currentFrame = 0; private int frameCount = 0; - private long startFrame = 0; - private long endFrame = 0; - private long framesSinceOpen = 0; + private int startFrame = 0; + private int endFrame = 0; + private int framesSinceOpen = 0; private AudioFormat currentFormat = null; private static final AudioFormat DEFAULT_FORMAT = new AudioFormat( AudioFormat.Encoding.PCM_UNSIGNED, 22050, 8, 2, 2, 22050 / 2, false); - private boolean isOpen = false; - private List<LineListener> lineListeners = null; private static final int DEFAULT_BUFFER_SIZE = 0; public static final String DEFAULT_CLIP_NAME = "Clip"; - private Stream stream; + private Object clipLock = new Object(); + private boolean clipThreadStarted; + private int loopsLeft; + private Semaphore clipSemaphore= new Semaphore(1); - private Thread clipLoop = new Thread() { + private class ClipThread extends Thread { @Override public void run() { - while (true) { - + clipThreadStarted = true; + while(loopsLeft >= 0) { + writeFrames(currentFrame, endFrame + 1); + if(Thread.interrupted()) { + //Thread.currentThread().interrupt(); + clipThreadStarted = false; + return; + } + + + + 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()); + return; + } else { + synchronized (clipLock) { + currentFrame = startFrame; + loopsLeft--; + } + } + clipSemaphore.release(); + } catch (InterruptedException e) { + return; } - + + + } } - - }; + } + + private ClipThread clipThread; + + + private void writeFrames(int startingFrame, int lastFrame) { - public PulseAudioClip() { - lineListeners = new LinkedList<LineListener>(); + int remainingFrames = lastFrame - startingFrame - 1; + while(remainingFrames > 0) { + synchronized (eventLoop.threadLock) { + int availableSize = stream.getWritableSize(); + int framesToWrite = Math.min(remainingFrames, availableSize / getFormat().getFrameSize()); + stream.write(data, currentFrame * getFormat().getFrameSize(), framesToWrite * getFormat().getFrameSize()); + remainingFrames -= framesToWrite; + currentFrame += framesToWrite; + if(Thread.interrupted()) { + Thread.currentThread().interrupt(); + break; + } + System.out.println("remaining frames" + remainingFrames); + } + } } - @Override - public void addLineListener(LineListener listener) { - lineListeners.add(listener); + + static { + try { + String library = new java.io.File(".").getCanonicalPath() + + java.io.File.separatorChar + + System.mapLibraryName("pulse-java"); + System.out.println(library); + System.load(library); + } catch (IOException e) { + assert ("Loading failed".endsWith("library")); + } } + public PulseAudioClip(EventLoop eventLoop, AudioFormat[] formats, AudioFormat defaultFormat) { + supportedFormats = formats; + this.eventLoop = eventLoop; + this.lineListeners = new ArrayList<LineListener>(); + this.defaultFormat = defaultFormat; + this.currentFormat = defaultFormat; + clipThread = new ClipThread(); + + } + + protected void connectLine() { + StreamBufferAttributes bufferAttributes = new StreamBufferAttributes( + StreamBufferAttributes.SANE_DEFAULT, + StreamBufferAttributes.SANE_DEFAULT, + StreamBufferAttributes.SANE_DEFAULT, + StreamBufferAttributes.SANE_DEFAULT, + StreamBufferAttributes.SANE_DEFAULT); + + stream.connectForPlayback(null, bufferAttributes); + } + + @Override public int available() { return 0; // a clip always returns 0 @@ -105,7 +183,12 @@ @Override public void close() { - // TODO Auto-generated method stub + try { + clipThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + stream.drain(); stream.disconnect(); isOpen = false; @@ -113,7 +196,15 @@ @Override public void drain() { - stream.drain(); + Operation operation; + + synchronized (eventLoop.threadLock) { + operation = stream.drain(); + } + + operation.waitForCompletion(); + operation.releaseReference(); + } @Override @@ -169,7 +260,9 @@ @Override public long getLongFramePosition() { - return framesSinceOpen; + synchronized(clipLock) { + return framesSinceOpen; + } } @Override @@ -177,12 +270,16 @@ if (!isOpen) { return AudioSystem.NOT_SPECIFIED; } - return frameCount / currentFormat.getFrameSize(); + synchronized(clipLock) { + return frameCount / currentFormat.getFrameSize(); + } } @Override public long getMicrosecondPosition() { - return framesSinceOpen / currentFormat.getFrameSize(); + synchronized(clipLock) { + return framesSinceOpen / currentFormat.getFrameSize(); + } } @Override @@ -210,8 +307,23 @@ @Override public void loop(int count) { - // TODO Auto-generated method stub - + System.out.println("Loop " + count + " called"); + if(clipThreadStarted && count != 0) { + //Do nothing; behavior not specified by the Java API + return; + } + synchronized(clipLock) { + if(currentFrame > endFrame) { + loopsLeft = 0; + } else { + loopsLeft = count; + } + } + if (!clipThread.isAlive()) { + clipThread = new ClipThread(); + clipThread.start(); + } + } @Override @@ -223,24 +335,22 @@ public void open(AudioFormat format, byte[] data, int offset, int bufferSize) throws LineUnavailableException { - long contextPointer = EventLoop.getEventLoop().getContextPointer(); - int channels = 2; - int sampleRate = 22050; - stream = new Stream(contextPointer, DEFAULT_CLIP_NAME, - Stream.Format.PA_SAMPLE_U8, sampleRate, channels); - + super.open(format); + this.data = new byte[bufferSize]; + System.arraycopy(data, offset, this.data, 0, bufferSize); + frameCount = bufferSize / format.getFrameSize(); isOpen = true; } @Override public void open(AudioInputStream stream) throws LineUnavailableException, IOException { - // TODO Auto-generated method stub - byte[] buffer = new byte[(int) (stream.getFrameLength() * stream .getFormat().getFrameSize())]; + stream.read(buffer, 0, buffer.length); - open(stream.getFormat(), buffer, 0, DEFAULT_BUFFER_SIZE); + open(stream.getFormat(), buffer, 0, buffer.length); + } @@ -255,8 +365,10 @@ if (frames > frameCount) { throw new IllegalArgumentException("incorreft frame value"); } - - currentFrame = frames; + + synchronized(clipLock) { + currentFrame = frames; + } } @@ -271,26 +383,47 @@ "ending point must be greater than or equal to the starting point"); } - startFrame = start; - endFrame = end; + synchronized(clipLock) { + startFrame = start; + endFrame = end; + } } @Override public void setMicrosecondPosition(long microseconds) { float frameIndex = microseconds * currentFormat.getFrameRate(); - currentFrame = (long) frameIndex; + synchronized(clipLock) { + currentFrame = (int) frameIndex; + } } @Override public void start() { - stream.cork(false); + if(!clipThread.isAlive()) { + synchronized(clipLock) { + loopsLeft = 0; + } + clipThread = new ClipThread(); + clipThread.start(); + } } - - @Override + + public void stop() { - stream.cork(true); + if(clipThread.isAlive()) { + clipThread.interrupt(); + } + try { + clipThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + synchronized(clipLock) { + currentFrame = 0; + loopsLeft = 0; + } } }
--- a/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java Wed Aug 20 13:34:24 2008 -0400 +++ b/src/java/org/classpath/icedtea/pulseaudio/PulseAudioMixer.java Wed Aug 20 14:27:45 2008 -0400 @@ -49,6 +49,7 @@ import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Clip; import javax.sound.sampled.Control; import javax.sound.sampled.DataLine; import javax.sound.sampled.Line; @@ -318,10 +319,9 @@ defaultFormat); } - PulseAudioClip clip = new PulseAudioClip(); - - if (info.matches(clip.getLineInfo())) { - clips.add(clip); + if ((info.getLineClass() == Clip.class)) { + Clip clip = new PulseAudioClip(eventLoop, formats, defaultFormat); + clips.add((PulseAudioClip) clip); return clip; }
--- a/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java Wed Aug 20 13:34:24 2008 -0400 +++ b/unittests/org/classpath/icedtea/pulseaudio/PulseAudioClipTest.java Wed Aug 20 14:27:45 2008 -0400 @@ -102,16 +102,47 @@ } @Test - public void testPlayClip() throws LineUnavailableException, IOException, UnsupportedAudioFileException { + public void testLoopStopStartClip() throws LineUnavailableException, IOException, UnsupportedAudioFileException { Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); File soundFile = new File("testsounds/startup.wav"); AudioInputStream audioInputStream = AudioSystem .getAudioInputStream(soundFile); clip.open(audioInputStream); - clip.loop(1); + clip.setLoopPoints((int) (clip.getFrameLength() / 4), (int) (clip.getFrameLength() / 2)); + clip.loop(4); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + clip.stop(); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + clip.start(); + clip.close(); + } + + @Test + public void testLoop0Clip() throws LineUnavailableException, IOException, UnsupportedAudioFileException { + Clip clip = (Clip) mixer.getLine(new Line.Info(Clip.class)); + File soundFile = new File("testsounds/startup.wav"); + AudioInputStream audioInputStream = AudioSystem + .getAudioInputStream(soundFile); + clip.open(audioInputStream); - + clip.setLoopPoints((int) (clip.getFrameLength() / 4), (int) (clip.getFrameLength() / 2)); + clip.loop(4); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + clip.loop(0); + clip.close(); } @Test (expected=IllegalArgumentException.class)