Package toon :: Module sampler
[hide private]
[frames] | no frames]

Source Code for Module toon.sampler

  1  #!/usr/bin/env python 
  2  # 
  3  # Toonloop for Python 
  4  # 
  5  # Copyright 2008 Alexandre Quessy & Tristan Matthews 
  6  # <alexandre@quessy.net> & <le.businessman@gmail.com> 
  7  # http://www.toonloop.com 
  8  # 
  9  # Original idea by Alexandre Quessy 
 10  # http://alexandre.quessy.net 
 11  # 
 12  # Toonloop is free software: you can redistribute it and/or modify 
 13  # it under the terms of the GNU General Public License as published by 
 14  # the Free Software Foundation, either version 3 of the License, or 
 15  # (at your option) any later version. 
 16  # 
 17  # Toonloop is distributed in the hope that it will be useful, 
 18  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 19  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 20  # GNU General Public License for more details. 
 21  # 
 22  # You should have received a copy of the gnu general public license 
 23  # along with Toonloop.  If not, see <http://www.gnu.org/licenses/>. 
 24  # 
 25  """ 
 26  Controls the Toonloop audio sampler 
 27  """ 
 28  from rats import sig 
 29   
 30  # uses toon/opensoundcontrol, rats/sig and toon/core 
31 -class AllocationError(Exception):
32 """ 33 Any error thrown by the Allocator or Mapper class. 34 """ 35 pass
36
37 -class Allocator(object):
38 """ 39 Allocates sound indices. 40 """
41 - def __init__(self, max_index=500):
42 self.max_index = max_index 43 self.pool = set()
44
45 - def allocate(self):
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
62 -class Mapper(object):
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 = {} # dict of dicts. Indices are clip_id, frame_id
72 #self.signal_clear = sig.Signal() # int buffer_id 73 #self.signal_record = sig.Signal() # int bufer_id 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 # self.signal_clear(buffer_id) 86 # TODO: the actual sampler is responsible for clearer its buffer 87 # if it has a sound in it when recording. 88 # (since UDP packet order's is not guaranteed) 89 else: 90 buffer_id = self.allocator.allocate() 91 self.clips[clip_id][frame_id] = buffer_id 92 #self.signal_record(buffer_id) 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
101 - def get_data(self):
102 # for serialize 103 return self.clips
104
105 - def set_data(self, data):
106 # for unserialize 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 #self.signal_clear(buffer_id) 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
125 -class Sampler(object):
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 # 500 ? 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 # FIXME 141 self._setup()
142
143 - def _setup(self):
144 self.toonloop.signal_sampler_record.connect(self._slot_sampler_record)# bool start/stop 145 self.toonloop.signal_sampler_clear.connect(self._slot_sampler_clear) 146 self.toonloop.signal_playhead.connect(self._slot_playhead)
147
148 - def _slot_playhead(self, frame_id):
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
157 - def _slot_sampler_record(self, starting):
158 """ 159 Slot which listens for a Toonloop's signal 160 :param starting: bool start/stop 161 """ 162 if starting: 163 #TODO: send_record_start 164 #TODO: send_record_stop 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
179 - def _slot_sampler_clear(self):
180 """ 181 Slot which listens for a Toonloop's signal 182 """ 183 # TODO 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