Pydub 的实时连续声音

Real time continuous sounds with Pydub

我正在尝试制作一个从 wifi 探测日志生成声音的程序,以便设备数量(在一定距离内)生成音调,并且 rssi 是频率。

我试图让它尽可能实时,但无法弄清楚如何使音调连续并根据值的变化改变频率。

'''
This program takes a log file from a
wifi probe and translates it into sound
'''

import time
import math        #import needed modules
import pyaudio     #sudo apt-get install python-pyaudio
import threading
from threading import Thread
from pydub import AudioSegment
from pydub.generators import Sine
from pydub.playback import play
import signal


def logData():
    '''
    Takes log file data and puts it into database
    updates every 1 sec
    '''
    global dic
    global tone
    tone = []
    dic = {}
    while True:
        with open("/Users/CWT/Documents/VÆRKER/probemon.log") as f:
            for line in f:
                (key, val) = line.split()
                if val <= str(-50):
                    dic[(key)] = val
        print (dic)
        time.sleep(1)



def sound():

    '''
    Generate sounds
    '''

    # Play final tone
    while (True):
        with open("/Users/CWT/Documents/VÆRKER/probemon.log") as i:
            try:
                tone1 = Sine(abs(int(list(dic.values())[0]))).to_audio_segment(3000)            
                tone2 = Sine(abs(int(list(dic.values())[1]))).to_audio_segment(3000)        
                tone3 = Sine(abs(int(list(dic.values())[2]))).to_audio_segment(3000)
            except:
                print('Index error')
            try:
                multitone1 = tone1
                multitone2 = tone1.overlay(tone2)
                multitone3 = tone3.overlay(multitone2)
            except:
                print('Multitone error')


            try:
                if len(dic) <= 1:
                    play(multitone1.fade_in(250).fade_out(250))
                elif len(dic) == 2:
                    play(multitone2.fade_in(250).fade_out(250))
                elif len(dic) >= 3:
                    play(multitone3.fade_in(250).fade_out(250))
            except:
                print('Playback error')

if __name__ == '__main__':
    try:
        Thread(target = logData).start()
        time.sleep(1)
        Thread(target = sound).start()
    except KeyboardInterrupt:
        print('Interrupted')

我能够制作一个样板解决方案,您可以根据自己的需要进行定制。

这是中心思想

1) 在每秒

重复的连续循环中使用 os.popen 读取日志文件的最后一行

2) RSSI值很小,这些值之间的差异也很小。我们在这里将其乘以常量 100 以产生明显的差异。您可以尝试不同的值。

3) 使用 pydub 我们创建正弦音并播放它们

代码

from pydub.generators import Sine
from pydub import AudioSegment
from pydub.playback import play
import os
import time

sr = 44100  # sample rate
bd = 16     # bit depth
l  = 50.0     # duration in millisec

last_line = ""  #to avoid same line played again
log_file = "probemon.log"


while True:

    line = os.popen('tail -n 1 {}'.format(log_file)).read()
    if last_line  == line:
        pass

    else:
        key, val = line.split()
        f = abs(int(val)) * 100

        #create sine wave of given freq
        sine_wave = Sine(f, sample_rate=sr, bit_depth=bd)

        #Convert waveform to audio_segment for playback and export
        sine_segment = sine_wave.to_audio_segment(duration=l)

        print "mac:{} , rssi:{}".format(key,val)
        #Play audio segment
        play(sine_segment)

        last_line = line
        time.sleep(1)  #sleep 1 sec, synch this with log file fill

我通过从不同的终端逐行填充 probemon.log 文件进行测试,延迟 1 秒。如果没有新数据,循环将等待。

EDIT1

音频 "tone" 有 "frequency" ,当你改变频率时,音调会改变。 根据我们的讨论,由于我们需要实时改变音调,因此我们不能使用 pydub,这对离线操作非常有用。

pyaudio 有一个使用回调的非阻塞方法,它允许在实时播放流数据时对其进行操作。

此解决方案根据日志的最后一行连续播放音频,直到日志数据发生变化。

此解决方案还消除了合并两个音调时出现的爆音/爆音。

灵感来自

import pyaudio
import numpy as np
from time import time,sleep
import os

CHANNELS = 2
RATE = 44100

TT = time()
freq = 100
newfreq = 100
phase = 0
log_file = "probemon.log"

def callback(in_data, frame_count, time_info, status):
    global TT,phase,freq,newfreq
    if newfreq != freq:
        phase = 2*np.pi*TT*(freq-newfreq)+phase
        freq=newfreq
    left = (np.sin(phase+2*np.pi*freq*(TT+np.arange(frame_count)/float(RATE))))
    data = np.zeros((left.shape[0]*2,),np.float32)
    data[0::2] = left  #left data
    data[1::2] = left  #right data
    TT+=frame_count/float(RATE)
    return (data, pyaudio.paContinue)

p = pyaudio.PyAudio()

stream = p.open(format=pyaudio.paFloat32,
                channels=CHANNELS,
                rate=RATE,
                output=True,
                stream_callback=callback)

stream.start_stream()
tmphold = ""
try:
    while True:
        line = os.popen('tail -n 1 {}'.format(log_file)).read()
        try:
            key, val = line.split()
        except:
            key, val = "default", 0.0

        f = abs(int(val))  
        newfreq = f * 10  #update freq per log
        if newfreq != tmphold:
            tmphold = newfreq
            print "mac:{} , rssi:{} , freq:{} 
finally:
    stream.stop_stream()
    stream.close()
    p.terminate()

结果

mac:default , rssi:0.0 , freq:0 Hz
mac:d8:8f:76:1a:cb:65 , rssi:-43 , freq:430 Hz
mac:04:4f:4c:77:72:8f , rssi:-51 , freq:510 Hz
mac:20:39:56:af:51:49 , rssi:-39 , freq:390 Hz
mac:20:39:56:af:51:49 , rssi:-45 , freq:450 Hz
mac:5e:e2:1d:a3:d2:da , rssi:-47 , freq:470 Hz
mac:5e:e2:1d:a3:d2:da , rssi:-49 , freq:490 Hz
mac:12:84:16:9c:75:ee , rssi:-43 , freq:430 Hz
mac:da:a1:19:71:4d:0c , rssi:-55 , freq:550 Hz
mac:d8:8f:76:1a:cb:65 , rssi:-49 , freq:490 Hz