SoundLineSource.java
/*
* Created on 2010/01/04
* Copyright (C) 2010 Koga Laboratory. All rights reserved.
*
*/
package org.mklab.tool.control.system.source;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioFormat.Encoding;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.TargetDataLine;
import org.mklab.nfc.matrix.ComplexNumericalMatrix;
import org.mklab.nfc.matrix.DoubleComplexMatrix;
import org.mklab.nfc.matrix.DoubleMatrix;
import org.mklab.nfc.matrix.RealNumericalMatrix;
import org.mklab.nfc.ode.SolverStopException;
import org.mklab.nfc.scalar.ComplexNumericalScalar;
import org.mklab.nfc.scalar.DoubleComplexNumber;
import org.mklab.nfc.scalar.DoubleNumber;
import org.mklab.nfc.scalar.RealNumericalScalar;
/**
* ライン入力のPCM波ソースです。
*
* @author Yuhi Ishikura
* @version $Revision$, 2010/01/04
* @param <RS> type of real scalar
* @param <RM> type of real matrix
* @param <CS> type of complex scalar
* @param <CM> type of complex matrix
*/
public final class SoundLineSource<RS extends RealNumericalScalar<RS,RM,CS,CM>, RM extends RealNumericalMatrix<RS,RM,CS,CM>, CS extends ComplexNumericalScalar<RS,RM,CS,CM>, CM extends ComplexNumericalMatrix<RS,RM,CS,CM>> extends FixedRateAsynchronousSource<RS,RM,CS,CM> {
/** サンプリングレートです。 */
private static final float DEFAULT_SAMPLE_RATE = 44100.0F / 10;
/** デフォルトのラインのオーディオフォーマットです。 */
private static final AudioFormat DEFAULT_AUDIO_FORMAT;
/** クローズ命令の呼出しを示します。 */
private boolean closed;
/** ラインのオーディオフォーマットです。 */
private AudioFormat audioFormat;
static {
final Encoding encoding = AudioFormat.Encoding.PCM_SIGNED;
final float sampleRate = DEFAULT_SAMPLE_RATE;
final float frameRate = sampleRate;
final int sampleSizeInBits = 16;
final int channels = 1;
final int frameSize = 2;
final boolean bigEndian = false;
DEFAULT_AUDIO_FORMAT = new AudioFormat(encoding, sampleRate, sampleSizeInBits, channels, frameSize, frameRate, bigEndian);
}
/**
* 入力を行う時間から、 {@link SoundLineSource}オブジェクトを構築します。 <p> 例えば時間に1000を指定すると、0ms ~ 999msまでの値を記録できるソースを構築します。
*
* @param audioFormat データ保存に使用するフォーマット
* @param timeMillis 保存する時間(ms)
* @param sunit unit of scalar
* @return インスタンス
*/
public SoundLineSource<RS,RM,CS,CM> createFromTime(AudioFormat audioFormat, long timeMillis, RS sunit) {
final int size = (int)Math.ceil((double)timeMillis / 1000 * audioFormat.getSampleRate());
return new SoundLineSource<>(audioFormat, size, sunit);
}
/**
* 入力を行う時間から、 {@link SoundLineSource}オブジェクトを構築します。 <p> 例えば時間に1000を指定すると、0ms ~ 999msまでの値を記録できるソースを構築します。
*
* @param timeMillis 保存する時間(ms)
* @param sunit unit of scalar
* @return インスタンス
*/
public SoundLineSource<RS,RM,CS,CM> createFromTime(long timeMillis, RS sunit) {
return createFromTime(DEFAULT_AUDIO_FORMAT, timeMillis, sunit);
}
/**
* {@link SoundLineSource}オブジェクトを構築します。
*
* @param size 最大出力保存数
* @param sunit unit of scalar
*/
public SoundLineSource(int size, RS sunit) {
this(DEFAULT_AUDIO_FORMAT, size, sunit);
}
/**
* {@link SoundLineSource}オブジェクトを構築します。
*
* @param audioFormat 出力フォーマット
* @param size 最大出力保存数
* @param sunit unit of scalar
*/
public SoundLineSource(AudioFormat audioFormat, int size, RS sunit) {
super(size, sunit.create(audioFormat.getSampleRate()).inverse(), sunit);
this.audioFormat = audioFormat;
}
/**
* @throws LineUnavailableException ラインが利用不可能だった場合
* @see org.mklab.tool.control.system.source.AsynchronousSource#processImport()
*/
@Override
protected void processImport() throws LineUnavailableException {
try (final TargetDataLine line = AudioSystem.getTargetDataLine(this.audioFormat)) {
final byte[] buf = new byte[DEFAULT_AUDIO_FORMAT.getSampleSizeInBits() / 8];
try {
line.open();
line.start();
while (isClosed() == false && isOutputDataFilled() == false) {
final int size = line.read(buf, 0, buf.length);
if (size < buf.length) {
break;
}
RS sample = this.sunit.create((buf[0] & 0xFF) | ((buf[1] & 0xFF) << 8));
publish(sample);
}
} finally {
line.stop();
line.close();
}
}
}
/**
* クローズ要求がなされたかどうか調べます。
*
* @return closed クローズ要求がなされたらtrue,そうでなければfalse
*/
private boolean isClosed() {
return this.closed;
}
/**
* @see org.mklab.tool.control.system.source.Importer#close()
*/
@Override
public void close() {
this.closed = true;
}
/**
* メインメソッドです。
*
* @param args コマンドライン引数
*/
public static void main(final String[] args) {
final int recTimeSec = 1;
SoundLineSource<DoubleNumber,DoubleMatrix,DoubleComplexNumber,DoubleComplexMatrix> s = new SoundLineSource<>(0,new DoubleNumber(1));
final SoundLineSource<DoubleNumber,DoubleMatrix,DoubleComplexNumber,DoubleComplexMatrix> source = s.createFromTime(recTimeSec * 1000, new DoubleNumber(1));
source.open();
new Thread() {
/**
* @see java.lang.Thread#run()
*/
@Override
public void run() {
for (double i = 0; i < recTimeSec; i += 0.1) {
try {
DoubleMatrix m = source.outputEquation(new DoubleNumber(i));
m.print();
} catch (SolverStopException e) {
e.printStackTrace();
}
}
}
}.start();
}
}