我试图做一些类似于VoIP的事情,在VoIP中,我录制语音并使用UDP将其发送到网络上的另一个程序,这不是加密的问题,但当我运行代码时,它起了作用,除了音频不稳定之外。
换言之,在我丢弃的一些单词中,我可以完整地听到它们,但其他较长的短语总是可以识别信号被中断的时刻,他等待另一个数据包被发送以继续传输。
我在问如何让我的声音在接收端听起来柔和?因为我试着使用线程来优化录制,但没有太大区别,我不知道还能去哪里。
服务器端:
import sounddevice as sd
import socket, pickle
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
h = socket.gethostbyname(socket.gethostname())
s.bind((h,9001))
print("Servidor Rodando em "+str(h)+":9001")
while True:
r = pickle.loads(s.recvfrom(102400)[0])
sd.play(r,4410)
客户端:
import sounddevice as sd
import socket, pickle, threading
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ip = input("IP >> ")
data = None
def Enviar():
global data
s.sendto(pickle.dumps(data),(ip,9001))
while True:
data = sd.rec(4410, samplerate=4410, channels=2)
sd.wait()
threading.Thread(target=Enviar, args=()).start()
对于计算机音频,接收计算机的声卡有一个采样时钟,用于确定将音频采样值转换为驱动扬声器的电信号的速度。采样时钟以固定的速率运行(例如,每秒48000个采样,或您将其设置为的任何值(,为了使音频听起来正确,必须每隔1/48000秒将一个新的音频采样输入声卡。
为了减少主机上的CPU负载,声卡通常有一个内置的音频缓冲区,因此,您可以让CPU每100mS唤醒一次,并一次写入4800个样本,而不是强迫CPU每1/48000秒唤醒一次以发送一个样本。声卡的内部电子设备将转而从缓冲区中输入单个样本。
因此,连续声音的秘诀是永远不要让声卡的缓冲区变空。当缓冲区耗尽时(因此声卡无法在需要播放的时刻获得下一个样本(,这被称为音频不足,并会导致音频出现故障,正如您所听到的。
防止欠载的最简单方法是在接收计算机上缓冲更多的音频,这样在发生欠载之前就可以在没有接收到数据的情况下度过更多的时间。当然,这样做的缺点是,在发送方发送数据和接收方播放数据之间会有更多的延迟;这可能适用于流媒体录制的音乐,但不太适合现场语音对话。
更难的方法是确保所有数据在短时间内通过网络;要保证可靠性,你需要一个特殊的网络交换机,允许设备预先预留带宽,这样他们就可以保证音频数据包不会丢失。没有这个保证,你只能抱着最好的希望;在有线以太网连接上,你通常可以在少量音频通道中使用它,但正如你所看到的,在WiFi上,网络通常非常不可靠,因此在许多情况下你可能会听到未充分运行的故障,除非你经常调高缓冲。
一些协议使用前向纠错数学对音频进行编码,使得即使UDP数据包的某个子集丢失,仍然可以从接收到的剩余数据包重建原始音频采样值。这在一定程度上增加了整体带宽使用,但只要丢弃的数据包数量相对较少,就可以避免音频出现故障。然而,我对它们的工作方式不太熟悉,所以我不能再多说了。
最后一种方法(我认为这就是你所问的(是让接收计算机以某种方式尝试";纸张覆盖";通过为丢失的音频组成其自己的替换采样值来识别丢失的音频。有一些语音协议试图做到这一点,并取得了不同程度的成功(你可能在通过糟糕的手机连接通话时听到了结果(,但IMHO这并不值得实施,因为音频中仍然会有明显的故障;只是一个听起来不一样的小故障。如果你没有更多的样本来跟随接收到的音频的最后一个样本(至少为了避免突然的"爆裂"(,那么将它们淡出到零可能是值得的,然后在接收到新的(欠载后(音频之后,也将新接收到的音频的第一个样本淡出(以避免第二个"爆裂"(,但这只会使小故障不那么烦人;它无法摆脱它。