2004-01-24 Francisco Javier F. Serrador <serrador@cvs.gnome.org>
[rhythmbox.git] / INTERNALS
blob130468bedc859a336724108c862433179204a64a
1 * Rhythmbox Internals
2 * This file is completely out of date, but it's kept in the hope that it will be sorta useful
3 * See rhythmdb/DESIGN for some bits
5 This document will attempt to gather up some of the bits and pieces
6 I've learned while hacking Rhythmbox.  Rhythmbox is fairly complex,
7 and while some people would claim it is unnecessarily so, I think
8 writing a good music player is just not as simple as you might
9 think at first.  So, let's begin.
11 ** The Shell  (RBShell, RBShellPlayer, RBSourceHeader, RBStatusBar, RBShellPreferences)
13 The shell is the outer Rhythmbox framework.  It controls the playback,
14 menus, preferences, and most of the user interface in general.  The core
15 component of the shell is RBShell, in shell/rb-shell.c.  It acts as
16 kind of a catch-all for the various bits of glue needed to keep
17 Rhythmbox working together.  It handles most of the menu callbacks,
18 handles the playlist addition/deletion interface, maintains
19 the tray icon, and forms the main widget which holds all of the widgets below.
20 Unfortunately because of this rb-shell.c is getting a fairly big, and
21 we plan to break it up into smaller classes eventually.
23 There are other more specialized components of the shell, such as
24 RBShellPlayer. RBShellPlayer is a widget which handles the
25 play/previous/next buttons, and contains various other widgets for the
26 status display and volume.  RBShellPlayer is a pretty important class,
27 because it contains a lot of the playback logic.
29 RBSourceHeader is that thingy with the "Hide Browser" button and the search
30 entry.
32 RBStatusBar is the thing on the bottom with the Shuffle and Repeat buttons
33 and the status output.
35 RBShellPreferences manages the user preferences.  It is just a dialog box
36 which pops up when you hit Edit->Preferences.
38 ** Sources
40 Rhythmbox has an idea of multiple music "sources", like the Library
41 and (Internet) Radio.  The RBSource classes are basically the
42 user interface part of the "source" concept.
44 All of these sources derive from RBSource (sources/rb-source.[ch]),
45 which is an abstract base class.  RBSource has a number of methods
46 which the specific sources like the Library implement.  For example,
47 one of the simpler ones is:
49 gboolean        rb_source_can_pause             (RBSource *player);
51 So here, a source returns TRUE if it can pause (i.e. pause button should
52 be displayed).  Another example is the rb_source_get_status method,
53 which is called to display a status string at the bottom of the window.
55 The RBShell maintains a list of available RBSources.
57 ** Backends
59 All RBSource instances use some sort of"backend" too.  The RBLibrarySource
60 has the RBLibrary.  RBIRadioSource has the RBIRadioBackend.  The RBPlaylistSource
61 uses the RBLibrary.  As you might expect, while the RBSource components act as
62 a "frontend", the various backends maintain all the data and don't deal much
63 with user interfaces.
65 ** A first look at the Library
67 The Library is the main part of Rhythmbox; it is the thing that lets
68 you play local files.  We'll go over some of the most important things,
69 and then come back to it later once we know more.
71 *** RBNode
73 Understanding RBNode (library/rb-node.c) is crucial.  RBNode is not
74 just used by the Library actually; every other source uses it as well.
75 RBNode is therefore a pretty abstract thing.  Essentially, it
76 is a node in a tree, which can have both parents and children.  We
77 will examine how the tree structure is used shortly.
79 **** Dynamic properties
81 An RBNode also has a dynamic set of properties associated with it.  A
82 property has both a name and a value.  The value can be of many different
83 types; e.g. a string, integer, or a pointer to another node. 
85 These dynamic properties pretty much correspond to the song
86 metadata you can see like song length, duration, location, etc.
88 **** Thread safety
90 If you look at the RBNode code, you will notice that it has a lot
91 of thread locking.  That's because any thread can manipulate the
92 node database concurrently.  Each RBNode contains a read/write
93 mutex.  With just one or two sepcial exceptions, all the locking is
94 handled transparently by the rb_node_* functions.  It is safe to
95 call e.g. rb_node_set_property from any thread at any time, and
96 this is in fact done.
98 *** The tree structure (RBLibrary)
100 So where does this RBNode parent/children relationship come in?
101 Rhythmbox maintains a sort of tree structured database of your music.
102 This is what allows it implement the browser (filtering by genre, artist,
103 album). As we said, an RBNode is part of a tree.  The Library sets up a
104 tree (actually technically it's more like a forest) which looks like
105 this:
108           Genre1                     Genre2                  Genre3
109          /      ---                    |                       ...
110         /          \-                  |
111        Artist1      Artist2          Artist3
112      --   |   ---        ---            --  -----
113    -/     |      \-         Album4        \      \---
114 Album1  Album2   Album3--       --        Album5   Album6---
115   |       /  \     ---   \--      \--       \----    ----   \---
116   |     -/    \       \-    \-       \-      \   \---    \---   \--
117 Song1  Song2  Song3   Song4 Song5    Song6   Song7  Song8  Song9  Song10
119 This hierarchy is the core of what Rhythmbox presents to the user.
120 When you go into the song properties dialog and edit a song title,
121 you're really changing the RB_NODE_PROP_NAME of an RBNode.  If you
122 change the artist of an RBNode, it is reparented to the new artist
123 (and a new album is created if necessary).
125 **** The "All" nodes maintained by RBLibrary/RBIRadioBackend
127 Actually, the tree is slightly more complicated than this; besides
128 The Genre->Artist->Album->Song chains, there are also special
129 "All" nodes.  These represent the bold "All" entries you see
130 on the display.  These "All" nodes basically point to
131 all of one class of node.  For example, there is an "All Songs"
132 node, which is the parent of every song.
133 This allows us to easily iterate over all songs, and will come
134 in handy in a minute when we look at RBNodeView. 
136 **** Saving/loading
138 RBNode also has a generic "serialization" infrastructure, which
139 converts an RBNode to XML, which is then saved to disk.  This is
140 how Rhythmbox stores information about songs.  If you look in
141 ~/.gnome2/rhythmbox, you should see several XML files there.
142 Essentially that file just contains property IDs (which correspond
143 to the values in library/rb-node.h), and their values.
145 ** Node Views (RBNodeView)
147 Now that we understand that Rhythmbox has this tree database of our
148 music, a logical question: how is it displayed to the user?  Well,
149 that's the job of an RBNodeView (lib/widgets/rb-node-view.[ch]).
151 An RBNodeView basically takes an RBNode and presents it all of its
152 children using a GtkTreeView.  Remember special the "All" nodes?
153 This is where they come in; for example, the Library has four
154 RBNodeViews; one corresponding to  each "All" node.  
156 Simply speaking, an RBNodeView contains both a GtkTreeView and an
157 RBTreeModelNode.  The RBTreeModelNode takes care of the details of
158 implementing the GtkTreeModel interface (see the GTK+ docs for more
159 details).  Then we just use a GtkTreeView to show that to the user.
160 RBNodeView hooks in a number of callbacks for sorting and
161 filtering things, but this is the general idea.
163 *** Usage
165 Each RBSource has at least one RBNodeView.  This is also very important
166 to understand.  RBSource has a method which looks like this:
168 RBNodeView *    (*impl_get_node_view)   (RBSource *source);
170 This method is called by RBShellPlayer to access a source's RBNodeView.
171 The RBNodeView maintains state, such as which song is selected.  When
172 a song is double-clicked, a signal is emitted which RBShellPlayer catches,
173 and then gets the currently selected RBNode, and uses it to play the song.
175 *** Filtering
177 In addition to providing a view of the RBNode tree, the RBNodeView
178 also takes care of filtering it, using an RBNodeFilter
179 (library/rb-node-filter.[ch]).  Note that Rhythmbox is quite fast
180 at filtering by genre/artist/album.  This is because of the tree
181 structure used above.  To check whether a particular song should be
182 displayed, we just check whether it has the selected genre or whatever
183 as a parent.
185 ** More on the Library
187 When you add music or delete music in the library, you might notice that it happens
188 "in the background".  This is because there is are several separate threads
189 dedicated to processing addition/removal/update requests on the library.
191 The main one is (unsurprisingly) called RBLibraryMainThread.  This class
192 continually reads the main RBLibraryActionQueue (which is a queue of pending
193 requests from the user), and processes them.  For instance, an
194 RB_LIBRARY_ACTION_ADD_FILE request causes a new node to be created.
195 Note that the node system itself has the required locks needed for this to work.
197 There is a secondary thread RBLibraryWalkerThread which is dedicated to handling
198 recursive library additions.  It has its own RBLibraryActionQueue instance, and
199 it watches for RB_LIBRARY_ACTION_ADD_DIRECTORY requests.  It then recursively
200 traverses the URI and adds RB_LIBRARY_ACTION_ADD_FILE requests to the main
201 queue.
203 ** The Radio Source
205 Internet radio is handled similarly to the library.  Instead of the
206 Genre->Artist->Album->Song tree, the hierarchy simply looks like:
207 Genre->Song.  This hierarchy is maintained by the RBIRadioBackend.
208 The RBIRadioSource handles most of the user-interface bits such
209 as popping up dialog boxes and filtering and such.
211 ** The Playlist Source
213 Once we have the library and RBNode, the playlist source is actually pretty
214 easy to implement.  It maintains a single RBNode similar to the "All" node
215 of the other sources.  This node is the parent of all the songs in the playlist.
217 Note also that the Playlist source doesn't have its own backend; instead
218 it just uses the node database from the library.  In the future, we hope
219 to unify the backends so that both local songs and internet radio stations
220 can be added to a playlist.
222 Local Variables:
223 mode: outline
224 End:
226 arch-tag: A description of the Rhythmbox internals