commit 817fcc796d5552db8cbffb2426e8844b31f432d3 Author: Rik Veenboer Date: Fri Mar 22 19:19:12 2013 +0100 initial commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..7c6048f --- /dev/null +++ b/.classpath @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4bf388 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/bin +/.settings +/sound diff --git a/.project b/.project new file mode 100644 index 0000000..65916d7 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + Sound + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..dbf0eb1 --- /dev/null +++ b/build.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lame.exe b/lame.exe new file mode 100644 index 0000000..7e69522 Binary files /dev/null and b/lame.exe differ diff --git a/launch4j.xml b/launch4j.xml new file mode 100644 index 0000000..548e148 --- /dev/null +++ b/launch4j.xml @@ -0,0 +1,22 @@ + + false + gui + stream.jar + stream.exe + + + + normal + http://java.com/download + + false + false + + + + + 1.6.0 + + preferJre + + \ No newline at end of file diff --git a/lib/commons-cli-1.2.jar b/lib/commons-cli-1.2.jar new file mode 100644 index 0000000..ce4b9ff Binary files /dev/null and b/lib/commons-cli-1.2.jar differ diff --git a/lib/commons-io-2.4.jar b/lib/commons-io-2.4.jar new file mode 100644 index 0000000..90035a4 Binary files /dev/null and b/lib/commons-io-2.4.jar differ diff --git a/lib/jid3lib-0.5.4.jar b/lib/jid3lib-0.5.4.jar new file mode 100644 index 0000000..63e5a67 Binary files /dev/null and b/lib/jid3lib-0.5.4.jar differ diff --git a/lib/jl1.0.1.jar b/lib/jl1.0.1.jar new file mode 100644 index 0000000..bd5fb8b Binary files /dev/null and b/lib/jl1.0.1.jar differ diff --git a/lib/ostermillerutils-1.08.01.jar b/lib/ostermillerutils-1.08.01.jar new file mode 100644 index 0000000..a3d081c Binary files /dev/null and b/lib/ostermillerutils-1.08.01.jar differ diff --git a/libgomp-1.dll b/libgomp-1.dll new file mode 100644 index 0000000..f4afc3e Binary files /dev/null and b/libgomp-1.dll differ diff --git a/libmad.dll b/libmad.dll new file mode 100644 index 0000000..98730c5 Binary files /dev/null and b/libmad.dll differ diff --git a/libmp3lame.dll b/libmp3lame.dll new file mode 100644 index 0000000..719126b Binary files /dev/null and b/libmp3lame.dll differ diff --git a/pipes.dll b/pipes.dll new file mode 100644 index 0000000..be5b824 Binary files /dev/null and b/pipes.dll differ diff --git a/play.exe b/play.exe new file mode 100644 index 0000000..e3382ea Binary files /dev/null and b/play.exe differ diff --git a/pthreadgc2.dll b/pthreadgc2.dll new file mode 100644 index 0000000..f64cc52 Binary files /dev/null and b/pthreadgc2.dll differ diff --git a/sox.exe b/sox.exe new file mode 100644 index 0000000..e3382ea Binary files /dev/null and b/sox.exe differ diff --git a/soxi.exe b/soxi.exe new file mode 100644 index 0000000..e3382ea Binary files /dev/null and b/soxi.exe differ diff --git a/src/old/Converter.java b/src/old/Converter.java new file mode 100644 index 0000000..4064754 --- /dev/null +++ b/src/old/Converter.java @@ -0,0 +1,186 @@ +package old; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javazoom.jl.decoder.Bitstream; +import javazoom.jl.decoder.BitstreamException; +import mimis.exception.worker.ActivateException; +import mimis.exception.worker.DeactivateException; +import mimis.worker.Worker; + +import com.Ostermiller.util.CircularByteBuffer; + +public class Converter extends Worker { + public static final String COMMAND = "lame --mp3input --cbr %s - - --quiet"; + public static final int BYTES = 4096; // bytes + public static final int BUFFER = 30000; // milliseconds + public static final int BUFFERING = 1000; // milliseconds + + protected int targetRate; + protected int rate; + protected int buffer; + protected boolean convert; + + protected Process process; + protected InputStream sourceInputStream, processInputStream, inputStream; + protected OutputStream processOutputStream; + protected CircularByteBuffer circularByteBuffer; + protected BufferWorker bufferWorker; + + public Converter(InputStream inputStream) { + this(inputStream, -1); + } + + public Converter(InputStream inputStream, int targetRate) { + this.sourceInputStream = inputStream; + this.targetRate = targetRate; + bufferWorker = new BufferWorker(); + convert = false; + } + + public void exit() { + super.exit(); + bufferWorker.exit(); + } + + public synchronized void activate() throws ActivateException { + /* Read bitrate */ + BufferedInputStream bufferedInputStream = new BufferedInputStream(sourceInputStream); + Bitstream bitStream = new Bitstream(bufferedInputStream); + try { + rate = bitStream.readFrame().bitrate() / 1000; + buffer = BUFFER * rate / 8; + } catch (BitstreamException e) { + log.error(e); + throw new ActivateException(); + } + + /* Check for need to convert */ + if (targetRate < 0 || rate == targetRate) { + log.debug("No conversion required"); + inputStream = sourceInputStream; + } else { + log.debug("Converting from " + rate + "kbps to " + targetRate + "kbps"); + try { + String command = String.format(COMMAND, rate > targetRate ? "-B " + targetRate : "-F -b " + targetRate); + log.debug("Starting process: " + command); + process = Runtime.getRuntime().exec(command); + processInputStream = process.getInputStream(); + processOutputStream = process.getOutputStream(); + + /* Buffer output */ + circularByteBuffer = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE); + inputStream = circularByteBuffer.getInputStream(); + bufferWorker.start(); + convert = true; + } catch (IOException e) { + log.error(e); + throw new ActivateException(); + } + } + super.activate(); + notifyAll(); + } + + protected void deactivate() throws DeactivateException { + super.deactivate(); + try { + sourceInputStream.close(); + bufferWorker.stop(); + if (convert) { + circularByteBuffer.clear(); + convert = false; + } + inputStream.close(); + } catch (IOException e) { + log.error(e); + throw new DeactivateException(); + } + } + + protected void work() { + if (!convert) { + try { + synchronized (this) { + wait(); + } + } catch (InterruptedException e) { + log.error(e); + } + return; + } + byte[] bytes = new byte[BYTES]; + int read = 0; + try { + log.debug("Writing input to process"); + while ((read = sourceInputStream.read(bytes)) > 0 && !deactivate) { + /* Limit buffer size */ + while (inputStream.available() > buffer) { + int progress = (int) ((1 - (inputStream.available() - buffer) / (float) buffer) * 100); + log.trace("Waiting for buffer to empty: " + progress + "%"); + sleep(BUFFERING); + } + processOutputStream.write(bytes, 0, read); + } + processOutputStream.close(); + log.debug("Stopped writing input to process"); + process.waitFor(); + log.debug("Process finished"); + } catch (IOException e) { + } catch (InterruptedException e) { + log.error(e); + } + stop(); + } + + public synchronized InputStream getInputStream() { + if (!active()) { + if (!activate) { + start(); + } + try { + wait(); + } catch (InterruptedException e) { + log.error(e); + } + } + return inputStream; + } + + public synchronized void setInputStream(InputStream inputStream) { + this.inputStream = inputStream; + } + + class BufferWorker extends Worker { + protected void work() { + byte[] bytes = new byte[BYTES]; + int read = 0; + try { + OutputStream bufferOutputStream = circularByteBuffer.getOutputStream(); + log.debug("Start buffering process output"); + while ((read = processInputStream.read(bytes, 0, BYTES)) > 0) { + bufferOutputStream.write(bytes, 0, read); + } + log.debug("Finished buffering process output"); + bufferOutputStream.close(); + } catch (IOException e) {} + stop(); + } + } + + public static void main(String[] args) { + Mp3 mp3 = new Mp3(new File("stream.mp3"), 128); + InputStream inputStream = mp3.getInputStream(); + + /* Play */ + //Utils.play(inputStream); + + /* Write to file */ + Utils.write(inputStream, new File("output.mp3")); + mp3.exit(); + } +} diff --git a/src/old/List.java b/src/old/List.java new file mode 100644 index 0000000..3976268 --- /dev/null +++ b/src/old/List.java @@ -0,0 +1,198 @@ +package old; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Scanner; + +import mimis.exception.worker.ActivateException; +import mimis.worker.Worker; + +import com.Ostermiller.util.BufferOverflowException; +import com.Ostermiller.util.CircularByteBuffer; +import com.Ostermiller.util.CircularObjectBuffer; + +public class List extends Worker { + public static final int STEP = 80; // milliseconds + public static final int RATE = 192; // kbps + public static final int OVERLAP = 20000; // milliseconds + + protected File file; + protected String[] fileArray; + + protected int rate; + protected int chunk; + protected int overlap; + protected byte[] bytes; + protected boolean next; + protected Mp3 mp3, nextMp3; + + protected InputStream mp3InputStream; + protected OutputStream audioOutputStream; + protected CircularByteBuffer circularByteBuffer; + protected CircularObjectBuffer circularStringBuffer; + + public List(File file) { + this(file, RATE); + } + + public List(File file, int rate) { + this.file = file; + this.rate = rate; + chunk = STEP * rate / 8; + overlap = OVERLAP * RATE / 8; + bytes = new byte[chunk]; + next = true; + } + + public void exit() { + super.exit(); + if (mp3 != null) { + mp3.exit(); + } + if (nextMp3 != null) { + nextMp3.exit(); + } + } + + protected synchronized void activate() throws ActivateException { + try { + Scanner scanner = new Scanner(file); + ArrayList fileList = new ArrayList(); + while (scanner.hasNextLine()) { + fileList.add(scanner.nextLine()); + } + if (fileList.size() > 0) { + fileArray = fileList.toArray(new String[0]); + + circularByteBuffer = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE); + audioOutputStream = circularByteBuffer.getOutputStream(); + + circularStringBuffer = new CircularObjectBuffer(CircularByteBuffer.INFINITE_SIZE); + setNext(); + super.activate(); + notifyAll(); + return; + } + } catch (FileNotFoundException e) { + log.error(e); + } + throw new ActivateException(); + } + + protected synchronized void work() { + try { + int left = chunk; + while (left > 0) { + /* Check for need to load next mp3 */ + int available = mp3InputStream == null ? -1 : mp3InputStream.available(); + boolean expect = mp3 == null ? false : mp3.active(); + + /* Act when no more data is expected */ + if (!expect) { + if (available < overlap) { + setNext(); + next = false; + nextMp3.start(); + } + if (available < 1) { + swap(); + } + } + + /* Transfer data */ + int read = mp3InputStream.read(bytes, 0, left); + left -= read; + audioOutputStream.write(bytes, 0, read); + } + } catch (IOException e) { + /* Swap to next if stream has stopped */ + setNext(); + swap(); + } catch (IllegalStateException e) { + log.error(e); + } + sleep(STEP); + } + + protected File getRandomFile() { + return new File(fileArray[(int) (Math.random() * fileArray.length)]); + } + + public synchronized void setNext() { + if (nextMp3 == null) { + log.debug("Initialize next mp3"); + nextMp3 = new Mp3(getRandomFile(), rate); + } else if (next) { + log.debug("Load next mp3"); + nextMp3.setFile(getRandomFile()); + } + } + + public synchronized void next() { + log.debug("Stop current mp3"); + mp3.stop(); + } + + public void swap() { + log.debug("Swap to next mp3"); + Mp3 swapMp3 = mp3; + mp3 = nextMp3; + nextMp3 = swapMp3; + next = true; + + /* Swap stream and announce title */ + mp3InputStream = mp3.getInputStream(); + try { + circularStringBuffer.write(mp3.getTitle()); + } catch (BufferOverflowException e) { + log.error(e); + } catch (IllegalStateException e) { + log.error(e); + } catch (InterruptedException e) { + log.error(e); + } + } + + public synchronized InputStream getInputStream() { + if (circularByteBuffer == null) { + start(); + try { + wait(); + } catch (InterruptedException e) { + log.error(e); + } + } + return circularByteBuffer.getInputStream(); + } + + public synchronized CircularObjectBuffer getMetaBuffer() { + if (circularStringBuffer == null) { + start(); + try { + wait(); + } catch (InterruptedException e) { + log.error(e); + } + } + return circularStringBuffer; + } + + public static void main(String[] args) { + int rate = 192; + List list = new List(new File("mp3"), rate); + /*Shoutcast shoutcast = new Shoutcast(null, rate, 9876); + shoutcast.start(); + shoutcast.setInputStream(list.getInputStream()); + shoutcast.setMetaBuffer(list.getMetaBuffer());*/ + while (true) { + try { + Thread.sleep(15000); + list.next(); + } catch (InterruptedException e) {} + } + } +} diff --git a/src/old/Mp3.java b/src/old/Mp3.java new file mode 100644 index 0000000..5089a9b --- /dev/null +++ b/src/old/Mp3.java @@ -0,0 +1,79 @@ +package old; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +import mimis.exception.worker.ActivateException; + +import org.farng.mp3.MP3File; +import org.farng.mp3.TagException; + +public class Mp3 extends Converter { + protected File file; + protected String title; + + public Mp3(File file) { + this(file, -1); + } + + public Mp3(File file, int targetRate) { + super(null, targetRate); + setFile(file); + title = ""; + } + + public synchronized void activate() throws ActivateException { + /* Open file */ + try { + sourceInputStream = new FileInputStream(file); + } catch (FileNotFoundException e) { + log.error(e); + throw new ActivateException(); + } + + /* Read ID3V2 tags */ + try { + MP3File mp3File = new MP3File(file); + String album = clean(mp3File.getID3v2Tag().getAlbumTitle()); + String artist = clean(mp3File.getID3v2Tag().getLeadArtist()); + String track = clean(mp3File.getID3v2Tag().getSongTitle()); + if (album.isEmpty()) { + title = String.format("%s - %s", artist, track, album); + } else { + title = String.format("%s - %s {%s}", artist, track, album); + } + log.debug("Title: " + title); + } catch (IOException e) { + log.error(e); + } catch (TagException e) { + log.error(e); + } + try { + sourceInputStream.skip(100000); + } catch (IOException e) {} + super.activate(); + } + + protected String clean(String input) { + String output = input.replace("\0", ""); + return output.replace("ÿþ", ""); + } + + public String getTitle() { + return title; + } + + public void setFile(File file) { + this.file = file; + } + + public static void main(String[] args) { + final Mp3 mp3 = new Mp3(new File("input.mp3"), 128); + Utils.write(mp3.getInputStream(), new File("one.mp3")); + mp3.setFile(new File("stream.mp3")); + Utils.write(mp3.getInputStream(), new File("two.mp3")); + mp3.exit(); + } +} diff --git a/src/old/Utils.java b/src/old/Utils.java new file mode 100644 index 0000000..bd7f2a8 --- /dev/null +++ b/src/old/Utils.java @@ -0,0 +1,42 @@ +package old; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import javazoom.jl.decoder.JavaLayerException; +import javazoom.jl.player.Player; + +public class Utils { + public static final int BUFFER = 2048; // bytes + + public static void play(InputStream inputStream) { + try { + Thread.sleep(5000); + new Player(new BufferedInputStream(inputStream)).play(); + } catch (JavaLayerException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public static void write(InputStream inputStream, File file) { + byte[] bytes = new byte[BUFFER]; + int read = 0; + try { + FileOutputStream fileOutputStream = new FileOutputStream(file); + while ((read = inputStream.read(bytes)) > 0) { + fileOutputStream.write(bytes, 0, read); + } + fileOutputStream.close(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/sound/Consumer.java b/src/sound/Consumer.java new file mode 100644 index 0000000..5dcc737 --- /dev/null +++ b/src/sound/Consumer.java @@ -0,0 +1,8 @@ +package sound; + +public interface Consumer { + public void start(Producer producer); + public void start(); + public void stop(); + public void exit(); +} diff --git a/src/sound/Format.java b/src/sound/Format.java new file mode 100644 index 0000000..c24c3d3 --- /dev/null +++ b/src/sound/Format.java @@ -0,0 +1,13 @@ +package sound; + +import javax.sound.sampled.AudioFormat; + +public interface Format extends Cloneable { + public interface Standard extends Format { + public AudioFormat getAudioFormat(); + } + + public interface Mp3 extends Format { + public int getRate(); + } +} \ No newline at end of file diff --git a/src/sound/GreedyInputStream.java b/src/sound/GreedyInputStream.java new file mode 100644 index 0000000..f9999cb --- /dev/null +++ b/src/sound/GreedyInputStream.java @@ -0,0 +1,76 @@ +package sound; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class GreedyInputStream extends BufferedInputStream { + protected Log log = LogFactory.getLog(getClass()); + + protected static final int SLEEP = 500; + protected static final int BUFFER_SIZE = 30000; // in bytes + protected static final int MINIMUM_SIZE = 1000; // in bytes + + protected int bufferSize; + protected int minimumSize; + protected boolean hoard; + + public GreedyInputStream(InputStream inputStream) { + this(inputStream, BUFFER_SIZE, MINIMUM_SIZE); + } + + public GreedyInputStream(InputStream inputStream, int bufferSize) { + super(inputStream, bufferSize); + this.bufferSize = bufferSize; + hoard = true; + } + + public GreedyInputStream(InputStream inputStream, int bufferSize, int minimumSize) { + this(inputStream, bufferSize); + this.minimumSize = minimumSize; + } + + public int read() throws IOException { + hoard(); + byte[] buffer = new byte[1]; + in.read(buffer, 0, 1); + return (int) buffer[0]; + } + + public int read(byte[] buffer, int offset, int length) throws IOException { + hoard(); + in.read(buffer, offset, length); + return length; + } + + public void hoard() throws IOException { + int available = available(); + if (hoard && available < MINIMUM_SIZE) { + long time = System.currentTimeMillis(); + do { + try { + Thread.sleep(SLEEP); + } catch (InterruptedException e) { + log.warn(e); + } + } while (available() < BUFFER_SIZE); + log.debug(String.format("Buffered %d bytes in %s milliseconds", BUFFER_SIZE - available, System.currentTimeMillis() - time)); + } + } + + public void clear() throws IOException { + this.buf = new byte[buf.length]; + reset(); + } + + public void drain() { + drain(true); + } + + public void drain(boolean drain) { + hoard = !drain; + } +} diff --git a/src/sound/Port.java b/src/sound/Port.java new file mode 100644 index 0000000..b123ec1 --- /dev/null +++ b/src/sound/Port.java @@ -0,0 +1,119 @@ +package sound; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import javax.sound.sampled.AudioFormat; + +import mimis.exception.worker.ActivateException; +import mimis.exception.worker.DeactivateException; +import mimis.worker.Worker; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import sound.Format.Standard; +import sound.SoxBuilder.File; +import sound.SoxBuilder.Option; +import sound.SoxBuilder.File.Type; + +public class Port extends Worker implements Consumer { + protected Log log = LogFactory.getLog(getClass()); + + protected static final int BUFFER_SIZE = 1024 * 4; // in bytes + + protected String device; + protected Producer producer; + protected Process process; + protected InputStream producerInputStream; + protected OutputStream processOutputStream; + protected ProcessBuilder processBuilder; + + public Port() { + this("0"); + } + + public Port(String device) { + this.device = device; + } + + @SuppressWarnings("static-access") + public void start(Producer producer) { + this.producer = producer; + producerInputStream = producer.getInputStream(); + + String command = ""; + if (producer instanceof Standard) { + AudioFormat audioFormat = ((Standard) producer).getAudioFormat(); + SoxBuilder.addFile(File.setType(Type.STANDARD).setOptions(audioFormat)); + } else if (producer instanceof Format.Mp3) { + SoxBuilder.addFile(File.setType(Type.STANDARD).setOption(File.Format.MP3)); + } + command = SoxBuilder + .setOption(Option.QUIET) + .addFile(File.setType(Type.DEVICE)) + .build(); + + log.debug(String.format("Build process (\"%s\")", command)); + processBuilder = new ProcessBuilder(command.split(" ")); + processBuilder.environment().put("AUDIODEV", device); + + start(true); + } + + protected void activate() throws ActivateException { + producer.start(); + if (process == null) { + try { + process = processBuilder.start(); + } catch (IOException e) { + log.error(e); + throw new ActivateException(); + } + processOutputStream = process.getOutputStream(); + } + super.activate(); + } + + protected void deactivate() throws DeactivateException { + super.deactivate(); + try { + processOutputStream.flush(); + } catch (IOException e) { + log.error(e); + throw new DeactivateException(); + } + } + + public void exit() { + try { + log.debug("close process output stream"); + processOutputStream.close(); + + log.debug("wait for process to terminate"); + process.waitFor(); + } catch (IOException e) { + log.error(e); + } catch (InterruptedException e) { + log.error(e); + } finally { + process = null; + } + } + + protected void work() { + try { + byte[] buffer = new byte[BUFFER_SIZE]; + int read = producerInputStream.read(buffer, 0, buffer.length); + if (read > 0) { + processOutputStream.write(buffer, 0, read); + } else { + exit(); + } + } catch (IOException e) { + log.error(e); + exit(); + } + } +} diff --git a/src/sound/Producer.java b/src/sound/Producer.java new file mode 100644 index 0000000..c25eb48 --- /dev/null +++ b/src/sound/Producer.java @@ -0,0 +1,10 @@ +package sound; + +import java.io.InputStream; + +public interface Producer extends Format { + public InputStream getInputStream(); + public void start(); + public void stop(); + public void exit(); +} \ No newline at end of file diff --git a/src/sound/Source.java b/src/sound/Source.java new file mode 100644 index 0000000..d5ef45d --- /dev/null +++ b/src/sound/Source.java @@ -0,0 +1,159 @@ +package sound; + +import java.io.IOException; +import java.io.InputStream; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.SourceDataLine; + +import javazoom.jl.decoder.JavaLayerException; +import javazoom.jl.player.Player; +import mimis.exception.worker.ActivateException; +import mimis.exception.worker.DeactivateException; +import mimis.worker.Worker; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class Source implements Consumer { + protected Log log = LogFactory.getLog(getClass()); + + protected static final int BUFFER_SIZE = 1024 * 4; // in bytes + protected static final int PLAY_FRAMES = 10; // count + + protected String name; + protected Producer producer; + protected InputStream producerInputStream; + protected Worker worker; + + public Source(String name) throws LineUnavailableException { + this.name = name; + } + + public void start() { + if (worker != null) { + worker.start(true); + } + } + + public void start(Producer producer) { + this.producer = producer; + producerInputStream = producer.getInputStream(); + if (worker != null) { + worker.exit(); + } + if (producer instanceof Format.Standard) { + log.debug("Format.Standard"); + worker = new DefaultWorker((Format.Standard) producer); + } else if (producer instanceof Format.Mp3) { + log.debug("Format.Mp3"); + worker = new Mp3Worker((Format.Mp3) producer); + } + start(); + } + + public void stop() { + if (worker != null) { + worker.stop(); + } + } + + public void exit() { + if (worker != null) { + worker.exit(); + } + } + + protected class DefaultWorker extends Worker { + protected Format.Standard format; + protected SourceDataLine line; + + public DefaultWorker(Format.Standard format) { + this.format = format; + } + + public void activate() throws ActivateException { + AudioFormat audioFormat = format.getAudioFormat(); + try { + if (line == null) { + line = Tool.getSourceDataLine(name, audioFormat); + } + if (!line.isOpen()) { + line.open(); + } + } catch (LineUnavailableException e) { + log.error(e); + throw new ActivateException(); + } + if (!line.isRunning()) { + line.start(); + } + super.activate(); + } + + public void deactivate() throws DeactivateException { + super.deactivate(); + line.flush(); + } + + public void exit() { + super.exit(); + line.close(); + } + + protected void work() { + try { + byte[] buffer = new byte[BUFFER_SIZE]; + int read = producerInputStream.read(buffer, 0, buffer.length); + if (read > 0) { + line.write(buffer, 0, read); + } else { + exit(); + } + } catch (IOException e) { + log.error(e); + exit(); + } + } + } + + protected class Mp3Worker extends Worker { + protected Format.Mp3 format; + protected Player player; + + public Mp3Worker(Format.Mp3 format) { + this.format = format; + } + + public void activate() throws ActivateException { + producer.start(); + super.activate(); + } + + public void deactivate() throws DeactivateException { + super.deactivate(); + producer.stop(); + } + + public void exit() { + super.exit(); + player.close(); + } + + protected void work() { + try { + if (player == null) { + player = new Player(producerInputStream); + sleep(500); + } + player.play(PLAY_FRAMES); + } catch (JavaLayerException e) { + log.error(e); + } + if (player.isComplete()) { + stop(); + } + } + } +} diff --git a/src/sound/SoxBuilder.java b/src/sound/SoxBuilder.java new file mode 100644 index 0000000..9f17c00 --- /dev/null +++ b/src/sound/SoxBuilder.java @@ -0,0 +1,322 @@ +package sound; + +import java.util.HashMap; +import java.util.Map.Entry; + +import javax.sound.sampled.AudioFormat; + +import sound.SoxBuilder.Option.Combine; +import sound.SoxBuilder.Option.Replay; + + +public final class SoxBuilder { + protected static SoxBuilder instance; + protected static HashMap optionMap; + protected static String files; + protected static String effects; + + static { + instance = new SoxBuilder(); + reset(); + } + + public static void reset() { + optionMap = new HashMap(); + files = ""; + effects = ""; + } + + public static SoxBuilder setOption(Option option, String value) { + optionMap.put(option.getCode(), value); + return instance; + } + + public static SoxBuilder setOption(Option option) { + return SoxBuilder.setOption(option, ""); + } + + public static SoxBuilder setOption(Option option, int value) { + return SoxBuilder.setOption(option, String.valueOf(value)); + } + + public static SoxBuilder setOption(Option option, Combine combine) { + return SoxBuilder.setOption(option, combine.getCode()); + } + + public static SoxBuilder setOption(Combine combine) { + return SoxBuilder.setOption(Option.COMBINE, combine); + } + + public static SoxBuilder setOption(Option option, Replay replay) { + return SoxBuilder.setOption(option, replay.toString().toLowerCase()); + } + + public static SoxBuilder setOption(Replay replay) { + return SoxBuilder.setOption(Option.REPLAY, replay); + } + + public static SoxBuilder addFile(File file) { + files = String.format("%s %s", files, file.build()); + return instance; + } + + public static SoxBuilder addEffect(Effect effect) { + effects = String.format("%s %s", effects, effect.build()); + return instance; + } + + public String build() { + String build = "sox"; + for (Entry entry : optionMap.entrySet()) { + String value = entry.getValue(); + if (value.equals("")) { + build = String.format("%s %s", build, entry.getKey()); + } else { + String option = String.format("%s %s", entry.getKey(), value); + build = String.format("%s %s", build, option); + } + } + build = String.format("%s%s%s", build, files, effects); + reset(); + return build; + } + + public enum Environment { + AUDIODRIVER, AUDIODEV + } + + public enum Option { + BUFFER ("--buffer"), // default=8192 + INPUT_BUFFER ("--input-buffer"), + CLOBBER ("--clobber"), + COMBINE ("--combine"), // |Combine| + NO_DITHER ("--no-dither"), // (-D) + EFFECTS_FILE ("--efects-file"), + GUARD ("--guard"), // (-G) + MIX ("-m"), // (--combine mix) + MERGE ("-M"), // (--combine merge) + MAGIC ("--magic"), + MULTI_THREADED ("--multi-threaded"), + SINGLE_THREADED ("--single-threaded"), + NORM ("--norm"), // [=dB-level] + PLAY_RATE_ARG ("--play-rate-arg"), + QUIET ("--no-show-progress"), // (-q) + REPEATABLE ("-R"), + REPLAY ("--replay-gain"), // |Replay| + MULTIPLY ("-T"), // (--combine multiply) + TEMP ("--temp"); + + protected String code; + + private Option(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + + public enum Combine { + CONCATENATE ("concatenate"), + MERGE ("merge"), // (-M) + MIX ("mix"), // (-m) + MIX_POWER ("mix-power"), + MULTIPLY ("multiply"), // (-T) + SEQUENCE ("sequence"); + + protected String code; + + private Combine(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + } + + public enum Replay { + TRACK, ALBUM, OFF + } + } + + public static class File { + protected static File instance; + + protected static HashMap optionMap; + protected static Type type; + + static { + instance = new File(); + reset(); + } + + public static void reset() { + optionMap = new HashMap(); + type = Type.PIPE; + } + + public static File setOption(Option option, String value) { + optionMap.put(option.getCode(), value); + return instance; + } + + public static File setOption(Option option) { + return File.setOption(option, ""); + } + + public static File setOption(Option option, int value) { + return File.setOption(option, String.valueOf(value)); + } + + public static File setOption(Option option, Encoding encoding) { + return File.setOption(option, encoding.getCode()); + } + + public static File setOption(Encoding encoding) { + return File.setOption(Option.ENCODING, encoding); + } + + public static File setOption(Option option, Format format) { + return File.setOption(option, format.toString().toLowerCase()); + } + + public static File setOption( Format format) { + return File.setOption(Option.FORMAT, format); + } + + public static File setOption(Option option, Endian endian) { + return File.setOption(option, endian.toString().toLowerCase()); + } + + public static File setOption(Endian endian) { + return File.setOption(Option.ENDIAN, endian); + } + + public File setOptions(AudioFormat audioFormat) { + setOption(Option.CHANNELS, audioFormat.getChannels()); + setOption(Option.RATE, String.format("%sk", String.valueOf(audioFormat.getSampleRate() / 1000f))); + AudioFormat.Encoding encoding = audioFormat.getEncoding(); + int bits = audioFormat.getSampleSizeInBits(); + if (encoding.equals(AudioFormat.Encoding.ALAW)) { + setOption(Format.AL); + setOption(Encoding.A_LAW); + } else if (encoding.equals(AudioFormat.Encoding.ULAW)) { + setOption(Format.UL); + setOption(Encoding.U_LAW); + } else if (encoding.equals(AudioFormat.Encoding.PCM_SIGNED)) { + setOption(Format.valueOf(String.format("S%d", bits))); + setOption(Encoding.SIGNED_INTEGER); + } else if (encoding.equals(AudioFormat.Encoding.PCM_UNSIGNED)) { + setOption(Format.valueOf(String.format("U%d", bits))); + setOption(Encoding.UNSIGNED_INTEGER); + } + setOption(audioFormat.isBigEndian() ? Endian.BIG : Endian.LITTLE); + return instance; + } + + public static File setType(Type type) { + File.type = type; + return instance; + } + + public String build() { + String build = type.getCode(); + for (Entry entry : optionMap.entrySet()) { + String value = entry.getValue(); + if (value.equals("")) { + build = String.format("%s %s", entry.getKey(), build); + } else { + String option = String.format("%s %s", entry.getKey(), value); + build = String.format("%s %s", option, build); + } + } + reset(); + return build; + } + + public enum Option { + BITS ("--bits"), // (-b) + CHANNELS ("--channels"), // (-c) + ENCODING ("--encoding"), // (-e), |Encoding| + NO_GLOB ("--no-glob"), + RATE ("--rate"), // (-r) + FORMAT ("--type"), // (-t), |Format| + ENDIAN ("--endian"), // (-L, -B, -x), |Endian| + REVERSE_NIBBLES ("--reverse-nibbles"), // (-N) + REVERSE_BITS ("--reverse-bits"), // (-X) + /* Input only */ + IGNORE_LENGTH ("--ignore-length"), + VOLUME ("--volume"), // (-v) + /* Output only */ + ADD_COMMENT ("--add-comment"), + COMMENT ("--comment"), + COMMENT_FILE ("--comment-file"), + COMPRESSION ("--compression"); // -C + + protected String code; + + private Option(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + } + + public enum Encoding { + SIGNED_INTEGER ("signed-integer"), // PCM data stored as signed integers + UNSIGNED_INTEGER ("unsigned-integer"), // PCM data stored as unsigned integers + FLOATING_POINT ("floating-point"), // PCM data stored as single precision (32-bit) or double precision (64-bit) floating-point numbers + A_LAW ("a-lawW"), // International telephony standard for logarithmic encoding to 8 bits per sample (~13-bit PCM) + U_LAW ("u-law"), // North American telephony standard for logarithmic encoding to 8 bits per sample (~14-bit PCM) + MU_LAW ("mu-law"), // alias for u-law (~14-bit PCM) + OKI_ADPCM ("oki-adpcm"), // OKI (VOX, Dialogic or Intel) 4-bit ADPCM (~12-bit PCM) + IMA_ADPCM ("ima-adpcm"), // IMA (DVI) 4-bit ADPCM (~13-bit PCM) + MS_ADPCM ("ms-adpcm"), // Microsoft 4-bit ADPCM (~14-bit PCM) + GSM_FULL_RATE ("gsm-full-rate"); // Several audio formats used for digital wireless telephone calls + + protected String code; + + private Encoding(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + } + + public enum Format { + AIF, AIFC, AIFF, AIFFC, AL, AMB, AMR, ANY, ARL, AU, AVR, BIN, CAF, CDDA, CDR, CVS, CVSD, CVU, DAT, DVMS, EDU, F32, F64, FAP, FLAC, FSSD, GSM, GSRT, HCOM, HTK, IMA, IRCAM, LA, LPC, LPC10, LU, M3U, M4A, MAT, MAT4, MAT5, MAUD, MP2, MP3, MP4, NIST, OGG, PAF, PLS, PRC, PVF, RAW, S16, S24, S32, S8, SD2, SDS, SF, SLN, SMP, SND, SNDR, SNDT, SOU, SOX, SPH, TXW, U16, U24, U32, U8, UL, VMS, VOC, VORBIS, VOX, W64, WAV, WAVPCM, WV, WVE, XA, XI; + } + + public enum Endian { + LITTLE, BIG, SWAP; + } + + public enum Type { + STANDARD ("-"), // -t must be given + PIPE ("-p"), // (--sox-pipe) + DEVICE ("-d"), // (--default-device) + NULL ("-n"); // (--null) + + protected String code; + + private Type(String code) { + this.code = code; + } + + public String getCode() { + return code; + } + } + } + + public class Effect { + public String build() { + return null; + } + } +} diff --git a/src/sound/Stream.java b/src/sound/Stream.java new file mode 100644 index 0000000..14e62a8 --- /dev/null +++ b/src/sound/Stream.java @@ -0,0 +1,195 @@ +package sound; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.net.Socket; +import java.net.URL; + +import mimis.exception.worker.ActivateException; +import mimis.exception.worker.DeactivateException; +import mimis.worker.Worker; + +import com.Ostermiller.util.CircularByteBuffer; +import com.Ostermiller.util.CircularObjectBuffer; + +public class Stream extends Worker implements Producer, Format.Mp3 { + public static final String HTTP = "http://shoutcast.omroep.nl:8104/"; + public static final int STEP = 80; // in milliseconds + + protected String http; + protected Socket socket; + protected InputStream socketInputStream; + protected OutputStreamWriter socketOutputStreamWriter; + protected GreedyInputStream greedyInputStream; + protected int meta; + protected int rate; + protected int chunk; + protected int untilMeta; + protected CircularByteBuffer audioCircularByteBuffer; + protected CircularObjectBuffer metaCircularObjectBuffer; + protected String metaData; + + public Stream() { + this(HTTP); + } + + public Stream(String http) { + super(true); + this.http = http; + meta = -1; + rate = -1; + audioCircularByteBuffer = new CircularByteBuffer(CircularByteBuffer.INFINITE_SIZE); + metaCircularObjectBuffer = new CircularObjectBuffer(); + } + + protected void connect(URL url) { + try { + /* Open socket communication */ + socket = new Socket(url.getHost(), url.getPort()); + socketInputStream = socket.getInputStream(); + socketOutputStreamWriter = new OutputStreamWriter(socket.getOutputStream()); + + /* Write stream request */ + socketOutputStreamWriter.write("GET " + url.getFile() + " HTTP/1.1\r\n"); + socketOutputStreamWriter.write("Host: " + url.getHost() + "\r\n"); + socketOutputStreamWriter.write("Icy-MetaData: 1\r\n"); + socketOutputStreamWriter.write("Connection: close\r\n"); + socketOutputStreamWriter.write("\r\n"); + socketOutputStreamWriter.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + protected void activate() throws ActivateException { + try { + /* Initialize connection */ + URL url = new URL(http); + + /* Parse headers */ + connect(url); + InputStreamReader inputStreamReader = new InputStreamReader(socketInputStream); + StringBuffer stringBuffer = new StringBuffer(); + char character; + int skip = 0; + while ((character = (char) inputStreamReader.read()) > 0) { + ++skip; + if (character == '\n') { + /* Fetch relevant headers */ + String line = stringBuffer.toString().trim(); + if (line.startsWith("icy-metaint")) { + meta = Integer.valueOf(line.substring(line.indexOf(":") + 1).trim()); + } else if (line.startsWith("icy-br")) { + rate = Integer.valueOf(line.substring(line.indexOf(":") + 1).trim()); + } else if (line.equals("")) { + break; + } + stringBuffer = new StringBuffer(); + } else { + stringBuffer.append(character); + } + } + inputStreamReader.close(); + + /* Reconnect to bypass pre-buffering problems */ + connect(url); + socketInputStream = socket.getInputStream(); + socketInputStream.skip(skip); + } catch (IOException e) { + e.printStackTrace(); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + /* Calculate streaming parameters */ + untilMeta = meta; + chunk = STEP * rate / 8; + super.activate(); + } + + public void deactivate() throws DeactivateException { + super.deactivate(); + audioCircularByteBuffer.clear(); + metaCircularObjectBuffer.clear(); + try { + greedyInputStream.clear(); + } catch (IOException e) { + log.error(e); + throw new DeactivateException(); + } + } + + protected void work() { + int left = chunk; + + /* Handle media at appropriate times */ + while (meta > 0 && left >= untilMeta) { + stream(untilMeta); + meta(); + left -= untilMeta; + untilMeta = meta; + } + + /* Stream at fixed rate */ + stream(left); + untilMeta -= left; + sleep(STEP); + + } + + protected void stream(int length) { + try { + byte[] bytes = new byte[length]; + int read = 0; + while (length > 0 && (read = socketInputStream.read(bytes)) > 0) { + length -= read; + audioCircularByteBuffer.getOutputStream().write(bytes); + } + } catch (IOException e) { + log.error(e); + stop(); + } + } + + protected void meta() { + try { + /* Retrieve data length */ + byte[] data = new byte[1]; + socketInputStream.read(data); + int length = 16 * data[0]; + data = new byte[length]; + socketInputStream.read(data); + + /* Check for new data */ + String newMetaData = new String(data); + if (!newMetaData.isEmpty() && !newMetaData.equals(metaData)) { + metaData = newMetaData; + metaCircularObjectBuffer.write(new String(data)); + log.debug("data: " + metaData); + } + return; + } catch (IOException e) { + log.error(e); + } catch (IllegalStateException e) { + log.error(e); + } catch (InterruptedException e) { + log.error(e); + } + stop(); + } + + public InputStream getInputStream() { + greedyInputStream = new GreedyInputStream(audioCircularByteBuffer.getInputStream()); + return greedyInputStream; + } + + public CircularObjectBuffer getMetaBufferStream() { + return metaCircularObjectBuffer; + } + + public int getRate() { + return rate; + } +} diff --git a/src/sound/Target.java b/src/sound/Target.java new file mode 100644 index 0000000..1a9f6c7 --- /dev/null +++ b/src/sound/Target.java @@ -0,0 +1,91 @@ +package sound; + +import java.io.IOException; +import java.io.InputStream; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.TargetDataLine; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class Target implements Producer, Format.Standard { + protected Log log = LogFactory.getLog(getClass()); + + protected Standard format; + + protected TargetDataLine line; + protected InputStream targetInputStream; + + protected AudioFormat audioFormat; + + public Target(String name) throws LineUnavailableException { + log.debug(String.format("Target \"%s\" without format", name)); + line = Tool.getTargetDataLine(name); + audioFormat = line.getFormat(); + targetInputStream = new TargetInputStream(); + } + + public Target(String name, AudioFormat audioFormat) throws LineUnavailableException { + log.debug(String.format("Target \"%s\" with format: %s", name, audioFormat)); + this.audioFormat = audioFormat; + line = Tool.getTargetDataLine(name, audioFormat); + targetInputStream = new TargetInputStream(); + } + + public AudioFormat getAudioFormat() { + return audioFormat; + } + + public InputStream getInputStream() { + return targetInputStream; + } + + public class TargetInputStream extends InputStream { + protected boolean open; + + public TargetInputStream() { + open = false; + } + + public int read() throws IOException { + start(); + byte[] buffer = new byte[1]; + line.read(buffer, 0, 1); + return (int) buffer[0]; + } + + public int read(byte[] buffer, int offset, int length) { + start(); + line.read(buffer, offset, length); + return length; + } + + public int available() { + start(); + return line.available(); + } + } + + public void start() { + if (!line.isOpen()) { + try { + line.open(); + } catch (LineUnavailableException e) { + log.error(e); + } + } + if (!line.isRunning()) { + line.start(); + } + } + + public void stop() { + line.flush(); + } + + public void exit() { + line.close(); + } +} diff --git a/src/sound/Test.java b/src/sound/Test.java new file mode 100644 index 0000000..4e47b1b --- /dev/null +++ b/src/sound/Test.java @@ -0,0 +1,27 @@ +package sound; + +import javax.sound.sampled.AudioFormat; + +public class Test { + public static void main(String[] args) { + AudioFormat audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 48000f, 16, 2, 4, 48000f, true); + try { + Producer p1 = new Target("Line-In (Creative SB X-Fi)"); + Producer p2 = new Target("Line 1 (Virtual Audio Cable)", audioFormat); + Producer p3 = new Stream("http://ics2gss.omroep.nl:80/3fm-bb-mp3"); + Consumer c1 = new Source("Java Sound Audio Engine"); + Consumer c2 = new Port("Speakers (Creative SB X-Fi)"); + + c2.start(p3); + + /*while (true) { + Thread.sleep(3000); + c2.stop(); + Thread.sleep(1000); + c2.start(); + }*/ + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/sound/Tool.java b/src/sound/Tool.java new file mode 100644 index 0000000..cb277a8 --- /dev/null +++ b/src/sound/Tool.java @@ -0,0 +1,140 @@ +package sound; + +import java.util.ArrayList; +import java.util.HashMap; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.DataLine; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.Mixer; +import javax.sound.sampled.Port; +import javax.sound.sampled.Port.Info; +import javax.sound.sampled.SourceDataLine; +import javax.sound.sampled.TargetDataLine; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +public class Tool { + protected static Log log = LogFactory.getLog(Tool.class); + + protected static HashMap> targetMap; + protected static HashMap> sourceMap; + protected static ArrayList portList; + + protected static ArrayList targetList; + protected static ArrayList sourceList; + + static { + Tool tool = new Tool(); + + targetMap = new HashMap>(); + sourceMap = new HashMap>(); + targetList = new ArrayList(); + sourceList = new ArrayList(); + portList = new ArrayList(); + + for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) { + String name = mixerInfo.getName(); + Mixer mixer = AudioSystem.getMixer(mixerInfo); + + for (Line.Info lineInfo : mixer.getSourceLineInfo()) { + String lineClassName = lineInfo.getLineClass().getName(); + if (lineClassName.equals("javax.sound.sampled.SourceDataLine")) { + if (mixer.isLineSupported(lineInfo)) { + log.debug(" " + name); + sourceMap.put(name, tool.new Device(mixer, lineInfo)); + } + } + } + for (Line.Info lineInfo : mixer.getTargetLineInfo()) { + String lineClassName = lineInfo.getLineClass().getName(); + if (lineClassName.equals("javax.sound.sampled.TargetDataLine")) { + if (mixer.isLineSupported(lineInfo)) { + log.debug(" " + name); + targetMap.put(name, tool.new Device(mixer, lineInfo)); + } + } else if (lineClassName.equals("javax.sound.sampled.Port")) { + name = name.substring(5); + try { + Port port = (Port) mixer.getLine(lineInfo); + Port.Info portInfo = (Info) port.getLineInfo(); + if (!targetMap.containsKey(name) || portInfo.equals(Port.Info.LINE_OUT) || portInfo.equals(Port.Info.SPEAKER)) { + log.debug(" " + name); + portList.add(name); + } + } catch (LineUnavailableException e) { + log.error(e); + } + } + } + } + } + + public static String[] getTargets() { + return targetMap.keySet().toArray(new String[0]); + } + + public static String[] getSources() { + return sourceMap.keySet().toArray(new String[0]); + } + + public static String[] getPorts() { + return portList.toArray(new String[0]); + } + + public static TargetDataLine getTargetDataLine(String name) throws LineUnavailableException { + if (targetMap.containsKey(name)) { + return targetMap.get(name).getLine(); + } else { + throw new LineUnavailableException(); + } + } + + public static TargetDataLine getTargetDataLine(String name, AudioFormat audioFormat) throws LineUnavailableException { + if (targetMap.containsKey(name)) { + return targetMap.get(name).getLine(audioFormat); + } else { + throw new LineUnavailableException(); + } + } + + public static SourceDataLine getSourceDataLine(String name) throws LineUnavailableException { + if (sourceMap.containsKey(name)) { + return sourceMap.get(name).getLine(); + } else { + throw new LineUnavailableException(); + } + } + + public static SourceDataLine getSourceDataLine(String name, AudioFormat audioFormat) throws LineUnavailableException { + if (sourceMap.containsKey(name)) { + return sourceMap.get(name).getLine(audioFormat); + } else { + throw new LineUnavailableException(); + } + } + + public class Device { + protected Mixer mixer; + protected Line.Info lineInfo; + + public Device(Mixer mixer, Line.Info lineInfo) { + this.mixer = mixer; + this.lineInfo = lineInfo; + } + + @SuppressWarnings("unchecked") + public T getLine() throws LineUnavailableException { + return (T) mixer.getLine(lineInfo); + } + + @SuppressWarnings("unchecked") + public T getLine(AudioFormat audioFormat) throws LineUnavailableException { + DataLine.Info dataLineInfo = new DataLine.Info(lineInfo.getLineClass(), audioFormat); + return (T) mixer.getLine(dataLineInfo); + } + } +} diff --git a/src/sound/Transducer.java b/src/sound/Transducer.java new file mode 100644 index 0000000..2ee55f5 --- /dev/null +++ b/src/sound/Transducer.java @@ -0,0 +1,43 @@ +package sound; + +import java.io.InputStream; + +public class Transducer implements Consumer, Producer { + public int rate; + + public Transducer(Producer producer) { + //setProducer(producer); + } + + public int getRate() { + return rate; + } + + public InputStream getInputStream() { + // TODO Auto-generated method stub + return null; + } + + public void start(Producer producer) { + // TODO Auto-generated method stub + + } + + @Override + public void start() { + // TODO Auto-generated method stub + + } + + @Override + public void stop() { + // TODO Auto-generated method stub + + } + + @Override + public void exit() { + // TODO Auto-generated method stub + + } +} diff --git a/src/test/SoundAudit.java b/src/test/SoundAudit.java new file mode 100644 index 0000000..ee44f6a --- /dev/null +++ b/src/test/SoundAudit.java @@ -0,0 +1,55 @@ +package test; + +import javax.sound.sampled.*; +public class SoundAudit { + public static void main(String[] args) { try { + System.out.println("OS: "+System.getProperty("os.name")+" "+ + System.getProperty("os.version")+"/"+ + System.getProperty("os.arch")+"\nJava: "+ + System.getProperty("java.version")+" ("+ + System.getProperty("java.vendor")+")\n"); + for (Mixer.Info thisMixerInfo : AudioSystem.getMixerInfo()) { + System.out.println("Mixer: "+thisMixerInfo.getDescription()+ + " ["+thisMixerInfo.getName()+"]"); + Mixer thisMixer = AudioSystem.getMixer(thisMixerInfo); + for (Line.Info thisLineInfo:thisMixer.getSourceLineInfo()) { + if (thisLineInfo.getLineClass().getName().equals( + "javax.sound.sampled.Port")) { + Line thisLine = thisMixer.getLine(thisLineInfo); + thisLine.open(); + System.out.println(" Source Port: " + +thisLineInfo.toString()); + for (Control thisControl : thisLine.getControls()) { + System.out.println(AnalyzeControl(thisControl));} + thisLine.close();}} + for (Line.Info thisLineInfo:thisMixer.getTargetLineInfo()) { + if (thisLineInfo.getLineClass().getName().equals( + "javax.sound.sampled.Port")) { + Line thisLine = thisMixer.getLine(thisLineInfo); + thisLine.open(); + System.out.println(" Target Port: " + +thisLineInfo.toString()); + for (Control thisControl : thisLine.getControls()) { + System.out.println(AnalyzeControl(thisControl));} + thisLine.close();}}} + } catch (Exception e) {e.printStackTrace();}} + public static String AnalyzeControl(Control thisControl) { + String type = thisControl.getType().toString(); + if (thisControl instanceof BooleanControl) { + return " Control: "+type+" (boolean)"; } + if (thisControl instanceof CompoundControl) { + System.out.println(" Control: "+type+ + " (compound - values below)"); + String toReturn = ""; + for (Control children: + ((CompoundControl)thisControl).getMemberControls()) { + toReturn+=" "+AnalyzeControl(children)+"\n";} + return toReturn.substring(0, toReturn.length()-1);} + if (thisControl instanceof EnumControl) { + return " Control:"+type+" (enum: "+thisControl.toString()+")";} + if (thisControl instanceof FloatControl) { + return " Control: "+type+" (float: from "+ + ((FloatControl) thisControl).getMinimum()+" to "+ + ((FloatControl) thisControl).getMaximum()+")";} + return " Control: unknown type";} +} diff --git a/src/test/lines/Main.java b/src/test/lines/Main.java new file mode 100644 index 0000000..09b80bf --- /dev/null +++ b/src/test/lines/Main.java @@ -0,0 +1,29 @@ +package test.lines; + +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.Mixer; +import javax.sound.sampled.TargetDataLine; + +public class Main { + public static void main(String[] args) { + System.out.println(System.getProperty("javax.sound.sampled.SourceDataLine")); + + new AudioFormat(44100, 16, 2, true, false); + + for (Mixer.Info mixerInfo : AudioSystem.getMixerInfo()) { + Mixer mixer = AudioSystem.getMixer(mixerInfo); + for (Line.Info lineInfo : mixer.getTargetLineInfo()) { + try { + Line line = mixer.getLine(lineInfo); + if (mixer.isLineSupported(lineInfo) && line instanceof TargetDataLine) { + new TargetLine(mixer, (TargetDataLine) line); + } + } catch (LineUnavailableException e) {} + } + + } + } +} diff --git a/src/test/lines/SourceLine.java b/src/test/lines/SourceLine.java new file mode 100644 index 0000000..ede5969 --- /dev/null +++ b/src/test/lines/SourceLine.java @@ -0,0 +1,19 @@ +package test.lines; + +import javax.sound.sampled.Mixer; +import javax.sound.sampled.SourceDataLine; + +public class SourceLine { + //private Mixer mixer; + private SourceDataLine line; + + public SourceLine(Mixer mixer, SourceDataLine line) { + //this.mixer = mixer; + this.line = line; + System.out.println("SOURCE " + mixer.getMixerInfo().getName() + " || " + line.getLineInfo()); + } + + public int write(byte[] bytes, int offset, int length) { + return line.write(bytes, offset, length); + } +} diff --git a/src/test/lines/TargetLine.java b/src/test/lines/TargetLine.java new file mode 100644 index 0000000..e1e7823 --- /dev/null +++ b/src/test/lines/TargetLine.java @@ -0,0 +1,19 @@ +package test.lines; + +import javax.sound.sampled.Mixer; +import javax.sound.sampled.TargetDataLine; + +public class TargetLine { + //private Mixer mixer; + private TargetDataLine line; + + public TargetLine(Mixer mixer, TargetDataLine line) { + //this.mixer = mixer; + this.line = line; + System.out.println("TARGET " + mixer.getMixerInfo().getName() + " || " + line.getLineInfo()); + } + + public int read(byte[] bytes, int offset, int length) { + return line.read(bytes, offset, length); + } +} diff --git a/stream.bat b/stream.bat new file mode 100644 index 0000000..21afc51 --- /dev/null +++ b/stream.bat @@ -0,0 +1 @@ +java -classpath bin udp.StreamClient 234.5.6.7 4567 \ No newline at end of file diff --git a/stream.exe b/stream.exe new file mode 100644 index 0000000..36bf506 Binary files /dev/null and b/stream.exe differ diff --git a/stream.jar b/stream.jar new file mode 100644 index 0000000..1d8bbdc Binary files /dev/null and b/stream.jar differ diff --git a/wget.exe b/wget.exe new file mode 100644 index 0000000..f2a11c1 Binary files /dev/null and b/wget.exe differ diff --git a/zlib1.dll b/zlib1.dll new file mode 100644 index 0000000..89c8002 Binary files /dev/null and b/zlib1.dll differ