2009-05-21 8 views
66

Мне нужно захватить аудиоклипы в виде файлов WAV, которые я могу передать в другой бит python для обработки. Проблема в том, что мне нужно определить, когда есть звук, а затем записать его, остановить, когда он отключится, а затем передать этот файл в модуль обработки.Обнаружение и запись звука в Python

Я думаю, что с волновым модулем должно быть возможно обнаружить, когда есть чистая тишина, и отбросить его, как только произойдет обнаружение чего-либо, кроме молчания, начнется запись, а затем, когда линия снова замолчит, прекратите запись.

Просто не могу обойти его вокруг, может ли кто-нибудь завести меня с базового примера.

ответ

40

Я считаю, что модуль WAVE не поддерживает запись, просто обрабатывая существующие файлы. Вы можете посмотреть на PyAudio для записи. WAV - это самый простой в мире формат файла. В paInt16 вы просто получаете целое число со знаком, представляющее уровень, а ближе к 0 - тише. Я не могу вспомнить, если WAV файлы старший байт первый или младший байт, а что-то вроде этого должны работать (извините, я на самом деле не питон программист:

from array import array 

# you'll probably want to experiment on threshold 
# depends how noisy the signal 
threshold = 10 
max_value = 0 

as_ints = array('h', data) 
max_value = max(as_ints) 
if max_value > threshold: 
    # not silence 

PyAudio код для записи для справок:

import pyaudio 
import sys 

chunk = 1024 
FORMAT = pyaudio.paInt16 
CHANNELS = 1 
RATE = 44100 
RECORD_SECONDS = 5 

p = pyaudio.PyAudio() 

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

print "* recording" 
for i in range(0, 44100/chunk * RECORD_SECONDS): 
    data = stream.read(chunk) 
    # check for silence here by comparing the level with 0 (or some threshold) for 
    # the contents of data. 
    # then write data or not to a file 

print "* done" 

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

Спасибо Nick, Да, должно быть, сказал, что я также использую portaudio для захвата, бит, на котором я застрял, - это проверка на тишину, как получить уровень в куске данных? – 2009-05-21 10:44:49

+0

Я добавил некоторый действительно простой непроверенный код выше, но он должен выполнить задание, которое вы хотите –

+0

awesome thanks Nick – 2009-05-21 11:11:05

0

Вы можете посмотреть на csounds, а также. Он имеет несколько API, включая Python. Это может быть в состоянии взаимодействовать с интерфейсом AD и собрать звуковые образцы.

80

в продолжение Нику Ответ Фортескью, вот более полный пример того, как запись с микрофона и обрабатывать полученные данные:

from sys import byteorder 
from array import array 
from struct import pack 

import pyaudio 
import wave 

THRESHOLD = 500 
CHUNK_SIZE = 1024 
FORMAT = pyaudio.paInt16 
RATE = 44100 

def is_silent(snd_data): 
    "Returns 'True' if below the 'silent' threshold" 
    return max(snd_data) < THRESHOLD 

def normalize(snd_data): 
    "Average the volume out" 
    MAXIMUM = 16384 
    times = float(MAXIMUM)/max(abs(i) for i in snd_data) 

    r = array('h') 
    for i in snd_data: 
     r.append(int(i*times)) 
    return r 

def trim(snd_data): 
    "Trim the blank spots at the start and end" 
    def _trim(snd_data): 
     snd_started = False 
     r = array('h') 

     for i in snd_data: 
      if not snd_started and abs(i)>THRESHOLD: 
       snd_started = True 
       r.append(i) 

      elif snd_started: 
       r.append(i) 
     return r 

    # Trim to the left 
    snd_data = _trim(snd_data) 

    # Trim to the right 
    snd_data.reverse() 
    snd_data = _trim(snd_data) 
    snd_data.reverse() 
    return snd_data 

def add_silence(snd_data, seconds): 
    "Add silence to the start and end of 'snd_data' of length 'seconds' (float)" 
    r = array('h', [0 for i in xrange(int(seconds*RATE))]) 
    r.extend(snd_data) 
    r.extend([0 for i in xrange(int(seconds*RATE))]) 
    return r 

def record(): 
    """ 
    Record a word or words from the microphone and 
    return the data as an array of signed shorts. 

    Normalizes the audio, trims silence from the 
    start and end, and pads with 0.5 seconds of 
    blank sound to make sure VLC et al can play 
    it without getting chopped off. 
    """ 
    p = pyaudio.PyAudio() 
    stream = p.open(format=FORMAT, channels=1, rate=RATE, 
     input=True, output=True, 
     frames_per_buffer=CHUNK_SIZE) 

    num_silent = 0 
    snd_started = False 

    r = array('h') 

    while 1: 
     # little endian, signed short 
     snd_data = array('h', stream.read(CHUNK_SIZE)) 
     if byteorder == 'big': 
      snd_data.byteswap() 
     r.extend(snd_data) 

     silent = is_silent(snd_data) 

     if silent and snd_started: 
      num_silent += 1 
     elif not silent and not snd_started: 
      snd_started = True 

     if snd_started and num_silent > 30: 
      break 

    sample_width = p.get_sample_size(FORMAT) 
    stream.stop_stream() 
    stream.close() 
    p.terminate() 

    r = normalize(r) 
    r = trim(r) 
    r = add_silence(r, 0.5) 
    return sample_width, r 

def record_to_file(path): 
    "Records from the microphone and outputs the resulting data to 'path'" 
    sample_width, data = record() 
    data = pack('<' + ('h'*len(data)), *data) 

    wf = wave.open(path, 'wb') 
    wf.setnchannels(1) 
    wf.setsampwidth(sample_width) 
    wf.setframerate(RATE) 
    wf.writeframes(data) 
    wf.close() 

if __name__ == '__main__': 
    print("please speak a word into the microphone") 
    record_to_file('demo.wav') 
    print("done - result written to demo.wav") 
+11

Чтобы сделать эту работу на Python 3, просто замените xrange диапазоном. –

+0

Отличный пример! На самом деле полезно, когда я пытался обернуть голову тем, как записывать голос с помощью Python. Один быстрый вопрос, который у меня был, заключается в том, есть ли способ определить период времени записи. Теперь он записывает слово? Могу ли я играть с ним и иметь период записи, например. 10 секунд? Благодаря! – Swan87

+0

Выявление и нормализация неверны, поскольку они вычисляют по байтам, а не по шортам. Этот буфер должен быть преобразован в массив numpy перед обработкой. – ArekBulski

13

Благодаря крио для улучшенной версии, что я на основе моего протестированного кода ниже :

#Instead of adding silence at start and end of recording (values=0) I add the original audio . This makes audio sound more natural as volume is >0. See trim() 
#I also fixed issue with the previous code - accumulated silence counter needs to be cleared once recording is resumed. 

from array import array 
from struct import pack 
from sys import byteorder 
import copy 
import pyaudio 
import wave 

THRESHOLD = 500 # audio levels not normalised. 
CHUNK_SIZE = 1024 
SILENT_CHUNKS = 3 * 44100/1024 # about 3sec 
FORMAT = pyaudio.paInt16 
FRAME_MAX_VALUE = 2 ** 15 - 1 
NORMALIZE_MINUS_ONE_dB = 10 ** (-1.0/20) 
RATE = 44100 
CHANNELS = 1 
TRIM_APPEND = RATE/4 

def is_silent(data_chunk): 
    """Returns 'True' if below the 'silent' threshold""" 
    return max(data_chunk) < THRESHOLD 

def normalize(data_all): 
    """Amplify the volume out to max -1dB""" 
    # MAXIMUM = 16384 
    normalize_factor = (float(NORMALIZE_MINUS_ONE_dB * FRAME_MAX_VALUE) 
         /max(abs(i) for i in data_all)) 

    r = array('h') 
    for i in data_all: 
     r.append(int(i * normalize_factor)) 
    return r 

def trim(data_all): 
    _from = 0 
    _to = len(data_all) - 1 
    for i, b in enumerate(data_all): 
     if abs(b) > THRESHOLD: 
      _from = max(0, i - TRIM_APPEND) 
      break 

    for i, b in enumerate(reversed(data_all)): 
     if abs(b) > THRESHOLD: 
      _to = min(len(data_all) - 1, len(data_all) - 1 - i + TRIM_APPEND) 
      break 

    return copy.deepcopy(data_all[_from:(_to + 1)]) 

def record(): 
    """Record a word or words from the microphone and 
    return the data as an array of signed shorts.""" 

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

    silent_chunks = 0 
    audio_started = False 
    data_all = array('h') 

    while True: 
     # little endian, signed short 
     data_chunk = array('h', stream.read(CHUNK_SIZE)) 
     if byteorder == 'big': 
      data_chunk.byteswap() 
     data_all.extend(data_chunk) 

     silent = is_silent(data_chunk) 

     if audio_started: 
      if silent: 
       silent_chunks += 1 
       if silent_chunks > SILENT_CHUNKS: 
        break 
      else: 
       silent_chunks = 0 
     elif not silent: 
      audio_started = True    

    sample_width = p.get_sample_size(FORMAT) 
    stream.stop_stream() 
    stream.close() 
    p.terminate() 

    data_all = trim(data_all) # we trim before normalize as threshhold applies to un-normalized wave (as well as is_silent() function) 
    data_all = normalize(data_all) 
    return sample_width, data_all 

def record_to_file(path): 
    "Records from the microphone and outputs the resulting data to 'path'" 
    sample_width, data = record() 
    data = pack('<' + ('h' * len(data)), *data) 

    wave_file = wave.open(path, 'wb') 
    wave_file.setnchannels(CHANNELS) 
    wave_file.setsampwidth(sample_width) 
    wave_file.setframerate(RATE) 
    wave_file.writeframes(data) 
    wave_file.close() 

if __name__ == '__main__': 
    print("Wait in silence to begin recording; wait in silence to terminate") 
    record_to_file('demo.wav') 
    print("done - result written to demo.wav") 
1
import pyaudio 
import wave 
from array import array 

FORMAT=pyaudio.paInt16 
CHANNELS=2 
RATE=44100 
CHUNK=1024 
RECORD_SECONDS=15 
FILE_NAME="RECORDING.wav" 

audio=pyaudio.PyAudio() #instantiate the pyaudio 

#recording prerequisites 
stream=audio.open(format=FORMAT,channels=CHANNELS, 
        rate=RATE, 
        input=True, 
        frames_per_buffer=CHUNK) 

#starting recording 
frames=[] 

for i in range(0,int(RATE/CHUNK*RECORD_SECONDS)): 
    data=stream.read(CHUNK) 
    data_chunk=array('h',data) 
    vol=max(data_chunk) 
    if(vol>=500): 
     print("something said") 
     frames.append(data) 
    else: 
     print("nothing") 
    print("\n") 


#end of recording 
stream.stop_stream() 
stream.close() 
audio.terminate() 
#writing to file 
wavfile=wave.open(FILE_NAME,'wb') 
wavfile.setnchannels(CHANNELS) 
wavfile.setsampwidth(audio.get_sample_size(FORMAT)) 
wavfile.setframerate(RATE) 
wavfile.writeframes(b''.join(frames))#append frames recorded to file 
wavfile.close() 

Я думаю, что это будет help.It простой скрипт, который будет проверять, есть ли молчание или not.If silenc e обнаружено, что он не будет записывать, иначе он будет записываться.

Смежные вопросы