java - SourceDataLine 音频发出爆裂声

标签 java audio javasound

我有一个类可以以任何所需的频率和长度播放纯正弦音,并且它按预期工作 - 除了在每个音调的开始和结束时从扬声器发出轻微的爆裂声。这最初是一个音乐理论实验,但我最近一直在用它来演奏一些歌曲,甚至可能尝试将频率绑定(bind)到键盘并使其成为一种乐器。问题是每个音调之间都会出现爆音,使短语听起来错误。

来源如下:

import java.util.*;
import javax.sound.sampled.*; 

public class Tone {
    public static float SAMPLE_RATE = 44100;

    public static void sound(double frequency, int duration, double velocity)
    throws LineUnavailableException {
        if (frequency < 0)
            throw new IllegalArgumentException("Frequency too low: " + frequency + " is less than 0.0");

        if (duration <= 0)
            throw new IllegalArgumentException("Duration too low: " + duration + " is less than or equal to 0");

        if (velocity > 1.0 || velocity < 0.0)
            throw new IllegalArgumentException("Velocity out of range: " + velocity + " is less than 0.0 or greater than 1.0");

        byte[] wave = new byte[(int)SAMPLE_RATE * duration / 1000];

        for (int i=0; i<wave.length; i++) {
            double angle = i / (SAMPLE_RATE / frequency) * 2.0 * Math.PI;
            wave[i] = (byte)(Math.sin(angle) * 127.0 * velocity);
        }

        // Shape Waveform
        for (int i=0; i < SAMPLE_RATE / 100.0 && i < wave.length / 2; i++) {
            wave[i] = (byte)(wave[i] * i / (SAMPLE_RATE / 100.0));
            wave[wave.length-1-i] =
            (byte)(wave[wave.length-1-i] * i / (SAMPLE_RATE / 100.0));
        }

        AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 1, true, false);
        SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
        sdl.open(af);
        sdl.start();
        sdl.write(wave, 0, wave.length);
        sdl.drain();
        sdl.close();
    }

    public static double HALF_STEP = 1.0595;
    public static double WHOLE_STEP = HALF_STEP * HALF_STEP;
    public static double OCTAL_STEP = 2;

    public static double oct(double octive){
        if (octive < 3)
            throw new IllegalArgumentException("Octive too low: " + octive + " is less than 3.0");

        octive = octive - 2;
        octive = Math.pow(OCTAL_STEP, octive);

        return octive;

    }

    public static void main(String[] args) throws 
    LineUnavailableException {
        // Preset Frequencies in Concert Notation starting from Octive 3
        double rest = 0;
        double c = 130.81;
        double c$ = c * HALF_STEP;
        double d = c$ * HALF_STEP;
        double d$ = d * HALF_STEP;
        double e = d$ * HALF_STEP;
        double f = e * HALF_STEP;
        double f$ = f * HALF_STEP;
        double g = f$ * HALF_STEP;
        double g$ = g * HALF_STEP;
        double a = g$ * HALF_STEP;
        double a$ = a * HALF_STEP;
        double b = a$ * HALF_STEP;

        // Default BPM
        int bpm = 128;

        // Note Duration Calculations
        int whole = 1000 * 240 / bpm;
        int half = 1000 * 120 / bpm;
        int quarter = 1000 * 60 / bpm;
        int eighth = 1000 * 30 / bpm;
        int sixteenth = 1000 * 15 / bpm;
        int thirtysecond = 1000 * 7 / bpm;

        // Test Tones
        Tone.sound (c * oct(3), sixteenth, 0.5);
        Tone.sound (c$ * oct(3), sixteenth, 0.5);
        Tone.sound (d * oct(3), sixteenth, 0.5);
        Tone.sound (d$ * oct(3), sixteenth, 0.5);
        Tone.sound (e * oct(3), sixteenth, 0.5);
        Tone.sound (f * oct(3), sixteenth, 0.5);
        Tone.sound (f$ * oct(3), sixteenth, 0.5);
        Tone.sound (g * oct(3), sixteenth, 0.5);
        Tone.sound (g$ * oct(3), sixteenth, 0.5);
        Tone.sound (a * oct(3), sixteenth, 0.5);
        Tone.sound (a$ * oct(3), sixteenth, 0.5);
        Tone.sound (b * oct(3), sixteenth, 0.5);
        Tone.sound (c * oct(4), sixteenth, 0.5);
        Tone.sound (c$ * oct(4), sixteenth, 0.5);
        Tone.sound (d * oct(4), sixteenth, 0.5);
        Tone.sound (d$ * oct(4), sixteenth, 0.5);
        Tone.sound (e * oct(4), sixteenth, 0.5);
        Tone.sound (f * oct(4), sixteenth, 0.5);
        Tone.sound (f$ * oct(4), sixteenth, 0.5);

        // John Cena, Doot Doot Doot
        Tone.sound(g * oct(4), eighth, 0.5);
        Tone.sound(a * oct(4), sixteenth, 0.5);
        Tone.sound(f * oct(4), sixteenth, 0.5);
        Tone.sound(rest, thirtysecond, 0);
        Tone.sound(g * oct(4), eighth + half, 0.5);

        Tone.sound(rest, eighth, 0);
        Tone.sound(a$ * oct(4), eighth, 0.5);
        Tone.sound(a * oct(4), sixteenth, 0.5);
        Tone.sound(f * oct(4), sixteenth, 0.5);
        Tone.sound(rest, thirtysecond, 0);
        Tone.sound(g * oct(4), eighth + half, 0.5);

        Tone.sound(rest, eighth, 0);
        Tone.sound(g * oct(4), eighth, 0.5);
        Tone.sound(a * oct(4), sixteenth, 0.5);
        Tone.sound(f * oct(4), sixteenth, 0.5);
        Tone.sound(rest, thirtysecond, 0);
        Tone.sound(g * oct(4), eighth + half, 0.5);

        Tone.sound(rest, eighth, 0);
        Tone.sound(a$ * oct(4), eighth, 0.5);
        Tone.sound(a * oct(4), sixteenth, 0.5);
        Tone.sound(f * oct(4), sixteenth, 0.5);
        Tone.sound(rest, thirtysecond, 0);
        Tone.sound(g * oct(4), eighth + half, 0.5);
    }
}

注意:我对java确实很陌生,而且我确信我的风格很困惑。当您在这里时,请随意批评或更改。

最佳答案

您听到的是频率从 0 音量到最大音量不连续的结果。要消除爆音,您需要逐渐启动或停止音调。

实现此目的的一个简单方法是创建一个音量因子,并在音频样本的第一个和/或最后一个声音帧的过程中将其从 0 增加到 1。您必须进行试验才能确定所需的确切帧数。我会尝试类似 64 帧的东西。如果持久性有机污染物仍然存在,您可以随时将该数字调大,调小也可以。

也许是这样的:

int rampFrames = 64;

for (int i = 0; i < rampFrames; i++)
{
     wave[i] *= i/(float)rampFrames;
}

可以对版本执行类似的操作。而且,如果在转换时出现爆音,每当您更改音调的音量时,可能都必须执行类似的操作。

感知量并不严格符合上面使用的线性增量,但进展如此之快,这很可能不会成为问题。

关于java - SourceDataLine 音频发出爆裂声,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33453489/

相关文章:

java - 抛出 ConcurrentModificationException 系统依赖

java - selenium-java-2.43.0 不适用于 firefox 36

video - 您如何安全地识别 mp4 是纯音频文件还是视频文件

javascript - HTML5 检查音频是否正在播放?

python - 代码完成时发出声音警报

java - 将 Vorbis SPI 依赖项添加到 Maven 项目

java - 如何在Java中的两个混合器之间创建 "connection"?

java - 分页库的意外行为

java - 获取外部存储目录不起作用

Java midi 音量控制不起作用