Python에서 오디오 감지 및 녹음


99

오디오 클립을 WAV 파일로 캡처 한 다음 처리를 위해 다른 파이썬으로 전달할 수 있어야합니다. 문제는 오디오가 언제 있는지 확인한 다음 녹음하고 소리가 나지 않으면 중지 한 다음 해당 파일을 처리 모듈로 전달해야한다는 것입니다.

나는 웨이브 모듈로 순수한 무음이있을 때를 감지하고 무음 이외의 것이 감지되면 즉시 폐기하고 녹음을 시작하고 라인이 다시 조용 해지면 녹음을 중지하는 것이 가능해야한다고 생각합니다.

그저 내 머리를 잘 이해하지 못합니다. 누구든지 기본적인 예제로 저를 시작할 수 있습니다.

답변:


106

Nick Fortescue의 답변에 대한 후속 조치로 마이크에서 녹음하고 결과 데이터를 처리하는 방법에 대한 더 완전한 예가 있습니다.

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)"
    silence = [0] * int(seconds * RATE)
    r = array('h', silence)
    r.extend(snd_data)
    r.extend(silence)
    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")

17
Python 3에서이 작업을 수행하려면 xrange를 범위로 바꾸십시오.
Ben Elgar

1
좋은 예! Python을 사용하여 음성을 녹음하는 방법에 대해 머리를 감쌀 때 정말 유용합니다. 한 가지 빠른 질문은 녹화 기간을 정의하는 방법이 있는지 여부입니다. 이제 단어를 녹음합니까? 그것을 가지고 놀 수 있고 10 초의 녹음 기간을 가질 수 있습니까? 감사!
Swan87

탐지 및 정규화는 단락이 아닌 바이트로 계산하기 때문에 올바르지 않습니다. 그 버퍼는 처리하기 전에 numpy 배열로 변환되어야합니다.
ArekBulski

둘 다 실제로 필요 xrange하지도 않았습니다 (지금은 사라졌습니다). 나는 Arek이 여기에 무언가에 있다고 생각합니다. 침묵에서 '단어'로의 전환은 너무 갑작스럽게 들립니다. 나는 그것을 해결하는 다른 답변이 있다고 생각합니다. rangeadd_silence
Tomasz Gandor

47

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()

닉 고마워요, 예, 캡처를 위해 포트 오디오를 사용하고 있다고 말 했어야했는데, 제가 갇혀있는 부분은 침묵을 확인하는 것입니다. 데이터 덩어리에서 레벨을 얻는 방법은 무엇입니까?

나는 위의 몇 가지 정말 간단한 테스트되지 않은 코드를 추가 한, 그러나 당신이 원하는 작업을 수행한다고
닉 Fortescue의

내 이전 버전에는 버그가 있었고 표시를 제대로 처리하지 못했습니다. 지금 제대로 구문 분석하기 위해 라이브러리 함수 array ()를 사용했습니다
Nick Fortescue

WAV 파일 형식은 컨테이너이며 다양한 코덱 (예 : GSM 또는 MP3)을 통해 인코딩 된 오디오를 포함 할 수 있으며 일부는 '세계에서 가장 단순한'것과는 거리가 멀습니다.
Jacek Konieczny

2
스트림을 여는 동안 "output = True"옵션은 녹음에 필요하지 않으며 내 장치에서 "IOError : [Errno Input overflowed] -9981"이 발생하는 것 같습니다. 그렇지 않으면 코드 예제 덕분에 매우 유용했습니다.
Binus

19

아래 테스트 코드를 기반으로 한 개선 된 버전에 대한 cryo에게 감사드립니다.

#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")

감사합니다. 제 경우에는 편집 return copy.deepcopy(data_all[_from:(_to + 1)])해야합니다copy.deepcopy(data_all[int(_from):(int(_to) + 1)])
lukassliacky

6
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()

나는 이것이 도움이 될 것이라고 생각한다. 그것은 침묵이 있는지 여부를 확인하는 간단한 스크립트입니다. 무음이 감지되면 기록하지 않으면 기록합니다.


3

pyaudio 웹 사이트에는 매우 짧고 명확한 많은 예가 있습니다. http://people.csail.mit.edu/hubert/pyaudio/

2019 년 12 월 14 일 업데이트-2017 년 위 링크 된 웹 사이트의 주요 예 :


"""PyAudio Example: Play a WAVE file."""

import pyaudio
import wave
import sys

CHUNK = 1024

if len(sys.argv) < 2:
    print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
    sys.exit(-1)

wf = wave.open(sys.argv[1], 'rb')

p = pyaudio.PyAudio()

stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                channels=wf.getnchannels(),
                rate=wf.getframerate(),
                output=True)

data = wf.readframes(CHUNK)

while data != '':
    stream.write(data)
    data = wf.readframes(CHUNK)

stream.stop_stream()
stream.close()

p.terminate()

0

csounds 를 볼 수도 있습니다. Python을 포함한 여러 API가 있습니다. AD 인터페이스와 상호 작용하고 사운드 샘플을 수집 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.