示例#1
0
class WaveformSeekBar(Gtk.Box):
    """A widget containing labels and the seekbar."""

    def __init__(self, player, library):
        super(WaveformSeekBar, self).__init__()

        self._player = player
        self._rms_vals = []

        self._elapsed_label = TimeLabel()
        self._remaining_label = TimeLabel()
        self._waveform_scale = WaveformScale()

        self.pack_start(Align(self._elapsed_label, border=6), False, True, 0)
        self.pack_start(self._waveform_scale, True, True, 0)
        self.pack_start(Align(self._remaining_label, border=6), False, True, 0)

        for child in self.get_children():
            child.show_all()

        self._tracker = TimeTracker(player)
        self._tracker.connect('tick', self._on_tick, player)

        connect_destroy(player, 'seek', self._on_player_seek)
        connect_destroy(player, 'song-started', self._on_song_started)
        connect_destroy(player, 'song-ended', self._on_song_ended)
        connect_destroy(player, 'notify::seekable', self._on_seekable_changed)
        connect_destroy(library, 'changed', self._on_song_changed, player)

        self.connect('destroy', self._on_destroy)
        self._update(player)
        self._tracker.tick()

        if player.info:
            self._create_waveform(player.info, CONFIG.data_size)

    def _create_waveform(self, song, points):
        # Close any existing pipelines to avoid warnings
        if hasattr(self, "_pipeline") and self._pipeline:
            self._pipeline.set_state(Gst.State.NULL)

        command_template = """
        filesrc location="{}"
        ! decodebin
        ! level name=audiolevel interval={} post-messages=true
        ! fakesink sync=false"""
        interval = int(song("~#length") * 1E9 / points)
        print_d("Computing data for each %.3f seconds" % (interval / 1E9))

        filename = song("~filename").replace('"', '\\"')
        command = command_template.format(filename, interval)
        pipeline = Gst.parse_launch(command)

        bus = pipeline.get_bus()
        self._bus_id = bus.connect("message", self._on_bus_message)
        bus.add_signal_watch()

        pipeline.set_state(Gst.State.PLAYING)

        self._pipeline = pipeline
        self._rms_vals = []

    def _on_bus_message(self, bus, message):
        if message.type == Gst.MessageType.ERROR:
            error, debug = message.parse_error()
            print_d("Error received from element {name}: {error}".format(
                name=message.src.get_name(), error=error))
            print_d("Debugging information: {}".format(debug))
        elif message.type == Gst.MessageType.ELEMENT:
            structure = message.get_structure()
            if structure.get_name() == "level":
                rms_db = structure.get_value("rms")
                # Calculate average of all channels (usually 2)
                rms_db_avg = sum(rms_db) / len(rms_db)
                # Normalize dB value to value between 0 and 1
                rms = pow(10, (rms_db_avg / 20))
                self._rms_vals.append(rms)
            else:
                print_w("Got unexpected message of type {}"
                        .format(message.type))
        elif message.type == Gst.MessageType.EOS:
            self._pipeline.set_state(Gst.State.NULL)

            if self._player.info:
                self._waveform_scale.reset(self._rms_vals, self._player)
                self._waveform_scale.set_placeholder(False)

    def _on_destroy(self, *args):
        self._tracker.destroy()

    def _on_tick(self, tracker, player):
        self._update(player)

    def _on_seekable_changed(self, player, *args):
        self._update(player)

    def _on_player_seek(self, player, song, ms):
        self._update(player)

    def _on_song_changed(self, library, songs, player):
        if player.info:
            self._create_waveform(player.info, CONFIG.data_size)

        self._waveform_scale.set_placeholder(True)
        self._update(player)

    def _on_song_started(self, player, song):
        self._update(player)

    def _on_song_ended(self, player, song, ended):
        self._update(player)

    def _update(self, player):
        if player.info:
            # Position in ms, length in seconds
            position = player.get_position() / 1000.0
            length = player.info("~#length")
            remaining = length - position

            if length != 0:
                self._waveform_scale.set_position(position / length)
            else:
                print_d("Length reported as zero for %s" % player.info)
                self._waveform_scale.set_position(0)

            self._elapsed_label.set_time(position)
            self._remaining_label.set_time(remaining)
            self._remaining_label.set_disabled(not player.seekable)
            self._elapsed_label.set_disabled(not player.seekable)

            self.set_sensitive(player.seekable)
        else:
            self._waveform_scale.set_placeholder(True)
            self._remaining_label.set_disabled(True)
            self._elapsed_label.set_disabled(True)

            self.set_sensitive(player.seekable)

        self._waveform_scale.queue_draw()
示例#2
0
class SeekBar(Gtk.Box):

    def __init__(self, player, library):
        super(SeekBar, self).__init__()

        self._elapsed_label = TimeLabel()
        self._remaining_label = TimeLabel()
        scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL)
        scale.set_adjustment(Gtk.Adjustment.new(0, 0, 0, 3, -15, 0))
        scale.set_draw_value(False)
        self._scale = scale

        self.pack_start(Align(self._elapsed_label, border=6), False, True, 0)
        self.pack_start(scale, True, True, 0)
        self.pack_start(Align(self._remaining_label, border=6), False, True, 0)
        for child in self.get_children():
            child.show_all()

        self._id = self._scale.connect(
            'value-changed', self._on_user_changed, player)
        self._scale.connect(
            'value-changed', self._on_scale_value_changed, player)

        self._tracker = TimeTracker(player)
        self._tracker.connect('tick', self._on_tick, player)

        connect_destroy(player, 'seek', self._on_player_seek)
        connect_destroy(player, 'song-started', self._on_song_started)
        connect_destroy(player, "notify::seekable", self._on_seekable_changed)

        connect_destroy(
            library, "changed", self._on_song_changed, player)

        self.connect("destroy", self._on_destroy)

        with self._inhibit():
            self._update(player)
        self._tracker.tick()

    def _on_destroy(self, *args):
        self._tracker.destroy()

    @contextlib.contextmanager
    def _inhibit(self):
        with GObject.signal_handler_block(self._scale, self._id):
            yield

    def _on_user_changed(self, scale, player):
        if player.seekable:
            player.seek(scale.get_value() * 1000)

    def _on_scale_value_changed(self, scale, player):
        self._update(player)

    def _on_tick(self, tracker, player):
        position = player.get_position() // 1000
        with self._inhibit():
            self._scale.set_value(position)

    def _on_seekable_changed(self, player, *args):
        with self._inhibit():
            self._update(player)

    def _on_song_changed(self, library, songs, player):
        if player.info in songs:
            with self._inhibit():
                self._update(player)

    def _on_player_seek(self, player, song, ms):
        with self._inhibit():
            self._scale.set_value(ms // 1000)
            self._update(player)

    def _on_song_started(self, player, song):
        with self._inhibit():
            self._scale.set_value(0)
            self._update(player)

    def _update(self, player):
        if player.info:
            self._scale.set_range(0, player.info("~#length"))
        else:
            self._scale.set_range(0, 1)

        if not player.seekable:
            self._scale.set_value(0)

        value = self._scale.get_value()
        max_ = self._scale.get_adjustment().get_upper()
        remaining = value - max_
        self._elapsed_label.set_time(value)
        self._remaining_label.set_time(remaining)
        self._remaining_label.set_disabled(not player.seekable)
        self._elapsed_label.set_disabled(not player.seekable)

        self.set_sensitive(player.seekable)
示例#3
0
class SeekBar(Gtk.Box):
    def __init__(self, player, library):
        super(SeekBar, self).__init__()

        self._elapsed_label = TimeLabel()
        self._remaining_label = TimeLabel()
        scale = Gtk.Scale(orientation=Gtk.Orientation.HORIZONTAL)
        scale.set_adjustment(Gtk.Adjustment.new(0, 0, 0, 3, 15, 0))
        scale.set_draw_value(False)
        self._scale = scale

        self.pack_start(Align(self._elapsed_label, border=6), False, True, 0)
        self.pack_start(scale, True, True, 0)
        self.pack_start(Align(self._remaining_label, border=6), False, True, 0)
        for child in self.get_children():
            child.show_all()

        self._id = self._scale.connect("value-changed", self._on_user_changed, player)
        self._scale.connect("value-changed", self._on_scale_value_changed, player)

        self._tracker = TimeTracker(player)
        self._tracker.connect("tick", self._on_tick, player)

        connect_destroy(player, "seek", self._on_player_seek)
        connect_destroy(player, "song-started", self._on_song_started)
        connect_destroy(player, "notify::seekable", self._on_seekable_changed)

        connect_destroy(library, "changed", self._on_song_changed, player)

        self.connect("destroy", self._on_destroy)

        with self._inhibit():
            self._update(player)
        self._tracker.tick()

    def _on_destroy(self, *args):
        self._tracker.destroy()

    @contextlib.contextmanager
    def _inhibit(self):
        with GObject.signal_handler_block(self._scale, self._id):
            yield

    def _on_user_changed(self, scale, player):
        if player.seekable:
            player.seek(scale.get_value() * 1000)

    def _on_scale_value_changed(self, scale, player):
        self._update(player)

    def _on_tick(self, tracker, player):
        position = player.get_position() // 1000
        with self._inhibit():
            self._scale.set_value(position)

    def _on_seekable_changed(self, player, *args):
        with self._inhibit():
            self._update(player)

    def _on_song_changed(self, library, songs, player):
        if player.info in songs:
            with self._inhibit():
                self._update(player)

    def _on_player_seek(self, player, song, ms):
        with self._inhibit():
            self._scale.set_value(ms // 1000)
            self._update(player)

    def _on_song_started(self, player, song):
        with self._inhibit():
            self._scale.set_value(0)
            self._update(player)

    def _update(self, player):
        if player.info:
            self._scale.set_range(0, player.info("~#length"))
        else:
            self._scale.set_range(0, 1)

        if not player.seekable:
            self._scale.set_value(0)

        value = self._scale.get_value()
        max_ = self._scale.get_adjustment().get_upper()
        remaining = value - max_
        self._elapsed_label.set_time(value)
        self._remaining_label.set_time(remaining)
        self._remaining_label.set_disabled(not player.seekable)
        self._elapsed_label.set_disabled(not player.seekable)

        self.set_sensitive(player.seekable)
示例#4
0
class WaveformSeekBar(Gtk.Box):
    """A widget containing labels and the seekbar."""

    def __init__(self, player, library):
        super(WaveformSeekBar, self).__init__()

        self._player = player
        self._rms_vals = []

        self._elapsed_label = TimeLabel()
        self._remaining_label = TimeLabel()
        self._waveform_scale = WaveformScale()

        self.pack_start(Align(self._elapsed_label, border=6), False, True, 0)
        self.pack_start(self._waveform_scale, True, True, 0)
        self.pack_start(Align(self._remaining_label, border=6), False, True, 0)

        for child in self.get_children():
            child.show_all()

        self._tracker = TimeTracker(player)
        self._tracker.connect('tick', self._on_tick, player)

        connect_destroy(player, 'seek', self._on_player_seek)
        connect_destroy(player, 'song-started', self._on_song_started)
        connect_destroy(player, 'song-ended', self._on_song_ended)
        connect_destroy(player, 'notify::seekable', self._on_seekable_changed)
        connect_destroy(library, 'changed', self._on_song_changed, player)

        self.connect('destroy', self._on_destroy)
        self._update(player)
        self._tracker.tick()

        if player.info:
            self._create_waveform(player.info, CONFIG.data_size)

    def _create_waveform(self, song, points):
        # Close any existing pipelines to avoid warnings
        if hasattr(self, "_pipeline") and self._pipeline:
            self._pipeline.set_state(Gst.State.NULL)

        command_template = """
        filesrc name=fs
        ! decodebin ! audioconvert
        ! level name=audiolevel interval={} post-messages=true
        ! fakesink sync=false"""
        interval = int(song("~#length") * 1E9 / points)
        print_d("Computing data for each %.3f seconds" % (interval / 1E9))

        command = command_template.format(interval)
        pipeline = Gst.parse_launch(command)
        pipeline.get_by_name("fs").set_property("location", song("~filename"))

        bus = pipeline.get_bus()
        self._bus_id = bus.connect("message", self._on_bus_message)
        bus.add_signal_watch()

        pipeline.set_state(Gst.State.PLAYING)

        self._pipeline = pipeline
        self._rms_vals = []

    def _on_bus_message(self, bus, message):
        if message.type == Gst.MessageType.ERROR:
            error, debug = message.parse_error()
            print_d("Error received from element {name}: {error}".format(
                name=message.src.get_name(), error=error))
            print_d("Debugging information: {}".format(debug))
        elif message.type == Gst.MessageType.ELEMENT:
            structure = message.get_structure()
            if structure.get_name() == "level":
                rms_db = structure.get_value("rms")
                # Calculate average of all channels (usually 2)
                rms_db_avg = sum(rms_db) / len(rms_db)
                # Normalize dB value to value between 0 and 1
                rms = pow(10, (rms_db_avg / 20))
                self._rms_vals.append(rms)
            else:
                print_w("Got unexpected message of type {}"
                        .format(message.type))
        elif message.type == Gst.MessageType.EOS:
            self._pipeline.set_state(Gst.State.NULL)

            if self._player.info:
                self._waveform_scale.reset(self._rms_vals, self._player)
                self._waveform_scale.set_placeholder(False)

    def _on_destroy(self, *args):
        self._tracker.destroy()

    def _on_tick(self, tracker, player):
        self._update(player)

    def _on_seekable_changed(self, player, *args):
        self._update(player)

    def _on_player_seek(self, player, song, ms):
        self._update(player)

    def _on_song_changed(self, library, songs, player):
        if player.info:
            self._create_waveform(player.info, CONFIG.data_size)

        self._waveform_scale.set_placeholder(True)
        self._update(player)

    def _on_song_started(self, player, song):
        self._update(player)

    def _on_song_ended(self, player, song, ended):
        self._update(player)

    def _update(self, player):
        if player.info:
            # Position in ms, length in seconds
            position = player.get_position() / 1000.0
            length = player.info("~#length")
            remaining = length - position

            if length != 0:
                self._waveform_scale.set_position(position / length)
            else:
                print_d("Length reported as zero for %s" % player.info)
                self._waveform_scale.set_position(0)

            self._elapsed_label.set_time(position)
            self._remaining_label.set_time(remaining)
            self._remaining_label.set_disabled(not player.seekable)
            self._elapsed_label.set_disabled(not player.seekable)

            self.set_sensitive(player.seekable)
        else:
            self._waveform_scale.set_placeholder(True)
            self._remaining_label.set_disabled(True)
            self._elapsed_label.set_disabled(True)

            self.set_sensitive(player.seekable)

        self._waveform_scale.queue_draw()