2 # Python library to play sounds using ALSA from inside a
5 # We use the 'non-blocking' output routines, write until
6 # we can write no more, or we hit the stop-latency limit.
7 # Then set a timer to try again when we estimate the buffer
10 # playing can be interrupted at any time - we allow the buffers
13 # Currently only wav files
15 import gobject, alsaaudio, time, struct, sys
18 def __init__(self, file, latency_ms = 1000, done = None):
19 # Arrange to play 'file' - which is the name of a .wav file
20 self.pcm = alsaaudio.PCM(alsaaudio.PCM_PLAYBACK, alsaaudio.PCM_NONBLOCK)
21 self.latency_ms = latency_ms
26 def setfile(self, file):
29 # 4-7 Bytes in rest of file.
32 # 16-19 bytes of format
33 # 20-21 ==1 Microsoft PCM
37 # 32-33 bytes per frame
38 # 34-35 bits per sample
40 # 40-43 number of bytes of data
41 # 44... actual samples
43 header = self.f.read(44)
46 riff, b1, wave, fmt, b2, format, chan, rate, br, bf, bs, data, b3 = \
47 struct.unpack("4si4s 4sihhiihh 4si", header)
49 if riff != "RIFF" or wave != "WAVE" or fmt != "fmt " or data != "data":
51 if format != 1 or bs != 16:
54 self.pcm.setformat(alsaaudio.PCM_FORMAT_S16_LE)
56 if chan < 1 or chan > 4:
59 self.pcm.setchannels(chan)
61 self.pcm.setrate(rate)
62 self.bytes_per_second = rate * 2 * chan
64 # choose the period to be 1/8 of the latency,
65 # probably need to set an upper bound
66 frames_per_latency = rate * self.latency_ms / 1000
67 self.bytes_per_latency = frames_per_latency * chan * 2;
68 #self.bytes_per_period = (frames_per_latency / 8) * chan * 2
69 self.bytes_per_period = 320
73 self.pcm.setperiodsize(self.bytes_per_period / chan / 2)
74 #print "bytes_per_period", self.bytes_per_period
75 #print "period size", self.bytes_per_period / chan / 2
77 self.start = time.time()
88 pos = int( (time.time() - self.start) * self.bytes_per_second)
89 buffered = self.loaded - pos
92 while buffered < self.bytes_per_latency + self.bytes_per_period:
94 data = self.f.read(self.bytes_per_period)
101 if not self.pcm.write(data):
106 buffered += self.bytes_per_period
109 self.loaded = buffered + pos
111 pos = int( (time.time() - self.start) * self.bytes_per_second)
112 buffered = self.loaded - pos
113 delay = int(buffered /4 * 1000 / self.bytes_per_second)
114 print "wrote", cnt, "delay" ,delay
116 self.start += float( 20 - delay) / 1000
118 gobject.timeout_add(delay, self.playsome, priority = gobject.PRIORITY_HIGH)
121 if __name__ == "__main__":
123 # play given wav file in a loop for 20 seconds, then stop
126 p.setfile(sys.argv[1])
127 p = Play(sys.argv[1], 400, done)
128 c = gobject.main_context_default()
131 gobject.timeout_add(20000, abort)
132 while not p.finished: