views:

350

answers:

1

Hello

I have a program that records audio from firewire device (FA-66) with Jack connection. The interface is created with pygtk and the recording with py-jack (http://sourceforge.net/projects/py-jack/). The recording is done in a different thread because the GUI must be used at the same time for viewing results from the audio.

The problem is that when I start the recording thread, the GUI becomes very slow to respond. I have gtk.gdk function start_threads() at the beginning of the main thread. If I've got it right, I don't need threads_enter() and threads_leave(), because the recording doesn't affect the GUI. Please correct if I'm wrong.

The function jack.process() records audio from three microphones. If I replace it, for example, with time.sleep(2), everything works ok.

What is the best way to create threading in this case? Why does the jack.process freeze the GUI? Does it take all the cpu time or something? Samples of my code below:

soundrecorder.py:

...
def start(self):
    Thread(target=self._start).start()

def _start(self):
    while self.eventhandler.record.isSet():
        data = self._jackRecord(self.sample_length)
        self.datahandler.queue.put(data)

def _jackRecord(self, length):
    capture = Numeric.zeros((self.inputs, int(self.sample_rate * length)), 'f')
    output = Numeric.zeros((self.inputs, self.buffer_size), 'f')
    i = 0
    while i < capture.shape[1] - self.buffer_size:
        try:
            jack.process(output, capture[:,i:i+self.buffer_size])
            i += self.buffer_size
        except:
            pass
    return capture

eventhandler.py: recordStart() and recordStop() are simply callback functions that are called when start and stop buttons are pressed.

...
def recordStart(self, widget, data=None):
    if not self.record.isSet():
        self.record.set()
        self.soundrecorder = SoundRecorder(self, self.datahandler)
        self.soundrecorder.connect()
        self.soundrecorder.start()
def recordStop(self, widget, data=None):
    if self.record.isSet():
       self.record.clear()
       del(self.soundrecorder)
+2  A: 

You misunderstand how threads work.

Threads don't help you in this case.

"Then when one sample is recorded, it will be analyzed and the results are shown in the GUI. At the same time the next sample is already being recorded."

WRONG. Threads don't do two things at the same time. In python there's a global lock that prevent two threads from running python code or touching python objects at the same time. And besides that, two things don't ever happen at the same time if you don't have two CPUs or cores. The threading mechanism just switches between them executing a fixed number of instructions of each at a time.

Threads also add a processing, memory and code complexibility overhead for no benefit. Python code using threads run slower and have lower performance than if it was single-threaded. There are only a few exceptions for this rule and your case is not one of them.

You probably want to rewrite your recording loop as a callback and integrate it with the GTK loop (you'll get better performance than using threads).

For that, use a gobject.idle_add with a big priority.

If you want to run two things really at "the same time", using two processors/cores, you want to launch another process. Launch a process to collect data and transmit it via some inter-process communication mechanism to the other process that is analizing and plotting data. multiprocessing module can help you with that.

nosklo
Thanks for your response. I am aware of the fact that things in threads don't happen exactly the same time unless I have two CPUs. I just didn't know any other way of doing this. I'll try that gobject.idle_all method.
@jushie: even with two CPUs you're limited by the python global interpreter lock. Threads just aren't useful for that. Go either single threaded approach (gobject.idle_add, gobject.timeout_add, gobject.io_add_watch, or use twisted) or the multiprocess approach.
nosklo