1#!/usr/bin/env python 2""" pygame.examples.playmus 3 4A simple music player. 5 6 Use pygame.mixer.music to play an audio file. 7 8A window is created to handle keyboard events for playback commands. 9 10 11Keyboard Controls 12----------------- 13 14space - play/pause toggle 15r - rewind 16f - fade out 17q - stop 18 19""" 20import sys 21 22import pygame as pg 23import pygame.freetype 24 25 26class Window(object): 27 """The application's Pygame window 28 29 A Window instance manages the creation of and drawing to a 30 window. It is a singleton class. Only one instance can exist. 31 32 """ 33 34 instance = None 35 36 def __new__(cls, *args, **kwds): 37 """Return an open Pygame window""" 38 39 if Window.instance is not None: 40 return Window.instance 41 self = object.__new__(cls) 42 pg.display.init() 43 self.screen = pg.display.set_mode((600, 400)) 44 Window.instance = self 45 return self 46 47 def __init__(self, title): 48 pg.display.set_caption(title) 49 self.text_color = (254, 231, 21, 255) 50 self.background_color = (16, 24, 32, 255) 51 self.screen.fill(self.background_color) 52 pg.display.flip() 53 54 pygame.freetype.init() 55 self.font = pygame.freetype.Font(None, 20) 56 self.font.origin = True 57 self.ascender = int(self.font.get_sized_ascender() * 1.5) 58 self.descender = int(self.font.get_sized_descender() * 1.5) 59 self.line_height = self.ascender - self.descender 60 61 self.write_lines( 62 "\nPress 'q' or 'ESCAPE' or close this window to quit\n" 63 "Press 'SPACE' to play / pause\n" 64 "Press 'r' to rewind to the beginning (restart)\n" 65 "Press 'f' to fade music out over 5 seconds\n\n" 66 "Window will quit automatically when music ends\n", 67 0, 68 ) 69 70 def __enter__(self): 71 return self 72 73 def __exit__(self, exc_type, exc_val, exc_tb): 74 self.close() 75 return False 76 77 def close(self): 78 pg.display.quit() 79 Window.instance = None 80 81 def write_lines(self, text, line=0): 82 w, h = self.screen.get_size() 83 line_height = self.line_height 84 nlines = h // line_height 85 if line < 0: 86 line = nlines + line 87 for i, text_line in enumerate(text.split("\n"), line): 88 y = i * line_height + self.ascender 89 # Clear the line first. 90 self.screen.fill( 91 self.background_color, (0, i * line_height, w, line_height) 92 ) 93 # Write new text. 94 self.font.render_to(self.screen, (15, y), text_line, self.text_color) 95 pg.display.flip() 96 97 98def show_usage_message(): 99 print("Usage: python playmus.py <file>") 100 print(" python -m pygame.examples.playmus <file>") 101 102 103def main(file_path): 104 """Play an audio file with pg.mixer.music""" 105 106 with Window(file_path) as win: 107 win.write_lines("Loading ...", -1) 108 pg.mixer.init(frequency=44100) 109 try: 110 paused = False 111 pg.mixer.music.load(file_path) 112 113 # Make sure the event loop ticks over at least every 0.5 seconds. 114 pg.time.set_timer(pg.USEREVENT, 500) 115 116 pg.mixer.music.play() 117 win.write_lines("Playing ...\n", -1) 118 119 while pg.mixer.music.get_busy() or paused: 120 e = pg.event.wait() 121 if e.type == pg.KEYDOWN: 122 key = e.key 123 if key == pg.K_SPACE: 124 if paused: 125 pg.mixer.music.unpause() 126 paused = False 127 win.write_lines("Playing ...\n", -1) 128 else: 129 pg.mixer.music.pause() 130 paused = True 131 win.write_lines("Paused ...\n", -1) 132 elif key == pg.K_r: 133 if file_path[-3:].lower() in ("ogg", "mp3", "mod"): 134 status = "Rewound." 135 pg.mixer.music.rewind() 136 else: 137 status = "Restarted." 138 pg.mixer.music.play() 139 if paused: 140 pg.mixer.music.pause() 141 win.write_lines(status, -1) 142 elif key == pg.K_f: 143 win.write_lines("Fading out ...\n", -1) 144 pg.mixer.music.fadeout(5000) 145 # when finished get_busy() will return False. 146 elif key in [pg.K_q, pg.K_ESCAPE]: 147 paused = False 148 pg.mixer.music.stop() 149 # get_busy() will now return False. 150 elif e.type == pg.QUIT: 151 paused = False 152 pg.mixer.music.stop() 153 # get_busy() will now return False. 154 pg.time.set_timer(pg.USEREVENT, 0) 155 finally: 156 pg.mixer.quit() 157 pg.quit() 158 159 160if __name__ == "__main__": 161 # Check the only command line argument, a file path 162 if len(sys.argv) != 2: 163 show_usage_message() 164 else: 165 main(sys.argv[1]) 166