]> git.parisson.com Git - timeside.git/commitdiff
make flac encoder streaming, add test for flac, bugfixes on encoders
authoryomguy <yomguy@parisson.com>
Thu, 3 Mar 2011 18:20:12 +0000 (18:20 +0000)
committeryomguy <yomguy@parisson.com>
Thu, 3 Mar 2011 18:20:12 +0000 (18:20 +0000)
timeside/encoder/__init__.py
timeside/encoder/flac.py
timeside/encoder/mp3.py
timeside/encoder/ogg.py
timeside/encoder/wav.py
timeside/tests/api/test_flac.py [new file with mode: 0644]

index 4698489fd39cacc30e413614d35b57fbf0699a91..6eacec5ae4d81b3e98a4a5432de79371d77abc47 100644 (file)
@@ -4,6 +4,7 @@ from core import *
 from ogg import *
 from wav import *
 from mp3 import *
-from m4a import *
+from flac import *
+#from m4a import *
 
 #from timeside.encoder.flac import *
index 07534411dcba9606338d88937cb474e45379d163..0cb6e0337b6159f9826adeec028eb1a55d7d9fcb 100644 (file)
 
 # Author: Guillaume Pellerin <yomguy@parisson.com>
 
-import os
-import string
-import subprocess
-
-from timeside.encoder.core import *
+from timeside.core import Processor, implements, interfacedoc
 from timeside.api import IEncoder
-from tempfile import NamedTemporaryFile
+from numpy import array, frombuffer, getbuffer, float32
 
-class FlacEncoder(EncoderCore):
-    """Defines methods to encode to FLAC"""
+import pygst
+pygst.require('0.10')
+import gst
+import gobject
+gobject.threads_init()
 
+class FlacEncoder(Processor):
+    """ gstreamer-based FLAC encoder """
     implements(IEncoder)
 
-    def __init__(self):
-        self.quality_default = '-5'
-        self.dub2args_dict = {'creator': 'artist',
-                             'relation': 'album'
-                             }
+    def __init__(self, output,  streaming=False):
+        if isinstance(output, basestring):
+            self.filename = output
+        else:
+            self.filename = None
+        self.streaming = streaming
+        
+        if not self.filename and self.streaming:
+            raise Exception('Must give an output')
+
+    @interfacedoc
+    def setup(self, channels=None, samplerate=None, nframes=None):
+        super(FlacEncoder, self).setup(channels, samplerate, nframes)
+        # TODO open file for writing
+        # the output data format we want        
+        pipe = ''' appsrc name=src max-bytes=32768 block=true
+                  ! audioconvert 
+                  ! flacenc
+                  '''
+        if self.filename and self.streaming:
+            pipe += '''
+            ! queue2 name=q0 ! tee name=tee
+            tee. ! queue name=q1 ! appsink name=app sync=false
+            tee. ! queue name=q2 ! filesink location=%s
+            ''' % self.filename
+            
+        elif self.filename :
+            pipe += '! filesink location=%s ' % self.filename
+        else:
+            pipe += '! appsink name=app sync=false '
+            
+        self.pipeline = gst.parse_launch(pipe)
+        # store a pointer to appsrc in our encoder object
+        self.src = self.pipeline.get_by_name('src')
+        # store a pointer to appsink in our encoder object
+        self.app = self.pipeline.get_by_name('app')
+        
+        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
+        self.pipeline.set_state(gst.STATE_PLAYING)
 
     @staticmethod
+    @interfacedoc
     def id():
-        return "flacenc"
+        return "gst_flac_enc"
+
+    @staticmethod
+    @interfacedoc
+    def description():
+        return "FLAC GStreamer based encoder"
 
-    def format(self):
-        return 'FLAC'
+    @staticmethod
+    @interfacedoc
+    def format():
+        return "FLAC"
 
-    def file_extension(self):
-        return 'flac'
+    @staticmethod
+    @interfacedoc
+    def file_extension():
+        return "flac"
 
-    def mime_type(self):
+    @staticmethod
+    @interfacedoc
+    def mime_type():
         return 'audio/x-flac'
 
-    def description(self):
-        return """
-        Free Lossless Audio Codec (FLAC) is a file format for lossless audio
-        data compression.
-        """
-
-    def get_file_info(self):
-        try:
-            file1, file2 = os.popen4('metaflac --list "'+self.dest+'"')
-            info = []
-            for line in file2.readlines():
-                info.append(clean_word(line[:-1]))
-            self.info = info
-            return self.info
-        except:
-            raise IOError('EncoderError: metaflac is not installed or ' + \
-                           'file does not exist.')
-
-    def write_tags(self, file):
-        from mutagen.flac import FLAC
-        media = FLAC(file)
-        for tag in self.metadata:
-            name = tag[0]
-            value = clean_word(tag[1])
-            if name in self.dub2args_dict.keys():
-                name = self.dub2args_dict[name]
-            if name == 'comment':
-                media['DESCRIPTION'] = unicode(value)
-            else:
-                media[name] = unicode(value)
-        try:
-            media.save()
-        except:
-            raise IOError('EncoderError: cannot write tags.')
-
-
-    def get_args(self,options=None):
-        """Get process options and return arguments for the encoder"""
-        args = []
-        if not options is None:
-            self.options = options
-            if not ('verbose' in self.options and self.options['verbose'] != '0'):
-                args.append('-s')
-            if 'flac_quality' in self.options:
-                args.append('-f ' + self.options['flac_quality'])
-            else:
-                args.append('-f ' + self.quality_default)
+    @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 self.streaming:
+            pull = self.app.emit('pull-buffer')
+        if eod: self.src.emit('end-of-stream')
+        if not self.streaming:
+            return frames, eod
         else:
-            args.append('-s -f ' + self.quality_default)
-
-        #for tag in self.metadata.keys():
-            #value = clean_word(self.metadata[tag])
-            #args.append('-c %s="%s"' % (tag, value))
-            #if tag in self.dub2args_dict.keys():
-                #arg = self.dub2args_dict[tag]
-                #args.append('-c %s="%s"' % (arg, value))
-
-        return args
-
-    def process(self, source, metadata, options=None):
-        buffer_size = 0xFFFF
-        self.metadata= metadata
-        self.options = options
-        args = self.get_args()
-        args = ' '.join(args)
-        ext = self.file_extension()
-        temp_file = NamedTemporaryFile()
-        command = 'flac %s - -o %s ' % (args, temp_file.name)
-
-        stream = self.core_process(command, source)
-
-        for __chunk in stream:
-            #temp_file.write(__chunk)
-            #temp_file.flush()
-            pass
-
-        #self.write_tags(temp_file.name)
-
-        while True:
-            __chunk = temp_file.read(buffer_size)
-            if len(__chunk) == 0:
-                break
-            yield __chunk
-
-        temp_file.close()
+            return pull, eod
 
+    def numpy_array_to_gst_buffer(self, frames):
+        """ gstreamer buffer to numpy array conversion """
+        buf = gst.Buffer(getbuffer(frames))
+        return buf
 
index 9bd27f28a63acdf8b6225012d45745727b118f13..6baba1217dfb7573a9e820809f0ce82254b88138 100644 (file)
@@ -58,14 +58,14 @@ class Mp3Encoder(Processor):
         if self.filename and self.streaming:
             pipe += '''
             ! queue2 name=q0 ! tee name=tee
-            tee. ! queue name=q1 ! appsink name=app 
+            tee. ! queue name=q1 ! appsink name=app sync=false
             tee. ! queue name=q2 ! filesink location=%s
             ''' % self.filename
             
         elif self.filename :
-            pipe += '! filesink location=%s' % self.filename
+            pipe += '! filesink location=%s ' % self.filename
         else:
-            pipe += '! appsink name=app'
+            pipe += '! appsink name=app sync=false '
             
         self.pipeline = gst.parse_launch(pipe)
         # store a pointer to appsrc in our encoder object
@@ -118,9 +118,12 @@ class Mp3Encoder(Processor):
         buf = self.numpy_array_to_gst_buffer(frames)
         self.src.emit('push-buffer', buf)
         if self.streaming:
-            frames = self.app.emit('pull-buffer')
+            pull = self.app.emit('pull-buffer')
         if eod: self.src.emit('end-of-stream')
-        return frames, eod
+        if not self.streaming:
+            return frames, eod
+        else:
+            return pull, eod
         
     def numpy_array_to_gst_buffer(self, frames):
         """ gstreamer buffer to numpy array conversion """
index 25d224d330dacc62a81c7b7b615cc371046d1745..1d2bbb6dd03ed52e78512904c6a3133ad74651b5 100644 (file)
@@ -57,14 +57,14 @@ class VorbisEncoder(Processor):
         if self.filename and self.streaming:
             pipe += '''
             ! queue2 name=q0 ! tee name=tee
-            tee. ! queue name=q1 ! appsink name=app 
+            tee. ! queue name=q1 ! appsink name=app sync=false
             tee. ! queue name=q2 ! filesink location=%s
             ''' % self.filename
             
         elif self.filename :
-            pipe += '! filesink location=%s' % self.filename
+            pipe += '! filesink location=%s ' % self.filename
         else:
-            pipe += '! appsink name=app'
+            pipe += '! appsink name=app sync=false '
             
         self.pipeline = gst.parse_launch(pipe)
         # store a pointer to appsrc in our encoder object
@@ -117,9 +117,12 @@ class VorbisEncoder(Processor):
         buf = self.numpy_array_to_gst_buffer(frames)
         self.src.emit('push-buffer', buf)
         if self.streaming:
-            frames = self.app.emit('pull-buffer')
+            pull = self.app.emit('pull-buffer')
         if eod: self.src.emit('end-of-stream')
-        return frames, eod
+        if not self.streaming:
+            return frames, eod
+        else:
+            return pull, eod
 
     def numpy_array_to_gst_buffer(self, frames):
         """ gstreamer buffer to numpy array conversion """
index b49e5b09eb3e3bc0a7d169f737e486024c76587d..1ff5a1b4ba97c43cea78bf3dfd4387e75da4a421 100644 (file)
@@ -35,24 +35,43 @@ class WavEncoder(Processor):
     """ gstreamer-based encoder """
     implements(IEncoder)
 
-    def __init__(self, output):
-        self.file = None
+    def __init__(self, output,  streaming=False):
         if isinstance(output, basestring):
             self.filename = output
         else:
-            raise Exception("Streaming not supported")
+            self.filename = None
+        self.streaming = streaming
+        
+        if not self.filename and self.streaming:
+            raise Exception('Must give an output')
 
     @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
-        self.pipeline = gst.parse_launch(''' appsrc name=src
-            ! audioconvert
-            ! wavenc
-            ! filesink location=%s ''' % self.filename)
-        # store a pointer to appsink in our encoder object
+        pipe = ''' appsrc name=src
+                  ! audioconvert 
+                  ! wavenc
+                  '''
+        if self.filename and self.streaming:
+            pipe += '''
+            ! queue2 name=q0 ! tee name=tee
+            tee. ! queue name=q1 ! appsink name=app sync=false
+            tee. ! queue name=q2 ! filesink location=%s
+            ''' % self.filename
+            
+        elif self.filename :
+            pipe += '! filesink location=%s ' % self.filename
+        else:
+            pipe += '! appsink name=app sync=false '
+            
+        self.pipeline = gst.parse_launch(pipe)
+        # store a pointer to appsrc in our encoder object
         self.src = self.pipeline.get_by_name('src')
+        # store a pointer to appsink in our encoder object
+        self.app = self.pipeline.get_by_name('app')
+        
         srccaps = gst.Caps("""audio/x-raw-float,
             endianness=(int)1234,
             channels=(int)%s,
@@ -62,7 +81,7 @@ class WavEncoder(Processor):
 
         # start pipeline
         self.pipeline.set_state(gst.STATE_PLAYING)
-
+        
     @staticmethod
     @interfacedoc
     def id():
@@ -97,8 +116,13 @@ class WavEncoder(Processor):
     def process(self, frames, eod=False):
         buf = self.numpy_array_to_gst_buffer(frames)
         self.src.emit('push-buffer', buf)
+        if self.streaming:
+            pull = self.app.emit('pull-buffer')
         if eod: self.src.emit('end-of-stream')
-        return frames, eod
+        if not self.streaming:
+            return frames, eod
+        else:
+            return pull, eod
 
     def numpy_array_to_gst_buffer(self, frames):
         """ gstreamer buffer to numpy array conversion """
diff --git a/timeside/tests/api/test_flac.py b/timeside/tests/api/test_flac.py
new file mode 100644 (file)
index 0000000..ee590c4
--- /dev/null
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+from timeside.decoder import *
+from timeside.encoder import *
+import os.path
+
+source = os.path.join(os.path.dirname(__file__), "../samples/sweep.wav")
+dest = os.path.join(os.path.dirname(__file__), "../results/sweep_wav.flac")
+
+decoder  = FileDecoder(source)
+encoder  = FlacEncoder(dest)
+
+(decoder | encoder).run()