summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Seguin <gseguin@alakatha.inria.fr>2009-07-11 00:15:03 +0200
committerGuillaume Seguin <gseguin@alakatha.inria.fr>2009-07-11 00:15:03 +0200
commitfc2aa869a5e3400e4583853342bb18d533095de0 (patch)
treec6efeff22bef68eddbfcc7eb5ef467b14b411573
downloadpylyricsdisplay-fc2aa869a5e3400e4583853342bb18d533095de0.tar.gz
pylyricsdisplay-fc2aa869a5e3400e4583853342bb18d533095de0.tar.bz2
Initial import, almost finished recorder, wip display tool
-rw-r--r--pylyricsdisplay.py189
-rw-r--r--pylyricsrecord.py183
2 files changed, 372 insertions, 0 deletions
diff --git a/pylyricsdisplay.py b/pylyricsdisplay.py
new file mode 100644
index 0000000..c35b20a
--- /dev/null
+++ b/pylyricsdisplay.py
@@ -0,0 +1,189 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+'''
+Copyright (C) 2009 Les Amis d'UlmInfo
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+Authors :
+ Les Amis d'UlmInfo
+'''
+
+import pygst
+pygst.require('0.10')
+import gst
+import cluttergtk
+import clutter
+import gobject, gtk, sys
+import cairo, pango
+
+class LyricsDisplay (cluttergtk.Embed):
+
+ def __init__ (self, uri):
+ super (LyricsDisplay, self).__init__ ()
+ self.set_size_request (800, 400)
+ self.stage = self.get_stage ()
+ self.stage.set_color (clutter.Color (0, 0, 0, 255))
+ self.label = clutter.Label ()
+ self.label.set_width (800)
+ self.label.set_alignment (pango.ALIGN_CENTER)
+ self.label.set_anchor_point_from_gravity (clutter.GRAVITY_CENTER)
+ self.label.set_position (400, 200)
+ self.label.set_use_markup (True)
+ self.label.set_color (clutter.Color (255, 255, 255, 255))
+ self.label.show ()
+ self.stage.add (self.label)
+ self.parse (uri)
+
+ def start (self):
+ self.line = 0
+ self.item = -1
+ self.next_timer = self.bits[self.line][0][0]
+ self.next_line = 0
+ self.next_item = 0
+ if self.bits:
+ self.label.set_text (self.get_current_line (0))
+
+ def reset (self):
+ pass
+
+ def get_current_line (self, current_timer):
+ line = ""
+ got_current = False
+ for timer, content in self.bits[self.line]:
+ if not got_current and current_timer >= timer:
+ got_current = True
+ line += content
+ else:
+ line += content
+ return line
+
+ def update (self, timer):
+ if self.next_line != -1 and timer >= self.next_timer:
+ self.line = self.next_line
+ self.item = self.next_item
+ if self.item == len (self.bits[self.line]) - 1:
+ if self.line == len (self.bits) - 1:
+ self.next_line = -1
+ else:
+ self.next_line += 1
+ self.next_item = 0
+ self.next_timer = self.bits[self.next_line][self.next_item][0]
+ else:
+ self.next_item +=1
+ self.next_timer = self.bits[self.next_line][self.next_item][0]
+ print "prout"
+ self.label.set_text (self.get_current_line (timer))
+ return
+
+ def parse (self, uri):
+ f = open (uri)
+ data = f.read ()
+ f.close ()
+ self.bits = {0 : []}
+ line = 0
+ pos = data.find ("[", 0)
+ while pos != -1:
+ pos_end = data.find ("]", pos)
+ pos_next = data.find ("[", pos + 1)
+ time = int (data[pos + 1:pos_end])
+ if pos_next != -1:
+ content = data[pos_end + 1:pos_next]
+ else:
+ content = data[pos_end + 1:]
+ self.bits[line].append ((time, content))
+ if "\n" in content:
+ line += 1
+ self.bits[line] = []
+ pos = pos_next
+ if not self.bits[line]:
+ del self.bits[line]
+ for line in sorted (self.bits.keys ()):
+ print self.bits[line]
+
+class PyLyricsDisplay (object):
+
+ def __init__ (self, music_uri, lyrics_uri):
+ self.window = gtk.Window (gtk.WINDOW_TOPLEVEL)
+ self.window.set_title ("PyLyricsDisplay")
+ self.window.set_default_size (800, 400)
+ self.window.connect ("delete-event", gtk.main_quit)
+
+ vbox = gtk.VBox ()
+ self.window.add (vbox)
+
+ self.start_stop_button = gtk.Button ("Start")
+ self.start_stop_button.connect ("clicked", self.start_stop)
+ vbox.pack_start (self.start_stop_button, False, False)
+
+ self.display = LyricsDisplay (lyrics_uri)
+ vbox.pack_start (self.display)
+ self.display.realize ()
+
+ self.statusbar = gtk.Statusbar ()
+ vbox.pack_end (self.statusbar, False, False)
+
+ self.player = gst.element_factory_make ("playbin", "player")
+ self.player.set_property ('uri', "file://" + music_uri)
+ fakesink = gst.element_factory_make ("fakesink", "fakesink")
+ self.player.set_property ("video-sink", fakesink)
+
+ self.window.show_all ()
+
+ def start_stop (self, widget):
+ if widget.get_label () == "Start":
+ self.player.set_state (gst.STATE_PLAYING)
+ self.display.start ()
+ self.status_update_timer = gobject.timeout_add (500, self.update_statusbar)
+ self.display_update_timer = gobject.timeout_add (100, self.update_display)
+ widget.set_label ("Stop")
+ else:
+ self.player.set_state (gst.STATE_NULL)
+ self.display.reset ()
+ gobject.source_remove (self.status_update_timer)
+ gobject.source_remove (self.display_update_timer)
+ widget.set_label ("Start")
+
+ def update_display (self):
+ try:
+ position = self.player.query_position (gst.FORMAT_TIME)[0] / (1000 * 1000)
+ self.display.update (position)
+ return True
+ except:
+ return False
+
+ def update_statusbar (self):
+ try:
+ position = self.player.query_position (gst.FORMAT_TIME)[0] / (1000 * 1000 * 1000)
+ duration = self.player.query_duration (gst.FORMAT_TIME)[0] / (1000 * 1000 * 1000)
+ pos_t = datetime.fromtimestamp (position) - timedelta (hours = 1)
+ dur_t = datetime.fromtimestamp (duration) - timedelta (hours = 1)
+ self.set_status ("%s / %s" % (pos_t.strftime ("%H:%M:%S"), dur_t.strftime ("%H:%M:%S")))
+ return True
+ except:
+ return False
+
+ def set_status (self, status):
+ context = self.statusbar.get_context_id ("main")
+ self.statusbar.pop (context)
+ self.statusbar.push (context, status)
+
+if __name__ == "__main__":
+ if len (sys.argv) != 3:
+ print "Usage : %s SOUNDFILE LYRICSFILE"
+ raise SystemExit
+ recorder = PyLyricsDisplay (sys.argv[1], sys.argv[2])
+ gtk.main ()
diff --git a/pylyricsrecord.py b/pylyricsrecord.py
new file mode 100644
index 0000000..d1d5628
--- /dev/null
+++ b/pylyricsrecord.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python
+# coding: utf-8
+
+'''
+Copyright (C) 2009 Les Amis d'UlmInfo
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+Authors :
+ Les Amis d'UlmInfo
+'''
+
+import pygst
+pygst.require('0.10')
+import gst
+import gobject, gtk, sys
+import pango
+from datetime import datetime, timedelta
+
+class LyricsEditor (gtk.TextView):
+
+ def __init__ (self, uri):
+ super (LyricsEditor, self).__init__ ()
+ self.uri = uri
+ self.can_mark = False
+ self.buffer = self.get_buffer ()
+ self.tag = self.buffer.create_tag ()
+ self.tag.set_property ("background", "darkred")
+ self.tag.set_property ("foreground", "white")
+ self.tag.set_property ("weight", pango.WEIGHT_BOLD)
+ self.scroll_mark = self.buffer.create_mark (None, self.buffer.get_start_iter ())
+ self.timers = []
+
+ def lock (self):
+ self.set_editable (False)
+ self.current = 0
+ self.text = unicode (self.buffer.get_text (self.buffer.get_start_iter (), self.buffer.get_end_iter ()))
+ self.select_next ()
+
+ def select_next (self):
+ def next_pos ():
+ space_pos = self.text.find (" ", self.current)
+ tab_pos = self.text.find ("\t", self.current)
+ newline_pos = self.text.find ("\n", self.current)
+ pos = [space_pos, tab_pos, newline_pos]
+ if max (pos) == -1:
+ return -1
+ return min ([p for p in pos if p != -1])
+ pos = next_pos ()
+ while pos == self.current:
+ self.current += 1
+ pos = next_pos ()
+ if pos == -1:
+ self.can_mark = False
+ return
+ self.current_end = pos
+ self.can_mark = True
+ self.start_iter = self.buffer.get_iter_at_offset (self.current)
+ self.end_iter = self.buffer.get_iter_at_offset (self.current_end)
+ self.buffer.move_mark (self.scroll_mark, self.start_iter)
+ self.scroll_mark_onscreen (self.scroll_mark)
+ self.buffer.apply_tag (self.tag, self.start_iter, self.end_iter)
+
+ def mark (self, timer):
+ if not self.can_mark:
+ return
+ self.buffer.remove_tag (self.tag, self.start_iter, self.end_iter)
+ print self.current, self.current_end, timer
+ self.timers.append ((timer, self.current))
+ self.current = self.current_end + 1
+ self.select_next ()
+
+ def save (self):
+ f = open (self.uri, "w")
+ prev_timer, prev_start = None, None
+ for (timer, start) in self.timers:
+ if prev_timer != None:
+ f.write ("[%d]%s" % (timer, self.text[prev_start:start]))
+ prev_timer, prev_start = timer, start
+ if prev_timer != None:
+ f.write ("[%d]%s" % (timer, self.text[prev_start:]))
+ f.close ()
+
+class PyLyricsRecord (object):
+
+ def __init__ (self, music_uri, lyrics_uri):
+ self.window = gtk.Window (gtk.WINDOW_TOPLEVEL)
+ self.window.set_title ("PyLyricsRecord")
+ self.window.set_default_size (600, 500)
+ self.window.connect ("delete-event", gtk.main_quit)
+
+ vbox = gtk.VBox ()
+ self.window.add (vbox)
+
+ self.lock_button = gtk.Button ("Lock lyrics")
+ self.lock_button.connect ("clicked", self.lock_lyrics)
+ vbox.pack_start (self.lock_button, False, False)
+
+ self.start_stop_button = gtk.Button ("Start")
+ self.start_stop_button.connect ("clicked", self.start_stop)
+ self.start_stop_button.set_sensitive (False)
+ vbox.pack_start (self.start_stop_button, False, False)
+
+ self.mark_button = gtk.Button ("Mark")
+ self.mark_button.connect ("clicked", self.mark)
+ self.mark_button.set_sensitive (False)
+ vbox.pack_start (self.mark_button, False, False)
+
+ self.editor_window = gtk.ScrolledWindow ()
+ self.editor_window.set_policy (gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ self.editor = LyricsEditor (lyrics_uri)
+ self.editor_window.add (self.editor)
+ vbox.pack_start (self.editor_window)
+
+ self.statusbar = gtk.Statusbar ()
+ vbox.pack_end (self.statusbar, False, False)
+
+ self.player = gst.element_factory_make ("playbin", "player")
+ self.player.set_property ('uri', "file://" + music_uri)
+ fakesink = gst.element_factory_make ("fakesink", "fakesink")
+ self.player.set_property ("video-sink", fakesink)
+
+ self.window.show_all ()
+
+ def start_stop (self, widget):
+ if widget.get_label () == "Start":
+ self.player.set_state (gst.STATE_PLAYING)
+ self.mark_button.set_sensitive (True)
+ self.status_update_timer = gobject.timeout_add (500, self.update_statusbar)
+ widget.set_label ("Stop")
+ else:
+ self.player.set_state (gst.STATE_NULL)
+ self.mark_button.set_sensitive (False)
+ self.editor.save ()
+ self.set_status ("Saved")
+ gobject.source_remove (self.status_update_timer)
+ widget.set_label ("Start")
+
+ def mark (self, widget):
+ position, format = self.player.query_position (gst.FORMAT_TIME)
+ self.editor.mark (position / (1000 * 1000))
+
+ def lock_lyrics (self, widget):
+ if not widget.get_property ("sensitive"):
+ return
+ self.lock_button.set_sensitive (False)
+ self.editor.lock ()
+ self.start_stop_button.set_sensitive (True)
+
+ def update_statusbar (self):
+ try:
+ position = self.player.query_position (gst.FORMAT_TIME)[0] / (1000 * 1000 * 1000)
+ duration = self.player.query_duration (gst.FORMAT_TIME)[0] / (1000 * 1000 * 1000)
+ pos_t = datetime.fromtimestamp (position) - timedelta (hours = 1)
+ dur_t = datetime.fromtimestamp (duration) - timedelta (hours = 1)
+ self.set_status ("%s / %s" % (pos_t.strftime ("%H:%M:%S"), dur_t.strftime ("%H:%M:%S")))
+ return True
+ except:
+ return False
+
+ def set_status (self, status):
+ context = self.statusbar.get_context_id ("main")
+ self.statusbar.pop (context)
+ self.statusbar.push (context, status)
+
+if __name__ == "__main__":
+ if len (sys.argv) != 3:
+ print "Usage : %s SOUNDFILE LYRICSFILE"
+ raise SystemExit
+ recorder = PyLyricsRecord (sys.argv[1], sys.argv[2])
+ gtk.main ()