/*
* Copyright (c) 2007 by Damien Di Fede <ddf@compartmental.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library 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 Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package ddf.minim;
import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.SourceDataLine;
/**
* An <code>AudioRecorder</code> can be used to record audio that is being
* played by a <code>Recordable</code> object. An <code>AudioRecorder</code>
* need not necessarily record to disk, but the recorders you receive from
* {@link Minim#createRecorder(Recordable, String, boolean)} will do so. If
* you'd like to save a file somewhere other than the sketches root folder, you
* can constructor an <code>AudioRecorder</code> directly, using an absolute
* path for the filename (such as "C:\My Documents\Music\song.wav"). You can
* also create a recorder that uses your own implementation of
* {@link SampleRecorder} (like if you wanted to implement an mp3 encoder).
*
* @author Damien Di Fede
*
*/
public class AudioRecorder
{
private Recordable source;
private SampleRecorder recorder;
/**
* Constructs an <code>AudioRecorder</code> with the given parameters. Note
* that <code>fileName</code> must be an absolute path and that
* <code>fileType</code> should be a supported audio file type. When using
* buffered recording, be aware that all recorded samples are stored in memory
* (meaning a long recording will take up a lot of memory) and that a
* subsequent call to {@link #save()} will write the entire buffer to disk,
* causing the thread of execution to block until all samples have been
* written. This means that your sketch will appear to hang when you save
* large recordings.
*
* @param recordSource
* the source to record
* @param fileName
* the file to save to (must be an absolute path)
* @param fileType
* the format of the file (ie Minim.WAV, Minim.AU, etc.)
* @param buffered
* whether or not to use buffered recording
*/
public AudioRecorder(Recordable recordSource, String fileName,
AudioFileFormat.Type fileType, boolean buffered)
{
source = recordSource;
if (buffered)
{
recorder = new MBufferedRecorder(fileName, fileType, source.getFormat(),
source.bufferSize());
}
else
{
recorder = new MStreamRecorder(fileName, fileType, source.getFormat(),
source.bufferSize());
}
source.addListener(recorder);
}
/**
* Constructs an <code>AudioRecorder</code> that will use
* <code>recorder</code> to record <code>recordSource</code>.
*
* @param recordSource
* the <code>Recordable</code> object to record
* @param recorder
* the <code>SampleRecorder</code> to use to record it
*/
public AudioRecorder(Recordable recordSource, SampleRecorder recorder)
{
source = recordSource;
this.recorder = recorder;
source.addListener(recorder);
}
/**
* Begins recording audio from the current record source. If recording was
* previously halted, and {@link #save()} was not called, samples will be
* appended to the end of the material recorded so far.
*
*/
public void beginRecord()
{
recorder.beginRecord();
}
/**
* Halts the recording of audio from the current record source.
*
*/
public void endRecord()
{
recorder.endRecord();
}
/**
* Returns the current record state.
*
* @return true if this is currently recording
*/
public boolean isRecording()
{
return recorder.isRecording();
}
/**
* Requests the current <code>SampleRecorder</code> to save. This will only
* work if you have called {@link #endRecord()}. If this was created with a
* buffered recorder, then calling {@link #beginRecord()} after saving will
* not overwrite the file on the disk, unless this method is subsequently
* called. However, if this was created with an unbuffered recorder, it is
* likely that a call to {@link #beginRecord()} will create the file again,
* overwriting the file that had previously been saved. An
* <code>AudioRecording</code> will be returned if the
* <code>SampleRecorder</code> used to record the audio saved to a file
* (this will always be the case if you use <code>createRecorder</code> or
* the first constructor for <code>AudioRecorder</code>).
*
* @return the audio that was recorded as an <code>AudioRecording</code>,
* or null if the audio was not recorded to a file
*/
public AudioRecording save()
{
recorder.save();
String filePath = recorder.filePath();
if (!filePath.equals(""))
{
AudioInputStream ais = Minim.getAudioInputStream(filePath);
SourceDataLine sdl = Minim.getSourceDataLine(ais.getFormat());
// this is fine because the recording will always be
// in a raw format (WAV, AU, etc).
MAudioFile recording = new MAudioFile(ais, sdl, source.bufferSize());
return recording;
}
return null;
}
/**
* Sets the record source for this recorder. The record source can be set at
* any time, though in practice it is probably a good idea to mute the old
* record source, then add the new record source, also muted, and then unmute
* the new record source. Otherwise, you'll probably wind up with a pop in the
* recording.
*
* @param recordSource
* the new record source
*/
public void setRecordSource(Recordable recordSource)
{
source.removeListener(recorder);
source = recordSource;
source.addListener(recorder);
}
/**
* Sets the <code>SampleRecorder</code> for this recorder. Similar caveats
* apply as with {@link #setRecordSource(Recordable)}. This calls
* <code>endRecord</code> and <code>save</code> on the current
* <code>SampleRecorder</code> before setting the new one.
*
* @param recorder
* the new <code>SampleRecorder</code> to use
*/
public void setSampleRecorder(SampleRecorder recorder)
{
this.recorder.endRecord();
this.recorder.save();
source.removeListener(this.recorder);
source.addListener(recorder);
this.recorder = recorder;
}
}