Skip to content

Instantly share code, notes, and snippets.

@D3f0
Created December 20, 2016 19:39
Show Gist options
  • Save D3f0/35011bc1e3788011583c54da2b0f4681 to your computer and use it in GitHub Desktop.
Save D3f0/35011bc1e3788011583c54da2b0f4681 to your computer and use it in GitHub Desktop.
Plot metl schema XML using Matplotlib to produce a PNG
#!/usr/bin/env python2
'''
Plots a MELT xml schema with matplotlib
'''
from __future__ import print_function
from lxml import etree
import sys
from datetime import datetime, timedelta
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
# JS like object access for dicts
try:
from bunch import Bunch
except ImportError:
print("please install bunch to run this command")
sys.exit(1)
# Easy argparse alternative
try:
import click
except ImportError:
print("please install click to run this command")
sys.exit(1)
def _dbg(e):
'''Display etree to stdout'''
print(etree.tostring(e, pretty_print=True))
def seconds(secs):
'''Time delta in seconds. Accepts strings'''
if isinstance(secs, basestring):
secs = float(secs)
return timedelta(seconds=secs)
def get_musix_xy(music_prod, base_time=None):
points = []
# Go through music
for flt in music_prod.xpath('./filter'):
gain = flt.find('./property[@name="gain"]')
end = flt.find('./property[@name="end"]')
in_ = float(flt.attrib['in'])
out = float(flt.attrib['out'])
start = (base_time + seconds(in_), float(gain.text))
end = (base_time + seconds(out), float(end.text))
points.append(start)
points.append(end)
# Sort
points.sort(cmp=lambda a, b: cmp(a[0], b[0]))
x = [e[0] for e in points]
y = [e[1] for e in points]
return x, y
def plot_schema(xml_schema):
"""Converts schema into plot"""
music_prod = xml_schema.xpath('//producer[contains(@id,"music")]')[0]
fig = Figure()
ax = fig.add_subplot(111)
# We need a datetime to do timedelta cacls
base_time = datetime(1970, 1, 1)
# Draw palylist
max_out_time = base_time
for i, plst in enumerate(xml_schema.xpath('//playlist')):
data = Bunch(plst.attrib) # Adds Id, autoclose
try:
data.update(plst.find('blank').attrib) # Adds length
except AttributeError:
data.update({'length': 0})
data.update(plst.find('entry').attrib) # Adds in and out
if 'music' in data.id: # lousy comparision
offsetd_time = base_time + seconds(data.length)
music_prod = xml_schema.xpath('//producer[@id="%s"]' % data.producer)[0]
x, y = get_musix_xy(music_prod, base_time=offsetd_time)
ax.plot_date(x, y, '-', label='audio volume')
else:
in_time = base_time + seconds(data.length) + seconds(data['in']) # In resvd
out_time = base_time + seconds(data.length) + seconds(data.out)
x = [in_time - seconds(-0.01), in_time, out_time, out_time + seconds(0.01)]
y = [0, 1, 1, 0]
ax.plot_date(x, y, '-', label=data.id)
if out_time > max_out_time:
max_out_time = out_time
# The following two lines could be improved if there's no space left for
# lables
ax.axis([base_time, max_out_time, 0, 1.1 + 2])
ax.legend()
fig.autofmt_xdate()
#import ipdb; ipdb.set_trace()
canvas = FigureCanvas(fig)
return canvas
@click.command()
@click.argument('paths', nargs=-1)
@click.argument('dst', nargs=1, default='-')
def main(paths, dst):
print(dst)
if not paths and dst:
paths = [dst, ]
dst = 'output.png'
for p in paths:
click.echo("Processing {}".format(p), file=sys.stderr)
with open(p) as fp:
xml = etree.fromstring(fp.read())
canvas = plot_schema(xml)
click.echo("Writing output to {}".format(dst), file=sys.stderr)
canvas.print_png(dst)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment