카테고리 없음
[pyaudio] 푸시버튼(스위치)으로 녹음하고 중단하기 (라즈베라파이 4 bookworm)
미친토끼
2024. 11. 16. 21:37
1. signal의 pause 함수를 사용한 버전.
푸시 버턴1을 누르면 녹음을 시작하고 버튼2를 누르면 녹음을 종료한다.
pause 함수를 사용해 버턴 입력을 대기한다.
-- 버턴, LED 연결
버턴 1: GPIO 17 (누르면 녹음 시작)
버턴 2: GPIO 3 (누르면 녹음 중지. 녹음을 종료하지만 프로그램은 대기. 버턴1을 누르면 다른 파일로 다시 녹음 시작.)
LED: GPIO 2 (버턴 1을 누르면 켜지고, 버턴 2를 누르면 꺼짐)
# 버턴을 누르면 녹음을 시작하고, 버턴을 손에서 떼면 녹음을 중단한다.
import pyaudio
import wave
from gpiozero import Button, LED
from signal import pause
from time import sleep
import datetime
# 푸시 버턴을 누르면 시작되는 함수
def start_recording():
global p, stream, frames, recording # 이 변수들을 다른 함수에서도 쓸 수 있도록 global 선언을 해둠
led.on()
recording = 1
p = pyaudio.PyAudio()
stream = p.open(format=format, channels=channels, rate=rate, input=True, frames_per_buffer=frames_per_buffer)
print("start recording")
frames = []
while True:
data = stream.read(frames_per_buffer)
frames.append(data)
print(len(frames))
if button2.is_pressed: # 녹음 중단 버튼이 눌러졌으면 stop_recording 함수를 실행함
stop_recording()
break # break를 반드시 사용해야 함. 아래 참조
if len(frames) > 5000: # 지나치게 오랫동안 녹음한다면 (녹음 시간 제한) 녹음 중단. frames을 버퍼에 쓰는 횟수 기준, 대략 몇 분 정도
stop_recording()
break
'''
break 관련: stop_recording()을 실행하고 나면 여기로 돌아오는데 break를 걸지 않으면, while 루프를 계속 돌게 된다.
그런데 이미 읽기 스트림을 우리가 stop_recording에서 닫았기 때문에 여기(start_recording 함수)에서 stream.read(frames_per_buffer)를 수행하는
스레드가 스트림을 읽을 수 없다고 에러를 발생시키고 종료해버리는 오류가 발생한다.
따라서 이미 stop_recording을 통해 소기의 목적을 달성했기 때문에 break를 통해 반드시 이 함수(start_recording)를 빠져 나가야 한다.
stop_recording을 start_recording 안에서 호출한 까닭은, 녹음 중지 푸쉬 버턴이 아무 때나 눌릴 수 있는데, 그것을 체크하는 루틴이
start_recording 안에 있는 게 좋은데, 그 까닭은 start_recording 루틴 자체가 상당히 오랫동안 수행될 수 있기 때문이다.
'''
def stop_recording():
global recording
if recording == 0: # 녹음 중이 아닌데 녹음 중지 버턴이 눌렸다면... 녹음 중지 버턴이 눌리고 연달아 중지 버턴이 눌린 상황이 종종 발생함
print("It's not recording state!") # 버턴을 한번 눌러도 버턴이 눌린 연속 시간 때문에 이 신호가 여러 번 발생할 수 있음. 그럴 때는 수행하지 않고 호출 지점으로 다시 돌아가야 함
return
led.off()
# 스트림을 닫고 핸들러를 닫는 작업
stream.stop_stream()
stream.close()
p.terminate()
# 파일명을 날짜시간에서 가져옴
filepath = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # 20241116_162808
filename = filepath + ".wav"
obj = wave.open(filename, "wb")
# wav 파일 정보를 세팅하고 저장
obj.setnchannels(channels)
obj.setsampwidth(p.get_sample_size(format))
obj.setframerate(rate)
obj.writeframes(b"".join(frames))
obj.close()
print("file wrote: ", filename)
recording = 0
## 메인 루틴
button1 = Button(17)
button2 = Button(3)
led = LED(2)
button1.when_pressed = start_recording # 버턴을 한번 눌러도 버턴이 눌린 연속 시간 때문에 이 신호가 여러 번 발생할 수 있음.
button2.when_pressed = stop_recording # " "
recording = 0 # 현재 레코딩 중인지 아닌지 상태
frames_per_buffer = 1024
format = pyaudio.paInt16
channels = 1
rate = 48000
pause() # pause () 함수를 while 문으로 길게 풀어써도 됨. pause()가 확실히 스레드를 발생시켜 콜백 함수를 실행시키는 듯.
2. pause 대신 while 문을 사용한 버전
while 문을 사용해 pause와 동일한 효과를 내었다. 아무래도 스레드를 사용하는 pause가 작동 방식이나 코드에서 간결하긴 하지만 while문이 아무래도 그립긴 하다.
# 버턴을 누르면 녹음을 시작하고, 버턴을 손에서 떼면 녹음을 중단한다.
import pyaudio
import wave
from gpiozero import Button, LED
from signal import pause
from time import sleep
import datetime
# 푸시 버턴을 누르면 시작되는 함수
def start_recording():
global p, stream, frames # 이 변수들을 다른 함수에서도 쓸 수 있도록 global 선언을 해둠
led.on()
#recording = 1
p = pyaudio.PyAudio()
stream = p.open(format=format, channels=channels, rate=rate, input=True, frames_per_buffer=frames_per_buffer)
print("start recording")
frames = []
while True:
data = stream.read(frames_per_buffer)
frames.append(data)
print(len(frames))
#print(".", end="")
if button2.is_pressed: # 녹음 중지 버턴이 눌렸으면
stop_recording() # 녹음 중지 과정을 시작함
break # 녹음 중지 함수stop_recording()를 실행하고 나서 이리로 오는데 break 가 없을 경우, 계속 루프를 돌면서 stream.read()를 실행하는데, 이때 우리가 stop_recording에서 stream을 닫았기 때문에 프로세스(혹은 스레드)가 스트림을 읽을 수 없어서 종료하는 오류가 생김. break를 통해 이 함수를 빠져나가 메인으로 돌아가야 제대로 동작함
if len(frames) > 1000000: # 지나치게 오랫동안 녹음한다면 (녹음 시간 제한). 수치는 버퍼 기록하는 횟수임
stop_recording()
break
def stop_recording():
global recording
if recording != 1: # 현재 녹음 상태가 아니라면...
print("It's not recording state.")
return
led.off()
# 스트림을 닫고 핸들러를 닫는 작업
stream.stop_stream()
#sleep(0.5)
# 그냥 스트림을 닫으면 스레드에 의해 "OSError: [Errno -9988] Stream closed" 에러가 발생하기 때문에 스레드가 대응할 시간을 줌
stream.close()
p.terminate()
# 파일명을 날짜시간에서 가져옴
filepath = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # 20241116_162808
filename = filepath + ".wav"
obj = wave.open(filename, "wb")
# wav 파일 정보를 세팅하고 저장
obj.setnchannels(channels)
obj.setsampwidth(p.get_sample_size(format))
obj.setframerate(rate)
obj.writeframes(b"".join(frames))
obj.close()
print("file wrote: ", filename)
#recording = 0
## 메인 루틴
button1 = Button(17)
button2 = Button(3)
led = LED(2)
recording = 0 # 현재 레코딩 중인지 아닌지 상태
#button1.when_pressed = start_recording
#button2.when_pressed = stop_recording
frames_per_buffer = 1024
format = pyaudio.paInt16
channels = 1
rate = 48000
#pause()
while True:
if button1.is_pressed:
if recording == 1:
print("Already recording state. Press stop button first.")
continue
recording = 1
start_recording()
if button2.is_pressed:
if recording == 0:
print("It's not recording state. Press recording button first.")
continue
recording = 0
stop_recording()
sleep(0.1)