|
| 1 | +from sys import byteorder |
| 2 | +from array import array |
| 3 | +from struct import pack |
| 4 | + |
| 5 | +import pyaudio |
| 6 | +import wave |
| 7 | + |
| 8 | +THRESHOLD = 500 |
| 9 | +CHUNK_SIZE = 1024 |
| 10 | +FORMAT = pyaudio.paInt16 |
| 11 | +RATE = 44100 |
| 12 | + |
| 13 | +def is_si<a href="https://www.baidu.com/s?wd=len&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">len</a>t(snd_data): |
| 14 | + "Returns 'True' if below the 'si<a href="https://www.baidu.com/s?wd=len&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">len</a>t' threshold" |
| 15 | + return <a href="https://www.baidu.com/s?wd=max&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">max</a>(snd_data) < THRESHOLD |
| 16 | + |
| 17 | +def normalize(snd_data): |
| 18 | + "Average the volume out" |
| 19 | + <a href="https://www.baidu.com/s?wd=MAX&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">MAX</a>IMUM = 16384 |
| 20 | + times = float(<a href="https://www.baidu.com/s?wd=MAX&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">MAX</a>IMUM)/max(<a href="https://www.baidu.com/s?wd=abs&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">abs</a>(i) for i in snd_data) |
| 21 | + |
| 22 | + r = array('h') |
| 23 | + for i in snd_data: |
| 24 | + r.app<a href="https://www.baidu.com/s?wd=end&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">end</a>(int(i*times)) |
| 25 | + return r |
| 26 | + |
| 27 | +def trim(snd_data): |
| 28 | + "Trim the blank spots at the start and <a href="https://www.baidu.com/s?wd=end&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">end</a>" |
| 29 | + def _trim(snd_data): |
| 30 | + snd_started = False |
| 31 | + r = array('h') |
| 32 | + |
| 33 | + for i in snd_data: |
| 34 | + if not snd_started and <a href="https://www.baidu.com/s?wd=abs&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">abs</a>(i)>THRESHOLD: |
| 35 | + snd_started = True |
| 36 | + r.app<a href="https://www.baidu.com/s?wd=end&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">end</a>(i) |
| 37 | + |
| 38 | + elif snd_started: |
| 39 | + r.append(i) |
| 40 | + return r |
| 41 | + |
| 42 | + # Trim to the left |
| 43 | + snd_data = _trim(snd_data) |
| 44 | + |
| 45 | + # Trim to the right |
| 46 | + snd_data.reverse() |
| 47 | + snd_data = _trim(snd_data) |
| 48 | + snd_data.reverse() |
| 49 | + return snd_data |
| 50 | + |
| 51 | +def add_si<a href="https://www.baidu.com/s?wd=len&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1Y3m1bvnhPBuWuhmhN-nHTd0AP8IA3qPjfsn1bkrjKxmLKz0ZNzUjdCIZwsrBtEXh9GuA7EQhF9pywdQhPEUiqkIyN1IA-EUBtkPWbsnHcznjn4nW6Lnj0krjc3" target="_blank" class="baidu-highlight">len</a>ce(snd_data, seconds): |
| 52 | + "Add silence to the start and end of 'snd_data' of length 'seconds' (float)" |
| 53 | + r = array('h', [0 for i in xrange(int(seconds*RATE))]) |
| 54 | + r.extend(snd_data) |
| 55 | + r.extend([0 for i in xrange(int(seconds*RATE))]) |
| 56 | + return r |
| 57 | + |
| 58 | +def record(): |
| 59 | + """ |
| 60 | + Record a word or words from the microphone and |
| 61 | + return the data as an array of signed shorts. |
| 62 | + |
| 63 | + Normalizes the audio, trims silence from the |
| 64 | + start and end, and pads with 0.5 seconds of |
| 65 | + blank sound to make sure VLC et al can play |
| 66 | + it without getting chopped off. |
| 67 | + """ |
| 68 | + p = pyaudio.PyAudio() |
| 69 | + stream = p.open(format=FORMAT, channels=1, rate=RATE, |
| 70 | + input=True, output=True, |
| 71 | + frames_per_buffer=CHUNK_SIZE) |
| 72 | + |
| 73 | + num_silent = 0 |
| 74 | + snd_started = False |
| 75 | + |
| 76 | + r = array('h') |
| 77 | + |
| 78 | + while 1: |
| 79 | + # little endian, signed short |
| 80 | + snd_data = array('h', stream.read(CHUNK_SIZE)) |
| 81 | + if byteorder == 'big': |
| 82 | + snd_data.byteswap() |
| 83 | + r.extend(snd_data) |
| 84 | + |
| 85 | + silent = is_silent(snd_data) |
| 86 | + |
| 87 | + if silent and snd_started: |
| 88 | + num_silent += 1 |
| 89 | + elif not silent and not snd_started: |
| 90 | + snd_started = True |
| 91 | + |
| 92 | + if snd_started and num_silent > 30: |
| 93 | + break |
| 94 | + |
| 95 | + sample_width = p.get_sample_size(FORMAT) |
| 96 | + stream.stop_stream() |
| 97 | + stream.close() |
| 98 | + p.terminate() |
| 99 | + |
| 100 | + r = normalize(r) |
| 101 | + r = trim(r) |
| 102 | + r = add_silence(r, 0.5) |
| 103 | + return sample_width, r |
| 104 | + |
| 105 | +def record_to_file(path): |
| 106 | + "Records from the microphone and outputs the resulting data to 'path'" |
| 107 | + sample_width, data = record() |
| 108 | + data = pack('<' + ('h'*len(data)), *data) |
| 109 | + |
| 110 | + wf = wave.open(path, 'wb') |
| 111 | + wf.setnchannels(1) |
| 112 | + wf.setsampwidth(sample_width) |
| 113 | + wf.setframerate(RATE) |
| 114 | + wf.writeframes(data) |
| 115 | + wf.close() |
| 116 | + |
| 117 | +if __name__ == '__main__': |
| 118 | + print("please speak a word into the microphone") |
| 119 | + record_to_file('demo.wav') |
| 120 | + print("done - result written to demo.wav") |
0 commit comments