def __init__(self): super(CrossBar, self).__init__() w = Window.width h = Window.height leftX = 0 bottomY = 0 self.shape = Line(points=[0,0,w,0], width=2) self.color = Color((1, 1, 1), a=1) #speed of the song is 104/60 = 1.733333 beats per second. #lets say that the height of a quarter note (one beat) is 100. #Then I would want that square to travel 2*100 =200 units per second. beatsInHeight = h/200 secondsToTravel = beatsInHeight / 1.7333333 #print("seconds to travel: ",secondsToTravel) self.yPos_anim = KFAnim((0, bottomY), (secondsToTravel, h)) #self.alpha_anim = KFAnim((0, 1), (1+decay, 0)) self.add(self.color) self.add(self.shape) self.time = 0 self.on_update(0)
class SoundBlock(InstructionGroup): """ This module is a rectangular, static block that plays a sound when either someone clicks it, or another sound module (e.g. PhysicsBubble) collides with it. """ name = 'SoundBlock' def __init__(self, norm, sandbox, pos, size, channel, pitch, color, handler, callback=None): super(SoundBlock, self).__init__() self.norm = norm self.sandbox = sandbox self.pos = np.array(pos, dtype=np.float) self.size = np.array(size, dtype=np.float) self.handler = handler self.callback = callback self.rect = Rectangle( pos=self.pos, size=self.size, ) self.white = (239 / 255, 226 / 255, 222 / 255) self.color = Color(*self.white) self.add(self.color) self.add(self.rect) self.channel = channel self.pitch = pitch self.hit_color = color self.add(Color(*self.hit_color)) self.border = Line(rectangle=(*self.pos, *self.size), width=2) self.add(self.border) self.hit = False self.flash_anim = KFAnim((0, *self.hit_color), (.5, *self.white)) self.time = 0 def flash(self): self.callback(self.channel, self.pitch) self.time = 0 self.hit = True def on_update(self, dt): if self.hit: rgb = self.flash_anim.eval(self.time) self.color.rgb = rgb self.time += dt if not self.flash_anim.is_active(self.time): self.hit = False self.time = 0
class FadingMusicNote(InstructionGroup): def __init__(self, pos=(0, 0)): super(FadingMusicNote, self).__init__() self.body = Rectangle( pos=pos, size=(50, 50), texture=Img('./data/scene/eightnote.png').texture) self.pop_anim = KFAnim((0, self.body.size[0]), (.5, self.body.size[0]), (1.0, 0)) mag = random.uniform(20, 30) theta = random.uniform(0, 2 * np.pi) dx, dy = mag * np.cos(theta), mag * np.sin(theta) self.pos_anim = KFAnim((0, pos[0], pos[1]), (.5, pos[0] + dx, pos[1] + dy)) self.add(self.body) self.time = 0 self.active = True self.on_update(0) def on_update(self, dt): # the disappearing animation just reduces the size new_size = self.pop_anim.eval(self.time) new_pos = self.pos_anim.eval(self.time) self.body.size = (new_size, new_size) self.body.pos = new_pos self.time += dt return self.pop_anim.is_active(self.time) def start_anim(self): self.active = True
def __init__(self, norm, sandbox, pos, vel, pitch, timbre, color, bounces, handler, gravity=False, callback=None): """ :param norm: normalizer :param sandbox: client's sandbox :param pos: initial position :param vel: initial velocity :param pitch: MIDI pitch value, where 60 is middle C :param timbre: type of waveform, e.g. 'sine' or 'sawtooth' :param color: 3-tuple of RGB color :param bounces: number of times the bubble bounces before fading away :param gravity: whether or not the bubble is subjected to downwards gravity :param callback: the sound function that is called when the bubble bounces """ super(PhysicsBubble, self).__init__() self.norm = norm self.sandbox = sandbox self.r = self.norm.nv(40) self.pos = np.array(pos, dtype=np.float) self.vel = 2 * np.array(vel, dtype=np.float) self.pitch = pitch self.timbre = timbre self.color = Color(*color) self.text_color = Color(0, 0, 0) self.bounces = bounces self.gravity = gravity self.callback = callback self.handler = handler self.text = CLabelRect(cpos=pos, text=str(self.bounces)) self.bubble = self.timbre_to_shape(self.timbre, pos) self.add(self.color) self.add(self.bubble) self.add(self.text_color) self.add(self.text) # have the bubble fade away when self.bounces = 0 self.fade_anim = KFAnim((0, 1), (0.25, 0)) self.time = 0 self.on_update(0)
def __init__(self, time, time_quant, seconds, lane_ratio): super(Gem, self).__init__(time, time_quant, seconds) # self.color_stages = ((.8, .3, .4), (.85, .75, .1), (.2, .8, .4)) self.color = Color(0.3 + 0.7 * lane_ratio, 0.8, 1, mode='hsv').rgb self.sprite = GemSprite(self.color) self.posistion = (100, 100) self.add_graphic(self.sprite) self.y = 0 self.total_height = None # For fading self.last_time = time self.anim = KFAnim((0, 0.8), (seconds, .3))
def render_elements(self): self.height = self.win_size[1] * 3 / 4 self.staff_lines_height = (self.win_size[1] / 4) * (2 / 3) / 5 self.middle_c_h = (self.win_size[1] / 4) / 6 self.staff_h = self.middle_c_h + self.staff_lines_height self.notes_start = self.win_size[0] / 10 self.notes_width = self.win_size[0] - self.notes_start t = self.tempo_map.tick_to_time( sum(note.get_dur() for note in self.actual_notes)) # t = (sum(note.get_dur() for note in self.actual_notes) + 480) / 960 self.now_bar = Line(points=(self.notes_start, self.height, self.notes_start, self.win_size[1])) self.now_bar_pos = KFAnim((0, self.notes_start), (t, self.win_size[0])) self.border = Line(points=(0, self.height, self.win_size[0], self.height)) # loop thru all notes and map positions -only draw lines for certain ones self.staff_lines = [] self.staff_mappings = dict() for i in range(len(all_notes)): height = self.height + self.middle_c_h + self.staff_lines_height * i / 2.0 if all_notes[i] in notes_w_staff_lines: self.staff_lines.append( Line(points=(0, height, self.win_size[0], height))) self.staff_mappings[ all_notes[i]] = height - self.staff_lines_height / 2.0 if self.render_user_notes: self.place_notes(actual=True) self.place_notes(actual=False) self.add(Color(a=1)) self.add(self.now_bar) self.add(self.border) for line in self.staff_lines: self.add(line) self.clef = Rectangle( source="./data/treble_clef_white.png", pos=(self.win_size[0] / 50, self.height + self.middle_c_h), size=(self.win_size[0] / 22, self.height / 4.5), ) self.add(self.clef)
def __init__(self, pos=(0, 0)): super(FadingMusicNote, self).__init__() self.body = Rectangle( pos=pos, size=(50, 50), texture=Img('./data/scene/eightnote.png').texture) self.pop_anim = KFAnim((0, self.body.size[0]), (.5, self.body.size[0]), (1.0, 0)) mag = random.uniform(20, 30) theta = random.uniform(0, 2 * np.pi) dx, dy = mag * np.cos(theta), mag * np.sin(theta) self.pos_anim = KFAnim((0, pos[0], pos[1]), (.5, pos[0] + dx, pos[1] + dy)) self.add(self.body) self.time = 0 self.active = True self.on_update(0)
class NoteShape(InstructionGroup): def __init__(self, topLeftPos, height, width): super(NoteShape, self).__init__() w = Window.width h = Window.height self.height = height self.width = width leftX = topLeftPos[0] topY = topLeftPos[1] bottomY = topLeftPos[1] - height self.shape = Rectangle(pos = (leftX, bottomY), size=(width, height)) colorChange = leftX / w hue = .6 + .5 * (colorChange) #hue = .76 self.color = Color(hsv=(hue, 1, 1), a=1) #speed of the song is 2 beats per second. #lets say that the height of a quarter note (one beat) is 150. #Then I would want that square to travel 2*100 =200 units per second. beatsInHeight = h/200 secondsToTravel = beatsInHeight / 1.7333333 #print("seconds to travel: ",secondsToTravel) self.yPos_anim = KFAnim((0, topY), (secondsToTravel, h)) #self.alpha_anim = KFAnim((0, 1), (1+decay, 0)) self.add(self.color) self.add(self.shape) self.time = 0 self.on_update(0) def on_update(self, dt): w = Window.width h = Window.height leftPos = self.shape.pos[0] yPos = self.yPos_anim.eval(self.time) self.shape.pos = (leftPos, yPos-self.height) #alpha = self.alpha_anim.eval(self.time) #self.color.a = alpha self.time += dt return yPos < h
def __init__(self, norm, sandbox, pos, size, channel, pitch, color, handler, callback=None): super(SoundBlock, self).__init__() self.norm = norm self.sandbox = sandbox self.pos = np.array(pos, dtype=np.float) self.size = np.array(size, dtype=np.float) self.handler = handler self.callback = callback self.rect = Rectangle( pos=self.pos, size=self.size, ) self.white = (239 / 255, 226 / 255, 222 / 255) self.color = Color(*self.white) self.add(self.color) self.add(self.rect) self.channel = channel self.pitch = pitch self.hit_color = color self.add(Color(*self.hit_color)) self.border = Line(rectangle=(*self.pos, *self.size), width=2) self.add(self.border) self.hit = False self.flash_anim = KFAnim((0, *self.hit_color), (.5, *self.white)) self.time = 0
class Gem(VirtualGem): def __init__(self, time, time_quant, seconds, lane_ratio): super(Gem, self).__init__(time, time_quant, seconds) # self.color_stages = ((.8, .3, .4), (.85, .75, .1), (.2, .8, .4)) self.color = Color(0.3 + 0.7 * lane_ratio, 0.8, 1, mode='hsv').rgb self.sprite = GemSprite(self.color) self.posistion = (100, 100) self.add_graphic(self.sprite) self.y = 0 self.total_height = None # For fading self.last_time = time self.anim = KFAnim((0, 0.8), (seconds, .3)) def set_pos(self): self.y = self.lane.track.time2y(self.time) self.position = (0, self.y - self.sprite.size[1]) def on_release(self, time): super(Gem, self).on_release(time) # Draw gradient gem self.remove_graphic(self.sprite) self.sprite = GradientGemSprite(self.sprite.size, self.color, self.sprite.color.rgb) self.add_graphic(self.sprite) # Function called to render gem based on it's # self.time and self.length parameters. def set_pos_and_size(self): top_y = self.lane.track.time2y(self.time) bot_y = self.lane.track.time2y(self.time + self.length) size_x, size_y = self.sprite.texture.size self.sprite.texture.size = (size_x, top_y - bot_y) self.position = (0, bot_y) def get_height(self): return self.sprite.texture.size[1] def update_length(self, y): size_x, size_y = self.sprite.texture.size self.sprite.texture.size = (size_x, self.y - y) self.position.y = y def update_remove_length(self, y): if self.total_height is None: # Rememeber height before shrinking self.total_height = self.get_height() size_x, size_y = self.sprite.texture.size self.sprite.texture.size = (size_x, self.total_height - self.y + y) def on_update(self): # Fade Color now = self.lane.now dt = now - self.last_time % self.seconds if now < self.last_time % self.seconds: # New frame dt += self.seconds s = self.anim.eval(self.last_time - self.time) self.sprite.color.v = s self.last_time += dt def __str__(self): return '<GEM %0.2f : %0.2f>' % (self.time, self.length)
class MusicBar(InstructionGroup): def __init__(self, actual_notes, user_notes, render_user_notes, sound): super().__init__() self.win_size = (Window.width, Window.height) self.actual_notes = actual_notes self.user_notes = user_notes self.render_user_notes = render_user_notes self.tempo_map = sound.tempo_map self.render_elements() self.time = 0 self.now_bar_moving = False def render_elements(self): self.height = self.win_size[1] * 3 / 4 self.staff_lines_height = (self.win_size[1] / 4) * (2 / 3) / 5 self.middle_c_h = (self.win_size[1] / 4) / 6 self.staff_h = self.middle_c_h + self.staff_lines_height self.notes_start = self.win_size[0] / 10 self.notes_width = self.win_size[0] - self.notes_start t = self.tempo_map.tick_to_time( sum(note.get_dur() for note in self.actual_notes)) # t = (sum(note.get_dur() for note in self.actual_notes) + 480) / 960 self.now_bar = Line(points=(self.notes_start, self.height, self.notes_start, self.win_size[1])) self.now_bar_pos = KFAnim((0, self.notes_start), (t, self.win_size[0])) self.border = Line(points=(0, self.height, self.win_size[0], self.height)) # loop thru all notes and map positions -only draw lines for certain ones self.staff_lines = [] self.staff_mappings = dict() for i in range(len(all_notes)): height = self.height + self.middle_c_h + self.staff_lines_height * i / 2.0 if all_notes[i] in notes_w_staff_lines: self.staff_lines.append( Line(points=(0, height, self.win_size[0], height))) self.staff_mappings[ all_notes[i]] = height - self.staff_lines_height / 2.0 if self.render_user_notes: self.place_notes(actual=True) self.place_notes(actual=False) self.add(Color(a=1)) self.add(self.now_bar) self.add(self.border) for line in self.staff_lines: self.add(line) self.clef = Rectangle( source="./data/treble_clef_white.png", pos=(self.win_size[0] / 50, self.height + self.middle_c_h), size=(self.win_size[0] / 22, self.height / 4.5), ) self.add(self.clef) def play(self): self.now_bar_moving = True def place_notes(self, actual=True): notes_to_place = self.actual_notes if actual else self.user_notes if actual: self.add(Color(a=0.5)) else: self.user_note_instructions = set() num_measures = int( sum(note.get_dur() / 480 / 4 for note in self.actual_notes)) note_index = 0 # place all measure lines x_start = self.notes_start for i in range(num_measures): # measure = [] measure_beats = 0 x_end = self.notes_start + self.notes_width * (i + 1) / num_measures while measure_beats < 1 and note_index < len(notes_to_place): duration = notes_to_place[note_index].get_dur() / 480 / 4 # pitch = notes_to_place[note_index].get_pitch() n_val = notes_to_place[note_index].get_letter() if len(n_val) == 3: n_val = n_val[0::2] height = (self.staff_mappings[n_val] if n_val in self.staff_mappings else self.height) x_pos = x_start + (measure_beats) * (x_end - x_start) if n_val == "C4": # ledger line ledger_width = 15 ledger_height = height + self.staff_lines_height / 2.0 ledger = Line(points=( x_pos - ledger_width, ledger_height, x_pos + self.staff_lines_height + ledger_width, ledger_height, )) self.add(ledger) note_obj = NoteIcon(self.staff_lines_height, x_pos, height) self.add(note_obj) if not actual: self.user_note_instructions.add(note_obj) if n_val == "C4": self.user_note_instructions.add(ledger) measure_beats += duration note_index += 1 self.add( Line(points=( x_end, self.height + self.staff_h, x_end, self.win_size[1] - self.middle_c_h, ))) x_start = x_end def on_update(self, dt): if self.now_bar_moving: pos = self.now_bar_pos.eval(self.time) self.now_bar.points = (pos, self.height, pos, self.win_size[1]) self.time += dt if pos == self.win_size[0]: self.time = 0 self.now_bar_moving = False pos = self.now_bar_pos.eval(self.time) self.now_bar.points = (pos, self.height, pos, self.win_size[1]) for ins in self.user_note_instructions: self.remove(ins) self.place_notes(actual=False) return def on_layout(self, win_size): self.clear() self.win_size = win_size self.render_elements()
class PhysicsBubble(InstructionGroup): """ This module is a drag-and-release physics-based bubble that plays a sound upon colliding with another collidable object, including the sandbox edges. """ name = 'PhysicsBubble' def __init__(self, norm, sandbox, pos, vel, pitch, timbre, color, bounces, handler, gravity=False, callback=None): """ :param norm: normalizer :param sandbox: client's sandbox :param pos: initial position :param vel: initial velocity :param pitch: MIDI pitch value, where 60 is middle C :param timbre: type of waveform, e.g. 'sine' or 'sawtooth' :param color: 3-tuple of RGB color :param bounces: number of times the bubble bounces before fading away :param gravity: whether or not the bubble is subjected to downwards gravity :param callback: the sound function that is called when the bubble bounces """ super(PhysicsBubble, self).__init__() self.norm = norm self.sandbox = sandbox self.r = self.norm.nv(40) self.pos = np.array(pos, dtype=np.float) self.vel = 2 * np.array(vel, dtype=np.float) self.pitch = pitch self.timbre = timbre self.color = Color(*color) self.text_color = Color(0, 0, 0) self.bounces = bounces self.gravity = gravity self.callback = callback self.handler = handler self.text = CLabelRect(cpos=pos, text=str(self.bounces)) self.bubble = self.timbre_to_shape(self.timbre, pos) self.add(self.color) self.add(self.bubble) self.add(self.text_color) self.add(self.text) # have the bubble fade away when self.bounces = 0 self.fade_anim = KFAnim((0, 1), (0.25, 0)) self.time = 0 self.on_update(0) def timbre_to_shape(self, timbre, pos): if timbre == 'sine': return CEllipse(cpos=pos, size=self.norm.nt((80, 80)), segments=20) elif timbre == 'triangle': return CEllipse(cpos=pos, size=self.norm.nt((90, 90)), segments=3) elif timbre == 'square': return CRectangle(cpos=pos, size=self.norm.nt((80, 80))) elif timbre == 'sawtooth': # square rotated 45 degrees return CEllipse(cpos=pos, size=self.norm.nt((90, 90)), segments=4) def on_update(self, dt): if self.gravity: self.vel += downwards_gravity * dt self.pos += self.vel * dt else: self.pos += self.vel * dt if self.bounces > 0: if self.check_for_block_collisions() and self.callback is not None: self.callback(self.pitch, self.timbre) if self.check_for_collisions() and self.callback is not None: self.callback(self.pitch, self.timbre) # second condition checks if bubble hasn't been moving but there's no gravity -- # since bubble would be on the screen forever without making sound, fade it away if self.bounces <= 0 or (not self.gravity and np.linalg.norm(self.vel) == 0): self.color.a = self.fade_anim.eval(self.time) self.time += dt self.bubble.set_cpos(self.pos) self.text.set_cpos(self.pos) return self.fade_anim.is_active(self.time) def check_for_collisions(self): bottom = self.sandbox.pos[1] top = self.sandbox.pos[1] + self.sandbox.height left = self.sandbox.pos[0] right = self.sandbox.pos[0] + self.sandbox.width # collision with bottom if self.pos[1] - self.r < bottom: self.vel[1] = -self.vel[ 1] * damping_factor if self.gravity else -self.vel[1] self.pos[1] = bottom + self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) return True # collision with top if self.pos[1] + self.r > top: self.vel[1] = -self.vel[1] self.pos[1] = top - self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) return True # collision with left if self.pos[0] - self.r < left: self.vel[0] = -self.vel[0] self.pos[0] = left + self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) return True # collision with right if self.pos[0] + self.r > right: self.vel[0] = -self.vel[0] self.pos[0] = right - self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) return True def check_for_block_collisions(self): """Check to see if this bubble collided with any SoundBlocks.""" collide = False blocks = self.handler.block_handler.blocks.objects for block in blocks: left_x = block.pos[0] right_x = block.pos[0] + block.size[0] bottom_y = block.pos[1] top_y = block.pos[1] + block.size[1] # going left if self.pos[0] + self.r >= left_x and \ self.pos[0]+self.r <= right_x and \ self.pos[1] >= bottom_y and self.pos[1] <= top_y: self.vel[0] *= -1 self.pos[0] = left_x - self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) block.flash() collide = True # going right if self.pos[0] - self.r <= right_x and \ self.pos[0]-self.r >= left_x and \ self.pos[1] >= bottom_y and self.pos[1] <= top_y: self.vel[0] *= -1 self.pos[0] = right_x + self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) block.flash() collide = True # going up if self.pos[1] + self.r >= bottom_y and \ self.pos[1] + self.r <= top_y and \ self.pos[0] >= left_x and self.pos[0] <= right_x: self.vel[1] *= -1 self.pos[1] = bottom_y - self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) block.flash() collide = True # going down if self.pos[1] - self.r <= top_y and \ self.pos[1]-self.r >= bottom_y and \ self.pos[0] >= left_x and self.pos[0] <= right_x: self.vel[1] *= -1 self.pos[1] = top_y + self.r self.bounces -= 1 self.text.set_text(str(self.bounces)) block.flash() collide = True return collide