""" Record a few seconds of audio and save to a WAVE file. Based on http://stackoverflow.com/questions/892199/detect-record-audio-in-python/6743593#6743593 """ import pyaudio import wave import sys from array import array from struct import unpack, pack from pydispatch import dispatcher THRESHOLD = 500 CHUNK_SIZE = 1024 FORMAT = pyaudio.paInt16 RATE = 44100 if sys.platform.startswith('darwin'): CHANNELS = 2 elif sys.platform.startswith('win32'): CHANNELS = 1 MAX_SILENT = 80 def is_silent(L): "Returns `True` if below the 'silent' threshold" "print max(L)" "print max(L) < THRESHOLD" return max(L) < THRESHOLD def normalize(L): "Average the volume out" MAXIMUM = 16384 times = float(MAXIMUM) / max(abs(i) for i in L) LRtn = array('h') for i in L: LRtn.append(int(i * times)) return LRtn def trim(L): "Trim the blank spots at the start and end" def _trim(L): snd_started = False LRtn = array('h') for i in L: if not snd_started and abs(i) > THRESHOLD: snd_started = True LRtn.append(i) elif snd_started: LRtn.append(i) return LRtn # Trim to the left L = _trim(L) # Trim to the right L.reverse() L = _trim(L) L.reverse() return L def add_silence(L, seconds): "Add silence to the start and end of `L` of length `seconds` (float)" LRtn = array('h', [0 for i in xrange(int(seconds * RATE))]) LRtn.extend(L) LRtn.extend([0 for i in xrange(int(seconds * RATE))]) return LRtn 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=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK_SIZE) num_silent = 0 snd_started = False LRtn = array('h') while 1: try: data = stream.read(CHUNK_SIZE) # Fewer than n bytes may be returned if the operating system call returns fewer than n bytes. #if len(data) < 4096: #print "cropped packet detected! " + len(data) #data.extend('\x00' * CHUNK_SIZE - len(data)) #print "fixed to " + len(data) except IOError as ex: if ex[1] != pyaudio.paInputOverflowed: raise data = '\x00' * CHUNK_SIZE L = unpack('<' + ('h' * (len(data) / 2)), data) # little endian, signed short L = array('h', L) silent = is_silent(L) #print silent, num_silent, L[:10] if silent and snd_started: num_silent += 1 #print num_silent elif not silent and not snd_started: dispatcher.send(signal='SND_STARTED') snd_started = True print snd_started if snd_started and not silent: num_silent = 0 if snd_started: LRtn.extend(L) if snd_started and num_silent > MAX_SILENT: break sample_width = p.get_sample_size(FORMAT) stream.stop_stream() stream.close() p.terminate() LRtn = normalize(LRtn) LRtn = trim(LRtn) LRtn = add_silence(LRtn, 0.5) return sample_width, LRtn 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(CHANNELS) wf.setsampwidth(sample_width) wf.setframerate(RATE) wf.writeframes(data) wf.close() print("done - result written to " + path) del data if __name__ == '__main__': filename = 'demo.wav' record_to_file(filename) print("done - result written to " + filename)