0

我从发送器收到周期性信号(每分钟节拍) ,现在想在一段时间内调用方法,例如发送 1/1、1/2、1/4、1/8、1/16。 . 笔记。

我的解决方案是创建一个线程,做一个忙碌的等待,然后执行这些方法。这里的问题是监听信号、处理信号并将其发回会产生几毫秒的延迟(取决于系统)。

所以现在我想确定传入信号和线程周期信号之间的延迟,如果延迟是!= 0,则停止当前线程并在“bpm - delay”毫秒后启动一个新线程。如何才能做到这一点 ?

插图:

发射机信号:|----|----|----|----|

******跑步者信号:|----|----|----|----|

将跑步者信号延迟“onePeriod - delay”毫秒:

发射机信号:|----|----|----|----|

***"转轮信号:**** |----|----|----|----|

两个信号现在同步。

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() {

    }
4

2 回答 2

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 循环应该“锁定”,这样每 16 个半分音符就会准确地发生在节拍上。


改进:

  • 让节拍循环计算速度。
  • 基于当前速度的半分音符时段的起始猜测。
  • 注意显着(即突然)的速度变化,并根据需要重新设置半四分音符循环。
于 2015-02-13T20:31:08.643 回答
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

于 2015-02-13T19:36:58.537 回答