jspeex/ 0000755 0001750 0001750 00000000000 10245633664 012323 5 ustar twerner twerner jspeex/dist/ 0000755 0001750 0001750 00000000000 10245633322 013255 5 ustar twerner twerner jspeex/lib/ 0000755 0001750 0001750 00000000000 10245633322 013060 5 ustar twerner twerner jspeex/include/ 0000755 0001750 0001750 00000000000 10245633324 013737 5 ustar twerner twerner jspeex/include/metainf/ 0000755 0001750 0001750 00000000000 10245633322 015360 5 ustar twerner twerner jspeex/include/metainf/services/ 0000755 0001750 0001750 00000000000 10245633322 017203 5 ustar twerner twerner jspeex/include/metainf/services/javax.sound.sampled.spi.AudioFileReader 0000644 0001750 0001750 00000000133 10061610410 026551 0 ustar twerner twerner # Providers of Speex sound file-reading services
org.xiph.speex.spi.SpeexAudioFileReader
jspeex/include/metainf/services/javax.sound.sampled.spi.AudioFileWriter 0000644 0001750 0001750 00000000133 10061610410 026623 0 ustar twerner twerner # Providers of Speex sound file-writing services
org.xiph.speex.spi.SpeexAudioFileWriter
jspeex/include/metainf/services/javax.sound.sampled.spi.FormatConversionProvider 0000644 0001750 0001750 00000000142 10061610410 030576 0 ustar twerner twerner # Providers of Speex sound conversion services
org.xiph.speex.spi.SpeexFormatConvertionProvider
jspeex/src/ 0000755 0001750 0001750 00000000000 10245633324 013103 5 ustar twerner twerner jspeex/src/java/ 0000755 0001750 0001750 00000000000 10245633324 014024 5 ustar twerner twerner jspeex/src/java/org/ 0000755 0001750 0001750 00000000000 10245633324 014613 5 ustar twerner twerner jspeex/src/java/org/xiph/ 0000755 0001750 0001750 00000000000 10245633324 015563 5 ustar twerner twerner jspeex/src/java/org/xiph/speex/ 0000755 0001750 0001750 00000000000 10245633324 016707 5 ustar twerner twerner jspeex/src/java/org/xiph/speex/ant/ 0000755 0001750 0001750 00000000000 10245633324 017471 5 ustar twerner twerner jspeex/src/java/org/xiph/speex/ant/JSpeexDecoderTask.java 0000644 0001750 0001750 00000062360 10135761046 023653 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2004 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: JSpeexDecoderTask.java *
* *
* Author: Marc GIMPEL *
* *
* Date: Jun 4, 2004 *
* *
******************************************************************************/
/* $Id: JSpeexDecoderTask.java,v 1.2 2004/10/21 16:21:57 mgimpel Exp $ */
package org.xiph.speex.ant;
import java.io.DataInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Random;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.xiph.speex.AudioFileWriter;
import org.xiph.speex.NbEncoder;
import org.xiph.speex.OggCrc;
import org.xiph.speex.PcmWaveWriter;
import org.xiph.speex.RawWriter;
import org.xiph.speex.SbEncoder;
import org.xiph.speex.SpeexDecoder;
/**
* Ant Task
to Decode an audio file from Speex to PCM Wave.
* Here is an usage example:
*
** * @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com) * @version $Revision: 1.2 $ */ public class JSpeexDecoderTask extends Task { /** Version of the Speex Encoder */ public static final String VERSION = "Java Speex Decoder Task v0.9.4 ($Revision: 1.2 $)"; /** Copyright display String */ public static final String COPYRIGHT = "Copyright (C) 2002-2004 Wimba S.A."; /** Print level for messages : Print debug information */ public static final int DEBUG = 0; /** Print level for messages : Print basic information */ public static final int INFO = 1; /** Print level for messages : Print only warnings and errors */ public static final int WARN = 2; /** Print level for messages : Print only errors */ public static final int ERROR = 3; /** File format for input or output audio file: Raw */ public static final int FILE_FORMAT_RAW = 0; /** File format for input or output audio file: Ogg */ public static final int FILE_FORMAT_OGG = 1; /** File format for input or output audio file: Wave */ public static final int FILE_FORMAT_WAVE = 2; /** Random number generator for packet loss simulation. */ protected static Random random = new Random(); /** Speex Decoder */ protected SpeexDecoder speexDecoder; /** Source file to decode */ private File srcFile; /** List of source files to decode */ private final Vector srcFileset = new Vector(); /** Destination file of decoded audio */ private File destFile; /** Directory to place destination files */ private File destDir; /** */ private boolean failOnError = true; /** Print level for messages */ private int printlevel = INFO; /** Tells the task to suppress all but the most important output */ private boolean quiet; /** Tells the task to output as much information as possible */ private boolean verbose; /** Defines File format for input audio file (Raw, Ogg or Wave). */ private int srcFormat; /** Defines File format for output audio file (Raw or Wave). */ private int destFormat; /** Defines whether or not the perceptual enhancement is used. */ private boolean enhanced = true; /** If input is raw, defines the decoder mode (0=NB, 1=WB and 2-UWB). */ private int mode = 0; /** If input is raw, defines the quality setting used by the encoder. */ private int quality = 8; /** If input is raw, defines the number of frmaes per packet. */ private int nframes = 1; /** If input is raw, defines the sample rate of the audio. */ private int sampleRate = -1; /** */ private float vbr_quality = -1; /** */ private boolean vbr = false; /** If input is raw, defines th number of channels (1=mono, 2=stereo). */ private int channels = 1; /** The percentage of packets to lose in the packet loss simulation. */ private int loss = 0; //------------------------------------------------------------------------- // Ant Task //------------------------------------------------------------------------- /** * The method executing the task. * @throws BuildException */ public void execute() throws BuildException { if (srcFile == null && srcFileset.size() == 0) { throw new BuildException("There must be a file attribute or a fileset child element"); } boolean hadError = false; if (srcFile != null) { if (destFile == null) { destFile = buildDestFile(srcFile); } try { setupTask(srcFile, destFile); decode(srcFile, destFile); } catch (IOException e) { log(e.getMessage()); } } for (int i=0; i* * ** ** **
srcfile
attribute.
* @param file the attribute value converted to a File.
*/
public void setSrcfile(final File file)
{
this.srcFile = file;
}
/**
* Handles the destfile
attribute.
* @param file the attribute value converted to a File.
*/
public void setDestfile(final File file)
{
this.destFile = file;
}
/**
* Handles the destdir
attribute.
* @param dir the attribute value converted to a File.
*/
public void setDestdir(final File dir)
{
this.destDir = dir;
}
/**
* Handles the failonerror
attribute.
* @param failOnError the attribute value converted to a boolean.
*/
public void setFailonerror(final boolean failOnError)
{
this.failOnError = failOnError;
}
/**
* Handles the quiet
attribute.
* @param quiet the attribute value converted to a boolean.
*/
public void setQuiet(final boolean quiet)
{
this.quiet = quiet;
this.printlevel = WARN;
}
/**
* Handles the verbose
attribute.
* @param verbose the attribute value converted to a boolean.
*/
public void setVerbose(final boolean verbose)
{
this.verbose = verbose;
this.printlevel = DEBUG;
}
/**
* Handles the enhanced
attribute.
* @param enhanced the attribute value converted to a boolean.
*/
public void setEnhanced(final boolean enhanced)
{
this.enhanced = enhanced;
}
//-------------------------------------------------------------------------
// Decoder
//-------------------------------------------------------------------------
/**
* Prints the version.
*/
public void version()
{
log(VERSION);
log("using " + SpeexDecoder.VERSION);
log(COPYRIGHT);
}
/**
* Decodes a spx file to wave.
* @param srcPath the Speex encoded source file.
* @param destPath the destination file.
* @exception IOException
*/
public void decode(final File srcPath, final File destPath)
throws IOException
{
byte[] header = new byte[2048];
byte[] payload = new byte[65536];
byte[] decdat = new byte[44100*2*2];
final int WAV_HEADERSIZE = 8;
final short WAVE_FORMAT_SPEEX = (short) 0xa109;
final String RIFF = "RIFF";
final String WAVE = "WAVE";
final String FORMAT = "fmt ";
final String DATA = "data";
final int OGG_HEADERSIZE = 27;
final int OGG_SEGOFFSET = 26;
final String OGGID = "OggS";
int segments=0;
int curseg=0;
int bodybytes=0;
int decsize=0;
int packetNo=0;
// Display info
if (printlevel <= INFO) version();
if (printlevel <= DEBUG) log("");
if (printlevel <= DEBUG) log("Input File: " + srcPath);
// construct a new decoder
speexDecoder = new SpeexDecoder();
// open the input stream
DataInputStream dis = new DataInputStream(new FileInputStream(srcPath));
AudioFileWriter writer = null;
int origchksum;
int chksum;
try {
// read until we get to EOF
while (true) {
if (srcFormat == FILE_FORMAT_OGG) {
// read the OGG header
dis.readFully(header, 0, OGG_HEADERSIZE);
origchksum = readInt(header, 22);
header[22] = 0;
header[23] = 0;
header[24] = 0;
header[25] = 0;
chksum=OggCrc.checksum(0, header, 0, OGG_HEADERSIZE);
// make sure its a OGG header
if (!OGGID.equals(new String(header, 0, 4))) {
log("missing ogg id!");
return;
}
/* how many segments are there? */
segments = header[OGG_SEGOFFSET] & 0xFF;
dis.readFully(header, OGG_HEADERSIZE, segments);
chksum=OggCrc.checksum(chksum, header, OGG_HEADERSIZE, segments);
/* decode each segment, writing output to wav */
for (curseg=0; curseg < segments; curseg++) {
/* get the number of bytes in the segment */
bodybytes = header[OGG_HEADERSIZE+curseg] & 0xFF;
if (bodybytes==255) {
log("sorry, don't handle 255 sizes!");
return;
}
dis.readFully(payload, 0, bodybytes);
chksum=OggCrc.checksum(chksum, payload, 0, bodybytes);
/* decode the segment */
/* if first packet, read the Speex header */
if (packetNo == 0) {
if (readSpeexHeader(payload, 0, bodybytes)) {
if (printlevel <= DEBUG) {
log("File Format: Ogg Speex");
log("Sample Rate: " + sampleRate);
log("Channels: " + channels);
log("Encoder mode: " + (mode==0 ? "Narrowband" : (mode==1 ? "Wideband" : "UltraWideband")));
log("Frames per packet: " + nframes);
}
/* once Speex header read, initialize the wave writer with output format */
if (destFormat == FILE_FORMAT_WAVE) {
writer = new PcmWaveWriter(speexDecoder.getSampleRate(),
speexDecoder.getChannels());
if (printlevel <= DEBUG) {
log("");
log("Output File: " + destPath);
log("File Format: PCM Wave");
log("Perceptual Enhancement: " + enhanced);
}
}
else {
writer = new RawWriter();
if (printlevel <= DEBUG) {
log("");
log("Output File: " + destPath);
log("File Format: Raw Audio");
log("Perceptual Enhancement: " + enhanced);
}
}
writer.open(destPath);
writer.writeHeader(null);
packetNo++;
}
else {
packetNo = 0;
}
}
else if (packetNo == 1) { // Ogg Comment packet
packetNo++;
}
else {
if (loss>0 && random.nextInt(100)* 0 - 7: speex_string: "Speex " * 8 - 27: speex_version: "speex-1.0" * 28 - 31: speex_version_id: 1 * 32 - 35: header_size: 80 * 36 - 39: rate * 40 - 43: mode: 0=narrowband, 1=wb, 2=uwb * 44 - 47: mode_bitstream_version: 4 * 48 - 51: nb_channels * 52 - 55: bitrate: -1 * 56 - 59: frame_size: 160 * 60 - 63: vbr * 64 - 67: frames_per_packet * 68 - 71: extra_headers: 0 * 72 - 75: reserved1 * 76 - 79: reserved2 ** @param packet * @param offset * @param bytes * @return true if the Speex header was successfully parsed, false otherwise. */ private boolean readSpeexHeader(final byte[] packet, final int offset, final int bytes) { if (bytes!=80) { log("Oooops"); return false; } if (!"Speex ".equals(new String(packet, offset, 8))) { return false; } mode = packet[40+offset] & 0xFF; sampleRate = readInt(packet, offset+36); channels = readInt(packet, offset+48); nframes = readInt(packet, offset+64); return speexDecoder.init(mode, sampleRate, channels, enhanced); } /** * Converts Little Endian (Windows) bytes to an int (Java uses Big Endian). * @param data the data to read. * @param offset the offset from which to start reading. * @return the integer value of the reassembled bytes. */ protected static int readInt(final byte[] data, final int offset) { return (data[offset] & 0xff) | ((data[offset+1] & 0xff) << 8) | ((data[offset+2] & 0xff) << 16) | (data[offset+3] << 24); // no 0xff on the last one to keep the sign } /** * Converts Little Endian (Windows) bytes to an short (Java uses Big Endian). * @param data the data to read. * @param offset the offset from which to start reading. * @return the integer value of the reassembled bytes. */ protected static int readShort(final byte[] data, final int offset) { return (data[offset] & 0xff) | (data[offset+1] << 8); // no 0xff on the last one to keep the sign } } jspeex/src/java/org/xiph/speex/ant/JSpeexEncoderTask.java 0000644 0001750 0001750 00000051362 10135761046 023665 0 ustar twerner twerner /****************************************************************************** * * * Copyright (c) 1999-2004 Wimba S.A., All Rights Reserved. * * * * COPYRIGHT: * * This software is the property of Wimba S.A. * * This software is redistributed under the Xiph.org variant of * * the BSD license. * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * - Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * - Neither the name of Wimba, the Xiph.org Foundation nor the names of * * its contributors may be used to endorse or promote products derived * * from this software without specific prior written permission. * * * * WARRANTIES: * * This software is made available by the authors in the hope * * that it will be useful, but without any warranty. * * Wimba S.A. is not liable for any consequence related to the * * use of the provided software. * * * * Class: JSpeexEncoderTask.java * * * * Author: Marc GIMPEL * * * * Date: Jun 4, 2004 * * * ******************************************************************************/ /* $Id: JSpeexEncoderTask.java,v 1.2 2004/10/21 16:21:57 mgimpel Exp $ */ package org.xiph.speex.ant; import java.io.DataInputStream; import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Vector; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet; import org.xiph.speex.AudioFileWriter; import org.xiph.speex.OggSpeexWriter; import org.xiph.speex.PcmWaveWriter; import org.xiph.speex.RawWriter; import org.xiph.speex.SpeexDecoder; import org.xiph.speex.SpeexEncoder; /** * Ant
Task
to Encode an audio file from PCM Wave to Speex.
* Here is an usage example:
* ** * @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com) * @version $Revision: 1.2 $ */ public class JSpeexEncoderTask extends Task { /** Version of the Speex Encoder */ public static final String VERSION = "Java Speex Encoder Task v0.9.4 ($Revision: 1.2 $)"; /** Copyright display String */ public static final String COPYRIGHT = "Copyright (C) 2002-2004 Wimba S.A."; /** Print level for messages : Print debug information */ public static final int DEBUG = 0; /** Print level for messages : Print basic information */ public static final int INFO = 1; /** Print level for messages : Print only warnings and errors */ public static final int WARN = 2; /** Print level for messages : Print only errors */ public static final int ERROR = 3; /** File format for input or output audio file: Raw */ public static final int FILE_FORMAT_RAW = 0; /** File format for input or output audio file: Ogg */ public static final int FILE_FORMAT_OGG = 1; /** File format for input or output audio file: Wave */ public static final int FILE_FORMAT_WAVE = 2; /** Source file to decode */ private File srcFile; /** List of source files to decode */ private final Vector srcFileset = new Vector(); /** Destination file of decoded audio */ private File destFile; /** Directory to place destination files */ private File destDir; /** */ private boolean failOnError = true; /** Print level for messages */ private int printlevel = INFO; /** Tells the task to suppress all but the most important output */ private boolean quiet; /** Tells the task to output as much information as possible */ private boolean verbose; /** Defines File format for input audio file (Raw, Ogg or Wave). */ protected int srcFormat = FILE_FORMAT_OGG; /** Defines File format for output audio file (Raw or Wave). */ protected int destFormat = FILE_FORMAT_WAVE; /** Whether the mode is manualy set or automatically determined. */ protected boolean modeset = false; /** Defines the encoder mode (0=NB, 1=WB and 2-UWB). */ protected int mode = -1; /** Defines the encoder quality setting (integer from 0 to 10). */ protected int quality = 8; /** Defines the encoders algorithmic complexity. */ protected int complexity = 3; /** Defines the number of frames per speex packet. */ protected int nframes = 1; /** Defines the desired bitrate for the encoded audio. */ protected int bitrate = -1; /** Defines the sampling rate of the audio input. */ protected int sampleRate = -1; /** Defines the number of channels of the audio input (1=mono, 2=stereo). */ protected int channels = 1; /** Defines the encoder VBR quality setting (float from 0 to 10). */ protected float vbr_quality = -1; /** Defines whether or not to use VBR (Variable Bit Rate). */ protected boolean vbr = false; /** Defines whether or not to use VAD (Voice Activity Detection). */ protected boolean vad = false; /** Defines whether or not to use DTX (Discontinuous Transmission). */ protected boolean dtx = false; //------------------------------------------------------------------------- // Ant Task //------------------------------------------------------------------------- /** * The method executing the task. * @throws BuildException */ public void execute() throws BuildException { if (srcFile == null && srcFileset.size() == 0) { throw new BuildException("There must be a file attribute or a fileset child element"); } boolean hadError = false; if (srcFile != null) { if (destFile == null) { destFile = buildDestFile(srcFile); } try { setupTask(srcFile, destFile); encode(srcFile, destFile); } catch (IOException e) { log(e.getMessage()); } } for (int i=0; i* * ** ** **
srcfile
attribute.
* @param file the attribute value converted to a File.
*/
public void setSrcfile(final File file)
{
this.srcFile = file;
}
/**
* Handles the destfile
attribute.
* @param file the attribute value converted to a File.
*/
public void setDestfile(final File file)
{
this.destFile = file;
}
/**
* Handles the destdir
attribute.
* @param dir the attribute value converted to a File.
*/
public void setDestdir(final File dir)
{
this.destDir = dir;
}
/**
* Handles the failonerror
attribute.
* @param failOnError the attribute value converted to a boolean.
*/
public void setFailonerror(final boolean failOnError)
{
this.failOnError = failOnError;
}
/**
* Handles the quiet
attribute.
* @param quiet the attribute value converted to a boolean.
*/
public void setQuiet(final boolean quiet)
{
this.quiet = quiet;
this.printlevel = WARN;
}
/**
* Handles the verbose
attribute.
* @param verbose the attribute value converted to a boolean.
*/
public void setVerbose(final boolean verbose)
{
this.verbose = verbose;
this.printlevel = DEBUG;
}
/**
* Handles the quality
attribute.
* @param quality the attribute value converted to a float.
*/
public void setQuality(float quality)
{
if (quality < 0)
quality = 0;
if (quality > 10)
quality = 10;
this.quality = (int) quality;
this.vbr_quality = quality;
}
/**
* Handles the complexity
attribute.
* @param complexity the attribute value converted to an integer.
*/
public void setComplexity(final int complexity)
{
this.complexity = complexity;
}
/**
* Handles the nframes
attribute.
* @param nframes the attribute value converted to an integer.
*/
public void setNframes(final int nframes)
{
this.nframes = nframes;
}
/**
* Handles the vbr
attribute.
* @param vbr the attribute value converted to a boolean.
*/
public void setVbr(final boolean vbr)
{
this.vbr = vbr;
}
/**
* Handles the vad
attribute.
* @param vad the attribute value converted to a boolean.
*/
public void setVad(final boolean vad)
{
this.vad = vad;
}
/**
* Handles the dtx
attribute.
* @param dtx the attribute value converted to a boolean.
*/
public void setDtx(final boolean dtx)
{
this.dtx = dtx;
}
/**
* Handles the mode
attribute.
* @param mode the attribute value converted to a String.
*/
public void setMode(final String mode)
{
modeset = true;
if ("ultrawideband".equalsIgnoreCase(mode) ||
"uwb".equalsIgnoreCase(mode) ||
"2".equals(mode))
this.mode = 2;
else if ("wideband".equalsIgnoreCase(mode) ||
"wb".equalsIgnoreCase(mode) ||
"1".equals(mode))
this.mode = 1;
else if ("narrowband".equalsIgnoreCase(mode) ||
"nb".equalsIgnoreCase(mode) ||
"0".equals(mode))
this.mode = 0;
else {
this.mode = -1;
}
}
//-------------------------------------------------------------------------
// Encoder
//-------------------------------------------------------------------------
/**
* Prints the version.
*/
public void version()
{
log(VERSION);
log("using " + SpeexDecoder.VERSION);
log(COPYRIGHT);
}
/**
* Encodes a PCM file to Speex.
* @param srcPath
* @param destPath
* @exception IOException
*/
public void encode(final File srcPath, final File destPath)
throws IOException
{
byte[] temp = new byte[2560]; // stereo UWB requires one to read 2560b
final int HEADERSIZE = 8;
final String RIFF = "RIFF";
final String WAVE = "WAVE";
final String FORMAT = "fmt ";
final String DATA = "data";
final int WAVE_FORMAT_PCM = 0x0001;
// Display info
if (printlevel <= INFO) version();
if (printlevel <= DEBUG) System.out.println("");
if (printlevel <= DEBUG) System.out.println("Input File: " + srcPath);
// Open the input stream
DataInputStream dis = new DataInputStream(new FileInputStream(srcPath));
// Prepare input stream
if (srcFormat == FILE_FORMAT_WAVE) {
// read the WAVE header
dis.readFully(temp, 0, HEADERSIZE+4);
// make sure its a WAVE header
if (!RIFF.equals(new String(temp, 0, 4)) &&
!WAVE.equals(new String(temp, 8, 4))) {
System.err.println("Not a WAVE file");
return;
}
// Read other header chunks
dis.readFully(temp, 0, HEADERSIZE);
String chunk = new String(temp, 0, 4);
int size = readInt(temp, 4);
while (!chunk.equals(DATA)) {
dis.readFully(temp, 0, size);
if (chunk.equals(FORMAT)) {
/*
typedef struct waveformat_extended_tag {
WORD wFormatTag; // format type
WORD nChannels; // number of channels (i.e. mono, stereo...)
DWORD nSamplesPerSec; // sample rate
DWORD nAvgBytesPerSec; // for buffer estimation
WORD nBlockAlign; // block size of data
WORD wBitsPerSample; // Number of bits per sample of mono data
WORD cbSize; // The count in bytes of the extra size
} WAVEFORMATEX;
*/
if (readShort(temp, 0) != WAVE_FORMAT_PCM) {
System.err.println("Not a PCM file");
return;
}
channels = readShort(temp, 2);
sampleRate = readInt(temp, 4);
if (readShort(temp, 14) != 16) {
System.err.println("Not a 16 bit file " + readShort(temp, 18));
return;
}
// Display audio info
if (printlevel <= DEBUG) {
System.out.println("File Format: PCM wave");
System.out.println("Sample Rate: " + sampleRate);
System.out.println("Channels: " + channels);
}
}
dis.readFully(temp, 0, HEADERSIZE);
chunk = new String(temp, 0, 4);
size = readInt(temp, 4);
}
if (printlevel <= DEBUG) System.out.println("Data size: " + size);
}
else {
if (sampleRate < 0) {
switch (mode) {
case 0:
sampleRate = 8000;
break;
case 1:
sampleRate = 16000;
break;
case 2:
sampleRate = 32000;
break;
default:
sampleRate = 8000;
break;
}
}
// Display audio info
if (printlevel <= DEBUG) {
System.out.println("File format: Raw audio");
System.out.println("Sample rate: " + sampleRate);
System.out.println("Channels: " + channels);
System.out.println("Data size: " + srcPath.length());
}
}
// Set the mode if it has not yet been determined
if (mode < 0) {
if (sampleRate < 100) // Sample Rate has probably been given in kHz
sampleRate *= 1000;
if (sampleRate < 12000)
mode = 0; // Narrowband
else if (sampleRate < 24000)
mode = 1; // Wideband
else
mode = 2; // Ultra-wideband
}
// Construct a new encoder
SpeexEncoder speexEncoder = new SpeexEncoder();
speexEncoder.init(mode, quality, sampleRate, channels);
if (complexity > 0) {
speexEncoder.getEncoder().setComplexity(complexity);
}
if (bitrate > 0) {
speexEncoder.getEncoder().setBitRate(bitrate);
}
if (vbr) {
speexEncoder.getEncoder().setVbr(vbr);
if (vbr_quality > 0) {
speexEncoder.getEncoder().setVbrQuality(vbr_quality);
}
}
if (vad) {
speexEncoder.getEncoder().setVad(vad);
}
if (dtx) {
speexEncoder.getEncoder().setDtx(dtx);
}
// Display info
if (printlevel <= DEBUG) {
System.out.println("");
System.out.println("Output File: " + destPath);
System.out.println("File format: Ogg Speex");
System.out.println("Encoder mode: " + (mode==0 ? "Narrowband" : (mode==1 ? "Wideband" : "UltraWideband")));
System.out.println("Quality: " + (vbr ? vbr_quality : quality));
System.out.println("Complexity: " + complexity);
System.out.println("Frames per packet: " + nframes);
System.out.println("Varible bitrate: " + vbr);
System.out.println("Voice activity detection: " + vad);
System.out.println("Discontinouous Transmission: " + dtx);
}
// Open the file writer
AudioFileWriter writer;
if (destFormat == FILE_FORMAT_OGG) {
writer = new OggSpeexWriter(mode, sampleRate, channels, nframes, vbr);
}
else if (destFormat == FILE_FORMAT_WAVE) {
nframes = PcmWaveWriter.WAVE_FRAME_SIZES[mode-1][channels-1][quality];
writer = new PcmWaveWriter(mode, quality, sampleRate, channels, nframes, vbr);
}
else {
writer = new RawWriter();
}
writer.open(destPath);
writer.writeHeader("Encoded with: " + VERSION);
int pcmPacketSize = 2 * channels * speexEncoder.getFrameSize();
try {
// read until we get to EOF
while (true) {
dis.readFully(temp, 0, nframes*pcmPacketSize);
for (int i=0; iJSpeex Ant package.
It contains the Ant tasks for encoding and decoding Speex files.
$Id: package.html,v 1.1 2004/10/20 17:50:54 mgimpel Exp $
jspeex/src/java/org/xiph/speex/spi/ 0000755 0001750 0001750 00000000000 10245633324 017502 5 ustar twerner twerner jspeex/src/java/org/xiph/speex/spi/FilteredAudioInputStream.java 0000644 0001750 0001750 00000046017 10135761046 025272 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2003 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: FilteredAudioInputStream.java *
* *
* Author: Marc GIMPEL *
* *
* Date: 12th July 2003 *
* *
******************************************************************************/
/* $Id: FilteredAudioInputStream.java,v 1.2 2004/10/21 16:21:58 mgimpel Exp $ */
package org.xiph.speex.spi;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
/**
* A FilteredAudioInputStream
is an AudioInputStream with buffers
* to facilitate transcoding the audio.
* A first byte array can buffer the data from the underlying inputstream until
* sufficient data for transcoding is present.
* A second byte array can hold the transcoded audio data, ready to be read out
* by the stream user.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public abstract class FilteredAudioInputStream
extends AudioInputStream
{
/** The default size of the buffer. */
public static final int DEFAULT_BUFFER_SIZE = 2048;
/** The underlying inputStream. */
protected InputStream in;
/**
* The internal buffer array where the data is stored. When necessary,
* it may be replaced by another array of a different size.
*/
protected byte[] buf;
/**
* The index one greater than the index of the last valid byte in the buffer.
* This value is always in the range 0 through buf.length
;
* elements buf[0]
through buf[count-1]
contain
* buffered input data obtained and filtered from the underlying inputstream.
*/
protected int count;
/**
* The current position in the buffer. This is the index of the next
* character to be read from the buf
array.
*
* This value is always in the range 0 through count
.
* If it is less than count
, then buf[pos]
is the
* next byte to be supplied as input.
* If it is equal to count
, then the next read
or
* skip
operation will require more bytes to be read and
* filtered from the underlying inputstream.
*
* @see #buf
*/
protected int pos;
/**
* The value of the pos
field at the time the last
* mark
method was called.
*
* This value is always in the range -1 through pos
.
* If there is no marked position in the inputstream, this field is -1.
* If there is a marked position in the inputstream, then
* buf[markpos]
is the first byte to be supplied as input after
* a reset
operation.
* If markpos
is not -1, then all bytes from positions
* buf[markpos]
through buf[pos-1]
must remain in
* the buffer array (though they may be moved to another place in the buffer
* array, with suitable adjustments to the values of count
,
* pos
, and markpos
); they may not be discarded
* unless and until the difference between pos
and
* markpos
exceeds marklimit
.
*
* @see #mark(int)
* @see #pos
*/
protected int markpos;
/**
* The maximum read ahead allowed after a call to the mark
* method before subsequent calls to the reset
method fail.
* Whenever the difference between pos
and markpos
* exceeds marklimit
, then the mark may be dropped by setting
* markpos
to -1.
*
* @see #mark(int)
* @see #reset()
*/
protected int marklimit;
/**
* Array of size 1, used by the read method to read just 1 byte.
*/
private final byte[] single = new byte[1];
/**
* The internal buffer array where the unfiltered data is temporarily stored.
*/
protected byte[] prebuf;
/**
* The index one greater than the index of the last valid byte in the
* unfiltered data buffer.
* This value is always in the range 0 through prebuf.length
;
* elements prebuf[0]
through prebuf[count-1]
* contain buffered input data obtained from the underlying input stream.
*/
protected int precount;
/**
* The current position in the unfiltered data buffer. This is the index of
* the next character to be read from the prebuf
array.
*
* This value is always in the range 0 through precount
.
* If it is less than precount
, then prebuf[pos]
is
* the next byte to be supplied as input.
* If it is equal to precount
, then the next read
* or skip
operation will require more bytes to be read from the
* contained inputstream.
*
* @see #prebuf
*/
protected int prepos;
/**
* Check to make sure that this stream has not been closed
* @exception IOException
*/
protected void checkIfStillOpen()
throws IOException
{
if (in == null)
throw new IOException("Stream closed");
}
/**
* Creates a FilteredAudioInputStream
and saves its argument,
* the input stream in
, for later use.
* An internal buffer array is created and stored in buf
.
*
* @param in the underlying input stream.
* @param format the format of this stream's audio data.
* @param length the length in sample frames of the data in this stream.
* @exception IllegalArgumentException if size <= 0 or presize <= 0.
*/
public FilteredAudioInputStream(final InputStream in,
final AudioFormat format,
final long length)
{
this(in, format, length, DEFAULT_BUFFER_SIZE);
}
/**
* Creates a FilteredAudioInputStream
with the specified buffer
* size, and saves its argument, the inputstream in
for later use.
* An internal buffer array of length size
is created and stored
* in buf
.
*
* @param in the underlying input stream.
* @param format the format of this stream's audio data.
* @param length the length in sample frames of the data in this stream.
* @param size the buffer sizes.
* @exception IllegalArgumentException if size <= 0.
*/
public FilteredAudioInputStream(final InputStream in,
final AudioFormat format,
final long length,
final int size)
{
this(in, format, length, size, size);
}
/**
* Creates a FilteredAudioInputStream
with the specified buffer
* size, and saves its argument, the inputstream in
for later use.
* An internal buffer array of length size
is created and stored
* in buf
.
*
* @param in the underlying input stream.
* @param format the format of this stream's audio data.
* @param length the length in sample frames of the data in this stream.
* @param size the buffer size.
* @param presize the prebuffer size.
* @exception IllegalArgumentException if size <= 0 or presize <= 0.
*/
public FilteredAudioInputStream(final InputStream in,
final AudioFormat format,
final long length,
final int size,
final int presize)
{
super(in, format, length);
this.in = in;
if ((size <= 0) || (presize <= 0)) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
count = 0;
prebuf = new byte[presize];
precount = 0;
marklimit = size;
markpos = -1;
}
/**
* Fills the buffer with more data, taking into account shuffling and other
* tricks for dealing with marks.
* Assumes that it is being called by a synchronized method.
* This method also assumes that all data has already been read in,
* hence pos > count.
* @exception IOException
*/
protected void fill()
throws IOException
{
makeSpace();
while (true) {
int read = in.read(prebuf, precount, prebuf.length - precount);
if (read < 0) { // inputstream has ended
// do last stuff here
break;
}
else if (read > 0) {
// do stuff here
precount += read;
break;
}
else { // read == 0
// read 0 bytes from underlying stream yet it is not finished.
}
}
}
/**
* Free up some space in the buffers.
*/
protected void makeSpace()
{
if (markpos < 0)
pos = 0; /* no mark: throw away the buffer */
else if (pos >= buf.length) /* no room left in buffer */
if (markpos > 0) { /* can throw away early part of the buffer */
int sz = pos - markpos;
System.arraycopy(buf, markpos, buf, 0, sz);
pos = sz;
markpos = 0;
}
else if (buf.length >= marklimit) {
markpos = -1; /* buffer got too big, invalidate mark */
pos = 0; /* drop buffer contents */
}
else { /* grow buffer */
int nsz = pos * 2;
if (nsz > marklimit)
nsz = marklimit;
byte[] nbuf = new byte[nsz];
System.arraycopy(buf, 0, nbuf, 0, pos);
buf = nbuf;
}
count = pos;
}
/**
* See the general contract of the read
method of
* InputStream
.
*
* @return the next byte of data, or -1
if the end of the
* stream is reached.
* @exception IOException if an I/O error occurs.
* @see #in
*/
public synchronized int read()
throws IOException
{
if (read(single, 0, 1) == -1) {
return (-1);
}
else {
return (single[0] & 0xFF);
}
}
/**
* Reads bytes from this byte-input stream into the specified byte array,
* starting at the given offset.
*
*
This method implements the general contract of the corresponding
* {@link InputStream#read(byte[], int, int) read}
method of
* the {@link InputStream}
class. As an additional
* convenience, it attempts to read as many bytes as possible by repeatedly
* invoking the read
method of the underlying stream. This
* iterated read
continues until one of the following
* conditions becomes true:
read
method of the underlying stream returns
* -1
, indicating end-of-file, or
*
* available
method of the underlying stream
* returns zero, indicating that further input requests would block.
*
* read
on the underlying stream returns
* -1
to indicate end-of-file then this method returns
* -1
. Otherwise this method returns the number of bytes
* actually read.
*
* Subclasses of this class are encouraged, but not required, to
* attempt to read as many bytes as possible in the same fashion.
*
* @param b destination buffer.
* @param off offset at which to start storing bytes.
* @param len maximum number of bytes to read.
* @return the number of bytes read, or -1
if the end of
* the stream has been reached.
* @exception IOException if an I/O error occurs.
*/
public synchronized int read(final byte[] b,
final int off,
final int len)
throws IOException
{
checkIfStillOpen();
if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
else if (len == 0) {
return 0;
}
int avail = count - pos;
if (avail <= 0) {
fill();
avail = count - pos;
if (avail <= 0) return -1;
}
int cnt = (avail < len) ? avail : len;
System.arraycopy(buf, pos, b, off, cnt);
pos += cnt;
return cnt;
}
/**
* See the general contract of the skip
method of
* InputStream
.
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
* @exception IOException if an I/O error occurs.
*/
public synchronized long skip(final long n)
throws IOException
{
checkIfStillOpen();
// Sanity check
if (n <= 0) {
return 0;
}
// Skip buffered data if there is any
if (pos < count) {
int avail = count - pos;
if (avail > n) {
pos += n;
return n;
}
else {
pos = count;
return avail;
}
}
// Read into buffers and skip
else {
fill(); // This is potentially blocking (i.e. the read on the underlying inputStream could be blocking)
int avail = count - pos;
if (avail <= 0)
return 0;
long skipped = (avail < n) ? avail : n;
pos += skipped;
return skipped;
}
}
/**
* Returns the number of bytes that can be read from this inputstream without
* blocking.
*
* The available
method of FilteredAudioInputStream
* returns the sum of the the number of bytes remaining to be read in the
* buffer (count - pos
).
* The result of calling the available
method of the underlying
* inputstream is not used, as this data will have to be filtered, and thus
* may not be the same size after processing (although subclasses that do the
* filtering should override this method and use the amount of data available
* in the underlying inputstream).
*
* @return the number of bytes that can be read from this inputstream without
* blocking.
* @exception IOException if an I/O error occurs.
* @see #in
*/
public synchronized int available()
throws IOException
{
checkIfStillOpen();
return (count - pos);
}
/**
* See the general contract of the mark
method of
* InputStream
.
*
* @param readlimit the maximum limit of bytes that can be read before
* the mark position becomes invalid.
* @see #reset()
*/
public synchronized void mark(final int readlimit)
{
if (readlimit > buf.length - pos) { // not enough room
byte[] newbuf;
if (readlimit <= buf.length) {
newbuf = buf; // just shift buffer
}
else {
newbuf = new byte[readlimit]; // need a new buffer
}
System.arraycopy(buf, pos, newbuf, 0, count - pos);
buf = newbuf;
count -= pos;
pos = markpos = 0;
}
else {
markpos = pos;
}
marklimit = readlimit;
}
/**
* See the general contract of the reset
method of
* InputStream
.
*
* If
* The
* The JSpeex JavaSound SPI package.
It contains the JavaSound SPI (Service Provider Interface) classes for Speex.
$Id: package.html,v 1.1 2004/10/20 17:50:54 mgimpel Exp $
jspeex/src/java/org/xiph/speex/player/ 0000755 0001750 0001750 00000000000 10245633324 020203 5 ustar twerner twerner jspeex/src/java/org/xiph/speex/player/Player.java 0000644 0001750 0001750 00000050637 10135761046 022316 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2004 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: Player.java *
* *
* Author: Marc GIMPEL *
* *
* Date: Jun 1, 2004 *
* *
******************************************************************************/
/* $Id: Player.java,v 1.2 2004/10/21 16:21:57 mgimpel Exp $ */
package org.xiph.speex.player;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.Timer;
/**
* JavaSound Player.
* Here is the Finite State Machine describing it's state.
* JSpeex player package.
It contains an example swing player and recorder application and applet.
$Id: package.html,v 1.1 2004/10/20 17:50:54 mgimpel Exp $
jspeex/src/java/org/xiph/speex/AudioFileWriter.java 0000644 0001750 0001750 00000033065 10135761046 022620 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2004 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: RawWriter.java *
* *
* Author: Marc GIMPEL *
* *
* Date: 6th January 2004 *
* *
******************************************************************************/
/* $Id: AudioFileWriter.java,v 1.2 2004/10/21 16:21:57 mgimpel Exp $ */
package org.xiph.speex;
import java.io.DataOutput;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
/**
* Abstract Class that defines an Audio File Writer.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public abstract class AudioFileWriter
{
/**
* Closes the output file.
* @exception IOException if there was an exception closing the Audio Writer.
*/
public abstract void close()
throws IOException;
/**
* Open the output file.
* @param file - file to open.
* @exception IOException if there was an exception opening the Audio Writer.
*/
public abstract void open(File file)
throws IOException;
/**
* Open the output file.
* @param filename - file to open.
* @exception IOException if there was an exception opening the Audio Writer.
*/
public abstract void open(String filename)
throws IOException;
/**
* Writes the header pages that start the Ogg Speex file.
* Prepares file for data to be written.
* @param comment description to be included in the header.
* @exception IOException
*/
public abstract void writeHeader(String comment)
throws IOException;
/**
* Writes a packet of audio.
* @param data audio data
* @param offset the offset from which to start reading the data.
* @param len the length of data to read.
* @exception IOException
*/
public abstract void writePacket(byte[] data, int offset, int len)
throws IOException;
/**
* Writes an Ogg Page Header to the given byte array.
* @param buf the buffer to write to.
* @param offset the from which to start writing.
* @param headerType the header type flag
* (0=normal, 2=bos: beginning of stream, 4=eos: end of stream).
* @param granulepos the absolute granule position.
* @param streamSerialNumber
* @param pageCount
* @param packetCount
* @param packetSizes
* @return the amount of data written to the buffer.
*/
public static int writeOggPageHeader(byte[] buf, int offset, int headerType,
long granulepos, int streamSerialNumber,
int pageCount, int packetCount,
byte[] packetSizes)
{
writeString(buf, offset, "OggS"); // 0 - 3: capture_pattern
buf[offset+4] = 0; // 4: stream_structure_version
buf[offset+5] = (byte) headerType; // 5: header_type_flag
writeLong(buf, offset+6, granulepos); // 6 - 13: absolute granule position
writeInt(buf, offset+14, streamSerialNumber); // 14 - 17: stream serial number
writeInt(buf, offset+18, pageCount); // 18 - 21: page sequence no
writeInt(buf, offset+22, 0); // 22 - 25: page checksum
buf[offset+26] = (byte) packetCount; // 26: page_segments
System.arraycopy(packetSizes, 0, // 27 - x: segment_table
buf, offset+27, packetCount);
return packetCount+27;
}
/**
* Builds and returns an Ogg Page Header.
* @param headerType the header type flag
* (0=normal, 2=bos: beginning of stream, 4=eos: end of stream).
* @param granulepos the absolute granule position.
* @param streamSerialNumber
* @param pageCount
* @param packetCount
* @param packetSizes
* @return an Ogg Page Header.
*/
public static byte[] buildOggPageHeader(int headerType, long granulepos,
int streamSerialNumber, int pageCount,
int packetCount, byte[] packetSizes)
{
byte[] data = new byte[packetCount+27];
writeOggPageHeader(data, 0, headerType, granulepos, streamSerialNumber,
pageCount, packetCount, packetSizes);
return data;
}
/**
* Writes a Speex Header to the given byte array.
* @param buf the buffer to write to.
* @param offset the from which to start writing.
* @param sampleRate
* @param mode
* @param channels
* @param vbr
* @param nframes
* @return the amount of data written to the buffer.
*/
public static int writeSpeexHeader(byte[] buf, int offset, int sampleRate,
int mode, int channels, boolean vbr,
int nframes)
{
writeString(buf, offset, "Speex "); // 0 - 7: speex_string
writeString(buf, offset+8, "speex-1.0"); // 8 - 27: speex_version
System.arraycopy(new byte[11], 0, buf, offset+17, 11); // : speex_version (fill in up to 20 bytes)
writeInt(buf, offset+28, 1); // 28 - 31: speex_version_id
writeInt(buf, offset+32, 80); // 32 - 35: header_size
writeInt(buf, offset+36, sampleRate); // 36 - 39: rate
writeInt(buf, offset+40, mode); // 40 - 43: mode (0=NB, 1=WB, 2=UWB)
writeInt(buf, offset+44, 4); // 44 - 47: mode_bitstream_version
writeInt(buf, offset+48, channels); // 48 - 51: nb_channels
writeInt(buf, offset+52, -1); // 52 - 55: bitrate
writeInt(buf, offset+56, 160 << mode); // 56 - 59: frame_size (NB=160, WB=320, UWB=640)
writeInt(buf, offset+60, vbr?1:0); // 60 - 63: vbr
writeInt(buf, offset+64, nframes); // 64 - 67: frames_per_packet
writeInt(buf, offset+68, 0); // 68 - 71: extra_headers
writeInt(buf, offset+72, 0); // 72 - 75: reserved1
writeInt(buf, offset+76, 0); // 76 - 79: reserved2
return 80;
}
/**
* Builds a Speex Header.
* @param sampleRate
* @param mode
* @param channels
* @param vbr
* @param nframes
* @return a Speex Header.
*/
public static byte[] buildSpeexHeader(int sampleRate, int mode, int channels,
boolean vbr, int nframes)
{
byte[] data = new byte[80];
writeSpeexHeader(data, 0, sampleRate, mode, channels, vbr, nframes);
return data;
}
/**
* Writes a Speex Comment to the given byte array.
* @param buf the buffer to write to.
* @param offset the from which to start writing.
* @param comment the comment.
* @return the amount of data written to the buffer.
*/
public static int writeSpeexComment(byte[] buf, int offset, String comment)
{
int length = comment.length();
writeInt(buf, offset, length); // vendor comment size
writeString(buf, offset+4, comment); // vendor comment
writeInt(buf, offset+length+4, 0); // user comment list length
return length+8;
}
/**
* Builds and returns a Speex Comment.
* @param comment the comment.
* @return a Speex Comment.
*/
public static byte[] buildSpeexComment(String comment)
{
byte[] data = new byte[comment.length()+8];
writeSpeexComment(data, 0, comment);
return data;
}
/**
* Writes a Little-endian short.
* @param out the data output to write to.
* @param v value to write.
* @exception IOException
*/
public static void writeShort(DataOutput out, short v)
throws IOException
{
out.writeByte((0xff & v));
out.writeByte((0xff & (v >>> 8)));
}
/**
* Writes a Little-endian int.
* @param out the data output to write to.
* @param v value to write.
* @exception IOException
*/
public static void writeInt(DataOutput out, int v)
throws IOException
{
out.writeByte(0xff & v);
out.writeByte(0xff & (v >>> 8));
out.writeByte(0xff & (v >>> 16));
out.writeByte(0xff & (v >>> 24));
}
/**
* Writes a Little-endian short.
* @param os - the output stream to write to.
* @param v - the value to write.
* @exception IOException
*/
public static void writeShort(OutputStream os, short v)
throws IOException
{
os.write((0xff & v));
os.write((0xff & (v >>> 8)));
}
/**
* Writes a Little-endian int.
* @param os - the output stream to write to.
* @param v - the value to write.
* @exception IOException
*/
public static void writeInt(OutputStream os, int v)
throws IOException
{
os.write(0xff & v);
os.write(0xff & (v >>> 8));
os.write(0xff & (v >>> 16));
os.write(0xff & (v >>> 24));
}
/**
* Writes a Little-endian long.
* @param os - the output stream to write to.
* @param v - the value to write.
* @exception IOException
*/
public static void writeLong(OutputStream os, long v)
throws IOException
{
os.write((int)(0xff & v));
os.write((int)(0xff & (v >>> 8)));
os.write((int)(0xff & (v >>> 16)));
os.write((int)(0xff & (v >>> 24)));
os.write((int)(0xff & (v >>> 32)));
os.write((int)(0xff & (v >>> 40)));
os.write((int)(0xff & (v >>> 48)));
os.write((int)(0xff & (v >>> 56)));
}
/**
* Writes a Little-endian short.
* @param data the array into which the data should be written.
* @param offset the offset from which to start writing in the array.
* @param v the value to write.
*/
public static void writeShort(byte[] data, int offset, int v)
{
data[offset] = (byte) (0xff & v);
data[offset+1] = (byte) (0xff & (v >>> 8));
}
/**
* Writes a Little-endian int.
* @param data the array into which the data should be written.
* @param offset the offset from which to start writing in the array.
* @param v the value to write.
*/
public static void writeInt(byte[] data, int offset, int v)
{
data[offset] = (byte) (0xff & v);
data[offset+1] = (byte) (0xff & (v >>> 8));
data[offset+2] = (byte) (0xff & (v >>> 16));
data[offset+3] = (byte) (0xff & (v >>> 24));
}
/**
* Writes a Little-endian long.
* @param data the array into which the data should be written.
* @param offset the offset from which to start writing in the array.
* @param v the value to write.
*/
public static void writeLong(byte[] data, int offset, long v)
{
data[offset] = (byte) (0xff & v);
data[offset+1] = (byte) (0xff & (v >>> 8));
data[offset+2] = (byte) (0xff & (v >>> 16));
data[offset+3] = (byte) (0xff & (v >>> 24));
data[offset+4] = (byte) (0xff & (v >>> 32));
data[offset+5] = (byte) (0xff & (v >>> 40));
data[offset+6] = (byte) (0xff & (v >>> 48));
data[offset+7] = (byte) (0xff & (v >>> 56));
}
/**
* Writes a String.
* @param data the array into which the data should be written.
* @param offset the offset from which to start writing in the array.
* @param v the value to write.
*/
public static void writeString(byte[] data, int offset, String v)
{
byte[] str = v.getBytes();
System.arraycopy(str, 0, data, offset, str.length);
}
}
jspeex/src/java/org/xiph/speex/Bits.java 0000644 0001750 0001750 00000017075 10135761046 020466 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2003 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: Bits.java *
* *
* Author: James LAWRENCE *
* Modified by: Marc GIMPEL *
* Based on code by: Jean-Marc VALIN *
* *
* Date: March 2003 *
* *
******************************************************************************/
/* $Id: Bits.java,v 1.2 2004/10/21 16:21:57 mgimpel Exp $ */
/* Copyright (C) 2002 Jean-Marc Valin
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.xiph.speex;
/**
* Speex bit packing and unpacking class.
*
* @author Jim Lawrence, helloNetwork.com
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class Bits
{
/** Default buffer size */
public static final int DEFAULT_BUFFER_SIZE = 1024;
/** "raw" data */
private byte[] bytes;
/** Position of the byte "cursor" */
private int bytePtr;
/** Position of the bit "cursor" within the current byte */
private int bitPtr;
/**
* Initialise the bit packing variables.
*/
public void init()
{
bytes = new byte[DEFAULT_BUFFER_SIZE];
bytePtr=0;
bitPtr=0;
}
/**
* Advance n bits.
* @param n - the number of bits to advance.
*/
public void advance(final int n)
{
bytePtr += n >> 3;
bitPtr += n & 7;
if (bitPtr>7) {
bitPtr-=8;
bytePtr++;
}
}
/**
* Sets the buffer to the given value.
* @param newBuffer
*/
protected void setBuffer(final byte[] newBuffer)
{
bytes = newBuffer;
}
/**
* Take a peek at the next bit.
* @return the next bit.
*/
public int peek()
{
return ((bytes[bytePtr] & 0xFF) >> (7-bitPtr)) & 1;
}
/**
* Read the given array into the buffer.
* @param newbytes
* @param offset
* @param len
*/
public void read_from(final byte[] newbytes,
final int offset,
final int len)
{
for (int i=0; imarkpos
is -1 (no mark has been set or the mark has been
* invalidated), an IOException
is thrown.
* Otherwise, pos
is set equal to markpos
.
*
* @exception IOException if this stream has not been marked or
* if the mark has been invalidated.
* @see #mark(int)
*/
public synchronized void reset()
throws IOException
{
checkIfStillOpen();
if (markpos < 0)
throw new IOException("Attempt to reset when no mark is valid");
pos = markpos;
}
/**
* Tests if this input stream supports the mark
and
* reset
methods. The markSupported
method of
* FilteredAudioInputStream
returns true
.
*
* @return a boolean
indicating if this stream type supports
* the mark
and reset
methods.
* @see #mark(int)
* @see #reset()
*/
public boolean markSupported()
{
return true;
}
/**
* Closes this input stream and releases any system resources associated with
* the stream.
*
* @exception IOException if an I/O error occurs.
*/
public synchronized void close()
throws IOException
{
if (in == null)
return;
in.close();
in = null;
buf = null;
prebuf = null;
}
}
jspeex/src/java/org/xiph/speex/spi/Pcm2SpeexAudioInputStream.java 0000644 0001750 0001750 00000056755 10135761046 025354 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2003 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: Pcm2SpeexAudioInputStream.java *
* *
* Author: Marc GIMPEL *
* *
* Date: 12th July 2003 *
* *
******************************************************************************/
/* $Id: Pcm2SpeexAudioInputStream.java,v 1.2 2004/10/21 16:21:58 mgimpel Exp $ */
package org.xiph.speex.spi;
import java.util.Random;
import java.io.IOException;
import java.io.InputStream;
import java.io.StreamCorruptedException;
import javax.sound.sampled.AudioFormat;
import org.xiph.speex.AudioFileWriter;
import org.xiph.speex.OggCrc;
import org.xiph.speex.Encoder;
import org.xiph.speex.SpeexEncoder;
/**
* Converts a PCM 16bits/sample mono audio stream to Ogg Speex
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class Pcm2SpeexAudioInputStream
extends FilteredAudioInputStream
{
/** The default size of the buffer (UWB stereo requires at least 2560b). */
public static final int DEFAULT_BUFFER_SIZE = 2560;
// public static final boolean DEFAULT_VBR = true;
/** The default sample rate if none is given in the constructor. */
public static final int DEFAULT_SAMPLERATE = 8000;
/** The default number of channels if none is given in the constructor. */
public static final int DEFAULT_CHANNELS = 1;
/** The default quality setting for the Speex encoder. */
public static final int DEFAULT_QUALITY = 3;
/** The default number of Speex frames that will be put in each Ogg packet. */
public static final int DEFAULT_FRAMES_PER_PACKET = 1;
/** The default number of Ogg packets that will be put in each Ogg page. */
public static final int DEFAULT_PACKETS_PER_OGG_PAGE = 20; // .4s of audio
/** Indicates the value is unknown or undetermined. */
public static final int UNKNOWN = -1;
// Speex variables
/** The Speex Encoder class. */
private SpeexEncoder encoder;
/** The encoder mode (0=NB, 1=WB, 2=UWB). */
private int mode;
/** The size in bytes of PCM data that will be encoded into 1 Speex frame. */
private int frameSize;
/** The number of Speex frames that will be put in each Ogg packet. */
private int framesPerPacket;
/** The number of audio channels (1=mono, 2=stereo). */
private int channels;
// Ogg variables
/** The comment String that will appear in the Ogg comment packet. */
private String comment = null;
/** A counter for the number of PCM samples that have been encoded. */
private int granulepos;
/** A unique serial number that identifies the Ogg stream. */
private int streamSerialNumber;
/** The number of Ogg packets that will be put in each Ogg page. */
private int packetsPerOggPage;
/** The number of Ogg packets that have been encoded in the current page. */
private int packetCount;
/** The number of Ogg pages that have been written to the stream. */
private int pageCount;
/** Pointer in the buffer to the point where Ogg data is added. */
private int oggCount;
/** Flag to indicate if this is the first time a encode method is called. */
private boolean first;
/**
* Constructor
* @param in the underlying input stream.
* @param format the target format of this stream's audio data.
* @param length the length in sample frames of the data in this stream.
*/
public Pcm2SpeexAudioInputStream(final InputStream in,
final AudioFormat format,
final long length)
{
this(UNKNOWN, UNKNOWN, in, format, length, DEFAULT_BUFFER_SIZE);
}
/**
* Constructor
* @param mode the mode of the encoder (0=NB, 1=WB, 2=UWB).
* @param quality the quality setting of the encoder (between 0 and 10).
* @param in the underlying input stream.
* @param format the target format of this stream's audio data.
* @param length the length in sample frames of the data in this stream.
*/
public Pcm2SpeexAudioInputStream(final int mode,
final int quality,
final InputStream in,
final AudioFormat format,
final long length)
{
this(mode, quality, in, format, length, DEFAULT_BUFFER_SIZE);
}
/**
* Constructor
* @param in the underlying input stream.
* @param format the target format of this stream's audio data.
* @param length the length in sample frames of the data in this stream.
* @param size the buffer size.
* @exception IllegalArgumentException if size <= 0.
*/
public Pcm2SpeexAudioInputStream(final InputStream in,
final AudioFormat format,
final long length,
final int size)
{
this(UNKNOWN, UNKNOWN, in, format, length, size);
}
/**
* Constructor
* @param mode the mode of the encoder (0=NB, 1=WB, 2=UWB).
* @param quality the quality setting of the encoder (between 0 and 10).
* @param in the underlying input stream.
* @param format the target format of this stream's audio data.
* @param length the length in sample frames of the data in this stream.
* @param size the buffer size.
* @exception IllegalArgumentException if size <= 0.
*/
public Pcm2SpeexAudioInputStream(int mode,
int quality,
final InputStream in,
final AudioFormat format,
final long length,
final int size)
{
super(in, format, length, size);
// Ogg initialisation
granulepos = 0;
if (streamSerialNumber == 0)
streamSerialNumber = new Random().nextInt();
packetsPerOggPage = DEFAULT_PACKETS_PER_OGG_PAGE;
packetCount = 0;
pageCount = 0;
// Speex initialisation
framesPerPacket = DEFAULT_FRAMES_PER_PACKET;
int samplerate = (int) format.getSampleRate();
if (samplerate < 0)
samplerate = DEFAULT_SAMPLERATE;
channels = format.getChannels();
if (channels < 0)
channels = DEFAULT_CHANNELS;
if (mode < 0)
mode = (samplerate < 12000) ? 0 : ((samplerate < 24000) ? 1 : 2);
this.mode = mode;
AudioFormat.Encoding encoding = format.getEncoding();
if (quality < 0) {
if (encoding instanceof SpeexEncoding) {
quality = ((SpeexEncoding) encoding).getQuality();
}
else {
quality = DEFAULT_QUALITY;
}
}
encoder = new SpeexEncoder();
encoder.init(mode, quality, samplerate, channels);
if (encoding instanceof SpeexEncoding &&
((SpeexEncoding) encoding).isVBR()) {
setVbr(true);
}
else {
setVbr(false);
}
frameSize = 2 * channels * encoder.getFrameSize();
// Misc initialsation
comment = "Encoded with " + SpeexEncoder.VERSION;
first = true;
}
/**
* Sets the Stream Serial Number.
* Must not be changed mid stream.
* @param serialNumber
*/
public void setSerialNumber(final int serialNumber)
{
if (first) {
this.streamSerialNumber = serialNumber;
}
}
/**
* Sets the number of Audio Frames that are to be put in every Speex Packet.
* An Audio Frame contains 160 samples for narrowband, 320 samples for
* wideband and 640 samples for ultra-wideband.
* @param framesPerPacket
* @see #DEFAULT_FRAMES_PER_PACKET
*/
public void setFramesPerPacket(int framesPerPacket)
{
if (framesPerPacket <= 0) {
framesPerPacket = DEFAULT_FRAMES_PER_PACKET;
}
this.framesPerPacket = framesPerPacket;
}
/**
* Sets the number of Speex Packets that are to be put in every Ogg Page.
* This value must be less than 256 as the value is encoded in 1 byte in the
* Ogg Header (just before the array of packet sizes)
* @param packetsPerOggPage
* @see #DEFAULT_PACKETS_PER_OGG_PAGE
*/
public void setPacketsPerOggPage(int packetsPerOggPage)
{
if (packetsPerOggPage <= 0) {
packetsPerOggPage = DEFAULT_PACKETS_PER_OGG_PAGE;
}
if (packetsPerOggPage > 255) {
packetsPerOggPage = 255;
}
this.packetsPerOggPage = packetsPerOggPage;
}
/**
* Sets the comment for the Ogg Comment Header.
* @param comment
* @param appendVersion
*/
public void setComment(final String comment,
final boolean appendVersion)
{
this.comment = comment;
if (appendVersion) {
this.comment += SpeexEncoder.VERSION;
}
}
/**
* Sets the Speex encoder Quality.
* @param quality
*/
public void setQuality(final int quality)
{
encoder.getEncoder().setQuality(quality);
if (encoder.getEncoder().getVbr()) {
encoder.getEncoder().setVbrQuality((float)quality);
}
}
/**
* Sets whether of not the encoder is to use VBR.
* @param vbr
*/
public void setVbr(final boolean vbr)
{
encoder.getEncoder().setVbr(vbr);
}
/**
* Returns the Encoder.
* @return the Encoder.
*/
public Encoder getEncoder()
{
return encoder.getEncoder();
}
/**
* Fills the buffer with more data, taking into account
* shuffling and other tricks for dealing with marks.
* Assumes that it is being called by a synchronized method.
* This method also assumes that all data has already been read in,
* hence pos > count.
* @exception IOException
*/
protected void fill()
throws IOException
{
makeSpace();
if (first) {
writeHeaderFrames();
first = false;
}
while (true) {
if ((prebuf.length - prepos) < framesPerPacket*frameSize*packetsPerOggPage) { // grow prebuf
int nsz = prepos + framesPerPacket*frameSize*packetsPerOggPage;
byte[] nbuf = new byte[nsz];
System.arraycopy(prebuf, 0, nbuf, 0, precount);
prebuf = nbuf;
}
int read = in.read(prebuf, precount, prebuf.length - precount);
if (read < 0) { // inputstream has ended
if ((precount-prepos) % 2 != 0) { // we don't have a complete last PCM sample
throw new StreamCorruptedException("Incompleted last PCM sample when stream ended");
}
while (prepos < precount) { // still data to encode
if ((precount - prepos) < framesPerPacket*frameSize) {
// fill end of frame with zeros
for (;precount < (prepos+framesPerPacket*frameSize); precount++) {
prebuf[precount] = 0;
}
}
if (packetCount == 0) {
writeOggPageHeader(packetsPerOggPage, 0);
}
for (int i=0; iavailable
method of FilteredAudioInputStream
* returns the sum of the the number of bytes remaining to be read in the
* buffer (count - pos
) and the result of calling the
* available
method of the underlying inputstream.
*
* @return the number of bytes that can be read from this inputstream without
* blocking.
* @exception IOException if an I/O error occurs.
* @see #in
*/
public synchronized int available()
throws IOException
{
int avail = super.available();
int unencoded = precount - prepos + in.available();
if (encoder.getEncoder().getVbr()) {
switch (mode) {
case 0: // Narrowband
// ogg header size = 27 + packetsPerOggPage
// count 1 byte (min 5 bits) for each block available
return avail + (27 + 2 * packetsPerOggPage) *
(unencoded / (packetsPerOggPage*framesPerPacket*320));
case 1: // Wideband
// ogg header size = 27 + packetsPerOggPage
// count 2 byte (min 9 bits) for each block available
return avail + (27 + 2 * packetsPerOggPage) *
(unencoded / (packetsPerOggPage*framesPerPacket*640));
case 2: // Ultra wideband
// ogg header size = 27 + packetsPerOggPage
// count 2 byte (min 13 bits) for each block available
return avail + (27 + 3 * packetsPerOggPage) *
(unencoded / (packetsPerOggPage*framesPerPacket*1280));
default:
return avail;
}
}
else {
// Calculate size of a packet of Speex data.
int spxpacketsize = encoder.getEncoder().getEncodedFrameSize();
if (channels > 1) {
spxpacketsize += 17; // 1+4(14=inband)+4(9=stereo)+8(stereo data)
}
spxpacketsize *= framesPerPacket;
spxpacketsize = (spxpacketsize + 7) >> 3; // convert bits to bytes
// Calculate size of an Ogg packet containing X Speex packets.
// Ogg Packet = Ogg header + size of each packet + Ogg packets
int oggpacketsize = 27 + packetsPerOggPage * (spxpacketsize + 1);
int pcmframesize; // size of PCM data necessary to encode 1 Speex packet.
switch (mode) {
case 0: // Narrowband
// 1 frame = 20ms = 160ech * channels = 320bytes * channels
pcmframesize = framesPerPacket * 320 * encoder.getChannels();
avail += oggpacketsize *
(unencoded / (packetsPerOggPage * pcmframesize));
return avail;
case 1: // Wideband
// 1 frame = 20ms = 320ech * channels = 640bytes * channels
pcmframesize = framesPerPacket * 640 * encoder.getChannels();
avail += oggpacketsize *
(unencoded / (packetsPerOggPage * pcmframesize));
return avail;
case 2: // Ultra wideband
// 1 frame = 20ms = 640ech * channels = 1280bytes * channels
pcmframesize = framesPerPacket * 1280 * encoder.getChannels();
avail += oggpacketsize *
(unencoded / (packetsPerOggPage * pcmframesize));
return avail;
default:
return avail;
}
}
}
//---------------------------------------------------------------------------
// Ogg Specific Code
//---------------------------------------------------------------------------
/**
* Write an OGG page header.
* @param packets - the number of packets in the Ogg Page (must be between 1 and 255)
* @param headertype - 2=bos: beginning of sream, 4=eos: end of sream
*/
private void writeOggPageHeader(final int packets,
final int headertype)
{
while ((buf.length - count) < (27 + packets)) { // grow buffer
int nsz = buf.length * 2;
byte[] nbuf = new byte[nsz];
System.arraycopy(buf, 0, nbuf, 0, count);
buf = nbuf;
}
AudioFileWriter.writeOggPageHeader(buf, count, headertype, granulepos,
streamSerialNumber, pageCount++,
packets, new byte[packets]);
oggCount = count + 27 + packets;
}
/**
* Calculate and write the OGG page checksum. This now closes the Ogg page.
*/
private void writeOggPageChecksum()
{
// write the granulpos
granulepos += framesPerPacket * frameSize * packetCount / 2;
AudioFileWriter.writeLong(buf, count+6, granulepos);
// write the checksum
int chksum = OggCrc.checksum(0, buf, count, oggCount-count);
AudioFileWriter.writeInt(buf, count+22, chksum);
// reset variables for a new page.
count = oggCount;
packetCount = 0;
}
/**
* Write the OGG Speex header then the comment header.
*/
private void writeHeaderFrames()
{
int length = comment.length();
if (length > 247) {
comment = comment.substring(0, 247);
length = 247;
}
while ((buf.length - count) < length + 144) {
// grow buffer (108 = 28 + 80 = size of Ogg Header Frame)
// (28 + length + 8 = size of Comment Frame)
int nsz = buf.length * 2;
byte[] nbuf = new byte[nsz];
System.arraycopy(buf, 0, nbuf, 0, count);
buf = nbuf;
}
// writes the OGG header page
AudioFileWriter.writeOggPageHeader(buf, count, 2, granulepos,
streamSerialNumber, pageCount++,
1, new byte[] {80});
oggCount = count + 28;
/* writes the Speex header */
AudioFileWriter.writeSpeexHeader(buf, oggCount, encoder.getSampleRate(),
mode, encoder.getChannels(),
encoder.getEncoder().getVbr(),
framesPerPacket);
oggCount += 80;
/* Calculate Checksum */
int chksum = OggCrc.checksum(0, buf, count, oggCount-count);
AudioFileWriter.writeInt(buf, count+22, chksum);
count = oggCount;
// writes the OGG header page
AudioFileWriter.writeOggPageHeader(buf, count, 0, granulepos,
streamSerialNumber, pageCount++,
1, new byte[] {(byte)(length+8)});
oggCount = count + 28;
/* writes the OGG comment page */
AudioFileWriter.writeSpeexComment(buf, oggCount, comment);
oggCount += length+8;
/* Calculate Checksum */
chksum = OggCrc.checksum(0, buf, count, oggCount-count);
AudioFileWriter.writeInt(buf, count+22, chksum);
count = oggCount;
// reset variables for a new page.
packetCount = 0;
}
}
jspeex/src/java/org/xiph/speex/spi/Speex2PcmAudioInputStream.java 0000644 0001750 0001750 00000051676 10245641604 025350 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2003 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: Speex2PcmAudioInputStream.java *
* *
* Author: Marc GIMPEL *
* *
* Date: 12th July 2003 *
* *
******************************************************************************/
/* $Id: Speex2PcmAudioInputStream.java,v 1.5 2005/05/27 15:57:55 mgimpel Exp $ */
package org.xiph.speex.spi;
import java.io.InputStream;
import java.io.IOException;
import java.io.StreamCorruptedException;
import javax.sound.sampled.AudioFormat;
import org.xiph.speex.Bits;
import org.xiph.speex.Decoder;
import org.xiph.speex.NbDecoder;
import org.xiph.speex.SbDecoder;
/**
* Converts an Ogg Speex bitstream into a PCM 16bits/sample audio stream.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.5 $
*/
public class Speex2PcmAudioInputStream
extends FilteredAudioInputStream
{
// InputStream variables
/** Flag to indicate if this Stream has been initialised. */
private boolean initialised;
// audio parameters
/** The sample rate of the audio, in samples per seconds (Hz). */
private int sampleRate;
/** The number of audio channels (1=mono, 2=stereo). */
private int channelCount;
// Speex variables
/** Array containing the decoded audio samples. */
private float[] decodedData;
/** Array containing the decoded audio samples converted into bytes. */
private byte[] outputData;
/** Speex bit packing and unpacking class. */
private Bits bits;
/** Speex Decoder. */
private Decoder decoder;
/** The frame size, in samples. */
private int frameSize;
/** The number of Speex frames that will be put in each Ogg packet. */
private int framesPerPacket;
// Ogg variables
/** A unique serial number that identifies the Ogg stream. */
private int streamSerialNumber;
/** The number of Ogg packets that are in each Ogg page. */
private int packetsPerOggPage;
/** The number of Ogg packets that have been decoded in the current page. */
private int packetCount;
/** Array containing the sizes of Ogg packets in the current page.*/
private byte[] packetSizes;
/**
* Constructor
* @param in the underlying input stream.
* @param format the target format of this stream's audio data.
* @param length the length in sample frames of the data in this stream.
*/
public Speex2PcmAudioInputStream(final InputStream in,
final AudioFormat format,
final long length)
{
this(in, format, length, DEFAULT_BUFFER_SIZE);
}
/**
* Constructor
* @param in the underlying input stream.
* @param format the target format of this stream's audio data.
* @param length the length in sample frames of the data in this stream.
* @param size the buffer size.
* @exception IllegalArgumentException if size <= 0.
*/
public Speex2PcmAudioInputStream(final InputStream in,
final AudioFormat format,
final long length,
final int size)
{
super(in, format, length, size);
bits = new Bits();
packetSizes = new byte[256];
initialised = false;
}
/**
* Initialises the Ogg Speex to PCM InputStream.
* Read the Ogg Speex header and extract the speex decoder parameters to
* initialise the decoder. Then read the Comment header.
* Ogg Header description:
*
* 0 - 3: capture_pattern
* 4: stream_structure_version
* 5: header_type_flag (2=bos: beginning of sream)
* 6 - 13: absolute granule position
* 14 - 17: stream serial number
* 18 - 21: page sequence no
* 22 - 25: page checksum
* 26: page_segments
* 27 -...: segment_table
*
* Speex Header description
*
* 0 - 7: speex_string
* 8 - 27: speex_version
* 28 - 31: speex_version_id
* 32 - 35: header_size
* 36 - 39: rate
* 40 - 43: mode (0=narrowband, 1=wb, 2=uwb)
* 44 - 47: mode_bitstream_version
* 48 - 51: nb_channels
* 52 - 55: bitrate
* 56 - 59: frame_size
* 60 - 63: vbr
* 64 - 67: frames_per_packet
* 68 - 71: extra_headers
* 72 - 75: reserved1
* 76 - 79: reserved2
*
* @param blocking whether the method should block until initialisation is
* successfully completed or not.
* @exception IOException
*/
protected void initialise(final boolean blocking)
throws IOException
{
while (!initialised) {
int readsize = prebuf.length - precount - 1;
int avail = in.available();
if (!blocking && avail <= 0) {
return;
}
readsize = (avail > 0 ? Math.min(avail, readsize) : readsize);
int n = in.read(prebuf, precount, readsize);
if (n < 0) {
throw new StreamCorruptedException("Incomplete Ogg Headers");
}
if (n == 0) {
// This should never happen.
//assert false : "Read 0 bytes from stream - possible infinate loop";
}
precount += n;
if (decoder==null && precount>=108) { // we can process the speex header
if (!(new String(prebuf, 0, 4).equals("OggS"))) {
throw new StreamCorruptedException("The given stream does not appear to be Ogg.");
}
streamSerialNumber = readInt(prebuf, 14);
if (!(new String(prebuf, 28, 8).equals("Speex "))) {
throw new StreamCorruptedException("The given stream does not appear to be Ogg Speex.");
}
sampleRate = readInt(prebuf, 28+36);
channelCount = readInt(prebuf, 28+48);
framesPerPacket = readInt(prebuf, 28+64);
int mode = readInt(prebuf, 28+40);
switch (mode) {
case 0:
decoder = new NbDecoder();
((NbDecoder)decoder).nbinit();
break;
case 1:
decoder = new SbDecoder();
((SbDecoder)decoder).wbinit();
break;
case 2:
decoder = new SbDecoder();
((SbDecoder)decoder).uwbinit();
break;
default:
}
/* initialize the speex decoder */
decoder.setPerceptualEnhancement(true);
/* set decoder format and properties */
frameSize = decoder.getFrameSize();
decodedData = new float[frameSize*channelCount];
outputData = new byte[2*frameSize*channelCount*framesPerPacket];
bits.init();
}
if (decoder!=null && precount>=108+27) { // we can process the comment (skip them)
packetsPerOggPage = 0xff & prebuf[108+26];
if (precount>=108+27+packetsPerOggPage) {
int size = 0;
for (int i=0; iskip
method of
* InputStream
.
*
* @param n the number of bytes to be skipped.
* @return the actual number of bytes skipped.
* @exception IOException if an I/O error occurs.
*/
public synchronized long skip(long n)
throws IOException
{
while (!initialised) {
initialise(true);
}
checkIfStillOpen();
// Sanity check
if (n <= 0) {
return 0;
}
// Skip buffered data if there is any
if (pos < count) {
return super.skip(n);
}
// Nothing in the buffers to skip
else {
int decodedPacketSize = 2*framesPerPacket*frameSize*channelCount;
if (markpos < 0 && n >= decodedPacketSize) {
// We aren't buffering and skipping more than a complete Speex packet:
// Lets try to skip complete Speex packets without decoding
if (packetCount >= packetsPerOggPage) { // read new Ogg Page header
readOggPageHeader();
}
if (packetCount < packetsPerOggPage) { // read the next packet
int skipped = 0;
if ((precount-prepos) < packetSizes[packetCount]) { // we don't have enough data
int avail = in.available();
if (avail > 0) {
int size = Math.min(prebuf.length - precount, avail);
int read = in.read(prebuf, precount, size);
if (read < 0) { // inputstream has ended
throw new IOException("End of stream but there are still supposed to be packets to decode");
}
precount += read;
}
}
while (((precount-prepos) >= packetSizes[packetCount]) &&
(packetCount < packetsPerOggPage) &&
(n >= decodedPacketSize)) { // lets skip all we can
prepos += packetSizes[packetCount++];
skipped += decodedPacketSize;
n -= decodedPacketSize;
if (packetCount >= packetsPerOggPage) { // read new Ogg Page header
readOggPageHeader();
}
}
System.arraycopy(prebuf, prepos, prebuf, 0, precount-prepos);
precount -= prepos;
prepos = 0;
return skipped; // we have skipped some data (all that we could), so we can leave now, otherwise we return to a potentially blocking read of the underlying inputstream.
}
}
// We are buffering, or couldn't skip a complete Speex packet:
// Read (decode) into buffers and skip (this is potentially blocking)
return super.skip(n);
}
}
/**
* Returns the number of bytes that can be read from this inputstream without
* blocking.
* available
method of FilteredAudioInputStream
* returns the sum of the the number of bytes remaining to be read in the
* buffer (count - pos
).
* The result of calling the available
method of the underlying
* inputstream is not used, as this data will have to be filtered, and thus
* may not be the same size after processing (although subclasses that do the
* filtering should override this method and use the amount of data available
* in the underlying inputstream).
*
* @return the number of bytes that can be read from this inputstream without
* blocking.
* @exception IOException if an I/O error occurs.
* @see #in
*/
public synchronized int available()
throws IOException
{
if (!initialised) {
initialise(false);
if (!initialised) {
return 0;
}
}
int avail = super.available();
if (packetCount >= packetsPerOggPage) { // read new Ogg Page header
readOggPageHeader();
}
// See how much we could decode from the underlying stream.
if (packetCount < packetsPerOggPage) {
int undecoded = precount - prepos + in.available();
int size = packetSizes[packetCount];
int tempCount = 0;
while (size < undecoded &&
packetCount + tempCount < packetsPerOggPage) {
undecoded -= size;
avail += 2 * frameSize * framesPerPacket;
tempCount++;
size = packetSizes[packetCount + tempCount];
}
}
return avail;
}
//---------------------------------------------------------------------------
// Ogg Specific code
//---------------------------------------------------------------------------
/**
* Read the Ogg Page header and extract the speex packet sizes.
* Note: the checksum is ignores.
* Note: the method should no block on a read because it will not read more
* then is available
*/
private void readOggPageHeader()
throws IOException
{
int packets = 0;
if (precount-prepos<27) {
int avail = in.available();
if (avail > 0) {
int size = Math.min(prebuf.length - precount, avail);
int read = in.read(prebuf, precount, size);
if (read < 0) { // inputstream has ended
throw new IOException("End of stream but available was positive");
}
precount += read;
}
}
if (precount-prepos>=27) { // can read beginning of Page header
if (!(new String(prebuf, prepos, 4).equals("OggS"))) {
throw new StreamCorruptedException("Lost Ogg Sync");
}
if (streamSerialNumber != readInt(prebuf, prepos+14)) {
throw new StreamCorruptedException("Ogg Stream Serial Number mismatch");
}
packets = 0xff & prebuf[prepos+26];
}
if (precount-prepos<27+packets) {
int avail = in.available();
if (avail > 0) {
int size = Math.min(prebuf.length - precount, avail);
int read = in.read(prebuf, precount, size);
if (read < 0) { // inputstream has ended
throw new IOException("End of stream but available was positive");
}
precount += read;
}
}
if (precount-prepos>=27+packets) { // can read entire Page header
System.arraycopy(prebuf, prepos+27, packetSizes, 0, packets);
packetCount = 0;
prepos += 27+packets;
packetsPerOggPage = packets;
}
}
/**
* Converts Little Endian (Windows) bytes to an int (Java uses Big Endian).
* @param data the data to read.
* @param offset the offset from which to start reading.
* @return the integer value of the reassembled bytes.
*/
private static int readInt(final byte[] data, final int offset)
{
return (data[offset] & 0xff) |
((data[offset+1] & 0xff) << 8) |
((data[offset+2] & 0xff) << 16) |
(data[offset+3] << 24); // no & 0xff at the end to keep the sign
}
}
jspeex/src/java/org/xiph/speex/spi/SpeexAudioFileReader.java 0000644 0001750 0001750 00000041212 10135761046 024337 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2003 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: SpeexAudioFileReader.java *
* *
* Author: Marc GIMPEL *
* *
* Date: 12th July 2003 *
* *
******************************************************************************/
/* $Id: SpeexAudioFileReader.java,v 1.2 2004/10/21 16:21:58 mgimpel Exp $ */
package org.xiph.speex.spi;
import java.io.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.SequenceInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.sound.sampled.spi.AudioFileReader;
import org.xiph.speex.OggCrc;
/**
* Provider for Speex audio file reading services.
* This implementation can parse the format information from Speex audio file,
* and can produce audio input streams from files of this type.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class SpeexAudioFileReader
extends AudioFileReader
{
/** */
public static final int OGG_HEADERSIZE = 27;
/** The size of the Speex header. */
public static final int SPEEX_HEADERSIZE = 80;
/** */
public static final int SEGOFFSET = 26;
/** The String that identifies the beginning of an Ogg packet. */
public static final String OGGID = "OggS";
/** The String that identifies the beginning of the Speex header. */
public static final String SPEEXID = "Speex ";
/**
* Obtains the audio file format of the File provided.
* The File must point to valid audio file data.
* @param file the File from which file format information should be
* extracted.
* @return an AudioFileFormat object describing the audio file format.
* @exception UnsupportedAudioFileException if the File does not point to
* a valid audio file data recognized by the system.
* @exception IOException if an I/O exception occurs.
*/
public AudioFileFormat getAudioFileFormat(final File file)
throws UnsupportedAudioFileException, IOException
{
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
return getAudioFileFormat(inputStream, (int) file.length());
}
finally {
inputStream.close();
}
}
/**
* Obtains an audio input stream from the URL provided.
* The URL must point to valid audio file data.
* @param url the URL for which the AudioInputStream should be constructed.
* @return an AudioInputStream object based on the audio file data pointed to
* by the URL.
* @exception UnsupportedAudioFileException if the File does not point to
* a valid audio file data recognized by the system.
* @exception IOException if an I/O exception occurs.
*/
public AudioFileFormat getAudioFileFormat(final URL url)
throws UnsupportedAudioFileException, IOException
{
InputStream inputStream = url.openStream();
try {
return getAudioFileFormat(inputStream);
}
finally {
inputStream.close();
}
}
/**
* Obtains an audio input stream from the input stream provided.
* @param stream the input stream from which the AudioInputStream should be
* constructed.
* @return an AudioInputStream object based on the audio file data contained
* in the input stream.
* @exception UnsupportedAudioFileException if the File does not point to
* a valid audio file data recognized by the system.
* @exception IOException if an I/O exception occurs.
*/
public AudioFileFormat getAudioFileFormat(final InputStream stream)
throws UnsupportedAudioFileException, IOException
{
return getAudioFileFormat(stream, AudioSystem.NOT_SPECIFIED);
}
/**
* Return the AudioFileFormat from the given InputStream.
* @param stream the input stream from which the AudioInputStream should be
* constructed.
* @param medialength
* @return an AudioInputStream object based on the audio file data contained
* in the input stream.
* @exception UnsupportedAudioFileException if the File does not point to
* a valid audio file data recognized by the system.
* @exception IOException if an I/O exception occurs.
*/
protected AudioFileFormat getAudioFileFormat(final InputStream stream,
final int medialength)
throws UnsupportedAudioFileException, IOException
{
return getAudioFileFormat(stream, null, medialength);
}
/**
* Return the AudioFileFormat from the given InputStream. Implementation.
* @param bitStream
* @param baos
* @param mediaLength
* @return an AudioInputStream object based on the audio file data contained
* in the input stream.
* @exception UnsupportedAudioFileException if the File does not point to
* a valid audio file data recognized by the system.
* @exception IOException if an I/O exception occurs.
*/
protected AudioFileFormat getAudioFileFormat(final InputStream bitStream,
ByteArrayOutputStream baos,
final int mediaLength)
throws UnsupportedAudioFileException, IOException
{
AudioFormat format;
try {
// If we can't read the format of this stream, we must restore stream to
// beginning so other providers can attempt to read the stream.
if (bitStream.markSupported()) {
// maximum number of bytes to determine the stream encoding:
// Size of 1st Ogg Packet (Speex header) = OGG_HEADERSIZE + SPEEX_HEADERSIZE + 1
// Size of 2nd Ogg Packet (Comment) = OGG_HEADERSIZE + comment_size + 1
// Size of 3rd Ogg Header (First data) = OGG_HEADERSIZE + number_of_frames
// where number_of_frames < 256 and comment_size < 256 (if within 1 frame)
bitStream.mark(3*OGG_HEADERSIZE + SPEEX_HEADERSIZE + 256 + 256 + 2);
}
int mode = -1;
int sampleRate = 0;
int channels = 0;
int frameSize = AudioSystem.NOT_SPECIFIED;
float frameRate = AudioSystem.NOT_SPECIFIED;
byte[] header = new byte[128];
int segments = 0;
int bodybytes = 0;
DataInputStream dis = new DataInputStream(bitStream);
if (baos == null)
baos = new ByteArrayOutputStream(128);
int origchksum;
int chksum;
// read the OGG header
dis.readFully(header, 0, OGG_HEADERSIZE);
baos.write(header, 0, OGG_HEADERSIZE);
origchksum = readInt(header, 22);
header[22] = 0;
header[23] = 0;
header[24] = 0;
header[25] = 0;
chksum=OggCrc.checksum(0, header, 0, OGG_HEADERSIZE);
// make sure its a OGG header
if (!OGGID.equals(new String(header, 0, 4))) {
throw new UnsupportedAudioFileException("missing ogg id!");
}
// how many segments are there?
segments = header[SEGOFFSET] & 0xFF;
if (segments > 1) {
throw new UnsupportedAudioFileException("Corrupt Speex Header: more than 1 segments");
}
dis.readFully(header, OGG_HEADERSIZE, segments);
baos.write(header, OGG_HEADERSIZE, segments);
chksum=OggCrc.checksum(chksum, header, OGG_HEADERSIZE, segments);
// get the number of bytes in the segment
bodybytes = header[OGG_HEADERSIZE] & 0xFF;
if (bodybytes!=SPEEX_HEADERSIZE) {
throw new UnsupportedAudioFileException("Corrupt Speex Header: size=" + bodybytes);
}
// read the Speex header
dis.readFully(header, OGG_HEADERSIZE+1, bodybytes);
baos.write(header, OGG_HEADERSIZE+1, bodybytes);
chksum=OggCrc.checksum(chksum, header, OGG_HEADERSIZE+1, bodybytes);
// make sure its a Speex header
if (!SPEEXID.equals(new String(header, OGG_HEADERSIZE+1, 8))) {
throw new UnsupportedAudioFileException("Corrupt Speex Header: missing Speex ID");
}
mode = readInt(header, OGG_HEADERSIZE+1+40);
sampleRate = readInt(header, OGG_HEADERSIZE+1+36);
channels = readInt(header, OGG_HEADERSIZE+1+48);
int nframes = readInt(header, OGG_HEADERSIZE+1+64);
boolean vbr = readInt(header, OGG_HEADERSIZE+1+60) == 1;
// Checksum
if (chksum != origchksum)
throw new IOException("Ogg CheckSums do not match");
// Calculate frameSize
if (!vbr) {
// Frames size is a constant so:
// Read Comment Packet the Ogg Header of 1st data packet;
// the array table_segment repeats the frame size over and over.
}
// Calculate frameRate
if (mode >= 0 && mode <= 2 && nframes > 0) {
frameRate = ((float) sampleRate) /
((mode == 0 ? 160f : (mode == 1 ? 320f : 640f)) * ((float) nframes));
}
format = new AudioFormat(SpeexEncoding.SPEEX, (float)sampleRate,
AudioSystem.NOT_SPECIFIED, channels, frameSize,
frameRate, false);
}
catch(UnsupportedAudioFileException e) {
// reset the stream for other providers
if (bitStream.markSupported()) {
bitStream.reset();
}
// just rethrow this exception
throw e;
}
catch (IOException ioe) {
// reset the stream for other providers
if (bitStream.markSupported()) {
bitStream.reset();
}
throw new UnsupportedAudioFileException(ioe.getMessage());
}
return new AudioFileFormat(SpeexFileFormatType.SPEEX, format,
AudioSystem.NOT_SPECIFIED);
}
/**
* Obtains an audio input stream from the File provided.
* The File must point to valid audio file data.
* @param file the File for which the AudioInputStream should be constructed.
* @return an AudioInputStream object based on the audio file data pointed to
* by the File.
* @exception UnsupportedAudioFileException if the File does not point to
* a valid audio file data recognized by the system.
* @exception IOException if an I/O exception occurs.
*/
public AudioInputStream getAudioInputStream(final File file)
throws UnsupportedAudioFileException, IOException
{
InputStream inputStream = new FileInputStream(file);
try {
return getAudioInputStream(inputStream, (int) file.length());
}
catch (UnsupportedAudioFileException e) {
inputStream.close();
throw e;
}
catch (IOException e) {
inputStream.close();
throw e;
}
}
/**
* Obtains an audio input stream from the URL provided.
* The URL must point to valid audio file data.
* @param url the URL for which the AudioInputStream should be constructed.
* @return an AudioInputStream object based on the audio file data pointed to
* by the URL.
* @exception UnsupportedAudioFileException if the File does not point to
* a valid audio file data recognized by the system.
* @exception IOException if an I/O exception occurs.
*/
public AudioInputStream getAudioInputStream(final URL url)
throws UnsupportedAudioFileException, IOException
{
InputStream inputStream = url.openStream();
try {
return getAudioInputStream(inputStream);
}
catch (UnsupportedAudioFileException e) {
inputStream.close();
throw e;
}
catch (IOException e) {
inputStream.close();
throw e;
}
}
/**
* Obtains an audio input stream from the input stream provided.
* The stream must point to valid audio file data.
* @param stream the input stream from which the AudioInputStream should be
* constructed.
* @return an AudioInputStream object based on the audio file data contained
* in the input stream.
* @exception UnsupportedAudioFileException if the File does not point to
* a valid audio file data recognized by the system.
* @exception IOException if an I/O exception occurs.
*/
public AudioInputStream getAudioInputStream(final InputStream stream)
throws UnsupportedAudioFileException, IOException
{
return getAudioInputStream(stream, AudioSystem.NOT_SPECIFIED);
}
/**
* Obtains an audio input stream from the input stream provided.
* The stream must point to valid audio file data.
* @param inputStream the input stream from which the AudioInputStream should
* be constructed.
* @param medialength
* @return an AudioInputStream object based on the audio file data contained
* in the input stream.
* @exception UnsupportedAudioFileException if the File does not point to
* a valid audio file data recognized by the system.
* @exception IOException if an I/O exception occurs.
*/
protected AudioInputStream getAudioInputStream(final InputStream inputStream,
final int medialength)
throws UnsupportedAudioFileException, IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
AudioFileFormat audioFileFormat = getAudioFileFormat(inputStream,
baos,
medialength);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
SequenceInputStream sequenceInputStream = new SequenceInputStream(bais, inputStream);
return new AudioInputStream(sequenceInputStream,
audioFileFormat.getFormat(),
audioFileFormat.getFrameLength());
}
/**
* Converts Little Endian (Windows) bytes to an int (Java uses Big Endian).
* @param data the data to read.
* @param offset the offset from which to start reading.
* @return the integer value of the reassembled bytes.
*/
private static int readInt(final byte[] data, final int offset)
{
return (data[offset] & 0xff) |
((data[offset+1] & 0xff) << 8) |
((data[offset+2] & 0xff) << 16) |
(data[offset+3] << 24); // no & 0xff at the end to keep the sign
}
}
jspeex/src/java/org/xiph/speex/spi/SpeexAudioFileWriter.java 0000644 0001750 0001750 00000020003 10203125356 024376 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2003 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: SpeexAudioFileWriter.java *
* *
* Author: Marc GIMPEL *
* *
* Date: 12th July 2003 *
* *
******************************************************************************/
/* $Id: SpeexAudioFileWriter.java,v 1.3 2005/02/11 12:54:05 mgimpel Exp $ */
package org.xiph.speex.spi;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.FileOutputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.spi.AudioFileWriter;
/**
* Provider for Speex audio file writing services.
* This implementation can write Speex audio files from an audio stream.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.3 $
*/
public class SpeexAudioFileWriter
extends AudioFileWriter
{
/** */
public static final AudioFileFormat.Type[] NO_FORMAT = {};
/** */
public static final AudioFileFormat.Type[] SPEEX_FORMAT = {SpeexFileFormatType.SPEEX};
/**
* Obtains the file types for which file writing support is provided by this audio file writer.
* @return array of file types. If no file types are supported, an array of length 0 is returned.
*/
public AudioFileFormat.Type[] getAudioFileTypes()
{
return SPEEX_FORMAT;
}
/**
* Obtains the file types that this audio file writer can write from the
* audio input stream specified.
* @param stream - the audio input stream for which audio file type support
* is queried.
* @return array of file types. If no file types are supported, an array of
* length 0 is returned.
*/
public AudioFileFormat.Type[] getAudioFileTypes(final AudioInputStream stream)
{
if (stream.getFormat().getEncoding() instanceof SpeexEncoding) {
return SPEEX_FORMAT;
}
else {
return NO_FORMAT;
}
}
/**
* Writes a stream of bytes representing an audio file of the file type
* indicated to the output stream provided. Some file types require that the
* length be written into the file header, and cannot be written from start
* to finish unless the length is known in advance.
* An attempt to write such a file type will fail with an IOException if the
* length in the audio file format is AudioSystem.NOT_SPECIFIED.
* @param stream - the audio input stream containing audio data to be written
* to the output stream.
* @param fileType - file type to be written to the output stream.
* @param out - stream to which the file data should be written.
* @return the number of bytes written to the output stream.
* @exception IOException - if an I/O exception occurs.
* @exception IllegalArgumentException - if the file type is not supported by the system.
* @see #isFileTypeSupported(AudioFileFormat.Type, AudioInputStream)
* @see #getAudioFileTypes()
*/
public int write(final AudioInputStream stream,
final AudioFileFormat.Type fileType,
final OutputStream out)
throws IOException
{
AudioFileFormat.Type[] formats = getAudioFileTypes(stream);
if (formats != null && formats.length > 0) {
return write(stream, out);
}
else {
throw new IllegalArgumentException("cannot write given file type");
}
}
/**
* Writes a stream of bytes representing an audio file of the file format
* indicated to the external file provided.
* @param stream - the audio input stream containing audio data to be written
* to the file.
* @param fileType - file type to be written to the file.
* @param out - external file to which the file data should be written.
* @return the number of bytes written to the file.
* @exception IOException - if an I/O exception occurs.
* @exception IllegalArgumentException - if the file format is not supported by the system
* @see #isFileTypeSupported(javax.sound.sampled.AudioFileFormat.Type)
* @see #getAudioFileTypes()
*/
public int write(final AudioInputStream stream,
final AudioFileFormat.Type fileType,
final File out)
throws IOException
{
AudioFileFormat.Type[] formats = getAudioFileTypes(stream);
if (formats != null && formats.length > 0) {
FileOutputStream fos = new FileOutputStream(out);
return write(stream, fos);
}
else {
throw new IllegalArgumentException("cannot write given file type");
}
}
/**
* Writes a stream of bytes representing an audio file of the file type
* indicated to the output stream provided.
* @param stream - the audio input stream containing audio data to be written
* to the output stream.
* @param out - stream to which the file data should be written.
* @return the number of bytes written to the output stream.
* @exception IOException - if an I/O exception occurs.
*/
private int write(final AudioInputStream stream,
final OutputStream out)
throws IOException
{
byte[] data = new byte[2048];
int read = 0;
int temp;
while ((temp = stream.read(data, 0, 2048)) > 0) {
out.write(data, 0, temp);
read += temp;
}
/*
According to jsresources.org, the write() method is supposed to close the
File or FileOutputStream on completion.
Without it, people passing in a File parameter are not able to close the
file on their own, and hence can't delete the audio file (especially on
FAT32 systems), since it's still technically open.
*/
out.flush();
out.close();
return read;
}
}
jspeex/src/java/org/xiph/speex/spi/SpeexEncoding.java 0000644 0001750 0001750 00000020144 10135761046 023102 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2003 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: SpeexEncoding.java *
* *
* Author: Marc GIMPEL *
* *
* Date: 12th July 2003 *
* *
******************************************************************************/
/* $Id: SpeexEncoding.java,v 1.2 2004/10/21 16:21:58 mgimpel Exp $ */
package org.xiph.speex.spi;
import javax.sound.sampled.AudioFormat;
/**
* Encodings used by the Speex audio decoder.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class SpeexEncoding
extends AudioFormat.Encoding
{
/** Specifies any Speex encoding. */
public static final SpeexEncoding SPEEX = new SpeexEncoding("SPEEX");
/** Specifies constant bitrate, quality 0, Speex encoding. */
public static final SpeexEncoding SPEEX_Q0 =
new SpeexEncoding("SPEEX_quality_0", 0, false);
/** Specifies constant bitrate, quality 1, Speex encoding. */
public static final SpeexEncoding SPEEX_Q1 =
new SpeexEncoding("SPEEX_quality_1", 1, false);
/** Specifies constant bitrate, quality 2, Speex encoding. */
public static final SpeexEncoding SPEEX_Q2 =
new SpeexEncoding("SPEEX_quality_2", 2, false);
/** Specifies constant bitrate, quality 3, Speex encoding. */
public static final SpeexEncoding SPEEX_Q3 =
new SpeexEncoding("SPEEX_quality_3", 3, false);
/** Specifies constant bitrate, quality 4, Speex encoding. */
public static final SpeexEncoding SPEEX_Q4 =
new SpeexEncoding("SPEEX_quality_4", 4, false);
/** Specifies constant bitrate, quality 5, Speex encoding. */
public static final SpeexEncoding SPEEX_Q5 =
new SpeexEncoding("SPEEX_quality_5", 5, false);
/** Specifies constant bitrate, quality 6, Speex encoding. */
public static final SpeexEncoding SPEEX_Q6 =
new SpeexEncoding("SPEEX_quality_6", 6, false);
/** Specifies constant bitrate, quality 7, Speex encoding. */
public static final SpeexEncoding SPEEX_Q7 =
new SpeexEncoding("SPEEX_quality_7", 7, false);
/** Specifies constant bitrate, quality 8, Speex encoding. */
public static final SpeexEncoding SPEEX_Q8 =
new SpeexEncoding("SPEEX_quality_8", 8, false);
/** Specifies constant bitrate, quality 9, Speex encoding. */
public static final SpeexEncoding SPEEX_Q9 =
new SpeexEncoding("SPEEX_quality_9", 9, false);
/** Specifies constant bitrate, quality 10, Speex encoding. */
public static final SpeexEncoding SPEEX_Q10 =
new SpeexEncoding("SPEEX_quality_10", 10, false);
/** Specifies variable bitrate, quality 0, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR0 =
new SpeexEncoding("SPEEX_VBR_quality_0", 0, true);
/** Specifies variable bitrate, quality 1, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR1 =
new SpeexEncoding("SPEEX_VBR_quality_1", 1, true);
/** Specifies variable bitrate, quality 2, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR2 =
new SpeexEncoding("SPEEX_VBR_quality_2", 2, true);
/** Specifies variable bitrate, quality 3, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR3 =
new SpeexEncoding("SPEEX_VBR_quality_3", 3, true);
/** Specifies variable bitrate, quality 4, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR4 =
new SpeexEncoding("SPEEX_VBR_quality_4", 4, true);
/** Specifies variable bitrate, quality 5, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR5 =
new SpeexEncoding("SPEEX_VBR_quality_5", 5, true);
/** Specifies variable bitrate, quality 6, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR6 =
new SpeexEncoding("SPEEX_VBR_quality_6", 6, true);
/** Specifies variable bitrate, quality 7, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR7 =
new SpeexEncoding("SPEEX_VBR_quality_7", 7, true);
/** Specifies variable bitrate, quality 8, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR8 =
new SpeexEncoding("SPEEX_VBR_quality_8", 8, true);
/** Specifies variable bitrate, quality 9, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR9 =
new SpeexEncoding("SPEEX_VBR_quality_9", 9, true);
/** Specifies variable bitrate, quality 10, Speex encoding. */
public static final SpeexEncoding SPEEX_VBR10 =
new SpeexEncoding("SPEEX_VBR_quality_10", 10, true);
/** Default quality setting for the Speex encoding. */
public static final int DEFAULT_QUALITY = 3;
/** Default VBR setting for the Speex encoding. */
public static final boolean DEFAULT_VBR = false;
/** Quality setting for the Speex encoding. */
protected int quality;
/** Defines whether or not the encoding is Variable Bit Rate. */
protected boolean vbr;
/**
* Constructs a new encoding.
* @param name - Name of the Speex encoding.
* @param quality - Quality setting for the Speex encoding.
* @param vbr - Defines whether or not the encoding is Variable Bit Rate.
*/
public SpeexEncoding(final String name,
final int quality,
final boolean vbr)
{
super(name);
this.quality = quality;
this.vbr = vbr;
}
/**
* Constructs a new encoding.
* @param name - Name of the Speex encoding.
*/
public SpeexEncoding(final String name)
{
this(name, DEFAULT_QUALITY, DEFAULT_VBR);
}
/**
* Returns the quality setting for the Speex encoding.
* @return the quality setting for the Speex encoding.
*/
public int getQuality()
{
return quality;
}
/**
* Returns whether or not the encoding is Variable Bit Rate.
* @return whether or not the encoding is Variable Bit Rate.
*/
public boolean isVBR()
{
return vbr;
}
}
jspeex/src/java/org/xiph/speex/spi/SpeexFileFormatType.java 0000644 0001750 0001750 00000006644 10135761046 024257 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2003 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: SpeexFileFormatType.java *
* *
* Author: Marc GIMPEL *
* *
* Date: 12th July 2003 *
* *
******************************************************************************/
/* $Id: SpeexFileFormatType.java,v 1.2 2004/10/21 16:21:58 mgimpel Exp $ */
package org.xiph.speex.spi;
import javax.sound.sampled.AudioFileFormat;
/**
* FileFormatTypes used by the Speex audio decoder.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class SpeexFileFormatType
extends AudioFileFormat.Type
{
/**
* Specifies an OGG Speex file.
*/
public static final AudioFileFormat.Type SPEEX =new SpeexFileFormatType("SPEEX", "spx");
/**
* Constructs a file type.
* @param name - the name of the Speex File Format.
* @param extension - the file extension for this Speex File Format.
*/
public SpeexFileFormatType(final String name, final String extension)
{
super(name, extension);
}
}
jspeex/src/java/org/xiph/speex/spi/SpeexFormatConvertionProvider.java 0000644 0001750 0001750 00000035564 10135761046 026402 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2003 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: SpeexFormatConvertionProvider.java *
* *
* Author: Marc GIMPEL *
* *
* Date: 12th July 2003 *
* *
******************************************************************************/
/* $Id: SpeexFormatConvertionProvider.java,v 1.2 2004/10/21 16:21:58 mgimpel Exp $ */
package org.xiph.speex.spi;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.spi.FormatConversionProvider;
/**
* A format conversion provider provides format conversion services from one or
* more input formats to one or more output formats. Converters include codecs,
* which encode and/or decode audio data, as well as transcoders, etc.
* Format converters provide methods for determining what conversions are
* supported and for obtaining an audio stream from which converted data can be
* read.
*
* The source format represents the format of the incoming audio data, which
* will be converted.
*
* The target format represents the format of the processed, converted audio
* data. This is the format of the data that can be read from the stream
* returned by one of the getAudioInputStream methods.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class SpeexFormatConvertionProvider
extends FormatConversionProvider
{
/** */
public static final AudioFormat.Encoding[] NO_ENCODING = {};
/** */
public static final AudioFormat.Encoding[] PCM_ENCODING =
{AudioFormat.Encoding.PCM_SIGNED};
/** */
public static final AudioFormat.Encoding[] SPEEX_ENCODING =
{SpeexEncoding.SPEEX};
/** */
public static final AudioFormat.Encoding[] BOTH_ENCODINGS =
{SpeexEncoding.SPEEX, AudioFormat.Encoding.PCM_SIGNED};
/** */
public static final AudioFormat[] NO_FORMAT = {};
/**
* Obtains the set of source format encodings from which format conversion
* services are provided by this provider.
* @return array of source format encodings.
* The array will always have a length of at least 1.
*/
public AudioFormat.Encoding[] getSourceEncodings()
{
AudioFormat.Encoding[] encodings = {SpeexEncoding.SPEEX,
AudioFormat.Encoding.PCM_SIGNED};
return encodings;
}
/**
* Obtains the set of target format encodings to which format conversion
* services are provided by this provider.
* @return array of target format encodings.
* The array will always have a length of at least 1.
*/
public AudioFormat.Encoding[] getTargetEncodings()
{
AudioFormat.Encoding[] encodings = {SpeexEncoding.SPEEX_Q0,
SpeexEncoding.SPEEX_Q1,
SpeexEncoding.SPEEX_Q2,
SpeexEncoding.SPEEX_Q3,
SpeexEncoding.SPEEX_Q4,
SpeexEncoding.SPEEX_Q5,
SpeexEncoding.SPEEX_Q6,
SpeexEncoding.SPEEX_Q7,
SpeexEncoding.SPEEX_Q8,
SpeexEncoding.SPEEX_Q9,
SpeexEncoding.SPEEX_Q10,
SpeexEncoding.SPEEX_VBR0,
SpeexEncoding.SPEEX_VBR1,
SpeexEncoding.SPEEX_VBR2,
SpeexEncoding.SPEEX_VBR3,
SpeexEncoding.SPEEX_VBR4,
SpeexEncoding.SPEEX_VBR5,
SpeexEncoding.SPEEX_VBR6,
SpeexEncoding.SPEEX_VBR7,
SpeexEncoding.SPEEX_VBR8,
SpeexEncoding.SPEEX_VBR9,
SpeexEncoding.SPEEX_VBR10,
AudioFormat.Encoding.PCM_SIGNED};
return encodings;
}
/**
* Obtains the set of target format encodings supported by the format
* converter given a particular source format. If no target format encodings
* are supported for this source format, an array of length 0 is returned.
* @param sourceFormat format of the incoming data.
* @return array of supported target format encodings.
*/
public AudioFormat.Encoding[] getTargetEncodings(final AudioFormat sourceFormat)
{
if (sourceFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)) {
AudioFormat.Encoding[] encodings = {SpeexEncoding.SPEEX_Q0,
SpeexEncoding.SPEEX_Q1,
SpeexEncoding.SPEEX_Q2,
SpeexEncoding.SPEEX_Q3,
SpeexEncoding.SPEEX_Q4,
SpeexEncoding.SPEEX_Q5,
SpeexEncoding.SPEEX_Q6,
SpeexEncoding.SPEEX_Q7,
SpeexEncoding.SPEEX_Q8,
SpeexEncoding.SPEEX_Q9,
SpeexEncoding.SPEEX_Q10,
SpeexEncoding.SPEEX_VBR0,
SpeexEncoding.SPEEX_VBR1,
SpeexEncoding.SPEEX_VBR2,
SpeexEncoding.SPEEX_VBR3,
SpeexEncoding.SPEEX_VBR4,
SpeexEncoding.SPEEX_VBR5,
SpeexEncoding.SPEEX_VBR6,
SpeexEncoding.SPEEX_VBR7,
SpeexEncoding.SPEEX_VBR8,
SpeexEncoding.SPEEX_VBR9,
SpeexEncoding.SPEEX_VBR10};
return encodings;
}
else if (sourceFormat.getEncoding() instanceof SpeexEncoding) {
AudioFormat.Encoding[] encodings = {AudioFormat.Encoding.PCM_SIGNED};
return encodings;
}
else {
AudioFormat.Encoding[] encodings = {};
return encodings;
}
}
/**
* Obtains the set of target formats with the encoding specified supported by
* the format converter. If no target formats with the specified encoding are
* supported for this source format, an array of length 0 is returned.
* @param targetEncoding desired encoding of the outgoing data.
* @param sourceFormat format of the incoming data.
* @return array of supported target formats.
*/
public AudioFormat[] getTargetFormats(final AudioFormat.Encoding targetEncoding,
final AudioFormat sourceFormat)
{
if (sourceFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED) &&
targetEncoding instanceof SpeexEncoding) {
if (sourceFormat.getChannels() > 2 || sourceFormat.getChannels() <= 0 ||
sourceFormat.isBigEndian()) {
AudioFormat[] formats = {};
return formats;
}
else {
AudioFormat[] formats = {new AudioFormat(targetEncoding,
sourceFormat.getSampleRate(),
-1, // sample size in bits
sourceFormat.getChannels(),
-1, // frame size
-1, // frame rate
false)}; // little endian
return formats;
}
}
else if (sourceFormat.getEncoding() instanceof SpeexEncoding &&
targetEncoding.equals(AudioFormat.Encoding.PCM_SIGNED)) {
AudioFormat[] formats = {new AudioFormat(sourceFormat.getSampleRate(),
16, // sample size in bits
sourceFormat.getChannels(),
true, // signed
false)}; // little endian (for PCM wav)
return formats;
}
else {
AudioFormat[] formats = {};
return formats;
}
}
/**
* Obtains an audio input stream with the specified encoding from the given
* audio input stream.
* @param targetEncoding - desired encoding of the stream after processing.
* @param sourceStream - stream from which data to be processed should be read.
* @return stream from which processed data with the specified target
* encoding may be read.
* @exception IllegalArgumentException - if the format combination supplied
* is not supported.
*/
public AudioInputStream getAudioInputStream(final AudioFormat.Encoding targetEncoding,
final AudioInputStream sourceStream)
{
if (isConversionSupported(targetEncoding, sourceStream.getFormat())) {
AudioFormat[] formats = getTargetFormats(targetEncoding,
sourceStream.getFormat());
if (formats != null && formats.length > 0) {
AudioFormat sourceFormat = sourceStream.getFormat();
AudioFormat targetFormat = formats[0];
if (sourceFormat.equals(targetFormat)) {
return sourceStream;
}
else if (sourceFormat.getEncoding() instanceof SpeexEncoding &&
targetFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)) {
return new Speex2PcmAudioInputStream(sourceStream, targetFormat, -1);
}
else if (sourceFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED) &&
targetFormat.getEncoding() instanceof SpeexEncoding) {
return new Pcm2SpeexAudioInputStream(sourceStream, targetFormat, -1);
}
else {
throw new IllegalArgumentException("unable to convert " + sourceFormat.toString() +
" to " + targetFormat.toString());
}
}
else {
throw new IllegalArgumentException("target format not found");
}
}
else {
throw new IllegalArgumentException("conversion not supported");
}
}
/**
* Obtains an audio input stream with the specified format from the given
* audio input stream.
* @param targetFormat - desired data format of the stream after processing.
* @param sourceStream - stream from which data to be processed should be read.
* @return stream from which processed data with the specified format may be
* read.
* @exception IllegalArgumentException - if the format combination supplied
* is not supported.
*/
public AudioInputStream getAudioInputStream(final AudioFormat targetFormat,
final AudioInputStream sourceStream)
{
if (isConversionSupported(targetFormat, sourceStream.getFormat())) {
AudioFormat[] formats = getTargetFormats(targetFormat.getEncoding(),
sourceStream.getFormat());
if (formats != null && formats.length > 0) {
AudioFormat sourceFormat = sourceStream.getFormat();
if (sourceFormat.equals(targetFormat)) {
return sourceStream;
}
else if (sourceFormat.getEncoding() instanceof SpeexEncoding &&
targetFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED)) {
return new Speex2PcmAudioInputStream(sourceStream, targetFormat, -1);
}
else if (sourceFormat.getEncoding().equals(AudioFormat.Encoding.PCM_SIGNED) &&
targetFormat.getEncoding() instanceof SpeexEncoding) {
return new Pcm2SpeexAudioInputStream(sourceStream, targetFormat, -1);
}
else {
throw new IllegalArgumentException("unable to convert " + sourceFormat.toString() +
" to " + targetFormat.toString());
}
}
else {
throw new IllegalArgumentException("target format not found");
}
}
else {
throw new IllegalArgumentException("conversion not supported");
}
}
}
jspeex/src/java/org/xiph/speex/spi/package.html 0000644 0001750 0001750 00000000451 10135522576 021767 0 ustar twerner twerner
* +----+
* |Init|
* +----+
* V
* ----->+----+
* / >|Stop| \
* / / +----+< \
* / / \ V
* | +-----+ ---> +----+
* | |Pause| |Play|
* | +-----+ <--- +----+
* \ A / A
* \ \+----+< /
* ------|Buff| /
* +----+
*
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class Player
extends JPanel
implements ActionListener
{
/** Build Number */
public static final String BUILD = "@BUILD@";
/** Version Number */
public static final String VERSION = "@VERSION@";
/** Revision Number */
public static final String REVISION = "$Revision: 1.2 $";
// Possible States for the Finite State Machine
/** Finite State Machine State: Initialised */
protected static final int STATE_INIT = 0;
/** Finite State Machine State: Stopped */
protected static final int STATE_STOPPED = 1;
/** Finite State Machine State: Playing */
protected static final int STATE_PLAYING = 2;
/** Finite State Machine State: Paused */
protected static final int STATE_PAUSED = 3;
/** Finite State Machine State: Buffering */
protected static final int STATE_BUFFERING = 4;
/** Finite State Machine State: Error */
protected static final int STATE_ERROR = 5;
/** The Players Scroll Panel */
protected JPanel playerScrollPane;
/** The Players Button Panel */
protected JPanel playerButtonPane;
/** Play Button */
protected JButton playButton;
/** Pause Button */
protected JButton pauseButton;
/** Stop Button */
protected JButton stopButton;
/** Progress Bar */
protected JSlider progressBar;
protected Timer timer;
/** Current State of the Finite State Machine */
protected int state;
/** Previous State of the Finite State Machine */
protected int oldstate;
protected String audioFilename;
protected URL audioFile;
protected int audioLength;
protected Playback playback;
//--------------------------------------------------------------------------
// Initialization code
//--------------------------------------------------------------------------
/**
* Command Line entrance.
* @param args
*/
public static void main(final String[] args)
{
String filename = null;
if (args.length > 0) {
filename = args[0];
}
final Player player = new Player(filename);
player.init();
JFrame frame = new JFrame("Player");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
public void windowDeiconified(WindowEvent e) { player.start(); }
public void windowIconified(WindowEvent e) { player.stop(); }
});
frame.getContentPane().add("Center", player);
frame.pack();
frame.setVisible(true);
}
/**
* Build a Player.
* @param file
*/
public Player(final String file)
{
this.audioFilename = file;
createGUI();
}
/**
* Initialize the Player Component.
*/
public void init()
{
state = STATE_STOPPED;
if (!audioFilename.startsWith("http://") &&
!audioFilename.startsWith("file:/"))
audioFilename = "file:/" + audioFilename;
try {
audioFile = new URL(audioFilename);
}
catch (MalformedURLException e) {
e.printStackTrace();
}
audioLength = 0;
playback = new Playback();
}
/**
* Start the Player Component.
*/
public void start()
{
}
/**
* Stop the Player Component.
*/
public void stop()
{
if (state != STATE_STOPPED) {
stopButton.doClick();
}
}
/**
* Returns an InputSteam containing the Audio to playback.
* @return an InputSteam containing the Audio to playback.
* @throws IOException
*/
protected InputStream getAudioStream()
throws IOException
{
return new BufferedInputStream(audioFile.openStream());
}
//--------------------------------------------------------------------------
// Running code
//--------------------------------------------------------------------------
/**
* Playback thread
*/
protected class Playback
implements Runnable
{
protected InputStream audioStream;
protected AudioInputStream audioInputStream;
protected AudioFormat audioFormat;
protected DataLine.Info info;
protected SourceDataLine line;
protected byte[] buffer;
protected int written;
protected int read;
protected Thread thread;
/**
* Start the playback thread which fills the JavaSound playback buffer.
*/
protected void start() {
thread = new Thread(this);
thread.setName("Playback");
thread.start();
}
/**
* Stop the playback thread and destroy all resources.
*/
protected void stop() {
// Stop the thread
thread = null;
// Close the line
if (line != null) {
line.stop();
line.close();
line = null;
}
// Close the audio InputStream
try {
audioInputStream.close();
}
catch (IOException e) {
e.printStackTrace();
}
audioInputStream = null;
}
/**
* Setup the JavaSound System to play the Audio.
*/
protected void setupSound()
{
// We have to read in the sound file.
try {
audioStream = getAudioStream();
if (audioStream instanceof AudioInputStream) {
audioInputStream = (AudioInputStream) audioStream;
}
else {
audioInputStream = AudioSystem.getAudioInputStream(audioStream);
}
}
catch (Exception e) {
e.printStackTrace();
}
/*
From the AudioInputStream, i.e. from the sound file,
we fetch information about the format of the audio data.
This information includes the sampling frequency, the number of channels
and the size of the samples.
This information is needed to ask JavaSound for a suitable output line
for this audio file.
*/
audioFormat = audioInputStream.getFormat();
/*
Asking for a line is a rather tricky thing.
We have to construct an Info object that specifies the desired properties
for the line.
First, we have to say which kind of line we want. The possibilities are:
SourceDataLine (for playback), Clip (for repeated playback) and
TargetDataLine (for recording).
Here, we want to do normal playback, so we ask for a SourceDataLine.
Then, we have to pass an AudioFormat object, so that the Line knows which
format the data passed to it will have.
Furthermore, we can give JavaSound a hint about how big the internal
buffer for the line should be. This isn't used here, signaling that we
don't care about the exact size. JavaSound will use some default value
for the buffer size.
*/
info = new DataLine.Info(SourceDataLine.class, audioFormat);
// If the audioFormat is not directly supported
if (!AudioSystem.isLineSupported(info)) {
AudioFormat sourceFormat = audioFormat;
AudioFormat targetFormat = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
sourceFormat.getSampleRate(),
16,
sourceFormat.getChannels(),
sourceFormat.getChannels() * 2,
sourceFormat.getSampleRate(),
false);
audioInputStream = AudioSystem.getAudioInputStream(targetFormat, audioInputStream);
audioFormat = audioInputStream.getFormat();
info = new DataLine.Info(SourceDataLine.class, audioFormat);
}
// We have to open the line for it to be ready to receive audio data.
try {
line = (SourceDataLine) AudioSystem.getLine(info);
// We have to open the line for it to be ready to receive audio data.
line.open(audioFormat);
}
catch (LineUnavailableException e) {
e.printStackTrace();
}
catch (Exception e) {
e.printStackTrace();
}
// setup copying buffer
buffer = new byte[128000];
written = 0;
read = 0;
}
/**
* The code that runs in the thread and fills the JavaSound playback buffer.
* Implemented from Runnable interface.
*/
public void run()
{
while (thread != null && state == STATE_PLAYING && read != -1) {
if (written >= read) {
try {
read = audioInputStream.read(buffer, 0, buffer.length);
written = 0;
}
catch (IOException e) {
e.printStackTrace();
}
}
if (read > written) {
int temp = line.write(buffer, written, read-written);
written += temp;
}
}
if (state == STATE_PLAYING) {
/*
Wait until all data are played. This is only necessary because of the
bug noted below. (If we do not wait, we would interrupt the playback by
prematurely closing the line and exiting the VM.)
*/
line.drain();
// All data are played. We can close the shop.
line.close();
stopButton.doClick();
}
}
} // End class Playback
//--------------------------------------------------------------------------
// Action processing code
//--------------------------------------------------------------------------
/**
* Process Actions when button are pressed.
* Implemented from ActionListener interface.
* @param e
*/
public void actionPerformed(final ActionEvent e) {
if (e.getSource() == timer) {
progressBar.setValue(getProgress());
}
else {
if ("Play".equals(e.getActionCommand())) {
playIt();
}
else if ("Pause".equals(e.getActionCommand())) {
if (state == STATE_PAUSED) {
playIt();
}
else if (state == STATE_PLAYING) {
pauseIt();
}
}
else if ("Stop".equals(e.getActionCommand())) {
stopIt();
}
else {
}
}
}
/**
*
*/
public synchronized void stopIt()
{
playButton.setEnabled(false);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
oldstate = state;
state = STATE_STOPPED;
playback.stop();
timer.stop();
progressBar.setValue(0);
playButton.setEnabled(true);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
}
/**
*
*/
public synchronized void playIt()
{
playButton.setEnabled(false);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
oldstate = state;
state = STATE_PLAYING;
if (oldstate == STATE_STOPPED) {
playback.setupSound();
}
if (playback.thread == null || !playback.thread.isAlive()) {
playback.start();
}
playback.line.start();
timer.start();
playButton.setEnabled(false);
pauseButton.setEnabled(true);
stopButton.setEnabled(true);
}
/**
* Pause
*/
public synchronized void pauseIt()
{
playButton.setEnabled(false);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
oldstate = state;
state = STATE_PAUSED;
playback.line.stop();
timer.stop();
playButton.setEnabled(true);
pauseButton.setEnabled(true);
stopButton.setEnabled(true);
}
/**
* Return the progress of the playback.
* @return the progress of the playback.
*/
protected int getProgress()
{
audioLength = 500000;
if (state == STATE_PLAYING || state == STATE_PAUSED) {
return playback.line.getFramePosition() * 1000 / audioLength;
}
else {
return 0;
}
}
//--------------------------------------------------------------------------
// GUI code
//--------------------------------------------------------------------------
/**
* Create GUI for the player.
* The player panel that should look something like this:
*
* +-----------------------+
* | ----|-------------- | Scroll Panel
* +-----------------------+
* | +----+ +-----+ +----+ |
* | |play| |pause| |stop| | Button Panel
* | +----+ +-----+ +----+ |
* +-----------------------+
*
*/
protected void createGUI()
{
// Create a player panel:
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
playerScrollPane = new JPanel(new FlowLayout());
playerButtonPane = new JPanel(new FlowLayout());
createScrollPanel();
createButtonPanel();
add(playerScrollPane);
add(playerButtonPane);
setBorder(BorderFactory.createMatteBorder(1,1,1,1,Color.black));
}
/**
* Create the ScrollPanel for the player.
* The player scroll panel that should look something like this:
*
* +-----------------------+
* | ----|-------------- | Scroll Panel
* +-----------------------+
*
*/
protected void createScrollPanel()
{
// Create the Progress Bar
progressBar = new JSlider();
progressBar.setMinimum(0);
progressBar.setMaximum(1000);
progressBar.setValue(0);
progressBar.setPreferredSize(new Dimension(120, 16));
progressBar.setBackground(Color.WHITE);
progressBar.setEnabled(false);
playerScrollPane.add(progressBar);
playerScrollPane.setBackground(Color.WHITE);
// Create the timer
timer = new Timer(100, this);
}
/**
* Create the ButtonPanel for the player.
* The player button panel that should look something like this:
*
* +-----------------------+
* | +----+ +-----+ +----+ |
* | |play| |pause| |stop| | Button Panel
* | +----+ +-----+ +----+ |
* +-----------------------+
*
*/
protected void createButtonPanel()
{
playButton = buildButton("Play", "Play",
"/images/player_play.gif",
"/images/player_play2.gif",
"/images/player_play3.gif", this);
playerButtonPane.add(playButton);
pauseButton = buildButton("Pause", "Pause",
"/images/player_pause.gif",
"/images/player_pause2.gif",
"/images/player_pause3.gif", this);
playerButtonPane.add(pauseButton);
stopButton = buildButton("Stop", "Stop",
"/images/player_stop.gif",
"/images/player_stop2.gif",
"/images/player_stop3.gif", this);
playerButtonPane.add(stopButton);
playerButtonPane.setBackground(Color.WHITE);
// Make the player panel the content pane.
setOpaque(true);
}
/**
* Build a Button.
* @param actionCommand
* @param toolTip
* @param pathIconDefault
* @param pathIconDisabled
* @param pathIconRollover
* @param listener
* @return the Button that was built.
*/
protected static JButton buildButton(final String actionCommand,
final String toolTip,
final String pathIconDefault,
final String pathIconDisabled,
final String pathIconRollover,
final ActionListener listener)
{
ImageIcon IconDefault = createImageIcon(pathIconDefault);
ImageIcon IconDisabled = createImageIcon(pathIconDisabled);
ImageIcon IconRollover = createImageIcon(pathIconRollover);
JButton button = new JButton();
button.setActionCommand(actionCommand);
button.setToolTipText(toolTip);
button.setIcon(IconDefault);
button.setDisabledIcon(IconDisabled);
button.setRolloverIcon(IconRollover);
button.setBorderPainted(false);
button.setBackground(Color.WHITE);
button.setPreferredSize(new Dimension(IconDefault.getIconWidth(),
IconDefault.getIconHeight()));
button.addActionListener(listener);
return button;
}
/**
* Returns an ImageIcon, or null if the path was invalid.
* @param path
* @return an ImageIcon, or null if the path was invalid.
*/
protected static ImageIcon createImageIcon(final String path)
{
URL imgURL = Player.class.getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL);
}
else {
System.err.println("Couldn't find file: " + path);
System.err.println("path: " + Player.class.getResource("."));
return null;
}
}
}
jspeex/src/java/org/xiph/speex/player/PlayerApplet.java 0000644 0001750 0001750 00000012243 10135761046 023453 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2004 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: PlayerApplet.java *
* *
* Author: Marc GIMPEL *
* *
* Date: Jun 8, 2004 *
* *
******************************************************************************/
/* $Id: PlayerApplet.java,v 1.2 2004/10/21 16:21:58 mgimpel Exp $ */
package org.xiph.speex.player;
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
/**
* JavaSound Player Applet.
* This is simply a Player Panel placed inside an applet.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class PlayerApplet
extends JApplet
{
private Player player;
private String filename;
/**
* Initialize Applet.
* Called by the browser or applet viewer to inform this applet that it has
* been loaded into the system. It is always called before the first time
* that the start
method is called.
*/
public void init()
{
System.out.println("****** Player Applet starting, copyright Wimba 2004");
System.out.println("****** Version: " + Player.VERSION +
", Revision: " + Player.REVISION +
", build: " + Player.BUILD);
filename = getParameter("file");
// Some initialising should be done on the event-dispatching thread.
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
initGUI();
}
});
}
catch (Exception e) {
System.err.println("couldn't successfully initialise from event thread");
}
// The rest of the initialising is done from the applet launching thread.
player.init();
}
/**
* Initialize Applet, but run from the event-dispatching thread.
*/
public void initGUI()
{
player = new Player(filename);
setContentPane(player);
}
/**
* Called by the browser or applet viewer to inform this applet that it
* should start its execution. It is called after the init
* method and each time the applet is revisited in a Web page.
*/
public void start() {
player.start();
}
/**
* Called by the browser or applet viewer to inform this applet that it
* should stop its execution. It is called when the Web page that contains
* this applet has been replaced by another page, and also just before the
* applet is to be destroyed.
*/
public void stop() {
player.stop();
}
/**
* Called by the browser or applet viewer to inform this applet that it is
* being reclaimed and that it should destroy any resources that it has
* allocated. The stop
method will always be called before
* destroy
.
*/
public void destroy()
{
player = null;
}
}
jspeex/src/java/org/xiph/speex/player/Recorder.java 0000644 0001750 0001750 00000036044 10135761046 022623 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2004 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: Recorder.java *
* *
* Author: Marc GIMPEL *
* *
* Date: Jun 1, 2004 *
* *
******************************************************************************/
/* $Id: Recorder.java,v 1.2 2004/10/21 16:21:58 mgimpel Exp $ */
package org.xiph.speex.player;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import javax.swing.JButton;
import javax.swing.JFrame;
import org.xiph.speex.spi.SpeexEncoding;
/**
* JavaSound Recorder.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class Recorder
extends Player
{
/** Revision Number */
public static final String REVISION = "$Revision: 1.2 $";
/** Audio sampled at 8 kHz (telephone quality). */
public static final String SAMPLERATE_8KHZ = "8000 Hz";
/** Audio sampled at 11 kHz. */
public static final String SAMPLERATE_11KHZ = "11025 Hz";
/** Audio sampled at 16 kHz (wideband). */
public static final String SAMPLERATE_16KHZ = "16000 Hz";
/** Audio sampled at 22 kHz (FM radio quality). */
public static final String SAMPLERATE_22KHZ = "22050 Hz";
/** Audio sampled at 32 kHz (ultra-wideband). */
public static final String SAMPLERATE_32KHZ = "32000 Hz";
/** Audio sampled at 44 kHz (CD quality). */
public static final String SAMPLERATE_44KHZ = "44100 Hz";
/** Mono Audio (1 channel). */
public static final String CHANNELS_MONO = "mono";
/** Stereo Audio (2 channels). */
public static final String CHANNELS_STEREO = "stereo";
// Possible States for the Finite State Machine
/** Finite State Machine State: Recording */
protected static final int STATE_RECORDING = 6;
/** Finite State Machine State: Recording Paused */
protected static final int STATE_REC_PAUSED = 7;
/** Record Button */
protected JButton recordButton;
protected Capture capture;
protected byte[] audio;
//--------------------------------------------------------------------------
// Initialization code
//--------------------------------------------------------------------------
/**
* Command Line entrance.
* @param args
*/
public static void main(final String[] args)
{
String filename = null;
if (args.length > 0) {
filename = args[0];
}
final Recorder recorder = new Recorder(filename);
recorder.init();
JFrame frame = new JFrame("Recorder");
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {System.exit(0);}
public void windowDeiconified(WindowEvent e) { recorder.start(); }
public void windowIconified(WindowEvent e) { recorder.stop(); }
});
frame.getContentPane().add("Center", recorder);
frame.pack();
frame.setVisible(true);
}
/**
* Build a Recorder.
* @param file
*/
public Recorder(final String file)
{
super(file);
}
/**
* Initialize the Player Component.
*/
public void init()
{
super.init();
capture = new Capture();
}
/**
* Returns an InputSteam containing the Audio to playback.
* @return an InputSteam containing the Audio to playback.
* @throws IOException
*/
protected InputStream getAudioStream()
throws IOException
{
if (audio == null) {
return new BufferedInputStream(audioFile.openStream());
}
else {
return new ByteArrayInputStream(audio);
}
}
//--------------------------------------------------------------------------
// Running code
//--------------------------------------------------------------------------
/**
* Capture thread
*/
protected class Capture
implements Runnable
{
protected ByteArrayOutputStream out;
protected AudioInputStream audioInputStream;
protected AudioFormat audioFormat;
protected DataLine.Info info;
protected AudioFileFormat.Type targetType;
protected TargetDataLine line;
protected byte[] buffer;
protected Thread thread;
/**
* Start the playback thread which fills the JavaSound playback buffer.
*/
protected void start() {
thread = new Thread(this);
thread.setName("Capture");
thread.start();
}
/**
* Stop the playback thread and destroy all resources.
*/
protected void stop() {
// Stop the thread
thread = null;
// Close the line
if (line != null) {
line.stop();
line.close();
line = null;
}
// stop and close the output stream
try {
out.flush();
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
// load bytes into the audio input stream for playback
audio = out.toByteArray();
System.out.println("size="+audio.length);
}
/**
* Setup the JavaSound System to play the Audio.
*/
protected void setupSound()
{
// define the required attributes for our line,
// and make sure a compatible line is supported.
audioFormat = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED,
44100.0F, 16, 1, 2, 44100.0F, false);
/*
Now, we are trying to get a TargetDataLine. The TargetDataLine is used
later to read audio data from it.
If requesting the line was successful, we are opening it (important!).
*/
info = new DataLine.Info(TargetDataLine.class, audioFormat);
try {
line = (TargetDataLine) AudioSystem.getLine(info);
line.open(audioFormat);
}
catch (LineUnavailableException e) {
e.printStackTrace();
}
catch (SecurityException e) {
// An applet requires the following permissions to record audio:
// permission javax.sound.sampled.AudioPermission : record
e.printStackTrace();
}
audioInputStream = new AudioInputStream(line);
AudioFormat targetFormat = new AudioFormat(SpeexEncoding.SPEEX_Q6,
audioFormat.getSampleRate(),
-1, // sample size in bits
audioFormat.getChannels(),
-1, // frame size
-1, // frame rate
false); // little endian
audioInputStream = AudioSystem.getAudioInputStream(targetFormat, audioInputStream);
audioFormat = audioInputStream.getFormat();
out = new ByteArrayOutputStream();
// setup copying buffer
buffer = new byte[128000];
}
/**
* The code that runs in the thread and recovers the JavaSound capture
* buffer.
* Implemented from Runnable interface.
*/
public void run()
{
int read = 0;
while (thread != null && state == STATE_RECORDING && read != -1) {
try {
read = audioInputStream.read(buffer, 0, buffer.length);
}
catch (IOException e) {
e.printStackTrace();
}
if (read > 0) {
out.write(buffer, 0, read);
}
}
}
} // End class Capture
//--------------------------------------------------------------------------
// Action processing code
//--------------------------------------------------------------------------
/**
* Process Actions when button are pressed.
* Implemented from ActionListener interface.
*/
public void actionPerformed(final ActionEvent e) {
if (e.getSource() == timer) {
progressBar.setValue(getProgress());
}
else {
if ("Play".equals(e.getActionCommand())) {
playIt();
}
else if ("Record".equals(e.getActionCommand())) {
recordIt();
}
else if ("Pause".equals(e.getActionCommand())) {
if (state == STATE_PAUSED) {
playIt();
}
else if (state == STATE_REC_PAUSED) {
recordIt();
}
else {
pauseIt();
}
}
else if ("Stop".equals(e.getActionCommand())) {
stopIt();
}
else {
}
}
}
/**
*
*/
public synchronized void stopIt()
{
recordButton.setEnabled(false);
playButton.setEnabled(false);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
oldstate = state;
state = STATE_STOPPED;
if (oldstate == STATE_PLAYING || oldstate == STATE_PAUSED) {
playback.stop();
}
else if (oldstate == STATE_RECORDING || oldstate == STATE_REC_PAUSED) {
capture.stop();
}
timer.stop();
progressBar.setValue(0);
recordButton.setEnabled(true);
playButton.setEnabled(true);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
}
/**
*
*/
public synchronized void playIt()
{
recordButton.setEnabled(false);
playButton.setEnabled(false);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
oldstate = state;
state = STATE_PLAYING;
if (oldstate == STATE_STOPPED) {
playback.setupSound();
}
if (playback.thread == null || !playback.thread.isAlive()) {
playback.start();
}
playback.line.start();
timer.start();
recordButton.setEnabled(false);
playButton.setEnabled(false);
pauseButton.setEnabled(true);
stopButton.setEnabled(true);
}
/**
*
*/
public synchronized void pauseIt()
{
recordButton.setEnabled(false);
playButton.setEnabled(false);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
oldstate = state;
if (oldstate == STATE_PLAYING) {
state = STATE_PAUSED;
playback.line.stop();
recordButton.setEnabled(false);
playButton.setEnabled(true);
}
else if (oldstate == STATE_RECORDING) {
state = STATE_REC_PAUSED;
capture.line.stop();
recordButton.setEnabled(true);
playButton.setEnabled(false);
}
timer.stop();
pauseButton.setEnabled(true);
stopButton.setEnabled(true);
}
/**
*
*/
public synchronized void recordIt()
{
recordButton.setEnabled(false);
playButton.setEnabled(false);
pauseButton.setEnabled(false);
stopButton.setEnabled(false);
oldstate = state;
state = STATE_RECORDING;
if (oldstate == STATE_STOPPED) {
capture.setupSound();
}
if (capture.thread == null || !capture.thread.isAlive()) {
capture.start();
}
capture.line.start();
timer.start();
recordButton.setEnabled(false);
playButton.setEnabled(false);
pauseButton.setEnabled(true);
stopButton.setEnabled(true);
}
/**
* Return the progress of the playback.
* @return the progress of the playback.
*/
protected int getProgress()
{
audioLength = 500000;
if (state == STATE_PLAYING || state == STATE_PAUSED) {
return super.getProgress();
}
else if (state == STATE_RECORDING || state == STATE_REC_PAUSED) {
return capture.line.getFramePosition() * 1000 / audioLength;
}
else {
return 0;
}
}
//--------------------------------------------------------------------------
// GUI code
//--------------------------------------------------------------------------
/**
* Create the ButtonPanel for the recorder.
* The recorder button panel that should look something like this:
*
* +--------------------------------+
* | +------+ +----+ +-----+ +----+ |
* | |record| |play| |pause| |stop| | Button Panel
* | +------+ +----+ +-----+ +----+ |
* +--------------------------------+
*
*/
protected void createButtonPanel()
{
recordButton = buildButton("Record", "Record",
"/images/player_record.gif",
"/images/player_record2.gif",
"/images/player_record3.gif", this);
playerButtonPane.add(recordButton);
super.createButtonPanel();
}
}
jspeex/src/java/org/xiph/speex/player/RecorderApplet.java 0000644 0001750 0001750 00000012305 10135761046 023763 0 ustar twerner twerner /******************************************************************************
* *
* Copyright (c) 1999-2004 Wimba S.A., All Rights Reserved. *
* *
* COPYRIGHT: *
* This software is the property of Wimba S.A. *
* This software is redistributed under the Xiph.org variant of *
* the BSD license. *
* Redistribution and use in source and binary forms, with or without *
* modification, are permitted provided that the following conditions *
* are met: *
* - Redistributions of source code must retain the above copyright *
* notice, this list of conditions and the following disclaimer. *
* - Redistributions in binary form must reproduce the above copyright *
* notice, this list of conditions and the following disclaimer in the *
* documentation and/or other materials provided with the distribution. *
* - Neither the name of Wimba, the Xiph.org Foundation nor the names of *
* its contributors may be used to endorse or promote products derived *
* from this software without specific prior written permission. *
* *
* WARRANTIES: *
* This software is made available by the authors in the hope *
* that it will be useful, but without any warranty. *
* Wimba S.A. is not liable for any consequence related to the *
* use of the provided software. *
* *
* Class: RecorderApplet.java *
* *
* Author: Marc GIMPEL *
* *
* Date: Jun 8, 2004 *
* *
******************************************************************************/
/* $Id: RecorderApplet.java,v 1.2 2004/10/21 16:21:58 mgimpel Exp $ */
package org.xiph.speex.player;
import javax.swing.JApplet;
import javax.swing.SwingUtilities;
/**
* JavaSound Recorder Applet.
* This is simply a Recorder Panel placed inside an applet.
*
* @author Marc Gimpel, Wimba S.A. (mgimpel@horizonwimba.com)
* @version $Revision: 1.2 $
*/
public class RecorderApplet
extends JApplet
{
private Recorder recorder;
private String filename;
/**
* Initialize Applet.
* Called by the browser or applet viewer to inform this applet that it has
* been loaded into the system. It is always called before the first time
* that the start
method is called.
*/
public void init()
{
System.out.println("****** Recorder Applet starting, copyright Wimba 2004");
System.out.println("****** Version: " + Recorder.VERSION +
", Revision: " + Recorder.REVISION +
", build: " + Recorder.BUILD);
filename = getParameter("file");
// Some initialising should be done on the event-dispatching thread.
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
initGUI();
}
});
}
catch (Exception e) {
System.err.println("couldn't successfully initialise from event thread");
}
// The rest of the initialising is done from the applet launching thread.
recorder.init();
}
/**
* Initialize Applet, but run from the event-dispatching thread.
*/
public void initGUI()
{
recorder = new Recorder(filename);
setContentPane(recorder);
}
/**
* Called by the browser or applet viewer to inform this applet that it
* should start its execution. It is called after the init
* method and each time the applet is revisited in a Web page.
*/
public void start() {
recorder.start();
}
/**
* Called by the browser or applet viewer to inform this applet that it
* should stop its execution. It is called when the Web page that contains
* this applet has been replaced by another page, and also just before the
* applet is to be destroyed.
*/
public void stop() {
recorder.stop();
}
/**
* Called by the browser or applet viewer to inform this applet that it is
* being reclaimed and that it should destroy any resources that it has
* allocated. The stop
method will always be called before
* destroy
.
*/
public void destroy()
{
recorder = null;
}
}
jspeex/src/java/org/xiph/speex/player/package.html 0000644 0001750 0001750 00000000426 10135522576 022472 0 ustar twerner twerner