Started working at an audio player
authorEnrico Zini <enrico@enricozini.org>
Thu, 17 Dec 2009 00:14:26 +0000 (00:14 +0000)
committerEnrico Zini <enrico@enricozini.org>
Thu, 17 Dec 2009 00:14:26 +0000 (00:14 +0000)
CMakeLists.txt
README
player/CMakeLists.txt [new file with mode: 0644]
player/zavai-player-update-info [new file with mode: 0755]
player/zavai-player.vala [new file with mode: 0644]

index 3f43f6679c6725f16c9ad35146160cedad0fe4da..a022b788d460754ee43bd6a6aa567fc70715272b 100644 (file)
@@ -3,5 +3,6 @@ project(zavai)
 set(zavai_version "0.1")
 add_subdirectory(gtkfisheyelist)
 add_subdirectory(polygen)
+add_subdirectory(player)
 add_subdirectory(src)
 #add_subdirectory(hooks)
diff --git a/README b/README
index 8e98adb429d68bdf433c04c27f7339df82bfaf65..4c8edd7e5cd36444f81bd160136bca93141e079e 100644 (file)
--- a/README
+++ b/README
@@ -155,6 +155,17 @@ TODO list / wish list
  http://git.freesmartphone.org/?p=specs.git;a=blob_plain;f=html/index.html;hb=HEAD
 
  * Features to add:
+ - fisheyelist: compute the number of full focus items so that they fill always at leat 1/2 of the screen
+ - fisheye list music player
+    - player page with seek/skip controls
+    - playlist editor
+    - chosen song start playing if nothing is being played
+    - chosen song moves to playlist editor: play, add to playlist, add dir to playlist
+      (show a standard list multiselectable with all the songs in the dir and
+      only the selected song selected; allow to select others, select all, add
+      selected to playlist)
+    - playlist as reorderable standard list, allow to delete tracks, reorder tracks
+    - save playlist to file to reload later
  - lua config
  - contacts: show as a fancy focus+context list (see prefuse)
     - vcard on e-vcard.{h,c}
diff --git a/player/CMakeLists.txt b/player/CMakeLists.txt
new file mode 100644 (file)
index 0000000..735b106
--- /dev/null
@@ -0,0 +1,24 @@
+project(zavai-player)
+include(../vala.cmake)
+
+set(zavai_player_version 0.1)
+
+set(packages gtk+-2.0 gee-1.0)
+set(VALA_PACKAGES ${packages} gtkfisheyelist)
+
+add_packages(ZP ${packages})
+
+set(VFLAGS --vapidir=${gtkfisheyelist_BINARY_DIR})
+file(GLOB zpvala *.vala)
+add_vala_program(zavai-player ${zpvala})
+
+include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR})
+
+# -Werror 
+add_definitions(-Wall -I${gtkfisheyelist_BINARY_DIR})
+link_libraries(gtkfisheyelist-static)
+
+add_executable(zavai-player ${zavai-player_CSOURCES})
+
+install(TARGETS zavai-player RUNTIME DESTINATION bin)
+install(FILES zavai-player-update-info DESTINATION bin)
diff --git a/player/zavai-player-update-info b/player/zavai-player-update-info
new file mode 100755 (executable)
index 0000000..f321d78
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/python
+
+# apt-get install python-mutagen
+
+import os
+import os.path
+import mutagen
+
+MUSICDIR="/store/musica"
+TARGET=os.path.expanduser("~/.zavai/player-info")
+
+# Ensure there's a / in the end, which makes it easier to strip the prefix
+# later
+if MUSICDIR[-1] != '/':
+    MUSICDIR += '/'
+
+def names():
+    for root, dirs, files in os.walk(MUSICDIR):
+        for name in (f for f in files if f.endswith(".mp3")):
+            fname = os.path.join(root, name)
+            mf = mutagen.File(fname)
+            # Scan fname somehow
+            yield dict(name=fname, length=mf.info.length)
+
+out = open(TARGET, "w")
+pfxlen = len(MUSICDIR)
+for info in sorted(names(), key=lambda x:x["name"]):
+    name = info["name"][pfxlen:]
+    print >>out, "file:", name
+    print >>out, "length:", int(round(info["length"] * 1000))
+    print >>out
+out.close()
diff --git a/player/zavai-player.vala b/player/zavai-player.vala
new file mode 100644 (file)
index 0000000..a84f778
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * zavai-player - zavai music player
+ *
+ * Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+using GLib;
+
+protected class Player : Gtk.VBox
+{
+    public Gtk.ScrolledWindow scroll;
+    protected Gtk.TextBuffer text_buffer;
+    protected Gtk.TextView text;
+    protected Regex unhtml;
+    protected Regex seplines;
+
+    public string grm_name { get; set; }
+    public string grm_type { get; set; }
+
+    public Player() throws RegexError
+    {
+        grm_name = "";
+        grm_type = "";
+        unhtml = new Regex("[ ]*<[^>]+>[ ]*", 0, 0);
+        seplines = new Regex("\n");
+        text_buffer = new Gtk.TextBuffer(null);
+        text = new Gtk.TextView.with_buffer(text_buffer);
+        text.wrap_mode = Gtk.WrapMode.WORD;
+        text.cursor_visible = false;
+        text.editable = false;
+        text.add_events(Gdk.EventMask.BUTTON_PRESS_MASK);
+        text.button_press_event += on_button_press;
+        scroll = new Gtk.ScrolledWindow (null, null);
+        scroll.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
+        scroll.add(text);
+        pack_start(scroll, true, true, 0);
+    }
+
+    public bool on_button_press(Gdk.EventButton event)
+    {
+        update();
+        return true;
+    }
+
+    public void update()
+    {
+        if (grm_name == "") return;
+        string[] args = new string[5];
+        args[0] = "/usr/bin/polygen";
+        args[1] = "/usr/share/polygen/" + grm_name + ".grm";
+        if (grm_type == "line/text" || grm_type == "line/html")
+        {
+            args[2] = "-X";
+            args[3] = "10";
+            args[4] = null;
+        }
+        else
+            args[2] = null;
+        int pipe_out;
+        string result;
+        try {
+            Process.spawn_async_with_pipes(homedir, args, null, 0, null, null, null, out pipe_out, null);
+            IOChannel ch = new IOChannel.unix_new(pipe_out);
+            size_t res_len;
+            ch.read_to_end(out result, out res_len);
+            ch.shutdown(false);
+
+            if (grm_type == "line/html" || grm_type == "block/html")
+            {
+                result = unhtml.replace(result, (long)res_len, 0, " ", 0);
+            } else if (grm_type == "line/text") {
+                result = seplines.replace(result, (long)res_len, 0, "\n\n", 0);
+            }
+
+
+        } catch (Error e) {
+            result = "Error: " + e.message;
+        }
+        text_buffer.text = result;
+        Gtk.TextIter iter;
+        text_buffer.get_iter_at_offset(out iter, 0);
+        Gtk.TextMark mark = text_buffer.create_mark(null, iter, true);
+        text.scroll_mark_onscreen(mark);
+    }
+}
+
+public class ZavaiPlayer : Gtk.Window
+{
+    //protected Gee.ArrayList<PolygenPage> pages;
+    protected Gtk.Notebook notebook;
+    protected Player player;
+    protected Gtk.ListStore tracks;
+    protected FisheyeListView tracklist;
+
+    private void add_track(string name, int length)
+    {
+        Gtk.TreeIter iter;
+        tracks.append (out iter);
+        length = length / 1000;
+        tracks.set(iter, 0, "%s - %d'%d".printf(name, length/60, length % 60));
+        tracks.set(iter, 1, name);
+    }
+
+    /*
+    protected void on_selected(string page, string name, string type)
+    {
+        result.grm_name = page + "/" + name;
+        result.grm_type = type;
+        result.update();
+        notebook.set_current_page(notebook.get_n_pages()-1);
+    }
+    */
+
+    public ZavaiPlayer(string label, IOChannel data) throws ConvertError, IOChannelError, RegexError
+    {
+        title = label;
+        destroy += Gtk.main_quit;
+
+        tracks = new Gtk.ListStore(2, typeof(string), typeof(string));
+
+        var vbox = new Gtk.VBox(false, 0);
+        add(vbox);
+
+        //pages = new Gee.ArrayList<PolygenPage>();
+
+        string cur_file = null;
+        int cur_len = 0;
+        while (true)
+        {
+            string line;
+            var res = data.read_line(out line, null, null);
+            if (res != IOStatus.NORMAL) break;
+            line._strip();
+            if (line.size() == 0)
+            {
+                add_track(cur_file, cur_len);
+                cur_file = null;
+                cur_len = 0;
+            } else if (line.has_prefix("file: "))
+                cur_file = line.substring(6);
+            else if (line.has_prefix("length: "))
+                cur_len = line.substring(8).to_int();
+        }
+        if (cur_file != null)
+            add_track(cur_file, cur_len);
+
+        tracklist = new FisheyeListView();
+        tracklist.set_model(tracks);
+        tracklist.row_activated += on_row_activated;
+
+        notebook = new Gtk.Notebook();
+        notebook.append_page(tracklist, new Gtk.Label("Tracks"));
+        /*
+        foreach (PolygenPage p in pages)
+        {
+            notebook.append_page(p.scroll, new Gtk.Label(p.name));
+        }
+        */
+        player = new Player();
+        notebook.append_page(player, new Gtk.Label("Player"));
+
+        vbox.pack_start(notebook, true, true, 0);
+
+        //Gtk.Button select = new Gtk.Button.with_label("Select");
+        //button_box.pack_start(select, true, true, 0);
+        //select.clicked += on_click;
+    }
+
+    public void on_row_activated(Gtk.TreePath path)
+    {
+        Gtk.TreeIter iter;
+        tracks.get_iter(out iter, path);
+        string grm;
+        string type;
+        tracks.get(iter, 1, out grm, 2, out type, -1);
+        stdout.printf("Clicked on %s %s\n", type, grm);
+
+        player.grm_name = grm;
+        player.grm_type = type;
+        player.update();
+        notebook.set_current_page(notebook.get_n_pages()-1);
+    }
+
+    /*
+    private void on_click(Gtk.Button b)
+    {
+        stderr.printf("ZA\n");
+        if (notebook.page >= pages.size)
+            result.update(); // If we are in the result page, just update
+        else
+        {
+            PolygenPage page = pages[notebook.page];
+            page.select();
+        }
+    }
+            */
+}
+
+string homedir;
+
+static int main (string[] args) {
+    Gtk.init (ref args);
+
+    homedir = GLib.Environment.get_home_dir() + "/.zavai";
+    ZavaiPlayer polygen;
+    try {
+        var data = new IOChannel.file(homedir + "/player-info", "r");
+        polygen = new ZavaiPlayer("Zavai Player", data);
+        polygen.set_size_request(300, 500);
+        polygen.show_all();
+        data.shutdown(false);
+
+        //zavai.registry.register_applet("ui.polygen", polygen);
+        //zavai.registry.getmenu("menu.misc").add_applet("ui.polygen");
+    } catch (Error e) {
+        stderr.printf("%s", e.message);
+    }
+
+    Gtk.main();
+    return 0;
+}