我试着做一个合成器,它可以工作,我可以和他们一起演奏音乐。然而,我做的第一个合成器有延迟,你不能播放快速歌曲。因此,我再次尝试使用sourceDataline.flush()方法来加快速度。嗯,它有点修正了,但是拖延太久了。我也试过降低采样率,但延迟太大了。
编辑:原来你可以评论这行keyStateInterface.setFlush(false);,它改善了延迟,但是你仍然不能播放快歌
以下是代码:
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class SoundLine implements Runnable{
KeyStateInterface keyStateInterface;
public SoundLine(KeyStateInterface arg){
keyStateInterface=arg;
}
@Override
public void run() {
AudioFormat audioFormat = new AudioFormat(44100,8,1,true,false);
try {
SourceDataLine sourceDataLine = AudioSystem.getSourceDataLine(audioFormat);
sourceDataLine.open(audioFormat);
sourceDataLine.start();
SynthMain synthMain = new SynthMain();
int v = 0;
while (true) {
int bytesAvailable = sourceDataLine.available();
if (bytesAvailable > 0) {
int sampling = 256/(64);
byte[] bytes = new byte[sampling];
for (int i = 0; i < sampling; i++) {
//bytes[i] = (byte) (Math.sin(angle) * 127f);
float t = (float) (synthMain.makeSound((double)v,44100,keyStateInterface)* 127f);
bytes[i] = (byte) (t);
v += 1;
}
if(keyStateInterface.getFlush()){
sourceDataLine.flush();
}
sourceDataLine.write(bytes, 0, sampling);
//if(!keyStateInterface.isCacheKeysSame())sourceDataLine.flush();
//System.out.println(bytesWritten);
} else {
Thread.sleep(1);
}
//System.out.println(bytesAvailable);
//System.out.println();
//if((System.currentTimeMillis()-mil)%50==0)freq+=0.5;
}
}catch (Exception e){
}
}
}public class SynthMain {
double[] noteFrequency = {
466.1637615181,
493.8833012561,
523.2511306012,
554.3652619537,
587.3295358348,
622.2539674442,
659.2551138257,
698.4564628660,
739.9888454233,
783.9908719635,
830.6093951599,
880.0000000000,
932.3275230362,
987.7666025122,
1046.5022612024,
1108.7305239075,
1174.6590716696,
1244.5079348883,
1318.5102276515,
1396.9129257320,
1479.9776908465,
1567.9817439270,
1661.2187903198,
1760.0000000000,
1864.6550460724,
1975.5332050245,
2093.0045224048,
2217.4610478150,
2349.3181433393,
2489.0158697766,
2637.0204553030,
2793.8258514640,
2959.9553816931,
3135.9634878540,
3322.4375806396,
3520.0000000000,
3729.3100921447,
};
boolean[] keys = new boolean[noteFrequency.length];
public double makeSound(double dTime,double SampleRate,KeyStateInterface keyStateInterface){
if(keyStateInterface.getSizeOfMidiKey()>0){
keyStateInterface.setFlush(true);
for(int i=0;i<keyStateInterface.getSizeOfMidiKey();i++) {
KeyRequest keyRequest = keyStateInterface.popMidiKey();
if(keyRequest.getCommand()==-112){
if(keyRequest.getVelocity()>0)keys[keyRequest.getArg1()] = true;
if(keyRequest.getVelocity()<1)keys[keyRequest.getArg1()] = false;
System.out.println(keyRequest.getVelocity());
}
}
}else{
keyStateInterface.setFlush(false);
}
//System.out.println("makeSound");
double a = 0.0;
for(int i=0;i<keys.length;i++){
if(keys[i]){
a+=Oscillate(dTime,noteFrequency[i],(int)SampleRate);
}
}
return a*0.4;
}
public double Oscillate(double dTime,double dFreq,int sampleRate){
double period = (double)sampleRate / dFreq;
return Math.sin(2.0 * Math.PI * (int)dTime / period);
}
}import java.util.ArrayList;
import java.util.Stack;
public class KeyState implements KeyStateInterface{
boolean isFlush;
ArrayList<KeyRequest> keyRequest = new ArrayList<KeyRequest>();
ArrayList<KeyRequest> midiKeyRequest = new ArrayList<KeyRequest>();
@Override
public void pushKey(int keyCode, boolean press) {
keyRequest.add(new KeyRequest(KeyRequest.KEY,keyCode,press));
}
@Override
public void pushMidiKey(int command, int arg1, int velocity) {
midiKeyRequest.add(new KeyRequest(KeyRequest.MIDI_KEY,command,arg1,velocity));
}
@Override
public KeyRequest popKey() {
KeyRequest t = keyRequest.get(keyRequest.size());
return t;
}
@Override
public KeyRequest popMidiKey() {
KeyRequest t = midiKeyRequest.get(keyRequest.size());
midiKeyRequest.remove(keyRequest.size());
return t;
}
@Override
public int getSizeOfKey() {
return keyRequest.size();
}
@Override
public int getSizeOfMidiKey() {
return midiKeyRequest.size();
}
@Override
public boolean getFlush() {
boolean v = isFlush;
isFlush = false;
return v;
}
@Override
public void setFlush(boolean arg) {
isFlush=arg;
}
}发布于 2021-08-16 21:27:35
我还没有深入了解您的代码,但是下面的信息可能会很有用。
SourceDataLine.write()方法在内部使用阻塞队列。它只有在处理数据时才能取得进展。因此,在填充和发送bytes之前,不需要测试可用容量。
我会给予SDL线程10的优先级,因为它的大部分时间都是在阻塞状态下度过的。
而且,我会让这条线路开着运行。我首先从Praxis Live的Neil那里得到了这个建议。不断地重建它是有代价的。在我看来,你正在为每4个字节的音频数据创建一个新的SDL。那将是非常低效的。我怀疑,在256到8K的范围内,在一条开放的线路上运送货物是一个更好的选择,但我没有确凿的事实来支持这一观点。Neil写过所有传输数组都是相同大小的(例如,由synth生成的数据数组与SDL写的大小相同)。
我用java做了一个实时搜索,其中的延迟包括读取鼠标单击和位置的任务,然后将其发送到生成音频数据的synth。我不会把我的延迟归结为允许“在口袋里”开始和停止笔记的精确性,但它仍然很好。我怀疑在我的一端可能会有进一步的优化。
我认为尼尔(前面提到)取得了较好的结果。早在2011年,他就谈到了在5毫秒或更短的时间内实现延迟。
https://stackoverflow.com/questions/68801835
复制相似问题