2011-02-03 67 views
1

我試圖用Java生成聲音。最後,我願意不斷地向聲卡發送聲音,但現在我可以發送一個獨特的聲波。使用javax.sound.sampled生成聲音

所以,我用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[]轉換爲輸入流!

回答

5

首先,我想你想short樣品,而不是int

short[] data = new short[samples]; 

因爲你AudioFormat指定16位採樣。 short是16位寬,但是int是32位。

一個簡單的方法將其轉換爲一個流:

  • 分配一個ByteBuffer
  • 使用putShort電話
  • 填充它
  • 裹在ByteArrayInputStream
  • 產生的byte[]創建從一個AudioInputStreamByteArrayInputStream和格式

例子:

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工作正常。我還介紹了一些變量,以使計算更清晰。

+0

您真棒。保存了我的一天:) – NewbiZ 2011-02-04 01:05:06

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(); 
    } 
} 

在這個例子中你時間可持續產生正弦波,但你可以使用這個代碼從任何你想要的來源播放聲音。你只需要確保你的樣本格式正確。在這種情況下,我使用44100 Hz採樣率的原始未壓縮16位採樣。但是,如果要播放文件中的音頻,可以使用Clip對象

public void play(File file){ 
    Clip clip=AudioSystem.getClip(); 
    clip.open(AudioSystem.getAudioInputStream(file)); 
    clip.loop(1); 
}