Merge branch 'master' of ssh://apples.lambdacomplex.org/git/scannr
[scannr.git] / snd.py












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
""" 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
import audioop # http://docs.python.org/library/audioop
from os.path import exists
from array import array
from struct import unpack, pack
import threading
from pydispatch import dispatcher
 
CHANNELS = 1
THRESHOLD = 500
CHUNK_SIZE = 1024
FORMAT = pyaudio.paInt16
RATE = 44100
MAX_SILENT = 30
 
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=1, rate=RATE, 
                    input=True, output=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
            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 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(1)
    wf.setsampwidth(sample_width)
    wf.setframerate(RATE)
    wf.writeframes(data)
    wf.close()
    print("done - result written to "+path)
 
 
 
 
if __name__ == '__main__':
        filename = 'demo.wav'
        record_to_file(filename)
        print("done - result written to "+filename)