Как реализовать алгоритм Карплюса-Стронга в Java? - PullRequest
0 голосов
/ 19 апреля 2020

Я посмотрел повсюду, и я не могу найти пример простой Java программы, которая использует алгоритм Karplus-Strong, что странно, потому что я думаю, что это должен быть класс c упражнение по кодированию. Уравнение имеет вид y [n] = x [n] + 0,5 * (y [nN] + y [n- (N + 1)]). Предполагается, что он создает звуковую волну, которая имитирует звучание гитарной струны. Пока у меня есть следующий код:

import javax.sound.sampled.*;

public class Main {

    public static void main(String args[]) throws LineUnavailableException {
        int rate = 44100;
        byte[] buffer = new byte[1];
        AudioFormat af = new AudioFormat(rate, 8, 1, false, true);
        SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
        sdl.open();
        sdl.start();

        for(int i = 1; i < rate; i++) {
            buffer[0] = (byte) (...);
            sdl.write(buffer, 0, 1);
        }   
    }   

}

Как бы я использовал уравнение для создания массива байтов, который я мог бы вставить в свой код?

1 Ответ

1 голос
/ 19 апреля 2020

Вам понадобится массив значений 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;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...