Exemple #1
0
    def _render(self):
        self.matrix.reset()

        self.gl.glClearColor(*self.clear_color)
        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT)
        next(self.frames)
        graphics.get_renderer().cleanup()
Exemple #2
0
    def _render(self):
        self.matrix.reset()

        self.gl.glClearColor(*self.clear_color)
        self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT)
        next(self.frames)
        graphics.get_renderer().cleanup()
Exemple #3
0
 def render():
     t = time.time()
     nonlocal song_time
     while not mpv.eof_reached():
         graphics.get_renderer().clear(0, 0, 0, 1)
         t1 = time.time()
         mpv.draw()
         dt = time.time() - t1
         mpv.poll()
         song_time = mpv.get_song_time() or song_time
         mpv.draw_fade(song_time)
         renderer.draw(song_time + headstart * 2**(speed_i/12.0), song_layout)
         yield None
         t2 = time.time()
         if opts.st:
             print("T:%7.3f/%7.3f B:%7.3f FPS:%.2f draw:%.3f" % (song_time, mpv.duration, s.timing.time2beat(song_time), (1.0/(t2-t)), dt))
         t = t2
         mpv.flip()
     mpv.shutdown()
     os._exit(0)
Exemple #4
0
def render():
    t = time.time()
    global song_time
    while not mpv.eof_reached():
        graphics.get_renderer().clear(0, 0, 0, 1)
        t1 = time.time()
        mpv.draw()
        dt = time.time() - t1
        mpv.poll()
        song_time = mpv.get_song_time() or song_time
        mpv.draw_fade(song_time)
        renderer.draw(song_time + headstart * 2**(speed_i/12.0), layout)
        yield None
        t2 = time.time()
        if opts.st:
            print("T:%7.3f/%7.3f B:%7.3f FPS:%.2f draw:%.3f" % (song_time, mpv.duration, s.timing.time2beat(song_time), (1.0/(t2-t)), dt))
        t = t2
        mpv.flip()
    mpv.shutdown()
    os._exit(0)
Exemple #5
0
    def __init__(self, display):
        self.display = display

        tr = graphics.get_texture_renderer()
        ImageTexture = graphics.get_renderer().ImageTexture
        self.logo = ImageTexture(util.get_resgfx_path("logo.png"), tr)
        self.tablet = ImageTexture(util.get_resgfx_path("tablet.png"), tr)
        self.hand = ImageTexture(util.get_resgfx_path("hand.png"), tr)
        self.silhouette = ImageTexture(util.get_resgfx_path("silhouette.png"),
                                       tr)
        self.reset()
Exemple #6
0
def entry():
    data_home = os.getenv('XDG_DATA_HOME', '~/.local/share')
    songs_dir = os.path.join(data_home, 'blitzloop', 'songs')

    def csv_list(s):
        return s.split(",")

    parser = util.get_argparser()
    parser.add_argument(
        '--songdir', default=os.path.expanduser(songs_dir),
        help='directory with songs')
    parser.add_argument('--host', default='0.0.0.0', help='IP to listen on')
    parser.add_argument(
        '--port', default=10111, type=int,
        help='port for the UI')
    parser.add_argument(
        '--width', type=int, default=1024,
        help='width of blitzloop window (ignored in fs)')
    parser.add_argument(
        '--height', type=int, default=768,
        help='height of blitzloop window (ignored in fs)')
    parser.add_argument(
        '--no-audioengine', action="store_true",
        help='Disable JACK-based audio engine (mic echo effect)')
    parser.add_argument(
        '--mics', type=csv_list, default=["system:capture_1"],
        help='Mic input connections (list of JACK ports)')
    opts = util.get_opts()

    songs_dir = os.path.expanduser(opts.songdir)

    print("Loading song DB...")
    song_database = songlist.SongDatabase(songs_dir)
    print("Done.")

    display = graphics.Display(opts.width, opts.height, opts.fullscreen)
    renderer = graphics.get_renderer().KaraokeRenderer(display)
    mpv = mpvplayer.Player(display)

    if not opts.no_audioengine and opts.mics:
        from blitzloop._audio import AudioEngine
        print(repr(opts.mics))
        audio = AudioEngine([s.encode("ascii") for s in opts.mics])
        print("Engine sample rate: %dHz" % audio.sample_rate)

    queue = songlist.SongQueue()

    class AudioConfig(object):
        def __init__(self):
            self.nmics = len(opts.mics) if not opts.no_audioengine else 0
            self.volume = 80
            if opts.no_audioengine:
                self.mic_channels = []
            else:
                self.mic_channels = [{"volume": 80} for i in range(self.nmics)]
            self.mic_feedback = 20
            self.mic_delay = 12
            self.headstart = 30

        def update(self, song=None):
            mpv.set_volume(((self.volume / 100.0) ** 2) * 0.5)
            if not opts.no_audioengine:
                for i, j in enumerate(self.mic_channels):
                    audio.set_mic_volume(i, ((j["volume"] / 100.0) ** 2) * 2.0)
                audio.set_mic_feedback(self.mic_feedback / 100.0)
                audio.set_mic_delay(self.mic_delay / 100.0)

    audio_config = AudioConfig()

    web.database = song_database
    web.queue = queue
    web.audio_config = audio_config
    server = web.ServerThread(host=opts.host, port=opts.port)
    server.start()

    idle_screen = idlescreen.IdleScreen(display)

    def main_render():
        # Wait for element in queue
        print("Waiting for song to appear in queue...")
        qe = None
        with queue.lock:
            if len(queue) != 0:
                qe = queue[0]
        if not qe:
            idle_screen.reset()
            graphics.get_renderer().clear(0, 0, 0, 1)
            for f in idle_screen:
                audio_config.update()
                yield None
                graphics.get_renderer().clear(0, 0, 0, 1)
                if not qe:
                    with queue.lock:
                        if len(queue) != 0:
                            qe = queue[0]
                            idle_screen.close()

        for i in range(2):
            yield None
            graphics.get_renderer().clear(0, 0, 0, 1)

        print("Loading audio/video...")
        mpv.load_song(qe.song)
        display.set_aspect(mpv.aspect)

        def update_params(defer=False):
            mpv.set_speed(1.0 / (2**(qe.speed / 12.0)))
            mpv.set_pitch(2**(qe.pitch / 12.0))
            for i, j in enumerate(qe.channels):
                mpv.set_channel(i, j["volume"] / 10., defer=defer)
            mpv.set_pause(qe.pause)

        update_params(True)
        mpv.update_mixer(force=True)

        print("Laying out song...")
        renderer.reset()
        variant_key = list(qe.song.variants.keys())[qe.variant]
        song_layout = layout.SongLayout(qe.song, variant_key, renderer)
        print("Loaded.")

        song_time = -10
        stopping = False
        while not (mpv.eof_reached() or (stopping and qe.pause)):
            while qe.commands:
                cmd, arg = qe.commands.pop(0)
                if cmd == "seek":
                    mpv.seek(arg)
                elif cmd == "seekto":
                    mpv.seek_to(arg)
            mpv.draw()
            mpv.poll()
            song_time = mpv.get_song_time() or song_time

            if qe.stop and not stopping:
                stopping = True
                mpv.fade_out = 2
                mpv.duration = min(mpv.duration, song_time + 2)

            if not stopping:
                mpv.draw_fade(song_time)

            speed = 2**(qe.speed / 12.0)
            renderer.draw(song_time + audio_config.headstart / 100.0 * speed,
                          song_layout)

            if stopping:
                fade = mpv.draw_fade(song_time)
                mpv.set_fadevol(max(fade * 1.3 - 0.3, 0))

            update_params()
            audio_config.update(qe.song)
            yield None
            mpv.flip()

        graphics.get_renderer().clear(0, 0, 0, 1)
        for i in range(2):
            yield None
            graphics.get_renderer().clear(0, 0, 0, 1)

        print("Song complete.")
        del song_layout
        try:
            queue.pop(qe.qid)
        except (IndexError, KeyError):
            pass
        mpv.stop()
        display.set_aspect(None)

    def main():
        while True:
            for i in main_render():
                yield i

    def exit():
        print("Exit handler called")
        mpv.shutdown()
        if not opts.no_audioengine:
            audio.shutdown()
        server.stop()
        print("Exit handler done")

    def key(k):
        if k == 'KEY_ESCAPE':
            display.queue_exit()
        elif k == 'f':
            display.toggle_fullscreen()

    display.set_render_gen(main)
    display.set_keyboard_handler(key)
    display.set_exit_handler(exit)
    display.main_loop()
    threads = threading.enumerate()
    if len(threads) > 1:
        print("Loose threads: %r" % threads)
Exemple #7
0
    def main_render():
        # Wait for element in queue
        print("Waiting for song to appear in queue...")
        qe = None
        with queue.lock:
            if len(queue) != 0:
                qe = queue[0]
        if not qe:
            idle_screen.reset()
            graphics.get_renderer().clear(0, 0, 0, 1)
            for f in idle_screen:
                audio_config.update()
                yield None
                graphics.get_renderer().clear(0, 0, 0, 1)
                if not qe:
                    with queue.lock:
                        if len(queue) != 0:
                            qe = queue[0]
                            idle_screen.close()

        for i in range(2):
            yield None
            graphics.get_renderer().clear(0, 0, 0, 1)

        print("Loading audio/video...")
        mpv.load_song(qe.song)
        display.set_aspect(mpv.aspect)

        def update_params(defer=False):
            mpv.set_speed(1.0 / (2**(qe.speed / 12.0)))
            mpv.set_pitch(2**(qe.pitch / 12.0))
            for i, j in enumerate(qe.channels):
                mpv.set_channel(i, j["volume"] / 10., defer=defer)
            mpv.set_pause(qe.pause)

        update_params(True)
        mpv.update_mixer(force=True)

        print("Laying out song...")
        renderer.reset()
        variant_key = list(qe.song.variants.keys())[qe.variant]
        song_layout = layout.SongLayout(qe.song, variant_key, renderer)
        print("Loaded.")

        song_time = -10
        stopping = False
        while not (mpv.eof_reached() or (stopping and qe.pause)):
            while qe.commands:
                cmd, arg = qe.commands.pop(0)
                if cmd == "seek":
                    mpv.seek(arg)
                elif cmd == "seekto":
                    mpv.seek_to(arg)
            mpv.draw()
            mpv.poll()
            song_time = mpv.get_song_time() or song_time

            if qe.stop and not stopping:
                stopping = True
                mpv.fade_out = 2
                mpv.duration = min(mpv.duration, song_time + 2)

            if not stopping:
                mpv.draw_fade(song_time)

            speed = 2**(qe.speed / 12.0)
            renderer.draw(song_time + audio_config.headstart / 100.0 * speed,
                          song_layout)

            if stopping:
                fade = mpv.draw_fade(song_time)
                mpv.set_fadevol(max(fade * 1.3 - 0.3, 0))

            update_params()
            audio_config.update(qe.song)
            yield None
            mpv.flip()

        graphics.get_renderer().clear(0, 0, 0, 1)
        for i in range(2):
            yield None
            graphics.get_renderer().clear(0, 0, 0, 1)

        print("Song complete.")
        del song_layout
        try:
            queue.pop(qe.qid)
        except (IndexError, KeyError):
            pass
        mpv.stop()
        display.set_aspect(None)
Exemple #8
0
def main_render():
    # Wait for element in queue
    print("Waiting for song to appear in queue...")
    qe = None
    with queue.lock:
        if len(queue) != 0:
            qe = queue[0]
    if not qe:
        idle_screen.reset()
        graphics.get_renderer().clear(0, 0, 0, 1)
        for f in idle_screen:
            audio_config.update()
            yield None
            graphics.get_renderer().clear(0, 0, 0, 1)
            if not qe:
                with queue.lock:
                    if len(queue) != 0:
                        qe = queue[0]
                        idle_screen.close()

    for i in range(2):
        yield None
        graphics.get_renderer().clear(0, 0, 0, 1)

    print("Loading audio/video...")
    mpv.load_song(qe.song)
    display.set_aspect(mpv.aspect)

    print("Laying out song...")
    renderer.reset()
    variant_key = list(qe.song.variants.keys())[qe.variant]
    song_layout = layout.SongLayout(qe.song, variant_key, renderer)
    print("Loaded.")

    def update_params():
        mpv.set_speed(1.0 / (2**(qe.speed / 12.0)))
        mpv.set_pitch(2**(qe.pitch / 12.0))
        for i, j in enumerate(qe.channels):
            mpv.set_channel(i, j["volume"] / 10.0)
        mpv.set_pause(qe.pause)

    update_params()
    song_time = -10
    stopping = False
    while not (mpv.eof_reached() or (stopping and qe.pause)):
        while qe.commands:
            cmd, arg = qe.commands.pop(0)
            if cmd == "seek":
                mpv.seek(arg)
            elif cmd == "seekto":
                mpv.seek_to(arg)
        mpv.draw()
        mpv.poll()
        song_time = mpv.get_song_time() or song_time

        if qe.stop and not stopping:
            stopping = True
            mpv.fade_out = 2
            mpv.duration = min(mpv.duration, song_time + 2)

        if not stopping:
            mpv.draw_fade(song_time)

        speed = 2**(qe.speed / 12.0)
        renderer.draw(song_time + audio_config.headstart / 100.0 * speed,
                      song_layout)

        if stopping:
            fade = mpv.draw_fade(song_time)
            mpv.set_fadevol(max(fade * 1.3 - 0.3, 0))

        update_params()
        audio_config.update(qe.song)
        yield None
        mpv.flip()

    graphics.get_renderer().clear(0, 0, 0, 1)
    for i in range(2):
        yield None
        graphics.get_renderer().clear(0, 0, 0, 1)

    print("Song complete.")
    try:
        queue.pop(qe.qid)
    except (IndexError, KeyError):
        pass
    mpv.stop()
    display.set_aspect(None)
Exemple #9
0
                    action="store_true",
                    help='Disable JACK-based audio engine (mic echo effect)')
parser.add_argument('--mics',
                    type=csv_list,
                    default=["system:capture_1"],
                    help='Mic input connections (list of JACK ports)')
opts = util.get_opts()

songs_dir = os.path.expanduser(opts.songdir)

print("Loading song DB...")
song_database = songlist.SongDatabase(songs_dir)
print("Done.")

display = graphics.Display(opts.width, opts.height, opts.fullscreen)
renderer = graphics.get_renderer().KaraokeRenderer(display)
mpv = mpvplayer.Player(display)

if not opts.no_audioengine and opts.mics:
    print(repr(opts.mics))
    audio = AudioEngine([s.encode("ascii") for s in opts.mics])
    print("Engine sample rate: %dHz" % audio.sample_rate)

queue = songlist.SongQueue()


class AudioConfig(object):
    def __init__(self):
        self.nmics = len(opts.mics) if not opts.no_audioengine else 0
        self.volume = 80
        if opts.no_audioengine:
Exemple #10
0
    def _load_lyrics(self, s):
        telop = self.root.find("telop")

        renderer = graphics.get_renderer().KaraokeRenderer(self.display)
        lyt = layout.SongLayout(s,
                                list(s.variants.keys())[opts.variant],
                                renderer)

        lines = lyt.lines[song.TagInfo.BOTTOM]

        class Page(object):
            pass

        def ms(i):
            return str(int(round(i * 1000)))

        # Group lines into pages
        pages = []
        page = Page()
        page.lines = {}
        last = None
        for l in lines:
            if l.row in page.lines or (last and (l.row > last.row
                                                 or l.start > last.end)):
                pages.append(page)
                page = Page()
                page.lines = {}
            page.lines[l.row] = l
            last = l
        pages.append(page)

        # Compute page show/hide times
        prev = None
        pages2 = []
        for page in pages:
            lines = page.lines
            page.start = min(l.start for l in lines.values())
            page.min_start = min(l._start_t for l in lines.values())
            page.end = max(l.end for l in lines.values())
            page.min_end = max(l._end_t for l in lines.values())

            if prev and page.start < prev.end:
                page.start = min(page.min_start, prev.end)
                if prev and page.start < prev.end:
                    prev.end = max(prev.min_end, page.start)
                    if prev and page.start < prev.end:
                        li = " ".join(
                            repr(v.molecules[0][0].text)
                            for k, v in sorted(lines.items(), reverse=True))
                        raise Exception("overlapping lines! %r" % (li))
            prev = page
            pages2.append(page)

        # Generate XML
        for page in pages:
            pe = ET.Element("page")
            pe.append(self._tag("show_time", ms(page.start)))
            pe.append(self._tag("hide_time", ms(page.end)))
            pe.append(self._tag("paint_timing", "0"))
            pe.append(self._tag("layout", "xing_0"))

            t = page.min_start
            for i in range(max(page.lines.keys()), -1, -1):

                le = ET.Element("line")
                text = ""
                if i not in page.lines:
                    # dummy line
                    w = ET.Element("word")
                    w.append(self._tag("text", ""))
                    w.append(self._tag("start_time", ms(t)))
                    w.append(self._tag("end_time", ms(t)))
                    le.append(w)
                else:
                    line = page.lines[i]
                    assert len(line.molecules) == 1
                    mol, get_atom_time = line.molecules[0]
                    ruby = []
                    step = 0
                    for atom in mol.atoms:
                        start, end = get_atom_time(step, atom.steps)
                        step += atom.steps

                        w = ET.Element("word")
                        w.append(self._tag("text", atom.text))
                        w.append(self._tag("start_time", ms(start)))
                        w.append(self._tag("end_time", ms(end)))
                        le.append(w)

                        if atom.particles is not None:
                            edge = len(atom.text)
                            if atom.particle_edge:
                                edge = atom.particle_edge
                            rt = ""
                            for i in atom.particles:
                                rt += i.text
                            r = ET.Element("ruby")
                            r.append(self._tag("text", rt))
                            r.append(self._tag("start_pos", "%d" % len(text)))
                            r.append(
                                self._tag("end_pos",
                                          "%d" % (len(text) + edge - 1)))
                            ruby.append(r)

                        text += atom.text

                    for r in ruby:
                        le.append(r)

                    print(text)

                e = ET.Element("duet")
                e.append(self._tag("mark", "0"))
                e.append(self._tag("start_pos", "0"))
                e.append(self._tag("end_pos", "%d" % (len(text) - 1)))
                le.append(e)

                e = ET.Element("offset")
                e.append(self._tag("type", "0"))
                e.append(self._tag("offset", "0"))
                le.append(e)

                pe.append(le)

            telop.append(pe)
Exemple #11
0
 def build(self):
     self.rline = get_renderer().RenderedLine(self)
     self.rline.build()
Exemple #12
0
def entry():
    parser = util.get_argparser()
    parser.add_argument('songpath',
                        metavar='SONGPATH',
                        help='path to the song file')
    parser.add_argument('--show-timings',
                        dest='st',
                        action='store_true',
                        help='show mpv timings')
    parser.add_argument('--offset',
                        type=float,
                        default=0.0,
                        help='song offset')
    parser.add_argument('--variant', type=int, default=0, help='song variant')
    opts = util.get_opts()

    fullscreen = opts.fullscreen
    s = song.Song(opts.songpath)

    headstart = 0.3

    if fullscreen:
        display = graphics.Display(1920, 1200, fullscreen, None)
    else:
        display = graphics.Display(1280, 720, fullscreen, None)
    print(display.width, display.height)

    mpv = mpvplayer.Player(display)
    mpv.load_song(s)

    display.set_aspect(mpv.aspect)

    renderer = graphics.get_renderer().KaraokeRenderer(display)
    song_layout = layout.SongLayout(s,
                                    list(s.variants.keys())[opts.variant],
                                    renderer)

    song_time = -10

    speed_i = 0
    pitch_i = 0
    channels_i = s.channel_defaults

    for idx, val in enumerate(channels_i):
        mpv.set_channel(idx, val / 10.0)

    if opts.offset:
        mpv.seek_to(opts.offset)

    def render():
        t = time.time()
        nonlocal song_time
        while not mpv.eof_reached():
            graphics.get_renderer().clear(0, 0, 0, 1)
            t1 = time.time()
            mpv.draw()
            dt = time.time() - t1
            mpv.poll()
            song_time = mpv.get_song_time() or song_time
            mpv.draw_fade(song_time)
            renderer.draw(song_time + headstart * 2**(speed_i / 12.0),
                          song_layout)
            yield None
            t2 = time.time()
            if opts.st:
                print("T:%7.3f/%7.3f B:%7.3f FPS:%.2f draw:%.3f" %
                      (song_time, mpv.duration, s.timing.time2beat(song_time),
                       (1.0 / (t2 - t)), dt))
            t = t2
            mpv.flip()
        mpv.shutdown()
        os._exit(0)

    pause = False

    CH_UP = "+456"
    CH_DOWN = "-123"

    def key(k):
        nonlocal speed_i, pitch_i, pause
        if k == 'KEY_ESCAPE':
            mpv.shutdown()
            os._exit(0)
        elif k == 'f':
            display.toggle_fullscreen()
        elif k == '[' and speed_i > -12:
            speed_i -= 1
            print("Speed: %d" % speed_i)
            mpv.set_speed(2**(-speed_i / 12.0))
        elif k == ']' and speed_i < 12:
            speed_i += 1
            print("Speed: %d" % speed_i)
            mpv.set_speed(2**(-speed_i / 12.0))
        elif k == 'KEY_UP' and pitch_i < 12:
            pitch_i += 1
            print("Pitch: %d" % pitch_i)
            mpv.set_pitch(2**(pitch_i / 12.0))
        elif k == 'KEY_DOWN' and pitch_i > -12:
            pitch_i -= 1
            print("Pitch: %d" % pitch_i)
            mpv.set_pitch(2**(pitch_i / 12.0))
        elif k in CH_UP:
            idx = CH_UP.index(k)
            if len(channels_i) > idx and channels_i[idx] < 30:
                channels_i[idx] += 1
                print("Channel %d: %d" % (idx, channels_i[idx]))
                mpv.set_channel(idx, channels_i[idx] / 10.0)
        elif k in CH_DOWN:
            idx = CH_DOWN.index(k)
            if len(channels_i) > idx and channels_i[idx] > 0:
                channels_i[idx] -= 1
                print("Channel %d: %d" % (idx, channels_i[idx]))
                mpv.set_channel(idx, channels_i[idx] / 10.0)
        elif k == 'KEY_LEFT':
            mpv.seek(-10)
        elif k == 'KEY_RIGHT':
            mpv.seek(10)
        elif k == ' ':
            pause = not pause
            t = time.time()
            mpv.set_pause(pause)
            print("P %.03f" % (time.time() - t))

    mpv.play()
    display.set_render_gen(render)
    display.set_keyboard_handler(key)
    display.main_loop()
    mpv.shutdown()
Exemple #13
0
def entry():
    parser = util.get_argparser()
    parser.add_argument(
        'songpath', metavar='SONGPATH', help='path to the song file')
    parser.add_argument(
        '--show-timings', dest='st', action='store_true',
        help='show mpv timings')
    parser.add_argument(
        '--offset', type=float, default=0.0, help='song offset')
    parser.add_argument(
        '--variant', type=int, default=0, help='song variant')
    opts = util.get_opts()

    fullscreen = opts.fullscreen
    s = song.Song(opts.songpath)

    headstart = 0.3

    if fullscreen:
        display = graphics.Display(1920, 1200, fullscreen, None)
    else:
        display = graphics.Display(1280, 720, fullscreen, None)
    print(display.width, display.height)

    mpv = mpvplayer.Player(display)
    mpv.load_song(s)

    display.set_aspect(mpv.aspect)

    renderer = graphics.get_renderer().KaraokeRenderer(display)
    song_layout = layout.SongLayout(s, list(s.variants.keys())[opts.variant], renderer)

    song_time = -10

    speed_i = 0
    pitch_i = 0
    channels_i = s.channel_defaults

    for idx, val in enumerate(channels_i):
        mpv.set_channel(idx, val / 10.0)

    if opts.offset:
        mpv.seek_to(opts.offset)

    def render():
        t = time.time()
        nonlocal song_time
        while not mpv.eof_reached():
            graphics.get_renderer().clear(0, 0, 0, 1)
            t1 = time.time()
            mpv.draw()
            dt = time.time() - t1
            mpv.poll()
            song_time = mpv.get_song_time() or song_time
            mpv.draw_fade(song_time)
            renderer.draw(song_time + headstart * 2**(speed_i/12.0), song_layout)
            yield None
            t2 = time.time()
            if opts.st:
                print("T:%7.3f/%7.3f B:%7.3f FPS:%.2f draw:%.3f" % (song_time, mpv.duration, s.timing.time2beat(song_time), (1.0/(t2-t)), dt))
            t = t2
            mpv.flip()
        mpv.shutdown()
        os._exit(0)

    pause = False

    CH_UP = "+456"
    CH_DOWN = "-123"

    def key(k):
        nonlocal speed_i, pitch_i, pause
        if k == 'KEY_ESCAPE':
            mpv.shutdown()
            os._exit(0)
        elif k == 'f':
            display.toggle_fullscreen()
        elif k == '[' and speed_i > -12:
            speed_i -= 1
            print("Speed: %d" % speed_i)
            mpv.set_speed(2**(-speed_i/12.0))
        elif k == ']' and speed_i < 12:
            speed_i += 1
            print("Speed: %d" % speed_i)
            mpv.set_speed(2**(-speed_i/12.0))
        elif k == 'KEY_UP' and pitch_i < 12:
            pitch_i += 1
            print("Pitch: %d" % pitch_i)
            mpv.set_pitch(2**(pitch_i/12.0))
        elif k == 'KEY_DOWN' and pitch_i > -12:
            pitch_i -= 1
            print("Pitch: %d" % pitch_i)
            mpv.set_pitch(2**(pitch_i/12.0))
        elif k in CH_UP:
            idx = CH_UP.index(k)
            if len(channels_i) > idx and channels_i[idx] < 30:
                channels_i[idx] += 1
                print("Channel %d: %d" % (idx, channels_i[idx]))
                mpv.set_channel(idx, channels_i[idx]/10.0)
        elif k in CH_DOWN:
            idx = CH_DOWN.index(k)
            if len(channels_i) > idx and channels_i[idx] > 0:
                channels_i[idx] -= 1
                print("Channel %d: %d" % (idx, channels_i[idx]))
                mpv.set_channel(idx, channels_i[idx]/10.0)
        elif k == 'KEY_LEFT':
            mpv.seek(-10)
        elif k == 'KEY_RIGHT':
            mpv.seek(10)
        elif k == ' ':
            pause = not pause
            t = time.time()
            mpv.set_pause(pause)
            print("P %.03f" % (time.time()-t))

    mpv.play()
    display.set_render_gen(render)
    display.set_keyboard_handler(key)
    display.main_loop()
    mpv.shutdown()
Exemple #14
0
parser = util.get_argparser()
parser.add_argument(
    'songpath', metavar='SONGPATH', help='path to the song file')
parser.add_argument(
    'quant', metavar='QUANT', type=int, help='quantization of the song')
parser.add_argument(
    '--speed', default=1.0, type=float, help='divisor of the audio speed')
parser.add_argument(
    '--position', default=0.0, type=float,
    help='starting position in the song, in seconds')
opts = util.get_opts()

s = song.Song(opts.songpath, ignore_steps=True)
display = graphics.Display(1280,720)
renderer = graphics.get_renderer().KaraokeRenderer(display)
layout = layout.SongLayout(s, list(s.variants.keys())[-1], renderer)

step = 0

cur_beat = 0

compound = None
compounds = iter(s.compounds)

mpv = mpvplayer.Player(None)
mpv.load_song(s)
mpv.set_speed(opts.speed)

if opts.position > 0.0:
    mpv.seek_to(opts.position)
Exemple #15
0
    def _load_lyrics(self, s):
        telop = self.root.find("telop")

        renderer = graphics.get_renderer().KaraokeRenderer(self.display)
        lyt = layout.SongLayout(s,
                                list(s.variants.keys())[opts.variant],
                                renderer)

        lines = lyt.lines[song.TagInfo.BOTTOM]

        class Page(object):
            pass

        def ms(i):
            return str(int(round(i * 1000)))

        def color(rgb):
            r, g, b = rgb
            return "%d" % (r | (g << 8) | (b << 16))

        # Group lines into pages
        pages = []
        page = Page()
        page.lines = {}
        last = None
        for l in lines:
            if l.row in page.lines or (last and (l.row > last.row
                                                 or l.start > last.end)):
                pages.append(page)
                page = Page()
                page.lines = {}
            page.lines[l.row] = l
            last = l
        pages.append(page)

        # Compute page show/hide times
        prev = None
        pages2 = []
        for page in pages:
            lines = page.lines
            page.start = min(l.start for l in lines.values())
            page.min_start = min(l._start_t for l in lines.values())
            page.end = max(l.end for l in lines.values())
            page.min_end = max(l._end_t for l in lines.values())

            if prev and page.start < prev.end:
                page.start = min(page.min_start, prev.end)
                if prev and page.start < prev.end:
                    prev.end = max(prev.min_end, page.start)
                    if prev and page.start < prev.end:
                        li = " ".join(
                            repr(v.molecules[0][0].text)
                            for k, v in sorted(lines.items(), reverse=True))
                        raise Exception("overlapping lines! %r" % (li))
            prev = page
            pages2.append(page)

        # Generate XML
        for page in pages:
            pe = ET.Element("page")
            pe.append(self._tag("show_time", ms(page.start)))
            pe.append(self._tag("hide_time", ms(page.end)))
            pe.append(self._tag("paint_timing", "0"))
            pe.append(self._tag("layout", "xing_0"))

            t = page.min_start
            styles = None
            for i in range(max(page.lines.keys()), -1, -1):
                le = ET.Element("line")
                text = ""
                if i not in page.lines:
                    if styles:
                        styles = [[styles[-1][0], 0, 0]]
                    else:
                        styles = [[
                            page.lines.values()[0].molecules[0].style, 0, 0
                        ]]
                    # dummy line
                    w = ET.Element("word")
                    w.append(self._tag("text", " "))
                    w.append(self._tag("start_time", ms(t)))
                    w.append(self._tag("end_time", ms(t)))
                    le.append(w)
                    text = " "
                    width = cwidth = 0
                    align = 0
                else:
                    line = page.lines[i]
                    align = line.align
                    ruby = []
                    styles = []

                    for idx, instance in enumerate(line.molecules):
                        mol = instance.molecule
                        step = 0
                        start_pos = len(text)
                        for atom in mol.atoms:
                            start, end = instance.get_atom_time(
                                step, atom.steps)
                            t = max(t, end)
                            step += atom.steps

                            w = ET.Element("word")
                            w.append(self._tag("text", atom.text))
                            w.append(self._tag("start_time", ms(start)))
                            w.append(self._tag("end_time", ms(end)))
                            le.append(w)

                            if atom.particles is not None:
                                edge = len(atom.text)
                                edge_l = 0
                                if atom.particle_edge:
                                    edge = atom.particle_edge
                                if atom.particle_edge_l:
                                    edge_l = atom.particle_edge_l
                                rt = ""
                                for i in atom.particles:
                                    rt += i.text
                                r = ET.Element("ruby")
                                r.append(self._tag("text", rt))
                                r.append(
                                    self._tag("start_pos",
                                              "%d" % (len(text) + edge_l)))
                                r.append(
                                    self._tag("end_pos",
                                              "%d" % (len(text) + edge - 1)))
                                ruby.append(r)

                            text += atom.text
                        if idx != (len(line.molecules) - 1):
                            text += " "
                            w[0].text += " "
                        if styles and instance.style == styles[-1]:
                            styles[-1][2] = len(text) - 1
                        else:
                            styles.append(
                                [instance.style, start_pos,
                                 len(text) - 1])

                    for r in ruby:
                        le.append(r)

                    print(text)
                    width = line.max_px - line.min_px
                    cwidth = len(text)
                    for i in text:
                        ew = unicodedata.east_asian_width(i)
                        if ew in ("F", "W", "A"):
                            cwidth += 1

                    if cwidth > 26:
                        print("  ^-- WARNING: Line likely too long")

                for style, start, end in styles:
                    e = ET.Element("color")
                    e.append(
                        self._tag("before_text_color", color(style.colors[0])))
                    e.append(
                        self._tag("after_text_color",
                                  color(style.colors_on[0])))
                    e.append(
                        self._tag("before_shadow_color",
                                  color(style.colors[1])))
                    e.append(
                        self._tag("after_shadow_color",
                                  color(style.colors_on[1])))
                    e.append(
                        self._tag("before_text_no_fill_color",
                                  color(style.colors[0])))
                    e.append(
                        self._tag("after_text_no_fill_color",
                                  color(style.colors_on[0])))
                    e.append(
                        self._tag("before_shadow_no_fill_color",
                                  color(style.colors[1])))
                    e.append(
                        self._tag("after_shadow_no_fill_color",
                                  color(style.colors_on[1])))
                    e.append(self._tag("start_pos", "%d" % start))
                    e.append(self._tag("end_pos", "%d" % end))
                    e.append(
                        self._tag(
                            "no_fill",
                            "1" if style.colors == style.colors_on else "0"))
                    le.append(e)

                e = ET.Element("duet")
                e.append(self._tag("mark", "0"))
                e.append(self._tag("start_pos", "0"))
                e.append(self._tag("end_pos", "%d" % (len(text) - 1)))
                le.append(e)

                e = ET.Element("offset")
                w = 720
                margin = 54
                offset = margin + (w * (1 - width) - 2 * margin) * align
                if offset < 0 or offset + w * width > w - margin:
                    print("  ^-- WARNING: Line too wide")
                # 0 = default
                # 1 = offset from default
                # 2 = abs left side
                e.append(self._tag("type", "2"))
                e.append(self._tag("auto_pos", "0"))
                e.append(self._tag("offset", "%d" % round(offset)))
                le.append(e)

                pe.append(le)

            telop.append(pe)

        self.root.remove(telop)
        self.root.append(telop)

        telop_edit_setting = ET.XML("""
            <telop_edit_setting>
                <music_volume>50</music_volume>
                <vocal_volume>50</vocal_volume>
            </telop_edit_setting>
        """.replace("\n", "").replace(" ", ""))
        self.root.append(telop_edit_setting)
Exemple #16
0
def entry():
    data_home = os.getenv('XDG_DATA_HOME', '~/.local/share')
    songs_dir = os.path.join(data_home, 'blitzloop', 'songs')

    def csv_list(s):
        return s.split(",")

    parser = util.get_argparser()
    parser.add_argument('--songdir',
                        default=os.path.expanduser(songs_dir),
                        help='directory with songs')
    parser.add_argument('--host', default='0.0.0.0', help='IP to listen on')
    parser.add_argument('--port',
                        default=10111,
                        type=int,
                        help='port for the UI')
    parser.add_argument('--width',
                        type=int,
                        default=1024,
                        help='width of blitzloop window (ignored in fs)')
    parser.add_argument('--height',
                        type=int,
                        default=768,
                        help='height of blitzloop window (ignored in fs)')
    parser.add_argument(
        '--no-audioengine',
        action="store_true",
        help='Disable JACK-based audio engine (mic echo effect)')
    parser.add_argument('--mics',
                        type=csv_list,
                        default=["system:capture_1"],
                        help='Mic input connections (list of JACK ports)')
    opts = util.get_opts()

    songs_dir = os.path.expanduser(opts.songdir)

    print("Loading song DB...")
    song_database = songlist.SongDatabase(songs_dir)
    print("Done.")

    display = graphics.Display(opts.width, opts.height, opts.fullscreen)
    renderer = graphics.get_renderer().KaraokeRenderer(display)
    mpv = mpvplayer.Player(display)

    if not opts.no_audioengine and opts.mics:
        from blitzloop._audio import AudioEngine
        print(repr(opts.mics))
        audio = AudioEngine([s.encode("ascii") for s in opts.mics])
        print("Engine sample rate: %dHz" % audio.sample_rate)

    queue = songlist.SongQueue()

    class AudioConfig(object):
        def __init__(self):
            self.nmics = len(opts.mics) if not opts.no_audioengine else 0
            self.volume = 80
            if opts.no_audioengine:
                self.mic_channels = []
            else:
                self.mic_channels = [{"volume": 80} for i in range(self.nmics)]
            self.mic_feedback = 20
            self.mic_delay = 12
            self.headstart = 30

        def update(self, song=None):
            mpv.set_volume(((self.volume / 100.0)**2) * 0.5)
            if not opts.no_audioengine:
                for i, j in enumerate(self.mic_channels):
                    audio.set_mic_volume(i, ((j["volume"] / 100.0)**2) * 2.0)
                audio.set_mic_feedback(self.mic_feedback / 100.0)
                audio.set_mic_delay(self.mic_delay / 100.0)

    audio_config = AudioConfig()

    web.database = song_database
    web.queue = queue
    web.audio_config = audio_config
    server = web.ServerThread(host=opts.host, port=opts.port)
    server.start()

    idle_screen = idlescreen.IdleScreen(display)

    def main_render():
        # Wait for element in queue
        print("Waiting for song to appear in queue...")
        qe = None
        with queue.lock:
            if len(queue) != 0:
                qe = queue[0]
        if not qe:
            idle_screen.reset()
            graphics.get_renderer().clear(0, 0, 0, 1)
            for f in idle_screen:
                audio_config.update()
                yield None
                graphics.get_renderer().clear(0, 0, 0, 1)
                if not qe:
                    with queue.lock:
                        if len(queue) != 0:
                            qe = queue[0]
                            idle_screen.close()

        for i in range(2):
            yield None
            graphics.get_renderer().clear(0, 0, 0, 1)

        print("Loading audio/video...")
        mpv.load_song(qe.song)
        display.set_aspect(mpv.aspect)

        def update_params(defer=False):
            mpv.set_speed(1.0 / (2**(qe.speed / 12.0)))
            mpv.set_pitch(2**(qe.pitch / 12.0))
            for i, j in enumerate(qe.channels):
                mpv.set_channel(i, j["volume"] / 10., defer=defer)
            mpv.set_pause(qe.pause)

        update_params(True)
        mpv.update_mixer(force=True)

        print("Laying out song...")
        renderer.reset()
        variant_key = list(qe.song.variants.keys())[qe.variant]
        song_layout = layout.SongLayout(qe.song, variant_key, renderer)
        print("Loaded.")

        song_time = -10
        stopping = False
        while not (mpv.eof_reached() or (stopping and qe.pause)):
            while qe.commands:
                cmd, arg = qe.commands.pop(0)
                if cmd == "seek":
                    mpv.seek(arg)
                elif cmd == "seekto":
                    mpv.seek_to(arg)
            mpv.draw()
            mpv.poll()
            song_time = mpv.get_song_time() or song_time

            if qe.stop and not stopping:
                stopping = True
                mpv.fade_out = 2
                mpv.duration = min(mpv.duration, song_time + 2)

            if not stopping:
                mpv.draw_fade(song_time)

            speed = 2**(qe.speed / 12.0)
            renderer.draw(song_time + audio_config.headstart / 100.0 * speed,
                          song_layout)

            if stopping:
                fade = mpv.draw_fade(song_time)
                mpv.set_fadevol(max(fade * 1.3 - 0.3, 0))

            update_params()
            audio_config.update(qe.song)
            yield None
            mpv.flip()

        graphics.get_renderer().clear(0, 0, 0, 1)
        for i in range(2):
            yield None
            graphics.get_renderer().clear(0, 0, 0, 1)

        print("Song complete.")
        del song_layout
        try:
            queue.pop(qe.qid)
        except (IndexError, KeyError):
            pass
        mpv.stop()
        display.set_aspect(None)

    def main():
        while True:
            for i in main_render():
                yield i

    def exit():
        print("Exit handler called")
        mpv.shutdown()
        if not opts.no_audioengine:
            audio.shutdown()
        server.stop()
        print("Exit handler done")

    def key(k):
        if k == 'KEY_ESCAPE':
            display.queue_exit()
        elif k == 'f':
            display.toggle_fullscreen()

    display.set_render_gen(main)
    display.set_keyboard_handler(key)
    display.set_exit_handler(exit)
    display.main_loop()
    threads = threading.enumerate()
    if len(threads) > 1:
        print("Loose threads: %r" % threads)