Michael Lange wrote: > On Tue, 7 Feb 2006 23:31:06 +0100 > Michael Lange <[EMAIL PROTECTED]> wrote: > > >>So I think I need two Condition objects here; it is most important here that >>thread 1 does not >>block to minimize the risk of recording buffer overruns, but from reading the >>docs I am >>not sure about the correct procedure. So if I change self.rec_locked and >>self.vu_locked from the >>code above to be Condition objects is it correct to do: >> > > <snip> > > Ok, some testing gave me the answer, with the code I posted I got an > AssertionError, so obviously > the release() call has to be inside the "if > self.rec_lock.acquire(blocking=0):" block. > So now my functions look like: > > gui thread (periodically called by Tkinter): > > def get_peaks(self): > if not self.vu_lock.acquire(blocking=0): > return None > data = [x for x in self.vubuffer] > self.vubuffer = [] > self.vu_lock.release() > if not data: > return None > left, right = 0, 0 > for d in data: > left = max(audioop.max(audioop.tomono(d, 2, 1, 0), 2), left) > right = max(audioop.max(audioop.tomono(d, 2, 0, 1), 2), right) > return left, right > > child thread 1: > > vubuffer = [] > recbuffer = [] > while self.running: > data = self._audioobj.read(self._fragsize)# read data from > soundcard > vubuffer.append(data) > if self.vu_lock.acquire(blocking=0): > self.vubuffer += vubuffer > vubuffer = [] > self.vu_lock.release() > if self.recording: > recbuffer.append(data) > if self.rec_lock.acquire(blocking=0): > self.recbuffer += recbuffer > recbuffer = [] > self.rec_lock.release() > > child thread 2: > > while self.recording: > # wait a moment until there is something in the buffer to be > written > data = [] > time.sleep(0.1) > if self.rec_lock.acquire(blocking=0): > data = [x for x in self.recbuffer] > self.recbuffer = [] > self.rec_lock.release() > for d in data: > self._waveobj.writeframesraw(d)# write data to file >
This is much better than your original. Another architecture you might consider is to have thread 1 put the actual acquired buffers into two Queues that are read by the two consumer threads. This would save you a lot of copying and give you a cleaner implementation. It may block on the producer thread but the Queue is locked only while something is actually being put in or taken out so the blocks should be short. For example (not tested!): def get_peaks(self): try: data = self.vu_queue.get_nowait() except Queue.Empty: return None left, right = 0, 0 for d in data: left = max(audioop.max(audioop.tomono(d, 2, 1, 0), 2), left) right = max(audioop.max(audioop.tomono(d, 2, 0, 1), 2), right) return left, right child thread 1: while self.running: data = self._audioobj.read(self._fragsize)# read data from soundcard self.vu_queue.put(data) self.rec_queue.put(data) child thread 2: while self.recording: data = self.rec_queue.get() for d in data: self._waveobj.writeframesraw(d)# write data to file > This *seems* to work, however it looks like this code does not separate > properly the gui from the child > threads which everyone says should be avoided in any case. I don't understand your concern here. > On the other hand, with the technique I used before, with a boolean as > "lock", like: > > if not self.vu_locked: > self.vu_locked = 1 > self.vubuffer += vubuffer > vubuffer = [] > self.vu_locked = 0 > > it seems like the worst case is that both the gui and the child thread pass > the test "if not self.vu_locked" at > the same time which might cause some data to be lost from the vubuffer list; > probably that is something > I could live with. You will be reading and writing the same list from two different threads, which seems like a bad idea. > So now my question: > Does anyone know how a threading.Condition() object is handled internally, so > maybe its methods actually > can be called safely from the gui thread? Take a look at the source, threading is in Python. Kent _______________________________________________ Tutor maillist - Tutor@python.org http://mail.python.org/mailman/listinfo/tutor