我正试图在Java中同时播放2种声音(例如220Hz和440Hz)。
我设法使用StdAudio播放一首声音。 后来,我让它不是静态的,并删除了一些与我无关的方法。
我不知道的是如何同时播放2种声音。 我尝试用线程做到这一点,但它们并不总是同步。
下面是我修改过的StdAudio版本,下面是我尝试使用线程的示例。
program.java
public class program {
public static void main(String[] args) {
Thread t1 = new Thread(new soundThread(220));
t1.start();
Thread t2 = new Thread(new soundThread(440));
t2.start();
t1.notify();
t2.notify();
}
}
soundThread.java
public class soundThread implements Runnable {
private int fq;
public soundThread(int fq) {
this.fq = fq;
}
public void run() {
StdAudio s = new StdAudio();
double[] note = s.note(fq, 2, 1);
try {
this.wait();
} catch (Exception e) {
}
s.play(note);
s.close();
}
}
StdAudio.java
/*************************************************************************
* Compilation: javac this.java
* Execution: java StdAudio
*
* Simple library for reading, writing, and manipulating .wav files.
*
* Limitations
* -----------
* - Does not seem to work properly when reading .wav files from a .jar file.
* - Assumes the audio is monaural, with sampling rate of 44,100.
*
*************************************************************************/
import javax.sound.sampled.*;
/**
* <i>Standard audio</i>. This class provides a basic capability for creating,
* reading, and saving audio.
* <p>
* The audio format uses a sampling rate of 44,100 (CD quality audio), 16-bit,
* monaural.
*
* <p>
* For additional documentation, see <a
* href="http://introcs.cs.princeton.edu/15inout">Section 1.5</a> of
* <i>Introduction to Programming in Java: An Interdisciplinary Approach</i> by
* Robert Sedgewick and Kevin Wayne.
*/
public final class StdAudio {
/**
* The sample rate - 44,100 Hz for CD quality audio.
*/
public final int SAMPLE_RATE = 44100;
private final int BYTES_PER_SAMPLE = 2; // 16-bit audio
private final int BITS_PER_SAMPLE = 16; // 16-bit audio
private final double MAX_16_BIT = Short.MAX_VALUE; // 32,767
private final int SAMPLE_BUFFER_SIZE = 4096;
private SourceDataLine line; // to play the sound
private byte[] buffer; // our internal buffer
private int bufferSize = 0; // number of samples currently in internal
// buffer
// initializer
{
init();
}
// open up an audio stream
private void init() {
try {
// 44,100 samples per second, 16-bit audio, mono, signed PCM, little
// Endian
AudioFormat format = new AudioFormat((float) SAMPLE_RATE,
BITS_PER_SAMPLE, 1, true, false);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(format, SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE);
// the internal buffer is a fraction of the actual buffer size, this
// choice is arbitrary
// it gets divided because we can't expect the buffered data to line
// up exactly with when
// the sound card decides to push out its samples.
buffer = new byte[SAMPLE_BUFFER_SIZE * BYTES_PER_SAMPLE / 3];
} catch (Exception e) {
System.out.println(e.getMessage());
System.exit(1);
}
// no sound gets made before this call
line.start();
}
/**
* Close standard audio.
*/
public void close() {
line.drain();
line.stop();
}
/**
* Write one sample (between -1.0 and +1.0) to standard audio. If the sample
* is outside the range, it will be clipped.
*/
public void play(double in) {
// clip if outside [-1, +1]
if (in < -1.0)
in = -1.0;
if (in > +1.0)
in = +1.0;
// convert to bytes
short s = (short) (MAX_16_BIT * in);
buffer[bufferSize++] = (byte) s;
buffer[bufferSize++] = (byte) (s >> 8); // little Endian
// send to sound card if buffer is full
if (bufferSize >= buffer.length) {
line.write(buffer, 0, buffer.length);
bufferSize = 0;
}
}
/**
* Write an array of samples (between -1.0 and +1.0) to standard audio. If a
* sample is outside the range, it will be clipped.
*/
public void play(double[] input) {
for (int i = 0; i < input.length; i++) {
play(input[i]);
}
}
/**
* Create a note (sine wave) of the given frequency (Hz), for the given
* duration (seconds) scaled to the given volume (amplitude).
*/
public double[] note(double hz, double duration, double amplitude) {
int N = (int) (this.SAMPLE_RATE * duration);
double[] a = new double[N + 1];
for (int i = 0; i <= N; i++)
a[i] = amplitude
* Math.sin(2 * Math.PI * i * hz / this.SAMPLE_RATE);
return a;
}
}
提前致谢, Shay Ben Moshe
修改 解决方案是写这个方法:
public double[] multipleNotes(double[] hzs, double duration,
double amplitude) {
amplitude = amplitude / hzs.length;
int N = (int) (SAMPLE_RATE * duration);
double[] a = new double[N + 1];
for (int i = 0; i <= N; i++) {
a[i] = 0;
for (int j = 0; j < hzs.length; j++)
a[i] += amplitude
* Math.sin(2 * Math.PI * i * hzs[j] / SAMPLE_RATE);
}
return a;
}
EDIT2: 对我来说更好的解决方案(O(1)内存):
public void multiplePlay(double[] hzs, double duration, double amplitude) {
amplitude = amplitude / hzs.length;
int N = (int) (SAMPLE_RATE * duration);
double sum;
for (int i = 0; i <= N; i++) {
sum = 0;
for (int j = 0; j < hzs.length; j++)
sum += amplitude
* Math.sin(2 * Math.PI * i * hzs[j] / SAMPLE_RATE);
this.play(sum);
}
}
答案 0 :(得分:6)
对我简单地将两个声音合二为一的评论进行了扩展...
你证明了这一点:
public double[] note(double hz, double duration, double amplitude) {
int N = (int) (this.SAMPLE_RATE * duration);
double[] a = new double[N + 1];
for (int i = 0; i <= N; i++)
a[i] = amplitude
* Math.sin(2 * Math.PI * i * hz / this.SAMPLE_RATE);
return a;
}
那么将两个声音混合成一个并播放那个独特的声音呢?例如,您可以这样做:
public double[] notes(double hz1, double hz2, double duration, double amplitude) {
final double[] a1 = note( hz1, duration, amplitude );
final double[] a2 = note( hz2, duration, amplitude );
final double[] a3 = new double[a2.length];
for ( int i = 0; i < a1.length; i++ ) {
a3[i] = (a1[i] + a2[i]) / 2;
}
return a3;
}
你只需要这样称呼它:
final double[] sound = notes(220,400,...,...);
答案 1 :(得分:0)
尝试Pulpcore
答案 2 :(得分:0)
OpenAL框架中的iOS中提供的开源OpenAL音频API提供了一个优化的界面,用于在播放期间定位立体声场中的声音。播放,定位和移动声音就像在其他平台上一样。 OpenAL还可以让你混合声音。更多信息:http://developer.apple.com/library/IOS/#documentation/AudioVideo/Conceptual/MultimediaPG/UsingAudio/UsingAudio.html
答案 3 :(得分:0)
您可以使用JSyn库播放多个特定频率的声音。
它现在可以满足您的需求,如果您想做更复杂的事情,您可能希望以后再转移它。
http://www.softsynth.com/jsyn/
作为一个例子,我还设法在这里找出一些稍微复杂的声音:
此代码将同时生成220 Hz和440 Hz的音调。
com.jsyn.Synthesizer synth = JSyn.createSynthesizer();
com.jsyn.unitgen.SineOscillator sine1 = new SineOscillator();
com.jsyn.unitgen.SineOscillator sine2 = new SineOscillator();
com.jsyn.unitgen.LineOut lineOut = new LineOut();synth.add(sine1);
synth.add(sine2);
synth.add(线路输出);sine1.frequency.set(220);
sine2.frequency.set(440);sine1.output.connect(0,lineOut.input,0); //左右声道
sine1.output.connect(0,lineOut.input,1);
sine2.output.connect(0,lineOut.input,0); //左右声道
sine2.output.connect(0,lineOut.input,1);lineOut.start();