Почему этот код создает очень шумную синусоиду?

Я пытаюсь написать очень простой звуковой синтезатор на Java. Я использую пакет javax.sound.sampled. Приведенный ниже код работает, но синусоида очень шумная и звучит так, будто рядом с волной воспроизводится какой-то тихий теплый шум.

try {
    double sampleRate = 44100;
    //8 bits per sample, so a byte.
    AudioFormat audioFormat = new AudioFormat((float) sampleRate, 8, 1, true, false);
    SourceDataLine line = AudioSystem.getSourceDataLine(audioFormat);

    line.open(audioFormat);
    line.start();

    //A4
    double freq = 440.0;

    byte[] buf = new byte[1];

    //the formula for a sample is amplitude * sin(2.0 * PI * freq * time)
    for (int i = 0; i < sampleRate; i++) {
        double t = (i / (sampleRate - 1));
        double sample = 0.1 * Math.sin(2.0 * Math.PI * freq * t);

        //scaling the sound from -1, 1 to -127, 127
        buf[0] = (byte) (sample * (double) Byte.MAX_VALUE);

        line.write(buf, 0, 1);
    }

    line.drain();
    line.stop();
    line.close();
} catch (Exception e) {
    throw new RuntimeException(e);
}

Я поместил сгенерированный звук в эквалайзер, чтобы убедиться, что звук на самом деле шумный, и действительно:

это то, что у меня есть

Доминирующая частота 440 Гц, но есть и другие частоты, которых быть не должно. Почему это происходит? Как это исправить?


person abababab    schedule 06.03.2019    source источник
comment
Интересно. Я немного работал с выводом звука на Java. Можете ли вы напечатать, чтобы утешить результат вычисления, входящего в переменную выборки? Я бы предположил, что это проблема точности с плавающей запятой, так как ваш результат будет приблизительным.   -  person Scuba Steve    schedule 06.03.2019
comment
Глядя на ваш код, может ли размер выборки быть проблемой? Вы теряете точность, помещая этот образец в 8 бит.   -  person Scuba Steve    schedule 06.03.2019
comment
Есть ли причина, по которой вы умножаете на 0.1? Что произойдет, если вы избавитесь от умножения на 0.1, а затем подадите этот сигнал в свой эквалайзер?   -  person Luke Woodward    schedule 06.03.2019
comment
Вот первые 100 образцов pastebin.com/tMCK5MUH.   -  person abababab    schedule 06.03.2019
comment
Я умножаю на 0,1, чтобы амплитуда не была слишком высокой. Если я не умножаю, происходит то же самое, только сигнал становится громче.   -  person abababab    schedule 06.03.2019
comment
Во-первых, я согласен с Люком, что вы странным образом настраиваете амплитуду. Вы умножаете функцию Sin на 0,1, а затем снова умножаете на Byte.MaxValue. Во-вторых, вы храните 64-битное число с плавающей запятой в 8 битах, так что это приведет к потере тонны точности, если вы не сделаете еще более странные вещи (я не помню, что происходит, когда вы приводите тип double к byte на Яве).   -  person Scuba Steve    schedule 06.03.2019
comment
Сделайте размер выборки 64-битным и попробуйте следующее: bethecoder.com/applications/articles/java/basics/ Вам также потребуется изменить способ записи в строку исходных данных.   -  person Scuba Steve    schedule 07.03.2019


Ответы (1)


Вот ваша синусоида:

Зубчатая синусоида

Это очень неровно, потому что вы используете низкую битовую глубину в сочетании с низкой амплитудой. У вас есть только 25 различных образцов значений на выбор.

Вот вместо этого ваша синусоида, если вы установите амплитуду на 1,0, используя полный диапазон вашего 8-битного образца:

Гладкая синусоида

А здесь сохраняется амплитуда 0,1, но вместо этого используются 16-битные сэмплы:

Одиночно ровная синусоида

Оба этих варианта явно будут менее шумными.

person that other guy    schedule 06.03.2019
comment
Хороший ответ. Каким графическим инструментом вы пользовались? - person Scuba Steve; 07.03.2019
comment
У меня не было доступного gnuplot, так что обычный Excel - person that other guy; 07.03.2019
comment
Это хорошее объяснение. Переход от байтовых к коротким образцам устранил проблему. Спасибо :D - person abababab; 07.03.2019