def count(self, time=None): # Count the events that should have taken place between 0 and now() n = 0 acc = 0 dur = 0 now = (time if time is not None else self.metro.now()) + self.metro.get_latency() durations = self.rhythm() total_dur = float(sum(durations)) if total_dur == 0: WarningMsg("Player object has a total duration of 0. Set to 1") durations = [1] total_dur = 1 self.dur = 1 acc = now - (now % total_dur) try: n = int(len(durations) * (acc / total_dur)) except TypeError as e: WarningMsg(e) self.stop() return 0, 0 while True: dur = float(modi(durations, n)) if acc + dur > now: break else: acc += dur n += 1 # Store duration times self.old_dur = self.attr['dur'] # Returns value for self.event_n and self.event_index self.notes_played = n return n, acc
def shuffle(self, attr=None): """ Shuffles """ if attr is None: shuffle(self.attr['degree']) elif attr in self.attr: shuffle(self.attr[attr]) else: WarningMsg("Player Object has no attribute '{}'".format(attr))
def __setattr__(self, name, value): try: for p in self.players: try: setattr(p, name, value) except: WarningMsg("'%s' object has no attribute '%s'" % (str(p), name)) except: self.__dict__[name] = value return self
def calculate_freq(self): """ Uses the scale, octave, and degree to calculate the frequency values to send to SuperCollider """ # If the scale is frequency only, just return the degree if self.scale == Scale.freq: try: return list(self.event['degree']) except: return [self.event['degree']] now = {} for attr in ('degree', 'oct'): now[attr] = self.event[attr] try: now[attr] = list(now[attr]) except: now[attr] = [now[attr]] size = max(len(now['oct']), len(now['degree'])) f = [] for i in range(size): try: midinum = midi(self.scale, modi(now['oct'], i), modi(now['degree'], i), self.now('root')) except: WarningMsg( "Invalid degree / octave arguments for frequency calculation, reset to default" ) print now['degree'], modi(now['degree'], i) raise f.append(miditofreq(midinum)) return f
def whenmod(self, mod, n, cmd, *args, **kwargs): """ Every n beats, do self.cmd(args) """ try: # Make sure cmd is a method attr = cmd.split(".") if len(attr) == 1: method = getattr(self, attr[0]) elif len(attr) == 2: #sub_method = getattr(self.attr[attr[0]], attr[1]) sub_method = lambda *args, **kwargs: getattr(self.attr[attr[0]], attr[1]).__call__(*args, **kwargs) method = lambda *args, **kwargs: self.attr.update({attr[0]: sub_method(*args, **kwargs)}) assert callable(method) except: WarningMsg("{} is not a valid method for type {}".format(cmd, self.__class__)) return self # If the method call already exists, just update it key = ('whenmod', cmd) if key in self.repeat_events: self.repeat_events[key].update(n, args, kwargs) if not self.repeat_events[key].isScheduled(): self.repeat_events[key].schedule() else: call = WhenModMethodCall(self, method, mod, n, args, kwargs) self.repeat_events[key] = call call.schedule() return self
def every(self, n, cmd, args=()): """ Every n beats, do self.cmd(args) """ try: # Make sure cmd is a method method = getattr(self, cmd) assert callable(method) except: WarningMsg("{} is not a valid method for type {}".format( cmd, self.__class__)) return self # If the method call already exists, just update it if cmd in self.repeat_events: self.repeat_events[cmd].update(n, args) if not self.repeat_events[cmd].isScheduled(): self.repeat_events[cmd].schedule() else: call = MethodCall(self, cmd, n, args) self.repeat_events[cmd] = call call.schedule() return self
def every(self, n, cmd, *args, **kwargs): """ Every n beats, call a method (defined as a string) on the object and use the args and kwargs. To call the method every n-th beat of a timeframe, use the `cycle` keyword argument to specify that timeframe. ``` # Call the shuffle method every 4 beats p1.every(4, 'shuffle') # Call the stutter method on the 5th beat of every 8 beat cycle p1.every(5, 'stutter', 4, cycle=8) ``` """ try: # Make sure cmd is a method attr = cmd.split(".") if len(attr) == 1: method = getattr(self, attr[0]) elif len(attr) == 2: #sub_method = getattr(self.attr[attr[0]], attr[1]) sub_method = lambda *args, **kwargs: getattr(self.attr[attr[0]], attr[1]).__call__(*args, **kwargs) method = lambda *args, **kwargs: self.attr.update({attr[0]: sub_method(*args, **kwargs)}) assert callable(method) except: WarningMsg("{} is not a valid method for type {}".format(cmd, self.__class__)) return self # Collect the cycle length cycle = kwargs.get("cycle", None) kwargs = {key: value for key, value in kwargs.items() if key != "cycle"} # If the method call already exists, just update it # key = ('every', cmd) key = cmd if key in self.repeat_events: self.repeat_events[key].update(n, cycle, args, kwargs) if not self.repeat_events[key].isScheduled(): self.repeat_events[key].schedule() else: call = MethodCall(self, method, n, cycle, args, kwargs) self.repeat_events[key] = call call.schedule() return self
def count(self, time=None, event_after=False): """ Counts the number of events that will have taken place between 0 and `time`. If `time` is not specified the function uses self.metro.now(). Setting `event_after` to `True` will find the next event *after* `time`""" n = 0 acc = 0 dur = 0 now = (time if time is not None else self.metro.now()) durations = self.rhythm() total_dur = float(sum(durations)) if total_dur == 0: WarningMsg("Player object has a total duration of 0. Set to 1") durations = [1] total_dur = 1 self.dur = 1 acc = now - (now % total_dur) try: n = int(len(durations) * (acc / total_dur)) except TypeError as e: WarningMsg(e) self.stop() return 0, 0 if acc != now: while True: dur = float(modi(durations, n)) if acc + dur == now: acc += dur n += 1 break elif acc + dur > now: if event_after: acc += dur n += 1 break else: acc += dur n += 1 # Store duration times self.old_dur = self.attr['dur'] # Returns value for self.event_n and self.event_index return n, acc
def osc_message(self, index=0, **kwargs): """ Creates an OSC packet to play a SynthDef in SuperCollider, use kwargs to force values in the packet, e.g. pan=1 will force ['pan', 1] """ message = [] fx_dict = {} # Calculate frequency / buffer number if self.synthdef != SamplePlayer: degree = group_modi(kwargs.get("degree", self.event["degree"]), index) octave = group_modi(kwargs.get("oct", self.event["oct"]), index) root = group_modi(kwargs.get("root", self.event["root"]), index) freq = miditofreq( midi(kwargs.get("scale", self.scale), octave, degree, root)) message = ['freq', freq] else: degree = group_modi(kwargs.get("degree", self.event['degree']), index) sample = group_modi(kwargs.get("sample", self.event["sample"]), index) buf = int(Samples[str(degree)].bufnum(sample)) message = ['buf', buf] attributes = self.attr.copy() # Go through the attr dictionary and add kwargs for key in attributes: try: # Don't use fx keywords or foxdot keywords if key not in FxList.kwargs() and key not in self.keywords: group_value = kwargs.get(key, self.event[key]) val = group_modi(group_value, index) ## DEBUG if isinstance(val, (Pattern, PGroup)): print "In osc_message:", key, group_value, self.event[ key], val # Special case modulation if key == "sus": val = val * self.metro.beat_dur() * group_modi( kwargs.get('blur', self.event['blur']), index) elif key == "amp": val = val * group_modi( kwargs.get('amplify', self.event['amplify']), index) message += [key, val] except KeyError as e: WarningMsg("KeyError in function 'osc_message'", key, e) # See if any fx_attributes for key in self.fx_attributes: if key in attributes: # All effects use sustain to release nodes fx_dict[key] = [] # Look for any other attributes require e.g. room and verb for sub_key in FxList[key].args: if sub_key in self.event: if sub_key in message: i = message.index(sub_key) + 1 val = message[i] else: try: val = group_modi( kwargs.get(sub_key, self.event[sub_key]), index) if isinstance(val, Pattern): print sub_key, val except TypeError as e: val = 0 except KeyError as e: del fx_dict[key] break # Don't send fx with zero values, unless it is a timevar or playerkey i.e. has a "now" attr if val == 0 and not hasattr(val, 'now'): del fx_dict[key] break else: fx_dict[key] += [sub_key, val] return message, fx_dict
def get_event(self): """ Returns a dictionary of attr -> now values """ # Get the current event # self.event = {} attributes = copy(self.attr) for key in attributes: # Eg. sp.sus returns the currently used value for sustain value = self.event[key] = self.now(key) # Make sure the object's dict uses PlayerKey instances if key not in self.__dict__: self.__dict__[key] = PlayerKey(value, parent=self) elif not isinstance(self.__dict__[key], PlayerKey): self.__dict__[key] = PlayerKey(value, parent=self) else: self.__dict__[key].update(value) # Special case: sample player if self.synthdef == SamplePlayer: try: event_dur = float(self.event['dur']) event = self.event['degree'].now() if hasattr( self.event['degree'], "now") else self.event['degree'] # Store a "char" variable size = self.largest_attribute() event_buf = list(range(size)) if isinstance(event, PlayGroup): # Nest the Play Group buf_list = [event] else: try: buf_list = list(event) except TypeError as e: buf_list = [event] # buf_list is our list of samples to play as characters # event_buf is the list of buffer id's ############## # TODO - Allow for different sample bank IDs in the buffer delay self.buf_delay = [] #for i, bufchar in buf_list: # This should iter over largest event? for i in range(size): # Get the char / group from the buf_list bufchar = group_modi(buf_list, i) # If it is a group if isinstance(bufchar, PlayGroup): # Get the first character, then "delay" the rest self.__dict__['char'].update(bufchar[0]) char = Samples[bufchar[0]] # Get the buffer number to play for this sample bank (char) buf_mod_index = int(group_modi(self.event['sample'], i)) event_buf[i] = char.bufnum(buf_mod_index).bufnum # Begin delay delay = 0 for n, b in enumerate(bufchar[1:]): # If it is a timevar / random play group, get the value if hasattr(b, 'now'): b = b.now() # Get the sample bank / char char = Samples[b] # Find the appropriate sample in the bank buf_mod_index = int(modi(self.event['sample'], i)) # Add the delay delay += (bufchar[n].dur * event_dur) # Add it to our delay list self.buf_delay.append( (char.bufnum(buf_mod_index), delay)) else: # Get the char / bank char = Samples[bufchar] self.__dict__['char'].update(bufchar) # Get the buffer number to play buf_mod_index = int(modi(self.event['sample'], i)) event_buf[i] = char.bufnum(buf_mod_index).bufnum self.event['buf'] = P(event_buf) except TypeError as e: WarningMsg("Sample player get_event", e, bufchar) return self
def osc_message(self, index=0, **kwargs): """ Creates an OSC packet to play a SynthDef in SuperCollider, use kwargs to force values in the packet, e.g. pan=1 will force ['pan', 1] """ freq = float(group_modi(self.attr['freq'], index)) message = ['freq', freq] fx_dict = {} attributes = self.attr.copy() # Go through the attr dictionary and add kwargs for key in attributes: try: # Don't use fx keywords or foxdot keywords if key not in FxList.kwargs() and key not in self.keywords: val = group_modi(kwargs.get(key, self.event[key]), index) # Special case modulation if key == "sus": val = val * self.metro.beat_dur() * modi( kwargs.get('blur', self.event['blur']), index) elif key == "amp": val = val * modi( kwargs.get('amplify', self.event['amplify']), index) message += [key, val] except KeyError as e: WarningMsg("KeyError in function 'osc_message'", key, e) # See if any fx_attributes for key in self.fx_attributes: if key in attributes: # All effects use sustain to release nodes fx_dict[key] = [] # Look for any other attributes require e.g. room and verb for sub_key in FxList[key].args: if sub_key in self.event: if sub_key in message: i = message.index(sub_key) + 1 val = message[i] else: try: val = group_modi( kwargs.get(sub_key, self.event[sub_key]), index) except TypeError as e: val = 0 except KeyError as e: del fx_dict[key] break # Don't send fx with zero values, unless it is a timevar or playerkey i.e. has a "now" attr if val == 0 and not hasattr(val, 'now'): del fx_dict[key] break else: fx_dict[key] += [sub_key, val] return message, fx_dict
def sendPlayerMessage(self, synthdef, packet, effects, player=None): # Create a bundle bundle = OSCBundle() # Create a group for the note group_id = self.nextnodeID() msg = OSCMessage("/g_new") msg.append([group_id, 1, 1]) bundle.append(msg) # Get the bus and SynthDef nodes this_bus = self.nextbusID() this_node = self.nextnodeID() # Make sure messages release themselves after 8 * the duration at max (temp) i = packet.index('sus') + 1 max_sus = float(packet[i] * 8) # Synth msg = OSCMessage("/s_new") new_packet = [] for i in range(0, len(packet), 2): try: packet[i + 1] = float(packet[i + 1]) except TypeError as e: WarningMsg( "Could not convert '{}' argument '{}' to float. Set to 0". format(packet[i], packet[i + 1])) packet[i + 1] = 0.0 packet = [synthdef, this_node, 0, group_id, 'bus', this_bus] + packet msg.append(packet) bundle.append(msg) # Effects for fx in effects: this_effect = effects[fx] # effects should not have 0 values nonzero = True for i in range(0, len(this_effect), 2): try: val = float(this_effect[i + 1]) except TypeError as e: WarningMsg( "Could not convert '{}' argument '{}' to float. Set to 0" .format(this_effect[i], this_effect[i + 1])) val = 0 if val == 0: nonzero = False else: this_effect[i + 1] = val if nonzero: # Get next node ID this_node, last_node = self.nextnodeID(), this_node msg = OSCMessage("/s_new") packet = [ self.fx_names[fx], this_node, 1, group_id, 'bus', this_bus ] + this_effect msg.append(packet) bundle.append(msg) # Finally, output sound through end node "makeSound" msg = OSCMessage("/s_new") this_node, last_node = self.nextnodeID(), this_node packet = [ 'makeSound', this_node, 1, group_id, 'bus', this_bus, 'sus', max_sus ] msg.append(packet) bundle.append(msg) return bundle