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 | from os.path import exists from array import array from struct import unpack, pack import pyaudio import wave THRESHOLD = 500 CHUNK_SIZE = 1024 FORMAT = pyaudio.paInt16 RATE = 44100 def is_silent(L): "Returns `True` if below the 'silent' 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=2, rate=RATE, input=True, frames_per_buffer=CHUNK_SIZE) num_silent = 0 snd_started = False LRtn = array('h') while 1: data = stream.read(CHUNK_SIZE) L = unpack('<' + ('h'*(len(data)/2)), data) # little endian, signed short L = array('h', L) LRtn.extend(L) silent = is_silent(L) #print silent, num_silent, L[:10] 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() 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(2) 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") |