使用PyAudio作为振荡器移除/控制咔嗒声

时间:2017-02-12 19:34:09

标签: python audio signal-processing pyaudio portaudio

当此项运行时,音高之间会发出咔嗒声。我不太介意咔哒声太多 - 它节奏愉快。那说......

  • 当我不想要的时候,我希望能够摆脱这种咔哒声。
  • 更好的是,能够控制咔哒声是很好的 在某种程度上 - 音量等。

我已经看过这个帖子了,但是我们还没弄明白如何将它应用到我的问题中: How to remove pops from concatented sound data in PyAudio

有什么想法吗?谢谢你的时间!

import numpy
import pyaudio
import math
import random


def sine(frequency, length, rate):
    length = int(length * rate)
    factor = float(frequency) * (math.pi * 2) / rate
    waveform = numpy.sin(numpy.arange(length) * factor)
    return waveform


def play_tone(stream, frequency, length, rate=44100):
    chunks = []
    chunks.append(sine(frequency, length, rate))

    chunk = numpy.concatenate(chunks) * .25

    stream.write(chunk.astype(numpy.float32).tostring())


def bassline():
        frequency = 300
        for i in range(1000000):
            play_tone(stream, frequency, .15)
            change = random.choice([-75, -75, -10, 10, 2, 3, 100, -125])
            print (frequency)
            if frequency < 0:
                frequency = random.choice([100, 200, 250, 300])
            else:
                frequency = frequency + change 

if __name__ == '__main__':
    p = pyaudio.PyAudio()
    stream = p.open(format=pyaudio.paFloat32,
                    channels=1, rate=44100, output=4)

bassline()

/ EDIT

我已经绘制了色调,看起来不连续性处于每个色调的开始和结束阶段之间的关系中。

First tone

Second tone

任何想法如何解决这个问题?

3 个答案:

答案 0 :(得分:2)

如在两个波形图像中所见,当您在频率之间切换时,由于波形幅度的快速变化,您将获得咔嗒声。要解决此问题,您需要在更改频率时保持波形的相位。我认为最简单的方法是在每次正弦调用后添加一个记录波形周期中最后位置的变量。结束位置可以用作下一次正弦调用中的起始位置。

类似的东西:

phase_start = phase_position
phase_end = phase_start + length
waveform = numpy.sin(numpy.arange(phase_start, phase_end) * factor)
phase_position = phase_end

Pitch shift maintaining cycle position

注意:我认为这是最简单的答案,但我建议您使用您引用的问题中的信息。您应该以弧度为单位保持所播放的正弦波的相位。 How to remove pops from concatented sound data in PyAudio

答案 1 :(得分:2)

感谢您EhzMatthias

最后,我通过在几百毫秒的过程中淡入和淡出每个音调来解决这个问题。它也是控制咔哒声的好方法。 fade越接近0,点击的次数就越大。

import math
import numpy
import pyaudio


def sine(frequency, length, rate):
    length = int(length * rate)
    factor = (float(frequency) * (math.pi * 2) / rate)
    return numpy.sin(numpy.arange(length) * factor)


def play_tone(stream, frequency, length, rate=44100):
    chunks = [sine(frequency, length, rate)]

    chunk = numpy.concatenate(chunks) * 0.25

    fade = 200.

    fade_in = numpy.arange(0., 1., 1/fade)
    fade_out = numpy.arange(1., 0., -1/fade)

    chunk[:fade] = numpy.multiply(chunk[:fade], fade_in)
    chunk[-fade:] = numpy.multiply(chunk[-fade:], fade_out)

    stream.write(chunk.astype(numpy.float32).tostring())


def test():
    test_freqs = [50, 100, 200, 400, 800, 1200, 2000, 3200]

    for i in range(2):
        for freq in test_freqs:
            play_tone(stream, freq, 1)


if __name__ == '__main__':
    p = pyaudio.PyAudio()
    stream = p.open(format=pyaudio.paFloat32,
                    channels=1, rate=44100, output=1)


test()

答案 2 :(得分:1)

单击是由于一个频率的结束波相位与下一频率的开始波相位不同。请参考下面的两个图像:检查第一波图显示的结束相位值约为-0.96。第二张图片显示了下一个频率,起始频率约为0.85。如果您不相应地移动每个新波形,您会在频率之间听到明显的喀哒声。事实证明,有一个非常简单的解决方案。使用numpy.arcsin()计算和存储所需的相移,以使波保持和谐运行:

wave_delta_arcsin = 0.0

def sine(frequency, length):
    global wave_delta_arcsin
    length = int(length * rate)
    factor = (math.pi * 2) * float(frequency) / rate
    wave = numpy.sin(numpy.arange(length) * factor + wave_delta_arcsin)
    wave_delta_arcsin = numpy.arcsin(wave[-1])
    return wave

enter image description here

enter image description here