def __init__(self, sched): super(InstrumentManager, self).__init__() self.synth = Synth("data/FluidR3_GM.sf2") self.add_generator(self.synth) self.metro = Metronome(sched, self.synth) self.inst_count = 1 self.instruments = []
class InstrumentManager(GameObject): def __init__(self, sched): super(InstrumentManager, self).__init__() self.synth = Synth("data/FluidR3_GM.sf2") self.add_generator(self.synth) self.metro = Metronome(sched, self.synth) self.inst_count = 1 self.instruments = [] # Adds instrument to manager. Each instrument is added to # a new channel. def add(self, ins): self.inst_count += 1 self.instruments.append(ins) ins.manager = self ins.channel = self.inst_count self.synth.program(self.inst_count, *ins.patch) def program(self, ins): self.synth.program(ins.channel, *ins.patch) def noteon(self, *args): self.synth.noteon(*args) def noteoff(self, *args): self.synth.noteoff(*args)
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 __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(MainWidget1, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') self.audio.set_generator(self.synth) self.label = topleft_label() self.add_widget(self.label) cc_range = range(0, 128, 8) self.modifier = Modifier() self.modifier.add('a', "program", range(128), lambda x: self.synth.program_change(0, x)) self.modifier.add('s', "vibrato", cc_range, lambda x: self.synth.cc(0, 1, x)) self.modifier.add('d', "volume", cc_range, lambda x: self.synth.cc(0, 7, x)) self.modifier.add('f', "sustain", (0, 127), lambda x: self.synth.cc(0, 64, x)) self.modifier.add('z', "bend", range(-8192, 8192, 256), lambda x: self.synth.pitch_bend(0, int(x)))
class MainWidget1(BaseWidget): def __init__(self): super(MainWidget1, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') self.audio.set_generator(self.synth) self.label = topleft_label() self.add_widget(self.label) cc_range = range(0, 128, 8) self.modifier = Modifier() self.modifier.add('a', "program", range(128), lambda x: self.synth.program_change(0, x)) self.modifier.add('s', "vibrato", cc_range, lambda x: self.synth.cc(0, 1, x)) self.modifier.add('d', "volume", cc_range, lambda x: self.synth.cc(0, 7, x)) self.modifier.add('f', "sustain", (0, 127), lambda x: self.synth.cc(0, 64, x)) self.modifier.add('z', "bend", range(-8192, 8192, 256), lambda x: self.synth.pitch_bend(0, int(x))) def on_key_down(self, keycode, modifiers): self.modifier.on_key_down(keycode[1]) pitch = lookup(keycode[1], kKeys, kPitches) if pitch: self.synth.noteon(0, pitch, 100) def on_key_up(self, keycode): self.modifier.on_key_up(keycode[1]) pitch = lookup(keycode[1], kKeys, kPitches) if pitch: self.synth.noteoff(0, pitch) def on_update(self): self.audio.on_update() self.modifier.on_update() self.label.text = self.modifier.get_txt() + '\n'
def __init__(self, songPattern, key, clock): self.slack_timout = beats_to_time(.5, self.bpm) self.gems, self.bars = self._initialize_bars(songPattern, key) self.synth = Synth('../bank/FluidR3_GM.sf2') totalBeats = self.measuresPerCall * self.numRepeats * len( self.bars) * 4 totalTime = beats_to_time(totalBeats, self.bpm) totalOffsetTicks = self.endBarTicks * totalBeats / 4 totalTicks = totalBeats / 4 * self.barLenTicks data = [(0, 0), ((totalTime + ticks_to_time(totalOffsetTicks, self.bpm)) * 2, totalTicks * 2)] self.tempo = TempoMap(data=data) self.num_bars = len(data) self.scheduler = Scheduler(clock, self.tempo) self.increment_bar = None self.catch_passes = None self.on_commands = [] # commands for a bar of call and response self.off_commands = [] self.active_gems = [] self.gem_commands = [] self.bar_tick = 0 self.bar_index = 0 print(songPattern)
def __init__(self): super(MainWidget4, self).__init__() self.audio = Audio(2) self.synth = Synth('../data/FluidR3_GM.sf2') self.audio.set_generator(self.synth) # create clock, tempo_map, scheduler self.clock = Clock() self.tempo_map = SimpleTempoMap(120) self.sched = Scheduler(self.clock, self.tempo_map) # 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 __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)
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 __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 __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 Ticker(object): ''' Maps beat patterns to ticks (time stamps) and plays the call of the call and response (if enabled) This class assumes quarter notes as beats. becase every loop in this game is 4 measures, we will assume a loop to last 16 beats. (4/4) ''' bpm = 400 numRepeats = 3 measuresPerCall = 1 channel = 0 vel = 80 playQueues = 1 noteDuration = 1 nowBarHeight = Window.height // 2 endBarTicks = kTicksPerQuarter * .5 barLenTicks = kTicksPerQuarter * 4 + endBarTicks gemRadius = 100 padding = gemRadius + 50 def __init__(self, songPattern, key, clock): self.slack_timout = beats_to_time(.5, self.bpm) self.gems, self.bars = self._initialize_bars(songPattern, key) self.synth = Synth('../bank/FluidR3_GM.sf2') totalBeats = self.measuresPerCall * self.numRepeats * len( self.bars) * 4 totalTime = beats_to_time(totalBeats, self.bpm) totalOffsetTicks = self.endBarTicks * totalBeats / 4 totalTicks = totalBeats / 4 * self.barLenTicks data = [(0, 0), ((totalTime + ticks_to_time(totalOffsetTicks, self.bpm)) * 2, totalTicks * 2)] self.tempo = TempoMap(data=data) self.num_bars = len(data) self.scheduler = Scheduler(clock, self.tempo) self.increment_bar = None self.catch_passes = None self.on_commands = [] # commands for a bar of call and response self.off_commands = [] self.active_gems = [] self.gem_commands = [] self.bar_tick = 0 self.bar_index = 0 print(songPattern) def reset(self): #TODO for i in range(len(self.num_bars)): self.clear_bar(i) self.bar_index = 0 def create_bar(self, barIndex): self.bar_tick = quantize_tick_up(self.scheduler.get_tick()) self.active_gems = self.gems[barIndex] # print(list(map(lambda x: x.beat, self.active_gems))) # [gem.activate() for gem in self.active_gems] self._initializeBarAudio(barIndex) self._initializeBarGems(barIndex) # self.increment_bar def clear_bar(self, barIndex): self._clearBarAudio(barIndex) self._clearBarGems() def getRelativeTick(self): tick = quantize_tick_up(self.scheduler.get_tick()) tick = (tick - self.bar_tick) % self.barLenTicks return tick def getTick(self): return quantize_tick_up(self.scheduler.get_tick()) def getTargetGem(self): tick = self.getRelativeTick() beatApprox = (tick / self.barLenTicks) * 4 #find gem by tick targetGem = None minDist = 9999999999 for gem in self.active_gems: if gem.hit or gem.miss: # print("skipping hit gem (target) ", tick) continue gemDist = min(abs(gem.beat - beatApprox), abs(beatApprox - gem.beat)) if gemDist < minDist: minDist = gemDist targetGem = gem # print("minimum dist: ", minDist) # print("targetGem: ", gem.beat) # print("beat approx: ", beatApprox) return targetGem def on_update(self): self.scheduler.on_update() tick = self.getTick() ticksEllapsed = (tick - self.bar_tick) % (self.barLenTicks * 4) if ticksEllapsed <= self.barLenTicks: return "call" elif ticksEllapsed <= self.numRepeats * self.barLenTicks: return "response" else: # print("tick %f, ticksEll %f, barTick %f, barLen %f " % (tick, ticksEllapsed, self.bar_tick, self.barLenTicks)) print('nextStatus') return "next" def bars_remaining(self): tick = self.getTick() ticksEllapsed = (tick - self.bar_tick) % (self.barLenTicks * 4) barNum = round((self.numRepeats * self.barLenTicks - ticksEllapsed) / self.barLenTicks) barsRemaining = np.clip(self.numRepeats - barNum - 1, 0, self.numRepeats - 1) return barsRemaining def _initialize_bars(self, pattern, key): gem_bars = [] chord_bars = [] for bar in pattern: numGems = len(bar) assert numGems > 1 w = (Window.width) // 5 x = self.padding y = self.nowBarHeight chords_and_ticks = [] gems = [] for b in bar: assert b[1] <= self.measuresPerCall * 4 and b[1] > 0 chord = key.generateChord(b[0]) chords_and_ticks.append((chord, b[1])) x = w * b[1] gem = Gem(chord, (x, y), self.gemRadius, self.slack_timout, b[1]) gems.append(gem) chord_bars.append(chords_and_ticks) gem_bars.append(gems) return gem_bars, chord_bars def _initializeBarAudio(self, barIndex): bar_tick = int(self.bar_tick) bar = self.bars[barIndex] # print(self.bar_tick) assert self.numRepeats >= self.playQueues for i in range(self.playQueues): for chord, beat in bar: tick = bar_tick + beat * kTicksPerQuarter # print('audio ', tick) self.on_commands.append( self.scheduler.post_at_tick(self._playChord, tick, chord)) self.off_commands.append( self.scheduler.post_at_tick( self._endChord, tick + kTicksPerQuarter * self.noteDuration, chord)) bar_tick += self.barLenTicks def _clearBarAudio(self, barIndex): for c in self.on_commands: self.scheduler.remove(c) for c in self.off_commands: self.scheduler.remove(c) c.execute() def _initializeBarGems(self, barIndex): slackWinOffset = quantize_tick_up( float(self.slack_timout) / 2 * kTicksPerQuarter) bar_tick = int(self.bar_tick) bar = self.gems[barIndex] for i in range(self.numRepeats): for gem in bar: tick = bar_tick + gem.beat * kTicksPerQuarter if i > 0: self.gem_commands.append( self.scheduler.post_at_tick(self._startGemTimer, tick - slackWinOffset, gem)) ticks_to_time(tick - slackWinOffset, self.bpm) bar_tick += self.barLenTicks self.gem_commands.append( self.scheduler.post_at_tick(self._onCompleteMeasure, bar_tick)) def _refreshBarGems(self): for gem in self.active_gems: gem.on_reset() # gem.activate() def _clearBarGems(self): for gem in self.active_gems: # pass gem.exit() def _onCompleteMeasure(self, tick, temp=None): allHit = True for gem in self.active_gems: allHit = allHit and gem.hit if allHit and not self.on_update() == "call": self.increment_bar(perfect=allHit) print('increment bar') else: print("measure over, resetting gems - %f" % self.getRelativeTick()) self.catch_passes(True) self._refreshBarGems() def _startGemTimer(self, tick, gem): ''' starts the gem timer''' # print("start gem timer %f " % self.getRelativeTick()) if not gem.hit: gem.activate() def _playChord(self, tick, chord): for note in chord._getMidiTones(): self.synth.noteon(self.channel, note, self.vel) def _endChord(self, tick, chord): for note in chord._getMidiTones(): self.synth.noteoff(self.channel, note) def initialize_callbacks(self, increment, catch_passes): self.increment_bar = increment self.catch_passes = catch_passes
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(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 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)