--- /dev/null
+
+
+default_color_schemes = {
+ 'default': {
+ 'waveform': [(50,0,200), (0,220,80), (255,224,0), (255,0,0)],
+ 'spectrogram': [(0, 0, 0), (58/4,68/4,65/4), (80/2,100/2,153/2), (90,180,100),
+ (224,224,44), (255,60,30), (255,255,255)]
+ },
+ 'iso': {
+ 'waveform': [(0,0,255), (0,255,255), (255,255,0), (255,0,0)],
+ 'spectrogram': [(0, 0, 0), (58/4,68/4,65/4), (80/2,100/2,153/2), (90,180,100),
+ (224,224,44), (255,60,30), (255,255,255)]
+ },
+ 'purple': {
+ 'waveform': [(173,173,173), (147,149,196), (77,80,138), (108,66,0)],
+ 'spectrogram': [(0, 0, 0), (58/4,68/4,65/4), (80/2,100/2,153/2), (90,180,100),
+ (224,224,44), (255,60,30), (255,255,255)]
+ },
+ 'awdio': {
+ 'waveform': [(255,255,255), (255,255,255), (255,255,255), (255,255,255)],
+ 'spectrogram': [(0, 0, 0), (58/4,68/4,65/4), (80/2,100/2,153/2), (90,180,100),
+ (224,224,44), (255,60,30), (255,255,255)]
+ },
+}
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2009-2013 Parisson SARL
+# Copyright (c) 2009-2012 Guillaume Pellerin <pellerin@parisson.com>
+# Copyright (C) 2008 MUSIC TECHNOLOGY GROUP (MTG)
+# UNIVERSITAT POMPEU FABRA
+
+# 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/>.
+
+
+# Authors:
+# Bram de Jong <bram.dejong at domain.com where domain in gmail>
+# Guillaume Pellerin <yomguy@parisson.com>
+
+try:
+ from PIL import ImageFilter, ImageChops, Image, ImageDraw, ImageColor, ImageEnhance
+except ImportError:
+ import ImageFilter, ImageChops, Image, ImageDraw, ImageColor, ImageEnhance
+
+import numpy
+
+
+def interpolate_colors(colors, flat=False, num_colors=256):
+ """ Given a list of colors, create a larger list of colors interpolating
+ the first one. If flatten is True a list of numers will be returned. If
+ False, a list of (r,g,b) tuples. num_colors is the number of colors wanted
+ in the final list """
+
+ palette = []
+
+ for i in range(num_colors):
+ index = (i * (len(colors) - 1))/(num_colors - 1.0)
+ index_int = int(index)
+ alpha = index - float(index_int)
+
+ if alpha > 0:
+ r = (1.0 - alpha) * colors[index_int][0] + alpha * colors[index_int + 1][0]
+ g = (1.0 - alpha) * colors[index_int][1] + alpha * colors[index_int + 1][1]
+ b = (1.0 - alpha) * colors[index_int][2] + alpha * colors[index_int + 1][2]
+ else:
+ r = (1.0 - alpha) * colors[index_int][0]
+ g = (1.0 - alpha) * colors[index_int][1]
+ b = (1.0 - alpha) * colors[index_int][2]
+
+ if flat:
+ palette.extend((int(r), int(g), int(b)))
+ else:
+ palette.append((int(r), int(g), int(b)))
+
+ return palette
+
+
+class Noise(object):
+ """A class that mimics audiolab.sndfile but generates noise instead of reading
+ a wave file. Additionally it can be told to have a "broken" header and thus crashing
+ in the middle of the file. Also useful for testing ultra-short files of 20 samples."""
+
+ def __init__(self, num_frames, has_broken_header=False):
+ self.seekpoint = 0
+ self.num_frames = num_frames
+ self.has_broken_header = has_broken_header
+
+ def seek(self, seekpoint):
+ self.seekpoint = seekpoint
+
+ def get_nframes(self):
+ return self.num_frames
+
+ def get_samplerate(self):
+ return 44100
+
+ def get_channels(self):
+ return 1
+
+ def read_frames(self, frames_to_read):
+ if self.has_broken_header and self.seekpoint + frames_to_read > self.num_frames / 2:
+ raise IOError()
+
+ num_frames_left = self.num_frames - self.seekpoint
+ if num_frames_left < frames_to_read:
+ will_read = num_frames_left
+ else:
+ will_read = frames_to_read
+ self.seekpoint += will_read
+ return numpy.random.random(will_read)*2 - 1
+
+
+def downsample(vector, factor):
+ """
+ downsample(vector, factor):
+ Downsample (by averaging) a vector by an integer factor.
+ """
+ if (len(vector) % factor):
+ print "Length of 'vector' is not divisible by 'factor'=%d!" % factor
+ return 0
+ vector.shape = (len(vector)/factor, factor)
+ return numpy.mean(vector, axis=1)
+
+
+def smooth(x, window_len=10, window='hanning'):
+ """
+ Smooth the data using a window with requested size.
+
+ This method is based on the convolution of a scaled window with the signal.
+ The signal is prepared by introducing reflected copies of the signal
+ (with the window size) in both ends so that transient parts are minimized
+ in the begining and end part of the output signal.
+
+ Parameters
+ ----------
+ x : numpy.array
+ the input signal
+ window_len : int
+ the dimension of the smoothing window
+ window : str
+ the type of window from 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'
+ flat window will produce a moving average smoothing.
+
+ Returns
+ -------
+ The smoothed signal
+
+ See Also
+ -------
+
+ numpy.hanning, numpy.hamming, numpy.bartlett, numpy.blackman, numpy.convolve
+ scipy.signal.lfilter
+
+
+ Examples
+ --------
+
+ >>> import numpy as np
+ >>> from timeside.grapher import smooth
+ >>> t = np.arange(-2,2,0.1)
+ >>> x = np.sin(t)+np.random.randn(len(t))*0.1
+ >>> y = smooth(x)
+ >>> import matplotlib.pyplot as plt
+ >>> plt.plot(x) # doctest: +ELLIPSIS
+ [<matplotlib.lines.Line2D object at 0x...>]
+ >>> plt.plot(y) # doctest: +ELLIPSIS
+ [<matplotlib.lines.Line2D object at 0x...>]
+ >>> plt.legend(['Source signal', 'Smoothed signal']) # doctest: +ELLIPSIS
+ <matplotlib.legend.Legend object at 0x...>
+ >>> plt.show() # doctest: +SKIP
+ """
+
+ # TODO: the window parameter could be the window itself if an array instead of a string
+
+
+ if x.ndim != 1:
+ raise ValueError, "smooth only accepts 1 dimension arrays."
+
+ if x.size < window_len:
+ raise ValueError, "Input vector needs to be bigger than window size."
+
+ if window_len < 3:
+ return x
+
+ if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
+ raise ValueError, "Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'"
+
+ s=numpy.r_[2*x[0]-x[window_len:1:-1], x, 2*x[-1]-x[-1:-window_len:-1]]
+
+ if window == 'flat': #moving average
+ w = numpy.ones(window_len,'d')
+ else:
+ w = getattr(numpy, window)(window_len)
+
+ y = numpy.convolve(w/w.sum(), s, mode='same')
+ return y[window_len-1:-window_len+1]
+
+
+def reduce_opacity(im, opacity):
+ """Returns an image with reduced opacity."""
+ assert opacity >= 0 and opacity <= 1
+ if im.mode != 'RGBA':
+ im = im.convert('RGBA')
+ else:
+ im = im.copy()
+ alpha = im.split()[3]
+ alpha = ImageEnhance.Brightness(alpha).enhance(opacity)
+ im.putalpha(alpha)
+ return im
+
+
+def im_watermark(im, inputtext, font=None, color=None, opacity=.6, margin=(30,30)):
+ """
+ imprints a PIL image with the indicated text in lower-right corner
+ """
+ if im.mode != "RGBA":
+ im = im.convert("RGBA")
+ textlayer = Image.new("RGBA", im.size, (0,0,0,0))
+ textdraw = ImageDraw.Draw(textlayer)
+ textsize = textdraw.textsize(inputtext, font=font)
+ textpos = [im.size[i]-textsize[i]-margin[i] for i in [0,1]]
+ textdraw.text(textpos, inputtext, font=font, fill=color)
+ if opacity != 1:
+ textlayer = reduce_opacity(textlayer,opacity)
+ return Image.composite(textlayer, im, textlayer)