Pythonでリアルタイムのプロットサウンドを作成しようとしています。マイクからチャンクを取得する必要があります。
PyAudioを使用して、使用してみてください
import pyaudio
import wave
import sys
chunk = 1024
FORMAT = pyaudio.Paint16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = chunk)
print "* recording"
all = []
for i in range(0, RATE / chunk * RECORD_SECONDS):
data = stream.read(chunk)
all.append(data)
print "* done recording"
stream.close()
p.terminate()
その後、フォローインエラーが発生します:
* recording
Traceback (most recent call last):
File "gg.py", line 23, in <module>
data = stream.read(chunk)
File "/usr/lib64/python2.7/site-packages/pyaudio.py", line 564, in read
return pa.read_stream(self._stream, num_frames)
IOError: [Errno Input overflowed] -9981
このバッファを理解できません。ブロッキングIO=モードを使用したいので、チャンクが利用できない場合、それらのチャンクを待ちます。しかし、セグメントまたはスリープ(0.1)以外のトライを作成すると、クリック音が聞こえるので、これは私が欲しいものではありません。
私の問題に最適なソリューションを提案してください。
pyaudio.Stream.read()
にはキーワードパラメータexception_on_overflow
、これをFalseに設定します。
次のようなサンプルコードの場合:
import pyaudio
import wave
import sys
chunk = 1024
FORMAT = pyaudio.Paint16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 5
WAVE_OUTPUT_FILENAME = "output.wav"
p = pyaudio.PyAudio()
stream = p.open(format = FORMAT,
channels = CHANNELS,
rate = RATE,
input = True,
frames_per_buffer = chunk)
print "* recording"
all = []
for i in range(0, RATE / chunk * RECORD_SECONDS):
data = stream.read(chunk, exception_on_overflow = False)
all.append(data)
print "* done recording"
stream.close()
p.terminate()
詳細は PyAudioのドキュメント を参照してください。
コードを実行したときにも同じエラーが発生しました。私は私のデフォルトのオーディオデバイス、私のMacbookの内部マイクのデフォルトのサンプルレートを見ました、それは44100Hzではなく48000Hzでした。
p.get_device_info_by_index(0)['defaultSampleRate']
Out[12]: 48000.0
RATEをこの値に変更すると、うまくいきました。
多くの人がこの問題に遭遇しているようです。私はそれに少し掘り下げました、そしてそれはstream.read()
への以前の呼び出しとこの現在の呼び出しの間で、ストリームからのデータが失われたことを意味すると思います(つまり、あなたがそれをクリアしたよりも速くバッファがいっぱいになりました)。
Pa_ReadStream()
のドキュメントから(stream.read()
が最終的に呼び出すPortAudio関数):
@return On success PaNoError will be returned, or PaInputOverflowed if
input data was discarded by PortAudio after the previous call and
before this call.
(PaInputOverflowed
はpyaudioラッパーでIOError
を発生させます)。
すべてのフレームをキャプチャしなくても問題ない場合は、このエラーを無視できます。すべてのフレームを使用することが絶対的に重要な場合は、アプリケーションの優先度を上げる方法を見つける必要があります。私はPythonに慣れていないため、Pythonでこれを行う方法を知ることはできませんが、単純なNice
コマンドを試すか、スケジューリングポリシーをSCHED_DEADLINEに変更する価値はあります。
編集:
現在の1つの問題は、IOErrorがスローされると、その呼び出しで収集されたすべてのフレームが失われることです。代わりにオーバーフローを無視して、私たちが持っているものだけを返すには、以下のパッチを適用します。これにより、stream.read()はPortAudioからの出力アンダーランおよび入力オーバーフローエラーを無視します(ただし、別のエラーが発生した場合は何かをスローします)。より良い方法は、ニーズに応じてこの動作(スロー/スローなし)をカスタマイズ可能にすることです。
diff --git a/src/_portaudiomodule.c b/src/_portaudiomodule.c
index a8f053d..0878e74 100644
--- a/src/_portaudiomodule.c
+++ b/src/_portaudiomodule.c
@@ -2484,15 +2484,15 @@ pa_read_stream(PyObject *self, PyObject *args)
} else {
/* clean up */
_cleanup_Stream_object(streamObject);
+
+ /* free the string buffer */
+ Py_XDECREF(rv);
+
+ PyErr_SetObject(PyExc_IOError,
+ Py_BuildValue("(s,i)",
+ Pa_GetErrorText(err), err));
+ return NULL;
}
-
- /* free the string buffer */
- Py_XDECREF(rv);
-
- PyErr_SetObject(PyExc_IOError,
- Py_BuildValue("(s,i)",
- Pa_GetErrorText(err), err));
- return NULL;
}
return rv;
私はこれをOS X 10.10で作業しました。SYBAUSBカード(Cメディアチップセット)のマイクからオーディオを取得しようとするときに同じエラーが発生し、fftなどでリアルタイムに処理しました。
IOError: [Errno Input overflowed] -9981
Libbkmzによって記述されているように、ブロッキングモードではなくコールバックモードを使用すると、オーバーフローが完全に解決されました。( https://www.python.org/dev/peps/pep-0263/ )
これに基づいて、作業コードのビットは次のようになりました。
"""
Creating the audio stream from our mic
"""
rate=48000
self.chunk=2**12
width = 2
p = pyaudio.PyAudio()
# callback function to stream audio, another thread.
def callback(in_data,frame_count, time_info, status):
self.audio = numpy.fromstring(in_data,dtype=numpy.int16)
return (self.audio, pyaudio.paContinue)
#create a pyaudio object
self.inStream = p.open(format = p.get_format_from_width(width, unsigned=False),
channels=1,
rate=rate,
input=True,
frames_per_buffer=self.chunk,
stream_callback = callback)
"""
Setting up the array that will handle the timeseries of audio data from our input
"""
self.audio = numpy.empty((self.buffersize),dtype="int16")
self.inStream.start_stream()
while True:
try:
self.ANY_FUNCTION() #any function to run parallel to the audio thread, running forever, until ctrl+C is pressed.
except KeyboardInterrupt:
self.inStream.stop_stream()
self.inStream.close()
p.terminate()
print("* Killed Process")
quit()
このコードは、コールバック関数を作成してから、ストリームオブジェクトを作成し、それを開始して、任意の関数でループします。別のスレッドがオーディオをストリーミングし、そのループはメインループが停止すると閉じられます。 self.audioは任意の関数で使用されます。終了しないと、スレッドが永久に実行されるという問題もありました。
Pyaudioはこのストリームを別のスレッドで実行し、これによりオーディオストリームが安定したため、スクリプトの残りのプロセスの速度またはタイミングによっては、ブロッキングモードが飽和している可能性があります。
チャンクサイズは2 ^ 12ですが、小さいチャンクも同様に機能します。私が考慮して遊んだ他のパラメーターがあり、それらがすべて理にかなっていることを確認します。
それが誰かのために働くことを願っています!
FORMAT = pyaudio.Paint16
必ず正しい形式を設定してください。内蔵マイクは24ビットに設定されています(Audio-Midi-Setupアプリケーションを参照)。
私の 他の答え はほとんどの場合問題を解決しました。ただし、エラーが発生することもあります。
それが私がpyaudioを破棄してpyalsaaudioに切り替えた理由です。 Raspyがサウンドをスムーズに録音できるようになりました。
import alsaaudio
import numpy as np
import array
# constants
CHANNELS = 1
INFORMAT = alsaaudio.PCM_FORMAT_FLOAT_LE
RATE = 44100
FRAMESIZE = 1024
# set up audio input
recorder=alsaaudio.PCM(type=alsaaudio.PCM_CAPTURE)
recorder.setchannels(CHANNELS)
recorder.setrate(RATE)
recorder.setformat(INFORMAT)
recorder.setperiodsize(FRAMESIZE)
buffer = array.array('f')
while <some condition>:
buffer.fromstring(recorder.read()[1])
data = np.array(buffer, dtype='f')
非常に遅いRaspberry Piでも同じ問題が発生しましたが、データの格納に高速のarray
モジュールを使用することで解決できました(ほとんどの場合)。
_import array
import pyaudio
FORMAT = pyaudio.Paint16
CHANNELS = 1
INPUT_CHANNEL=2
RATE = 48000
CHUNK = 512
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=INPUT_CHANNEL,
frames_per_buffer =CHUNK)
print("* recording")
try:
data = array.array('h')
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data.fromstring(stream.read(CHUNK))
finally:
stream.stop_stream()
stream.close()
p.terminate()
print("* done recording")
_
data
の内容は、後でバイナリになっています。しかし、numpy.array(data, dtype='i')
を使用して、整数の配列を取得できます。
私にとってこれは役に立ちました: https://stackoverflow.com/a/46787874/5047984
マルチプロセッシングを使用して、オーディオの録音と並行してファイルを書き込みました。これは私のコードです:
recordAudioSamples.py
import pyaudio
import wave
import datetime
import signal
import ftplib
import sys
import os
# configuration for assos_listen
import config
# run the audio capture and send sound sample processes
# in parallel
from multiprocessing import Process
# CONFIG
CHUNK = config.chunkSize
FORMAT = pyaudio.Paint16
CHANNELS = 1
RATE = config.samplingRate
RECORD_SECONDS = config.sampleLength
# HELPER FUNCTIONS
# write to ftp
def uploadFile(filename):
print("start uploading file: " + filename)
# connect to container
ftp = ftplib.FTP(config.ftp_server_ip, config.username, config.password)
# write file
ftp.storbinary('STOR '+filename, open(filename, 'rb'))
# close connection
ftp.quit()
print("finished uploading: " +filename)
# write to sd-card
def storeFile(filename,frames):
print("start writing file: " + filename)
wf = wave.open(filename, 'wb')
wf.setnchannels(CHANNELS)
wf.setsampwidth(p.get_sample_size(FORMAT))
wf.setframerate(RATE)
wf.writeframes(b''.join(frames))
wf.close()
print(filename + " written")
# abort the sampling process
def signal_handler(signal, frame):
print('You pressed Ctrl+C!')
# close stream and pyAudio
stream.stop_stream()
stream.close()
p.terminate()
sys.exit(0)
# MAIN FUNCTION
def recordAudio(p, stream):
sampleNumber = 0
while (True):
print("* recording")
sampleNumber = sampleNumber +1
frames = []
startDateTimeStr = datetime.datetime.now().strftime("%Y_%m_%d_%I_%M_%S_%f")
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
data = stream.read(CHUNK)
frames.append(data)
fileName = str(config.sensorID) + "_" + startDateTimeStr + ".wav"
# create a store process to write the file in parallel
storeProcess = Process(target=storeFile, args=(fileName,frames))
storeProcess.start()
if (config.upload == True):
# since waiting for the upload to finish will take some time
# and we do not want to have gaps in our sample
# we start the upload process in parallel
print("start uploading...")
uploadProcess = Process(target=uploadFile, args=(fileName,))
uploadProcess.start()
# ENTRYPOINT FROM CONSOLE
if __name__ == '__main__':
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels=CHANNELS,
rate=RATE,
input=True,
frames_per_buffer=CHUNK)
# directory to write and read files from
os.chdir(config.storagePath)
# abort by pressing C
signal.signal(signal.SIGINT, signal_handler)
print('\n\n--------------------------\npress Ctrl+C to stop the recording')
# start recording
recordAudio(p, stream)
config.py
### configuration file for assos_listen
# upload
upload = False
# config for this sensor
sensorID = "al_01"
# sampling rate & chunk size
chunkSize = 8192
samplingRate = 44100 # 44100 needed for Aves sampling
# choices=[4000, 8000, 16000, 32000, 44100] :: default 16000
# sample length in seconds
sampleLength = 10
# configuration for assos_store container
ftp_server_ip = "192.168.0.157"
username = "sensor"
password = "sensor"
# storage on assos_listen device
storagePath = "/home/pi/assos_listen_pi/storage/"
これは私にとって役に立ちました:
input_ = stream.read(chunk, exception_on_overflow=False)
exception_on_overflow = False