嗨,我正在尝试制作一个在 java 中实时播放的合成器。一个问题是延迟,通过一些建议,我能够达到可接受的延迟。但是,如果您在键盘上快速操作,它会开始跳过键或噼啪作响。我可以在技术上添加一些延音,至少可以部分缓解这个问题。
这是源代码
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;
OperatorStatesInterface operatorStatesInterface;
public SoundLine(KeyStateInterface arg,OperatorStatesInterface operatorStatesInterface){
keyStateInterface=arg;
this.operatorStatesInterface=operatorStatesInterface;
}
@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;
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,operatorStatesInterface)* 127f);
bytes[i] = (byte) (t);
v += 1;
}
if(keyStateInterface.getFlush()){
sourceDataLine.flush();
}
//for(int i = 0;i<100;i++)
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){
}
import javax.sound.midi.*;
import java.util.List;
public class MIDIKeyboardHandler implements Runnable {
KeyStateInterface keyStateInterface;
public MIDIKeyboardHandler(KeyStateInterface arg) {
keyStateInterface = arg;
}
@Override
public void run() {
MidiDevice device;
MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
for (int i = 0; i < infos.length; i++) {
try {
device = MidiSystem.getMidiDevice(infos[i]);
//does the device have any transmitters?
//if it does, add it to the device list
System.out.println(infos[i]);
//get all transmitters
List<Transmitter> transmitters = device.getTransmitters();
//and for each transmitter
for (int j = 0; j < transmitters.size(); j++) {
//create a new receiver
transmitters.get(j).setReceiver(
//using my own MidiInputReceiver
new MidiInputReceiver(device.getDeviceInfo().toString(), keyStateInterface)
);
}
Transmitter trans = device.getTransmitter();
trans.setReceiver(new MidiInputReceiver(device.getDeviceInfo().toString(), keyStateInterface));
//open each device
device.open();
//if code gets this far without throwing an exception
//print a success message
System.out.println(device.getDeviceInfo() + " Was Opened");
} catch (MidiUnavailableException e) {
}
}
}
public class MidiInputReceiver implements Receiver {
KeyStateInterface keyStateInterface;
public String name;
public MidiInputReceiver(String name, KeyStateInterface arg) {
this.name = name;
keyStateInterface = arg;
}
public void send(MidiMessage msg, long timeStamp) {
//System.out.println(msg.getStatus());
keyStateInterface.pushMidiKey(msg.getMessage()[0],msg.getMessage()[1],msg.getMessage()[2]);
System.out.println(msg.getMessage()[0]+","+msg.getMessage()[1]+","+msg.getMessage()[2]+" timestamp: "+timeStamp);
}
@Override
public void close() { }
}
}
如果需要,我可以提供一些额外的代码