Javaで任意の周波数の正弦波音を生成する最も簡単な方法は何ですか? 2バイトを超えるサンプルサイズが役立つでしょうが、それは実際には問題ではありません。
ありがとう
自己完結型の例については、 Beeper
を参照してください。
おそらくもっと簡単なことですか?
リンクされた回答の上部に示されている51行のスニペット(以下で繰り返されます-単一行とインラインコメント用に間隔が空けられています)は、トーンを生成するのとほぼ同じくらい簡単です(OK、5行以上を取り出すことができます高調波)。
人々は、それが純音を生成するためにツールキットに組み込まれた方法であるべきだと思っているようです。そうではなく、それを作るのに少し計算が必要です。
/** Generates a tone, and assigns it to the Clip. */
public void generateTone()
throws LineUnavailableException {
if ( clip!=null ) {
clip.stop();
clip.close();
} else {
clip = AudioSystem.getClip();
}
boolean addHarmonic = harmonic.isSelected();
int intSR = ((Integer)sampleRate.getSelectedItem()).intValue();
int intFPW = framesPerWavelength.getValue();
float sampleRate = (float)intSR;
// oddly, the sound does not loop well for less than
// around 5 or so, wavelengths
int wavelengths = 20;
byte[] buf = new byte[2*intFPW*wavelengths];
AudioFormat af = new AudioFormat(
sampleRate,
8, // sample size in bits
2, // channels
true, // signed
false // bigendian
);
int maxVol = 127;
for(int i=0; i<intFPW*wavelengths; i++){
double angle = ((float)(i*2)/((float)intFPW))*(Math.PI);
buf[i*2]=getByteValue(angle);
if(addHarmonic) {
buf[(i*2)+1]=getByteValue(2*angle);
} else {
buf[(i*2)+1] = buf[i*2];
}
}
try {
byte[] b = buf;
AudioInputStream ais = new AudioInputStream(
new ByteArrayInputStream(b),
af,
buf.length/2 );
clip.open( ais );
} catch(Exception e) {
e.printStackTrace();
}
}
Java Sound API 、およびMath.sin
を使用して、実際の波のレベルを作成します。
http://www.developer.com/Java/other/article.php/2226701 には、私が以前に参照した、これに関する優れたチュートリアルがあります。 http://jsresources.org/examples/ は別の有用なリファレンスでした。
簡単なコードで始めたい場合は、これが役立つはずです
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class SinSynth {
//
protected static final int SAMPLE_RATE = 16 * 1024;
public static byte[] createSinWaveBuffer(double freq, int ms) {
int samples = (int)((ms * SAMPLE_RATE) / 1000);
byte[] output = new byte[samples];
//
double period = (double)SAMPLE_RATE / freq;
for (int i = 0; i < output.length; i++) {
double angle = 2.0 * Math.PI * i / period;
output[i] = (byte)(Math.sin(angle) * 127f); }
return output;
}
public static void main(String[] args) throws LineUnavailableException {
final AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 1, true, true);
SourceDataLine line = AudioSystem.getSourceDataLine(af);
line.open(af, SAMPLE_RATE);
line.start();
boolean forwardNotBack = true;
for(double freq = 400; freq <= 800;) {
byte [] toneBuffer = createSinWaveBuffer(freq, 50);
int count = line.write(toneBuffer, 0, toneBuffer.length);
if(forwardNotBack) {
freq += 20;
forwardNotBack = false; }
else {
freq -= 10;
forwardNotBack = true;
} }
line.drain();
line.close();
}
}
ビープ音を鳴らすクラスだけを探している場合は、これを試してください:(Thumbzから借用したコード)
package ditdah;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class Beep {
protected static final int SAMPLE_RATE = 16 * 1024;
public void play(double freq, int length) {
final AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 1, true, true);
try {
SourceDataLine line = AudioSystem.getSourceDataLine(af);
line.open(af, SAMPLE_RATE);
line.start();
byte[] toneBuffer = this.createSinWaveBuffer(freq, length);
say.it(toneBuffer.toString() + " " + toneBuffer.length);
int count = line.write(toneBuffer, 0, toneBuffer.length);
line.drain();
line.close();
} catch (LineUnavailableException e) {
say.it(e.getLocalizedMessage());
}
}
public byte[] createSinWaveBuffer(double freq, int ms) {
int samples = (int) ((ms * SAMPLE_RATE) / 1000);
byte[] output = new byte[samples];
//
double period = (double) SAMPLE_RATE / freq;
for (int i = 0; i < output.length; i++) {
double angle = 2.0 * Math.PI * i / period;
output[i] = (byte) (Math.sin(angle) * 127f);
}
return output;
}
}
最初に、メモの頻度を返すクラスNoteを作成し、それをバイト配列に変換することをお勧めします。
その後、非常に簡単にストリーミングします
protected static final int SAMPLE_RATE = 8 * 1024;
public static void main(String[] args) throws LineUnavailableException {
final AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 1, true, true);
SourceDataLine line = AudioSystem.getSourceDataLine(af);
line.open(af, SAMPLE_RATE);
line.start();
// fist argument is duration of playing note
byte[] noteDo = Note.DO.getTone(1, SAMPLE_RATE);
byte[] noteRe = Note.RE.getTone(0.5, SAMPLE_RATE);
byte[] noteMi = Note.MI.getTone(1.5, SAMPLE_RATE);
line.write(noteDo, 0, noteDo.length);
line.write(noteRe, 0, noteRe.length);
line.write(noteMi, 0, noteMi.length);
line.drain();
line.close();
}
public enum Note {
DO(0.0f), DO_DIEZ(1.0f),
RE(2.0f), RE_DIEZ(3.0f),
MI(4.0f),
FA(5.0f), FA_DIEZ(6.0f),
SOL(7.0f),SOL_DIEZ(8.0f),
LYA(9.0f),LYA_DIEZ(10.0f),
SI(11.0f);
private final double mPhase;
Note(double phase) {
mPhase = phase;
}
public double getNoteFrequencies() {
double index = getmPhase()/ 12.0d;
return 440 * Math.pow(2, index);
}
public static Note getNote(double phase) throws Exception {
Note findNote = null;
for (Note note : Note.values()){
if (note.getmPhase() == phase){
findNote = note;
}
}
if (findNote == null)
throw new Exception("Note not found: Ilegal phase " + phase);
else
return findNote;
}
public byte[] getTone(double duration, int rate){
double frequencies = getNoteFrequencies();
int maxLength = (int)(duration * rate);
byte generatedTone[] = new byte[2 * maxLength];
double[] sample = new double[maxLength];
int idx = 0;
for (int x = 0; x < maxLength; x++){
sample[x] = sine(x, frequencies / rate);
}
for (final double dVal : sample) {
final short val = (short) ((dVal * 100f));
// in 16 bit wav PCM, first byte is the low order byte
generatedTone[idx++] = (byte) (val & 0x00ff);
generatedTone[idx++] = (byte) ((val & 0xff00) >>> 8);
}
return generatedTone;
}
private double sine(int x, double frequencies){
return Math.sin( 2*Math.PI * x * frequencies);
}
public double getmPhase() {
return mPhase;
}
}
これらの回答のcreateSinWaveBuffer()メソッドは、継続的に再生するための適切な波形データを生成しません。完全な波形を得るには、最後のバイトをゼロに近づける必要があります。より良い例-
protected static final float SAMPLE_RATE = 16 * 1024;
public static byte[] createSinWaveBuffer(double freq) {
double waveLen = 1.0/freq;
int samples = (int) Math.round(waveLen * 5 * SAMPLE_RATE);
byte[] output = new byte[samples];
double period = SAMPLE_RATE / freq;
for (int i = 0; i < output.length; i++) {
double angle = 2.0 * Math.PI * i / period;
output[i] = (byte)(Math.sin(angle) * 127f); }
return output;
}
正弦波を生成するための非常に効率的なアルゴリズムがあることを指摘したいと思います。
DSPトリック:正弦波トーンジェネレーター http://www.dspguru.com/dsp/tricks/sine_tone_generator