我从Transmitter获得定期信号(每分钟节拍),现在想要在一段时间内调用方法,例如发送1 / 1,1 / 2,1 / 4,1 / 8,1 / 16,..笔记。
我的解决方案是创建一个线程,执行繁忙的等待,然后执行这些方法。这里的问题是,收听信号,处理信号并将其发送回来会产生几毫秒的延迟(取决于系统)。
所以现在我想确定输入信号和线程的周期信号之间的延迟,如果延迟是!= 0,停止当前线程并在" bpm - delay&#34之后启动一个新线程;毫秒。怎么办呢?
插图:
发射器信号:| ---- | ---- | ---- | ---- |
******跑者信号:| ---- | ---- | ---- | ---- |
延迟转轮信号由" onePeriod - 延迟"毫秒:
发射器信号:| ---- | ---- | ---- | ---- |
***"跑者信号:**** | ---- | ---- | ---- | ---- |
两个信号现在都是同步的。
public class Quantiser implements Receiver{
private int[] bpmsInMillis = new int[4];
private int bpmInMillis=0;
private double smallestNote = 1;
private long period=0;
private long fire=0;
private long prevTimeStamp=0;
private Runnable runny = new Runnable() {
@Override
public void run() {
while(true){
fire = System.nanoTime() + period;
while(System.nanoTime() < fire){} // busy wait
// Call some methods here.
}
}
};
private Thread thread = new Thread(runny);
@Override
public void send(MidiMessage message, long timeStamp) {
// Calculate average bpm
for(int i=0; i<bpmsInMillis.length-1;i++)
bpmsInMillis[i] = bpmsInMillis[i+1];
bpmsInMillis[bpmsInMillis.length-1] = (int) ((timeStamp - prevTimeStamp) / 1000);
bpmInMillis = arithmeticMean(bpmsInMillis);
prevTimeStamp = timeStamp;
period = (long) (bpmInMillis * smallestNote * 1000000);
if(!thread.isAlive()) {
thread.start();
}
/*
else{
Calculate delay between signal and thread-signal.
if(delay != 0){
Delay new thread by "bpm - delay" milliseconds.
Stop old thread.
Start new thread.
}
*/
}
@Override
public void close() {
}
答案 0 :(得分:1)
一种选择是实现锁相环(PLL)。
http://en.wikipedia.org/wiki/Phase-locked_loop
基本上,你需要两个线程:一个线程坐在循环中等待输入节拍,每次获得节拍时,它会记录到达时间。
long time_of_last_beat;
while (true) {
wait_for_next_beat();
time_of_last_beat = System.currentTimeMillis();
}
另一个线程处于循环中,速度提高了16倍:
long semiquaver_duration = <starting guess>;
while (true) {
notify_whoever_cares_that_its_time_for_the_next_semiquaver();
Thread.sleep(sixteenth_note_duration);
long phase_error = System.currentTimeMillis() - time_of_last_beat;
semiquaver_duration += estimate_phase_correction(phase_error);
}
我会留给你写estimate_phase_correction()
函数。具有正确系数的给定误差的线性函数可能是您所需要的。如果你做对了,16x循环应该&#34;锁定&#34;所以每十六个半卦都会在节拍上发生。
改进:
答案 1 :(得分:0)
一般来说,当我处理声音(通常是采样而不是MIDI)时,我发现使用帧计数比使用时间更准确。随着时间的流逝,有太多未知数(线程切片,垃圾收集等)。延迟可能会有所不同,但44100帧(如果是格式)总是1秒。
对于MIDI,不是每个事件都有一个字段,其中包含事件应该发生的时间吗?我看过有节拍/测量和经过时间的读数。在对现有的Midi流进行任何定位时,我会使用该信息而非实时时间戳。
如果这是传送是实时/实时的,但你想通过量化传递它,你是否可以在即将到来的Midi上安排调度信息,即使传入的没有它?然后你就有了一个坚实的定位参考点。
实时参考。 Java中的低延迟音频处理: https://diuf.unifr.ch/main/pai/sites/diuf.unifr.ch.main.pai/files/publications/2007_Juillerat_Mueller_Schubiger-Banz_Real_Time.pdf