如何实时改变音量

Posted

技术标签:

【中文标题】如何实时改变音量【英文标题】:How to change sound volume in real time 【发布时间】:2017-07-20 15:49:31 【问题描述】:

我需要使用 Python 3.6 实时更改音量,使用 PyBinSim 和 Anaconda,但不是强制性的(可以只是 Python 3.6 和任何其他对此有用的库)。 情况如下: 使用笔记本电脑和麦克风,我们正在录制声音并立即播放它们,但我们需要在录制和播放之间更改音量。我尝试了一些代码示例,但我无法让它在没有错误的情况下工作。 有什么想法或建议吗? 提前致谢!

代码示例

    import sounddevice as sd 
import time
import numpy as np 
from scipy import signal

duration = 10 #seconds

def callback(indata, outdata, frames, time):
        outdata[:] = indata
def print_sound(indata, outdata, frames, time, status):
    volum_norm = np.linalg.norm(indata) * 10
    print("|") * int(volum_norm)

with sd.Stream(callback=print_sound):
    sd.sleep(duration * 1000)

    import sounddevice as sd 
import time
import numpy as np 
from scipy import signal
from ctypes import cast, POINTER
from comtypes import CLSCTX_ALL
from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume

duration = 1000 #seconds 

def callback(indata, outdata, frames, time, status):
    if status:
        print(status)
        outdata[:]=indata

devices = AudioUtilities.GetSpeakers()
interface = devices.Activate(
   IAudioEndpointVolume._iid_, CLSCTX_ALL, None)
volume = cast(interface, POINTER(IAudioEndpointVolume))

# Control volume
#volume.SetMasterVolumeLevel(-0.0, None) #max
#volume.SetMasterVolumeLevel(-5.0, None) #72%
volume.SetMasterVolumeLevel(-10.0, None) #72%
volume.SetMasterVolumeLevel(-10.0, None) #51%

with sd.Stream(channels=2, callback=callback):
    sd.sleep(duration*1000)

第二个例子没有返回错误,但在 Win10 笔记本电脑上测试时也没有声音。

【问题讨论】:

"我尝试了一些代码示例,但无法使其正常工作"。它们是哪些代码示例,它们是什么错误?这些信息将是一个很好的起点——如果我们不知道您已经尝试过什么,那么您可能会得到很多答案,暗示您已经尝试过的事情。对任何人来说,这都不是对时间的有效利用。 很抱歉,如果我的问题没有得到正确解释。这是我第一次在这里发帖。谢谢@Kevin @Kohane 您应该编辑您的问题以包含示例代码(使用正确的代码格式)。 @Matthias 我刚刚按照您的要求添加了代码。谢谢。 我不明白第二个例子,但在第一个例子中,您可以简单地乘以indata,然后将结果分配给outdata。例如,您可以写outdata[:] = 0.5 * indata 将音量降低约 6 分贝。 【参考方案1】:

pyaudio 有一个阻塞回调的方法。这意味着您可以从麦克风中分块录制,然后回放。此方法适用于根据自己的需要处理音频块。

但是,audioop 提供了更简单的方法来获取每个块的功能,然后根据个人的需要增加/减少,而不是将块转换为 numpy 数组,操作并转换回块,这可能非常混乱。

以下代码是对pyaudio callback code 的修改。

根据audioop 文档(如下),我们可以使用audioop.rms 来获取片段的RMS 值(在我们的例子中是CHUNK)。虽然这对于更改音频音量不是必需的,但它可以用作调试以查看是否确实在给定的音频块上发生了更改。

audioop.rms(fragment, width):   
    ''' Return the root-mean-square of the fragment, i.e. sqrt(sum(S_i^2)/n).
    This is a measure of the power in an audio signal.'''

接下来,我们将块中的所有值乘以某个因子。 例如乘以系数 (2) 将使 RMS 值大致翻倍。并因此相应地提高音量。

audioop.mul(fragment, width, factor):  
        '''Return a fragment that has all samples in the original fragment  
           multiplied by the floating-point value factor.   
          Samples are truncated in case of overflow.'''

借助这两种方法,我们只需使用来自pyaudio 站点的代码,并根据倍增因子修改每个块的体积。

我还添加了代码以在列表rmsdict 的字典中为每个块收集新旧 RMS 值。由于处理中断,简单地将它们打印在屏幕上会导致咔嗒声。

麦克风的音频已录制 (3) 秒。你可以尝试不同的长度。

工作代码

import pyaudio
import audioop
from collections import OrderedDict


class DefaultListOrderedDict(OrderedDict):
    '''
    Ordered Dict of list to hold old and new RMS values
    '''
    def __missing__(self,k):
        self[k] = []
        return self[k]

FORMAT = pyaudio.paInt16
CHUNK = 2048
WIDTH = 2
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 3
FACTOR = 2    

rmsdict = DefaultListOrderedDict()

p = pyaudio.PyAudio()

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

print("* recording from microphone")

for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    rmsdict["chunk".format(i)].append(audioop.rms(data, 2))
    new_data = audioop.mul(data, WIDTH, FACTOR)
    rmsdict["chunk".format(i)].append(audioop.rms(new_data, 2))
    stream.write(new_data, CHUNK)
print("* done recording")

stream.stop_stream()
stream.close()

p.terminate()

以下是来自rmsdict 的数据。为了能见度,我已经截断了它。

Win32 上的 Python 2.7.9(默认,2014 年 12 月 10 日,12:24:55)[MSC v.1500 32 位(英特尔)] 输入“copyright”、“credits”或“license()”了解更多信息。

================================= 重启============== ===================

从麦克风录音 完成录音

rmsdict DefaultListOrderedDict([('chunk0', [71, 143]), ('chunk1', [77, 155]), ('chunk2', [45, 91]), ('chunk3', [29, 59]), ('chunk4', [33, 66]), ('chunk5', [35, 71]), ('chunk6', [27, 54]), ('chunk7', [17, 34]), ('chunk8', [18, 36]), ('chunk9', [142, 285]), ('chunk10', [1628, 3257]), ('chunk11', [2666, 5333]), ('块 12', [2174, 4348]), .. .. ('chunk51', [1723, 3446]), ('chunk52', [1524, 3049]), ('chunk53', [1329, 2659]), ('chunk54', [1166, 2333]), ('chunk55', [573, 1147]), ('chunk56', [1777, 3555]), ('chunk57', [1588, 3176]), ('chunk58', [698, 1397]), ('chunk59', [383, 767]), ('chunk60', [152, 305]), ('chunk61', [799, 1599]), ('chunk62', [2158, 4316])])

【讨论】:

以上是关于如何实时改变音量的主要内容,如果未能解决你的问题,请参考以下文章

Android平台GB28181设备接入端如何调节实时音量?

iOS9如何改变音量?

如何在iOS上获得音量级别和音量更改通知?

如何更改 MusicTrack 的音量? - iOS开发

在java中改变midi音量

在不改变系统音量的情况下改变 MPMusicPlayerController 的音量