Ejemplo n.º 1
0
def delete_api_handler(music_id):
    music = Music(music_id)
    if music.load() == False: abort(404)

    def callback():
        abort(401)

    usr = auth(callback)
    if music.username == usr.username:
        music.delete()
    return succ(u'删除成功!')
Ejemplo n.º 2
0
def detail_api_handler(music_id):
    music = Music(music_id)
    if music.load() == False: abort(404)

    obj = {i: j for i, j in music.__dict__.items()}
    obj['file_path'] = music.file_path[-5:]
    obj['comments'] = music.getComments()
    obj['content_type'] = music.getContentType()
    obj['score'] = music.getScore()
    obj['genre'] = music.getAuthorGenre()

    return make_response(json.dumps(obj), 200)
Ejemplo n.º 3
0
def comment_api_handler(music_id):
    music = Music(music_id)
    if music.load() == False: abort(404)

    def callback():
        abort(401)

    usr = auth(callback)
    data = parse_req_body(['score', 'feedback'])
    try:
        score = int(data['score'])
    except:
        abort(400)
    comment = Comment()
    comment.score = score
    comment.feedback = data['feedback']
    comment.username = usr.username
    comment.music_id = music.music_id
    comment.save()
    return succ(u'评论成功!')
Ejemplo n.º 4
0
def get_music_api_handler(music_id):
    music = Music(music_id)
    if music.load() == False: abort(404)
    return send_file(music.file_path, music.getContentType())
Ejemplo n.º 5
0
class MidiMaster(GameJam):
    """The controlling object and main loop for the game.
    Should always be small and concise, calling out to other managing
    modules and namespaces where possible."""

    def __init__(self):
        super(MidiMaster, self).__init__()
        self.name = "MidiMaster"
        self.note_width_32nd = Staff.NoteWidth32nd       
        self.mode = MusicMode.PAUSE_AND_LEARN
        self.keyboard_mapping = KeyboardMapping.NOTE_NAMES
        self.reset()


    def reset(self):
        self.score = 0
        self.score_fade = 0.0
        self.music_time = 0.0  # The number of elapsed 32nd notes as a factor of absolute time
        self.player_notes_down = {}
        self.midi_notes = {}
        self.scored_notes = {}
        self.music_running = False


    def prepare(self):
        super().prepare()
        
        # Read the songbook and load the first song
        self.songbook = SongBook.load()
        if self.songbook is None:
            self.songbook = SongBook()
        self.songbook.validate()
        self.songbook.sort()

        # Add a new song supplied on the command line
        song_args = {
            "--song-add": "",
            "--song-track": "1",
        }
        if MidiMaster.get_cmd_argument(song_args):
            song_path = os.path.join(".", song_args["--song-add"])
            song_track = int(song_args["--song-track"])
            
            def add_song(path:str, track=None):
                new_song = Song()
                new_song.from_midi_file(path, track)
                self.songbook.add_update_song(new_song)

            if os.path.exists(song_path):
                if os.path.isdir(song_path):
                    for file in os.listdir(song_path):
                        full_path = os.path.join(song_path, file)
                        if os.path.isfile(full_path) and file.find("mid") >= 0:
                            add_song(full_path, song_track)    
                elif os.path.isfile(song_path):
                    add_song(song_path, song_track)                
            else:
                print(f"Cannot find specificed midi file or folder {song_path}! Exiting.")
                exit()

        # Setup all the game systems
        self.staff = Staff()
        self.menu = Menu(self.graphics, self.input, self.gui, self.window_width, self.window_height, self.textures)
        self.font_game = Font(os.path.join("ext", "BlackMetalSans.ttf"), self.graphics, self.window)
        self.staff.prepare(self.menu.get_menu(Menus.GAME), self.textures)
        self.note_render = NoteRender(self.graphics, self.window_width / self.window_height, self.staff)
        self.music = Music(self.graphics, self.note_render, self.staff)
        self.menu.prepare(self.font_game, self.music, self.songbook)
        
        if GameSettings.DEV_MODE:
            default_song = self.songbook.get_default_song()
            if default_song is None:
                print("Invalid or missing song data file, unable to continue!")
            else:
                self.music.load(default_song)
        
        # Connect midi inputs and outputs and player input
        self.devices = MidiDevices()
        self.devices.open_input_default()
        self.devices.open_output_default()
        self.setup_input()

    def update(self, dt):

        self.menu.update(dt, self.music_running)
        if self.menu.running == False:
            self.quit()

        def score_vfx(note_id = None):
            self.score_fade = 1.0
            self.menu.set_event("score_vfx")
            if note_id is not None:
                spawn_pos = [-0.71, self.staff.note_positions[note_id]]
                self.particles.spawn(2.0, spawn_pos, [0.37, 0.82, 0.4, 1.0])

        # Handle events from MIDI input, echo to output so player can hear
        for message in self.devices.input_messages:
            if message.type == "note_on" or message.type == "note_off":
                self.devices.output_messages.append(message)

        # Process any output messages and transfer them to player notes down
        for message in self.devices.output_messages:
            if message.type == "note_on":
                self.player_notes_down[message.note] = 1.0

                if self.mode == MusicMode.PAUSE_AND_LEARN:
                    if message.note in self.scored_notes:
                        score_vfx(message.note)
                        time_diff = self.music_time - self.scored_notes[message.note]
                        self.score += max(10 - time_diff, 0)
                        del self.scored_notes[message.note]

            elif message.type == "note_off":
                del self.player_notes_down[message.note]

        # Light up score box for any held note
        for note, velocity in self.player_notes_down.items():
            if velocity > 0.0:
                self.staff.set_score(note)

        self.devices.input_messages = []

        tempo_recip_60 = 1.0 / 60.0
        game_draw, _ = self.menu.is_menu_active(Menus.GAME)
        if game_draw:
            music_notes = self.music.draw(dt, self.music_time, self.note_width_32nd)

            music_time_advance = dt * Song.SDQNotesPerBeat * (tempo_recip_60 * self.music.tempo_bpm)
            if self.music_running:
                self.music_time += music_time_advance

            # Play the backing track in sync with the player
            self.music.update(self.dt, self.music_time, self.devices)

            # Process all notes that have hit the play head
            music_notes_off = {}
            for k in music_notes:
                note_off_time = 0
                note_entry = music_notes[k]
                if isinstance(note_entry, list):
                    note_off_time = note_entry[0]
                else:
                    note_off_time = note_entry

                # Highlight the note box to show this note should be currently played
                if note_off_time >= self.music_time:
                    self.staff.note_on(k)

                def new_note_to_play():
                    self.midi_notes[k] = note_off_time
                    self.scored_notes[k] = self.music_time
                    new_note_on = Message("note_on")
                    new_note_on.note = k
                    new_note_on.velocity = 100
                    self.devices.output_messages.append(new_note_on)

                # The note value in the dictionary is the time to turn off
                if k in self.midi_notes:
                    if self.music_time >= note_off_time:
                        music_notes_off[k] = True
                else:
                    new_note_to_play()

            # Send note off messages for all the notes in the music
            for k in music_notes_off:
                del music_notes[k]
                self.staff.note_off(k)
                new_note_off = Message("note_off")
                new_note_off.note = k
                self.devices.output_messages.append(new_note_off)
                self.midi_notes.pop(k)

            if self.mode == MusicMode.PERFORMANCE:
                if self.staff.is_scoring():
                    if self.score_fade < 0.5:
                        for note in self.scored_notes:
                            score_vfx(note)
                            break
                    self.score += 10 ** self.dt
            elif self.mode == MusicMode.PAUSE_AND_LEARN:
                if len(self.scored_notes) > 0 and self.music_running:
                    self.music_time -= music_time_advance

            self.staff.draw(dt)
 
            # Show the play mode
            mode_string = "Performance" if self.mode == MusicMode.PERFORMANCE else "Pause & Learn"
            self.font_game.draw(f"{mode_string}", 16, [0.5, 0.8], [0.6, 0.6, 0.6, 1.0])

            # Show the score on top of everything
            self.score_fade -= dt * 0.5
            self.font_game.draw(f"{math.floor(self.score)} XP", 22, [self.bg_score.sprite.pos[0] - 0.025, self.bg_score.sprite.pos[1] - 0.03], [0.1, 0.1, 0.1, 1.0])

        # Update and flush out the buffers
        self.devices.update()
        self.devices.output_messages = []


    def end(self):
        self.songbook.input_device = self.devices.input_device_name
        self.songbook.output_device = self.devices.output_device_name
        SongBook.save(self.songbook)
        super().end()
        self.devices.end()


    def setup_input(self):
        def play(self):
            self.music_running = True

        def pause(self):
            self.music_running = False

        def stop_rewind(self):
            self.reset()
            self.music.rewind()

        def mode_toggle(self):
            self.mode = MusicMode.PAUSE_AND_LEARN if self.mode == MusicMode.PERFORMANCE else MusicMode.PERFORMANCE

        def back_to_menu(self):
            pause(self)
            existing_score = self.music.song.score[self.mode] if self.mode in self.music.song.score else 0
            self.music.song.score[self.mode] = max(self.score, existing_score)
            self.reset()
            self.music.reset()
            self.menu.transition(Menus.GAME, Menus.SONGS)

        def play_button_colour(self):
            return [0.1, 0.87, 0.11, 1.0] if self.music_running else [0.8, 0.8, 0.8, 1.0]

        def pause_button_colour(self):
            return [0.3, 0.27, 0.81, 1.0] if not self.music_running else [0.8, 0.8, 0.8, 1.0]

        def score_bg_colour(self):
            return [1.0, 1.0, 1.0, max(self.score_fade, 0.65)]

        playback_button_size = [0.15, 0.125]
        controls_height = -0.85
        gui = self.menu.get_menu(Menus.GAME)
        btn_play = gui.add_widget(self.textures.create_sprite_texture("gui/btnplay.tga", [0.62, controls_height], playback_button_size))
        btn_pause = gui.add_widget(self.textures.create_sprite_texture("gui/btnpause.tga", [0.45, controls_height], playback_button_size))
        btn_stop = gui.add_widget(self.textures.create_sprite_texture("gui/btnstop.tga", [0.28, controls_height], playback_button_size))
        btn_mode = gui.add_widget(self.textures.create_sprite_texture("gui/panel_long.png", [0.655, 0.825], [0.32, 0.15]))
        btn_menu = gui.add_widget(self.textures.create_sprite_texture("gui/btnback.png", [-0.85, 0.85], [0.075, 0.075 * self.window_ratio]))

        btn_play.set_action(play, self)
        btn_play.set_colour_func(play_button_colour, self)
        btn_pause.set_action(pause, self)
        btn_pause.set_colour_func(pause_button_colour, self)
        btn_stop.set_action(stop_rewind, self)
        btn_mode.set_action(mode_toggle, self)
        btn_menu.set_action(back_to_menu, self)

        self.bg_score = gui.add_widget(self.textures.create_sprite_texture("score_bg.tga", [-0.33, controls_height - 0.10], [0.5, 0.25]))
        self.bg_score.set_colour_func(score_bg_colour, self)
        self.bg_score.align(AlignX.Centre, AlignY.Bottom)

        def note_width_inc():
            self.note_width_32nd = max(0.0, self.note_width_32nd + (self.dt * 0.1))

        def note_width_dec():
            self.note_width_32nd = max(0.0, self.note_width_32nd - (self.dt * 0.1))

        def music_time_fwd():
            self.music_time += self.dt * 30.0

        def music_time_back():
            self.music_time -= self.dt * 30.0

        def music_pause():
            self.music_running = not self.music_running

        self.input.add_key_mapping(32, InputActionKey.ACTION_KEYDOWN, InputActionModifier.NONE, music_pause)  # space for Pause on keyup
        self.input.add_key_mapping(61, InputActionKey.ACTION_KEYDOWN, InputActionModifier.NONE, note_width_inc)  # + Add more space in a bar
        self.input.add_key_mapping(45, InputActionKey.ACTION_KEYDOWN, InputActionModifier.NONE, note_width_dec)  # - Add less space in a bar
        self.input.add_key_mapping(262, InputActionKey.ACTION_KEYREPEAT, InputActionModifier.NONE, music_time_back)  # -> Manually advance forward in time
        self.input.add_key_mapping(263, InputActionKey.ACTION_KEYREPEAT, InputActionModifier.NONE, music_time_fwd)  # -> Manually advance backwards in time

        def create_key_note(note_val: int, note_on: bool):
            if self.menu.is_menu_active(Menus.GAME):
                note_name = "note_on"
                if note_on == False:
                    note_name = "note_off"
                new_note = Message(note_name)
                new_note.note = note_val
                new_note.velocity = 100
                self.devices.input_messages.append(new_note)

        def create_key_note_on(note_val: int):
            create_key_note(note_val, True)

        def create_key_note_off(note_val: int):
            create_key_note(note_val, False)

        def add_note_key_mapping(key_val, note_val, modifier=InputActionModifier.NONE):
            self.input.add_key_mapping(key_val, InputActionKey.ACTION_KEYDOWN, modifier, create_key_note_on, note_val)
            self.input.add_key_mapping(key_val, InputActionKey.ACTION_KEYUP, modifier, create_key_note_off, note_val)

        # Playing notes with the keyboard note names
        if self.keyboard_mapping == KeyboardMapping.NOTE_NAMES:
            note_keycode = 0
            keymap = [67, 68, 69, 70, 71, 65, 66]  # CDEFGAB
            for i in range(Staff.NumNotes):
                note = Staff.OriginNote + i
                note_lookup = note % 12
                if note_lookup in KeySignature.SharpsAndFlats:
                    add_note_key_mapping(keymap[(note_keycode - 1) % 7], note, InputActionModifier.LSHIFT)  # Shift for sharp (#)
                    add_note_key_mapping(keymap[note_keycode % 7], note, InputActionModifier.LCTRL)  # Ctrl for flat (b)
                else:
                    add_note_key_mapping(keymap[note_keycode % 7], note)
                    note_keycode += 1

        elif self.keyboard_mapping == KeyboardMapping.QWERTY_PIANO:
            add_note_key_mapping(47, Staff.OriginNote)  # C
            add_note_key_mapping(50, Staff.OriginNote + 1)  # Db
            add_note_key_mapping(87, Staff.OriginNote + 2)  # D
            add_note_key_mapping(51, Staff.OriginNote + 3)  # Eb
            add_note_key_mapping(69, Staff.OriginNote + 4)  # E
            add_note_key_mapping(82, Staff.OriginNote + 5)  # F
            add_note_key_mapping(53, Staff.OriginNote + 6)  # Gb
            add_note_key_mapping(84, Staff.OriginNote + 7)  # G
            add_note_key_mapping(54, Staff.OriginNote + 8)  # Ab
            add_note_key_mapping(89, Staff.OriginNote + 9)  # A
            add_note_key_mapping(55, Staff.OriginNote + 10)  # Bb
            add_note_key_mapping(85, Staff.OriginNote + 11)  # B


    @staticmethod
    def get_cmd_argument(values: dict) -> bool:
        """Search the command line args and modify the supplied dictionary with values."""
        found_arg = False
        num_args = len(sys.argv)
        for search in values:
            for i, arg in enumerate(sys.argv):
                if arg.find(search) >= 0:
                    found_arg = True
                    arg_parts = arg.split()
                    if len(arg_parts) > 1:
                        values[search] = arg_parts[1]
                    elif i + 1 < num_args - 1:
                        values[search] = sys.argv[i+1]
        return found_arg