4

我的目标是通过笔记本电脑的麦克风录制我的声音,同时在 python 中为其添加效果。我需要的类似于音乐效果踏板,您可以在其中连接吉他或麦克风,并添加混响或回声或失真等。

我正在使用“pyaudio”和“wave”来录制和播放音频。使用 'scikits.audiolab' 将音频作为数组导入,并能够使用反转、剪辑、平铺等功能编辑此数组。音频数组的这种操作让我可以将效果“添加”到原始音频中。

我有一个问题,这不是一个真正的问题,它只是不是我想要的效果。假设我记录了“你好”这个词。我的记录功能设置为记录 3 秒。然后我把这个音频阵列平铺一次。现在,当我回放这个时,它会说 hello 两次,一个延迟效果。但是,两个问候之间有一个“空白空间”的时间间隔,这是因为在我打完招呼后音频仍在录制。因此,当它重复时,单词之间有太多的空白。我想消除这个空白空间,以便播放更快地说你好你好。

我的老师建议穿线。他说我应该记录,同时抓取前 500 个样本,说出一个数字。他建议在录制时采集这 500 个样本并播放它们。我不太确定如何实现这一点。

我的问题是,如何同时录制,获取前 500 个样本,并创建一个新数组,并将“效果”添加到原始录制中。

import scikits.audiolab as audiolab
import pyaudio
import wave

def recordAudio():

    CHUNK = 1024
    FORMAT = pyaudio.paInt16
    CHANNELS = 1
    RATE = 44100
    RECORD_SECONDS = 3
    WAVE_OUTPUT_FILENAME = "audioOriginal.wav"

    p = pyaudio.PyAudio()

    stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

    print("* recording:")

    frames = []

    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data)

    print("* Finished recording.")

    stream.stop_stream()
    stream.close()
    p.terminate()

    wf = wave.open(WAVE_OUTPUT_FILENAME, 'wb')
    wf.setnchannels(CHANNELS)
    wf.setsampwidth(p.get_sample_size(FORMAT))
    wf.setframerate(RATE)
    wf.writeframes(b''.join(frames))
    wf.close()

    # Duplicate audio and save as Actual
    frames, fs, encoder = audiolab.wavread('audioOriginal.wav')
    audiolab.wavwrite(frames,'audioActual.wav',fs)

def playAudio():

    import pyaudio
    import wave

    CHUNK = 1024

    wf = wave.open('audioActual.wav', 'rb')

    p = pyaudio.PyAudio()

    stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), 
        channels=wf.getnchannels(), 
        rate=wf.getframerate(), 
        output=True)

    data = wf.readframes(CHUNK)

    while data != '':  
        stream.write(data)  
        data = wf.readframes(CHUNK)

    stream.stop_stream()
    stream.close()
    p.terminate()

def reverseAudio():

    frames, fs, encoder = audiolab.wavread('audioActual.wav')

    audiolab.wavwrite(frames[::-1],'audioActual.wav',44100)

def revert():
    frames, fs, encoder = audiolab.wavread('audioOriginal.wav')
    audiolab.wavwrite(frames,'audioActual.wav',fs)

def errorSelection():
    print("\nERROR.") # no option in menu
def showMenu():
    print("""
    1. Record audio
    2. Play audio
    3. Reverse audio
    4. Add delay
    5. Revert to original audio

    T to end program.
    """)

# Menu
def main():
    selecciones = {"1": recordAudio, "2": playAudio, "3": reverseAudio, "5": revert}
    while True:
        showMenu()
        seleccion = raw_input(u'What do you want to do? ')
        if "t" == seleccion:
            return
        elif "T" == seleccion:
            return
        toDo = selecciones.get(seleccion, errorSelection)
        toDo()

if __name__ == "__main__":
    main()
4

2 回答 2

4

首先,您提出的问题(能够平铺音频样本,同时自动删除它们之间的安静空间)不是线程可以解决的问题。您需要分析录制的声音以确定是否有静音,或者只是让用户指定录制结束的时间。您可以通过一个简单的循环来完成后者:

  1. 打开音频硬件并开始录制。
  2. 创建一个空列表来存储音频块
  3. 请求一小块音频数据,附加到列表中
  4. 检查用户已请求结束录制。如果不是,则循环回到 3。
  5. 完成后,将块组装成一个数组进行播放。

在这个简单的例子中,使用线程没有任何好处。

建议的方法,记录并同时播放,似乎是解决不同问题的方法,一个更复杂的问题。在这种情况下,有两个主要困难:

  1. 并非所有消费类声卡都能够同时录音和播放。寻找声称“全双工”而不是“半双工”的卡。
  2. 对着麦克风说话并在短时间内听到自己的声音会非常分散注意力。为了使这项工作正常进行,必须在大约 20 毫秒内处理录制的音频并将其发送回声卡。在 44.1 kHz 时,这意味着您应该在每个循环周期读取少于 880 帧,并且如果处理跟不上,输出中就会出现间隙。除非您有专门的软件来帮助,否则这是一个非常困难的问题。如果你真的想走这条路,你可以看看 Jack ( http://jackaudio.org/ ),它在大多数平台上提供低延迟音频访问,并且还有一个简单的 python 库 ( http://sourceforge.网络/项目/py-jack/)。线程可能对这种类型的程序没有帮助。
于 2013-07-20T19:12:50.547 回答
0

扩展卢克的答案:

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)

在您的代码中是您承诺录制特定时间的地方。如果您编写了一个isSilent可以确定块是否静默的函数“”,您的代码可能会更改为:

while len(frames) <= 0 or not isSilent(frames[-1]):
    data = stream.read(CHUNK)
    frames.append(data)

如果“ isSilent”很难写或者计算速度很慢,你可以等待用户输入。KeyboardInterrupt是使用此方法的第一个技巧:

try:
    while true:
        data = stream.read(CHUNK)
        frames.append(data)
except KeyboardInterrupt:
    pass

这是一个 hack,不是在生产中寻找用户输入的正确方法,但它会让你开始尝试这个。你会想找到或做一个stopButtonHasBeenPressed功能。

while not stopButtonHasBeenPressed():
    data = stream.read(CHUNK)
    frames.append(data)
于 2013-07-26T21:26:05.000 回答