package ddf.minim; import ddf.minim.javax.sound.sampled.AudioFormat; import ddf.minim.spi.AudioOut; /** * An <code>AudioSource</code> is a kind of wrapper around an * <code>AudioStream</code>. An <code>AudioSource</code> will add its * <code>AudioBuffer</code>s as listeners on the stream so that you can access * the stream's samples without having to implement <code>AudioListener</code> * yourself. It also provides the <code>Effectable</code> and * <code>Recordable</code> interface. Because an <code>AudioStream</code> must * be closed when you are finished with it, you must remember to call * {@link #close()} on any <code>AudioSource</code>s you obtain from Minim, such * as <code>AudioInput</code>s, <code>AudioOutput</code>s, and * <code>AudioPlayer</code>s. * * @author Damien Di Fede * @invisible * */ public class AudioSource extends Controller implements Effectable, Recordable { // the instance of Minim that created us, if one did. Minim parent; private AudioOut stream; // the signal splitter used to manage listeners to the source // our stereobuffer will be the first in the list private SignalSplitter splitter; // the StereoBuffer that will subscribe to synth private StereoBuffer buffer; // the effects chain used for effecting private EffectsChain effects; /** * The AudioBuffer containing the left channel samples. If this is a mono * sound, it contains the single channel of audio. * * @example Basics/PlayAFile * * @related AudioBuffer */ public final AudioBuffer left; /** * The AudioBuffer containing the right channel samples. If this is a mono * sound, <code>right</code> contains the same samples as <code>left</code>. * * @example Basics/PlayAFile * * @related AudioBuffer */ public final AudioBuffer right; /** * The AudioBuffer containing the mix of the left and right channels. If * this is a mono sound, <code>mix</code> contains the same samples as * <code>left</code>. * * @example Basics/PlayAFile * * @related AudioBuffer */ public final AudioBuffer mix; /** * Constructs an <code>AudioSource</code> that will subscribe to the samples * in <code>stream</code>. It is expected that the stream is using a * <code>DataLine</code> for playback. If it is not, calls to * <code>Controller</code>'s methods will result in a * <code>NullPointerException</code>. * * @param istream * the <code>AudioStream</code> to subscribe to and wrap * * @invisible */ public AudioSource(AudioOut istream) { super(istream.getControls()); stream = istream; // we gots a buffer for users to poll buffer = new StereoBuffer(stream.getFormat().getChannels(), stream.bufferSize(), this); left = buffer.left; right = buffer.right; mix = buffer.mix; // we gots a signal splitter that we'll add any listeners the user wants splitter = new SignalSplitter(stream.getFormat(), stream.bufferSize()); // we stick our buffer in the signal splitter because we can only set // one // listener on the stream splitter.addListener(buffer); // and there it goes. stream.setAudioListener(splitter); // we got an effects chain that we'll add user effects to effects = new EffectsChain(); // we set it as the effect on the stream stream.setAudioEffect(effects); stream.open(); } /** * Closes this source, making it unavailable. * * @invisible */ public void close() { Minim.debug("Closing " + this.toString()); stream.close(); // if we have a parent, tell them to stop tracking us // so that we can get garbage collected if (parent != null) { parent.removeSource(this); } } /** @deprecated */ public void addEffect(AudioEffect effect) { effects.add(effect); } /** @deprecated */ public void clearEffects() { effects.clear(); } /** @deprecated */ public void disableEffect(int i) { effects.disable(i); } /** @deprecated */ public void disableEffect(AudioEffect effect) { effects.disable(effect); } /** @deprecated */ public int effectCount() { return effects.size(); } /** @deprecated */ public void effects() { effects.enableAll(); } /** @deprecated */ public boolean hasEffect(AudioEffect e) { return effects.contains(e); } /** @deprecated */ public void enableEffect(int i) { effects.enable(i); } /** @deprecated */ public void enableEffect(AudioEffect effect) { effects.enable(effect); } /** @deprecated */ public AudioEffect getEffect(int i) { return effects.get(i); } /** @deprecated */ public boolean isEffected() { return effects.hasEnabled(); } /** @deprecated */ public boolean isEnabled(AudioEffect effect) { return effects.isEnabled(effect); } /** @deprecated */ public void noEffects() { effects.disableAll(); } /** @deprecated */ public void removeEffect(AudioEffect effect) { effects.remove(effect); } /** @deprecated */ public AudioEffect removeEffect(int i) { return effects.remove(i); } /** * Add an AudioListener to this sound generating object, which will have its * samples method called every time this object generates a new buffer of * samples. * * @shortdesc Add an AudioListener to this sound generating object. * * @example Advanced/AddAndRemoveAudioListener * * @param listener * the AudioListener that will listen to this * * @related AudioListener */ public void addListener(AudioListener listener) { splitter.addListener(listener); } /** * The internal buffer size of this sound object. The left, right, and mix * AudioBuffers of this object will be this large, and sample buffers passed * to AudioListeners added to this object will be this large. * * @shortdesc The internal buffer size of this sound object. * * @example Basics/PlayAFile * * @return int: the internal buffer size of this sound object, in sample * frames. */ public int bufferSize() { return stream.bufferSize(); } /** * Returns an AudioFormat object that describes the audio properties of this * sound generating object. This is often useful information when doing * sound analysis or some synthesis, but typically you will not need to know * about the specific format. * * @shortdesc Returns AudioFormat object that describes the audio properties * of this sound generating object. * * @example Advanced/GetAudioFormat * * @return an AudioFormat describing this sound object. */ public AudioFormat getFormat() { return stream.getFormat(); } /** * Removes an AudioListener that was previously added to this sound object. * * @example Advanced/AddAndRemoveAudioListener * * @param listener * the AudioListener that should stop listening to this * * @related AudioListener */ public void removeListener(AudioListener listener) { splitter.removeListener(listener); } /** * The type is an int describing the number of channels this sound object * has. * * @return Minim.MONO if this is mono, Minim.STEREO if this is stereo */ public int type() { return stream.getFormat().getChannels(); } /** * Returns the sample rate of this sound object. * * @return the sample rate of this sound object. */ public float sampleRate() { return stream.getFormat().getSampleRate(); } }