forked from wybiral/python-musical
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtimeline.py
63 lines (46 loc) · 1.65 KB
/
timeline.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
from collections import defaultdict
from musical.audio import source
# XXX: Early implementation of timeline/hit concepts. Needs lots of work
# TODO: Associate sound source with Hit instances somehow
# TODO: Amplitude, possibly other dynamics attributes
class Hit:
''' Rough draft of Hit class. Stores information about the hit and generates
the audio array accordingly. Currently implements a basic cache to avoid
having to rerender identical hits
'''
cache = {}
def __init__(self, note, length):
self.note = note
self.length = length
def render(self):
# Render hit of "key" for "length" amound of seconds
# XXX: Currently only uses a string pluck
key = (str(self.note), self.length)
if key not in Hit.cache:
Hit.cache[key] = source.pluck(self.note, self.length)
return Hit.cache[key]
class Timeline:
''' Rough draft of Timeline class. Handles the timing and mixing of Hits
'''
def __init__(self, rate=44100):
self.rate = rate
self.hits = defaultdict(list)
def add(self, time, hit):
# Add "hit" at "time" seconds in
self.hits[time].append(hit)
def calculate_length(self):
# Determine length of playback from end of last hit
length = 0.0
for time, hits in self.hits.items():
for hit in hits:
length = max(length, time + hit.length)
return length
def render(self):
# Return timeline as audio array by rendering the hits
out = source.silence(self.calculate_length())
for time, hits in self.hits.items():
index = int(time * self.rate)
for hit in hits:
data = hit.render()
out[index:index + len(data)] += data
return out