使用 SourceDataLine 音频弹出声音
Popping sound with SourceDataLine audio
我有一个 class 可以播放任何频率和长度的纯正弦音调,它按预期工作 - 除了在每个开始和结束时扬声器发出轻微的爆裂声语气。这最初是一个音乐理论实验,但我最近一直在用它来播放一些歌曲,甚至可能会尝试将频率绑定到键盘并使其成为一种乐器。问题是在每个音调之间出现爆音,使乐句听起来不对。
这是来源:
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 音量到全音量不连续的结果。要消除爆音,您需要逐渐开始或停止音调。
一个简单的方法是创建一个音量因子,并在音频样本的第一个 and/or 最后一个声音帧的过程中将其从 0 增加到 1。您必须通过试验来确定所需的确切帧数。我会尝试 64 帧之类的东西。如果持久性有机污染物仍然存在,您可以随时将此图调大,调小也可以。
也许是这样的:
int rampFrames = 64;
for (int i = 0; i < rampFrames; i++)
{
wave[i] *= i/(float)rampFrames;
}
可以对发布做类似的事情。并且,如果在转换时出现流行音乐,则每当您更改音调的音量时,可能都必须执行类似的操作。
感知量与上面使用的线性增量不严格匹配,但进展如此之快,这很可能不会成为问题。
我有一个 class 可以播放任何频率和长度的纯正弦音调,它按预期工作 - 除了在每个开始和结束时扬声器发出轻微的爆裂声语气。这最初是一个音乐理论实验,但我最近一直在用它来播放一些歌曲,甚至可能会尝试将频率绑定到键盘并使其成为一种乐器。问题是在每个音调之间出现爆音,使乐句听起来不对。
这是来源:
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 音量到全音量不连续的结果。要消除爆音,您需要逐渐开始或停止音调。
一个简单的方法是创建一个音量因子,并在音频样本的第一个 and/or 最后一个声音帧的过程中将其从 0 增加到 1。您必须通过试验来确定所需的确切帧数。我会尝试 64 帧之类的东西。如果持久性有机污染物仍然存在,您可以随时将此图调大,调小也可以。
也许是这样的:
int rampFrames = 64;
for (int i = 0; i < rampFrames; i++)
{
wave[i] *= i/(float)rampFrames;
}
可以对发布做类似的事情。并且,如果在转换时出现流行音乐,则每当您更改音调的音量时,可能都必须执行类似的操作。
感知量与上面使用的线性增量不严格匹配,但进展如此之快,这很可能不会成为问题。