0

我有一小群 Raspberry Pi,都在同一个本地网络 (192.168.1.2xx) 上都运行 Python 3.7.3,一个 (R Pi CM3) 在 Raspbian Buster 上,另一个 (R Pi 4B 8gig) 在 Raspberry Pi 上操作系统 64。

我在一台设备(Pi 4B)上有一个文件,位于 /tmp/speech.wav,它是实时生成的:

192.168.1.201 - /tmp/speech.wav

我有一个在该设备上运行良好的脚本,它告诉我 .wav 文件的播放持续时间(以秒为单位):

import wave
import contextlib

def getPlayTime():
    fname = '/tmp/speech.wav'
    with contextlib.closing(wave.open(fname,'r')) as f:
        frames = f.getnframes()
        rate = f.getframerate()
        duration = round(frames / float(rate), 2)
        return duration

但是 - 需要对该持续时间信息进行操作的节点正在 192.168.1.210 的另一个节点上运行。我不能简单地将各种文件全部移动到同一个节点,因为发生了很多事情,事情在哪里是有原因的。

所以我需要知道的是如何改变我的方法,以便我可以将脚本引用更改为类似这样的伪代码:

fname = '/tmp/speech.wav @ 192.168.1.201'

这样的事情可能吗?在网上搜索似乎我遇到了数以百万计的人在寻找如何获取 IP 地址、修复多个 IP 地址问题、修复重复的 IP 地址问题......但我似乎还没有找到如何简单地检查文件正如我在这里描述的那样,在不同的 IP 地址上。我没有网络安全限制,所以任何设置都可以考虑。帮助将不胜感激。

4

2 回答 2

0

好的,这就是我最终决定的——而且效果很好。使用 ZeroMQ 进行消息传递,我具有获取 wav 播放时间的功能,另一个收集有关即将说出的语音的数据,然后在发送语音之前将所有数据发送到电机核心。运动核心处理时间问题以使下巴与语音同步。所以,我实际上并没有将生成 wav 并将 wav 播放时间的长度返回到最终使用它的节点上的代码,但事实证明消息传递足够快,所以有足够的时间空间来接收、处理和执行运动控制以完美匹配语音。在此处发布此内容,以防它对将来处理类似问题的人们有所帮助。

import time
import zmq
import os
import re
import wave
import contextlib

context = zmq.Context()
socket = context.socket(zmq.REP)
socket.bind("tcp://*:5555")  #Listens for speech to output

print("Connecting to Motor Control")
jawCmd = context.socket(zmq.PUB)
jawCmd.connect("tcp://192.168.1.210:5554") #Sends to MotorFunctions for Jaw Movement

def getPlayTime():  # Checks to see if current file duration has changed
    fname = '/tmp/speech.wav'   # and if yes, sends new duration
    with contextlib.closing(wave.open(fname,'r')) as f:
        frames = f.getnframes()
        rate = f.getframerate()
        duration = round(frames / float(rate), 3)
        speakTime = str(duration)
        return speakTime

def set_voice(V,T):
    T2 = '"' + T + '"'
    audioFile = "/tmp/speech.wav"  # /tmp set as tmpfs, or RAMDISK to reduce SD Card write ops

    if V == "A":
        voice = "Allison"
    elif V == "B":
        voice = "Belle"
    elif V == "C":
        voice = "Callie"
    elif V == "D":
        voice = "Dallas"
    elif V == "V":
        voice = "David"
    else:
        voice = "Belle"

    os.system("swift -n " + voice + " -o " + audioFile + " " +T2) # Record audio
    tailTrim = .5                                                 # Calculate Jaw Timing
    speakTime = eval(getPlayTime())                               # Start by getting playlength
    speakTime = round((speakTime - tailTrim), 2)                  # Chop .5 s for trailing silence
    wordList = T.split()
    jawString = []
    for index in range(len(wordList)):
        wordLen = len(wordList[index])
        jawString.append(wordLen)
    jawString = str(jawString)
    speakTime = str(speakTime)
    jawString = speakTime + "|" + jawString  # 3.456|[4, 2, 7, 4, 2, 9, 3, 4, 3, 6] - will split on "|"
    jawCmd.send_string(jawString)   # Send Jaw Operating Sequence
    os.system("aplay " + audioFile) # Play audio

pronunciationDict = {'teh':'the','process':'prawcess','Maeve':'Mayve','Mariposa':'May-reeposah','Lila':'Lala','Trump':'Ass hole'}

def adjustResponse(response):     # Adjusts spellings in output string to create better speech output.
    for key, value in pronunciationDict.items():
        if key in response or key.lower() in response:
            response = re.sub(key, value, response, flags=re.I)
    return response

SpeakText="Speech center connected and online."
set_voice(V,SpeakText) # Cepstral  Voices: A = Allison; B = Belle; C = Callie; D = Dallas; V = David;

while True:
    SpeakText = socket.recv().decode('utf-8') # .decode gets rid of the b' in front of the string
    SpeakTextX = adjustResponse(SpeakText)    # Run the string through the pronunciation dictionary
    print("SpeakText = ",SpeakTextX)

    set_voice(V,SpeakTextX)
    print("Received request: %s" % SpeakTextX)
    socket.send_string(str(SpeakTextX))       # Send data back to source for confirmation
于 2021-03-08T15:48:47.290 回答
0

有很多可能性,它可能归结为您需要检查持续时间的频率、来自多少客户端、文件更改的频率以及您是否有其他要在节点之间共享的信息。

以下是一些选项:

  • 在具有 WAV 文件的 Pi 上设置 SMB (Samba) 服务器,并让其他节点挂载文件系统并像访问本地文件一样访问文件

  • 在具有 WAV 文件的 Pi 上设置 NFS 服务器,并让其他节点挂载文件系统并像访问本地文件一样访问文件

  • 让其他节点用于ssh登录和提取持续时间,或scp检索文件 - 请参阅Python 中的paramiko

  • 在一个节点上设置Redis并将 WAV 文件放入其中,以便任何人都可以获取它 - 如果您有很多想要在 Raspberry Pi 之间共享的列表、数组、字符串、整数、散列、队列或集合,这可能很有吸引力快速地。这里的例子。


这是一个非常简单的示例,它从一个节点(比如 Redis 在 192.168.0.200 上)将音轨写入 Redis,然后从任何其他节点读回。当然,您可能只希望写入节点在其中写入持续时间而不是整个轨道 - 这样会更有效率。或者您可能想要存储大量其他共享数据或设置。

这是作者:

#!/usr/bin/env python3

import redis
from pathlib import Path

host='192.168.1.200'

# Connect to Redis
r = redis.Redis(host)

# Load some music, or otherwise create it
music = Path('song.wav').read_bytes()

# Put music into Redis where others can see it
r.set("music",music)

这是读者:

#!/usr/bin/env python3

import redis
from pathlib import Path

host='192.168.1.200'

# Connect to Redis
r = redis.Redis(host)

# Retrieve music track from Redis
music = r.get("music")
print(f'{len(music)} bytes read from Redis')

然后,在测试期间,您可能希望从终端手动将轨道推送到 Redis:

redis-cli -x -h 192.168.0.200 set  music < OtherTrack.wav

或者手动将曲目从 Redis 检索到文件中:

redis-cli -h 192.168.0.200 get music > RetrievedFromRedis.wav
于 2021-02-19T13:32:41.830 回答