Python'da Sesi Algılama ve Kaydetme


99

Daha sonra işlemek için başka bir python bitine aktarabileceğim ses kliplerini WAV dosyaları olarak yakalamam gerekiyor. Sorun şu ki, sesin ne zaman mevcut olduğunu belirlemem ve ardından kaydetmem, sessizleştiğinde durmam ve ardından bu dosyayı işleme modülüne aktarmam gerekiyor.

Dalga modülüyle saf sessizliğin ne zaman olduğunu tespit etmenin ve onu atmanın mümkün olması gerektiğini düşünüyorum, ardından sessizlikten başka bir şey tespit edildiğinde kayda başlayın, ardından hat tekrar sessizleştiğinde kaydı durdurun.

Aklımı tam olarak anlayamıyorum, herhangi biri beni basit bir örnekle başlatabilir mi?

Yanıtlar:


106

Nick Fortescue'nun cevabının bir devamı olarak, mikrofondan nasıl kayıt yapılacağına ve elde edilen verilerin nasıl işleneceğine dair daha eksiksiz bir örnek:

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
Bunun Python 3'te çalışması için xrange'i range ile değiştirin.
Ben Elgar

1
Harika örnek! Python kullanarak nasıl ses kaydı yapacağımı kafamı çevirmeye çalıştığımda gerçekten yararlı. Aklıma gelen bir soru, kaydın zaman periyodunu tanımlamanın bir yolu olup olmadığıdır. Şimdi bir kelime mi kaydediyor? Onunla oynayabilir miyim ve örneğin 10 saniyelik bir rekor sürebilir miyim? Teşekkürler!
Swan87

Tespit ve normalleştirme doğru değildir, çünkü kısa devreler yerine baytlar üzerinden hesaplarlar. Bu tamponun işlemeden önce bir uyuşuk diziye dönüştürülmesi gerekir.
ArekBulski

Ne xrangene rangede gerçekten gerekliydi add_silence(yani şimdi gitti). Sanırım Arek burada bir şeyin üzerine düşüyor - sessizlikten 'kelimeye' geçiş çok sarsıntılı geliyor. Bence ona hitap eden başka cevaplar da var.
Tomasz Gandor

47

WAVE modülünün kaydı desteklemediğine inanıyorum, sadece mevcut dosyaları işliyor. Aslında kayıt için PyAudio'ya bakmak isteyebilirsiniz . WAV, dünyanın en basit dosya formatı ile ilgilidir. PaInt16'da sadece bir seviyeyi temsil eden işaretli bir tam sayı elde edersiniz ve 0'a yaklaşıldığında daha sessizdir. WAV dosyalarının önce yüksek bayt mı yoksa düşük bayt mı olduğunu hatırlayamıyorum, ancak bunun gibi bir şey çalışmalı (üzgünüm, ben gerçekten bir python programcısı değilim:

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

Kayıt için PyAudio kodu referans için tutuldu:

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

Teşekkürler Nick, Evet, yakalama için portaudio kullandığımı söylemeliydim, takıldığım kısım sessizliği kontrol etmek, veri yığınındaki seviyeye nasıl ulaşabilirim?

Yukarıya gerçekten basit bir test edilmemiş kod ekledim, ancak istediğiniz işi yapması gerekiyor
Nick Fortescue

Önceki sürümümde bir hata vardı, işaretle düzgün bir şekilde ilgilenmiyordu. Şimdi düzgün bir şekilde ayrıştırmak için kütüphane işlev dizisini () kullandım
Nick Fortescue

WAV dosya formatı bir kapsayıcıdır, çeşitli kodeklerle (GSM veya MP3 gibi) kodlanmış ses içerebilir, bazıları 'dünyanın en basitinden' uzaktır.
Jacek Konieczny

2
Akışı açarken "output = True" seçeneğinin kayıt için gerekli olmadığına ve ayrıca aygıtımda "IOError: [Errno Input overflowed] -9981" e neden olduğuna inanıyorum. Aksi takdirde kod örneği için teşekkürler, çok yardımcı oldu.
Binus

19

Aşağıdaki test koduma dayandırdığım geliştirilmiş sürüm için cryo sayesinde:

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

teşekkürler, harika çalışıyor. Benim durumumda düzenlemem return copy.deepcopy(data_all[_from:(_to + 1)])gerekiyorcopy.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()

Bunun yardımcı olacağını düşünüyorum.Sessizlik olup olmadığını kontrol edecek basit bir betiktir.Sessizlik algılanırsa kayıt yapmaz aksi takdirde kayıt yapar.


3

Pyaudio web sitesinde oldukça kısa ve net birçok örnek var: http://people.csail.mit.edu/hubert/pyaudio/

14 Aralık 2019 Güncellemesi - Yukarıdaki bağlantılı web sitesinden 2017 yılına ait ana örnek:


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

Csound'lara da bakmak isteyebilirsiniz . Python dahil birkaç API'ye sahiptir. Bir AD arayüzü ile etkileşime girebilir ve ses örnekleri toplayabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.