-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of github.com:kevinmcdonagh/mashbox
- Loading branch information
Showing
14 changed files
with
828 additions
and
12 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
<html> | ||
<head> | ||
<?php | ||
$dbh = new PDO('sqlite:mashbox.sqlite'); | ||
foreach($dbh->query('SELECT * FROM tracks') as $row){ | ||
echo ($row['artist'] . $row['title'] . $row['genre'] . "\n"); | ||
} | ||
$dbh = null; | ||
?> | ||
</head> | ||
</html> |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
S |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
This is a munged-up version of the capsule example code from echonest-remix, ploughed together for London Music Hack Day 2010. Copyright still belongs to EchoNest. | ||
|
||
To use, you will need the echonest-remix package, and a valid license key. | ||
|
||
You will need to clobber action.py in the src/echonest directory before building. As far as I can tell, this code should have been there all along- I need to have a chat to the EchoNest dudes tomorrow. | ||
|
||
Done all that? run: | ||
|
||
python file1.mp3 file2.mp3 --init=0 --trans=60 | ||
|
||
you get a file out called capsule.mp3 | ||
|
||
It's 3.40am, be understanding. | ||
|
||
-Alex | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
# encoding: utf-8 | ||
""" | ||
action.py | ||
Created by Tristan Jehan and Jason Sundram. | ||
""" | ||
import os | ||
from numpy import zeros, multiply, float32 | ||
from math import atan, pi | ||
import sys | ||
|
||
from echonest.audio import assemble, AudioData | ||
from cAction import limit, crossfade, fadein, fadeout | ||
|
||
import dirac | ||
|
||
def rows(m): | ||
"""returns the # of rows in a numpy matrix""" | ||
return m.shape[0] | ||
|
||
def make_mono(track): | ||
"Converts stereo tracks to mono; leaves mono tracks alone." | ||
if track.data.ndim == 2: | ||
mono = zeros((len(track.data), )) | ||
for i, d in enumerate(track.data): | ||
mono[i] = d[0] | ||
track.data = mono | ||
return track | ||
|
||
def make_stereo(track): | ||
"""If the track is mono, doubles it. otherwise, does nothing.""" | ||
if track.data.ndim == 1: | ||
stereo = zeros((len(track.data), 2)) | ||
for i, d in enumerate(track.data): | ||
stereo[i] = (d, d) | ||
track.data = stereo | ||
return track | ||
|
||
def render(actions, filename): | ||
"""Calls render on each action in actions, concatenates the results, renders an audio file, and returns a path to the file""" | ||
pieces = [a.render() for a in actions] | ||
out = assemble(pieces, numChannels=2, sampleRate=44100) # TODO: make these vary. | ||
return out, out.encode(filename) | ||
|
||
|
||
class Playback(object): | ||
"""A snippet of the given track with start and duration. Volume leveling may be applied.""" | ||
def __init__(self, track, start, duration): | ||
self.track = track | ||
self.start = float(start) | ||
self.duration = float(duration) | ||
|
||
def render(self): | ||
# self has start and duration, so it is a valid index into track. | ||
output = self.track[self] | ||
# Normalize volume if necessary | ||
gain = getattr(self.track, 'gain', None) | ||
if gain != None: | ||
# limit expects a float32 vector | ||
output.data = limit(multiply(output.data, float32(gain))) | ||
|
||
return output | ||
|
||
def __repr__(self): | ||
return "<Playback '%s'>" % self.track.analysis.name | ||
|
||
def __str__(self): | ||
return "Playback\t%.3f\t-> %.3f\t (%.3f)\t%s" % (self.start, self.start+self.duration, self.duration, self.track.analysis.name) | ||
|
||
|
||
class Fadeout(Playback): | ||
"""Fadeout""" | ||
def render(self): | ||
gain = getattr(self.track, 'gain', 1.0) | ||
output = self.track[self] | ||
# second parameter is optional -- in place function for now | ||
output.data = fadeout(output.data, gain) | ||
return output | ||
|
||
def __repr__(self): | ||
return "<Fadeout '%s'>" % self.track.analysis.name | ||
|
||
def __str__(self): | ||
return "Fade out\t%.3f\t-> %.3f\t (%.3f)\t%s" % (self.start, self.start+self.duration, self.duration, self.track.analysis.name) | ||
|
||
|
||
class Fadein(Playback): | ||
"""Fadein""" | ||
def render(self): | ||
gain = getattr(self.track, 'gain', 1.0) | ||
output = self.track[self] | ||
# second parameter is optional -- in place function for now | ||
output.data = fadein(output.data, gain) | ||
return output | ||
|
||
def __repr__(self): | ||
return "<Fadein '%s'>" % self.track.analysis.name | ||
|
||
def __str__(self): | ||
return "Fade in\t%.3f\t-> %.3f\t (%.3f)\t%s" % (self.start, self.start+self.duration, self.duration, self.track.analysis.name) | ||
|
||
|
||
class Edit(object): | ||
"""Refer to a snippet of audio""" | ||
def __init__(self, track, start, duration): | ||
self.track = track | ||
self.start = float(start) | ||
self.duration = float(duration) | ||
|
||
def __str__(self): | ||
return "Edit\t%.3f\t-> %.3f\t (%.3f)\t%s" % (self.start, self.start+self.duration, self.duration, self.track.analysis.name) | ||
|
||
def get(self): | ||
return self.track[self] | ||
|
||
@property | ||
def end(self): | ||
return self.start + self.duration | ||
|
||
|
||
class Crossfade(object): | ||
"""Crossfades between two tracks, at the start points specified, for the given duration""" | ||
def __init__(self, tracks, starts, duration, mode='linear'): | ||
self.t1, self.t2 = [Edit(t, s, duration) for t,s in zip(tracks, starts)] | ||
self.duration = self.t1.duration | ||
self.mode = mode | ||
|
||
def render(self): | ||
t1, t2 = map(make_stereo, (self.t1.get(), self.t2.get())) | ||
vecout = crossfade(t1.data, t2.data, self.mode) | ||
return AudioData(ndarray=vecout, shape=vecout.shape, sampleRate=t1.sampleRate, numChannels=vecout.shape[1]) | ||
|
||
def __repr__(self): | ||
return "<Crossfade '%s' and '%s'>" % (self.t1.track.analysis.name, self.t2.track.analysis.name) | ||
|
||
def __str__(self): | ||
return "Crossfade\t%.3f\t-> %.3f\t (%.3f)\t%s -> %s" % (self.t1.start, self.t2.start+self.duration, self.duration, | ||
self.t1.track.analysis.name, self.t2.track.analysis.name) | ||
|
||
|
||
class Jump(Crossfade): | ||
"""Move from one point """ | ||
def __init__(self, track, source, target, duration): | ||
self.track = track | ||
self.t1, self.t2 = (Edit(track, source, duration), Edit(track, target-duration, duration)) | ||
self.duration = float(duration) | ||
self.mode = 'equal_power' | ||
self.CROSSFADE_COEFF = 0.6 | ||
|
||
@property | ||
def source(self): | ||
return self.t1.start | ||
|
||
@property | ||
def target(self): | ||
return self.t2.end | ||
|
||
def __repr__(self): | ||
return "<Jump '%s'>" % (self.t1.track.analysis.name) | ||
|
||
def __str__(self): | ||
return "Jump\t\t%.3f\t-> %.3f\t (%.3f)\t%s" % (self.t1.start, self.t2.end, self.duration, self.t1.track.analysis.name) | ||
|
||
|
||
class Blend(object): | ||
"""Mix together two lists of beats""" | ||
def __init__(self, tracks, lists): | ||
self.t1, self.t2 = tracks | ||
self.l1, self.l2 = lists | ||
assert(len(self.l1) == len(self.l2)) | ||
|
||
self.calculate_durations() | ||
|
||
def calculate_durations(self): | ||
self.durations = [(d1 + d2) / 2.0 for ((s1, d1), (s2, d2)) in zip(self.l1, self.l2)] | ||
self.duration = sum(self.durations) | ||
|
||
def stretch(self, t, l): | ||
"""t is a track, l is a list""" | ||
signal_start = int(l[0][0] * t.sampleRate) | ||
signal_duration = int((sum(l[-1]) - l[0][0]) * t.sampleRate) | ||
vecin = t.data[signal_start:signal_start + signal_duration,:] | ||
|
||
rates = [] | ||
for i in xrange(len(l)): | ||
rates.append((int(l[i][0] * t.sampleRate) - signal_start, self.durations[i] / l[i][1])) | ||
|
||
vecout = dirac.timeScale(vecin, rates, t.sampleRate, 0) | ||
if hasattr(t, 'gain'): | ||
vecout = limit(multiply(vecout, float32(t.gain))) | ||
|
||
return AudioData(ndarray=vecout, shape=vecout.shape, sampleRate=t.sampleRate, numChannels=vecout.shape[1]) | ||
|
||
def render(self): | ||
out1 = self.stretch(self.t1, self.l1) | ||
out2 = self.stretch(self.t2, self.l2) | ||
return mix(out1,out2) | ||
|
||
def __repr__(self): | ||
return "<Blend '%s' and '%s'>" % (self.t1.analysis.name, self.t2.analysis.name) | ||
|
||
def __str__(self): | ||
s1, e1 = self.l1[0][0], sum(self.l1[-1]) # start and end for each of these lists. | ||
s2, e2 = self.l2[0][0], sum(self.l2[-1]) | ||
n1, n2 = self.t1.analysis.name, self.t2.analysis.name # names | ||
return "Blend [%.3f, %.3f] -> [%.3f, %.3f] (%.3f)\t%s + %s" % (s1, s2, e1, e2, self.duration, n1, n2) | ||
|
||
|
||
class Crossmatch(Blend): | ||
"""Makes a beat-matched crossfade between the two input tracks.""" | ||
def calculate_durations(self): | ||
c, dec = 1.0, 1.0 / float(len(self.l1)+1) | ||
self.durations = [] | ||
for ((s1, d1), (s2, d2)) in zip(self.l1, self.l2): | ||
c -= dec | ||
self.durations.append(c * d1 + (1 - c) * d2) | ||
self.duration = sum(self.durations) | ||
|
||
def stretch(self, t, l): | ||
"""t is a track, l is a list""" | ||
signal_start = int(l[0][0] * t.sampleRate) | ||
signal_duration = int((sum(l[-1]) - l[0][0]) * t.sampleRate) | ||
vecin = t.data[signal_start:signal_start + signal_duration,:] | ||
|
||
rates = [] | ||
for i in xrange(len(l)): | ||
rates.append((int(l[i][0] * t.sampleRate) - signal_start, self.durations[i] / l[i][1])) | ||
|
||
vecout = dirac.timeScale(vecin, rates, t.sampleRate, 0) | ||
if hasattr(t, 'gain'): | ||
vecout = limit(multiply(vecout, float32(t.gain))) | ||
|
||
return AudioData(ndarray=vecout, shape=vecout.shape, sampleRate=t.sampleRate, numChannels=vecout.shape[1]) | ||
|
||
def render(self): | ||
# use self.durations already computed | ||
# 1) stretch the duration of each item in t1 and t2 to the duration prescribed in durations. | ||
out1 = self.stretch(self.t1, self.l1) | ||
out2 = self.stretch(self.t2, self.l2) | ||
|
||
# 2) cross-fade the results | ||
# out1.duration, out2.duration, and self.duration should be about the same | ||
# but it never hurts to be safe. | ||
duration = min(out1.duration, out2.duration, self.duration) | ||
c = Crossfade([out1, out2], [0, 0], duration, mode='equal_power') | ||
return c.render() | ||
|
||
def __repr__(self): | ||
return "<Crossmatch '%s' and '%s'>" % (self.t1.analysis.name, self.t2.analysis.name) | ||
|
||
def __str__(self): | ||
s1, e1 = self.l1[0][0], sum(self.l1[-1]) # start and end for each of these lists. | ||
s2, e2 = self.l2[0][0], sum(self.l2[-1]) | ||
n1, n2 = self.t1.analysis.name, self.t2.analysis.name # names | ||
return "Crossmatch\t%.3f\t-> %.3f\t (%.3f)\t%s -> %s" % (s1, e2, self.duration, n1, n2) | ||
|
||
|
||
def humanize_time(secs): | ||
"""Turns seconds into a string of the form HH:MM:SS, or MM:SS if less than one hour.""" | ||
mins, secs = divmod(secs, 60) | ||
hours, mins = divmod(mins, 60) | ||
if 0 < hours: | ||
return '%02d:%02d:%02d' % (hours, mins, secs) | ||
|
||
return '%02d:%02d' % (mins, secs) | ||
|
||
|
||
def display_actions(actions): | ||
total = 0 | ||
for a in actions: | ||
print "%s\t %s" % (humanize_time(total), unicode(a)) | ||
total += a.duration | ||
Oops, something went wrong.