如何实时改变音量

时间:2017-07-20 15:49:32

标签: python python-3.x audio anaconda audio-streaming

我需要使用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笔记本电脑上测试时,也没有声音可以听到。

1 个答案:

答案 0 :(得分:1)

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

然而,audioop不是将块转换为numpy数组,而是操纵和转换回块,这可能相当混乱,pyaudio callback code提供了更简单的方法来获取每个块的功率,然后根据需要增加/减少。 / p>

以下代码是对audioop的修改。

根据{{3}}文档(下面),我们可以使用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的dict中收集旧的和新的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()”以获取更多信息。

     
    
      

================================ RESTART ============== ==================

             
          
  • 从麦克风录制
  •       
  • 完成录制
  •       
             

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]),(' chunk12',[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])])