@staticmethod
def id():
- """Short alphanumeric, lower-case string which uniquely identify this
- processor, suitable for use as an HTTP/GET argument value, in filenames,
+ """Short alphanumeric, lower-case string which uniquely identify this
+ processor, suitable for use as an HTTP/GET argument value, in filenames,
etc..."""
# implementation: only letters and digits are allowed. An exception will
# be raised by MetaProcessor if the id is malformed or not unique amongst
# registered processors.
- def setup(self, channels=None, samplerate=None, blocksize=None):
+ def setup(self, channels=None, samplerate=None, blocksize=None, totalframes=None):
"""Allocate internal resources and reset state, so that this processor is
- ready for a new run.
-
- The channels, samplerate and/or blocksize arguments may be required by
- processors which accept input. An error will occur if any of
+ ready for a new run.
+
+ The channels, samplerate and/or blocksize and/or totalframes arguments
+ may be required by processors which accept input. An error will occur if any of
these arguments is passed to an output-only processor such as a decoder.
"""
the samplerate passed to setup()"""
def blocksize():
+ """The total number of frames that this processor can output for each step
+ in the pipeline, or None if the duration is unknown."""
+
+ def totalframes():
"""The total number of frames that this processor can output, or None if
the duration is unknown."""
def process(self, frames=None, eod=False):
"""Process input frames and return a (output_frames, eod) tuple.
- Both input and output frames are 2D numpy arrays, where columns are
- channels, and containing an undetermined number of frames. eod=True
+ Both input and output frames are 2D numpy arrays, where columns are
+ channels, and containing an undetermined number of frames. eod=True
means that the end-of-data has been reached.
-
+
Output-only processors (such as decoders) will raise an exception if the
frames argument is not None. All processors (even encoders) return data,
even if that means returning the input unchanged.
-
+
Warning: it is required to call setup() before this method."""
def release(self):
format."""
def __init__(self, output):
- """Create a new encoder. output can either be a filename or a python callback
+ """Create a new encoder. output can either be a filename or a python callback
function/method for streaming mode.
The streaming callback prototype is: callback(data, eod)
Where data is a block of binary data of an undetermined size, and eod
True when end-of-data is reached."""
- # implementation: the constructor must always accept the output argument. It may
- # accept extra arguments such as bitrate, depth, etc.., but these must be optionnal
+ # implementation: the constructor must always accept the output argument. It may
+ # accept extra arguments such as bitrate, depth, etc.., but these must be optionnal
@staticmethod
def format():
def set_metadata(self, metadata):
"""Set the metadata to be embedded in the encoded output.
-
- In non-streaming mode, this method updates the metadata directly into the
- output file, without re-encoding the audio data, provided this file already
+
+ In non-streaming mode, this method updates the metadata directly into the
+ output file, without re-encoding the audio data, provided this file already
exists.
-
- It isn't required to call this method, but if called, it must be before
+
+ It isn't required to call this method, but if called, it must be before
process()."""
class IDecoder(IProcessor):
def __init__(self, filename):
"""Create a new decoder for filename."""
- # implementation: additional optionnal arguments are allowed
+ # implementation: additional optionnal arguments are allowed
def format():
"""Return a user-friendly file format string"""
-
+
def encoding():
"""Return a user-friendly encoding string"""
"""Create a new grapher. width and height are generally
in pixels but could be something else for eg. svg rendering, etc.. """
- # implementation: additional optionnal arguments are allowed
+ # implementation: additional optionnal arguments are allowed
@staticmethod
def name():
def __init__(self):
"""Create a new analyzer."""
- # implementation: additional optionnal arguments are allowed
+ # implementation: additional optionnal arguments are allowed
@staticmethod
def name():
repeatedly calling process()"""
def __str__(self):
- """Return a human readable string containing both result and unit
+ """Return a human readable string containing both result and unit
('5.30dB', '4.2s', etc...)"""
class IEffect(IProcessor):
def __init__(self):
"""Create a new effect."""
- # implementation: additional optionnal arguments are allowed
+ # implementation: additional optionnal arguments are allowed
@staticmethod
def name():
implements(IProcessor)
@interfacedoc
- def setup(self, channels=None, samplerate=None, blocksize=None):
- self.input_channels = channels
- self.input_samplerate = samplerate
- self.input_blocksize = blocksize
+ def setup(self, channels=None, samplerate=None, blocksize=None, totalframes=None):
+ self.input_channels = channels
+ self.input_samplerate = samplerate
+ self.input_blocksize = blocksize
+ self.input_totalframes = totalframes
# default channels(), samplerate() and blocksize() implementations returns
# the input characteristics, but processors may change this behaviour by
def blocksize(self):
return self.input_blocksize
+ @interfacedoc
+ def totalframes(self):
+ return self.input_totalframes
+
@interfacedoc
def process(self, frames, eod):
return frames, eod
return blocksize
+ def totalframes(self, input_totalframes):
+ """Return the total number of frames that this adapter will output according to the
+ input_blocksize argument"""
+
+ totalframes = input_totalframes
+ if self.pad:
+ mod = input_totalframes % self.buffer_size
+ if mod:
+ totalframes += self.buffer_size - mod
+
+ return totalframes
+
def process(self, frames, eod):
"""Returns an iterator over tuples of the form (buffer, eod) where buffer is a
fixed-sized block of data, and eod indicates whether this is the last block.
source.setup()
last = source
for item in items:
- item.setup(last.channels(), last.samplerate(), last.blocksize())
+ item.setup(last.channels(), last.samplerate(), last.blocksize(), last.totalframes())
last = item
# now stream audio data along the pipe
def setup(self, channels = None, samplerate = None, blocksize = None):
# the output data format we want
- if blocksize: self.output_blocksize = blocksize
- if samplerate: self.output_samplerate = int(samplerate)
- if channels: self.output_channels = int(channels)
+ if blocksize: self.output_blocksize = blocksize
+ if samplerate: self.output_samplerate = int(samplerate)
+ if channels: self.output_channels = int(channels)
+
uri = self.uri
self.pipe = ''' uridecodebin name=uridecodebin uri=%(uri)s
self.input_width = caps[0]["width"]
else:
self.input_width = caps[0]["depth"]
+ self.output_totalframes = self.totalframes()
def _on_message_cb(self, bus, message):
t = message.type
def blocksize(self):
return self.output_blocksize
+ @interfacedoc
def totalframes(self):
if self.input_samplerate == self.output_samplerate:
return self.input_totalframes
Adds pixels iteratively thanks to the adapter providing fixed size frame buffers.
Peaks are colored relative to the spectral centroids of each frame packet. """
- def __init__(self, image_width, image_height, nframes, samplerate, fft_size, bg_color, color_scheme):
+ def __init__(self, image_width, image_height, totalframes, samplerate, fft_size, bg_color, color_scheme):
self.image_width = image_width
self.image_height = image_height
- self.nframes = nframes
+ self.nframes = totalframes
self.samplerate = samplerate
self.fft_size = fft_size
self.bg_color = bg_color
self.samples_per_pixel = self.nframes / float(self.image_width)
self.buffer_size = int(round(self.samples_per_pixel, 0))
self.pixels_adapter = FixedSizeInputAdapter(self.buffer_size, 1, pad=False)
- self.pixels_adapter_nframes = self.pixels_adapter.nframes(self.nframes)
+ self.pixels_adapter_nframes = self.pixels_adapter.totalframes(self.nframes)
self.lower = 800
self.higher = 12000
self.color_scheme = scheme
@interfacedoc
- def setup(self, channels=None, samplerate=None, nframes=None):
- super(Waveform, self).setup(channels, samplerate, nframes)
- self.graph = WaveformImage(self.width, self.height, self.nframes(), self.samplerate(), self.FFT_SIZE,
- bg_color=self.bg_color, color_scheme=self.color_scheme)
+ def setup(self, channels=None, samplerate=None, blocksize=None, totalframes=None):
+ super(Waveform, self).setup(channels, samplerate, blocksize, totalframes)
+ self.graph = WaveformImage(self.width, self.height, self.totalframes(),
+ self.samplerate(), self.FFT_SIZE,
+ bg_color=self.bg_color,
+ color_scheme=self.color_scheme)
@interfacedoc
def process(self, frames, eod=False):
self.graph.process(frames, eod)
return frames, eod
-
+
@interfacedoc
def render(self, output=None):
if output:
self.graph.save(output)
return self.graph.image
-
+
def watermark(self, text, font=None, color=(255, 255, 255), opacity=.6, margin=(5,5)):
self.graph.watermark(text, color=color, opacity=opacity, margin=margin)