def testRunning(self): """Tests that the running boolean works as expected""" stopwatch = Stopwatch() self.assertTrue(stopwatch.running) stopwatch.stop() self.assertFalse(stopwatch.running) stopwatch.restart() self.assertTrue(stopwatch.running)
class Orchestra: SAMPLE_RATE = 44100 PLAYABLE_FORMATS = ['mp3', 'flac', 'wav', 'm4b'] JACK = "jack" SSR = "ssr" @staticmethod def add_parser_arguments(parser): parser.add_argument("--rt", action="store_true", dest="realtime") parser.add_argument("-t", "--torrent", dest="torrentname", default="") parser.add_argument("-z", "--timefactor", dest="timefactor", type=float, default=1) parser.add_argument("--start", dest="start_time", type=float, default=0) parser.add_argument("-q", "--quiet", action="store_true", dest="quiet") parser.add_argument("--pretend-sequential", action="store_true", dest="pretend_sequential") parser.add_argument("--gui", action="store_true", dest="gui_enabled") parser.add_argument("--predecode", action="store_true", dest="predecode", default=True) parser.add_argument("--file-location", dest="file_location", default=DOWNLOAD_LOCATION) parser.add_argument("--fast-forward", action="store_true", dest="ff") parser.add_argument("--fast-forward-to-start", action="store_true", dest="ff_to_start") parser.add_argument("--quit-at-end", action="store_true", dest="quit_at_end") parser.add_argument("--loop", dest="loop", action="store_true") parser.add_argument("--max-passivity", dest="max_passivity", type=float) parser.add_argument("--max-pause-within-segment", dest="max_pause_within_segment", type=float) parser.add_argument("--looped-duration", dest="looped_duration", type=float) parser.add_argument("-o", "--output", dest="output", type=str, default=Orchestra.JACK) parser.add_argument("--include-non-playable", action="store_true") parser.add_argument("-f", "--file", dest="selected_files", type=int, nargs="+") parser.add_argument("--no-synth", action="store_true") parser.add_argument("--locate-peers", action="store_true") _extension_re = re.compile('\.(\w+)$') def __init__(self, sessiondir, tr_log, options): self.options = options self.sessiondir = sessiondir self.tr_log = tr_log self.realtime = options.realtime self.timefactor = options.timefactor self.quiet = options.quiet self.predecode = options.predecode self.file_location = options.file_location self._loop = options.loop self._max_passivity = options.max_passivity self.looped_duration = options.looped_duration self.output = options.output self.include_non_playable = options.include_non_playable if options.locate_peers: import geo.ip_locator self._peer_location = {} ip_locator = geo.ip_locator.IpLocator() for peeraddr in tr_log.peers: self._peer_location[peeraddr] = ip_locator.locate(peeraddr) if options.predecode: predecoder = Predecoder(tr_log, options.file_location, self.SAMPLE_RATE) predecoder.decode() if options.selected_files: tr_log.select_files(options.selected_files) self.playback_enabled = True self.fast_forwarding = False self._log_time_for_last_handled_event = 0 self.gui = None self._check_which_files_are_audio() if options.no_synth: self.synth = None else: from synth_controller import SynthController self.synth = SynthController() self._create_players() self._prepare_playable_files() self.stopwatch = Stopwatch() self.playable_chunks = self._filter_playable_chunks(tr_log.chunks) if self.include_non_playable: self.chunks = tr_log.chunks self._num_selected_files = len(self.tr_log.files) else: self.chunks = self.playable_chunks self._num_selected_files = self._num_playable_files logger.debug("total num chunks: %s" % len(tr_log.chunks)) logger.debug("num playable chunks: %s" % len(self.playable_chunks)) logger.debug("num selected chunks: %s" % len(self.chunks)) self._interpret_chunks_to_score(options.max_pause_within_segment) self._chunks_by_id = {} self.segments_by_id = {} self._playing = False self._quitting = False self.space = Space() if options.ff_to_start: self._ff_to_time = options.start_time self.set_time_cursor(0) else: self._ff_to_time = None self.set_time_cursor(options.start_time) self.scheduler = sched.scheduler(time.time, time.sleep) self._run_scheduler_thread() if self.output == self.SSR: self.ssr = SsrControl() self._warned_about_max_sources = False else: self.ssr = None def _interpret_chunks_to_score(self, max_pause_within_segment): self.score = Interpreter(max_pause_within_segment).interpret(self.playable_chunks, self.tr_log.files) if self._max_passivity: self._reduce_max_passivity_in_score() for segment in self.score: segment["duration"] /= self.timefactor def _reduce_max_passivity_in_score(self): previous_onset = 0 reduced_time = 0 for i in range(len(self.score)): if (self.score[i]["onset"] - reduced_time - previous_onset) > self._max_passivity: reduced_time += self.score[i]["onset"] - reduced_time - previous_onset - self._max_passivity self.score[i]["onset"] -= reduced_time previous_onset = self.score[i]["onset"] def _filter_playable_chunks(self, chunks): return filter(lambda chunk: (self._chunk_is_playable(chunk)), chunks) def _chunk_is_playable(self, chunk): file_info = self.tr_log.files[chunk["filenum"]] return file_info["playable_file_index"] != -1 def _run_scheduler_thread(self): self._scheduler_thread = threading.Thread(target=self._process_scheduled_events) self._scheduler_thread.daemon = True self._scheduler_thread.start() def _process_scheduled_events(self): while not self._quitting: self.scheduler.run() time.sleep(0.01) def _handle_visualizing_message(self, path, args, types, src, data): segment_id = args[0] segment = self.segments_by_id[segment_id] logger.debug("visualizing segment %s" % segment) if self.output == self.SSR: if segment["sound_source_id"]: channel = segment["sound_source_id"] - 1 self._ask_synth_to_play_segment(segment, channel=channel, pan=None) else: player = self.get_player_for_segment(segment) self._ask_synth_to_play_segment(segment, channel=0, pan=player.spatial_position.pan) def _ask_synth_to_play_segment(self, segment, channel, pan): if self.synth: logger.debug("asking synth to play %s" % segment) file_info = self.tr_log.files[segment["filenum"]] self.synth.play_segment( segment["id"], segment["filenum"], segment["start_time_in_file"] / file_info["duration"], segment["end_time_in_file"] / file_info["duration"], segment["duration"], self.looped_duration, channel, pan) self.scheduler.enter( segment["playback_duration"], 1, self.stopped_playing, [segment]) def _check_which_files_are_audio(self): for file_info in self.tr_log.files: file_info["is_audio"] = self._has_audio_extension(file_info["name"]) @staticmethod def _has_audio_extension(filename): return Orchestra._extension(filename) in Orchestra.PLAYABLE_FORMATS @staticmethod def _extension(filename): m = Orchestra._extension_re.search(filename) if m: return m.group(1).lower() def _create_players(self): self._player_class = WavPlayer self.players = [] self._player_for_peer = dict() def _prepare_playable_files(self): if self.predecode: self._get_wav_files_info() self._load_sounds() else: raise Exception("playing wav without precoding is not supported") def _load_sounds(self): if self.synth: print "loading sounds" for filenum in range(len(self.tr_log.files)): file_info = self.tr_log.files[filenum] if file_info["playable_file_index"] != -1: logger.debug("load_sound(%s)" % file_info["decoded_name"]) result = self.synth.load_sound(filenum, file_info["decoded_name"]) logger.debug("result: %s" % result) print "OK" def _get_wav_files_info(self): playable_file_index = 0 for filenum in range(len(self.tr_log.files)): file_info = self.tr_log.files[filenum] file_info["playable_file_index"] = -1 if "decoded_name" in file_info: file_info["duration"] = self._get_file_duration(file_info) if file_info["duration"] > 0: file_info["num_channels"] = self._get_num_channels(file_info) file_info["playable_file_index"] = playable_file_index logger.debug("duration for %r: %r\n" % (file_info["name"], file_info["duration"])) playable_file_index += 1 if self.include_non_playable: file_info["index"] = filenum else: file_info["index"] = file_info["playable_file_index"] self._num_playable_files = playable_file_index def _get_file_duration(self, file_info): if "decoded_name" in file_info: cmd = 'soxi -D "%s"' % file_info["decoded_name"] try: stdoutdata, stderrdata = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() return float(stdoutdata) except: logger.debug("failed to get duration for %s" % file_info["decoded_name"]) return 0 def _get_num_channels(self, file_info): if "decoded_name" in file_info: cmd = 'soxi -c "%s"' % file_info["decoded_name"] stdoutdata, stderrdata = subprocess.Popen( cmd, shell=True, stdout=subprocess.PIPE).communicate() return int(stdoutdata) def get_current_log_time(self): if self.fast_forwarding: return self._log_time_for_last_handled_event else: return self.log_time_played_from + self.stopwatch.get_elapsed_time() * self.timefactor def play_non_realtime(self, quit_on_end=False): logger.debug("entering play_non_realtime") if self._loop: while True: self._play_until_end() self.set_time_cursor(0) else: self._play_until_end() if quit_on_end: self._quitting = True logger.debug("leaving play_non_realtime") def _play_until_end(self): logger.debug("entering _play_until_end") self._playing = True self.stopwatch.start() no_more_events = False while self._playing and not no_more_events: event = self._get_next_chunk_or_segment() if event: self._handle_event(event) else: no_more_events = True logger.debug("leaving _play_until_end") def _get_next_chunk_or_segment(self): logger.debug("chunk index = %d, segment index = %d" % ( self.current_chunk_index, self.current_segment_index)) chunk = self._get_next_chunk() segment = self._get_next_segment() logger.debug("next chunk: %s" % chunk) logger.debug("next segment: %s" % segment) if chunk and segment: return self._choose_nearest_chunk_or_segment(chunk, segment) elif chunk: return {"type": "chunk", "chunk": chunk} elif segment: return {"type": "segment", "segment": segment} else: return None def _get_next_chunk(self): if self.current_chunk_index < len(self.chunks): return self.chunks[self.current_chunk_index] def _get_next_segment(self): if len(self.score) == 0: return None elif self.current_segment_index < len(self.score): return self.score[self.current_segment_index] def _handle_event(self, event): if event["type"] == "chunk": self.handle_chunk(event["chunk"]) self.current_chunk_index += 1 elif event["type"] == "segment": self.handle_segment(event["segment"]) self.current_segment_index += 1 else: raise Exception("unexpected event %s" % event) def _choose_nearest_chunk_or_segment(self, chunk, segment): if chunk["t"] < segment["onset"]: return {"type": "chunk", "chunk": chunk} else: return {"type": "segment", "segment": segment} def stop(self): if self.synth: self.synth.stop_all() self._playing = False self.log_time_played_from = self.get_current_log_time() self.stopwatch.stop() def handle_segment(self, segment): logger.debug("handling segment %s" % segment) player = self.get_player_for_segment(segment) if not player: logger.debug("get_player_for_segment returned None - skipping playback") if self.fast_forwarding: self._stop_ff_if_necessary() else: now = self.get_current_log_time() time_margin = segment["onset"] - now logger.debug("time_margin=%f-%f=%f" % (segment["onset"], now, time_margin)) if not self.realtime and time_margin > 0: sleep_time = time_margin logger.debug("sleeping %f" % sleep_time) time.sleep(sleep_time) if player: logger.debug("player.enabled=%s" % player.enabled) if player and player.enabled: player.play(segment, pan=0.5) self._log_time_for_last_handled_event = segment["onset"] def handle_chunk(self, chunk): logger.debug("handling chunk %s" % chunk) player = self.get_player_for_chunk(chunk) logger.debug("get_player_for_chunk returned %s" % player) if self.fast_forwarding: self._stop_ff_if_necessary() else: now = self.get_current_log_time() time_margin = chunk["t"] - now logger.debug("time_margin=%f-%f=%f" % (chunk["t"], now, time_margin)) if not self.realtime and time_margin > 0: sleep_time = time_margin logger.debug("sleeping %f" % sleep_time) time.sleep(sleep_time) if player: logger.debug("player.enabled=%s" % player.enabled) if player and player.enabled: player.visualize(chunk) self._log_time_for_last_handled_event = chunk["t"] def _stop_ff_if_necessary(self): if self._ff_to_time is not None and \ self._log_time_for_last_handled_event >= self._ff_to_time: self._ff_to_time = None self.fast_forwarding = False self.set_time_cursor(self.log_time_played_from) def highlight_segment(self, segment): if self.gui: self.gui.highlight_segment(segment) def visualize_chunk(self, chunk, player): if len(self.visualizers) > 0: file_info = self.tr_log.files[chunk["filenum"]] self._chunks_by_id[chunk["id"]] = chunk self._tell_visualizers( "/chunk", chunk["id"], chunk["begin"], chunk["end"] - chunk["begin"], file_info["index"], player.id, chunk["t"]) def visualize_segment(self, segment, player): if len(self.visualizers) > 0: if self.ssr: segment["sound_source_id"] = self.ssr.allocate_source() if not segment["sound_source_id"] and not self._warned_about_max_sources: print "WARNING: max sources exceeded, skipping segment playback (this warning will not be repeated)" self._warned_about_max_sources = True file_info = self.tr_log.files[segment["filenum"]] self.segments_by_id[segment["id"]] = segment self._tell_visualizers( "/segment", segment["id"], segment["begin"], segment["end"] - segment["begin"], file_info["index"], player.id, segment["t"], segment["playback_duration"]) else: self._ask_synth_to_play_segment(segment, channel=0, pan=0.5) def stopped_playing(self, segment): logger.debug("stopped segment %s" % segment) if self.gui: self.gui.unhighlight_segment(segment) if len(self.visualizers) > 0: if self.ssr and segment["sound_source_id"]: self.ssr.free_source(segment["sound_source_id"]) def play_segment(self, segment, player): self.segments_by_id[segment["id"]] = segment if self.looped_duration: segment["playback_duration"] = self.looped_duration else: segment["playback_duration"] = segment["duration"] self.visualize_segment(segment, player) def _send_torrent_info_to_uninformed_visualizers(self): for visualizer in self.visualizers: if not visualizer.informed_about_torrent: self._send_torrent_info_to_visualizer(visualizer) def _send_torrent_info_to_visualizer(self, visualizer): visualizer.send( "/torrent", self._num_selected_files, self.tr_log.lastchunktime(), self.tr_log.total_file_size(), len(self.chunks), len(self.score)) for filenum in range(len(self.tr_log.files)): file_info = self.tr_log.files[filenum] if self.include_non_playable or file_info["playable_file_index"] != -1: visualizer.send( "/file", file_info["index"], file_info["offset"], file_info["length"]) visualizer.informed_about_torrent = True def get_player_for_chunk(self, chunk): try: return chunk["player"] except KeyError: peer_player = self.get_player_for_peer(chunk["peeraddr"]) chunk["player"] = peer_player return peer_player def get_player_for_segment(self, segment): try: return segment["player"] except KeyError: peer_player = self.get_player_for_peer(segment["peeraddr"]) segment["player"] = peer_player return peer_player def get_player_for_peer(self, peeraddr): peer_player = None try: peer_player = self._player_for_peer[peeraddr] except KeyError: peer_player = self._create_player(peeraddr) self.players.append(peer_player) self._player_for_peer[peeraddr] = peer_player return peer_player def _create_player(self, addr): count = len(self.players) logger.debug("creating player number %d" % count) player = self._player_class(self, count) if self.options.locate_peers and self._peer_location[addr] is not None: x, y = self._peer_location[addr] location_str = "%s,%s" % (x, y) else: location_str = "" self._tell_visualizers( "/peer", player.id, addr, player.spatial_position.bearing, location_str) return player def set_time_cursor(self, log_time): assert not self.realtime logger.debug("setting time cursor at %f" % log_time) self.log_time_played_from = log_time if self._playing: self.stopwatch.restart() self.current_chunk_index = self._get_current_chunk_index() self.current_segment_index = self._get_current_segment_index() def _get_current_chunk_index(self): index = 0 next_to_last_index = len(self.chunks) - 2 while index < next_to_last_index: if self.chunks[index+1]["t"] >= self.log_time_played_from: return index index += 1 return len(self.chunks) - 1 def _get_current_segment_index(self): index = 0 next_to_last_index = len(self.score) - 2 while index < next_to_last_index: if self.score[index+1]["onset"] >= self.log_time_played_from: return index index += 1 return len(self.score) - 1 def _handle_set_listener_position(self, path, args, types, src, data): if self.ssr: x, y = args self.ssr.set_listener_position(x, y) def _handle_set_listener_orientation(self, path, args, types, src, data): if self.ssr: orientation = args[0] self.ssr.set_listener_orientation(orientation) def _handle_place_segment(self, path, args, types, src, data): segment_id, x, y, duration = args if self.ssr: segment = self.segments_by_id[segment_id] sound_source_id = segment["sound_source_id"] if sound_source_id is not None: self.ssr.place_source(sound_source_id, x, y, duration) else: pan = self._spatial_position_to_stereo_pan(x, y) if self.synth: self.synth.pan(segment_id, pan) def _handle_enable_smooth_movement(self, path, args, types, src, data): if self.ssr: self.ssr.enable_smooth_movement() def _handle_start_segment_movement_from_peer(self, path, args, types, src, data): segment_id, duration = args if self.ssr: segment = self.segments_by_id[segment_id] sound_source_id = segment["sound_source_id"] if sound_source_id is not None: player = self.get_player_for_segment(segment) self.ssr.start_source_movement( sound_source_id, player.trajectory, duration) def _spatial_position_to_stereo_pan(self, x, y): # compare rectangular_visualizer.Visualizer.pan_segment # NOTE: assumes default listener position and orientation! return float(x) / 5 + 0.5 def reset(self): self._free_sounds() self._tell_visualizers("/reset") def _free_sounds(self): if self.synth: for filenum in range(len(self.tr_log.files)): file_info = self.tr_log.files[filenum] if file_info["playable_file_index"] != -1: self.synth.free_sound(filenum) def _tell_visualizers(self, *args): self._send_torrent_info_to_uninformed_visualizers() self.server._tell_visualizers(*args)
from sense_hat import SenseHat from time import sleep import time from datetime import datetime from stopwatch import Stopwatch # from constants import * from mazes import * now = datetime.now() current_time = now.strftime("%H:%M:%S") stopwatch = Stopwatch() sense = SenseHat() sense.clear() stopwatch.restart() maze = maze1 class MazePhysics: def move_marble(self, pitch, roll, x, y): new_x = x new_y = y if 1 < pitch < 179 and x != 0: new_x -= 1 elif 359 > pitch > 181 and x != 7: new_x += 1 if 1 < roll < 179 and y != 7: new_y += 1 elif 359 > roll > 181 and y != 0:
class Chrono(QtWidgets.QMainWindow): def __init__(self): super(Chrono, self).__init__() # Call the inherited classes __init__ method uic.loadUi("test" + path_separator + 'UI' + path_separator + 'cronometro.ui', self) # Load the .ui file self.showMaximized() # Show the GUI config = load_properties() self.current_user = config.get('UsersSection', 'currentUser') self.action_import.triggered.connect(self.import_db) self.action_import.setShortcut(QtGui.QKeySequence("Ctrl+i")) self.quali_label.setVisible(False) self.action_export.triggered.connect(self.export_db) self.action_export.setShortcut(QtGui.QKeySequence("Ctrl+e")) self.saved_message_thread = message_thread(self.saved_msg) styles = {'color': '(162,173,194)', 'font-size': '15px'} self.graph.showGrid(x=True, y=True) self.graph.setLabel('left', 'Tiempo por vuelta', **styles) self.graph.setLabel('bottom', 'Días', **styles) self.graph.setBackground('#fdfdff') self.graph.setTitle("Tiempos del paciente") self.lapsList.setStyleSheet( "QLabel#lapsList{ font-weight: bold; color: #555860; font-size: 17px; }" ) self.quali_label.setStyleSheet( "QLabel#quali_label{ font-weight: bold; color: #fdfdff; font-size: 15px; background-color: #222628; border: 2px solid #909293; }" ) self.moreInfo.setStyleSheet( "QPushButton#moreInfo{ border: 2px solid #717987; font-weight: bold; color: white; background-color: #a2adc2; } QPushButton#moreInfo::hover{ background-color: #BDC5D4;} QPushButton#moreInfo::pressed{background-color: #717987;}" ) self.moreInfo.clicked.connect(self.show_more_info) self.moreInfo.hide() self.saveIcon.clicked.connect(self.save_times) self.saveIcon.hide() self.saveIcon.setStyleSheet( "QPushButton#saveIcon{ border-radius: 20px; font-weight: bold; color: white; background-color: #a2adc2; } QPushButton#saveIcon::hover{ background-color: #BDC5D4;} QPushButton#saveIcon::pressed{background-color: #717987;}" ) self.startStop.setStyleSheet( "QPushButton#startStop{ border-radius: 20px; font-weight: bold; color: white; background-color: #a2adc2; } QPushButton#startStop::hover{ background-color: #BDC5D4;} QPushButton#startStop::pressed{background-color: #717987;}" ) self.startStop.clicked.connect(self.start_crono) self.users.clicked.connect(self.open_users_menu) self.pacientesIcon.clicked.connect(self.open_patients_menu) self.settingsIcon.clicked.connect(self.open_settings) self.comboPatients.setStyleSheet( "QComboBox#comboPatients QAbstractItemView{ background-color: #fdfdff; color: #222628; selection-background-color: #555860; selection-color: #fdfdff;}" ) self.comboPatients.setPlaceholderText("Selecciona un paciente") self.patients_dni = [] self.current_patient = -1 self.fill_combo() self.comboPatients.currentIndexChanged.connect(self.select_new_patient) self.stopwatch = Stopwatch() self.stopwatch.stop() self.saved_msg.setStyleSheet("QWidget#saved_msg{ color: black }") self.centralwidget.setStyleSheet( "QWidget#centralwidget{ background-color: #fdfdff}") self.barraLateral.setStyleSheet( "QWidget#barraLateral{ background-color: #555860; }") self.cronIcon.setStyleSheet( "QPushButton#cronIcon::hover{ border: none; background-color: #a2adc2;} QPushButton#cronIcon::pressed{background-color: #222628;}" ) self.users.setStyleSheet( "QPushButton#users::hover{ border: none; background-color: #a2adc2;} QPushButton#users::pressed{background-color: #222628;}" ) self.pacientesIcon.setStyleSheet( "QPushButton#pacientesIcon::hover{ border: none; background-color: #a2adc2;} QPushButton#pacientesIcon::pressed{background-color: #222628;}" ) self.settingsIcon.setStyleSheet( "QPushButton#settingsIcon::hover{ border: none; background-color: #a2adc2;} QPushButton#settingsIcon::pressed{background-color: #222628;}" ) self.izquierda.setStyleSheet( "QWidget#izquierda::hover{ border: none; background-color: #e9e8eb;} " ) self.derecha.setStyleSheet( "QWidget#derecha::hover{ border: none; background-color: #e9e8eb;} " ) self.laps_image = QtGui.QIcon('test/img/laps.png') self.restart_image = QtGui.QIcon('test/img/restart.png') self.start_image = QtGui.QIcon('test/img/white.png') self.text = "" self.lap_num = 0 self.previous_time = 0 self.lap_times = [] self.timer = QtCore.QTimer(self) self.timer.timeout.connect(self.run_watch) self.timer.setInterval(10) self.mscounter = 0 self.isreset = True self.showLCD() def open_settings(self): """ Obri la finestra dels settings on es canviaràn els temps màxims i mínims del segments i total. """ self.new_window = Settings() def export_db(self): """ Exportar la BD actual """ export_path = QtWidgets.QFileDialog.getSaveFileName( self, 'Save file', QtCore.QDir.homePath(), "Archivo SQLite (*.db)") # Arrepleguem el nou path copy_file( load_properties().get('DatabaseSection', 'dbname'), export_path[0] ) # Copiem la BD de la seva path a la path donada per l'usuari QtWidgets.QMessageBox.information( self, 'Parkthon', "La base de datos ha sido exportada con éxito" ) # Mostrem un missatge d'éxit def import_db(self): """ Importar una nova BD. Com que aquesta funció també és utilitzada en login.py, s'ha implementat en l'arxiu __manifest__.py """ import_db(self) def show_more_info(self): """ Mostrem més informació del pacient actual """ self.window = patient_info.Patient_info( self.current_user, self.current_patient) # Llancem la nova finestra def save_times(self): """ Aquesta funció crida al controlador de la DB per guardar una llista de temps al pacient actual """ sql_con = sqlite.sqlite_connector() # Creem la connexió sql_con.save_lap_times(self.lap_times, self.current_patient, self.observations.toPlainText() ) # Guardem els temps en l'usuari actual sql_con.close() # Tanquem la connexió self.saveIcon.hide( ) # Amaguem l'icona de guardar ja que ja s'ha guardat self.observations.setText("") #Borrem el text de les observacions self.show_patient_graph() # Afegim aquest ultim temps a la gràfica self.saved_message_thread.start( ) # Escomencem el thread que mostrarà que el temps ha sigut guardat def fill_combo(self): """ Funció que plena el QComboBox amb tots els pacients del metge actual """ sql_con = sqlite.sqlite_connector() # Arrepleguem la connexió a SQLite patients = sql_con.get_patient_names( self.current_user ) # Arrepleguem una matriu amb les dades de tots els pacients del metge actual sql_con.close() # Tanquem la connexió una vegada recuperades les dades for patient in patients: self.patients_dni.append( patient[2] ) # Ens guardem els DNI de tots els pacients de forma interna self.comboPatients.addItem( patient[0] + " " + patient[1]) # Afegim el nom i cognom al QComboBox def select_new_patient(self): """ Seleccionem el nou pacient, en el que es realitzarán totes les accions com registrar nous temps, mostrar la gràfica, etc. """ self.current_patient = self.patients_dni[self.comboPatients.currentIndex( )] # Guardem de forma interna el DNI del pacient depenent del index del QComboBox (Per això guardavem els DNI en una llista, la cual està de forma paral.lela) self.show_patient_graph() # Carreguem la gràfica del pacient self.moreInfo.show( ) # Mostrem el botó de més informació sobre el pacient def show_patient_graph(self): """ Mostra/Carrega la gràfica del pacient """ sql_con = sqlite.sqlite_connector() # Arrepleguem el connector a la BD total_times = sql_con.get_patient_total_times( self.current_patient ) # Arrepleguem en forma de llista tots els temps dels pacients seg1_times = sql_con.get_patient_segment_times(self.current_patient, "lap1") seg2_times = sql_con.get_patient_segment_times(self.current_patient, "lap2") seg3_times = sql_con.get_patient_segment_times(self.current_patient, "lap3") dates = sql_con.get_patient_dates( self.current_patient ) # Arrepleguem els díes en els que es van realitzar les proves (Son llistes paral.leles) sql_con.close() # Tanquem la connexió self.graph.clear() # Esborrem tot el que hi ha en el gràfic self.graph.addLegend() # Inicialitzem la llegenda self.graph.plot(dates, total_times, name="Total", pen=pg.mkPen(color=(162, 173, 194), width=4), symbol='o', symbolSize=10, symbolBrush=(0, 0, 0)) # Mostrem les dades self.graph.plot(dates, seg1_times, name="Segmento 1", pen=pg.mkPen(color=(255, 0, 0), width=4), symbol='o', symbolSize=5, symbolBrush=(0, 0, 0)) self.graph.plot(dates, seg2_times, name="Segmento 2", pen=pg.mkPen(color=(255, 255, 0), width=4), symbol='o', symbolSize=5, symbolBrush=(0, 0, 0)) self.graph.plot(dates, seg3_times, name="Segmento 3", pen=pg.mkPen(color=(255, 0, 255), width=4), symbol='o', symbolSize=5, symbolBrush=(0, 0, 0)) def record_lap(self): """ Afegeix una nova lap al cronómetro """ if ( self.mscounter > 1000 ): # El contador ha de ser major a un segon per evitar problemes de compilació this_time = float( str(self.stopwatch) [:-1]) # Arrepleguem com a float el temps de la lap actual self.text += "Vuelta " + str( self.lap_num + 1 ) + ": " # Comencem a crear el text que es mostrarà en el número de volta if (self.lap_num == 0): # Si és la primera volta self.text += "{:.2f}".format( this_time ) # Al text li afegim el temps formatejat amb sols 2 decimals lap_type = get_lap_type( self.lap_num, this_time ) # Arrepleguem el tipus de lap (Lleu, Moderat, Greu) color = get_color_type( lap_type) # Arrepleguem el color (Depenent de la lap) else: lap_type = get_lap_type(self.lap_num, this_time - self.previous_time) color = get_color_type(lap_type) self.text += "{:.2f}".format(this_time - self.previous_time) self.text += " - <span style='color:" + color + ";'>" + lap_type + "</span><br/>" self.lapsList.setText(self.text) self.lap_num += 1 self.lap_times.append( float("{:.2f}".format(this_time - self.previous_time))) self.previous_time = this_time if (self.lap_num == 3): self.show_total_qualification() self.saveIcon.show() self.pause_watch() self.previous_time = 0 self.text += "<span style='color: #222628;'>¡VUELTAS COMPLETADAS!</span>" self.lapsList.setText(self.text) def show_total_qualification(self): self.quali_label.setVisible(True) total_time = 0 for time in self.lap_times: total_time += time lap_type = get_lap_type(-1, total_time) color = get_color_type(lap_type) self.quali_label.setText("Clasificación: <span style='color:" + color + ";'>" + lap_type + "</span>") def showLCD(self): """ Aquesta funció serveix per a mostrar per pantalla el temps """ if not self.isreset: # si "isreset" es False self.cronNum.setText(str(self.stopwatch)) else: self.cronNum.setText('0.00') def run_watch(self): """ Executa el cronómetre de forma interna """ self.mscounter += 10 self.showLCD() def start_crono(self): """ Comença el cronómetre de forma visible """ self.saveIcon.hide() self.lapsList.setText("") if (self.current_patient == -1): QtWidgets.QMessageBox.critical( self, 'ERROR', "Primero tienes que seleccionar un paciente.") else: if (self.isreset): self.stopwatch.start() self.timer.start() else: self.stopwatch.restart() self.timer.restart() self.startStop.setIcon( self.laps_image) # Passa a ser el botó de laps self.startStop.clicked.disconnect( self.start_crono) # Desconectem l'antic slot self.startStop.clicked.connect(self.record_lap) # Conectem el nou self.isreset = False def pause_watch(self): """ Pausa el cronómetre quan s'aplega a 3 voltes """ self.stopwatch.stop() self.timer.stop() self.startStop.setIcon(self.restart_image) self.startStop.clicked.disconnect(self.record_lap) self.startStop.clicked.connect(self.reset_watch) def reset_watch(self): """ Reinicia el cronómetre """ self.stopwatch.restart() self.mscounter = 0 self.lap_num = 0 self.isreset = True self.showLCD() self.lap_times = [] self.text = "" self.quali_label.setText("") self.quali_label.setVisible(False) self.startStop.clicked.disconnect(self.reset_watch) self.startStop.clicked.connect(self.start_crono) self.start_crono() def open_users_menu(self): self.new_window = user_management.Users_management() self.setVisible(False) self.close() def open_patients_menu(self): self.new_window = patient_management.Patient_management() self.setVisible(False) self.close() def closeEvent(self, event): if (self.isVisible()): if (salir()): event.accept() else: event.ignore() else: event.ignore()
class Window(Frame): def __init__(self, master): Frame.__init__(self, master) self.master = master self.master.title("SST") self.pack() # initializing mouse + keyboard objects to be used in prnt_scrpt() and del_sums_scrpt() self.keyboard = kb.Controller() self.mouse = ms.Controller() # Game Timer self.game_sw = Stopwatch() # Individual Role Timer Objects self.t_sw = Stopwatch().reset() self.j_sw = Stopwatch().reset() self.m_sw = Stopwatch().reset() self.a_sw = Stopwatch().reset() self.s_sw = Stopwatch().reset() # vars to store ss cds for CheckButtons self.top_ci = IntVar(master) self.jg_ci = IntVar(master) self.mid_ci = IntVar(master) self.ad_ci = IntVar(master) self.supp_ci = IntVar(master) # how many seconds each role's timer is when called self.topExactSecs = 0 self.jgExactSecs = 0 self.midExactSecs = 0 self.adExactSecs = 0 self.suppExactSecs = 0 self.summs_list = [ self.t_sw, self.j_sw, self.m_sw, self.a_sw, self.s_sw ] self.ci_list = [ self.top_ci, self.jg_ci, self.mid_ci, self.ad_ci, self.supp_ci ] self.exactRoleSecs = [ self.topExactSecs, self.jgExactSecs, self.midExactSecs, self.adExactSecs, self.suppExactSecs ] self.music_list = [ 'extras/top.mp3', 'extras/jg.mp3', 'extras/mid.mp3', 'extras/ad.mp3', 'extras/supp.mp3' ] self.top_img = ImageTk.PhotoImage(Image.open("extras/top.png")) self.jg_img = ImageTk.PhotoImage(Image.open("extras/jg.png")) self.mid_img = ImageTk.PhotoImage(Image.open("extras/mid.png")) self.ad_img = ImageTk.PhotoImage(Image.open("extras/ad.png")) self.supp_img = ImageTk.PhotoImage(Image.open("extras/supp.png")) self.ci_img = ImageTk.PhotoImage(Image.open("extras/ci.png")) # Individual Role Buttons + images + placing them on grid self.tButton = Button(self, height=50, image=self.top_img, command=self.swtch_top) self.tButton.image = self.top_img self.tButton.grid(row=0, column=1, columnspan=3, sticky=W + E, padx=5, pady=(5, 1)) self.jButton = Button(self, width=10, height=50, image=self.jg_img, command=self.swtch_jg) self.jButton.image = self.jg_img self.jButton.grid(row=1, column=1, columnspan=3, sticky=W + E, padx=5, pady=1) self.mButton = Button(self, width=10, height=50, image=self.mid_img, command=self.swtch_mid) self.mButton.image = self.mid_img self.mButton.grid(row=2, column=1, columnspan=3, sticky=W + E, padx=5, pady=1) self.aButton = Button(self, width=10, height=50, image=self.ad_img, command=self.swtch_ad) self.aButton.image = self.ad_img self.aButton.grid(row=3, column=1, columnspan=3, sticky=W + E, padx=5, pady=1) self.sButton = Button(self, width=10, height=50, image=self.supp_img, command=self.swtch_supp) self.sButton.image = self.supp_img self.sButton.grid(row=4, column=1, columnspan=3, sticky=W + E, padx=5) self.buttonList = [ self.tButton, self.jButton, self.mButton, self.aButton, self.sButton ] # Specific timer(s) for each role (to be entered manually) self.manTopField = Entry(self, width=10) self.manTopField.insert(END, '') self.manTopField.grid(row=0, column=6, columnspan=2, sticky=E, pady=(5, 1)) self.manJgField = Entry(self, width=10) self.manJgField.insert(END, '') self.manJgField.grid(row=1, column=6, columnspan=2, sticky=E, pady=(5, 1)) self.manMidField = Entry(self, width=10) self.manMidField.insert(END, '') self.manMidField.grid(row=2, column=6, columnspan=2, sticky=E, pady=(5, 1)) self.manAdField = Entry(self, width=10) self.manAdField.insert(END, '') self.manAdField.grid(row=3, column=6, columnspan=2, sticky=E, pady=(5, 1)) self.manSuppField = Entry(self, width=10) self.manSuppField.insert(END, '') self.manSuppField.grid(row=4, column=6, columnspan=2, sticky=E, pady=(5, 1)) self.delay_list = [ self.manTopField.get(), self.manJgField.get(), self.manMidField.get(), self.manAdField.get(), self.manSuppField.get() ] self.entryList = [ self.manTopField, self.manJgField, self.manMidField, self.manAdField, self.manSuppField ] # Cosmic Insight Checkbuttons (for each role) + ci icon + placing them on grid self.t_ci = Checkbutton(self, variable=self.top_ci, onvalue=285, offvalue=300) self.t_ci.grid(row=0, column=0, pady=(5, 1), padx=1) self.j_ci = Checkbutton(self, variable=self.jg_ci, onvalue=285, offvalue=300) self.j_ci.grid(row=1, column=0, padx=1) self.m_ci = Checkbutton(self, variable=self.mid_ci, onvalue=285, offvalue=300) self.m_ci.grid(row=2, column=0, padx=1) self.a_ci = Checkbutton(self, variable=self.ad_ci, onvalue=285, offvalue=300) self.a_ci.grid(row=3, column=0, padx=1) self.s_ci = Checkbutton(self, variable=self.supp_ci, onvalue=285, offvalue=300) self.s_ci.grid(row=4, column=0, padx=1) self.ci_image = Label(self, image=self.ci_img) self.ci_image.image = self.ci_image self.ci_image.grid(row=6, column=0, sticky=W) # Game Timer label + grid self.game_gui_label = Label(self, text="Game Timer:") self.game_gui_label.grid(row=6, column=3, ipadx=20) # Textbox to type in timer offset self.game_timer = Entry(self, width=10) self.game_timer.insert(END, '') self.game_timer.grid(row=6, column=5, columnspan=3, pady=10) self.game_timer.bind("<Return>", self.retrieve_input) self.bind('<FocusOut>', self.start_sums) self.bind('<FocusIn>', self.on_focus) # Clipboard_final = final string to be "copied" and printed out to the screen self.cb_final = '' # game timer offset provided by the user self.rel_inp = 0 # 1st temp set of roles (used later) self.temp_top = 0 self.temp_jg = 0 self.temp_mid = 0 self.temp_ad = 0 self.temp_supp = 0 # 2nd temp set of roles (used later) self.t_top = '' self.t_jg = '' self.t_mid = '' self.t_ad = '' self.t_supp = '' # indiv. role strings to be appended to newest_iteration self.top_string = '' self.jg_string = '' self.mid_string = '' self.ad_string = '' self.supp_string = '' # buffer var to be checked w/ cb_final to allow mult roles to be printed in one action/line self.newest_iteration = '' # whether or not role's button is selected currently self.topBool = False self.jgBool = False self.midBool = False self.adBool = False self.suppBool = False # keeps track whether summ is currently copied to "clipboard" self.t_copied = False self.j_copied = False self.m_copied = False self.a_copied = False self.s_copied = False self.t_valid = False self.j_valid = False self.m_valid = False self.a_valid = False self.s_valid = False self.manValidList = [ self.t_valid, self.j_valid, self.m_valid, self.a_valid, self.s_valid ] # ALL 'swtch_foo()' methods are used to toggle state of its respective button. (probably refactor at some point) def swtch_top(self): if self.topBool: self.topBool = False self.tButton.configure(bg="SystemButtonFace") else: self.topBool = True self.tButton.configure(bg="green") def swtch_jg(self): if self.jgBool: self.jgBool = False self.jButton.configure(bg="SystemButtonFace") else: self.jgBool = True self.jButton.configure(bg="green") def swtch_mid(self): if self.midBool: self.midBool = False self.mButton.configure(bg="SystemButtonFace") else: self.midBool = True self.mButton.configure(bg="green") def swtch_ad(self): if self.adBool: self.adBool = False self.aButton.configure(bg="SystemButtonFace") else: self.adBool = True self.aButton.configure(bg="green") def swtch_supp(self): if self.suppBool: self.suppBool = False self.sButton.configure(bg="SystemButtonFace") else: self.suppBool = True self.sButton.configure(bg="green") # ========= Any "event" param added below necessary because the function calling it is passing a value ============= # is called whenever focus is lost on window, # if summ's button is active, its delay is applied, timer started, button color changed, and boolean reset def start_sums(self, event): # eventually make this so that it will allow the default to be a blank entry field w/o needing 0 # pretty low prio tbh # ALSO try and get summs to be printed out in order of which comes up first # ALSO need to copypasta da mechs to allow game timer to be written as mmss # ALSO need to figure out eventually how to get api to figure out whether or not CI is present # have a lil error handling section here! try and catch, if catch just pass it i believe # maybe need to lookup what the error name was (eeek! that means you will have to willingly let ur code break!) for q in range(len(self.delay_list)): try: self.delay_list[q] = int(self.entryList[q].get()) except ValueError: self.manValidList[q] = False else: self.manValidList[q] = True #self.delay_list[0] = int(self.manTopField.get()) #self.delay_list[1] = int(float(self.manJgField.get())) #self.delay_list[2] = int(float(self.manMidField.get())) #self.delay_list[3] = int(float(self.manAdField.get())) #self.delay_list[4] = int(float(self.manSuppField.get())) if self.topBool: self.t_sw.restart() self.tButton.configure(bg="SystemButtonFace") self.tButton["state"] = "disabled" self.topBool = False self.exactRoleSecs[0] = self.ci_list[0].get() elif self.manValidList[0] and self.t_sw.duration == 0: self.t_sw.restart() self.tButton.configure(bg="SystemButtonFace") self.tButton["state"] = "disabled" self.topBool = False self.exactRoleSecs[0] = int(self.delay_list[0] % 100 + (self.delay_list[0] // 100) * 60)\ - int(self.game_sw.duration + self.rel_inp) if self.jgBool: self.j_sw.restart() self.jButton.configure(bg="SystemButtonFace") self.jButton["state"] = "disabled" self.jgBool = False self.exactRoleSecs[1] = self.ci_list[1].get() elif self.manValidList[1] and self.j_sw.duration == 0: self.j_sw.restart() self.jButton.configure(bg="SystemButtonFace") self.jButton["state"] = "disabled" self.jgBool = False self.exactRoleSecs[1] = int(self.delay_list[1] % 100 + (self.delay_list[1] // 100) * 60)\ - int(self.game_sw.duration + self.rel_inp) if self.midBool: self.m_sw.restart() self.mButton.configure(bg="SystemButtonFace") self.mButton["state"] = "disabled" self.midBool = False self.exactRoleSecs[2] = self.ci_list[2].get() elif self.manValidList[2] and self.m_sw.duration == 0: self.m_sw.restart() self.mButton.configure(bg="SystemButtonFace") self.mButton["state"] = "disabled" self.midBool = False self.exactRoleSecs[2] = int(self.delay_list[2] % 100 + (self.delay_list[2] // 100) * 60)\ - int(self.game_sw.duration + self.rel_inp) if self.adBool: self.a_sw.restart() self.aButton.configure(bg="SystemButtonFace") self.aButton["state"] = "disabled" self.adBool = False self.exactRoleSecs[3] = self.ci_list[3].get() elif self.manValidList[3] and self.a_sw.duration == 0: self.a_sw.restart() self.aButton.configure(bg="SystemButtonFace") self.aButton["state"] = "disabled" self.adBool = False self.exactRoleSecs[3] = int(self.delay_list[3] % 100 + (self.delay_list[3] // 100) * 60)\ - int(self.game_sw.duration + self.rel_inp) if self.suppBool: self.s_sw.restart() self.sButton.configure(bg="SystemButtonFace") self.sButton["state"] = "disabled" self.suppBool = False self.exactRoleSecs[4] = self.ci_list[4].get() elif self.manValidList[4] and self.s_sw.duration == 0: self.s_sw.restart() self.sButton.configure(bg="SystemButtonFace") self.sButton["state"] = "disabled" self.suppBool = False self.exactRoleSecs[4] = int(self.delay_list[4] % 100 + (self.delay_list[4] // 100) * 60)\ - int(self.game_sw.duration + self.rel_inp) # is called whenever focus is gained on window, # flushes delay buttons' and unused/fresh summ's colors to default, def on_focus(self, event): for role in range(len(self.summs_list)): if self.summs_list[role].duration > 0.0: self.entryList[role].delete(0, 'end') self.entryList[role].insert( 0, str( int(self.exactRoleSecs[role] - self.summs_list[role].duration))) # red if (self.exactRoleSecs[role] - self.summs_list[role].duration) > 200: self.entryList[role].config(background=_from_rgb((255, 0, 0))) # orange elif 200 >= (self.exactRoleSecs[role] - self.summs_list[role].duration) > 100: self.entryList[role].config(background=_from_rgb((255, 100, 0))) # yellow elif 100 >= (self.exactRoleSecs[role] - self.summs_list[role].duration) > 0: self.entryList[role].config(background=_from_rgb((255, 255, 0))) #print(self.summs_list[role].duration) ## green(s) #if self.summs_list[role] == 0.0: # print('reached') # self.buttonList[role]["state"] = "normal" # self.entryList[role].config(background=_from_rgb((0, 255, 0))) # print('reached2') # called when <Enter> button is pressed -> apply's given offset + starts game timer def retrieve_input(self, event): # make this adaptive based off of number of digits self.rel_inp = int(self.game_timer.get()) self.game_sw.restart() print(self.rel_inp) # generalized method to add any summ def add_new_summ(self, role_index, temp_role, t_role, role_string, role_copied, role_name): temp_role = int(self.game_sw.duration + self.rel_inp - 1 + self.exactRoleSecs[role_index]) if summ_rounder(t_role, temp_role) == '59': role_string = str((temp_role // 60) + 1) + role_name else: role_string = str(temp_role // 60) + summ_rounder( t_role, temp_role) + role_name if role_string[:-1 * len(role_name)] in self.newest_iteration: role_string = role_name self.newest_iteration += role_string role_copied = True return temp_role, t_role, role_string, role_copied, self.newest_iteration # used to add new summs by bringing up chat + typing out updated string + closing out of League's chat def prnt_scrpt(self): time.sleep(.15) # bringing up chat self.keyboard.tap(kb.Key.enter) # typing timer(s) self.keyboard.type(self.cb_final) # selecting all self.keyboard.press(kb.Key.ctrl_l) time.sleep(.001) self.keyboard.press('a') self.keyboard.release('a') self.keyboard.release(kb.Key.ctrl_l) # copying all self.keyboard.press(kb.Key.ctrl_l) time.sleep(.001) self.keyboard.press('c') self.keyboard.release('c') self.keyboard.release(kb.Key.ctrl_l) # deleting all + esc'ing chat time.sleep(.001) self.keyboard.press(kb.Key.delete) self.keyboard.release(kb.Key.delete) self.keyboard.tap(kb.Key.enter) time.sleep(.15)
class Game(PygameGame): def __init__(self): width = 800 height = 500 fps = 40 fullCh = 255 bgColor = (fullCh, fullCh, fullCh) title = "Bouncy Bouncy Revamped" super().__init__(width, height, fps, title, bgColor) def initStyles(self): assert(pygame.font.get_init()) assert(pygame.image.get_extended()) self.font = Struct() textSize = 16 self.font.text = pygame.font.SysFont("Arial", textSize) titleSize = 30 self.font.title = pygame.font.SysFont("Arial", titleSize) self.font.titleEmph = pygame.font.SysFont("Arial", titleSize, bold=True, italic=True) self.margin = 10 def initMaps(self): self.maps = Maps(self.margin + 100, self.margin, self.width - 2*self.margin, self.height - 2*self.margin) self.progress = Progress(self.maps.levels, self) def initMainMenu(self): top = 130 left = 80 entries = ["Play", "Level select", "Instructions"] actions = [self.playLevel, self.doLevelMenu, self.doHelp] self.mainMenu = Menu(left, top, entries, actions) def initLevelMenu(self): top = 60 left = 130 entries = [] actions = [] for i, level in enumerate(self.maps.levels): prevLevel = self.maps.levels[i - 1] if i > 0 and self.progress[prevLevel]["score"] == None: entries.append("Level %s Locked" % level) actions.append(None) continue elif self.progress[level]["score"] == None: entries.append("Level %s Unlocked" % level) else: entries.append("Level %s Highscore: %d Best time: %s" % (level, self.progress[level]["score"], Stopwatch.secToMin(self.progress[level]["time"]))) # Locally scope level actions.append(lambda level=level: self.playLevel(level)) entries += ["Return to main menu", "Clear progress"] actions += [self.doMainMenu, self.progress.clear] self.levelMenu = Menu(left, top, entries, actions) def initPauseMenu(self): def play(): self.mode = "play" top = 200 left = 300 width = 140 height = 80 entries = ["Continue", "Main menu", "Turn sound off"] actions = [play, self.doMainMenu, self.toggleSound] self.pauseMenu = Menu(left, top, entries, actions) background = pygame.Surface((width + self.margin, height + self.margin)) background.fill(self.bgColor) pygame.draw.rect(background, (0, 0, 0), background.get_rect(), 2) self.pauseMenu.background = background def initMenus(self): self.initMainMenu() self.initLevelMenu() self.initPauseMenu() def init(self): self.initStyles() self.initMaps() Terrain.initImages() Terrain.initSounds() self.initMenus() self.stopwatch = Stopwatch() self.initHelp() self.mode = "menu" self.menu = self.mainMenu def loadLevel(self): terrain, self.startpos = self.maps.load(self.level) self.terrain = pygame.sprite.Group(*terrain) self.ball = Ball(*self.startpos) self.score = 0 self.stopwatch.restart() if self.level != "test": highscore = self.progress[self.level]["score"] bestTime = self.progress[self.level]["time"] else: highscore, bestTime = None, None self.highscoreText = str(highscore) if highscore != None else "---" if bestTime != None: self.bestTimeText = Stopwatch.secToMin(bestTime) else: self.bestTimeText = "---" def nextLevel(self): # Update and save progress saved = self.progress[self.level] if saved["score"] == None or self.score > saved["score"]: saved["score"] = self.score duration = self.stopwatch.getSeconds() if saved["time"] == None or duration < saved["time"]: saved["time"] = duration self.progress.save() # Load next level Terrain.playSound("happy") index = self.maps.levels.index(self.level) + 1 if index >= len(self.maps.levels): self.mode = "win" else: self.level = self.maps.levels[index] self.loadLevel() def initGame(self, level): self.level = level self.loadLevel() def killBall(self): Terrain.playSound("sad") self.loadLevel() def keyPressed(self, keyCode, modifier): #print(keyCode) if self.mode == "menu": self.menu.key(keyCode) elif self.mode == "play": if keyCode == 27: self.mode = "pause" self.menu = self.pauseMenu elif self.mode == "pause": self.menu.key(keyCode) if keyCode == 27: self.mode = "play" elif self.mode == "levelMenu": self.menu.key(keyCode) if keyCode == 27: self.mode = "menu" self.menu = self.mainMenu elif self.mode == "win": if keyCode == 13: self.mode = "menu" self.menu = self.mainMenu elif self.mode == "help": if keyCode == 27: self.mode = "menu" def ballFly(self, ball, cannon): ball.flying = True ball.vy = 0 ball.vx = cannon.direction*ball.flyvx # Reposition the ball ball.y = cannon.y + ball.realR if cannon.direction > 0: ball.x = cannon.x + cannon.width + ball.realR else: ball.x = cannon.x - ball.realR def timerFiredPlay(self): if (self.ball.x < self.maps.left or self.ball.x > self.maps.left + self.maps.width or self.ball.y < self.maps.top or self.ball.y > self.maps.top + self.maps.height): # Ball off map self.killBall() collided = pygame.sprite.spritecollide(self.ball, self.terrain, False, Terrain.collidedFn) if len(collided) > 0: elements = Terrain.manageCollision(self.ball, collided) for element, direction in elements: result = element.interactFromDir(self.ball, direction) if result == "score": self.score += element.points elif result == "win": self.nextLevel() elif result == "fly": self.ballFly(self.ball, element) elif result == "kill": self.killBall() self.ball.update(self.isKeyPressed) def timerFired(self, dt): if self.mode == "play": self.timerFiredPlay() self.stopwatch.tick(dt) def drawMenu(self, screen): titleTop = 70 titleLeft = 60 x = drawText(screen, titleLeft, titleTop, text="Bouncy Bouncy ", font=self.font.title, anchor="nw") space = 8 drawText(screen, x + space, titleTop, text="Revamped", font=self.font.titleEmph, anchor="nw") self.mainMenu.draw(screen, self.font.text) def drawGame(self, screen): self.terrain.draw(screen) self.ball.draw(screen) pos = 30 drawText(screen, self.margin, pos, text="Level %s" % self.level, font=self.font.text, anchor="nw") pos = 80 drawText(screen, self.margin, pos, text="Score: %d" % self.score, font=self.font.text, anchor="nw") pos = 110 drawText(screen, self.margin, pos, text="Highscore:", font=self.font.text, anchor="nw") pos = 130 drawText(screen, self.margin, pos, text=self.highscoreText, font=self.font.text, anchor="nw") self.drawGameTime(screen) def drawGameTime(self, screen): pos = 180 drawText(screen, self.margin, pos, text=str(self.stopwatch), font=self.font.text, anchor="nw") pos = 210 drawText(screen, self.margin, pos, text="Best time:", font=self.font.text, anchor="nw") pos = 230 drawText(screen, self.margin, pos, text=self.bestTimeText, font=self.font.text, anchor="nw") def drawPause(self, screen): screen.blit(self.pauseMenu.background, (self.pauseMenu.x - self.margin, self.pauseMenu.y - self.margin)) self.pauseMenu.draw(screen, self.font.text) def drawLevelMenu(self, screen): titleTop = 60 titleLeft = 30 drawText(screen, titleLeft, titleTop, text="Level select", font=self.font.text, anchor="nw") self.levelMenu.draw(screen, self.font.text) def drawWin(self, screen): space = 24 screen.blit(self.pauseMenu.background, (self.pauseMenu.x - self.margin, self.pauseMenu.y - self.margin)) drawText(screen, self.pauseMenu.x, self.pauseMenu.y, text="Congratulations!", font=self.font.text, anchor="nw") drawText(screen, self.pauseMenu.x, self.pauseMenu.y + space, text="Press Enter...", font=self.font.text, anchor="nw") def drawHelp(self, screen): top = 50 left = 40 drawText(screen, left, top, text="Instructions", font=self.font.text, anchor="nw") top = 80 lineHeight = 24 for line, text in enumerate(self.helpText): drawText(screen, left, top + line*lineHeight, text=text, font=self.font.text, anchor="nw") top = 350 drawText(screen, left, top, text="Press Escape now for the main menu", font=self.font.text, anchor="nw") def redrawAll(self, screen): if self.mode == "menu": self.drawMenu(screen) elif self.mode == "play": self.drawGame(screen) elif self.mode == "pause": self.drawGame(screen) self.drawPause(screen) elif self.mode == "levelMenu": self.drawLevelMenu(screen) elif self.mode == "win": self.drawGame(screen) self.drawWin(screen) elif self.mode == "help": self.drawHelp(screen) def playLevel(self, level="1"): self.mode = "play" self.initGame(level) def doMainMenu(self): self.mode = "menu" self.menu = self.mainMenu def doLevelMenu(self): self.mode = "levelMenu" self.initLevelMenu() self.menu = self.levelMenu def doHelp(self): self.mode = "help" def toggleSound(self): if Terrain.soundsOn: Terrain.soundsOn = False return "sound off" else: Terrain.soundsOn = True return "sound on" def initHelp(self): self.helpText = ["Use Left/Right arrow keys to move.", "Avoid pits and spikes.", "Hold both Left/Right arrow keys to wall jump!", "Cannons make you fly. Hit the opposite arrow key to stop flying.", "Press Escape in game to pause or change settings."]
class Orchestra: SAMPLE_RATE = 44100 BYTES_PER_SAMPLE = 2 # mpg123, used by predecode, outputs 16-bit PCM mono PLAYABLE_FORMATS = ['mp3', 'flac', 'wav', 'm4b'] JACK = "jack" SSR = "ssr" @staticmethod def add_parser_arguments(parser): parser.add_argument("--rt", action="store_true", dest="realtime") parser.add_argument("-t", "--torrent", dest="torrentname", default="") parser.add_argument("-z", "--timefactor", dest="timefactor", type=float, default=1) parser.add_argument("--start", dest="start_time", type=float, default=0) parser.add_argument("-q", "--quiet", action="store_true", dest="quiet") parser.add_argument("--pretend-sequential", action="store_true", dest="pretend_sequential") parser.add_argument("--gui", action="store_true", dest="gui_enabled") parser.add_argument("--fast-forward", action="store_true", dest="ff") parser.add_argument("--fast-forward-to-start", action="store_true", dest="ff_to_start") parser.add_argument("--quit-at-end", action="store_true", dest="quit_at_end") parser.add_argument("--loop", dest="loop", action="store_true") parser.add_argument("--max-pause-within-segment", type=float) parser.add_argument("--max-segment-duration", type=float) parser.add_argument("--looped-duration", dest="looped_duration", type=float) parser.add_argument("-o", "--output", dest="output", type=str, default=Orchestra.JACK) parser.add_argument("--include-non-playable", action="store_true") parser.add_argument("-f", "--file", dest="selected_files", type=int, nargs="+") parser.add_argument("--title", type=str, default="") parser.add_argument("--pretend-audio", dest="pretend_audio_filename") parser.add_argument("--capture-audio") parser.add_argument("--leading-pause", type=float, default=0) _extension_re = re.compile('\.(\w+)$') def __init__(self, server, sessiondir, tr_log, options): self.server = server self.options = options self.sessiondir = sessiondir self.tr_log = tr_log self.realtime = options.realtime self.timefactor = options.timefactor self.quiet = options.quiet self._loop = options.loop self.looped_duration = options.looped_duration self.output = options.output self.include_non_playable = options.include_non_playable self._leading_pause = options.leading_pause if server.options.locate_peers: self._peer_location = {} for peeraddr in tr_log.peers: self._peer_location[peeraddr] = server.ip_locator.locate(peeraddr) self._peers_center_location_x = self._get_peers_center_location_x() if options.pretend_audio_filename: self._pretended_file = self._fileinfo_for_pretended_audio_file() self._pretended_file["duration"] = self._get_file_duration(self._pretended_file) self._pretended_files = [self._pretended_file] self._files_to_play = self._pretended_files else: self._files_to_play = self.tr_log.files self.predecode = server.options.predecode if self.predecode: predecoder = Predecoder( tr_log.files, sample_rate=self.SAMPLE_RATE, location=tr_log.file_location) predecoder.decode(server.options.force_predecode) if options.pretend_audio_filename: predecoder = Predecoder( self._pretended_files, sample_rate=self.SAMPLE_RATE) predecoder.decode(server.options.force_predecode) if options.selected_files: tr_log.select_files(options.selected_files) self.playback_enabled = True self.fast_forwarding = False self.gui = None self._check_which_files_are_audio() self._player_class = WavPlayer self.players = [] self._player_for_peer = dict() self._prepare_playable_files() self.stopwatch = Stopwatch() self.playable_chunks = self._filter_playable_chunks(tr_log, tr_log.chunks) if self.include_non_playable: self.chunks = tr_log.chunks self._num_selected_files = len(self.tr_log.files) else: self.chunks = self.playable_chunks self._num_selected_files = self._num_playable_files logger.debug("total num chunks: %s" % len(tr_log.chunks)) logger.debug("num playable chunks: %s" % len(self.playable_chunks)) logger.debug("num selected chunks: %s" % len(self.chunks)) self.score = self._interpret_chunks_to_score(tr_log, self.playable_chunks, options) self.estimated_duration = self._estimated_playback_duration(self.score, options) print "playback duration: %s" % datetime.timedelta(seconds=self.estimated_duration) self._chunks_by_id = {} self.segments_by_id = {} self._playing = False self._quitting = False self._was_stopped = False self.space = Space() self._scheduler_queue = Queue.Queue() self.scheduler = sched.scheduler(time.time, time.sleep) self._run_scheduler_thread() if self.output == self.SSR: self.ssr = SsrControl() self._warned_about_max_sources = False def init_playback(self): if self.server.options.no_synth: self.synth = None else: from synth_controller import SynthController self.synth = SynthController(logger) self.synth.launch_engine(self.server.options.sc_mode) self.synth.connect(self.synth.lang_port) self.synth.subscribe_to_info() if self.options.capture_audio: self._load_sounds() self._start_capture_audio() self._tell_visualizers("/synth_address", self.synth.lang_port) if self.output == self.SSR: self.ssr.run() if not self.options.capture_audio: self._load_sounds() self._log_time_for_last_handled_event = 0 if self.options.ff_to_start: self._ff_to_time = self.options.start_time self.set_time_cursor(0) else: self._ff_to_time = None self.set_time_cursor(self.options.start_time) def _start_capture_audio(self): self._audio_capture_filename = self.options.capture_audio if os.path.exists(self._audio_capture_filename): os.remove(self._audio_capture_filename) self._audio_capture_process = subprocess.Popen( ["./jack_capture/jack_capture", "-f", self._audio_capture_filename, "-d", "-1", "-B", "65536", "SuperCollider:out_1", "SuperCollider:out_2"], shell=False, stdout=subprocess.PIPE) self._wait_until_audio_capture_started() def _wait_until_audio_capture_started(self): print "waiting for audio capture to start" while True: line = self._audio_capture_process.stdout.readline().rstrip("\r\n") m = re.match('^audio capture started at (.*)$', line) if m: audio_capture_start_time = float(m.group(1)) self._tell_visualizers("/audio_captured_started", str(audio_capture_start_time)) print "audio capture started" return @classmethod def _estimated_playback_duration(cls, score, options): last_segment = score[-1] return last_segment["onset"] / options.timefactor + last_segment["duration"] @classmethod def _interpret_chunks_to_score(cls, tr_log, chunks, options): score = Interpreter(options.max_pause_within_segment, options.max_segment_duration).interpret( chunks, tr_log.files) for segment in score: segment["duration"] /= options.timefactor return score @classmethod def _filter_playable_chunks(cls, tr_log, chunks): return filter(lambda chunk: (cls._chunk_is_playable(tr_log, chunk)), chunks) @classmethod def _chunk_is_playable(cls, tr_log, chunk): file_info = tr_log.files[chunk["filenum"]] return file_info["playable_file_index"] != -1 def _run_scheduler_thread(self): self._scheduler_thread = threading.Thread(target=self._process_scheduled_events) self._scheduler_thread.daemon = True self._scheduler_thread.start() def _process_scheduled_events(self): while not self._quitting: while True: try: delay, priority, action, arguments = self._scheduler_queue.get(True, 0.01) except Queue.Empty: break self.scheduler.enter(delay, priority, action, arguments) self.scheduler.run() def _handle_visualizing_message(self, path, args, types, src, data): segment_id = args[0] segment = self.segments_by_id[segment_id] logger.debug("visualizing segment %s" % segment) player = self.get_player_for_segment(segment) self._ask_synth_to_play_segment(segment, channel=0, pan=player.spatial_position.pan) def _ask_synth_to_play_segment(self, segment, channel, pan): if self.synth: logger.debug("asking synth to play %s" % segment) file_info = self.tr_log.files[segment["filenum"]] if self.output == self.SSR: segment["sound_source_id"] = self.ssr.allocate_source() if segment["sound_source_id"] and not self._warned_about_max_sources: channel = segment["sound_source_id"] - 1 pan = None else: print "WARNING: max sources exceeded, skipping segment playback (this warning will not be repeated)" self._warned_about_max_sources = True return self.synth.play_segment( segment["id"], segment["audio_filenum"], segment["relative_start_time_in_file"], segment["relative_end_time_in_file"], segment["duration"], self.looped_duration, channel, pan) self._scheduler_queue.put( (segment["playback_duration"], 1, self.stopped_playing, [segment])) def _check_which_files_are_audio(self): for file_info in self.tr_log.files: file_info["is_audio"] = self._has_audio_extension(file_info["name"]) @staticmethod def _has_audio_extension(filename): return Orchestra._extension(filename) in Orchestra.PLAYABLE_FORMATS @staticmethod def _extension(filename): m = Orchestra._extension_re.search(filename) if m: return m.group(1).lower() def _prepare_playable_files(self): if self.predecode: self._num_playable_files = self._get_wav_files_info( self.tr_log, self.include_non_playable) else: raise Exception("playing wav without precoding is not supported") def _load_sounds(self): if self.synth: print "loading sounds" for filenum in range(len(self._files_to_play)): file_info = self._files_to_play[filenum] if file_info["playable_file_index"] != -1: logger.info("load_sound(%s)" % file_info["decoded_name"]) result = self._load_sound_stubbornly(filenum, file_info["decoded_name"]) logger.info("load_sound result: %s" % result) print "OK" def _load_sound_stubbornly(self, filenum, filename): while True: result = self.synth.load_sound(filenum, filename) if result > 0: return result else: warn(logger, "synth returned %s - retrying soon" % result) time.sleep(1.0) @classmethod def _get_wav_files_info(cls, tr_log, include_non_playable=False): playable_file_index = 0 for filenum in range(len(tr_log.files)): file_info = tr_log.files[filenum] file_info["playable_file_index"] = -1 if "decoded_name" in file_info: file_info["duration"] = cls._get_file_duration(file_info) if file_info["duration"] > 0: file_info["playable_file_index"] = playable_file_index logger.debug("duration for %r: %r\n" % (file_info["name"], file_info["duration"])) playable_file_index += 1 if include_non_playable: file_info["index"] = filenum else: file_info["index"] = file_info["playable_file_index"] return playable_file_index @classmethod def _get_file_duration(cls, file_info): if "decoded_name" in file_info: statinfo = os.stat(file_info["decoded_name"]) wav_header_size = 44 return float((statinfo.st_size - wav_header_size) / cls.BYTES_PER_SAMPLE) / cls.SAMPLE_RATE def get_current_log_time(self): if self.fast_forwarding: return self._log_time_for_last_handled_event else: return self.log_time_played_from + self.stopwatch.get_elapsed_time() * self.timefactor def play_non_realtime(self, quit_on_end=False): logger.info("entering play_non_realtime") self._was_stopped = False self._num_finished_visualizers = 0 if self._loop: while True: self._play_until_end() if not self._was_stopped: self._wait_for_visualizers_to_finish() self.set_time_cursor(0) else: self._play_until_end() if not self._was_stopped: self._wait_for_visualizers_to_finish() if quit_on_end: self._quit() logger.info("leaving play_non_realtime") def _quit(self): if self.options.capture_audio: self._audio_capture_process.kill() self._quitting = True def _play_until_end(self): logger.info("entering _play_until_end") self._playing = True self.stopwatch.start() time.sleep(self._leading_pause) no_more_events = False while self._playing and not no_more_events: event = self._get_next_chunk_or_segment() if event: self._handle_event(event) else: no_more_events = True logger.info("leaving _play_until_end") def _get_next_chunk_or_segment(self): logger.debug("chunk index = %d, segment index = %d" % ( self.current_chunk_index, self.current_segment_index)) chunk = self._get_next_chunk() segment = self._get_next_segment() logger.debug("next chunk: %s" % chunk) logger.debug("next segment: %s" % segment) if chunk and segment: return self._choose_nearest_chunk_or_segment(chunk, segment) elif chunk: return {"type": "chunk", "chunk": chunk} elif segment: return {"type": "segment", "segment": segment} else: return None def _get_next_chunk(self): if self.current_chunk_index < len(self.chunks): return self.chunks[self.current_chunk_index] def _get_next_segment(self): if len(self.score) == 0: return None elif self.current_segment_index < len(self.score): return self.score[self.current_segment_index] def _handle_event(self, event): if event["type"] == "chunk": self.handle_chunk(event["chunk"]) self.current_chunk_index += 1 elif event["type"] == "segment": self.handle_segment(event["segment"]) self.current_segment_index += 1 else: raise Exception("unexpected event %s" % event) def _choose_nearest_chunk_or_segment(self, chunk, segment): if chunk["t"] < segment["onset"]: return {"type": "chunk", "chunk": chunk} else: return {"type": "segment", "segment": segment} def stop(self): # stop_all disabled as it also deletes ~reverb # if self.synth: # self.synth.stop_all() self._was_stopped = True self._playing = False self.log_time_played_from = self.get_current_log_time() self.stopwatch.stop() def handle_segment(self, segment): logger.debug("handling segment %s" % segment) player = self.get_player_for_segment(segment) if not player: logger.debug("get_player_for_segment returned None - skipping playback") if self.fast_forwarding: self._stop_ff_if_necessary() else: now = self.get_current_log_time() time_margin = segment["onset"] - now logger.debug("time_margin=%f-%f=%f" % (segment["onset"], now, time_margin)) if not self.realtime and time_margin > 0: sleep_time = time_margin logger.debug("sleeping %f" % sleep_time) time.sleep(sleep_time) if player: logger.debug("player.enabled=%s" % player.enabled) if player and player.enabled: player.play(segment, pan=0.5) self._log_time_for_last_handled_event = segment["onset"] def handle_chunk(self, chunk): logger.debug("handling chunk %s" % chunk) player = self.get_player_for_chunk(chunk) logger.debug("get_player_for_chunk returned %s" % player) if self.fast_forwarding: self._stop_ff_if_necessary() else: now = self.get_current_log_time() time_margin = chunk["t"] - now logger.debug("time_margin=%f-%f=%f" % (chunk["t"], now, time_margin)) if not self.realtime and time_margin > 0: sleep_time = time_margin logger.debug("sleeping %f" % sleep_time) time.sleep(sleep_time) if player: logger.debug("player.enabled=%s" % player.enabled) if player and player.enabled: player.visualize(chunk) self._log_time_for_last_handled_event = chunk["t"] def _stop_ff_if_necessary(self): if self._ff_to_time is not None and \ self._log_time_for_last_handled_event >= self._ff_to_time: self._ff_to_time = None self.fast_forwarding = False self.set_time_cursor(self.log_time_played_from) def highlight_segment(self, segment): if self.gui: self.gui.highlight_segment(segment) def visualize_chunk(self, chunk, player): if len(self.visualizers) > 0: self._inform_visualizers_about_peer(player) file_info = self.tr_log.files[chunk["filenum"]] self._chunks_by_id[chunk["id"]] = chunk self._tell_visualizers( "/chunk", chunk["id"], chunk["begin"], chunk["end"] - chunk["begin"], file_info["index"], player.id, chunk["t"]) def visualize_segment(self, segment, player): if len(self.visualizers) > 0: self._inform_visualizers_about_peer(player) file_info = self.tr_log.files[segment["filenum"]] self.segments_by_id[segment["id"]] = segment self._tell_visualizers( "/segment", segment["id"], segment["begin"], segment["end"] - segment["begin"], file_info["index"], player.id, segment["t"], segment["playback_duration"]) else: self._ask_synth_to_play_segment(segment, channel=0, pan=0.5) def stopped_playing(self, segment): logger.debug("stopped segment %s" % segment) if self.gui: self.gui.unhighlight_segment(segment) if self.output == self.SSR and segment["sound_source_id"]: self.ssr.free_source(segment["sound_source_id"]) def play_segment(self, segment, player): self.segments_by_id[segment["id"]] = segment if self.looped_duration: segment["playback_duration"] = self.looped_duration else: segment["playback_duration"] = segment["duration"] self.visualize_segment(segment, player) def _send_torrent_info_to_uninformed_visualizers(self): for visualizer in self.visualizers: if not visualizer.informed_about_torrent: self._send_torrent_info_to_visualizer(visualizer) def _inform_visualizers_about_peer(self, player): for visualizer in self.visualizers: if player.id not in visualizer.informed_about_peer: if visualizer.send( "/peer", player.id, player.addr, player.spatial_position.bearing, player.spatial_position.pan, player.location_str): visualizer.informed_about_peer[player.id] = True def _send_torrent_info_to_visualizer(self, visualizer): if not visualizer.send( "/torrent", self._num_selected_files, self.tr_log.lastchunktime(), self.tr_log.total_file_size(), len(self.chunks), len(self.score), self.options.title): return for filenum in range(len(self.tr_log.files)): file_info = self.tr_log.files[filenum] if self.include_non_playable or file_info["playable_file_index"] != -1: if not visualizer.send( "/file", file_info["index"], file_info["offset"], file_info["length"]): return visualizer.informed_about_torrent = True def get_player_for_chunk(self, chunk): try: return chunk["player"] except KeyError: peer_player = self.get_player_for_peer(chunk["peeraddr"]) chunk["player"] = peer_player return peer_player def get_player_for_segment(self, segment): try: return segment["player"] except KeyError: peer_player = self.get_player_for_peer(segment["peeraddr"]) segment["player"] = peer_player return peer_player def get_player_for_peer(self, peeraddr): peer_player = None try: peer_player = self._player_for_peer[peeraddr] except KeyError: peer_player = self._create_player(peeraddr) self.players.append(peer_player) self._player_for_peer[peeraddr] = peer_player return peer_player def _create_player(self, addr): count = len(self.players) logger.debug("creating player number %d" % count) player = self._player_class(self, count) player.addr = addr if self.server.options.locate_peers and self._peer_location[addr] is not None: x, y, place_name = self._peer_location[addr] if place_name: place_name = place_name.encode("unicode_escape") else: place_name = "" player.location_str = "%s,%s,%s" % (x, y, place_name) if x < self._peers_center_location_x: player.spatial_position.pan = -1.0 else: player.spatial_position.pan = 1.0 else: player.location_str = "" return player def set_time_cursor(self, log_time): assert not self.realtime logger.debug("setting time cursor at %f" % log_time) self.log_time_played_from = log_time if self._playing: self.stopwatch.restart() self.current_chunk_index = self._get_current_chunk_index() self.current_segment_index = self._get_current_segment_index() def _get_current_chunk_index(self): index = 0 next_to_last_index = len(self.chunks) - 2 while index < next_to_last_index: if self.chunks[index+1]["t"] >= self.log_time_played_from: return index index += 1 return len(self.chunks) - 1 def _get_current_segment_index(self): index = 0 next_to_last_index = len(self.score) - 2 while index < next_to_last_index: if self.score[index+1]["onset"] >= self.log_time_played_from: return index index += 1 return len(self.score) - 1 def _handle_set_listener_position(self, path, args, types, src, data): if self.output == self.SSR: x, y = args self.ssr.set_listener_position(x, y) def _handle_set_listener_orientation(self, path, args, types, src, data): if self.output == self.SSR: orientation = args[0] self.ssr.set_listener_orientation(orientation) def _handle_place_segment(self, path, args, types, src, data): segment_id, x, y, duration = args if self.output == self.SSR: segment = self.segments_by_id[segment_id] sound_source_id = segment["sound_source_id"] if sound_source_id is not None: self.ssr.place_source(sound_source_id, x, y, duration) else: pan = self._spatial_position_to_stereo_pan(x, y) if self.synth: self.synth.pan(segment_id, pan) def _handle_enable_smooth_movement(self, path, args, types, src, data): pass # OBSOLETE after smooth movement made default def _handle_start_segment_movement_from_peer(self, path, args, types, src, data): segment_id, duration = args if self.output == self.SSR: segment = self.segments_by_id[segment_id] sound_source_id = segment["sound_source_id"] if sound_source_id is not None: player = self.get_player_for_segment(segment) self.ssr.start_source_movement( sound_source_id, player.trajectory, duration) def _spatial_position_to_stereo_pan(self, x, y): # compare rectangular_visualizer.Visualizer.pan_segment # NOTE: assumes default listener position and orientation! return float(x) / 5 + 0.5 def reset(self): if self.synth: self.synth.stop_engine() self._tell_visualizers("/reset") for visualizer in self.visualizers: visualizer.informed_about_torrent = False visualizer.informed_about_peer = {} def _tell_visualizers(self, *args): self._send_torrent_info_to_uninformed_visualizers() self.server._tell_visualizers(*args) def _fileinfo_for_pretended_audio_file(self): return {"offset": 0, "length": os.stat(self.options.pretend_audio_filename).st_size, "name": self.options.pretend_audio_filename, "playable_file_index": 0} def _handle_finished(self, path, args, types, src, data): self._num_finished_visualizers += 1 def _wait_for_visualizers_to_finish(self): while self._num_finished_visualizers < len(self.visualizers): time.sleep(0.1) def _get_peers_center_location_x(self): if len(self._peer_location) <= 1: return 0 else: sorted_xs = sorted([x for x,y,location_str in self._peer_location.values()]) center_index = int((len(self._peer_location)-1) / 2) return float(sorted_xs[center_index] + sorted_xs[center_index+1]) / 2