[Twisted-Python] PyMedia & Tk - based mp3 sound recorder
![](https://secure.gravatar.com/avatar/0412034499afbb7563aa7c6b2be69fc3.jpg?s=120&d=mm&r=g)
I've tried to write a simple sound recorder for python. Basically, it just reads sound from mic or line in, and encodes it to mp3 on the fly. See http://pymedia.org/tut/src/voice_recorder.py.html for a simple example I've taken the pymedia recorder code from. Works nicely - but no GUI etc. of course. I've added a very simple Tk-base GUI using twisted, using task.LoopingCall to drive the recorder loop that reads chunks data from sound device and encodes them. The problem is that the sound is now garbled - it sounds as if the loop is not fast enough or it's dropping some sound data. Apart from twisted and Tk integration, the code should be essentially the same as the original example that works fine. I''ve tried with different task looping frequencies and even added Psyco optiomization to the code, and all this has made but tiny difference -and my PC is an AMD 2400+ with plenty of memory so I would think speed is not the issue here; the encoding preferences are mono, 22050Hz and 64kbit/s. The source code (about 120 lines in all) is attached - any advice or improvement suggestions would be greatly appreciated. Thanks, Petri import time, sys, urlparse, tkFileDialog import Tkinter as Tk import pymedia.audio.sound as sound import pymedia.audio.acodec as acodec from twisted.internet import task from twisted.internet import reactor, tksupport # constants STOPPED = 0 RECORDING = 1 # input params insrc = "VIA AC'97 Audio (WAVE)" format = sound.AFMT_U16_LE # encoder parameters params = { 'id': acodec.getCodecId("mp3"), 'bitrate': 64000, 'sample_rate': 22050, 'channels': 1 } class Recorder: def __init__(self, params=params): self.task = task.LoopingCall(self.process) self.status = STOPPED self.encoder= acodec.Encoder(params) # determine input source inputid=None for d in sound.getIDevices(): if d['name'] == insrc: inputid = d['id'] if not inputid: raise Exception("Invalid or no input source") self.snd= sound.Input(22050, 1, format, inputid) def process(self): #while 1: # snd.getPosition()<= secs: s= self.snd.getData() if s and len(s): for fr in self.encoder.encode(s): # We definitely should use mux first, but for # simplicity reasons this way it'll work also self.outfile.write(fr) #else: # time.sleep( .001 ) def start(self, file): self.outfile= file self.snd.start() self.task.start(.0000001) def stop(self): # Stop listening the incoming sound from the microphone or line in self.task.stop() self.snd.stop() self.outfile.close() class RecorderGUI: def __init__(self, root): frame = Tk.Frame(root) self.frame=frame self.startB = Tk.Button(frame, padx=20, pady=2, text="Start", command=self.startRecording) self.stopB = Tk.Button(frame, padx=20, pady=2, text="Stop", command=self.stopRecording, state=Tk.DISABLED) self.quitB = Tk.Button(frame, padx=20, pady=2, text="Quit", command=self.quitApplication) self.startB.pack(side=Tk.LEFT) self.stopB.pack(side=Tk.LEFT) self.quitB.pack(side=Tk.RIGHT) frame.pack() def setRecorder(self,recorder): self.recorder = recorder def startRecording(self): file = tkFileDialog.asksaveasfile(title="Enter file to save to") self.stopB['state'] = Tk.NORMAL self.startB['state'] =Tk.DISABLED self.recorder.start(file) def stopRecording(self): self.recorder.stop() self.stopB['state'] = Tk.DISABLED self.startB['state'] = Tk.NORMAL def quitApplication(self): if self.recorder.status == RECORDING: self.recorder.stop() reactor.stop() def run(): import psyco psyco.full() guiroot = Tk.Tk() guiroot.protocol("WM_DELETE_WINDOW", reactor.stop) guiroot.title("Sound recorder") tksupport.install(guiroot) gui=RecorderGUI(guiroot) gui.recorder = Recorder() reactor.run() if __name__ == '__main__': run()
![](https://secure.gravatar.com/avatar/d7875f8cfd8ba9262bfff2bf6f6f9b35.jpg?s=120&d=mm&r=g)
On Mon, 2004-11-29 at 16:55 +0200, Petri Savolainen wrote:
I've added a very simple Tk-base GUI using twisted, using task.LoopingCall to drive the recorder loop that reads chunks data from sound device and encodes them.
LoopingCall has a max resolution dependent on the clock resolution. E.g. on Linux 2.6 LoopingCall is good up to 1ms resolution (0.001 seconds), on 2.4 by default 10ms and 2ms on redhat boxes, but that's about it.
![](https://secure.gravatar.com/avatar/0412034499afbb7563aa7c6b2be69fc3.jpg?s=120&d=mm&r=g)
Itamar Shtull-Trauring wrote:
On Mon, 2004-11-29 at 16:55 +0200, Petri Savolainen wrote:
I've added a very simple Tk-base GUI using twisted, using task.LoopingCall to drive the recorder loop that reads chunks data from sound device and encodes them.
LoopingCall has a max resolution dependent on the clock resolution. E.g. on Linux 2.6 LoopingCall is good up to 1ms resolution (0.001 seconds), on 2.4 by default 10ms and 2ms on redhat boxes, but that's about it.
Good to know - I was wondering what's the maximum. I've tried quite a few different values for the resolution, ranging from 0.05 to about 0.00001 or so, with no significant variation to audio quality. Petri
![](https://secure.gravatar.com/avatar/7ed9784cbb1ba1ef75454034b3a8e6a1.jpg?s=120&d=mm&r=g)
On Mon, 29 Nov 2004 16:55:42 +0200, Petri Savolainen <petri.savolainen@iki.fi> wrote:
I've tried to write a simple sound recorder for python. Basically, it just reads sound from mic or line in, and encodes it to mp3 on the fly. See http://pymedia.org/tut/src/voice_recorder.py.html for a simple example I've taken the pymedia recorder code from. Works nicely - but no GUI etc. of course.
I've added a very simple Tk-base GUI using twisted, using task.LoopingCall to drive the recorder loop that reads chunks data from sound device and encodes them.
The problem is that the sound is now garbled - it sounds as if the loop is not fast enough or it's dropping some sound data. Apart from twisted and Tk integration, the code should be essentially the same as the original example that works fine.
I''ve tried with different task looping frequencies and even added Psyco optiomization to the code, and all this has made but tiny difference -and my PC is an AMD 2400+ with plenty of memory so I would think speed is not the issue here; the encoding preferences are mono, 22050Hz and 64kbit/s.
You may want to look at how Shtoom gets its audio input. It uses Twisted and seems to have audio handling down pretty well. http://www.divmod.org/Home/Projects/Shtoom/ Jp
![](https://secure.gravatar.com/avatar/5a2d56afc1b00fb87dbe5e2387f0072f.jpg?s=120&d=mm&r=g)
On Tuesday 30 November 2004 01:55, Petri Savolainen wrote:
I've tried to write a simple sound recorder for python. Basically, it just reads sound from mic or line in, and encodes it to mp3 on the fly. See http://pymedia.org/tut/src/voice_recorder.py.html for a simple example I've taken the pymedia recorder code from. Works nicely - but no GUI etc. of course.
Have a look at the scripts/shreader.py script in shtoom svn. It collects audio from any number of backend audio devices, and (optionally) records it to a file. It works fine. Anthony
![](https://secure.gravatar.com/avatar/0412034499afbb7563aa7c6b2be69fc3.jpg?s=120&d=mm&r=g)
Thanks for everyone who responded. Embarrassingly, I had forgotten to make sure the file was opened in binary mode...
The problem is that the sound is now garbled - it sounds as if the loop is not fast enough or it's dropping some sound data. Apart from twisted and Tk integration, the code should be essentially the same as the original example that works fine.
def startRecording(self): file = tkFileDialog.asksaveasfile(title="Enter file to save to")
So instead: file = tkFileDialog.asksaveasfile(mode="wb", title="Enter file to save to")
participants (4)
-
Anthony Baxter
-
Itamar Shtull-Trauring
-
Jp Calderone
-
Petri Savolainen