Java上的多个声音

我试图在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.*; /** * Standard audio. This class provides a basic capability for creating, * reading, and saving audio. * 

* The audio format uses a sampling rate of 44,100 (CD quality audio), 16-bit, * monaural. * *

* For additional documentation, see Section 1.5 of * Introduction to Programming in Java: An Interdisciplinary Approach 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; // 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); } } 

关于简单地将两个声音组合成一个的评论,我稍微扩展一下……

你表明了这一点:

 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,...,...); 

试试Pulpcore

OpenAL框架中的iOS中提供的开源OpenAL音频API提供了一个优化的界面,用于在播放期间定位立体声场中的声音。 播放,定位和移动声音就像在其他平台上一样。 OpenAL还可以让你混合声音。 有关更多信息,请访问: http : //developer.apple.com/library/IOS/#documentation/AudioVideo/Conceptual/MultimediaPG/UsingAudio/UsingAudio.html

您可以使用JSyn库播放多个特定频率的声音。

它现在可以满足您的需求,如果您想要做更复杂的事情,您可能希望以后再使用它。

http://www.softsynth.com/jsyn/

作为一个例子,我还设法找出一些稍微复杂的声音:

JSyn,警报声使用振荡器馈送/控制/ inputInto /菊花链由另一个振荡器和一个常数…并产生多个声音

该代码将立即生成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();