使用正弦波实现流畅过渡

huangapple go评论73阅读模式
英文:

Make a fluent transition using sine waves

问题

以下是你的代码的翻译部分:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

public class Test {
	private static final int MAX_LENGTH = 1000;
	private Random r = new Random();
	protected static final int SAMPLE_RATE = 32 * 1024;

	public static byte[] createSinWaveBuffer(double freq, int ms) {
		int samples = ((ms * SAMPLE_RATE) / 1000);
		byte[] output = new byte[samples];
		double period = (double) SAMPLE_RATE / freq;
		for (int i = 0; i < output.length; i++) {
			double angle = 2.0 * Math.PI * i / period;
			output[i] = (byte) (Math.sin(angle) * 0x7f);
		}
		return output;
	}

	public static void main(String[] args) throws LineUnavailableException {
		List<Double> freqs = new Test().generate();
		System.out.println(freqs);
		final AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 1, true, true);
		SourceDataLine line = AudioSystem.getSourceDataLine(af);
		line.open(af, SAMPLE_RATE);
		line.start();
		freqs.forEach(a -> {
			byte[] toneBuffer = createSinWaveBuffer(a, 75);
			line.write(toneBuffer, 0, toneBuffer.length);
		});
		line.drain();
		line.close();
	}

	private List<Double> generate() {
		List<Double> frequencies = new ArrayList<>();
		double[] values = new double[] { 4.0/3,1.5,1,2 };
		double current = 440.00;
		frequencies.add(current);
		while (frequencies.size() < MAX_LENGTH) {
            //Generate a frequency in Hz based on harmonics and a bit math.
			boolean goUp = Math.random() > 0.5;
			if (current < 300)
				goUp = true;
			else if (current > 1000)
				goUp = false;
			if (goUp) {
				current *= values[Math.abs(r.nextInt(values.length))];
			} else {
				current *= Math.pow(values[Math.abs(r.nextInt(values.length))], -1);
			}
			frequencies.add(current);
		}
		return frequencies;
	}
}

如果你想要去除音调之间的"knocking"声音,你可以尝试以下几种方法:

  1. 渐变淡出和淡入: 在音调切换的地方,你可以添加渐变淡出和渐变淡入效果,以平滑地过渡音调之间的差异,避免突然的音频变化。

  2. 平滑频率变化: 你可以尝试在音调变化时平滑地改变频率,而不是突然地跳跃到新的频率。这可以通过逐渐增加或减小频率来实现。

  3. 使用合成音: 你可以考虑使用合成音,而不是简单的正弦波。合成音可以提供更平滑的音频效果,并且可以更容易地控制音调之间的过渡。

  4. 添加音频效果: 你可以尝试添加音频效果,如混响或合唱效果,来模糊音调之间的过渡,使其听起来更自然。

你可以尝试这些方法来改进你的随机"melody"生成和播放过程,以减少"knocking"声音。

英文:

I have following code:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class Test {
private static final int MAX_LENGTH = 1000;
private Random r = new Random();
protected static final int SAMPLE_RATE = 32 * 1024;
public static byte[] createSinWaveBuffer(double freq, int ms) {
int samples = ((ms * SAMPLE_RATE) / 1000);
byte[] output = new byte[samples];
double period = (double) SAMPLE_RATE / freq;
for (int i = 0; i < output.length; i++) {
double angle = 2.0 * Math.PI * i / period;
output[i] = (byte) (Math.sin(angle) * 0x7f);
}
return output;
}
public static void main(String[] args) throws LineUnavailableException {
List<Double> freqs = new Test().generate();
System.out.println(freqs);
final AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 1, true, true);
SourceDataLine line = AudioSystem.getSourceDataLine(af);
line.open(af, SAMPLE_RATE);
line.start();
freqs.forEach(a -> {
byte[] toneBuffer = createSinWaveBuffer(a, 75);
line.write(toneBuffer, 0, toneBuffer.length);
});
line.drain();
line.close();
}
private List<Double> generate() {
List<Double> frequencies = new ArrayList<>();
double[] values = new double[] { 4.0/3,1.5,1,2 };
double current = 440.00;
frequencies.add(current);
while (frequencies.size() < MAX_LENGTH) {
//Generate a frequency in Hz based on harmonics and a bit math.
boolean goUp = Math.random() > 0.5;
if (current < 300)
goUp = true;
else if (current > 1000)
goUp = false;
if (goUp) {
current *= values[Math.abs(r.nextInt(values.length))];
} else {
current *= Math.pow(values[Math.abs(r.nextInt(values.length))], -1);
}
frequencies.add(current);
}
return frequencies;
}
}

I want to generate a random "melody", beginning from A(hz=440). I do this using random numbers to determine, whether the tone goes up or down.

My problem:
I can generate the melody, but if I play it, there is always a "knocking" sound between each tone. What could I do to remove it, so it sounds better?

答案1

得分: 0

在每个音调的开始和结束时,发送到您的SourceDataLine的信号会在瞬间从音量0跳变到完整的正弦波。或者,它会从一个正弦波中的某个任意值跳变到下一个正弦波的初始值。大的跳变可以产生许多常被听作点击声的泛音。

为了解决这个问题,在您的createSineWaveBuffer方法中,通过将值乘以一个因子来平滑缓冲区的开始和结束,对于音调的开始,这个因子的范围从0到1,对于音调的结束,从1到0。您执行此操作的帧数主要取决于美感和采样率。我认为1毫秒的过渡可能是一个大致的最小值。我所使用的一个商业数字合成器将其作为最小值。对于44100帧每秒,这意味着将过渡分为44个步骤,例如0/44,1/44,2/44等,您将其乘以缓冲区开头的数据值,然后将其反转,乘以缓冲区末尾的数据值。

我倾向于选择64或128个步骤。44100的128个步骤大约为音符开始时间只需约0.003秒,而且应该使过渡足够平滑,以消除信号中的“不连续性”。当然,如果听起来更令人愉悦,您可以选择更长的过渡时间。

如果您这样做(如果过渡时间足够长),就不应该需要应用低通滤波器。

英文:

At the beginning and ending of each tone, the signal going to your SourceDataLine jumps from volume 0 to the full out sine wave instantaneously. Or, it jumps from some arbitrary value in one sine wave to the beginning value in the next. Large jumps can create many overtones which are often heard as clicks.

To remedy this, in your method createSineWaveBuffer, it would be helpful to smooth out the start and end of the buffer by multiplying the values by a factor that ranges from 0 to 1 for the start of the tone, and 1 to 0 for the end of the tone. The number of frames over which you do this depends mostly on esthetics and the sample rate. I think 1 millisecond transitions might work as a ballpark minimum. A commercial digital synth that I have uses that as the smallest value. For 44100 fps, that comes to dividing the transition into 44 steps, e.g., 0/44, 1/44, 2/44, etc. that you multiply to the data values at the start of the buffer, and the reverse that you multiple against the end of the buffer.

I'd be tempted to prefer 64 or 128 steps. 128 steps at 44100 comes to a note onset that only takes about 0.003 seconds, and it should make the transition smooth enough to eliminate the "discontinuity" in the signal. Of course you can choose longer transitions if it sounds more pleasing.

If you do this (if the transition is long enough) there shouldn't be any need to apply low-pass filtering.

huangapple
  • 本文由 发表于 2020年7月25日 00:45:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/63077964.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定