如何使用 javax.sound.sampled 包同时播放一组频率(和弦)
Posted
技术标签:
【中文标题】如何使用 javax.sound.sampled 包同时播放一组频率(和弦)【英文标题】:How to play a set of frequencies (Chords) at the same time with javax.sound.sampled package 【发布时间】:2012-08-09 10:41:28 【问题描述】:我正在尝试实现一个系统,在该系统中,我给出了一组同时播放的频率,目前可以单独播放每个频率。下面我有一个代码,一次播放一个给定的频率。
import java.applet.*;
import java.io.*;
import java.net.*;
import javax.sound.sampled.*;
public final class StdAudio
public static final int SAMPLE_RATE = 44100;
private static final int BYTES_PER_SAMPLE = 2; // 16-bit audio
private static final int BITS_PER_SAMPLE = 16; // 16-bit audio
private static final double MAX_16_BIT = Short.MAX_VALUE; // 32,767
private static final int SAMPLE_BUFFER_SIZE = 4096;
private static SourceDataLine line; // to play the sound
private static byte[] buffer; // our internal buffer
private static int bufferSize = 0;
// not-instantiable
private StdAudio()
// static initializer
static init();
// open up an audio stream
private static 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);
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 static 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 static 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 static void play(double[] input)
for (int i = 0; i < input.length; i++)
play(input[i]);
private static double[] tone(double hz, double duration)
int N = (int) (StdAudio.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 / StdAudio.SAMPLE_RATE);
return a;
/**
* Test client - play an A major scale to standard audio.
*/
public static void main(String[] args)
double hz = 440.4// 440.4 Hz for 1 sec
double duration = 1.0;
double[] a = tone(hz,duration);
StdAudio.play(a);
【问题讨论】:
+1 sscce 嗨,你最终找到解决方案了吗? 【参考方案1】:简单地将每个时间点的样本值相加,然后除以适当的比例因子以使您的值保持在范围内。例如同时播放 A 440 和 C 523.25:
double[] a = tone(440,1.0);
double[] b = tone(523.25,1.0);
for( int i=0; i<a.length; ++ i )
a[i] = ( a[i] + b[i] ) / 2;
StdAudio.play(a);
【讨论】:
什么是StdAudio
? tone()
?
@trashgod:阅读问题:它们直接来自 abdal 的优秀代码。例如,参见他的主要功能。
啊,我现在明白了。 DYMa.length
?我可以看到在这个较低级别混合的吸引力。 Abdal 将不得不权衡可扩展性与复杂性。 +1 用于利用 OP 的方法。【参考方案2】:
在此example 中,Tone
为每个 Note
打开一个 SourceDataLine
,但您可以打开和弦中的音符一样多的行。然后只需将和弦的write()
的每个Note
转换为单独的一行。您可以使用Note.REST
创建琶音效果。
【讨论】:
感谢回复,我希望同时播放一组频率,而不是琶音效果。如果可能的话,你能给我一个两个不同频率的例子吗?非常感谢。 对于一个双音和弦,只需使用AudioSystem.getSourceDataLine()
打开两行,然后使用Tone#play()
在每行写一个和弦的音符。
这会带来很多开销,而且很难保持同步。【参考方案3】:
你可以为每个频率使用不同的线程:)
here you can find a small example of threads in java
希望对你有帮助:)
【讨论】:
我不明白这会有什么帮助。我会将这些行放在AudioSystem.getSourceDataLine()
内部使用的默认Mixer
中。以上是关于如何使用 javax.sound.sampled 包同时播放一组频率(和弦)的主要内容,如果未能解决你的问题,请参考以下文章
Java getAudioInputStream 试图读取音频文件,得到 javax.sound.sampled.UnsupportedAudioFileException,
使用 javax.sound.sampled.Clip 在游戏中播放、循环和停止多个声音。意外错误
Java Wav 文件错误(javax.sound.sampled.UnsupportedAudioFileException:无法从输入文件获取音频输入流)