|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | """ 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) 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) |