/** * MediaFrame is an Open Source streaming media platform in Java * which provides a fast, easy to implement and extremely small applet * that enables to view your audio/video content without having * to rely on external player applications or bulky plug-ins. * * Copyright (C) 2004/5 MediaFrame (http://www.mediaframe.org). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package mediaframe.mpeg4.audio; import java.io.IOException; import java.io.InterruptedIOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; /** * The <code>AudioDataInputStream</code> realizes the 8khz mono ulaw audio * stream which gets the data from connected by a pipe the mono or stereo 16 bit * linear source audio stream. It also implements the control of the playback * (start, pause/continue, stop), the mute on/off and the volume * functionalitities. */ public final class AudioDataInputStream extends PipedInputStream { /** Constant, the size of the piped buffer for audio samples. */ protected final static int PIPED_SIZE = 1048576; // 1000K /** Constant, the zero value in the 8 bit ulaw format. */ public final static int ULAW_ZERO = 127; /** Constant, the add-in bias for 16 bit samples. */ private final static int BIAS = 0x84; /** Constant, the max value of the magnitude. */ private final static int CLIP = 32635; /** The table that is used for look-up of the exponenta for a byte. */ private final static int[] exp_lut = { 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7 }; /** * The number of channels in the source audio stream (1 - mono or 2 - * stereo). */ private int channelCount; /** The step between the samples in the source audio stream. */ private int sampleStep = (8000 << 16) / 44100; /** The current sample position in the source audio stream. */ private int samplePosition = 0; /** <tt>True</tt>, if the audio device plays the audio. */ private boolean playing = false; /** The mute status of the audio device (equals <tt>true</tt> if no audio). */ private boolean mute = false; /** The current volume of the audio device. */ private int volume = 100; /** * <tt>True</tt>, if the audio data stream is closed, <tt>false</tt> * otherwise. */ private boolean closed = false; /** * The source audio stream in the pipe with samples in the 16 bit linear * format. */ private PipedOutputStream source; /** The reference to the <code>Java1AudioDevice</code> audio device. */ private Java1AudioDevice audioDevice; /** * <tt>True</tt>, if any samples haven't been read from the audio data * stream, <tt>false</tt> otherwise. */ private boolean firstLoop = true; /** * Constructs an <code>AudioDataInputStream</code> object and inits it using * the specified <code>Java1AudioDevice</code> audio device and the source * audio stream. * * @param audioDevice * the <code>Java1AudioDevice</code> audio device. * @param audioDataOutputStream * the source audio stream with samples in the 16 bit linear * format. * @throws IOException * raises if there is an I/O error occurs. */ public AudioDataInputStream(Java1AudioDevice audioDevice, PipedOutputStream audioDataOutputStream) throws IOException { super(audioDataOutputStream); this.audioDevice = audioDevice; this.source = audioDataOutputStream; this.buffer = new byte[PIPED_SIZE]; } /** * Inits the piped stream (writes to the source audio stream and reads from * the input audio data stream 1 byte). * * @throws IOException * raises if there is an I/O error occurs. */ public void init_stream() throws IOException { source.write(0); super.read(); } /** * Reads the next audio sample in the 8 bit ulaw format of 8khz mono audio * data stream. Implements the playback start, pause/continue, stop, the * mute on/off and the volume functionalities.<br/> * Converts the mono or stereo input audio stream from the 16 bit linear * format into the 8khz mono format with 8 bit ulaw samples. * * @return the next audio sample in the 8 bit ulaw format, or -1 the audio * data stream is closed. * @throws IOException * raises if there is an I/O error occurs. */ public int read() throws IOException { int sample = 0; int currentSamplePosition = samplePosition >> 16; int sample_size = channelCount == 2 ? 4 : 2; int samples_count = 0; int samples_summa = 0; try { while (!playing && !closed) { synchronized (audioDevice) { audioDevice.wait(); } } } catch (InterruptedException ex) { throw new InterruptedIOException(ex.getMessage()); } int available = available(); while (currentSamplePosition == (samplePosition >> 16)) { if (available < sample_size) { return ULAW_ZERO; } sample = (short) (super.read() + (super.read() << 8)); if (channelCount == 2) { sample = (sample + (short) (super.read() + (super.read() << 8))) >> 1; } available -= sample_size; samples_count++; samples_summa += sample; samplePosition += sampleStep; } sample = samples_summa / samples_count; if (closed) { return -1; } if (mute) { return ULAW_ZERO; } else { sample = sample * volume / 100; return linear2ulaw(sample); } } /** * Reads up to <code>len</code> audio samples in the 8 bit ulaw format into * the buffer <code>b</code> starting from the offset <code>off</code>. * * @param b * the buffer to store the readed audio samples. * @param off * the offset in the buffer to store the samples. * @param len * the number of samples to read. * @return the number of samples which have been read or -1 if any samples * haven't been read from the stream. * @throws IOException * raises if there is an I/O error occurs. */ public int read(byte[] b, int off, int len) throws IOException { firstLoop = false; for (int i = 0; i < len; i++) { int sample = read(); if (sample == -1) { if (i == 0) { return -1; } else { return i; } } b[i + off] = (byte) sample; } return len; } /* * * Source: http://www.speech.cs.cmu.edu/comp.speech/Section2/Q2.7.html** * This routine converts from linear to ulaw** Craig Reese: * IDA/Supercomputing Research Center* Joe Campbell: Department of Defense* * 29 September 1989** References:* 1) CCITT Recommendation G.711 (very * difficult to follow)* 2) "A New Digital Technique for Implementation of * Any* Continuous PCM Companding Law," Villeret, Michel,* et al. 1973 IEEE * Int. Conf. on Communications, Vol 1,* 1973, pg. 11.12-11.17* 3) * MIL-STD-188-113,"Interoperability and Performance Standards* for * Analog-to_Digital Conversion Techniques,"* 17 February 1987** Input: * Signed 16 bit linear sample* Output: 8 bit ulaw sample */ /* * Constant, set to <tt>true</tt> if you want to turn on the trap as per the * MIL-STD private final static boolean ZEROTRAP = true; */ /** * Converts the audio sample from the signed 16 bit linear format to the 8 * bit ulaw format. * * @param sample * the signed 16 bit linear sample. * @return the 8 bit ulaw sample. */ private int linear2ulaw(int sample) { /* Get the sample into sign-magnitude. */ int sign = (sample >> 8) & 0x80; /* set aside the sign */ if (sign != 0) sample = -sample; /* get magnitude */ if (sample > CLIP) sample = CLIP; /* clip the magnitude */ /* Convert from 16 bit linear to ulaw. */ sample = sample + BIAS; int exponent = exp_lut[(sample >> 7) & 0xFF]; int mantissa = (sample >> (exponent + 3)) & 0x0F; int ulawbyte = (~(sign | (exponent << 4) | mantissa)) & 0xff; /* * if(ZEROTRAP) { if (ulawbyte <= 0) ulawbyte = 0x02; // optional CCITT * trap } */ return ulawbyte; } /** * Sets the playing status of the audio device (<tt>true</tt>, if the audio * device plays the audio). * * @param playing * the playing status to set. */ public void setPlaying(boolean playing) { this.playing = playing; } /** * Sets the mute state of the audio device. * * @param mute * the mute state to set. */ public void setMute(boolean mute) { this.mute = mute; } /** * Sets the volume of the audio stream. * * @param volume * the volume to set. */ public void setVolume(int volume) { this.volume = volume; } /** * Sets the number of channels of the input audio stream. * * @param channelCount * the number of channels to set. */ public void setChannelCount(int channelCount) { this.channelCount = channelCount; } /** * Sets the sample frequency of the input audio stream. * * @param sampleFrequency * the sample frequency to set. */ public void setSampleFrequency(int sampleFrequency) { this.sampleStep = (8000 << 16) / sampleFrequency; } /** * Closes the audio data stream. */ public void close() throws IOException { closed = true; synchronized (audioDevice) { audioDevice.notifyAll(); } super.close(); } /** * Returns <tt>true</tt>, if any samples haven't been read from the audio * data stream. */ public boolean isFirstLoop() { return firstLoop; } }