用于播放固定频率声音的 Python 库

Posted

技术标签:

【中文标题】用于播放固定频率声音的 Python 库【英文标题】:Python library for playing fixed-frequency sound 【发布时间】:2010-11-01 17:22:50 【问题描述】:

我家里有蚊子问题。这通常与程序员社区无关;然而,我见过一些声称通过播放 17Khz 音调来阻止这些讨厌的生物的设备。我想用我的笔记本电脑来做这个。

一种方法是创建一个带有单一固定频率音调 (This can easily done by audacity)、opening it with a python library 的 MP3 并反复播放。

第二个是使用计算机内置扬声器播放声音。我正在寻找类似于 QBasic Sound 的东西:

SOUND 17000, 100

有没有相应的python库?

【问题讨论】:

小心使用 MP3,因为它通过去除人类听不见的频率进行压缩,并且听力阈值通常为 20kHz,与您的 17kHz 相差不远。因此,当您的固定频率音调变成 MP3 时,可能会播放一组不同的频率,或者减弱您所追求的频率。作为人类,您可能无法分辨出其中的区别。但是蚊子可能... 非常感谢。如果我选择这个解决方案,我会使用一个短(约 3 秒)的 WAV 文件。 那么它对蚊子有用吗??? 相关:How to play a fixed frequency sound using Python 是的,请告诉它是否有效 :) 我测试了其中一个手持设备,有一次将它放在靠近我手臂飞行的蚊子附近。它没有开始咬人,但似乎也没有任何反应,只是停留在皮肤附近。 【参考方案1】:

PyAudiere 是一个简单的跨平台解决方案:

>>> import audiere
>>> d = audiere.open_device()
>>> t = d.create_tone(17000) # 17 KHz
>>> t.play() # non-blocking call
>>> import time
>>> time.sleep(5)
>>> t.stop()

pyaudiere.org 消失了。 The site 和 Python 2(debian、windows)的二进制安装程序可通过回路机器获得,例如 here's source code pyaudiere-0.2.tar.gz

为了在 Linux、Windows、OSX 上同时支持 Python 2 和 3,可以改用pyaudio module:

#!/usr/bin/env python
"""Play a fixed frequency sound."""
from __future__ import division
import math

from pyaudio import PyAudio # sudo apt-get install python,3-pyaudio

try:
    from itertools import izip
except ImportError: # Python 3
    izip = zip
    xrange = range

def sine_tone(frequency, duration, volume=1, sample_rate=22050):
    n_samples = int(sample_rate * duration)
    restframes = n_samples % sample_rate

    p = PyAudio()
    stream = p.open(format=p.get_format_from_width(1), # 8bit
                    channels=1, # mono
                    rate=sample_rate,
                    output=True)
    s = lambda t: volume * math.sin(2 * math.pi * frequency * t / sample_rate)
    samples = (int(s(t) * 0x7f + 0x80) for t in xrange(n_samples))
    for buf in izip(*[samples]*sample_rate): # write several samples at a time
        stream.write(bytes(bytearray(buf)))

    # fill remainder of frameset with silence
    stream.write(b'\x80' * restframes)

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

例子:

sine_tone(
    # see http://www.phy.mtu.edu/~suits/notefreqs.html
    frequency=440.00, # Hz, waves per second A4
    duration=3.21, # seconds to play sound
    volume=.01, # 0..1 how loud it is
    # see http://en.wikipedia.org/wiki/Bit_rate#Audio
    sample_rate=22050 # number of samples per second
)

它是this AskUbuntu answer 的修改版(以支持 Python 3)。

【讨论】:

酷。你能谈谈稳定性问题吗?最低版本是 0.2。 @Udi Pasmon:PyAudiere 是对应 C++ 库audiere.sourceforge.net的简单包装器@ 花哨的示例编写代码不适用于持续时间 samples = (int(s(t) * 0x7f + 0x80) for t in xrange(n_samples)) for buf in izip(*[samples]*sample_rate): # write several samples at a time stream.write(bytes(bytearray(buf))) 替换为samples = (int(s(t) * 0x7f + 0x80) for t in range(n_samples)) stream.write(bytes(bytearray(samples))) 我的一个朋友与我分享了这个链接以获取和安装 pyaudio:people.csail.mit.edu/hubert/pyaudio 如果您使用此解决方案,我会收到发送到 STDERR 的奇怪错误消息。我怎样才能摆脱它们?详情请见***.com/questions/50162431/…!【参考方案2】:

您可以使用 SDL (Simple Direct Media Library) 的 Python binding。

【讨论】:

【参考方案3】:

模块 winsound 包含在 Python 中,因此无需安装外部库,它应该可以满足您的需求(仅此而已)。

 import winsound
 winsound.Beep(17000, 100)

它非常简单易用,但仅适用于 Windows。

但是: 这个问题的完整答案应该注意,虽然这种方法会产生声音,但它不会阻止蚊子。它已经过测试:见here 和here

【讨论】:

顺便说一句,虽然这会产生声音,但我真的怀疑它会阻止蚊子,事实上,我什至怀疑它们是否能听到。问题是大多数昆虫不像我们那样用鼓膜来听,而是用感觉毛来听。但是感觉毛发只对空气速度敏感,而不是压力,当你远离扬声器时,几乎都是压力,速度很小。也就是说,除非他们正站在您的扬声器上,否则他们不会听到。 所以扬声器必须非常强大,可能不是普通的PC扬声器。幸运的是,我仍然是科学学院的学生 - 我会问昆虫学家并在这里发布答案。 “虽然人耳是压力检测器,但蚊子检测声场的粒子速度分量,在声学近场中仅限于声源附近 . 蚊子的耳朵对声远场的压力波动不敏感。” ncbi.nlm.nih.gov/pmc/articles/PMC1636503 如何在 Django 中注册 winsound? ***.com/questions/59047909/…【参考方案4】:

我将代码放在这里是因为它可以帮助程序员清楚地了解代码的工作原理。解释在代码本身:

#!/usr/bin/env python3
import pyaudio
import struct
import math

FORMAT = pyaudio.paInt16
CHANNELS = 2
RATE = 44100

p = pyaudio.PyAudio()


def data_for_freq(frequency: float, time: float = None):
    """get frames for a fixed frequency for a specified time or
    number of frames, if frame_count is specified, the specified
    time is ignored"""
    frame_count = int(RATE * time)

    remainder_frames = frame_count % RATE
    wavedata = []

    for i in range(frame_count):
        a = RATE / frequency  # number of frames per wave
        b = i / a
        # explanation for b
        # considering one wave, what part of the wave should this be
        # if we graph the sine wave in a
        # displacement vs i graph for the particle
        # where 0 is the beginning of the sine wave and
        # 1 the end of the sine wave
        # which part is "i" is denoted by b
        # for clarity you might use
        # though this is redundant since math.sin is a looping function
        # b = b - int(b)

        c = b * (2 * math.pi)
        # explanation for c
        # now we map b to between 0 and 2*math.PI
        # since 0 - 2*PI, 2*PI - 4*PI, ...
        # are the repeating domains of the sin wave (so the decimal values will
        # also be mapped accordingly,
        # and the integral values will be multiplied
        # by 2*PI and since sin(n*2*PI) is zero where n is an integer)
        d = math.sin(c) * 32767
        e = int(d)
        wavedata.append(e)

    for i in range(remainder_frames):
        wavedata.append(0)

    number_of_bytes = str(len(wavedata))  
    wavedata = struct.pack(number_of_bytes + 'h', *wavedata)

    return wavedata


def play(frequency: float, time: float):
    """
    play a frequency for a fixed time!
    """
    frames = data_for_freq(frequency, time)
    stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, output=True)
    stream.write(frames)
    stream.stop_stream()
    stream.close()


if __name__ == "__main__":
    play(400, 1)

【讨论】:

【参考方案5】:

我简化了 jfs 对 Python3.6+ 的回答并做了一些小的改进:

import math
from pyaudio import PyAudio, paUInt8

def generate_sine_wave(frequency, duration, volume=0.2, sample_rate=22050):
    ''' Generate a tone at the given frequency.

        Limited to unsigned 8-bit samples at a given sample_rate.
        The sample rate should be at least double the frequency.
    '''
    if sample_rate < (frequency * 2):
        print('Warning: sample_rate must be at least double the frequency '
              f'to accurately represent it:\n    sample_rate sample_rate'
              f' ≯ frequency*2 (frequency frequency*2)')

    num_samples = int(sample_rate * duration)
    rest_frames = num_samples % sample_rate

    pa = PyAudio()
    stream = pa.open(
        format=paUInt8,
        channels=1,  # mono
        rate=sample_rate,
        output=True,
    )

    # make samples
    s = lambda i: volume * math.sin(2 * math.pi * frequency * i / sample_rate)
    samples = (int(s(i) * 0x7F + 0x80) for i in range(num_samples))

    # write several samples at a time
    for buf in zip( *([samples] * sample_rate) ):
        stream.write(bytes(buf))

    # fill remainder of frameset with silence
    stream.write(b'\x80' * rest_frames)

    stream.stop_stream()
    stream.close()
    pa.terminate()

generate_sine_wave(
    # see http://www.phy.mtu.edu/~suits/notefreqs.html
    frequency=523.25,   # Hz, waves per second C6
    duration=1.2,       # seconds to play sound
    volume=0.25,        # 0..1 how loud it is
    sample_rate=22050,  # number of samples per second: 11025, 22050, 44100
)

【讨论】:

以上是关于用于播放固定频率声音的 Python 库的主要内容,如果未能解决你的问题,请参考以下文章

播放纯声音(一个频率)并识别频率 C++

Arduino播放歌曲应该用有源蜂鸣器还是无源蜂鸣器

Arduino利用TimerOne库产生固定频率和占空比的方波

绘制录制声音时的奇怪频率

在声音文件 xcode 中查找单个频率

在 Windows 上播放任意声音?