Вам понадобится массив значений PCM, которые вы будете использовать для вычисления значений звуковой волны, и массив байтов, в котором будут храниться значения, которые будут записаны в SourceDataLine
.
Размер Массив PCM установлен на период волны, которую вы создаете. Таким образом, если вы хотите сделать A 440, период (основанный на частоте дискретизации 44100 кадров в секунду) будет 100 (чуть меньше 440).
Первый шаг - заполнить массив PCM случайным числа (достаточно поплавков в диапазоне от -1 до 1). Затем l oop с помощью следующих двух шагов (начиная со второго шага):
- Рассчитайте следующий набор значений PCM на основе приведенной вами формулы.
- Преобразование PCM буферизуйте значения в байтах для каждого аудиоформата и добавьте его в байтовый массив, который будет записан в
SourceDataLine
.
Когда байтовый буфер для SourceDataLine
заполнен, запишите буфер и начните пополнять его для следующей операции записи.
Здесь есть статья , которая также описывает некоторые усовершенствования алгоритма. Подробности преобразования PCM в байты для каждого аудиоформата были описаны в других статьях.
Ниже приведена быстрая и грязная реализация. Код воспроизводит только 200-нотную ноту. Очевидно, что можно было бы переписать это, чтобы оно работало для других заметок. Но он показывает алгоритм в действии и играет.
public class KarplusStrongTone {
float[] pcmArray;
SourceDataLine sdl;
int period = 200;
int sdlIdx = 0;
byte sdlBuffer[] = new byte[4000];
public static void main(String[] args) throws UnsupportedAudioFileException,
IOException, InterruptedException, LineUnavailableException {
KarplusStrongTone kst = new KarplusStrongTone();
kst.initializePCMArray();
kst.makeOutputLine();
kst.play();
}
private void initializePCMArray()
{
pcmArray = new float[period];
for (int i = 0; i < period; i++) pcmArray[i] = (float)(Math.random() * 2 - 1);
}
private void makeOutputLine() throws LineUnavailableException {
AudioFormat audioFmt = new AudioFormat(
AudioFormat.Encoding.PCM_SIGNED,
44100, 16, 1, 2, 44100, false);
Info info = new DataLine.Info(SourceDataLine.class, audioFmt);
sdl = (SourceDataLine)AudioSystem.getLine(info);
sdl.open();
sdl.start();
}
private void play()
{
int countIterations = 0;
float localMax = 1;
while (localMax > 0.00001f)
{
localMax = 0;
for (int i = period - 1; i > 0; i--)
{
pcmArray[i] = (pcmArray[i] + pcmArray[i-1])/2;
localMax = Math.max(Math.abs(pcmArray[i]), localMax);
}
pcmArray[0] = pcmArray[0]/2;
countIterations++; // just curious how long while runs
ship(pcmArray);
}
System.out.println("Iterations = " + countIterations);
}
private void ship(float[] pcm)
{
for (int i = 0; i < period; i++)
{
int audioVal = (int)(pcm[i] * 32767);
sdlBuffer[sdlIdx + i * 2] = (byte)audioVal;
sdlBuffer[sdlIdx + (i * 2) + 1] = (byte)(audioVal >> 8);
}
sdlIdx += (period * 2);
if (sdlIdx == 4000)
{
sdl.write(sdlBuffer, 0, 4000);
sdlIdx = 0;
}
}
}