def __init__(self, notes, bank=0, preset=0, loop=False, simon_says=False, bass_puzzle=False): super().__init__() self.audio = Audio(2) self.synth = Synth("./data/FluidR3_GM.sf2") self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) self.sched.set_generator(self.synth) self.audio.set_generator(self.sched) self.notes = notes self.bank = bank self.preset = preset self.loop = loop self.simon_says = simon_says self.bass_puzzle = bass_puzzle self.note_seq = NoteSequencer( sched=self.sched, synth=self.synth, channel=1, program=(self.bank, self.preset), notes=self.notes, loop=self.loop, )
def __init__(self): super(MainWidget, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) # Note Sequencers self.seq1 = NoteSequencer(self.sched, self.synth, 1, (0, 65), kYesterday, False) self.seq2 = NoteSequencer(self.sched, self.synth, 2, (0, 52), kSomewhere, True) # and text to display our status self.label = topleft_label() self.add_widget(self.label)
class MainWidget1(BaseWidget) : def __init__(self): super(MainWidget1, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) # create the arpeggiator: self.arpeg = Arpeggiator(self.sched, self.synth, channel = 1, program = (0,0) ) # and text to display our status self.label = topleft_label() self.add_widget(self.label) def on_key_down(self, keycode, modifiers): if keycode[1] == 'm': self.metro.toggle() if keycode[1] == 'a': self.arpeg.start() pitches = lookup(keycode[1], 'qwe', ((60, 64, 67, 72), (55, 59, 62, 65, 67, 71), (60, 65, 69))) if pitches: self.arpeg.set_pitches(pitches) rhythm = lookup(keycode[1], 'uiop', ((120, 1), (160, 1), (240, 0.75), (480, 0.25))) if rhythm: self.arpeg.set_rhythm(*rhythm) direction = lookup(keycode[1], '123', ('up', 'down', 'updown')) if direction: self.arpeg.set_direction(direction) def on_key_up(self, keycode): if keycode[1] == 'a': self.arpeg.stop() def on_update(self) : self.audio.on_update() self.label.text = self.sched.now_str() + '\n' self.label.text += 'tempo:%d\n' % self.tempo_map.get_tempo() self.label.text += 'm: toggle Metronome\n' self.label.text += 'a: Enable Arpeggiator\n' self.label.text += 'q w e: Changes pitches\n' self.label.text += 'u i o p: Change Rhythm\n' self.label.text += '1 2 3: Change Direction\n'
class MainWidget(BaseWidget): def __init__(self): super(MainWidget, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) # Note Sequencers self.seq1 = NoteSequencer(self.sched, self.synth, 1, (0, 65), kYesterday, False) self.seq2 = NoteSequencer(self.sched, self.synth, 2, (0, 52), kSomewhere, True) # and text to display our status self.label = topleft_label() self.add_widget(self.label) def on_key_down(self, keycode, modifiers): obj = lookup(keycode[1], 'mas', (self.metro, self.seq1, self.seq2)) if obj is not None: obj.toggle() bpm_adj = lookup(keycode[1], ('up', 'down'), (10, -10)) if bpm_adj: new_tempo = self.tempo_map.get_tempo() + bpm_adj self.tempo_map.set_tempo(new_tempo, self.sched.get_time()) def on_update(self): self.audio.on_update() self.label.text = self.sched.now_str() + '\n' self.label.text += 'tempo:%d\n' % self.tempo_map.get_tempo() self.label.text += 'm: toggle Metronome\n' self.label.text += 'a: toggle Sequence 1\n' self.label.text += 's: toggle Sequence 2\n' self.label.text += 'up/down: change speed\n'
def __init__(self): super(MainWidget2, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) # create the arpeggiator: self.arpeg = Arpeggiator(self.sched, self.synth, channel = 1, program = (0,0) ) self.arpeg.set_direction('updown') #size of the notes sent to the arpeggiator self.arpegSize = 3 #all of the notes this program can make. However, only self.arpegSize notes are sent to the arpegiator at a time self.allNotes = [50, 53, 55, 56, 57, 60, 62, 65, 67, 68, 69, 72, 74] self.lastPitchIndex = None self.lastPulseIndex = None self.noteLengths = [240, 210, 180, 150, 120, 90, 60] self.articulation = .75 # and text to display our status self.label = topleft_label() self.add_widget(self.label) # and text to display our status self.label = topleft_label() self.add_widget(self.label) self.objects = AnimGroup() self.canvas.add(self.objects) self.add_lines()
def __init__(self): super(MainWidget5, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) # and text to display our status self.label = topleft_label() self.add_widget(self.label)
class MainWidget5(BaseWidget): def __init__(self): super(MainWidget5, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) # and text to display our status self.label = topleft_label() self.add_widget(self.label) def on_key_down(self, keycode, modifiers): if keycode[1] == 'm': self.metro.toggle() bpm_adj = lookup(keycode[1], ('up', 'down'), (10, -10)) if bpm_adj: new_tempo = self.tempo_map.get_tempo() + bpm_adj self.tempo_map.set_tempo(new_tempo, self.sched.get_time()) def on_update(self): self.audio.on_update() bpm = self.tempo_map.get_tempo() self.label.text = self.sched.now_str() + '\n' self.label.text += 'tempo:{}\n'.format(bpm) self.label.text += 'm: toggle Metronome\n' self.label.text += 'up/down: change speed\n'
def __init__(self): super(MainWidget, self).__init__() self.writer = AudioWriter('song') self.audio = Audio(2, self.writer.add_audio) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(95*2) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) # variables to store options self.transposition = 0 self.style = None self.melody = None self.chords = None self.chord_option = None self.perc = None self.perc_option = None # variables to store screen options self.style_selection = StyleSelection(self.update_style_screen) # screen index 0 self.key_selection = KeySelection(self.update_key_screen) # screen index 1 self.chord_selection = None # screen index 2 self.perc_selection = None # screen index 3 self.melody_selection = None # screen index 4 self.active_screen = self.style_selection self.screen_index = 0 self.add_widget(self.active_screen)
class MainWidget2(BaseWidget) : def __init__(self): super(MainWidget2, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) # create the arpeggiator: self.arpeg = Arpeggiator(self.sched, self.synth, channel = 1, program = (0,0) ) self.arpeg.set_direction('updown') #size of the notes sent to the arpeggiator self.arpegSize = 3 #all of the notes this program can make. However, only self.arpegSize notes are sent to the arpegiator at a time self.allNotes = [50, 53, 55, 56, 57, 60, 62, 65, 67, 68, 69, 72, 74] self.lastPitchIndex = None self.lastPulseIndex = None self.noteLengths = [240, 210, 180, 150, 120, 90, 60] self.articulation = .75 # and text to display our status self.label = topleft_label() self.add_widget(self.label) # and text to display our status self.label = topleft_label() self.add_widget(self.label) self.objects = AnimGroup() self.canvas.add(self.objects) self.add_lines() def add_lines(self): w = Window.width h = Window.height numBuckets = len(self.allNotes) - self.arpegSize sizeOfBucket = w / numBuckets for i in range(numBuckets): xVal = i * sizeOfBucket line = Line(points=[xVal, 0, xVal, h], width=2) self.objects.add(line) numBuckets = len(self.noteLengths) sizeOfBucket = h / numBuckets for i in range(numBuckets): yVal = i * sizeOfBucket line = Line(points=[0, yVal, w, yVal], width=2) self.objects.add(line) def on_touch_down(self, touch): p = touch.pos self.update_pitches(p) self.update_pulse(p) self.arpeg.start() def on_touch_up(self, touch): self.arpeg.stop() def on_touch_move(self, touch): p = touch.pos self.update_pitches(p) self.update_pulse(p) def update_pitches(self, pos=(0,0)): mouseX = pos[0] w = Window.width numBuckets = len(self.allNotes) - self.arpegSize sizeOfBucket = w / numBuckets noteBucket = int(mouseX // sizeOfBucket) if noteBucket != self.lastPitchIndex: arpegNotes = self.allNotes[noteBucket:noteBucket+self.arpegSize] self.lastSlice = arpegNotes self.arpeg.set_pitches(arpegNotes) self.lastPitchIndex = noteBucket def update_pulse(self, pos=(0,0)): mouseY = pos[1] h = Window.height numBuckets = len(self.noteLengths) sizeOfBucket = h / numBuckets pulseBucket = int(mouseY // sizeOfBucket) if pulseBucket < len(self.noteLengths) and pulseBucket != self.lastPulseIndex: length = self.noteLengths[pulseBucket] self.arpeg.set_rhythm(length, self.articulation) self.lastPulseIndex = pulseBucket def on_update(self) : self.audio.on_update() self.label.text = self.sched.now_str() + '\n'
class MainWidget(BaseWidget): def __init__(self): super(MainWidget, self).__init__() self.writer = AudioWriter('song') self.audio = Audio(2, self.writer.add_audio) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(95*2) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) # variables to store options self.transposition = 0 self.style = None self.melody = None self.chords = None self.chord_option = None self.perc = None self.perc_option = None # variables to store screen options self.style_selection = StyleSelection(self.update_style_screen) # screen index 0 self.key_selection = KeySelection(self.update_key_screen) # screen index 1 self.chord_selection = None # screen index 2 self.perc_selection = None # screen index 3 self.melody_selection = None # screen index 4 self.active_screen = self.style_selection self.screen_index = 0 self.add_widget(self.active_screen) def update_style_screen(self, option): self.style = option Window.clearcolor = stylcol[self.style][0] self.change_screens(self.key_selection) self.screen_index = 1 def update_key_screen(self, instance): self.transposition = key_to_transpose[instance] # can now set up melody, chord, percussion settings self.melody = transpose_melody(self.style, 1, self.transposition) self.chords = transpose_instrument(self.style, "chords", self.transposition) self.chord_selection = ChordSelection(self.update_chord_screen, self.sched, self.synth, self.chords, stylcol[self.style][1]) self.perc = transpose_instrument(self.style, "percussion", 0) self.perc_selection = PercSelection(self.update_perc_screen, self.sched, self.synth, self.perc, stylcol[self.style][1]) self.change_screens(self.chord_selection) self.screen_index = 2 def update_chord_screen(self, option): self.chord_option = option self.change_screens(self.perc_selection) self.screen_index = 3 def update_perc_screen(self, option): self.perc_option = option # can now update our composition screen self.melody_selection = MelodySelection( self.synth, self.sched, self.chords[self.chord_option], self.perc[self.perc_option], self.melody, self.change_key_button, self.change_chord_button, self.change_perc_button, self.writer ) self.change_screens(self.melody_selection) self.screen_index = 4 def change_key_button(self, instance): self.transposition = key_to_transpose[instance] self.melody = transpose_melody(self.style, 1, self.transposition) self.chords = transpose_instrument(self.style, "chords", self.transposition) self.melody_selection.stop() self.remove_widget(self.melody_selection) self.melody_selection = MelodySelection( self.synth, self.sched, self.chords[self.chord_option], self.perc[self.perc_option], self.melody, self.change_key_button, self.change_chord_button, self.change_perc_button, self.writer ) self.add_widget(self.melody_selection) self.active_screen = self.melody_selection def change_chord_button(self, instance): if self.chord_option == 1: option = 2 else: option = 1 self.chord_option = option self.melody_selection.change_chords(self.chords[self.chord_option]) def change_perc_button(self, instance): if self.perc_option == 1: option = 2 else: option = 1 self.perc_option = option self.melody_selection.change_perc(self.perc[self.perc_option]) def change_screens(self, screen): self.remove_widget(self.active_screen) self.active_screen = screen self.add_widget(self.active_screen) def on_touch_down(self, touch): self.active_screen.on_touch_down(touch) def on_touch_move(self, touch): self.active_screen.on_touch_move(touch) def on_key_down(self, keycode, modifiers): self.active_screen.on_key_down(keycode, modifiers) if keycode[1] == 'r': self.writer.toggle() def on_layout(self, winsize): if self.style_selection != None: self.style_selection.on_layout(winsize) if self.key_selection != None: self.key_selection.on_layout(winsize) if self.chord_selection != None: self.chord_selection.on_layout(winsize) if self.perc_selection != None: self.perc_selection.on_layout(winsize) if self.melody_selection != None: self.melody_selection.on_layout(winsize) def on_update(self): self.audio.on_update() self.active_screen.on_update()
def __init__(self, norm, sandbox, mixer, client, client_id): self.norm = norm self.module_name = 'SoundBlock' self.sandbox = sandbox self.mixer = mixer self.tempo_map = SimpleTempoMap(bpm=60) self.sched = AudioScheduler(self.tempo_map) self.synth = Synth("data/FluidR3_GM.sf2") self.sched.set_generator(self.synth) self.mixer.add(self.sched) self.cmd = {} self.client = client self.cid = client_id self.instruments = { 'piano': 1, 'violin': 41, 'trumpet': 57, 'ocarina': 80, 'choir': 53 } self.inst_list = ['piano', 'violin', 'trumpet', 'ocarina', 'choir'] self.drumkit = { 'snare': 38, 'crash': 49, 'bass': 35, 'hihat': 46, 'triangle': 81 } self.drum_list = ['snare', 'crash', 'bass', 'hihat', 'triangle'] self.channel = 0 self.drum_channel = len(self.inst_list) # set up the correct sound (program: bank and preset) # each instrument is on a different channel for index, inst in enumerate(self.inst_list): self.synth.program(index, 0, self.instruments[inst]) for index, drum in enumerate(self.drum_list): self.synth.program(index + len(self.instruments), 128, 0) # many variables here are dicts because a user's module handler needs to keep track of # not just its own variables, but other users' variables as well! so we use dictionaries # with client ids as the keys. self.hold_point = {} self.hold_shape = {} # this variable is needed for when a user clicks on a soundblock in a touch_down event, # so that the corresponding touch_up event is skipped self.skip = {} self.color_dict = { 'red': (201 / 255, 108 / 255, 130 / 255), 'orange': (214 / 255, 152 / 255, 142 / 255), 'yellow': (238 / 255, 234 / 255, 202 / 255), 'green': (170 / 255, 220 / 255, 206 / 255), 'teal': (159 / 255, 187 / 255, 208 / 255), 'blue': (44 / 255, 85 / 255, 123 / 255), 'turquoise': (50 / 255, 147 / 255, 140 / 255), 'indigo': (46 / 255, 40 / 255, 90 / 255), 'violet': (147 / 255, 127 / 255, 159 / 255), 'pink': (199 / 255, 150 / 255, 170 / 255), 'peach': (238 / 255, 142 / 255, 154 / 255), 'magenta': (172 / 255, 69 / 255, 133 / 255), 'grey': (140 / 255, 143 / 255, 148 / 255), 'white': (239 / 255, 226 / 255, 222 / 255) } self.default_color = self.color_dict['violet'] self.default_pitch = 60 self.default_timbre = 'sine' self.default_instrument = 'piano' self.default_drum = 'snare' self.color = {} self.pitch = {} self.timbre = {} self.instrument = {} self.drum = {} self.display = False self.blocks = AnimGroup() self.sandbox.add(self.blocks) self.gui = BlockGUI(self.norm, pos=self.norm.nt((50, 100)), is_drum=False, pitch_callback=self.update_pitch, instrument_callback=self.update_instrument, drum_callback=self.update_drum) self.delete_mode = {}
class SoundBlockHandler(object): """ Handles user interaction and drawing of graphics before generating a SoundBlock. Also stores and updates all currently active SoundBlocks. """ def __init__(self, norm, sandbox, mixer, client, client_id): self.norm = norm self.module_name = 'SoundBlock' self.sandbox = sandbox self.mixer = mixer self.tempo_map = SimpleTempoMap(bpm=60) self.sched = AudioScheduler(self.tempo_map) self.synth = Synth("data/FluidR3_GM.sf2") self.sched.set_generator(self.synth) self.mixer.add(self.sched) self.cmd = {} self.client = client self.cid = client_id self.instruments = { 'piano': 1, 'violin': 41, 'trumpet': 57, 'ocarina': 80, 'choir': 53 } self.inst_list = ['piano', 'violin', 'trumpet', 'ocarina', 'choir'] self.drumkit = { 'snare': 38, 'crash': 49, 'bass': 35, 'hihat': 46, 'triangle': 81 } self.drum_list = ['snare', 'crash', 'bass', 'hihat', 'triangle'] self.channel = 0 self.drum_channel = len(self.inst_list) # set up the correct sound (program: bank and preset) # each instrument is on a different channel for index, inst in enumerate(self.inst_list): self.synth.program(index, 0, self.instruments[inst]) for index, drum in enumerate(self.drum_list): self.synth.program(index + len(self.instruments), 128, 0) # many variables here are dicts because a user's module handler needs to keep track of # not just its own variables, but other users' variables as well! so we use dictionaries # with client ids as the keys. self.hold_point = {} self.hold_shape = {} # this variable is needed for when a user clicks on a soundblock in a touch_down event, # so that the corresponding touch_up event is skipped self.skip = {} self.color_dict = { 'red': (201 / 255, 108 / 255, 130 / 255), 'orange': (214 / 255, 152 / 255, 142 / 255), 'yellow': (238 / 255, 234 / 255, 202 / 255), 'green': (170 / 255, 220 / 255, 206 / 255), 'teal': (159 / 255, 187 / 255, 208 / 255), 'blue': (44 / 255, 85 / 255, 123 / 255), 'turquoise': (50 / 255, 147 / 255, 140 / 255), 'indigo': (46 / 255, 40 / 255, 90 / 255), 'violet': (147 / 255, 127 / 255, 159 / 255), 'pink': (199 / 255, 150 / 255, 170 / 255), 'peach': (238 / 255, 142 / 255, 154 / 255), 'magenta': (172 / 255, 69 / 255, 133 / 255), 'grey': (140 / 255, 143 / 255, 148 / 255), 'white': (239 / 255, 226 / 255, 222 / 255) } self.default_color = self.color_dict['violet'] self.default_pitch = 60 self.default_timbre = 'sine' self.default_instrument = 'piano' self.default_drum = 'snare' self.color = {} self.pitch = {} self.timbre = {} self.instrument = {} self.drum = {} self.display = False self.blocks = AnimGroup() self.sandbox.add(self.blocks) self.gui = BlockGUI(self.norm, pos=self.norm.nt((50, 100)), is_drum=False, pitch_callback=self.update_pitch, instrument_callback=self.update_instrument, drum_callback=self.update_drum) self.delete_mode = {} def on_touch_down(self, cid, pos): if cid == self.cid: self.gui.on_touch_down(pos) if not self.sandbox.in_bounds(pos): return # when a block is clicked, flash and play a sound for block in self.blocks.objects: if in_bounds(pos, block.pos, block.size): if self.delete_mode[cid]: self.blocks.objects.remove(block) self.blocks.remove(block) return block.flash() self.skip[cid] = True return # don't start drawing a SoundBlock if self.delete_mode[cid]: return self.hold_point[cid] = pos self.hold_shape[cid] = Rectangle(pos=pos, size=(0, 0)) self.sandbox.add(Color(1, 1, 1)) self.sandbox.add(self.hold_shape[cid]) def on_touch_move(self, cid, pos): if not self.sandbox.in_bounds(pos): return #determine which direction rectangle is being created in hold_point = self.hold_point[cid] size = self.calculate_size(hold_point, pos) bottom_left = pos #moving northeast if pos[0] > hold_point[0] and pos[1] > hold_point[1]: bottom_left = hold_point #moving southeast elif pos[0] > hold_point[0] and pos[1] < hold_point[1]: bottom_left = (hold_point[0], pos[1]) #moving southwest elif pos[0] < hold_point[0] and pos[1] < hold_point[1]: bottom_left = pos #moving northwest elif pos[0] < hold_point[0] and pos[1] > hold_point[1]: bottom_left = (pos[0], hold_point[1]) self.hold_shape[cid].pos = bottom_left self.hold_shape[cid].size = size def on_touch_up(self, cid, pos): if self.skip.get(cid): self.skip[cid] = False return if self.delete_mode[cid]: return if self.hold_shape.get(cid) not in self.sandbox: if not self.sandbox.in_bounds(pos): return bottom_left = self.hold_shape[cid].pos size = self.hold_shape[cid].size if size[0] <= 10 or size[1] <= 10: self.sandbox.remove(self.hold_shape[cid]) return self.sandbox.remove(self.hold_shape[cid]) pitch = self.pitch[cid] color = self.color[cid] instrument = self.instrument[cid] self.channel = self.inst_list.index(instrument) drum = self.drum[cid] self.drum_channel = self.drum_list.index(drum) + len(self.inst_list) if self.gui.is_drum: block = SoundBlock(self.norm, self.sandbox, bottom_left, size, self.drum_channel, self.drumkit[drum], color, self, self.sound) else: block = SoundBlock(self.norm, self.sandbox, bottom_left, size, self.channel, pitch, color, self, self.sound) self.blocks.add(block) def on_key_down(self, cid, key): index = lookup(key, 'q2w3er5t6y7ui', range(13)) if index is not None: if self.cid == cid: self.gui.ps.select(index) if key == '[': if cid == self.cid: self.gui.ps.left_press() if key == ']': if cid == self.cid: self.gui.ps.right_press() if key == 'v' and cid == self.cid: self.delete_mode[cid] = not self.delete_mode[cid] self.update_server_state(post=True) if key == 'up': if not self.gui.is_drum: self.gui.switch_module() if key == 'down': if self.gui.is_drum: self.gui.switch_module() if self.gui.is_drum: drum = lookup(key, 'asdfg', ['snare', 'crash', 'bass', 'hihat', 'triangle']) if drum is not None: self.drum[cid] = drum if self.cid == cid: self.drum_channel = self.drum_list.index(drum) + len( self.inst_list) self.gui.ds.select(drum) else: instrument = lookup( key, 'asdfg', ['piano', 'violin', 'trumpet', 'ocarina', 'choir']) if instrument is not None: self.instrument[cid] = instrument if self.cid == cid: self.channel = self.inst_list.index(instrument) self.gui.ints.select( instrument) # have the GUI update as well def display_controls(self): info = 'delete mode: {}\n'.format(self.delete_mode[self.cid]) if self.gui.is_drum: info += 'drum: {}\n'.format(self.drum_list[self.drum_channel - len(self.inst_list)]) else: info += 'instrument: {}\n'.format(self.inst_list[self.channel]) return info def on_update(self): self.blocks.on_update() self.gui.on_update(Window.mouse_pos) def update_server_state(self, post=False): """ Update server state. If post is True, relay this updated state to all clients. """ state = { 'color': self.color, 'pitch': self.pitch, 'timbre': self.timbre, 'instrument': self.instrument, 'drum': self.drum, 'delete_mode': self.delete_mode } data = { 'module': self.module_name, 'cid': self.cid, 'state': state, 'post': post } self.client.emit('update_state', data) def update_client_state(self, cid, state): """ Update this handler's state. """ if cid != self.cid: # this client already updated its own state self.color = state['color'] self.pitch = state['pitch'] self.timbre = state['timbre'] self.instrument = state['instrument'] self.drum = state['drum'] self.delete_mode = state['delete_mode'] def sync_state(self, state): """ Initial sync with the server's copy of module state. We don't sync with hold_shape, hold_point, and hold_line because those objects are not json-serializable and are short-term values anyway. """ self.color = state['color'] self.pitch = state['pitch'] self.timbre = state['timbre'] self.instrument = state['instrument'] self.drum = state['drum'] self.delete_mode = state['delete_mode'] # after initial sync, add default values for this client self.color[self.cid] = self.default_color self.pitch[self.cid] = self.default_pitch self.timbre[self.cid] = self.default_timbre self.instrument[self.cid] = self.default_instrument self.drum[self.cid] = self.default_drum self.delete_mode[self.cid] = False self.skip[self.cid] = False # now that default values are set, we can display this module's info self.display = True # update server with these default values # post=True here because we want all other clients' states to update with this client's # default values. self.update_server_state(post=True) def sound(self, channel, pitch): """ Play a sound with a given pitch on the given channel. """ if self.cmd.get((channel, pitch)): self.sched.cancel(self.cmd[(channel, pitch)]) self.synth.noteon(channel, pitch, 100) now = self.sched.get_tick() self.cmd[(channel, pitch)] = self.sched.post_at_tick(self._noteoff, now + 240, (channel, pitch)) def update_pitch(self, color, pitch): """Update this client's color and pitch due to PitchSelect.""" self.color[self.cid] = self.color_dict[color] self.pitch[self.cid] = pitch self.update_server_state(post=True) def update_instrument(self, instrument): """Update this client's instrument due to InstrumentSelect.""" self.instrument[self.cid] = instrument self.channel = self.inst_list.index(instrument) self.update_server_state(post=True) def update_drum(self, drum): self.drum[self.cid] = drum self.drum_channel = self.drum_list.index(drum) + len(self.inst_list) self.update_server_state(post=True) def _noteoff(self, tick, args): channel, pitch = args self.synth.noteoff(channel, pitch) def calculate_size(self, corner, pos): x = abs(pos[0] - corner[0]) y = abs(pos[1] - corner[1]) return (x, y) def calculate_center(self, corner, size): c_x = corner[0] + (size[0] / 2) c_y = corner[1] + (size[1] / 2) return (c_x, c_y)
class MainWidget3(BaseWidget) : def __init__(self): super(MainWidget3, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(104) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) percNotes = [(480,35), (360,42), (120,35), (480,35), (480,42)] self.base1Notes = [(240,43), (240,43), (240,43), (120,47), (240,41), (240,41), (360,41), (120,40), (360,41), (240,41), (240,41), (120,40), (120,36), (480,-1), (120,40), (240,41), (120,43)] self.base2Notes = [(120,-1), (120,45), (240,43), (120,-1), (240,43), (120,40), (480,43), (120,-1), (120,45), (120,45), (120,48), (240,-1), (240,41), (120,-1), (240,41), (120,40), (480,41), (120,-1), (120,45), (120,45), (120,48), (240,-1), (240,45), (120,-1), (240,45), (120,45), (480,45), (240,43), (120,-1), (120,45), (240,-1), (240,45), (120,-1), (240,45), (120,45), (480,45), (120,-1), (120,45), (120,45), (120,48)] self.baseNotes = self.base2Notes #[40, 41, 43, 45 48,] #changes / pitch sutff self.changes = [ (1920, [72, 74, 76, 79, 81, 84]), (1920, [69, 72, 74, 81]), (3840, [69, 72, 74, 76, 79, 81, 84])] self.changesIndex = 0 self.curChanges = [] self.selectSize = 2 self.lastPitchIndex = None self.lastTouch = None #Note length stuff self.noteLengths = [480, 240, 120] self.articulation = 1 self.lastPulseIndex = 0 #Declare the players self.perc = NoteSequencer(self.sched, self.synth, 1, (128,0), percNotes) self.base1 = NoteSequencer(self.sched, self.synth, 2, (0,33), self.base1Notes, callback = self.graphic_callback) self.base2 = NoteSequencer(self.sched, self.synth, 2, (0,33), self.base2Notes, callback = self.graphic_callback) self.lead = Arpeggiator(self.sched, self.synth, channel = 3, program = (0,65), callback = self.graphic_callback) self.lead.set_direction('updown') #Start the non-interactive stuff now = self.sched.get_tick() next_beat = quantize_tick_up(now, 480) self.perc.toggle() self.base2.toggle() self.sched.post_at_tick(self._updateChanges, next_beat) #Update changes as music starts self.sched.post_at_tick(self._spawnCrossBar, next_beat) # and text to display our status #self.label = topleft_label() #self.add_widget(self.label) #Graphics stuff self.objects = AnimGroup() self.canvas.add(self.objects) #self.allNotes = [40, 41, 43, 45, 48, 900, 69, 72, 74, 76, 79, 81, 84] self.allNotes = [36, 40, 41, 43, 45, 47, 48, 900, 69, 72, 74, 76, 79, 81, 84] def graphic_callback(self, pitch, length): w = Window.width numBuckets = len(self.allNotes) bucket = self.allNotes.index(pitch) widthOfBucket = w/numBuckets width = widthOfBucket - 10 leftX = bucket*widthOfBucket + 5 height = length/480 * 100 shape = NoteShape((leftX,0), height, width) self.objects.add(shape) def _spawnCrossBar(self, tick, ignore): shape = CrossBar() self.objects.add(shape) self.sched.post_at_tick(self._spawnCrossBar, tick+480) def _updateChanges(self, tick, ignore): timeTillNextChange = self.changes[self.changesIndex][0] self.curChanges = self.changes[self.changesIndex][1] #print("CHANGE OCCURED: ", self.curChanges) self.changesIndex = (self.changesIndex + 1) % len(self.changes) self.sched.post_at_tick(self._updateChanges, tick+timeTillNextChange) self.lastPitchIndex = None if self.lastTouch != None: self.update_pitches(self.lastTouch) def changeBaseLine(self): self.base1.toggle() self.base2.toggle() def on_key_down(self, keycode, modifiers): obj = lookup(keycode[1], 'm', (self.metro)) if obj is not None: obj.toggle() if keycode[1] == 'q': self.changeBaseLine() def on_key_up(self, keycode): pass def on_touch_down(self, touch): p = touch.pos self.update_pitches(p) self.update_pulse(p) self.lead.start() self.lastTouch = p def on_touch_up(self, touch): self.lead.stop() def on_touch_move(self, touch): p = touch.pos self.update_pitches(p) self.update_pulse(p) self.lastTouch = p def update_pitches(self, pos=(0,0)): mouseX = pos[0] w = Window.width numBuckets = len(self.curChanges) - self.selectSize + 1 sizeOfBucket = w / numBuckets noteBucket = int(mouseX // sizeOfBucket) if noteBucket != self.lastPitchIndex: arpegNotes = self.curChanges[noteBucket:noteBucket+self.selectSize] self.lead.set_pitches(arpegNotes) self.lastPitchIndex = noteBucket def update_pulse(self, pos=(0,0)): mouseY = pos[1] h = Window.height numBuckets = len(self.noteLengths) sizeOfBucket = h / numBuckets pulseBucket = int(mouseY // sizeOfBucket) if pulseBucket < len(self.noteLengths) and pulseBucket != self.lastPulseIndex: length = self.noteLengths[pulseBucket] self.lead.set_rhythm(length, self.articulation) self.lastPulseIndex = pulseBucket def on_update(self) : self.audio.on_update() self.objects.on_update()
def __init__(self): super(MainWidget3, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(104) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.audio.set_generator(self.sched) self.sched.set_generator(self.synth) # create the metronome: self.metro = Metronome(self.sched, self.synth) percNotes = [(480,35), (360,42), (120,35), (480,35), (480,42)] self.base1Notes = [(240,43), (240,43), (240,43), (120,47), (240,41), (240,41), (360,41), (120,40), (360,41), (240,41), (240,41), (120,40), (120,36), (480,-1), (120,40), (240,41), (120,43)] self.base2Notes = [(120,-1), (120,45), (240,43), (120,-1), (240,43), (120,40), (480,43), (120,-1), (120,45), (120,45), (120,48), (240,-1), (240,41), (120,-1), (240,41), (120,40), (480,41), (120,-1), (120,45), (120,45), (120,48), (240,-1), (240,45), (120,-1), (240,45), (120,45), (480,45), (240,43), (120,-1), (120,45), (240,-1), (240,45), (120,-1), (240,45), (120,45), (480,45), (120,-1), (120,45), (120,45), (120,48)] self.baseNotes = self.base2Notes #[40, 41, 43, 45 48,] #changes / pitch sutff self.changes = [ (1920, [72, 74, 76, 79, 81, 84]), (1920, [69, 72, 74, 81]), (3840, [69, 72, 74, 76, 79, 81, 84])] self.changesIndex = 0 self.curChanges = [] self.selectSize = 2 self.lastPitchIndex = None self.lastTouch = None #Note length stuff self.noteLengths = [480, 240, 120] self.articulation = 1 self.lastPulseIndex = 0 #Declare the players self.perc = NoteSequencer(self.sched, self.synth, 1, (128,0), percNotes) self.base1 = NoteSequencer(self.sched, self.synth, 2, (0,33), self.base1Notes, callback = self.graphic_callback) self.base2 = NoteSequencer(self.sched, self.synth, 2, (0,33), self.base2Notes, callback = self.graphic_callback) self.lead = Arpeggiator(self.sched, self.synth, channel = 3, program = (0,65), callback = self.graphic_callback) self.lead.set_direction('updown') #Start the non-interactive stuff now = self.sched.get_tick() next_beat = quantize_tick_up(now, 480) self.perc.toggle() self.base2.toggle() self.sched.post_at_tick(self._updateChanges, next_beat) #Update changes as music starts self.sched.post_at_tick(self._spawnCrossBar, next_beat) # and text to display our status #self.label = topleft_label() #self.add_widget(self.label) #Graphics stuff self.objects = AnimGroup() self.canvas.add(self.objects) #self.allNotes = [40, 41, 43, 45, 48, 900, 69, 72, 74, 76, 79, 81, 84] self.allNotes = [36, 40, 41, 43, 45, 47, 48, 900, 69, 72, 74, 76, 79, 81, 84]
class LevelEasyMediumScreen(Screen): def __init__(self, level, notes, goal_values, gear_values, **kwargs): super(LevelEasyMediumScreen, self).__init__(**kwargs) # set up notes for the level self.notes = notes # set up gear values for the levels self.goal_values = goal_values # set up gear values for the levels self.gear_values = gear_values self.level = level # only turn on tutorial for level 1 if self.level == 1: self.use_tutorial = True self.tutorial_screen = 'A' self.goal_play_status = None self.gear_play_status = None self.size_dim = min(Window.width / 6, Window.height / 6) self.tutorial_full_overlay = CRectangle(cpos=(Window.width / 2, Window.height / 2), csize=(Window.width, Window.height)) self.tutorial_options_overlay = Rectangle( pos=(0, 0), size=(Window.width, self.size_dim + Window.height / 25)) self.tutorial_musicbox_overlay = Rectangle( pos=(Window.width // 2, self.size_dim + Window.height / 25), size=(Window.width / 2, Window.height - (self.size_dim + Window.height / 25))) self.tutorial_gearbox_overlay = Rectangle( pos=(0, self.size_dim + Window.height / 25), size=(Window.width / 2, Window.height - (self.size_dim + Window.height / 25))) self.skip_image = CoreImage('images/skip_tutorial.png') self.tutorial_skip_button = Rectangle( pos=(0.98 * Window.width - (self.skip_image.width * self.size_dim / 300), 0.98 * Window.height - self.skip_image.height * self.size_dim / 300), size=(self.skip_image.width * self.size_dim / 300, self.skip_image.height * self.size_dim / 300), texture=self.skip_image.texture) else: self.use_tutorial = False ############################################ ### GOAL MUSIC ### ############################################ self.goal_audio = Audio(2) self.goal_synth = Synth('./data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.goal_tempo_map = SimpleTempoMap(120) self.goal_sched = AudioScheduler(self.goal_tempo_map) # connect scheduler into audio system self.goal_audio.set_generator(self.goal_sched) self.goal_sched.set_generator(self.goal_synth) # generate goal music self.goal_music = MusicPlayer(notes=self.notes, sched=self.goal_sched, synth=self.goal_synth, channel=1, tempo_map=self.goal_tempo_map) self.goal_music.update_tempo(self.goal_values[0]) self.goal_music.update_instrument(self.goal_values[1]) self.goal_music.update_pitch(self.goal_values[2]) self.goal_music.update_volume(self.goal_values[3]) self.goal_music_seq = self.goal_music.generate() ############################################ ### GEAR MUSIC ### ############################################ self.gear_audio = Audio(2) self.gear_synth = Synth('./data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.gear_tempo_map = SimpleTempoMap(120) self.gear_sched = AudioScheduler(self.gear_tempo_map) # connect scheduler into audio system self.gear_audio.set_generator(self.gear_sched) self.gear_sched.set_generator(self.gear_synth) # generate gear music self.gear_music = MusicPlayer(notes=self.notes, sched=self.gear_sched, synth=self.gear_synth, channel=1, tempo_map=self.gear_tempo_map) self.gear_music_seq = None ############################################ ### BACKGROUND UI COMPONENTS ### ############################################ self.gear_area = GearArea() self.canvas.add(self.gear_area) self.music_box_area = MusicBoxArea() self.canvas.add(self.music_box_area) self.options = LevelOptions( level=level, goal_music_seq=self.goal_music_seq, duration=self.goal_music.duration, edit_goal_play_status=self.edit_goal_play_status) self.canvas.add(self.options) self.label = Label(text=kwargs['name'], font_name='./fonts/PassionOne-Regular', color=(.165, .718, .792, 1)) self.add_widget(self.label) ########################################### ### GEAR LABELS ### ########################################### self.tempo_label = Label(text='Tempo (bpm)', font_name='./fonts/PassionOne-Regular', color=(0.7254901960784313, 0.5529411764705883, 0.8196078431372549, 1), center_x=(Window.width / 4), center_y=(Window.height / 5.25 * (0.5 + 0.5) + self.gear_area.position[1]), font_size=str(Window.width // 50) + 'sp') self.instrument_label = Label( text='Instrument', font_name='./fonts/PassionOne-Regular', color=(0.996078431372549, 0.8431372549019608, 0.4, 1), center_x=(Window.width / 4), center_y=(Window.height / 5.25 * (1.5 + 0.5) + self.gear_area.position[1]), font_size=str(Window.width // 50) + 'sp') self.pitch_label = Label(text='Pitch (semitones)', font_name='./fonts/PassionOne-Regular', color=(1.0, 0.6509803921568628, 0.09019607843137255, 1), center_x=(Window.width / 4), center_y=(Window.height / 5.25 * (2.5 + 0.5) + self.gear_area.position[1]), font_size=str(Window.width // 50) + 'sp') self.volume_label = Label( text='Volume', font_name='./fonts/PassionOne-Regular', color=(0.9254901960784314, 0.32941176470588235, 0.3176470588235294, 1), center_x=(Window.width / 4), center_y=(Window.height / 5.25 * (3.5 + 0.5) + self.gear_area.position[1]), font_size=str(Window.width // 50) + 'sp') self.add_widget(self.volume_label) self.add_widget(self.pitch_label) self.add_widget(self.instrument_label) self.add_widget(self.tempo_label) ########################################### ### GEAR CONSTRUCTION ### ########################################### self.gears = [] self.gear_centers = [] self.gears_group = AnimGroup() self.canvas.add(self.gears_group) ########################################### ### GEARS ### ########################################### self.gear_storage_locations = [] self.gear_music_locations = [] self.gear_labels = [] self.set_up_gears() ########################################### ### PARTICLE EFFECT ### ########################################### self.win_ps = ParticleSystem('particles/star_particle.pex') self.win_ps.emitter_x = Window.width / 2 self.win_ps.emitter_y = Window.height / 2 self.add_widget(self.win_ps) self.you_win_label = Label(text=' ', font_name='./fonts/PassionOne-Regular', color=(0.5843137254901961, 0.796078431372549, 0.37254901960784315, 1), center_x=Window.width / 2, center_y=Window.height / 2, font_size=str(Window.width // 10) + 'sp') self.add_widget(self.you_win_label) self.lose_ps = ParticleSystem('particles/lose_particle.pex') self.lose_ps.emitter_x = Window.width / 2 self.lose_ps.emitter_y = Window.height / 2 self.add_widget(self.lose_ps) self.you_lose_label = Label(text=' ', font_name='./fonts/PassionOne-Regular', color=(0.9254901960784314, 0.32941176470588235, 0.3176470588235294, 1), center_x=Window.width / 2, center_y=Window.height / 2, font_size=str(Window.width // 10) + 'sp') self.add_widget(self.you_lose_label) ########################################### ### ERROR MESSAGE ### ########################################### self.error_msg = Label(text=' ', font_name='./fonts/PassionOne-Regular', color=(0.9254901960784314, 0.32941176470588235, 0.3176470588235294, 1), center_x=Window.width / 2, center_y=Window.height / 2, font_size=str(Window.width // 20) + 'sp') self.add_widget(self.error_msg) # ########################################### # ### ADD TUTORIAL OVERLAYS ### # ########################################### if self.use_tutorial: self.canvas.add(Color(rgba=(0, 0, 0, 0.85))) self.canvas.add(self.tutorial_full_overlay) self.tutorial_label = Label( markup=True, text= "[font=./fonts/lato-bold]Welcome to the\n[/font] [font=./fonts/options-icons]|[/font] [font=./fonts/PassionOne-Regular]Play It By Gear Tutorial[/font] [font=./fonts/options-icons]|[/font] [font=./fonts/lato-bold]\n\nThe goal of this game is to make the \n goal song match the song you create by \nplacing the correct gears in a music box \n\n[/font] [font=./fonts/lato-light] (click to see the next \nstep of the tutorial)[/font]", color=(86 / 255, 189 / 255, 205 / 255, 1), center_x=Window.width / 2, center_y=Window.height / 2, font_size=str(Window.width // 40) + 'sp', halign='center') self.add_widget(self.tutorial_label) self.canvas.add(self.tutorial_skip_button) self.on_layout((Window.width, Window.height)) def clear_overlays(self): if self.tutorial_full_overlay in self.canvas.children: self.canvas.remove(self.tutorial_full_overlay) if self.tutorial_options_overlay in self.canvas.children: self.canvas.remove(self.tutorial_options_overlay) if self.tutorial_gearbox_overlay in self.canvas.children: self.canvas.remove(self.tutorial_gearbox_overlay) if self.tutorial_musicbox_overlay in self.canvas.children: self.canvas.remove(self.tutorial_musicbox_overlay) while self.tutorial_skip_button in self.canvas.children: self.canvas.remove(self.tutorial_skip_button) def activate_overlays(self, overlay_names): self.clear_overlays() self.canvas.add(Color(rgba=(0, 0, 0, 0.85))) if 'full' in overlay_names: self.canvas.add(self.tutorial_full_overlay) if 'options' in overlay_names: self.canvas.add(self.tutorial_options_overlay) if 'gearbox' in overlay_names: self.canvas.add(self.tutorial_gearbox_overlay) if 'musicbox' in overlay_names: self.canvas.add(self.tutorial_musicbox_overlay) def get_scaled_x_y(self, winsize, x, y): width, height = winsize # scaled_x = width/8 * (x+1) scaled_x = width / (len(self.gear_values) // 4 * 2) * (x + 0.5) scaled_y = height / 5.25 * (y + 0.5) + self.gear_area.position[1] return scaled_x, scaled_y def set_up_gears(self): self.gears = [] self.gears_group.remove_all() center_gear_location = (Window.width / 6 * 4.5, Window.height / 4 * 2.5) center_gear_size = min(Window.width / 10, Window.height / 10) self.center_gear = Gear(None, None, center_gear_size, 10, 'center', 0, center_gear_location, center_gear_location, 1, colors['dark_grey']) self.canvas.add(self.center_gear.color) self.gears_group.add(self.center_gear) self.center_gear_center = GearCenter(None, None, center_gear_location, center_gear_location, 'center', center_gear_size / 2, colors['dark_grey']) self.canvas.add(colors['dark_grey']) self.canvas.add(self.center_gear_center) self.play_center_gear = False self.music_gears = [] tempo_location = (center_gear_location[0], center_gear_location[1] + center_gear_size + center_gear_size / 5) instrument_location = (center_gear_location[0], center_gear_location[1] - center_gear_size - center_gear_size / 5) pitch_location = (center_gear_location[0] + center_gear_size + center_gear_size / 5, center_gear_location[1]) volume_location = (center_gear_location[0] - center_gear_size - center_gear_size / 5, center_gear_location[1]) self.music_box_gear_locations = [ tempo_location, instrument_location, pitch_location, volume_location ] counter = 0 label_font_size = min(Window.width // 80, Window.height // 80) for y in range(0, 4): for x in range(len(self.gear_values) // 4): gear_type = gear_type_map[y] size = min(Window.width / 10, Window.height / 10) music_pos = self.music_box_gear_locations[y] scaled_x, scaled_y = self.get_scaled_x_y( (Window.width, Window.height), x, y) gear = Gear(x, y, size, 8, gear_type, self.gear_values[counter], (scaled_x, scaled_y), music_pos, 0, colors['dark_grey']) self.gears.append(gear) self.canvas.add(gear.color) self.gears_group.add(gear) gear_center = GearCenter(x, y, (scaled_x, scaled_y), music_pos, gear_type, size / 2, colors['dark_grey']) self.gear_centers.append(gear_center) self.canvas.add(gear_center) ## white dots for storage purposes gear_loc = GearLocation((scaled_x, scaled_y), size / 2, x, y, gear_type) self.gear_storage_locations.append(gear_loc) self.canvas.add(gear_loc) text = str(self.gear_values[counter]) font_name = './fonts/PassionOne-Regular' if y == 3: # get volume as percent text = str(100 * self.gear_values[counter] // 127) + '%' if y == 1: # get icon for instrument font_name = './fonts/music-instruments' text = instruments[self.gear_values[counter]] label = Label(text=text, font_name=font_name, color=(0, 0, 0, 1), center_x=scaled_x, center_y=scaled_y, font_size=str(label_font_size) + 'sp') self.gear_labels.append(label) self.add_widget(label) counter += 1 for indx, loc in enumerate(self.music_box_gear_locations): gear_type = gear_type_map[indx % 4] gear_loc = GearLocation(loc, center_gear_size / 2, None, None, gear_type) self.gear_music_locations.append(gear_loc) self.canvas.add(gear_loc) def edit_goal_play_status(self, value): if self.use_tutorial: if self.goal_play_status == None and value == 'started': self.goal_play_status = 'started' elif self.goal_play_status == 'started' and value == 'finished': self.goal_play_status = 'finished' def _can_add_gear(self, new_gear): for gear in self.music_gears: if gear.type == new_gear.type: return False return True def _check_music_gears(self): all_types = ['volume', 'pitch', 'tempo', 'instrument'] for gear in self.music_gears: if gear.type in all_types: all_types.remove(gear.type) return len(all_types) == 0 def update_center_gear_on_layout(self, winsize): width, height = winsize center_gear_location = (width / 6 * 4.5, height / 4 * 2.5) center_gear_size = min(Window.width / 10, Window.height / 10) self.center_gear_center.update_storage_pos(center_gear_location) self.center_gear_center.update_music_pos(center_gear_location) self.center_gear.update_storage_pos(center_gear_location) self.center_gear.update_music_pos(center_gear_location) self.center_gear.on_layout(center_gear_location, center_gear_size) self.center_gear_center.on_layout(center_gear_location, center_gear_size / 2) tempo_location = (center_gear_location[0], center_gear_location[1] + center_gear_size + center_gear_size / 5) instrument_location = (center_gear_location[0], center_gear_location[1] - center_gear_size - center_gear_size / 5) pitch_location = (center_gear_location[0] + center_gear_size + center_gear_size / 5, center_gear_location[1]) volume_location = (center_gear_location[0] - center_gear_size - center_gear_size / 5, center_gear_location[1]) self.music_box_gear_locations = [ tempo_location, instrument_location, pitch_location, volume_location ] for indx, loc in enumerate(self.gear_music_locations): loc.on_layout(self.music_box_gear_locations[indx], center_gear_size / 2) def on_enter(self): if not self.use_tutorial and self.level == 1: while self.tutorial_skip_button in self.canvas.children: self.canvas.remove(self.tutorial_skip_button) def on_layout(self, winsize): mapped_dic = {'tempo': 0, 'instrument': 1, 'pitch': 2, 'volume': 3} self.update_center_gear_on_layout(winsize) self.size_dim = min(Window.width / 6, Window.height / 6) size = min(Window.width / 10, Window.height / 10) label_font_size = min(Window.width // 80, Window.height // 80) for loc in self.gear_storage_locations: scaled_x, scaled_y = self.get_scaled_x_y(winsize, loc.x, loc.y) loc.on_layout((scaled_x, scaled_y), size / 2) for indx, gear in enumerate(self.gears): scaled_x, scaled_y = self.get_scaled_x_y(winsize, gear.x, gear.y) gear.update_storage_pos((scaled_x, scaled_y)) gear.update_music_pos( self.music_box_gear_locations[mapped_dic[gear.type]]) gear.on_layout((scaled_x, scaled_y), size) self.gear_labels[indx].center_x = scaled_x self.gear_labels[indx].center_y = scaled_y self.gear_labels[indx].font_size = str(label_font_size) + 'sp' for center in self.gear_centers: scaled_x, scaled_y = self.get_scaled_x_y(winsize, center.x, center.y) center.update_storage_pos((scaled_x, scaled_y)) center.update_music_pos( self.music_box_gear_locations[mapped_dic[center.type]]) center.on_layout((scaled_x, scaled_y), size / 2) # update level label self.label.center_x = self.size_dim * 1.5 self.label.center_y = self.size_dim * 3 / 5 self.label.font_size = str(Window.width // 20) + 'sp' # update you win label self.you_win_label.center_x = (Window.width / 2) self.you_win_label.center_y = (Window.height / 2) self.you_win_label.font_size = str(Window.width // 10) + 'sp' self.tempo_label.center_x = (Window.width / 4) self.tempo_label.center_y = (Window.height / 5.25 * (0.5 + 0.5) + self.gear_area.position[1]) self.tempo_label.font_size = str(Window.width // 50) + 'sp' self.instrument_label.center_x = (Window.width / 4) self.instrument_label.center_y = (Window.height / 5.25 * (1.5 + 0.5) + self.gear_area.position[1]) self.instrument_label.font_size = str(Window.width // 50) + 'sp' self.pitch_label.center_x = (Window.width / 4) self.pitch_label.center_y = (Window.height / 5.25 * (2.5 + 0.5) + self.gear_area.position[1]) self.pitch_label.font_size = str(Window.width // 50) + 'sp' self.volume_label.center_x = (Window.width / 4) self.volume_label.center_y = (Window.height / 5.25 * (3.5 + 0.5) + self.gear_area.position[1]) self.volume_label.font_size = str(Window.width // 50) + 'sp' # update child components self.gear_area.on_layout((Window.width, Window.height)) self.music_box_area.on_layout((Window.width, Window.height)) self.options.on_layout((Window.width, Window.height)) # update particle effect and win/lose labels self.win_ps.emitter_x = Window.width / 2 self.win_ps.emitter_y = Window.height / 2 self.you_win_label.center_x = Window.width / 2 self.you_win_label.center_y = Window.height / 2 self.you_win_label.font_size = str(Window.width // 10) + 'sp' self.lose_ps.emitter_x = Window.width / 2 self.lose_ps.emitter_y = Window.height / 2 self.you_lose_label.center_x = Window.width / 2 self.you_lose_label.center_y = Window.height / 2 self.you_lose_label.font_size = str(Window.width // 10) + 'sp' # update error message location self.error_msg.center_x = Window.width / 2 self.error_msg.center_y = Window.height / 2 self.error_msg.font_size = str(Window.width // 20) + 'sp' # update tutorial overlays, label, and skip button if self.use_tutorial: self.update_tutorial_screen(self.tutorial_screen) self.tutorial_full_overlay.cpos = (Window.width / 2, Window.height / 2) self.tutorial_full_overlay.csize = (Window.width, Window.height) self.tutorial_options_overlay.size = (Window.width, self.size_dim + Window.height / 25) self.tutorial_musicbox_overlay.pos = (Window.width // 2, self.size_dim + Window.height / 25) self.tutorial_musicbox_overlay.size = ( Window.width / 2, Window.height - (self.size_dim + Window.height / 25)) self.tutorial_gearbox_overlay.pos = (0, self.size_dim + Window.height / 25) self.tutorial_gearbox_overlay.size = ( Window.width / 2, Window.height - (self.size_dim + Window.height / 25)) self.tutorial_skip_button.pos = ( 0.98 * Window.width - (self.skip_image.width * self.size_dim / 300), 0.98 * Window.height - self.skip_image.height * self.size_dim / 300) self.tutorial_skip_button.size = (self.skip_image.width * self.size_dim / 300, self.skip_image.height * self.size_dim / 300) def reset(self): for gear in self.gears: # reset gear position gear.reset() # stop gear from rotating gear.stop() # stop center gear from rotating self.center_gear.stop() # stop music self.goal_music_seq.stop() if self.gear_music_seq: self.gear_music_seq.stop() # end tutorial if self.use_tutorial: self.tutorial_label.text = '' self.use_tutorial = False self.clear_overlays() while self.tutorial_skip_button in self.canvas.children: self.canvas.remove(self.tutorial_skip_button) def skip_tutorial_pressed(self, touch): if self.use_tutorial: if 0.98 * Window.width - ( self.skip_image.width * self.size_dim / 300 ) < touch.pos[ 0] < 0.98 * Window.width and 0.98 * Window.height - self.skip_image.height * self.size_dim / 300 < touch.pos[ 1] < 0.98 * Window.height: return True return False def on_touch_up(self, touch): # if click is on one of the lower menu buttons, perform the appropriate action self.options.on_touch_up(self.switch_to, self.gear_music_seq, self.check_level_complete(), self.win_ps, self.you_win_label, self.lose_ps, self.you_lose_label, self.reset, touch) for index, gear in enumerate(self.gears): # response is true if you click the current gear response = gear.on_touch_up(touch.pos, self.gear_area.max_x, self._can_add_gear) self.gear_centers[index].on_touch_up(touch.pos, self.gear_area.max_x, self._can_add_gear) if response: if gear not in self.music_gears: self.music_gears.append(gear) # update the gear music based on the gear that is selected function = 'self.gear_music.update_' + gear.type + '(' + str( gear.value) + ')' eval(function) else: if gear in self.music_gears: self.music_gears.remove(gear) self.center_gear.on_touch_up(touch.pos, self.gear_area.max_x, self._can_add_gear) if self.use_tutorial: # if skip button pressed, quit out of tutorial mode if self.skip_tutorial_pressed(touch): self.use_tutorial = False self.remove_widget(self.tutorial_label) self.clear_overlays() while self.tutorial_skip_button in self.canvas.children: self.canvas.remove(self.tutorial_skip_button) elif self.tutorial_screen == 'A': # show screen B (musicbox and gearbox covered) self.tutorial_screen = 'B' self.update_tutorial_screen('B') elif self.tutorial_screen == 'B': # show screen C (musicbox and options covered) self.tutorial_screen = 'C' self.update_tutorial_screen('C') elif self.tutorial_screen == 'C': # show screen D (gearbox and options covered) self.tutorial_screen = 'D' self.update_tutorial_screen('D') elif self.tutorial_screen == 'D': # show screen E (options covered) self.tutorial_screen = 'E' self.update_tutorial_screen('E') elif self.tutorial_screen == 'E' and self._check_music_gears(): # if all gears have been placed, show screen F (gearbox covered) self.tutorial_screen = 'F' self.update_tutorial_screen('F') elif self.tutorial_screen == 'F' and self.gear_play_status == 'finished' and self.goal_play_status == 'finished': # if both tunes have been played show screen G (gearbox and musicbox covered) self.tutorial_screen = 'G' self.update_tutorial_screen('G') elif self.tutorial_screen == 'G': # end tutorial self.use_tutorial = False self.remove_widget(self.tutorial_label) self.clear_overlays() while self.tutorial_skip_button in self.canvas.children: self.canvas.remove(self.tutorial_skip_button) def update_tutorial_screen(self, screen): if not self.use_tutorial: return self.remove_widget(self.tutorial_label) if self.tutorial_screen == 'A': self.activate_overlays(['full']) self.tutorial_label.center_x = Window.width / 2 self.tutorial_label.center_y = Window.height / 2 self.tutorial_label.text = "[font=./fonts/lato-bold]Welcome to the\n[/font] [font=./fonts/options-icons]|[/font] [font=./fonts/PassionOne-Regular]Play It By Gear Tutorial[/font] [font=./fonts/options-icons]|[/font] [font=./fonts/lato-bold]\n\nThe goal of this game is to make the \n goal song match the song you create by \nplacing the correct gears in a music box \n\n[/font] [font=./fonts/lato-light] (click to see the next \nstep of the tutorial)[/font]" self.tutorial_label.font_size = font_size = str( Window.width // 40) + 'sp' if self.tutorial_screen == 'B': self.activate_overlays(['musicbox', 'gearbox']) self.tutorial_label.center_x = 1 / 2 * Window.width self.tutorial_label.center_y = Window.height / 2 + (min( Window.width / 6, Window.height / 6) + Window.height / 25) / 2 self.tutorial_label.text = "[font=./fonts/lato-bold]\nAt the bottom of the screen is the menu\nbar which contains some helpful buttons\n\nthe [/font] [font=./fonts/options-icons]^[/font] [font=./fonts/lato-bold] button brings\nyou back to the level select menu\n\nthe [/font] [font=./fonts/options-icons]`[/font] [font=./fonts/lato-bold] button resets the level\n\n[/font] [font=./fonts/lato-light](click to continue)[/font]" self.tutorial_label.font_size = font_size = str( Window.width // 50) + 'sp' elif self.tutorial_screen == 'C': self.activate_overlays(['musicbox', 'options']) self.tutorial_label.center_x = 3 / 4 * Window.width self.tutorial_label.center_y = Window.height / 2 + (min( Window.width / 6, Window.height / 6) + Window.height / 25) / 2 self.tutorial_label.text = "[font=./fonts/lato-bold]The left side of the screen\nhas all the gears you can choose\nfrom in order to make the\nmusic box sound correct\n\n[/font] [font=./fonts/lato-light](click to continue)[/font]" self.tutorial_label.font_size = font_size = str( Window.width // 60) + 'sp' elif self.tutorial_screen == 'D': self.activate_overlays(['gearbox', 'options']) self.tutorial_label.center_x = 1 / 4 * Window.width self.tutorial_label.center_y = Window.height / 2 + (min( Window.width / 6, Window.height / 6) + Window.height / 25) / 2 self.tutorial_label.text = "[font=./fonts/lato-bold]The right side of the screen\nis the music box. Gears in\nthe music box modify the song.\n\nYou need one gear of each\ntype/color in the music box in\norder for the song to play. \n\n[/font] [font=./fonts/lato-light](click to continue)[/font]" self.tutorial_label.font_size = font_size = str( Window.width // 60) + 'sp' elif self.tutorial_screen == 'E': self.activate_overlays(['options']) self.tutorial_label.center_x = Window.width / 2 self.tutorial_label.center_y = (min( Window.width / 6, Window.height / 6) + Window.height / 25) / 2 self.tutorial_label.text = "[font=./fonts/lato-bold]Now drag one gear of each type/color into the music box\n[/font] [font=./fonts/lato-light](when there are 4 gears in the music box, the tutorial will continue)[/font]" self.tutorial_label.font_size = font_size = str( Window.width // 60) + 'sp' elif self.tutorial_screen == 'F': self.activate_overlays(['gearbox']) self.tutorial_label.center_x = 1 / 4 * Window.width self.tutorial_label.center_y = Window.height / 2 + (min( Window.width / 6, Window.height / 6) + Window.height / 25) / 2 self.tutorial_label.text = "[font=./fonts/lato-bold]Play the goal sound by pressing\nthe [/font] [font=./fonts/options-icons]_[/font] [font=./fonts/lato-bold] button, then press\nthe [/font] [font=./fonts/options-icons]~[/font] [font=./fonts/lato-bold] in the center gear to\nplay the song you created\nwith the gears\n\n[/font] [font=./fonts/lato-light](after you play both songs,\nclick again to continue)[/font]" self.tutorial_label.font_size = font_size = str( Window.width // 60) + 'sp' elif self.tutorial_screen == 'G': self.activate_overlays(['musicbox', 'gearbox']) self.tutorial_label.center_x = 1 / 2 * Window.width self.tutorial_label.center_y = Window.height / 2 + (min( Window.width / 6, Window.height / 6) + Window.height / 25) / 2 self.tutorial_label.text = "[font=./fonts/lato-bold]Did the two songs sound the same?\n\nIf yes, you can press the [/font] [font=./fonts/options-icons]{[/font] [font=./fonts/lato-bold] button\n to see if you're correct\n\n If no, you can switch the gears in the music box\nuntil you think both songs sound the same\n\n[/font] [font=./fonts/lato-light](click to exit the tutorial)[/font]" self.tutorial_label.font_size = font_size = str( Window.width // 55) + 'sp' self.add_widget(self.tutorial_label) self.canvas.add(self.tutorial_skip_button) def on_touch_down(self, touch): other_gears = False for index, gear in enumerate(self.gears): cur_gear = gear.on_touch_down(touch.pos) other_gears = other_gears or cur_gear self.gear_centers[index].on_touch_down(touch.pos) if other_gears: if self.gear_music_seq: for gear in self.music_gears: gear.stop() self.center_gear.stop() self.gear_music_seq.stop() self.gear_music_seq = None else: response = self.center_gear.on_touch_down(touch.pos) if response: if self._check_music_gears(): for gear in self.music_gears: gear.toggle_rotate() is_rotating = self.center_gear.toggle_rotate() if is_rotating: self.gear_music_seq = self.gear_music.generate() self.gear_music_seq.start() if self.use_tutorial and self.gear_play_status == None: self.gear_play_status = 'started' else: if self.gear_music_seq: self.gear_music_seq.stop() self.gear_music_seq = None else: if not self.use_tutorial: self.error_msg.text = 'Place all 4 gears' else: self.error_msg.text = ' ' return self.error_msg.text = ' ' def on_touch_move(self, touch): for index, gear in enumerate(self.gears): gear.on_touch_move(touch.pos) self.gear_centers[index].on_touch_move(touch.pos) def on_update(self): self.goal_audio.on_update() self.gear_audio.on_update() self.options.on_update() for gear in self.gears: gear.on_update(1) self.center_gear.on_update(1) if self.gear_music_seq and not self.gear_music_seq.playing: for gear in self.music_gears: gear.stop() self.center_gear.stop() self.gear_music_seq = None if self.use_tutorial and self.gear_play_status == 'started': self.gear_play_status = 'finished' def check_level_complete(self): return self.goal_music.is_equal(self.gear_music)
class MainWidget(BaseWidget): def __init__(self): super(MainWidget, self).__init__() self.audio = Audio(2, input_func=self.receive_audio, num_input_channels=1) self.mixer = Mixer() self.audio.set_generator(self.mixer) self.pitch = PitchDetector() self.recorder = VoiceAudioWriter('data') self.info = topleft_label() self.add_widget(self.info) self.anim_group = AnimGroup() self.mic_meter = MeterDisplay((50, 25), 150, (-96, 0), (.1, .9, .3)) self.mic_graph = GraphDisplay((110, 25), 150, 300, (-96, 0), (.1, .9, .3)) self.pitch_meter = MeterDisplay((50, 200), 150, (30, 90), (.9, .1, .3)) self.pitch_graph = GraphDisplay((110, 200), 150, 300, (30, 90), (.9, .1, .3)) self.canvas.add(self.mic_meter) self.canvas.add(self.mic_graph) self.canvas.add(self.pitch_meter) self.canvas.add(self.pitch_graph) # Record button self.record_button = InteractiveImage() self.record_button.source = "../data/mic.png" self.record_button.x = 400 self.record_button.y = 400 self.record_button.size = (100, 100) self.record_button.set_callback(self.init_recording) self.add_widget(self.record_button) # Play button self.play_button = InteractiveImage() self.play_button.source = "../data/play.png" self.play_button.x = 600 self.play_button.y = 400 self.play_button.size = (100, 100) self.play_button.set_callback(self.play_recording) self.add_widget(self.play_button) self.canvas.add(self.anim_group) self.onset_disp = None self.onset_x = 0 self.cur_pitch = 0 # Note Scheduler self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.mixer.add(self.sched) self.sched.set_generator(self.synth) # Note Sequencers self.seq = [] # live Generator self.live_wave = None def on_update(self): self.audio.on_update() self.anim_group.on_update() self.info.text = 'fps:%d\n' % kivyClock.get_fps() self.info.text += 'load:%.2f\n' % self.audio.get_cpu_load() self.info.text += "pitch: %.1f\n" % self.cur_pitch self.info.text += 'max delta: %.3f\n' % self.onset_detector.get_max_delta( ) self.info.text += 'onset delta thresh (up/down): %.3f\n' % self.onset_detector.onset_thresh if self.recorder.active: self.info.text += 'RECORDING' def receive_audio(self, frames, num_channels): assert (num_channels == 1) # Microphone volume level, take RMS, convert to dB. # display on meter and graph rms = np.sqrt(np.mean(frames**2)) rms = np.clip(rms, 1e-10, 1) # don't want log(0) db = 20 * np.log10(rms) # convert from amplitude to decibels self.mic_meter.set(db) self.mic_graph.add_point(db) # pitch detection: get pitch and display on meter and graph self.cur_pitch = self.pitch.write(frames) self.pitch_meter.set(self.cur_pitch) self.pitch_graph.add_point(self.cur_pitch) # record audio self.recorder.add_audio(frames, num_channels) # onset detection and classification self.onset_detector.write(frames) def init_recording(self): data = self.recorder.toggle() if data: print(data) wave_gen, filename, duration_midi = data for i in range(len(duration_midi)): if duration_midi[i][0] < 0.12: duration_midi[i] = (duration_midi[i][0], 0) duration_midi = harmony.harmonize(duration_midi) self.live_wave = wave_gen print([[i[1] for i in j] for j in duration_midi]) tempo = 120 multiplier = 1 / 60 * tempo * 480 converted_midi_duration = [[(i * multiplier, j) for i, j in k] for k in duration_midi] for i in converted_midi_duration: self.seq.append( NoteSequencer(self.sched, self.synth, 1, (0, 0), i, True)) def play_recording(self): print("hello") for i in self.seq: i.start() if self.live_wave: self.mixer.add(self.live_wave) def on_key_down(self, keycode, modifiers): t = lookup(keycode[1], ['up', 'down'], [.001, -.001]) if t is not None: self.onset_detector.onset_thresh += t if keycode[1] == "w": self.init_recording() if keycode[1] == "s" and self.seq: self.play_recording()
def __init__(self, level, notes, goal_values, gear_values, **kwargs): super(LevelEasyMediumScreen, self).__init__(**kwargs) # set up notes for the level self.notes = notes # set up gear values for the levels self.goal_values = goal_values # set up gear values for the levels self.gear_values = gear_values self.level = level # only turn on tutorial for level 1 if self.level == 1: self.use_tutorial = True self.tutorial_screen = 'A' self.goal_play_status = None self.gear_play_status = None self.size_dim = min(Window.width / 6, Window.height / 6) self.tutorial_full_overlay = CRectangle(cpos=(Window.width / 2, Window.height / 2), csize=(Window.width, Window.height)) self.tutorial_options_overlay = Rectangle( pos=(0, 0), size=(Window.width, self.size_dim + Window.height / 25)) self.tutorial_musicbox_overlay = Rectangle( pos=(Window.width // 2, self.size_dim + Window.height / 25), size=(Window.width / 2, Window.height - (self.size_dim + Window.height / 25))) self.tutorial_gearbox_overlay = Rectangle( pos=(0, self.size_dim + Window.height / 25), size=(Window.width / 2, Window.height - (self.size_dim + Window.height / 25))) self.skip_image = CoreImage('images/skip_tutorial.png') self.tutorial_skip_button = Rectangle( pos=(0.98 * Window.width - (self.skip_image.width * self.size_dim / 300), 0.98 * Window.height - self.skip_image.height * self.size_dim / 300), size=(self.skip_image.width * self.size_dim / 300, self.skip_image.height * self.size_dim / 300), texture=self.skip_image.texture) else: self.use_tutorial = False ############################################ ### GOAL MUSIC ### ############################################ self.goal_audio = Audio(2) self.goal_synth = Synth('./data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.goal_tempo_map = SimpleTempoMap(120) self.goal_sched = AudioScheduler(self.goal_tempo_map) # connect scheduler into audio system self.goal_audio.set_generator(self.goal_sched) self.goal_sched.set_generator(self.goal_synth) # generate goal music self.goal_music = MusicPlayer(notes=self.notes, sched=self.goal_sched, synth=self.goal_synth, channel=1, tempo_map=self.goal_tempo_map) self.goal_music.update_tempo(self.goal_values[0]) self.goal_music.update_instrument(self.goal_values[1]) self.goal_music.update_pitch(self.goal_values[2]) self.goal_music.update_volume(self.goal_values[3]) self.goal_music_seq = self.goal_music.generate() ############################################ ### GEAR MUSIC ### ############################################ self.gear_audio = Audio(2) self.gear_synth = Synth('./data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.gear_tempo_map = SimpleTempoMap(120) self.gear_sched = AudioScheduler(self.gear_tempo_map) # connect scheduler into audio system self.gear_audio.set_generator(self.gear_sched) self.gear_sched.set_generator(self.gear_synth) # generate gear music self.gear_music = MusicPlayer(notes=self.notes, sched=self.gear_sched, synth=self.gear_synth, channel=1, tempo_map=self.gear_tempo_map) self.gear_music_seq = None ############################################ ### BACKGROUND UI COMPONENTS ### ############################################ self.gear_area = GearArea() self.canvas.add(self.gear_area) self.music_box_area = MusicBoxArea() self.canvas.add(self.music_box_area) self.options = LevelOptions( level=level, goal_music_seq=self.goal_music_seq, duration=self.goal_music.duration, edit_goal_play_status=self.edit_goal_play_status) self.canvas.add(self.options) self.label = Label(text=kwargs['name'], font_name='./fonts/PassionOne-Regular', color=(.165, .718, .792, 1)) self.add_widget(self.label) ########################################### ### GEAR LABELS ### ########################################### self.tempo_label = Label(text='Tempo (bpm)', font_name='./fonts/PassionOne-Regular', color=(0.7254901960784313, 0.5529411764705883, 0.8196078431372549, 1), center_x=(Window.width / 4), center_y=(Window.height / 5.25 * (0.5 + 0.5) + self.gear_area.position[1]), font_size=str(Window.width // 50) + 'sp') self.instrument_label = Label( text='Instrument', font_name='./fonts/PassionOne-Regular', color=(0.996078431372549, 0.8431372549019608, 0.4, 1), center_x=(Window.width / 4), center_y=(Window.height / 5.25 * (1.5 + 0.5) + self.gear_area.position[1]), font_size=str(Window.width // 50) + 'sp') self.pitch_label = Label(text='Pitch (semitones)', font_name='./fonts/PassionOne-Regular', color=(1.0, 0.6509803921568628, 0.09019607843137255, 1), center_x=(Window.width / 4), center_y=(Window.height / 5.25 * (2.5 + 0.5) + self.gear_area.position[1]), font_size=str(Window.width // 50) + 'sp') self.volume_label = Label( text='Volume', font_name='./fonts/PassionOne-Regular', color=(0.9254901960784314, 0.32941176470588235, 0.3176470588235294, 1), center_x=(Window.width / 4), center_y=(Window.height / 5.25 * (3.5 + 0.5) + self.gear_area.position[1]), font_size=str(Window.width // 50) + 'sp') self.add_widget(self.volume_label) self.add_widget(self.pitch_label) self.add_widget(self.instrument_label) self.add_widget(self.tempo_label) ########################################### ### GEAR CONSTRUCTION ### ########################################### self.gears = [] self.gear_centers = [] self.gears_group = AnimGroup() self.canvas.add(self.gears_group) ########################################### ### GEARS ### ########################################### self.gear_storage_locations = [] self.gear_music_locations = [] self.gear_labels = [] self.set_up_gears() ########################################### ### PARTICLE EFFECT ### ########################################### self.win_ps = ParticleSystem('particles/star_particle.pex') self.win_ps.emitter_x = Window.width / 2 self.win_ps.emitter_y = Window.height / 2 self.add_widget(self.win_ps) self.you_win_label = Label(text=' ', font_name='./fonts/PassionOne-Regular', color=(0.5843137254901961, 0.796078431372549, 0.37254901960784315, 1), center_x=Window.width / 2, center_y=Window.height / 2, font_size=str(Window.width // 10) + 'sp') self.add_widget(self.you_win_label) self.lose_ps = ParticleSystem('particles/lose_particle.pex') self.lose_ps.emitter_x = Window.width / 2 self.lose_ps.emitter_y = Window.height / 2 self.add_widget(self.lose_ps) self.you_lose_label = Label(text=' ', font_name='./fonts/PassionOne-Regular', color=(0.9254901960784314, 0.32941176470588235, 0.3176470588235294, 1), center_x=Window.width / 2, center_y=Window.height / 2, font_size=str(Window.width // 10) + 'sp') self.add_widget(self.you_lose_label) ########################################### ### ERROR MESSAGE ### ########################################### self.error_msg = Label(text=' ', font_name='./fonts/PassionOne-Regular', color=(0.9254901960784314, 0.32941176470588235, 0.3176470588235294, 1), center_x=Window.width / 2, center_y=Window.height / 2, font_size=str(Window.width // 20) + 'sp') self.add_widget(self.error_msg) # ########################################### # ### ADD TUTORIAL OVERLAYS ### # ########################################### if self.use_tutorial: self.canvas.add(Color(rgba=(0, 0, 0, 0.85))) self.canvas.add(self.tutorial_full_overlay) self.tutorial_label = Label( markup=True, text= "[font=./fonts/lato-bold]Welcome to the\n[/font] [font=./fonts/options-icons]|[/font] [font=./fonts/PassionOne-Regular]Play It By Gear Tutorial[/font] [font=./fonts/options-icons]|[/font] [font=./fonts/lato-bold]\n\nThe goal of this game is to make the \n goal song match the song you create by \nplacing the correct gears in a music box \n\n[/font] [font=./fonts/lato-light] (click to see the next \nstep of the tutorial)[/font]", color=(86 / 255, 189 / 255, 205 / 255, 1), center_x=Window.width / 2, center_y=Window.height / 2, font_size=str(Window.width // 40) + 'sp', halign='center') self.add_widget(self.tutorial_label) self.canvas.add(self.tutorial_skip_button) self.on_layout((Window.width, Window.height))
def __init__(self): super(IntroScreen, self).__init__() self.genre_popup = CheckboxPopup(self.genre_callback, "GENRE", GENRE_CHECKBOXES) self.volume_popup = VolumePopup(self.slider_callback) self.record_popup = RecordPopup(self.init_recording, self.toggle_playing) self.instruments_popup = CheckboxPopup(self.instrument_callback, "INSTRUMENTS", INSTRUMENT_CHECKBOXES) self.storage_popup = StoragePopup(self.get_live_wave, self.set_live_wave) self.audio = Audio(2, input_func=self.receive_audio, num_input_channels=1) self.mixer = Mixer() self.audio.set_generator(self.mixer) self.pitch = PitchDetector() self.recorder = VoiceAudioWriter('data') self.playing = False self.recording = False self.cmd = None self.scene = Scene() self.add_widget(self.scene) self.scene.foreground.radio.set_callback(self.genre_popup.open) self.scene.foreground.amp.set_callback(self.volume_popup.open) self.scene.foreground.mic.set_callback(self.record_popup.open) self.scene.foreground.guitar.set_callback(self.instruments_popup.open) self.scene.foreground.storage.set_callback(self.storage_popup.open) self.cur_pitch = 0 self.midi_notes = None self.bass = [((40, 60), (0, 0)), ((43, 64), (0, 42)), ((28, 48), (0, 33))] self.tenor = [((52, 69), (0, 0)), ((52, 69), (0, 41)), ((45, 64), (0, 26))] self.alto = [((57, 77), (0, 0)), ((60, 79), (0, 40)), ((52, 72), (0, 29)), ((67, 86), (0, 73))] self.instruments = [self.bass, self.tenor, self.alto] self.genre = 'pop' self.indices = [0, 0, 0] # Note Scheduler self.synth = Synth('data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) self.metro = Metronome(self.sched, self.synth) self.start_tick = None # connect scheduler into audio system self.mixer.add(self.sched) self.sched.set_generator(self.synth) # Note Sequencers self.seq = [None, None, None] # live Generator self.live_wave = None # current .wav file self.current_wave_file = None
class IntroScreen(BaseWidget): image = "data/bedroom.jpg" def __init__(self): super(IntroScreen, self).__init__() self.genre_popup = CheckboxPopup(self.genre_callback, "GENRE", GENRE_CHECKBOXES) self.volume_popup = VolumePopup(self.slider_callback) self.record_popup = RecordPopup(self.init_recording, self.toggle_playing) self.instruments_popup = CheckboxPopup(self.instrument_callback, "INSTRUMENTS", INSTRUMENT_CHECKBOXES) self.storage_popup = StoragePopup(self.get_live_wave, self.set_live_wave) self.audio = Audio(2, input_func=self.receive_audio, num_input_channels=1) self.mixer = Mixer() self.audio.set_generator(self.mixer) self.pitch = PitchDetector() self.recorder = VoiceAudioWriter('data') self.playing = False self.recording = False self.cmd = None self.scene = Scene() self.add_widget(self.scene) self.scene.foreground.radio.set_callback(self.genre_popup.open) self.scene.foreground.amp.set_callback(self.volume_popup.open) self.scene.foreground.mic.set_callback(self.record_popup.open) self.scene.foreground.guitar.set_callback(self.instruments_popup.open) self.scene.foreground.storage.set_callback(self.storage_popup.open) self.cur_pitch = 0 self.midi_notes = None self.bass = [((40, 60), (0, 0)), ((43, 64), (0, 42)), ((28, 48), (0, 33))] self.tenor = [((52, 69), (0, 0)), ((52, 69), (0, 41)), ((45, 64), (0, 26))] self.alto = [((57, 77), (0, 0)), ((60, 79), (0, 40)), ((52, 72), (0, 29)), ((67, 86), (0, 73))] self.instruments = [self.bass, self.tenor, self.alto] self.genre = 'pop' self.indices = [0, 0, 0] # Note Scheduler self.synth = Synth('data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) self.metro = Metronome(self.sched, self.synth) self.start_tick = None # connect scheduler into audio system self.mixer.add(self.sched) self.sched.set_generator(self.synth) # Note Sequencers self.seq = [None, None, None] # live Generator self.live_wave = None # current .wav file self.current_wave_file = None def genre_callback(self, value, label): self.genre = value if value == 'classical': self.instruments_popup.set_checkboxes(ORCHESTRA) self.indices = [1, 1, 1] self.instrument_callback(None, None) if value == 'pop': self.instruments_popup.set_checkboxes(POP) self.indices = [2, 2, 0] self.instrument_callback(None, None) def instrument_callback(self, value, label): if label == 'high voice': self.indices[2] = ['piano', 'violin', 'guitar', 'flute'].index(value) if label == 'mid voice': self.indices[1] = ['piano', 'viola', 'guitar'].index(value) if label == 'low voice': self.indices[0] = ['piano', 'cello', 'bass'].index(value) if self.live_wave is not None: for i in self.seq: i.stop() self.live_wave.reset() #reharmonize and update NoteSequencers duration_midi = harmony.harmonize( self.midi_notes, self.genre, brange=self.bass[self.indices[0]][0], trange=self.tenor[self.indices[1]][0], arange=self.alto[self.indices[2]][0]) tempo = self.tempo_map.get_tempo() multiplier = 1 / 60 * tempo * 480 converted_midi_duration = [[(i * multiplier, j) for i, j in k] for k in duration_midi] for i in range(3): self.seq[i] = NoteSequencer( self.sched, self.synth, i + 1, self.instruments[i][self.indices[i]][1], converted_midi_duration[i + 1], self.scene.add_note_sprite, True) if self.playing: self.play_recording(1) def slider_callback(self, voice, value): val = int(value) idx = ["bass", "tenor", "alto", "melody"].index(voice) + 1 if idx < 4: self.synth.cc(idx, 7, val) else: if self.live_wave: self.live_wave.set_gain(val / 100) def on_update(self): self.audio.on_update() self.scene.on_update() def on_key_down(self, keycode, modifiers): if keycode[1] == 'm': self.metro.toggle() bpm_adj = lookup(keycode[1], ('up', 'down'), (10, -10)) if bpm_adj and not self.playing and not self.recording: new_tempo = max(self.tempo_map.get_tempo() + bpm_adj, 30) self.tempo_map.set_tempo(new_tempo, self.sched.get_time()) def receive_audio(self, frames, num_channels): assert (num_channels == 1) # Microphone volume level, take RMS, convert to dB. # display on meter and graph rms = np.sqrt(np.mean(frames**2)) rms = np.clip(rms, 1e-10, 1) # don't want log(0) db = 20 * np.log10(rms) # convert from amplitude to decibels self.record_popup.mic_meter.set(db) self.record_popup.mic_graph.add_point(db) # pitch detection: get pitch and display on meter and graph self.cur_pitch = self.pitch.write(frames) self.record_popup.pitch_meter.set(self.cur_pitch) self.record_popup.pitch_graph.add_point(self.cur_pitch) # record audio self.recorder.add_audio(frames, num_channels) def init_recording(self): if not self.recording: self.start_tick = self.sched.get_tick() data = self.recorder.toggle() if not data: self.recording = True if self.live_wave is not None: try: self.mixer.remove(self.live_wave) except: pass for i in self.seq: if i is not None: i.stop() self.playing = False else: self.recording = False stop_tick = self.sched.get_tick() wave_gen, filename, duration_midi = data self.current_wave_file = WaveFile(filename) #ignore short notes i = 0 while i < len(duration_midi): if duration_midi[i][0] < 0.1: duration_midi[i - 1] = (duration_midi[i][0] + duration_midi[i - 1][0], duration_midi[i - 1][1]) duration_midi.pop(i) else: i += 1 duration_midi[0] = (duration_midi[0][0] - .1, duration_midi[0][1]) ticks = [(int(note[0] * 480 * self.tempo_map.get_tempo() / 60), note[1]) for note in duration_midi] ticks[0] = (ticks[0]) duration_midi = [] tick_length = sum(i[0] for i in ticks) curr_beat = int(480 - self.start_tick % 480 + .22 * 8 * self.tempo_map.get_tempo()) % 480 ind = 0 ticks_passed = 0 while tick_length > 0: tot = 0 times = {} while tot < curr_beat and ind < len(ticks): left = ticks[ind][0] - ticks_passed if left > curr_beat - tot: ticks_passed += curr_beat - tot if ticks[ind][1] in times: times[ticks[ind][1]] += curr_beat - tot else: times[ticks[ind][1]] = curr_beat - tot tot = curr_beat else: tot += left ticks_passed = 0 if ticks[ind][1] in times: times[ticks[ind][1]] += left else: times[ticks[ind][1]] = left ind += 1 big = 80 note = 0 print(times) for guy in times: if times[guy] > big and guy != 0: note = guy big = times[guy] duration_midi.append( (60 * curr_beat / 480 / self.tempo_map.get_tempo(), note)) tick_length -= curr_beat curr_beat = min(480, tick_length) duration_midi = [(0.1, 0)] + duration_midi self.midi_notes = duration_midi #find harmonies self.live_wave = wave_gen good = False for i in duration_midi: if i[1] > 0: good = True break if good: duration_midi = harmony.harmonize( duration_midi, self.genre, brange=self.bass[self.indices[0]][0], trange=self.tenor[self.indices[1]][0], arange=self.alto[self.indices[2]][0]) #print([[i[1] for i in j] for j in duration_midi]) # cheat to use SimpleTempoMap tempo = self.tempo_map.get_tempo() multiplier = 1 / 60 * tempo * 480 converted_midi_duration = [[(i * multiplier, j) for i, j in k] for k in duration_midi] #make NoteSequencers for i in range(3): self.seq[i] = NoteSequencer( self.sched, self.synth, i + 1, self.instruments[i][self.indices[i]][1], converted_midi_duration[i + 1], self.scene.add_note_sprite, True) def play_recording(self, tick): for i in self.seq: if i is not None: i.start() if self.live_wave: self.live_wave.play() if self.live_wave not in self.mixer.generators: self.mixer.add(self.live_wave) def start_playing(self): if self.playing: return self.metro.stop() self.playing = True now = self.sched.get_tick() next_beat = quantize_tick_up(now, kTicksPerQuarter * 4) self.cmd = self.sched.post_at_tick(self.play_recording, next_beat) def stop_playing(self): if not self.playing: return self.playing = False for i in self.seq: i.stop() self.live_wave.reset() self.sched.cancel(self.cmd) self.cmd = None def toggle_playing(self): print(self.playing) if self.playing: self.stop_playing() else: self.start_playing() def get_live_wave(self): if self.live_wave: return WaveGenerator(self.current_wave_file, True), self.seq.copy() def set_live_wave(self, new_live_wave, note_sequencers): if self.live_wave: if self.live_wave is not None: try: self.mixer.remove(self.live_wave) except: pass for i in self.seq: if i is not None: i.stop() self.seq = note_sequencers for i in self.seq: if i is not None: i.start() self.live_wave = new_live_wave self.mixer.add(self.live_wave) self.start_playing()
def __init__(self): super(MainWidget, self).__init__() self.audio = Audio(2, input_func=self.receive_audio, num_input_channels=1) self.mixer = Mixer() self.audio.set_generator(self.mixer) self.pitch = PitchDetector() self.recorder = VoiceAudioWriter('data') self.info = topleft_label() self.add_widget(self.info) self.anim_group = AnimGroup() self.mic_meter = MeterDisplay((50, 25), 150, (-96, 0), (.1, .9, .3)) self.mic_graph = GraphDisplay((110, 25), 150, 300, (-96, 0), (.1, .9, .3)) self.pitch_meter = MeterDisplay((50, 200), 150, (30, 90), (.9, .1, .3)) self.pitch_graph = GraphDisplay((110, 200), 150, 300, (30, 90), (.9, .1, .3)) self.canvas.add(self.mic_meter) self.canvas.add(self.mic_graph) self.canvas.add(self.pitch_meter) self.canvas.add(self.pitch_graph) # Record button self.record_button = InteractiveImage() self.record_button.source = "../data/mic.png" self.record_button.x = 400 self.record_button.y = 400 self.record_button.size = (100, 100) self.record_button.set_callback(self.init_recording) self.add_widget(self.record_button) # Play button self.play_button = InteractiveImage() self.play_button.source = "../data/play.png" self.play_button.x = 600 self.play_button.y = 400 self.play_button.size = (100, 100) self.play_button.set_callback(self.play_recording) self.add_widget(self.play_button) self.canvas.add(self.anim_group) self.onset_disp = None self.onset_x = 0 self.cur_pitch = 0 # Note Scheduler self.synth = Synth('../data/FluidR3_GM.sf2') # create TempoMap, AudioScheduler self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) # connect scheduler into audio system self.mixer.add(self.sched) self.sched.set_generator(self.synth) # Note Sequencers self.seq = [] # live Generator self.live_wave = None
class PuzzleSound(object): def __init__(self, notes, bank=0, preset=0, loop=False, simon_says=False, bass_puzzle=False): super().__init__() self.audio = Audio(2) self.synth = Synth("./data/FluidR3_GM.sf2") self.tempo_map = SimpleTempoMap(120) self.sched = AudioScheduler(self.tempo_map) self.sched.set_generator(self.synth) self.audio.set_generator(self.sched) self.notes = notes self.bank = bank self.preset = preset self.loop = loop self.simon_says = simon_says self.bass_puzzle = bass_puzzle self.note_seq = NoteSequencer( sched=self.sched, synth=self.synth, channel=1, program=(self.bank, self.preset), notes=self.notes, loop=self.loop, ) def set_notes(self, notes): self.notes = notes if self.simon_says or self.bass_puzzle: self.note_seq = NoteSequencer( sched=self.sched, synth=self.synth, channel=1, program=(self.bank, self.preset), notes=self.notes, loop=self.loop, ) self.note_seq.stop() self.note_seq.set_notes(self.notes) if self.bank == 0: self.letters = [n.get_letter() for n in self.notes] def set_cb_ons(self, cb_ons): self.note_seq.set_cb_ons(cb_ons) def set_cb_offs(self, cb_offs): self.note_seq.set_cb_offs(cb_offs) def set_on_finished(self, on_finished): self.note_seq.set_on_finished(on_finished) def toggle(self): self.note_seq.toggle() def on_update(self): self.audio.on_update()