Python 尝试模拟恒定字节速率。与时间混淆。睡眠结果 上下文

Python 尝试模拟恒定字节速率。与时间混淆。睡眠结果 上下文,python,linux,windows,sleep,Python,Linux,Windows,Sleep,我在我的计算机(播放器)上使用windows 7,在我的大学计算机(拖缆)上使用linux(debian),我使用ssh控制拖缆。 我试图通过读取波形文件来模拟麦克风的恒定字节率,就像有人在说话一样。问题是字节速率低于目标 选择32KB/s的速率和0.020秒的捕获时间。 我使用time.sleep实现了模拟麦克风,以每0.020秒生成一段数据。但获得的速率约为27KB/s,而不是32KB/s 问题 我决定通过阅读来测试time.sleep在linux机器上的精确性 我做了两种测试。1) 繁忙睡

我在我的计算机(播放器)上使用windows 7,在我的大学计算机(拖缆)上使用linux(debian),我使用ssh控制拖缆。
我试图通过读取波形文件来模拟麦克风的恒定字节率,就像有人在说话一样。问题是字节速率低于目标

选择32KB/s的速率和0.020秒的捕获时间。
我使用time.sleep实现了模拟麦克风,以每0.020秒生成一段数据。但获得的速率约为27KB/s,而不是32KB/s

问题 我决定通过阅读来测试time.sleep在linux机器上的精确性

我做了两种测试。1) 繁忙睡眠2)正常睡眠

从我得到的样本来看,linux机器的睡眠分辨率平均为4ms。在windows上,其小于/等于1ms

问题
  • 什么可能会限制linux机器上的睡眠分辨率
  • (在linux上)为什么繁忙睡眠的分辨率与time.sleep相同
  • 如何通过读取波形文件成功模拟麦克风
  • 代码 实码 导入操作系统 输入波 导入系统 输入io 导入时间

    FORMAT = 8 #get_format_from_width(2)
    NCHANNELS = 1
    FRAMERATE = 16000 # samples per second
    SAMPWIDTH = 2 # bytes in a sample
    BYTE_RATE = FRAMERATE*SAMPWIDTH
    CHUNK_DURATION = 0.020
    CHUNK_BYTES = int(CHUNK_DURATION*BYTE_RATE)
    
    class StreamSimulator:
        def __init__(self):
            wf = wave.open("Kalimba.wav","rb")
            buf = io.BytesIO()
            buf.write(wf.readframes(wf.getnframes()))
            wf.close()
            buf.seek(0)
            self.buf = buf
            self.step = time.time()
    
        def delay(self):
            #delay
            delta = time.time() - self.step 
            self.step=time.time()
            delay = CHUNK_DURATION - delta      
            if delay > 0.001:
                time.sleep(delay)
    
    
        def read(self):
            buf = self.buf  
            data = buf.read(CHUNK_BYTES)
            if len(data) == 0:
                buf.seek(0)
                data = buf.read(CHUNK_BYTES)
    
            self.delay()
            return data
    
        def close(self):
            self.buf.close()
    
    class DynamicPainter:
        def __init__(self):
            self.l=0
    
        def paint(self,obj):        
            str1=str(obj)
            l1=len(str1)
            bs="\b"*self.l
            clean=" "*self.l
            total = bs+clean+bs+str1
            sys.stdout.write(total)
            sys.stdout.flush()
            self.l=l1
    
    if __name__=="__main__":
        painter = DynamicPainter()
        stream = StreamSimulator()
        produced = 0
        how_many = 0
        painted = time.time()
        while True:
            while time.time()-painted < 1:
                d = stream.read()
                produced += len(d)
                how_many += 1
            producing_speed = int(produced/(time.time()-painted))       
            painter.paint("Producing speed: {} how many: {}".format(producing_speed,how_many))
            produced=0
            how_many=0
            painted = time.time()
    

    正如您在链接的问题中所读到的,睡眠没有任何保证,而且根据操作系统的不同,睡眠可能会有很大的差异。但是,如果你想每20毫秒发送一次数据,4毫秒的分辨率似乎就足够了。应该没有必要提高睡眠的准确性

    在Debian上,输入大约0.02秒时,您的计算机睡眠时间约为您要求睡眠时间的12/10。10/12*32是26.6,因此,如果您只接收到27 KB/s,这是有意义的

    使用自适应睡眠时间代替0.02秒的睡眠时间。测量上一次迭代所用的时间(睡眠+发送数据),缩短睡眠时间,使整个迭代花费0.02秒。

    结论 感谢@J.F.Sebastian,我了解到:

    • 与其在每个循环中创建一个新的引用,不如使用截止日期作为时间引用
    • 使用截止日期“摊销”time.sleep的不精确性,在所需比特率附近波动一点,但得到正确(且更恒定)的平均值
    • 您只需要使用time.time()一次,这意味着计算的不精确性会降低
    因此,我得到了一个稳定的32000 B/s,有时达到31999,很少达到31745
    现在我可以听到音乐没有任何延迟或抖动

    我尝试使用,但KB/s的运行速度很奇怪,所以我决定保留deadline实现,它会因为不断添加浮点值而变得不精确。然而,总体结果足以满足我的需要。
    谢谢大家

    最终代码
    “我试图模拟一个恒定的字节速率”-告诉我们更多关于这一点。问题似乎就在这里,而不是在
    time.sleep()中ᵩ 在问题中添加了真正的代码这里是a.@J.F.Sebastian谢谢你提到单调,我不知道!但不幸的是,现在我有另一个问题,因为你看不到主要帖子
    time.monotonic()
    不会改变这里的结果,除非你把系统时间往后退;您可以使用
    time.time()
    。运行脚本,它就像节拍器一样工作。你还需要什么?我忘了包括0.02的尺寸!刚才加的。但我可以得出和你说的相同的结论。除了缩短睡眠时间,我还能预测下一次的睡眠时间吗?比如使用平均值或其他统计方法?我不认为你需要做一些花哨的事情,延迟应该是平衡的。只要测量一下你的read()花费了多长时间,减去你的睡眠时间,你就会得到你的开销。计算你的下一次睡眠时间为0.02-开销。如果这还不够准确,请跟踪全球时间并克服漂移。是的,我尝试了简单的方法,但现在我有了双KB/s。。。我编辑了问题“真实代码”。我看不出原因…你忘了减去告诉python睡觉时间的那部分。添加
    delta-=self.last\u delay
    ,您将获得正确的速率。抱歉,我发现J.F.Sebastian解决方案效果更好!检查我的问题结论。否则,谢谢!
    """
    Debian
    Testing busy_sleep:
    0.100:0.10223722934722901
    0.050:0.051996989250183104
    0.025:0.027996940612792967
    0.020:0.02207831859588623
    0.010:0.011997451782226562
    0.009:0.011997222900390625
    0.008:0.009998440742492676
    0.007:0.007997279167175292
    0.006:0.0079974365234375
    0.005:0.007997465133666993
    0.004:0.005918483734130859
    0.003:0.003997836112976074
    0.002:0.0039977550506591795
    0.001:0.003997611999511719
    Testing normal_sleep:
    0.100:0.1020797061920166
    0.050:0.051999988555908205
    0.025:0.028000001907348634
    0.020:0.02192000865936279
    0.010:0.011999979019165039
    0.009:0.012000055313110351
    0.008:0.010639991760253906
    0.007:0.008000001907348633
    0.006:0.00799997329711914
    0.005:0.008000059127807617
    0.004:0.006159958839416504
    0.003:0.004000000953674317
    0.002:0.00399998664855957
    0.001:0.004000091552734375
    
    $ uname -a
    Linux 3.2.0-4-amd64 #1 SMP Debian 3.2.57-3+deb7u2 x86_64 GNU/Linux
    """
    
    """
    Windows 7
    
    Testing busy_sleep:
    0.100:0.10000572204589844
    0.050:0.05000288486480713
    0.025:0.0250014066696167
    0.010:0.010500597953796388
    0.009:0.010500597953796388
    0.008:0.008000493049621582
    0.007:0.00740041732788086
    0.006:0.006400299072265625
    0.005:0.005400300025939942
    0.004:0.004700303077697754
    0.003:0.003200197219848633
    0.002:0.002700185775756836
    0.001:0.0016000032424926759
    Testing normal_sleep:
    0.100:0.10000579357147217
    0.050:0.0500028133392334
    0.025:0.02500150203704834
    0.010:0.01000049114227295
    0.009:0.0100006103515625
    0.008:0.008000493049621582
    0.007:0.007000398635864257
    0.006:0.006000304222106934
    0.005:0.00500030517578125
    0.004:0.0040001869201660155
    0.003:0.0030002117156982424
    0.002:0.0020000934600830078
    0.001:0.0010000944137573243
    """
    
    FORMAT = 8 #get_format_from_width(2)
    NCHANNELS = 1
    FRAMERATE = 16000 # samples per second
    SAMPWIDTH = 2 # bytes in a sample
    BYTE_RATE = FRAMERATE*SAMPWIDTH
    CHUNK_DURATION = 0.020
    CHUNK_BYTES = int(CHUNK_DURATION*BYTE_RATE)
    
    class StreamSimulator:
        def __init__(self):
            wf = wave.open("Kalimba.wav","rb")
            buf = io.BytesIO()
            buf.write(wf.readframes(wf.getnframes()))
            wf.close()
            buf.seek(0)
            self.buf = buf
            self.step = time.time()
    
        def delay(self):
            #delay
            delta = time.time() - self.step 
            self.step=time.time()
            delay = CHUNK_DURATION - delta      
            if delay > 0.001:
                time.sleep(delay)
    
    
        def read(self):
            buf = self.buf  
            data = buf.read(CHUNK_BYTES)
            if len(data) == 0:
                buf.seek(0)
                data = buf.read(CHUNK_BYTES)
    
            self.delay()
            return data
    
        def close(self):
            self.buf.close()
    
    class DynamicPainter:
        def __init__(self):
            self.l=0
    
        def paint(self,obj):        
            str1=str(obj)
            l1=len(str1)
            bs="\b"*self.l
            clean=" "*self.l
            total = bs+clean+bs+str1
            sys.stdout.write(total)
            sys.stdout.flush()
            self.l=l1
    
    if __name__=="__main__":
        painter = DynamicPainter()
        stream = StreamSimulator()
        produced = 0
        how_many = 0
        painted = time.time()
        while True:
            while time.time()-painted < 1:
                d = stream.read()
                produced += len(d)
                how_many += 1
            producing_speed = int(produced/(time.time()-painted))       
            painter.paint("Producing speed: {} how many: {}".format(producing_speed,how_many))
            produced=0
            how_many=0
            painted = time.time()
    
    def read(self):
        buf = self.buf  
        data = buf.read(CHUNK_BYTES)
        if len(data) == 0:
            buf.seek(0)
            data = buf.read(CHUNK_BYTES)
    
        self.deadline += CHUNK_DURATION 
        delay = self.deadline - time.time()
        if delay > 0:
            time.sleep(delay)
        return data
    
    def read(self):
        self.deadline += 0.020  
        delay = self.deadline - time.perf_counter()
        if delay > 0:
            time.sleep(delay)
        return self._read()