--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007-2009 Parisson
+# Copyright (c) 2007 Olivier Guilyardi <olivier@samalyse.com>
+# Copyright (c) 2007-2009 Guillaume Pellerin <pellerin@parisson.com>
+#
+# This file is part of TimeSide.
+
+# TimeSide is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+
+# TimeSide is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with TimeSide. If not, see <http://www.gnu.org/licenses/>.
+
+# Author: Paul Brossier <piem@piem.org>
+
+from timeside.core import Processor, implements, interfacedoc
+from timeside.api import IDecoder, IEncoder
+from numpy import array, frombuffer, getbuffer, float32
+
+import pygst
+pygst.require('0.10')
+import gst
+import gobject
+gobject.threads_init ()
+
+class FileDecoder(Processor):
+ """ gstreamer-based decoder """
+ implements(IDecoder)
+
+ # duration ms, before discovery process times out
+ MAX_DISCOVERY_TIME = 3000
+
+ audioformat = None
+ audiochannels = None
+ audiorate = None
+ audionframes = None
+ mimetype = ''
+
+ # IProcessor methods
+
+ @staticmethod
+ @interfacedoc
+ def id():
+ return "test_gstreamerdec"
+
+ def setup(self, channels = None, samplerate = None, nframes = None):
+ # the output data format we want
+ caps = "audio/x-raw-float, width=32"
+ pipeline = gst.parse_launch('''uridecodebin uri=%s
+ ! audioconvert
+ ! %s
+ ! appsink name=sink sync=False ''' % (self.uri, caps))
+ # store a pointer to appsink in our decoder object
+ self.sink = pipeline.get_by_name('sink')
+ # adjust length of emitted buffers
+ # self.sink.set_property('blocksize', 0x10000)
+ # start pipeline
+ pipeline.set_state(gst.STATE_PLAYING)
+
+ @interfacedoc
+ def channels(self):
+ return self.audiochannels
+
+ @interfacedoc
+ def samplerate(self):
+ return self.audiorate
+
+ @interfacedoc
+ def nframes(self):
+ return self.audionframes
+
+ @interfacedoc
+ def process(self, frames = None, eod = False):
+ try:
+ buf = self.sink.emit('pull-buffer')
+ except SystemError, e:
+ # should never happen
+ print 'SystemError', e
+ return array([0.]), True
+ if buf == None:
+ return array([0.]), True
+ return self.gst_buffer_to_numpy_array(buf), False
+
+ @interfacedoc
+ def release(self):
+ # nothing to do for now
+ pass
+
+ ## IDecoder methods
+
+ @interfacedoc
+ def __init__(self, uri):
+
+ # is this a file?
+ import os.path
+ if os.path.exists(uri):
+ # get the absolute path
+ uri = os.path.abspath(uri)
+ # first run the file/uri through the discover pipeline
+ self.discover(uri)
+ # and make a uri of it
+ from urllib import quote
+ self.uri = 'file://'+quote(uri)
+
+ @interfacedoc
+ def format(self):
+ # TODO check
+ return self.mimetype
+
+ @interfacedoc
+ def encoding(self):
+ # TODO check
+ return self.mimetype.split('/')[-1]
+
+ @interfacedoc
+ def resolution(self):
+ # TODO check: width or depth?
+ return self.audiowidth
+
+ @interfacedoc
+ def metadata(self):
+ # TODO check
+ return self.tags
+
+ ## gst.extend discoverer
+
+ def discover(self, path):
+ """ gstreamer based helper function to get file attributes """
+ from gst.extend import discoverer
+ d = discoverer.Discoverer(path, timeout = self.MAX_DISCOVERY_TIME)
+ d.connect('discovered', self.discovered)
+ self.mainloop = gobject.MainLoop()
+ d.discover()
+ self.mainloop.run()
+
+ def discovered(self, d, is_media):
+ """ gstreamer based helper executed upon discover() completion """
+ if is_media and d.is_audio:
+ # copy the discoverer attributes to self
+ self.audiorate = d.audiorate
+ self.mimetype= d.mimetype
+ self.audiochannels = d.audiochannels
+ self.audiowidth = d.audiowidth
+ # conversion from time in nanoseconds to frames
+ from math import ceil
+ duration = d.audiorate * d.audiolength * 1.e-9
+ self.audionframes = int (ceil ( duration ) )
+ self.tags = d.tags
+ elif not d.is_audio:
+ print "error, no audio found!"
+ else:
+ print "fail", path
+ self.mainloop.quit()
+
+ def gst_buffer_to_numpy_array(self, buf, chan = 1):
+ """ gstreamer buffer to numpy array conversion """
+ samples = frombuffer(buf.data, dtype=float32)
+ samples.resize([len(samples)/chan, chan])
+ return samples
+
+class WavEncoder(Processor):
+ """ gstreamer-based encoder """
+ implements(IEncoder)
+
+ def __init__(self, output):
+ self.file = None
+ if isinstance(output, basestring):
+ self.filename = output
+ else:
+ raise Exception("Streaming not supported")
+
+ @interfacedoc
+ def setup(self, channels=None, samplerate=None, nframes=None):
+ super(WavEncoder, self).setup(channels, samplerate, nframes)
+ # TODO open file for writing
+ # the output data format we want
+ pipeline = gst.parse_launch(''' appsrc name=src
+ ! audioconvert
+ ! wavenc
+ ! filesink location=%s ''' % self.filename)
+ # store a pointer to appsink in our encoder object
+ self.src = pipeline.get_by_name('src')
+ srccaps = gst.Caps("""audio/x-raw-float,
+ endianness=(int)1234,
+ channels=(int)%s,
+ width=(int)32,
+ rate=(int)%d""" % (int(channels), int(samplerate)))
+ self.src.set_property("caps", srccaps)
+
+ # start pipeline
+ pipeline.set_state(gst.STATE_PLAYING)
+ self.pipeline = pipeline
+
+ @staticmethod
+ @interfacedoc
+ def id():
+ return "test_gstreamerenc"
+
+ @staticmethod
+ @interfacedoc
+ def description():
+ return "Gstreamer based encoder"
+
+ @staticmethod
+ @interfacedoc
+ def file_extension():
+ return "wav"
+
+ @staticmethod
+ @interfacedoc
+ def mime_type():
+ return "audio/x-wav"
+
+ @interfacedoc
+ def set_metadata(self, metadata):
+ #TODO
+ pass
+
+ @interfacedoc
+ def process(self, frames, eod=False):
+ buf = self.numpy_array_to_gst_buffer(frames)
+ self.src.emit('push-buffer', buf)
+ if eod: self.src.emit('end-of-stream')
+ return frames, eod
+
+ def numpy_array_to_gst_buffer(self, frames):
+ """ gstreamer buffer to numpy array conversion """
+ buf = gst.Buffer(getbuffer(frames))
+ return buf