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 Controls the Toonloop audio sampler
27 """
28 from rats import sig
29
30
32 """
33 Any error thrown by the Allocator or Mapper class.
34 """
35 pass
36
38 """
39 Allocates sound indices.
40 """
42 self.max_index = max_index
43 self.pool = set()
44
46 ok = False
47 for value in xrange(self.max_index):
48 if value not in self.pool:
49 ok = True
50 break
51 if not ok:
52 raise AllocationError("No more numbers available. Pool: %s" % (self.pool))
53 self.pool.add(value)
54 return value
55
56 - def free(self, value):
57 try:
58 self.pool.remove(value)
59 except KeyError, e:
60 raise AllocationError("Value %d not in pool." % (value))
61
63 """
64 Maps clip numbers, frame numbers to sound index.
65 """
66 - def __init__(self, num_clips=10, max_num_frames=4000, num_sounds=500):
67 self.num_sounds = num_sounds
68 self.max_num_frames = max_num_frames
69 self.num_clips = num_clips
70 self.allocator = Allocator(self.num_sounds)
71 self.clips = {}
72
73
74
75 - def add(self, clip_id, frame_id):
76 """
77 Adds a sounds in the given clip, frame slot.
78 """
79 if not self.clips.has_key(clip_id):
80 if clip_id >= self.num_clips:
81 raise AllocationError("Clip number too big: %s." % (clip_id))
82 self.clips[clip_id] = {}
83 if self.clips[clip_id].has_key(frame_id):
84 buffer_id = self.clips[clip_id][frame_id]
85
86
87
88
89 else:
90 buffer_id = self.allocator.allocate()
91 self.clips[clip_id][frame_id] = buffer_id
92
93 return buffer_id
94
95 - def get(self, clip_id, frame_id):
96 try:
97 return self.clips[clip_id][frame_id]
98 except KeyError:
99 return None
100
102
103 return self.clips
104
106
107 self.clips = data
108
109 - def remove(self, clip_id, frame_id):
110 """
111 Clears a sound from a given clip, frame slot.
112 :return: buffer id
113 """
114 if self.clips.has_key(clip_id):
115 if self.clips[clip_id].has_key(frame_id):
116 buffer_id = self.clips[clip_id].pop(frame_id)
117 self.allocator.free(buffer_id)
118
119 return buffer_id
120 else:
121 raise AllocationError("Clip %d has no frame %s." % (clip_id, frame_id))
122 else:
123 raise AllocationError("No clip %d." % (clip_id))
124
126 """
127 == Sends OSC messages: ==
128 * /sampler/play/start <i>
129 * /sampler/play/stop <i>
130 * /sampler/record/start <i>
131 * /sampler/record/stop <i>
132 """
133 - def __init__(self, toonloop, osc_manager):
134 self.toonloop = toonloop
135 NUM_SOUNDS = toonloop.config.osc_sampler_num_sounds
136 self.mapper = Mapper(num_sounds=NUM_SOUNDS)
137 self.osc = osc_manager
138 self.verbose = self.toonloop.config.osc_verbose
139 self.player_id = 0
140 self.NUM_PLAYERS = 24
141 self._setup()
142
147
149 clip_id = self.toonloop.clip.id
150 buffer_id = self.mapper.get(clip_id, frame_id)
151 if buffer_id is not None:
152 player_id = (self.player_id + 1) % self.NUM_PLAYERS
153 if self.verbose:
154 print("send /sampler/play/start %d %s" % (player_id, buffer_id))
155 self.osc.send_sampler_play_start(player_id, buffer_id)
156
158 """
159 Slot which listens for a Toonloop's signal
160 :param starting: bool start/stop
161 """
162 if starting:
163
164
165 clip_id = self.toonloop.clip.id
166 frame_id = self.toonloop.clip.playhead
167 try:
168 buffer_id = self.mapper.add(clip_id, frame_id)
169 except AllocationError, e:
170 print(e.message)
171 else:
172 self.osc.send_sampler_record_start(buffer_id)
173 if self.verbose:
174 print("send /sampler/record/start %d" % (buffer_id))
175 else:
176 if self.verbose:
177 print("warning: record/stop not implemented.")
178
180 """
181 Slot which listens for a Toonloop's signal
182 """
183
184 clip_id = self.toonloop.clip.id
185 frame_id = self.toonloop.clip.playhead
186 try:
187 buffer_id = self.mapper.remove(clip_id, frame_id)
188 except AllocationError, e:
189 print(e.message)
190 else:
191 self.osc.send_sampler_clear(buffer_id)
192 if self.verbose:
193 print("send /sampler/clear %d" % (buffer_id))
194