コード例 #1
0
ファイル: main_ui.py プロジェクト: abcdefguan/piano_game
    def __init__(self, note_img, player, key_input, scores, fps):
        self.fps = fps
        self.note_img_cache = note_img
        self.player = player
        self.key_input = key_input
        self.scores = scores

        self.quit = False
        training_btn = Btn("Training", (250, 40), on_click = \
         self.on_training_btn_click)
        game_btn = Btn("Play", (250, 80), on_click = \
         self.on_play_btn_click)
        piano_btn = Btn("Piano", (250, 120), on_click = \
         self.on_piano_btn_click)
        exit_btn = Btn("Quit", (250, 200), on_click = \
         self.on_exit_btn_click)
        piano_game_txt = Text("Piano Game", (100, 40))
        dev_by_txt = Text("By Guanqun Wu, Zhaopeng Xu", (100, 80), \
         font_size = 20)

        self.stage = Stage()
        self.stage.add_btn(training_btn)
        self.stage.add_btn(game_btn)
        self.stage.add_btn(piano_btn)
        self.stage.add_btn(exit_btn)
        self.stage.add_elt(piano_game_txt)
        self.stage.add_elt(dev_by_txt)
コード例 #2
0
	def __init__(self, wrong_notes, early_notes, timing, score, fps):
		#Various settings
		self.stage = Stage()

		self.colors = {}
		self.colors["blue"] = (39, 117, 242)
		self.colors["black"] = (0, 0, 0)
		self.colors['green'] = (14, 230, 71)
		self.colors['red'] = (224, 9, 9)

		self.quit = False
		self.stage = Stage()
		self.exit_btn = Btn("Exit", (40, 200), on_click = \
			self.on_exit_btn_click)
		expected_dur = 0.0
		num_notes = 0
		total_bars = score.get_total_bars()
		for i in range(total_bars):
			curr_bar = score.get_bar(i)
			expected_dur += curr_bar.get_length() * 60.0 * fps \
			/ curr_bar.get_bpm()
			num_notes += len(curr_bar.get_treble())
			num_notes += len(curr_bar.get_bass())
		used_dur = float(timing)
		pct_time = used_dur / expected_dur * 100
		pct_early = float(early_notes) / num_notes * 100
		pct_wrong = float(wrong_notes) / num_notes * 100
		wrong_notes_txt = Text("Wrong Notes: {}".format(wrong_notes), \
			(20, 20), centering = "topleft")
		early_notes_txt = Text("Early Notes: {0:} ({1:.2f}%)"\
			.format(early_notes, pct_early), (20, 50), centering = "topleft")
		timing_txt = Text("Time Used: {0:.2f}s ({1:.2f}%)" \
			.format(used_dur / fps, pct_time), \
			(20, 80), centering = "topleft")
		expected_time = Text("Expected: {0:.2f}s".format(expected_dur / fps), \
			(20, 110), centering = "topleft")
		#cutoff (pct_wrong, pct_early, pct_time)
		grade_cutoff = [('S', 1.0, 1.0, 110.0), \
		('A', 5.0, 5.0, 125.0), \
		('B', 15.0, 15.0, 140.0), \
		('C', 30.0, 30.0, 180.0), \
		('D', 50.0, 50.0, 200.0)]
		grade = 'F'
		for (cutoff_grade, wrong_cutoff, early_cutoff, time_cutoff) \
		in grade_cutoff:
			if pct_wrong <= wrong_cutoff and pct_early <= early_cutoff and \
			pct_time <= time_cutoff:
				grade = cutoff_grade
				break
		#Assign grade
		grade_txt = Text("{}".format(grade), \
			(160, 140), centering = "center", font_size = 48)

		self.stage.add_btn(self.exit_btn)
		self.stage.add_elt(wrong_notes_txt)
		self.stage.add_elt(early_notes_txt)
		self.stage.add_elt(timing_txt)
		self.stage.add_elt(grade_txt)
コード例 #3
0
class PianoMode:
    def __init__(self, player, key_input):
        self.stage = Stage()
        self.player = player
        self.key_input = key_input
        self.playable_pitches = key_input.get_playable_pitches()
        #Set of currently played pitches
        self.played_pitches = set()
        #Various parameters
        self.quit = False
        #Construct buttons
        self.exit_btn = Btn("Exit", (40, 200), on_click = \
         self.on_exit_btn_click)
        self.piano_mode_txt = Text("Piano Mode", (160, 20))
        self.notes_played_txt = Text("", (20, 100), centering="topleft")
        self.stage.add_btn(self.exit_btn)
        self.stage.add_elt(self.piano_mode_txt)
        self.stage.add_elt(self.notes_played_txt)

    def on_exit_btn_click(self, btn, pos):
        self.quit = True

    def advance_time(self, fps):
        #Consume updates from input
        #print(self.key_input)
        self.key_input.poll()
        updates = self.key_input.get_updates()
        for pitch, is_pressed in updates.items():
            #print("Update: {}, {}".format(pitch, is_pressed))
            if is_pressed:
                self.played_pitches.add(pitch)
                self.player.play_note([pitch])
            elif pitch in self.played_pitches:
                self.played_pitches.remove(pitch)
                self.player.stop_note([pitch])
        if (len(updates) > 0):
            notes_played = ""
            for pitch in self.played_pitches:
                notes_played += pitch
                notes_played += " "
            self.notes_played_txt.text = notes_played

    def bind_screen(self, parent_screen):
        self.parent_screen = parent_screen

    def draw(self, screen):
        #print("Drawing")
        self.stage.draw(screen)

    def handle_click(self, pos):
        self.stage.handle_click(pos)

    def has_quit(self):
        return self.quit
コード例 #4
0
 def __init__(self, player, key_input):
     self.stage = Stage()
     self.player = player
     self.key_input = key_input
     self.playable_pitches = key_input.get_playable_pitches()
     #Set of currently played pitches
     self.played_pitches = set()
     #Various parameters
     self.quit = False
     #Construct buttons
     self.exit_btn = Btn("Exit", (40, 200), on_click = \
      self.on_exit_btn_click)
     self.piano_mode_txt = Text("Piano Mode", (160, 20))
     self.notes_played_txt = Text("", (20, 100), centering="topleft")
     self.stage.add_btn(self.exit_btn)
     self.stage.add_elt(self.piano_mode_txt)
     self.stage.add_elt(self.notes_played_txt)
コード例 #5
0
    def __init__(self, note_img_cache, player, key_input, scores \
     , fps, train_mode = True):
        #Various settings
        self.stage = Stage()
        self.scores_per_page = 4

        #Set various attributes
        self.scores = scores
        self.img_cache = note_img_cache
        self.player = player
        self.key_input = key_input
        self.train_mode = train_mode
        self.fps = fps

        self.colors = {}
        self.colors["blue"] = (39, 117, 242)
        self.colors["black"] = (0, 0, 0)

        self.quit = False
        self.score_btns = {}
        self.score_to_idx = {}
        self.curr_idx = 0
        self.sel_idx = -1
        self.return_from_mode = False
        self.stage = Stage()
        self.exit_btn = Btn("Exit", (40, 200), on_click = \
         self.on_exit_btn_click)
        self.select_btn = Btn("Select", (260, 180), on_click = \
         self.on_select_btn_click)
        self.up_btn = ImageBtn("./img/up.png", (260, 40), on_click = \
         self.on_up_btn_click, dimen = (20, 20))
        self.down_btn = ImageBtn("./img/down.png", (260, 120), on_click = \
         self.on_down_btn_click, dimen = (20, 20))
        self.stage.add_btn(self.exit_btn)
        self.stage.add_btn(self.up_btn)
        self.stage.add_btn(self.down_btn)
        self.stage.add_btn(self.select_btn)
        self.refresh_scores()
コード例 #6
0
class ScoreSelect:
    def __init__(self, note_img_cache, player, key_input, scores \
     , fps, train_mode = True):
        #Various settings
        self.stage = Stage()
        self.scores_per_page = 4

        #Set various attributes
        self.scores = scores
        self.img_cache = note_img_cache
        self.player = player
        self.key_input = key_input
        self.train_mode = train_mode
        self.fps = fps

        self.colors = {}
        self.colors["blue"] = (39, 117, 242)
        self.colors["black"] = (0, 0, 0)

        self.quit = False
        self.score_btns = {}
        self.score_to_idx = {}
        self.curr_idx = 0
        self.sel_idx = -1
        self.return_from_mode = False
        self.stage = Stage()
        self.exit_btn = Btn("Exit", (40, 200), on_click = \
         self.on_exit_btn_click)
        self.select_btn = Btn("Select", (260, 180), on_click = \
         self.on_select_btn_click)
        self.up_btn = ImageBtn("./img/up.png", (260, 40), on_click = \
         self.on_up_btn_click, dimen = (20, 20))
        self.down_btn = ImageBtn("./img/down.png", (260, 120), on_click = \
         self.on_down_btn_click, dimen = (20, 20))
        self.stage.add_btn(self.exit_btn)
        self.stage.add_btn(self.up_btn)
        self.stage.add_btn(self.down_btn)
        self.stage.add_btn(self.select_btn)
        self.refresh_scores()

    def bind_screen(self, parent_screen):
        self.parent_screen = parent_screen

    def draw(self, screen):
        self.stage.draw(screen)

    def handle_click(self, pos):
        self.stage.handle_click(pos)

    def refresh_scores(self):
        for _, btn in self.score_btns.items():
            self.stage.remove_btn(btn)
        #Maps index to button objects
        self.score_btns = {}
        #Maps score name to index
        self.score_to_idx = {}
        for i in range(self.curr_idx, min(self.curr_idx + \
         self.scores_per_page, len(self.scores))):
            score_name = self.scores[i].get_metadata()["name"]
            score_btn = Btn(score_name, (130, 40 + \
             (i - self.curr_idx) * 30), \
             on_click = self.on_score_btn_click, \
             font_size = 24)
            self.stage.add_btn(score_btn)
            #Make blue if selected
            if i == self.sel_idx:
                score_btn.color = self.colors["blue"]
            self.score_to_idx[score_name] = i
            self.score_btns[i] = score_btn

    def advance_time(self, fps):
        if self.return_from_mode:
            self.return_from_mode = False
            info = self.parent_screen.get_info()
            if "early_notes" in info:
                score_disp = AssignScore(info.pop("wrong_notes"), \
                 info.pop("early_notes"), info.pop("frames_used"), \
                 self.scores[self.sel_idx], fps)
                self.parent_screen.add_child(score_disp)

    def on_exit_btn_click(self, btn, pos):
        self.quit = True

    def on_up_btn_click(self, btn, pos):
        if self.curr_idx - self.scores_per_page >= 0:
            self.curr_idx -= self.scores_per_page
            self.sel_idx = -1
        self.refresh_scores()

    def on_down_btn_click(self, btn, pos):
        if self.curr_idx + self.scores_per_page < len(self.scores):
            self.curr_idx += self.scores_per_page
            self.sel_idx = -1
        self.refresh_scores()

    def on_select_btn_click(self, btn, pos):
        if self.sel_idx != -1:
            score = self.scores[self.sel_idx]
            if self.train_mode:
                train = TrainingScore(self.img_cache, self.player, \
                 self.key_input, score = score)
                #This passes off control to the training screen
                self.parent_screen.add_child(train)
                self.return_from_mode = True
            else:
                game = GameScore(self.img_cache, self.player, \
                 self.key_input, score = score)
                #This passes off control to the training screen
                self.parent_screen.add_child(game)
                self.return_from_mode = True

    def on_score_btn_click(self, btn, pos):
        btn_idx = self.score_to_idx[btn.text]
        if self.sel_idx in self.score_btns:
            self.score_btns[self.sel_idx].color = self.colors["black"]
        self.sel_idx = btn_idx
        self.score_btns[self.sel_idx].color = self.colors["blue"]

    def has_quit(self):
        return self.quit
コード例 #7
0
	def replace_score(self, new_score):
		self.score = new_score
		#Advance at 1.0 pace
		self.advance_rate = 1.0
		#Stage with no elements
		self.stage = Stage()
		#Keep track of current state
		#Start at the 0th bar
		self.curr_bar_idx = 0
		#Have not started playing music, set to False when paused
		self.has_started = False
		#Grab current bar
		self.bars = self.get_bars()
		#The current timing in the current bar (based on bar timing and bpm)
		self.curr_timing = 0.0
		#1.0 for normal, -<sth> for rewind, +<sth> for ffwd, 0 for pause
		self.advance_pace = 1.0
		#Add stage elements
		#Add trigger points
		self.left_margin = 10
		self.start_left_margin = 50
		self.right_margin = 310
		self.treble_begin = 70
		self.treble_increment = 10
		self.bass_begin = 160
		self.bass_increment = 10
		#Precompute adjustments
		self.bass_adj = self.get_adj(False)
		self.treble_adj = self.get_adj(True)
		#Add Treble Lines, Clef and Timing
		self.treble_lines = []
		self.treble_clef = Image("img/treble_clef.png", (20, 50), (35, 70))
		self.stage.add_elt(self.treble_clef)
		#30 up to 70
		for i in range(self.treble_begin - 4 * self.treble_increment, \
			self.treble_begin + self.treble_increment, self.treble_increment):
			line = Line((self.left_margin, i), (self.right_margin, i))
			self.treble_lines.append(line)
			self.stage.add_elt(line)
		#Add Bass Lines, Clef and Timing
		self.bass_lines = []
		self.bass_clef = Image("img/bass_clef.png", (25, 135), (35, 35))
		self.stage.add_elt(self.bass_clef)
		#120 up to 160
		for i in range(self.bass_begin - 4 * self.bass_increment, \
			self.bass_begin + self.bass_increment, self.bass_increment):
			line = Line((self.left_margin, i), (self.right_margin, i))
			self.bass_lines.append(line)
			self.stage.add_elt(line)
		#Add bar lines
		#Generate bar lines for left and right edges
		self.bar_lines = [\
		Line((self.left_margin, self.treble_begin - 4 * self.treble_increment),\
		 (self.left_margin,self.treble_begin)), \
		Line((self.left_margin, self.bass_begin - 4 * self.bass_increment),\
		 (self.left_margin,self.bass_begin))]
		
		#Draw bar lines (no changes)
		for bar_idx, bar in zip(range(len(self.bars)), self.bars):
			#Add bar lines
			end_x = self.get_bar_start_x(bar_idx + 1)
			self.bar_lines.append(Line((end_x, self.treble_begin \
			- 4 * self.treble_increment),(end_x,self.treble_begin)))
			self.bar_lines.append(Line((end_x, self.bass_begin \
			- 4 * self.bass_increment),(end_x,self.bass_begin)))
		for bar_line in self.bar_lines:
			self.stage.add_elt(bar_line)
		#Draw current position line
		play_line_pos = self.get_note_horizontal_pos(self.curr_bar_idx, \
			self.curr_timing) + 5
		self.play_line = Line((play_line_pos, self.treble_begin - \
			5 * self.treble_increment), (play_line_pos, self.bass_begin + \
			self.bass_increment))
		self.stage.add_elt(self.play_line)
		#Grab new timings
		self.refresh_timings()
コード例 #8
0
class RenderedScore:
	#[__init__ self note_imgs player score] generates a new RenderedScore
	#using the images from [note_imgs], audio player [player] and score [score]
	def __init__(self, note_imgs, player, score = None):
		self.colors = {"yellow": (244, 247, 35), "black": (0,0,0), \
		"dark_blue": (47, 29, 245)}
		self.note_imgs = note_imgs
		self.player = player
		self.num_bars = 2
		#Used to mark previous notes as black
		self.mark_black = True
		#Used to notate whether to play notes
		self.play_notes = True
		if score == None:
			self.score = None
		else:
			self.replace_score(score)

	#[bind_screen self parent_screen] is required by the parent Screen. 
	#It allows interaction with the parent Screen.
	def bind_screen(self, parent_screen):
		self.parent_screen = parent_screen

	#[replace_score self new_score] replaces the current score
	#with a new one and draws the new score onto the stage
	def replace_score(self, new_score):
		self.score = new_score
		#Advance at 1.0 pace
		self.advance_rate = 1.0
		#Stage with no elements
		self.stage = Stage()
		#Keep track of current state
		#Start at the 0th bar
		self.curr_bar_idx = 0
		#Have not started playing music, set to False when paused
		self.has_started = False
		#Grab current bar
		self.bars = self.get_bars()
		#The current timing in the current bar (based on bar timing and bpm)
		self.curr_timing = 0.0
		#1.0 for normal, -<sth> for rewind, +<sth> for ffwd, 0 for pause
		self.advance_pace = 1.0
		#Add stage elements
		#Add trigger points
		self.left_margin = 10
		self.start_left_margin = 50
		self.right_margin = 310
		self.treble_begin = 70
		self.treble_increment = 10
		self.bass_begin = 160
		self.bass_increment = 10
		#Precompute adjustments
		self.bass_adj = self.get_adj(False)
		self.treble_adj = self.get_adj(True)
		#Add Treble Lines, Clef and Timing
		self.treble_lines = []
		self.treble_clef = Image("img/treble_clef.png", (20, 50), (35, 70))
		self.stage.add_elt(self.treble_clef)
		#30 up to 70
		for i in range(self.treble_begin - 4 * self.treble_increment, \
			self.treble_begin + self.treble_increment, self.treble_increment):
			line = Line((self.left_margin, i), (self.right_margin, i))
			self.treble_lines.append(line)
			self.stage.add_elt(line)
		#Add Bass Lines, Clef and Timing
		self.bass_lines = []
		self.bass_clef = Image("img/bass_clef.png", (25, 135), (35, 35))
		self.stage.add_elt(self.bass_clef)
		#120 up to 160
		for i in range(self.bass_begin - 4 * self.bass_increment, \
			self.bass_begin + self.bass_increment, self.bass_increment):
			line = Line((self.left_margin, i), (self.right_margin, i))
			self.bass_lines.append(line)
			self.stage.add_elt(line)
		#Add bar lines
		#Generate bar lines for left and right edges
		self.bar_lines = [\
		Line((self.left_margin, self.treble_begin - 4 * self.treble_increment),\
		 (self.left_margin,self.treble_begin)), \
		Line((self.left_margin, self.bass_begin - 4 * self.bass_increment),\
		 (self.left_margin,self.bass_begin))]
		
		#Draw bar lines (no changes)
		for bar_idx, bar in zip(range(len(self.bars)), self.bars):
			#Add bar lines
			end_x = self.get_bar_start_x(bar_idx + 1)
			self.bar_lines.append(Line((end_x, self.treble_begin \
			- 4 * self.treble_increment),(end_x,self.treble_begin)))
			self.bar_lines.append(Line((end_x, self.bass_begin \
			- 4 * self.bass_increment),(end_x,self.bass_begin)))
		for bar_line in self.bar_lines:
			self.stage.add_elt(bar_line)
		#Draw current position line
		play_line_pos = self.get_note_horizontal_pos(self.curr_bar_idx, \
			self.curr_timing) + 5
		self.play_line = Line((play_line_pos, self.treble_begin - \
			5 * self.treble_increment), (play_line_pos, self.bass_begin + \
			self.bass_increment))
		self.stage.add_elt(self.play_line)
		#Grab new timings
		self.refresh_timings()

	#[refresh_timings self] refreshes all of the bars, timings and bpm
	#displayed on the stage based on the current timing (self.curr_timing) 
	#and current bar index (self.curr_bar_idx)
	def refresh_timings(self):
		self.stage.clear_tmp_elts()
		self.bar_numbers = []
		self.timings = []
		self.pace_text = []
		#Grab current bar
		self.bars = self.get_bars()
		prev_timing = (None, None)
		prev_pace = None
		#Render all the bars
		for bar_idx, bar in zip(range(len(self.bars)), self.bars):
			bar_timings = []
			top_timing, bottom_timing = bar.get_timing()
			curr_pace = bar.get_bpm()
			start_x = self.get_bar_start_x(bar_idx)
			bar_idx_norm = self.curr_bar_idx - self.curr_bar_idx % 2
			bar_num = bar_idx_norm + bar_idx + 1
			self.bar_numbers.append(Text(str(bar_num), (start_x + 5, \
				self.treble_begin - 4 * self.treble_increment - 10), \
				font_size = 20))
			if prev_timing != (top_timing, bottom_timing):
				top_timing = str(top_timing)
				bottom_timing = str(bottom_timing)
				bar_timings.append(Text(top_timing, (start_x + 10, 40), \
					font_size = 42))
				bar_timings.append(Text(bottom_timing, (start_x + 10, 62), \
					font_size = 42))
				bar_timings.append(Text(top_timing, (start_x + 10, 130), \
					font_size = 42))
				bar_timings.append(Text(bottom_timing, (start_x + 10, 152), \
					font_size = 42))
			if prev_pace != curr_pace:
				self.pace_text.append(Text(self.pace_to_str(curr_pace), \
					(start_x + 70, self.treble_begin - 4 * \
					self.treble_increment - 10), font_size = 20))
			#Add timings
			prev_timing = bar.get_timing()
			prev_pace = curr_pace
			self.timings.append(bar_timings)
			for timing in bar_timings:
				self.stage.add_tmp_elt(timing)
		for bar_number in self.bar_numbers:
			self.stage.add_tmp_elt(bar_number)
		for text in self.pace_text:
			self.stage.add_tmp_elt(text)
		self.refresh_notes()

	#[refresh_notes self] updates the position of all the notes on the screen
	#based on self.curr_timing and self.curr_bar_idx
	def refresh_notes(self):
		#Draw the notes
		#Stored by bar in same order as self.bars, then list of pitches for
		#each note, then a list of Components for each pitch
		#the note Image is always the last element in the list of Components
		self.treble_note_imgs = []
		self.bass_note_imgs = []
		for bar_idx, bar in zip(range(self.num_bars), self.bars):
			self.treble_bar_imgs = []
			self.add_notes_from_clef(bar_idx, bar.get_treble(), True, \
				self.treble_bar_imgs)
			self.bass_bar_imgs = []
			self.add_notes_from_clef(bar_idx, bar.get_bass(), False, \
				self.bass_bar_imgs)
			self.treble_note_imgs.append(self.treble_bar_imgs)
			self.bass_note_imgs.append(self.bass_bar_imgs)
		for bar in self.treble_note_imgs + self.bass_note_imgs:
			for pitches in bar:
				for pitch in pitches:
					for component in pitch:
						self.stage.add_tmp_elt(component)

	"""
	[add_notes_from_clef self bar_idx notes treble append_to] appends [notes]
	from the bar at [bar_idx] relative to self.curr_bar_idx from the clef
	indicated by [treble] (Treble if True, Bass if False) to the list
	[append_to]. Note that this takes into account note flips and chords
	where all notes point the same direction.
	"""
	def add_notes_from_clef(self, bar_idx, notes, treble, append_to):
		curr_dur = 0.0
		for pitches, duration in notes:
			note_imgs = []
			x_pos = self.get_note_horizontal_pos(self.curr_bar_idx + bar_idx, \
				curr_dur)
			#print("x_pos: {}".format(x_pos))
			should_force_flip = False
			for pitch in pitches:
				_,_,should_flip = self.pitch_adj_flip(pitch, treble)
				if should_flip:
					should_force_flip = True
					break
			for pitch in pitches:
				note_imgs.append(self.get_note_images(pitch, duration, \
					x_pos, treble, force_flip = should_force_flip))
			append_to.append(note_imgs)
			curr_dur += duration

	#[pace_to_str self pace] converts [pace] to a string based on the 
	#beats per minute
	def pace_to_str(self, pace):
		pace_text = [(24, "Larghissimo"), (40, "Grave"), (60, "Largo"), \
		(76, "Adagio"), (108, "Andante"), (120, "Moderato"), \
		(156, "Allegro"), (176, "Vivace"), (200, "Presto")]
		pace_name = "Prestissimo"
		for (max_pace, name) in pace_text:
			if pace <= max_pace:
				pace_name = name
				break
		return "{} {}".format(pace_name, int(pace))

	"""
	[get_bars self] gets the current bars based on self.curr_bar_idx and
	self.num_bars
	"""
	def get_bars(self):
		bars = []
		bar_idx = self.curr_bar_idx - (self.curr_bar_idx % self.num_bars)
		for i in range(bar_idx, min(bar_idx + 2, \
			self.score.get_total_bars())):
			bars.append(self.score.get_bar(i))
		return bars

	"""
	[get_bar_start_x self bar_idx] gets the x position where the bar with
	relative index [bar_idx] (0 to self.num_bars - 1) should start
	"""
	def get_bar_start_x(self, bar_idx):
		return self.start_left_margin + \
		float(self.right_margin - self.start_left_margin) * bar_idx \
		/ self.num_bars

	"""
	[get_note_horizontal_pos self bar_idx duration] gets the x position 
	of the note relative to the start of the bar based on the note [duration]
	and the relative index of the bar [bar_idx] (0 to self.num_bars - 1)
	"""
	def get_note_horizontal_pos(self, bar_idx, duration):
		bar_pos = bar_idx % self.num_bars
		start_x = self.get_bar_start_x(bar_pos)
		#print("bar_idx: {}, result: {}".format(bar_idx, start_x))
		#Consider position occupied by timing
		if bar_pos == 0 or self.bars[bar_pos].get_timing() \
		!= self.bars[bar_pos - 1].get_timing():
			start_x += 20
		end_x = self.get_bar_start_x(bar_pos + 1)
		bar_duration = self.bars[bar_pos].get_length()
		return start_x + float(end_x - start_x) * duration / bar_duration

	"""
	[get_adj self treble] gets the y axis adjustment needed based on the
	pitch of the note for the given clef based on [treble].
	[treble] indicates the Treble Clef if True and Bass Clef if False.
	"""
	def get_adj(self, treble):
		clef_adj = {'-' : self.treble_increment * 2}
		if treble:
			alphabet = 'F'
			octave = 3
			adj = -6 * float(self.treble_increment) / 2
		else:
			alphabet = 'E'
			octave = 2
			adj = -2 * float(self.bass_increment) / 2
		while octave < 7:
			clef_adj[alphabet + str(octave)] = adj
			alphabet = chr(ord(alphabet) + 1)
			if alphabet > 'G':
				alphabet = 'A'
			if alphabet == 'C':
				octave += 1
			adj += float(self.treble_increment) / 2
		return clef_adj

	"""
	[get_note_images self pitch duration x_pos treble force_flip]
	returns a list of components needed to draw a particular pitch.
	[pitch] refers to the pitch we're currently considering
	[duration] refers to the duration of the pitch that we're considering
	[x_pos] refers to the computed horizontal position of this pitch
	[duration] refers to the length of the note in crotchets
	[treble] refers to whether this note should be rendered in the treble
	clef (Treble if True, Bass if False)
	[force_flip is used to force the note to be flipped if it appears in a chord.
	Note that this can innately handle sharps, handle chords and draw
	extra lines if a note is too high or too low.
	"""
	def get_note_images(self, pitch, duration, x_pos, treble, force_flip = False):
		#May have to return additional lines to draw certain notes
		images = []
		#Pitch => ie 'A4', duration => ie 1.0
		is_sharp = pitch.find("#") != -1
		is_pause = pitch == '-'
		is_flipped = False
		if force_flip:
			is_flipped = True
		#Remove any sharps
		pitch = pitch.replace("#", "")
		duration = round(duration, 3)
		#Adjust x-axis by 10
		x_pos += 10
		adj = [0, 0]
		pitch_adj = 0
		#Adjust for position
		#if treble:
		#	pitch_adj = self.treble_adj[pitch]
		#	adj[1] = self.treble_begin - pitch_adj
		#else:
		#	pitch_adj = self.bass_adj[pitch]
		#	adj[1] = self.bass_begin - pitch_adj
		#Adjust for flipping
		#if pitch_adj >= 2 * self.treble_increment:
		#	is_flipped = True
		(adj, pitch_adj, should_flip) = self.pitch_adj_flip(pitch, treble)
		if should_flip:
			is_flipped = True
		#Draw extra lines if note is too low
		num_adj_lines = - int(pitch_adj) // int(self.treble_increment)
		for i in range(num_adj_lines):
			if treble:
				images.append(Line((x_pos - 10, self.treble_begin + (i + 1) * \
					self.treble_increment), (x_pos + 10, self.treble_begin + \
					(i + 1) * self.treble_increment)))
			else:
				images.append(Line((x_pos - 10, self.bass_begin + (i + 1) * \
					self.bass_increment), (x_pos + 10, self.bass_begin + \
					(i + 1) * self.bass_increment)))
		#Draw extra lines if note is too high
		num_adj_lines = (int(pitch_adj) - 4 * int(self.treble_increment)) // \
		int(self.treble_increment)
		for i in range(num_adj_lines):
			if treble:
				images.append(Line((x_pos - 10, self.treble_begin - (i + 5) * \
					self.treble_increment), (x_pos + 10, self.treble_begin - \
					(i + 5) * self.treble_increment)))
			else:
				images.append(Line((x_pos - 10, self.bass_begin - (i + 5) * \
					self.bass_increment), (x_pos + 10, self.bass_begin - \
					(i + 5) * self.bass_increment)))
		#Adjust for image
		img_adjustments = {1.0 : [(0, -13), (0, +14), (0, 0)], \
		2.0: [(0, -13), (0, +14), (0, -2)], \
		3.0: [(0, -13), (0, +14), (0, -2)], \
		4.0: [(0, 0), (0, 0), (0, -7)], \
		1.5: [(0, -13), (0, +14), (0, 0)], \
		0.5: [(+4, -13), (-4, +14), (0, 0)], \
		0.75: [(+4, -13), (-4, +14), (0, 0)], \
		0.25: [(+4, -13), (-4, +14), (0, 0)]}
		img_adj = (0, 0)
		if duration in img_adjustments:
			if not is_flipped and not is_pause:
				#Normal Note
				img_adj = img_adjustments[duration][0]
			elif is_pause:
				img_adj = img_adjustments[duration][2]
			else:
				img_adj = img_adjustments[duration][1]
			
		adj[0] += img_adj[0]
		adj[1] += img_adj[1]
		#Adjust for sharp
		if is_sharp:
			images.append(Text("#", (x_pos - 10, adj[1] - img_adj[1]),\
			 font_size = 20))
			#adj[0] += 10
		#Grab image and adjust
		img_surf = self.note_imgs.get_note(duration, rest = is_pause, \
			flip = is_flipped)
		images.append(Image(img_surf, (x_pos + adj[0], adj[1]), \
			from_surf = True))
		return images

	"""
	[pitch_adj_flip self pitch treble] adjusts a [pitch] for y position and
	determines if the pitch should be flipped based on its y position. [treble]
	is True if the note is in the Treble Clef and False otherwise.
	"""
	def pitch_adj_flip(self, pitch, treble):
		#Remove any sharps
		pitch = pitch.replace("#", "")
		adj = [0,0]
		is_flipped = False
		#Adjust for position
		if treble:
			pitch_adj = self.treble_adj[pitch]
			adj[1] = self.treble_begin - pitch_adj
		else:
			pitch_adj = self.bass_adj[pitch]
			adj[1] = self.bass_begin - pitch_adj
		#Adjust for flipping
		if pitch_adj >= 2 * self.treble_increment:
			is_flipped = True
		return (adj, pitch_adj, is_flipped)

	#[adjust_pace self new_pace] changes the relative pace that we're moving
	#along the song. 1.0 is the normal pace and other paces
	#would go faster or go slower. 0.0 is used to pause
	def adjust_pace(self, new_pace):
		#1.0 for normal, -<sth> for rewind, +<sth> for ffwd, 0 for pause
		self.advance_rate = new_pace

	#[advance_time self fps] steps through one frame at [fps] frames
	#per second. This causes the playback to advance according to
	#[self.advance_rate]
	def advance_time(self, fps):
		#Check if completed
		if self.curr_bar_idx >= self.score.get_total_bars():
			return
		#Move play line
		play_line_pos = self.get_note_horizontal_pos(self.curr_bar_idx, \
			self.curr_timing) + 5
		self.play_line.change_x(play_line_pos, play_line_pos)
		curr_bar = self.score.get_bar(self.curr_bar_idx)
		#Resume the piece
		if not self.has_started:
			self.has_started = True
			treble_pitches = self.get_curr_pitches(True)
			bass_pitches = self.get_curr_pitches(False)
			if self.play_notes:
				self.player.play_note(treble_pitches)
				self.player.play_note(bass_pitches)
			#Mark the playing notes as dark blue
			self.change_timing_note_color(self.curr_timing, \
				self.colors["dark_blue"], True)
			self.change_timing_note_color(self.curr_timing, \
				self.colors["dark_blue"], False)
		else:
			#Advance the current time
			bpm = curr_bar.get_bpm()
			prev_timing = self.curr_timing
			prev_treble = curr_bar.note_at_time(self.curr_timing, True)
			prev_bass = curr_bar.note_at_time(self.curr_timing, False)
			self.curr_timing += float(self.advance_rate) * bpm / 60.0 / fps
			new_treble = curr_bar.note_at_time(self.curr_timing, True)
			new_bass = curr_bar.note_at_time(self.curr_timing, False)
			treble = curr_bar.get_treble()
			bass = curr_bar.get_bass()
			#Transition notes when changing timing
			if prev_treble != new_treble:
				prev_treble_pitches = treble[prev_treble][0]
				if self.play_notes:
					self.player.stop_note(prev_treble_pitches)
				self.on_note_stop(prev_treble_pitches, True)
				if self.play_notes:
					self.player.play_note(treble[new_treble][0])
				if self.mark_black:
					self.change_timing_note_color(prev_timing, \
					self.colors["black"], True)
				self.change_timing_note_color(self.curr_timing, \
					self.colors["dark_blue"], True)
			if prev_bass != new_bass:
				prev_bass_pitches = bass[prev_bass][0]
				if self.play_notes:
					self.player.stop_note(prev_bass_pitches)
				self.on_note_stop(prev_bass_pitches, False)
				if self.play_notes:
					self.player.play_note(bass[new_bass][0])
				if self.mark_black:
					self.change_timing_note_color(prev_timing, \
					self.colors["black"], False)
				self.change_timing_note_color(self.curr_timing, \
					self.colors["dark_blue"], False)
		#Move to next bar when available
		if self.curr_timing > curr_bar.get_length():
			#Stop current notes
			prev_treble_pitches = self.get_curr_pitches(True)
			prev_bass_pitches = self.get_curr_pitches(False)
			if self.play_notes:
				self.player.stop_note(prev_treble_pitches)
				self.player.stop_note(prev_bass_pitches)
				self.player.stop_all()
			self.on_note_stop(prev_treble_pitches, True)
			self.on_note_stop(prev_bass_pitches, False)
			#Mark the stopped notes as black
			if self.mark_black:
				self.change_timing_note_color(self.curr_timing, \
					self.colors["black"], True)
				self.change_timing_note_color(self.curr_timing, \
					self.colors["black"], False)
			self.curr_timing -= curr_bar.get_length()
			self.curr_bar_idx += 1
			#Update notes if required
			if self.curr_bar_idx % self.num_bars == 0 and \
			self.curr_bar_idx < self.score.get_total_bars():
				self.refresh_timings()
			#Play treble and bass notes for next bar if possible
			if self.curr_bar_idx < self.score.get_total_bars():
				if self.play_notes:
					self.player.play_note(self.get_curr_pitches(True))
					self.player.play_note(self.get_curr_pitches(False))
				#Mark the playing notes as yellow
				self.change_timing_note_color(self.curr_timing, \
					self.colors["dark_blue"], True)
				self.change_timing_note_color(self.curr_timing, \
					self.colors["dark_blue"], False)

	#[on_note_stop self pitches treble] is called when we transition
	#between bars or between notes. [pitches] refer to the pitches which
	#are stopped and [treble] refers to the clef (Treble if True, Bass if False)
	def on_note_stop(self, pitches, treble):
		return True

	#[get_curr_pitches self treble] gets the pitches based on self.curr_bar_idx
	#and self.curr_timing that should be played now based on the clef indicated
	#by [treble] (Treble if True, Bass if False)
	def get_curr_pitches(self, treble):
		curr_bar = self.score.get_bar(self.curr_bar_idx)
		if treble:
			notes = curr_bar.get_treble()
		else:
			notes = curr_bar.get_bass()
		pitches,_ = notes[curr_bar.note_at_time(self.curr_timing, treble)]
		return pitches

	#[jump_to_next_timing self] is used to jump to the next note
	def jump_to_next_timing(self):
		#print("Changing color")
		bar_idx = self.curr_bar_idx % self.num_bars
		curr_bar = self.score.get_bar(self.curr_bar_idx)
		treble_idx = curr_bar.note_at_time(self.curr_timing, True)
		bass_idx = curr_bar.note_at_time(self.curr_timing, False)
		treble_dur = curr_bar.end_duration(treble_idx, True)
		bass_dur = curr_bar.end_duration(bass_idx, False)
		timing_jump = min(treble_dur, bass_dur) + 0.01 - self.curr_timing
		self.curr_timing += timing_jump
		self.on_early_note_release(timing_jump)

	#[on_early_note_release self timing_jump] is triggered when a note is
	#released early by the player. [timing_jump] is the amount of crotchets
	#missed when we jump to the next note.
	def on_early_note_release(self, timing_jump):
		return True

	#[change_timing_note_color self timing new_color treble] changes the color
	#of the note at [timing] to [new_color] with clef specified by [treble]
	#(Treble if True, Bass if False)
	def change_timing_note_color(self, timing, new_color, treble):
		#print("Changing color")
		bar_idx = self.curr_bar_idx % self.num_bars
		curr_bar = self.score.get_bar(self.curr_bar_idx)
		#print(self.treble_note_imgs)
		#print("Timing: {}".format(timing))
		if treble:
			treble_idx = curr_bar.note_at_time(timing, True)
			for pitch in self.treble_note_imgs[bar_idx][treble_idx]:
				pitch[-1].change_color(new_color)
		else:
			bass_idx = curr_bar.note_at_time(timing, False)
			for pitch in self.bass_note_imgs[bar_idx][bass_idx]:
				pitch[-1].change_color(new_color)

	#[change_curr_pitch_color] changes the specified [pitches] to [new_color]
	#at the current timing defined by self.curr_bar_idx and self.curr_timing
	def change_curr_pitch_color(self, pitches, new_color):
		bar_idx = self.curr_bar_idx % self.num_bars
		curr_bar = self.score.get_bar(self.curr_bar_idx)
		timing = self.curr_timing
		treble_idx = curr_bar.note_at_time(timing, True)
		treble = curr_bar.get_treble()
		bass = curr_bar.get_bass()
		for pitch, imgs in zip(treble[treble_idx][0], \
			self.treble_note_imgs[bar_idx][treble_idx]):
			if pitch in pitches:
				imgs[-1].change_color(new_color)
		bass_idx = curr_bar.note_at_time(timing, False)
		for pitch, imgs in zip(bass[bass_idx][0], \
			self.bass_note_imgs[bar_idx][bass_idx]):
			if pitch in pitches:
				imgs[-1].change_color(new_color)

	#[handle_click self pos] handles a click event at position [pos]
	def handle_click(self, pos):
		self.stage.handle_click(pos)

	#[draw self screen] draws the elements in the score onto [screen]
	def draw(self, screen):
		self.stage.draw(screen)

	#[has_quit self] queries whether this score has quitted
	def has_quit(self):
		return self.curr_bar_idx >= self.score.get_total_bars()
コード例 #9
0
ファイル: main_ui.py プロジェクト: abcdefguan/piano_game
class MainUI:
    def __init__(self, note_img, player, key_input, scores, fps):
        self.fps = fps
        self.note_img_cache = note_img
        self.player = player
        self.key_input = key_input
        self.scores = scores

        self.quit = False
        training_btn = Btn("Training", (250, 40), on_click = \
         self.on_training_btn_click)
        game_btn = Btn("Play", (250, 80), on_click = \
         self.on_play_btn_click)
        piano_btn = Btn("Piano", (250, 120), on_click = \
         self.on_piano_btn_click)
        exit_btn = Btn("Quit", (250, 200), on_click = \
         self.on_exit_btn_click)
        piano_game_txt = Text("Piano Game", (100, 40))
        dev_by_txt = Text("By Guanqun Wu, Zhaopeng Xu", (100, 80), \
         font_size = 20)

        self.stage = Stage()
        self.stage.add_btn(training_btn)
        self.stage.add_btn(game_btn)
        self.stage.add_btn(piano_btn)
        self.stage.add_btn(exit_btn)
        self.stage.add_elt(piano_game_txt)
        self.stage.add_elt(dev_by_txt)

    def bind_screen(self, parent_screen):
        self.parent_screen = parent_screen

    def draw(self, screen):
        self.stage.draw(screen)

    def handle_click(self, pos):
        self.stage.handle_click(pos)

    def advance_time(self, fps):
        return True

    def on_training_btn_click(self, btn, pos):
        select = ScoreSelect(self.note_img_cache, self.player, self.key_input, \
         self.scores, self.fps, True)
        self.parent_screen.add_child(select)

    def on_play_btn_click(self, btn, pos):
        select = ScoreSelect(self.note_img_cache, self.player, self.key_input, \
         self.scores, self.fps, False)
        self.parent_screen.add_child(select)

    def on_piano_btn_click(self, btn, pos):
        piano = PianoMode(self.player, self.key_input)
        self.parent_screen.add_child(piano)

    def on_exit_btn_click(self, btn, pos):
        self.quit = True

    def has_quit(self):
        return self.quit