我正在尝试使用Java生成声音。最后,我愿意不断向声卡发送声音,但是现在我可以发送一个独特的声波。
所以,我在一个阵列中填充了44100个符号整数,表示一个简单的正弦波,我想将它发送到我的声卡,但我无法让它工作。
int samples = 44100; // 44100 samples/s
int[] data = new int[samples];
// Generate all samples
for ( int i=0; i<samples; ++i )
{
data[i] = (int) (Math.sin((double)i/(double)samples*2*Math.PI)*(Integer.MAX_VALUE/2));
}
我使用以下方法将其发送到声音线:
AudioFormat format = new AudioFormat(Encoding.PCM_SIGNED, 44100, 16, 1, 1, 44100, false);
Clip clip = AudioSystem.getClip();
AudioInputStream inputStream = new AudioInputStream(ais,format,44100);
clip.open(inputStream);
clip.start();
我的问题存在于代码片段之间。我找不到将int[]
转换为输入流的方法!
答案 0 :(得分:5)
首先,我认为您需要short
个样本而不是int
:
short[] data = new short[samples];
因为您的AudioFormat
指定了16位样本。 short
为16位宽,int
为32位。
将其转换为流的简便方法是:
ByteBuffer
putShort
来电byte[]
换成ByteArrayInputStream
AudioInputStream
创建ByteArrayInputStream
并格式化示例:
float frameRate = 44100f; // 44100 samples/s
int channels = 2;
double duration = 1.0;
int sampleBytes = Short.SIZE / 8;
int frameBytes = sampleBytes * channels;
AudioFormat format =
new AudioFormat(Encoding.PCM_SIGNED,
frameRate,
Short.SIZE,
channels,
frameBytes,
frameRate,
true);
int nFrames = (int) Math.ceil(frameRate * duration);
int nSamples = nFrames * channels;
int nBytes = nSamples * sampleBytes;
ByteBuffer data = ByteBuffer.allocate(nBytes);
double freq = 440.0;
// Generate all samples
for ( int i=0; i<nFrames; ++i )
{
double value = Math.sin((double)i/(double)frameRate*freq*2*Math.PI)*(Short.MAX_VALUE);
for (int c=0; c<channels; ++ c) {
int index = (i*channels+c)*sampleBytes;
data.putShort(index, (short) value);
}
}
AudioInputStream stream =
new AudioInputStream(new ByteArrayInputStream(data.array()), format, nFrames*2);
Clip clip = AudioSystem.getClip();
clip.open(stream);
clip.start();
clip.drain();
注意:我已将AudioFormat
更改为立体声,因为当我请求单声道线时它会引发异常。我还将波形的频率提高到可听范围内。
更新 - 之前的修改(直接写入数据行)不是必需的 - 使用Clip
工作正常。我还介绍了一些变量,使计算更加清晰。
答案 1 :(得分:0)
如果您想播放简单的声音,则应使用SourceDataLine。
以下是一个例子:
import javax.sound.sampled.*;
public class Sound implements Runnable {
//Specify the Format as
//44100 samples per second (sample rate)
//16-bit samples,
//Mono sound,
//Signed values,
//Big-Endian byte order
final AudioFormat format=new AudioFormat(44100f,16,2,true,true);
//Your output line that sends the audio to the speakers
SourceDataLine line;
public Sound(){
try{
line=AudioSystem.getSourceDataLine(format);
line.open(format);
}catch(LineUnavailableExcecption oops){
oops.printStackTrace();
}
new Thread(this).start();
}
public void run(){
//a buffer to store the audio samples
byte[] buffer=new byte[1000];
int bufferposition=0;
//a counter to generate the samples
long c=0;
//The pitch of your sine wave (440.0 Hz in this case)
double wavelength=44100.0/440.0;
while(true){
//Generate a sample
short sample=(short) (Math.sin(2*Math.PI*c/wavelength)*32000);
//Split the sample into two bytes and store them in the buffer
buffer[bufferposition]=(byte) (sample>>>8);
bufferposition++;
buffer[bufferposition]=(byte) (sample & 0xff);
bufferposition++;
//if the buffer is full, send it to the speakers
if(bufferposition>=buffer.length){
line.write(buffer,0,buffer.length);
line.start();
//Reset the buffer
bufferposition=0;
}
}
//Increment the counter
c++;
}
public static void main(String[] args){
new Sound();
}
}
在此示例中,您将继续生成正弦波,但您可以使用此代码从您想要的任何来源播放声音。您只需要确保正确格式化样本。在这种情况下,我使用原始的,未压缩的16位采样,采样率为44100 Hz。但是,如果要播放文件中的音频,可以使用Clip对象
public void play(File file){
Clip clip=AudioSystem.getClip();
clip.open(AudioSystem.getAudioInputStream(file));
clip.loop(1);
}