diff options
Diffstat (limited to 'pysamples/grecord.py')
-rw-r--r-- | pysamples/grecord.py | 228 |
1 files changed, 0 insertions, 228 deletions
diff --git a/pysamples/grecord.py b/pysamples/grecord.py deleted file mode 100644 index a28b82c..0000000 --- a/pysamples/grecord.py +++ /dev/null @@ -1,228 +0,0 @@ -#Copyright (c) 2008, Media Modifications Ltd. -#Copyright (c) 2011, Walter Bender - -# This procedure is invoked when the user-definable block on the -# "extras" palette is selected. - -# Usage: Import this code into a Python (user-definable) block; Pass -# it 'start' to start recording; 'stop' to stop recording; 'play' to -# play back your recording; or 'save' to save your recording to the -# Sugar Journal. - - -def myblock(tw, arg): - ''' Record and playback a sound (Sugar only) ''' - import os - import gst - import gobject - gobject.threads_init() - - from TurtleArt.tautils import get_path - from TurtleArt.tagplay import play_audio_from_file - from sugar.datastore import datastore - from sugar import profile - - from gettext import gettext as _ - - class Grecord: - ''' A class for creating a gstreamer session for recording audio. ''' - - def __init__(self, tw): - ''' Set up the stream. We save to a raw .wav file and then - convert the sound to .ogg for saving. ''' - datapath = get_path(tw.parent, 'instance') - self.capture_file = os.path.join(datapath, 'output.wav') - self.save_file = os.path.join(datapath, 'output.ogg') - self._eos_cb = None - - self._can_limit_framerate = False - self._recording = False - - self._audio_transcode_handler = None - self._transcode_id = None - - self._pipeline = gst.Pipeline("Record") - self._create_audiobin() - self._pipeline.add(self._audiobin) - - bus = self._pipeline.get_bus() - bus.add_signal_watch() - bus.connect('message', self._bus_message_handler) - - def _create_audiobin(self): - ''' Assemble all the pieces we need. ''' - src = gst.element_factory_make("alsasrc", "absrc") - - # attempt to use direct access to the 0,0 device, solving - # some A/V sync issues - src.set_property("device", "plughw:0,0") - hwdev_available = src.set_state(gst.STATE_PAUSED) != \ - gst.STATE_CHANGE_FAILURE - src.set_state(gst.STATE_NULL) - if not hwdev_available: - src.set_property("device", "default") - - srccaps = gst.Caps( - "audio/x-raw-int,rate=16000,channels=1,depth=16") - - # guarantee perfect stream, important for A/V sync - rate = gst.element_factory_make("audiorate") - - # without a buffer here, gstreamer struggles at the start - # of the recording and then the A/V sync is bad for the - # whole video (possibly a gstreamer/ALSA bug -- even if it - # gets caught up, it should be able to resync without - # problem) - queue = gst.element_factory_make("queue", "audioqueue") - queue.set_property("leaky", True) # prefer fresh data - queue.set_property("max-size-time", 5000000000) # 5 seconds - queue.set_property("max-size-buffers", 500) - queue.connect("overrun", self._log_queue_overrun) - - enc = gst.element_factory_make("wavenc", "abenc") - - sink = gst.element_factory_make("filesink", "absink") - sink.set_property("location", self.capture_file) - - self._audiobin = gst.Bin("audiobin") - self._audiobin.add(src, rate, queue, enc, sink) - - src.link(rate, srccaps) - gst.element_link_many(rate, queue, enc, sink) - - def _log_queue_overrun(self, queue): - ''' We use a buffer, which may overflow. ''' - cbuffers = queue.get_property("current-level-buffers") - cbytes = queue.get_property("current-level-bytes") - ctime = queue.get_property("current-level-time") - - def is_recording(self): - ''' Are we recording? ''' - return self._recording - - def _get_state(self): - ''' What is the state of our gstreamer pipeline? ''' - return self._pipeline.get_state()[1] - - def start_recording_audio(self): - ''' Start the stream in order to start recording. ''' - if self._get_state() == gst.STATE_PLAYING: - return - self._pipeline.set_state(gst.STATE_PLAYING) - self._recording = True - - def stop_recording_audio(self): - ''' Stop recording and then convert the results into a - .ogg file using a new stream. ''' - self._pipeline.set_state(gst.STATE_NULL) - self._recording = False - - if not os.path.exists(self.capture_file) or \ - os.path.getsize(self.capture_file) <= 0: - return - - # Remove previous transcoding results. - if os.path.exists(self.save_file): - os.remove(self.save_file) - - line = 'filesrc location=' + self.capture_file + \ - ' name=audioFilesrc ! wavparse name=audioWavparse \ -! audioconvert name=audioAudioconvert ! vorbisenc name=audioVorbisenc \ -! oggmux name=audioOggmux ! filesink name=audioFilesink' - audioline = gst.parse_launch(line) - - # vorbis_enc = audioline.get_by_name('audioVorbisenc') - - audioFilesink = audioline.get_by_name('audioFilesink') - audioFilesink.set_property("location", self.save_file) - - audioBus = audioline.get_bus() - audioBus.add_signal_watch() - self._audio_transcode_handler = audioBus.connect( - 'message', self._onMuxedAudioMessageCb, audioline) - self._transcode_id = gobject.timeout_add( - 200, self._transcodeUpdateCb, audioline) - audioline.set_state(gst.STATE_PLAYING) - - def _transcodeUpdateCb(self, pipe): - ''' Where are we in the transcoding process? ''' - position, duration = self._query_position(pipe) - if position != gst.CLOCK_TIME_NONE: - value = position * 100.0 / duration - value = value/100.0 - return True - - def _query_position(self, pipe): - ''' Where are we in the stream? ''' - try: - position, format = pipe.query_position(gst.FORMAT_TIME) - except: - position = gst.CLOCK_TIME_NONE - - try: - duration, format = pipe.query_duration(gst.FORMAT_TIME) - except: - duration = gst.CLOCK_TIME_NONE - - return (position, duration) - - def _onMuxedAudioMessageCb(self, bus, message, pipe): - ''' Clean up at end of stream.''' - if message.type != gst.MESSAGE_EOS: - return True - - gobject.source_remove(self._audio_transcode_handler) - self._audio_transcode_handler = None - gobject.source_remove(self._transcode_id) - self._transcode_id = None - pipe.set_state(gst.STATE_NULL) - pipe.get_bus().remove_signal_watch() - pipe.get_bus().disable_sync_message_emission() - - os.remove(self.capture_file) - return False - - def _bus_message_handler(self, bus, message): - ''' Handle any messages associated with the stream. ''' - t = message.type - if t == gst.MESSAGE_EOS: - if self._eos_cb: - cb = self._eos_cb - self._eos_cb = None - cb() - elif t == gst.MESSAGE_ERROR: - # TODO: if we come out of suspend/resume with errors, then - # get us back up and running... TODO: handle "No space - # left on the resource.gstfilesink.c" err, debug = - # message.parse_error() - pass - - # We store the audio-record stream instance as tw.grecord so that - # we can use it repeatedly. - if not hasattr(tw, 'grecord'): - tw.grecord = Grecord(tw) - - # Sometime we need to parse multiple arguments, e.g., save, savename - save_name = '%s_%s' % (tw.activity.name, _('sound')) - if isinstance(arg, list): - cmd = arg[0].lower() - if len(arg) > 1: - save_name = str(arg[1]) - else: - cmd = arg.lower() - - if cmd == 'start' or cmd == _('start').lower(): - tw.grecord.start_recording_audio() - elif cmd == 'stop' or cmd == _('stop').lower(): - tw.grecord.stop_recording_audio() - elif cmd == 'play' or cmd == _('play').lower(): - play_audio_from_file(tw.lc, tw.grecord.save_file) - elif cmd == 'save' or cmd == _('save').lower(): - if os.path.exists(tw.grecord.save_file) and tw.running_sugar: - dsobject = datastore.create() - dsobject.metadata['title'] = save_name - dsobject.metadata['icon-color'] = profile.get_color().to_string() - dsobject.metadata['mime_type'] = 'audio/ogg' - dsobject.set_file_path(tw.grecord.save_file) - datastore.write(dsobject) - dsobject.destroy() |