在 Java 中交替播放 2 个不同的频率
Play 2 different frequencies alternatively in Java
我是 Java Sounds 的新手。我想在指定的时间内循环播放 2 个不同的频率,每个频率播放 1 秒。
比如,如果我有 2 个频率 440hz 和 16000hz,并且时间段是 10 秒,那么每 'even' 秒播放 440hz,每 'odd' 秒播放 16000hz,即交替播放 5 秒。
我通过一些示例学到了一些东西,并且我还制作了一个程序,该程序针对单个用户指定的频率运行一段时间,该频率也是用户在这些示例的帮助下给出的。
如果有人能帮我解决这个问题,我将不胜感激。
谢谢
我也附上那个单频代码供参考。
import java.nio.ByteBuffer;
import java.util.Scanner;
import javax.sound.sampled.*;
public class Audio {
public static void main(String[] args) throws InterruptedException, LineUnavailableException {
final int SAMPLING_RATE = 44100; // Audio sampling rate
final int SAMPLE_SIZE = 2; // Audio sample size in bytes
Scanner in = new Scanner(System.in);
int time = in.nextInt(); //Time specified by user in seconds
SourceDataLine line;
double fFreq = in.nextInt(); // Frequency of sine wave in hz
//Position through the sine wave as a percentage (i.e. 0 to 1 is 0 to 2*PI)
double fCyclePosition = 0;
//Open up audio output, using 44100hz sampling rate, 16 bit samples, mono, and big
// endian byte ordering
AudioFormat format = new AudioFormat(SAMPLING_RATE, 16, 1, true, true);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line matching " + info + " is not supported.");
throw new LineUnavailableException();
}
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(format);
line.start();
// Make our buffer size match audio system's buffer
ByteBuffer cBuf = ByteBuffer.allocate(line.getBufferSize());
int ctSamplesTotal = SAMPLING_RATE * time; // Output for roughly user specified time in seconds
//On each pass main loop fills the available free space in the audio buffer
//Main loop creates audio samples for sine wave, runs until we tell the thread to exit
//Each sample is spaced 1/SAMPLING_RATE apart in time
while (ctSamplesTotal > 0) {
double fCycleInc = fFreq / SAMPLING_RATE; // Fraction of cycle between samples
cBuf.clear(); // Discard samples from previous pass
// Figure out how many samples we can add
int ctSamplesThisPass = line.available() / SAMPLE_SIZE;
for (int i = 0; i < ctSamplesThisPass; i++) {
cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * fCyclePosition)));
fCyclePosition += fCycleInc;
if (fCyclePosition > 1) {
fCyclePosition -= 1;
}
}
//Write sine samples to the line buffer. If the audio buffer is full, this will
// block until there is room (we never write more samples than buffer will hold)
line.write(cBuf.array(), 0, cBuf.position());
ctSamplesTotal -= ctSamplesThisPass; // Update total number of samples written
//Wait until the buffer is at least half empty before we add more
while (line.getBufferSize() / 2 < line.available()) {
Thread.sleep(1);
}
}
//Done playing the whole waveform, now wait until the queued samples finish
//playing, then clean up and exit
line.drain();
line.close();
}
}
您最好的选择可能是创建 Clips,如下面的示例代码所示。
也就是说,MHz 范围通常是听不见的——看起来您的问题中有错字。如果没有错字,您将 运行 遇到 Mr. Nyquist.
的问题
另一个提示:没有人在 Java 中使用 Hungarian Notation。
import javax.sound.sampled.*;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
public class AlternatingTones {
public static void main(final String[] args) throws LineUnavailableException, InterruptedException {
final Clip clip0 = createOneSecondClip(440f);
final Clip clip1 = createOneSecondClip(16000f);
clip0.addLineListener(event -> {
if (event.getType() == LineEvent.Type.STOP) {
clip1.setFramePosition(0);
clip1.start();
}
});
clip1.addLineListener(event -> {
if (event.getType() == LineEvent.Type.STOP) {
clip0.setFramePosition(0);
clip0.start();
}
});
clip0.start();
// prevent JVM from exiting
Thread.sleep(10000000);
}
private static Clip createOneSecondClip(final float frequency) throws LineUnavailableException {
final Clip clip = AudioSystem.getClip();
final AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100f, 16, 1, 2, 44100, true);
final ByteBuffer buffer = ByteBuffer.allocate(44100 * format.getFrameSize());
final ShortBuffer shortBuffer = buffer.asShortBuffer();
final float cycleInc = frequency / format.getFrameRate();
float cyclePosition = 0f;
while (shortBuffer.hasRemaining()) {
shortBuffer.put((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * cyclePosition)));
cyclePosition += cycleInc;
if (cyclePosition > 1) {
cyclePosition -= 1;
}
}
clip.open(format, buffer.array(), 0, buffer.capacity());
return clip;
}
}
我将使用的方法是在输出到 SourceDataLine 时计算帧数。当你写完一秒钟的帧时,切换频率。这将比尝试 fiddle 使用 Clips 提供更好的计时准确性。
我不清楚您显示的代码是您编写的还是复制粘贴的。如果您对它如何不起作用有疑问,如果您展示您的尝试以及产生了哪些错误或异常,我很乐意提供帮助。
当输出到 SourceDataLine 时,必须有一个步骤,根据您拥有的音频格式中指定的 16 位编码,将短值 (-32768..+32767) 转换为两个字节。我看不到这是在您的代码中完成的。 [编辑:可以看到 putShort() 方法在哪里执行此操作,尽管它仅适用于 BigEndian,而不适用于更常见的 LittleEndian。]
您看过 Java 教程了吗
Sound Trail?
我是 Java Sounds 的新手。我想在指定的时间内循环播放 2 个不同的频率,每个频率播放 1 秒。 比如,如果我有 2 个频率 440hz 和 16000hz,并且时间段是 10 秒,那么每 'even' 秒播放 440hz,每 'odd' 秒播放 16000hz,即交替播放 5 秒。
我通过一些示例学到了一些东西,并且我还制作了一个程序,该程序针对单个用户指定的频率运行一段时间,该频率也是用户在这些示例的帮助下给出的。
如果有人能帮我解决这个问题,我将不胜感激。 谢谢
我也附上那个单频代码供参考。
import java.nio.ByteBuffer;
import java.util.Scanner;
import javax.sound.sampled.*;
public class Audio {
public static void main(String[] args) throws InterruptedException, LineUnavailableException {
final int SAMPLING_RATE = 44100; // Audio sampling rate
final int SAMPLE_SIZE = 2; // Audio sample size in bytes
Scanner in = new Scanner(System.in);
int time = in.nextInt(); //Time specified by user in seconds
SourceDataLine line;
double fFreq = in.nextInt(); // Frequency of sine wave in hz
//Position through the sine wave as a percentage (i.e. 0 to 1 is 0 to 2*PI)
double fCyclePosition = 0;
//Open up audio output, using 44100hz sampling rate, 16 bit samples, mono, and big
// endian byte ordering
AudioFormat format = new AudioFormat(SAMPLING_RATE, 16, 1, true, true);
DataLine.Info info = new DataLine.Info(SourceDataLine.class, format);
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line matching " + info + " is not supported.");
throw new LineUnavailableException();
}
line = (SourceDataLine) AudioSystem.getLine(info);
line.open(format);
line.start();
// Make our buffer size match audio system's buffer
ByteBuffer cBuf = ByteBuffer.allocate(line.getBufferSize());
int ctSamplesTotal = SAMPLING_RATE * time; // Output for roughly user specified time in seconds
//On each pass main loop fills the available free space in the audio buffer
//Main loop creates audio samples for sine wave, runs until we tell the thread to exit
//Each sample is spaced 1/SAMPLING_RATE apart in time
while (ctSamplesTotal > 0) {
double fCycleInc = fFreq / SAMPLING_RATE; // Fraction of cycle between samples
cBuf.clear(); // Discard samples from previous pass
// Figure out how many samples we can add
int ctSamplesThisPass = line.available() / SAMPLE_SIZE;
for (int i = 0; i < ctSamplesThisPass; i++) {
cBuf.putShort((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * fCyclePosition)));
fCyclePosition += fCycleInc;
if (fCyclePosition > 1) {
fCyclePosition -= 1;
}
}
//Write sine samples to the line buffer. If the audio buffer is full, this will
// block until there is room (we never write more samples than buffer will hold)
line.write(cBuf.array(), 0, cBuf.position());
ctSamplesTotal -= ctSamplesThisPass; // Update total number of samples written
//Wait until the buffer is at least half empty before we add more
while (line.getBufferSize() / 2 < line.available()) {
Thread.sleep(1);
}
}
//Done playing the whole waveform, now wait until the queued samples finish
//playing, then clean up and exit
line.drain();
line.close();
}
}
您最好的选择可能是创建 Clips,如下面的示例代码所示。 也就是说,MHz 范围通常是听不见的——看起来您的问题中有错字。如果没有错字,您将 运行 遇到 Mr. Nyquist.
的问题另一个提示:没有人在 Java 中使用 Hungarian Notation。
import javax.sound.sampled.*;
import java.nio.ByteBuffer;
import java.nio.ShortBuffer;
public class AlternatingTones {
public static void main(final String[] args) throws LineUnavailableException, InterruptedException {
final Clip clip0 = createOneSecondClip(440f);
final Clip clip1 = createOneSecondClip(16000f);
clip0.addLineListener(event -> {
if (event.getType() == LineEvent.Type.STOP) {
clip1.setFramePosition(0);
clip1.start();
}
});
clip1.addLineListener(event -> {
if (event.getType() == LineEvent.Type.STOP) {
clip0.setFramePosition(0);
clip0.start();
}
});
clip0.start();
// prevent JVM from exiting
Thread.sleep(10000000);
}
private static Clip createOneSecondClip(final float frequency) throws LineUnavailableException {
final Clip clip = AudioSystem.getClip();
final AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 44100f, 16, 1, 2, 44100, true);
final ByteBuffer buffer = ByteBuffer.allocate(44100 * format.getFrameSize());
final ShortBuffer shortBuffer = buffer.asShortBuffer();
final float cycleInc = frequency / format.getFrameRate();
float cyclePosition = 0f;
while (shortBuffer.hasRemaining()) {
shortBuffer.put((short) (Short.MAX_VALUE * Math.sin(2 * Math.PI * cyclePosition)));
cyclePosition += cycleInc;
if (cyclePosition > 1) {
cyclePosition -= 1;
}
}
clip.open(format, buffer.array(), 0, buffer.capacity());
return clip;
}
}
我将使用的方法是在输出到 SourceDataLine 时计算帧数。当你写完一秒钟的帧时,切换频率。这将比尝试 fiddle 使用 Clips 提供更好的计时准确性。
我不清楚您显示的代码是您编写的还是复制粘贴的。如果您对它如何不起作用有疑问,如果您展示您的尝试以及产生了哪些错误或异常,我很乐意提供帮助。
当输出到 SourceDataLine 时,必须有一个步骤,根据您拥有的音频格式中指定的 16 位编码,将短值 (-32768..+32767) 转换为两个字节。我看不到这是在您的代码中完成的。 [编辑:可以看到 putShort() 方法在哪里执行此操作,尽管它仅适用于 BigEndian,而不适用于更常见的 LittleEndian。]
您看过 Java 教程了吗 Sound Trail?