Генерация звука с помощью javax.sound.sampled - PullRequest
1 голос
/ 03 февраля 2011

Я пытаюсь генерировать звук с помощью 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[] во входной поток!

Ответы [ 2 ]

5 голосов
/ 03 февраля 2011

Во-первых, я думаю, что вы хотите 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 работает отлично.Я также ввел некоторые переменные, чтобы сделать расчеты более понятными.

0 голосов
/ 12 апреля 2015

Если вы хотите воспроизвести простой звук, вы должны использовать 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 Гц. Однако, если вы хотите воспроизвести аудио из файла, вы можете использовать объект Clip

public void play(File file){
  Clip clip=AudioSystem.getClip();
  clip.open(AudioSystem.getAudioInputStream(file));
  clip.loop(1);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...