GSoC Proposal Feedback

vedant agarwala vedant.kota at gmail.com
Mon Mar 10 20:51:49 UTC 2014


Hello again,

I have just submitted my proposal "Lyric Support Improvements" to google
melange for Google Summer of Code 2014. I have appended it below.
Please review and provide any feedback or appreciation ;)

Thanks,
Vedant.


---------------------proposal begins---------------------

Name: Vedant Agarwala

Email Address: vedant.kota at gmail.com

IRC Nick: vedu

IM Service and Username: xmpp-google: vedant.kota at gmail.com

Location (City, Country and/or Time Zone): Kolkata, India GMT+5.30

Proposal Title: Lyric Support Improvements

Motivation for Proposal and Overview: Currently Amarok supports the
fetching and display of lyrics. Fetching is performed by scripts, which by
default is Lyicwiki. Display is in a basic Text Browser widget that scrolls
at a constant rate so that the start and end times of the song playback and
lyrics display coincide. This is rarely the case in actual playback due to
many reasons: different lines of the song are played at different rates, in
between paragraphs there is one (blank) line but many seconds of music,
etc. Also the Text Browser Widget is currently in duress, as the background
is white instead of the consistent blue (Bug
314854[1]<https://bugs.kde.org/show_bug.cgi?id=314854>
).

I want to fix this. I have a two fold goal in my proposal: improve the
chances of lyrics fetching and, highlight the "current playing line"
according to the timestamp in the .lrc file. For smooth highlighting I want
to magnify the current playing line. It will appear as if there is a
rectangular magnifying glass along the width of the widget with the current
playing line at the centre while the lyrics scrolls underneath it (without
the lensing effect that occurs at the edges of a actual magnifying glass).
For better fetching I will add more sources from which lyrics can be
fetched, and make a more useful action for the "reload lyrics" button i.e.
lyrics will be taken from a new source on button click. To improve chances
of lyrics being fetched for songs with poor tags, I will add automatic
adjustment of tags (without changing the saved tracks) for artist and song
name i.e. omitting track numbers, bracket, parenthesis, 'feat', etc.

Implementation Details:

   -

   Changing the AmarokLyricScript API: The current amarok Lyrics scripting
   API has support only for plain text and rich text. I will add a new
   function 'showLyricsLrc( xml )'. It's schema will be very similar to that
   accepted by 'showLyrics( xml )' except that it will contain timestamped
   lines, as the simple format of .lrc files
contain[2]<http://en.wikipedia.org/wiki/LRC_(file_format)>.
   It may also contain ID tags, but (for now) they will be ignored by our xml
   parser. Only the [offset] tag will be of importance; it may be created (or
   edited) after lyrics have been fetched. As before, the lyrics may be
   embedded into the music file itself depending on user preference by the
   script itself via 'Amarok.Engine.currentTrack().changeTags( {
   "lyrics":lyrics }, false )'.
   -

   I will add a new signal 'Amarok.Lyrics.LyricsEdited( String trackUrl ,
   String newLyrics )'. This will signal that the user has edited the lyrics
   manually. Future scripts should connect to this and, if the user has
   allowed, embed the new lyrics. When the user edits the lyrics, the
   LyricsApplet will call a (say)
   ScriptManager::instance()->notifyLyricsEdited( String trackUrl , String
   newLyrics ) which will emit a signal ScriptManager::lyricsEdited(...). This
   is signal will be connected to a 'AmarokLyricsScript::lyricsEdited(...)'
   signal; this will be the signal scripts can (and all lyrics scripts should)
   connect to, to update the embedded lyrics. This should not happen directly
   from the LyricsApplet (by just running a WriteTagsJob) since the user
   preference for embedding lyrics or not is selected in the script settings.
   As a test I will manually check using
easyTag[3]<https://launchpad.net/lrcshow-x>,
   whether lyrics tags have been edited or not.


   -

   A new lyrics script: Lyrics from http://lyrics.wikia.com doesn't suit
   our needs anymore. It fetches lyrics without any timestamps, which is good
   for lyrics.txt but not for lyrics.lrc files. I found an app:
lrcShow-X[4]<https://launchpad.net/lrcshow-x>that downloads .lrc files
from the internet and displays it. It works well
   with Amarok (via the D-bus interface). It is written in PyQt and I can
   easily port (read 'pirate') the code in the 'engine' directory to
   javascript. I just need the code to fetch .lrc data from web services. The
   'ttplayer', which is the default, is a good choice. I can add more and more
   lrc providers as time permits. The providers will be searched in an order
   of preference i.e. if a provider fails to provide lyrics, the next provider
   will be queried. Also, when a user clicks on the "Reload Lyrics" button on
   the applet, a new provider will be queried, just as in the Ultimate Lyrics
   script[5]<http://kde-apps.org/content/show.php/Ultimate+Lyrics?content=108967>;
   code can easily be pirated from it (sources are in javascript itself). When
   this is new script is fully functional, the LyricsDisplay applet should
   display a timpestamped lyrics text if I just set the lyrics by
   'Amarok.Lyrics.showLyrics( xml )'.


   -

   Changing the LyricsBrowser: All the code for display of lyrics is
   contained in the directory 'src/context/applets/lyrics'. The LyricsApplet
   class (a subclass of the Plasma::Applet) has a object of the LyricsBrowser
   class set as the "browser".  This serves our purpose quite well because the
   LyricsBrowser ultimately inherits the QWidget that is a QPaintDevice. This
   means we can use QPainter on it. I will edit the LyricsBrowser.cpp|h files,
   add a public slot that will listen to the current position (in
   milliseconds) of the song playing. As before, the applet will display the
   current playing song's lyrics, and the process of displaying (fetching,
   parsing .lrc file, etc.) will begin as soon as a new track starts playing.
   Some changes will be needed in the properties of LyricsBrowser: it will be
   immune to all direct user interactions i.e. its focusPolicy property will
   be Qt::NoFocus. It will not accept any user clicks. The default alignment
   of the text will be center for scrolling lyrics. Any editing to this will
   have to be made from the settings box, like changing the font of the
   lyrics. The nativeWidget() (i.e. a KTextBrowser) will, however, accept
   wheelEvents. By reimplementing its wheelEvent() function, I will invoke
   KActions, 'incrementOffset' and 'decreaseOffset'. They are discussed
   further in the next point. The LyricsBrowser height will be fixed and width
   will be according to QSizePolicy::MinimumExpanding; similar to the "Current
   track" applet.
   -

   In the LyricsBrowser, I will create a rectangular "lens"; it will
   progressively zoom in on the line in focus. Lines above and below it will
   gradually return to their original size. Number of lines will depend on the
   font size. Say, the entire lens will affect five lines at any given time:
   the middle one is zoomed in to 1.5x at its centre while the start of the
   first line and end of last line at 1.0x, progression from 1.0x to 1.5x and
   back to 1.0x will be linear. The exact figures of number of lines, maximum
   zoom level and steepness of the gradient of the power of the lens are GUI
   components and can be decided accurately during actual implementation with
   some trial and error testing. The magnifying glass itself will be invisible
   or have a light translucent color in the middle that highlights the current
   playing line even more. If time permits, I can test certain colors with
   different Composition Modes of the QPainter (like
   QPainter::CompositionMode_Lighten) and share snapshots with the Amarok
   community to collaboratively decide which look would be the best. The code
   to do this is inspired from the "Vector Deformation"
demo[6]<http://qt-project.org/doc/qt-5/qtwidgets-painting-deform-example.html>:
    I will add a function generateLensPixmap() in the LyricsBrowser class
   to paint the lens (unless its invisible). I will initialize a QPainter as
   "QPainter painter(this)" and draw the lens as "painter.drawRect()".
   'generateLensPixmap()' will be called during object construction and
   everytime the font size is changed. Just the way in the Vector Deformation
   example, the lensDeform() function creates a spherical deformation, I will
   also create a lensDeform() that will perform a rectangular deformation. As
   the widget will should be updated continually as the song progresses, I
   will connect the 'The::engineController()->trackPositionChanged()' signal
   to an appropriate (private) slot in LyricsBrowser class, say
   'trackProgressed()'. This slot will call lensDeform() in a nested loop (for
   every point in the area of the widget) and then call update(). The current
   behaviour of showing a busy sign in the widget while a lyrics is being
   fetched, is just fine. If by chance the saved lyrics is a .txt file or a
   .lrc file with no timestamps, the lyrics will be displayed as they are now.
   As you will see in the next point, we will require total of three states of
   LyricsBrowser: scrolling lyrics, stagnant lyrics, and lyrics edit.


   -

   Change the LyricsApplet: The container for LyricsBrowser i.e.
   LyricsApplet, will also have to be updated. Clicking the "Edit lyrics"
   button will take the LyricsBrowser to the 'lyics edit' state. The widget
   will expand (in length) to take up all the available space and become just
   the old (i.e. current) LyricsBrowser in 'edit' state: the lyrics will
   become editable, the save and close buttons will become visible (rather
   than light up, as is the case currently). Pressing 'close' will make the
   widget shrink back to the old state. The 'old' state could be either of
   scolling or stagnant lyrics. In the editable LyricsBrowser, the raw .lrc
   file (with timestamps) will be shown and saved. If a user replaces the
   timestamps (or creates an erroneous .lrc file), the LyricsBrowser will
   default to 'stagnant lyrics' state, and the timestamps, if any, will be
   stripped out from the display. The 'scroll automatically' button will now
   manage the 'scrolling lyrics' and 'stagnant lyrics' states. By toggling
   'scroll automatically' a user can select to see the lyrics at his own pace
   i.e. the current behaviour with scrolling off. This won't cause any
   expansion or shrinking of the height between the edit and display states.
   But in case of my new lyrics display widget will change size. If time
   permits, I can make the expansion and shrinking animated. The exact code to
   change the states will be contained in three public slots in the
   LyricsBrowser class: toScollingState(), toStagnantState(), toEditState().
   The different properties will be defined (as it is now) in the
   LyricsBrowser class. The code to animate the transition, either added by me
   or by a future developer, can easily reside in these functions.
   -

   I will add two more buttons, just after the 'edit lyrics' button:
   'increase offset' and 'decrease offset'. They will be KIcon(
"arrow-down" ) and
   KIcon( "arrow-up" ) respectively. They will act on the KActions:
   increaseOffset and decreaseOffset. The buttons will be visible only in the
   'scrolling lyrics' state. The KActions will increase and decrease the
   offset of the current song lyrics by 100 milliseconds for each click. They
   will have 'shortcut' and 'shortcutConfigurable' properties set. Offset is
   the value by which each timestamp in the .lrc file is adjusted when the
   lyrics is being displayed. When a user changes this offset, the QString
   'lyrics' will be edited. If the offset tag (in 'lyrics') doesn't exist, it
   will be created. Then, 'LyricsManager::setLyricsForTrack' will be called
   with the edited lyrics. Unlike now,  file itself may be edited (depending
   on user preference in the lyrics script) to update the new lyrics as a tag.
   This is will be done by signalling the
   'AmarokLyricsScript::lyricsEdited(...)' function. Rest will be handled by
   the lyrics script.


   -

   Animated Lyrics Scrolling: After some research, I have decided I will
   implement scrolling just by listening to
   'The::engineController()->trackPositionChanged()' signal. There is no need
   to set up any QPropertyAnimation or something more complex using QTimer.
   The 'trackPositionChanged()' signal sends a signal every couple of
   milliseconds as track progresses. I will set up key-value pairs according
   the timestamps given in the .lrc file and positions of the lines in our
   'browser' object. I will store the QScrollBar returned by calling
   "browser->verticalScrollBar()" to 'browserScrollBar'. For every
   'trackChanged()' signal, I will check whether a timestamped lyrics file
   exists or not for the current track and also the next queued track. If it
   doesn't or even if the lyrics contains no timestamps I will emit the
   'signal Amarok.Lyrics.fetchLyrics()'. This is to try to have the lyrics
   ready as the song starts. A new KJob will parse the .lrc file into a
   QVector object (say 'm_keyValues') of 'KeyValue' (typdef for
   QPair<qreal,QVariant>) and different lines of lyrics that will go into the
   'browser' object. Lets call this QVector 'timestamps'. 'keys' will be the
   qreal quotient obtained by dividing the timestamp (in milliseconds) by
   total length of the song (also in milliseconds). 'values' will be integers
   representing the line number of the lyrics text. The integers will be the
   line numbers multiplied by 'load', where 'load' is the the length of song
   in milliseconds divided by 20. Hence, 1 integer value represents 20ms of
   the song. Updating the GUI every 20ms is ideal for smooth animation.
   However, in all probability, the GUI will be updated at a longer time than
   that, according to the 'trackProgressed()' signal.Some 'padding' (i.e.
   blank) lines will be needed before the [00:00:00] timestamp line and after
   the last timestamped line in the .lrc file, so that the first and last
   lyric lines will appear at the centre rather than at the beginning or end
   of the widget. This is desirable since the centre line is only highlighted
   while the ends are not. The value of 'padding' will be calculated according
   to the size of the font selected by the user. It will be be the number of
   lines before (also, after) the centre line. The 'values' will be offset by
   INT_MIN. This is so that we can accommodate the maximum number of lines
   that 'int' can allow. Even that results to a maximum of ~21 minutes per
   song. (This would be a known issue that can be solved later by breaking the
   song into parts and loading those parts or decreasing the refresh rate to
   50ms or even more.) We have to restrict ourselves to int (rather than
   quint64 or similar) since the 'minimum' and 'maximum' properties of
   QScrollBar are of 'int' type.
   -

   When this KJob is 'finished()', this QVector our song will be fully
   ready to be played with synchronized lyrics. All the operations (play,
   pause, seek) that a user (or other modules of Amarok or even KDE for that
   matter) will perform on the song playback can be easily reflected in the
   scrolling of the lyrics just by adding code to the
   LyricsBrowser::trackProgressed() slot. In this function, before
   QWidget::update() is called, 'QAbstractSlider::setValue(int lineNo)' will
   be emitted. 'lineNo' will be the value returned from an 'int
   interpolate(qint64 position)' function; it calculates the line number the
   lyrics should scroll to, based on current track 'position' and m_keyValues;
   calculation will be done according to linear interpolation of the closest
   earlier and later timestamps in m_keyValues. It will also account for the
   'offset' value saved by the user when determining the line number to scroll
   to.

Tentative Timeline:

May-

<--- GSoC commences--->

week 4: Change the AmarokLyricsScript API  and download script.

June-

week 1: Add the ttplayer provider to the script. Test (probably by easyTag).

week 2: Add feature to more providers to the above. If time permits add 1
more provider (First test, then go on to adding the next provider).

week 3: Change the LyricsBrowser.

<--- Mid term --->

week 4: Test whether the text is being deformed

July-

week 1: If it works, add more provider to the script; else fix it and
re-test.

week 2: Change the LyricsApplet. Test.

week 3: Fix any issues and begin implementing the animated Lyrics scrolling.

week 4: Finish implementing the animated Lyrics scrolling.

August-

week 1: Make sure all components work together properly.

<--- suggested "pens down" --->

week 2: Code cleaning. Documentation.

<--- firm "pens down" --->

week 3: Prepare and submit reports

Other Obligations: I have no other obligations. I can easily spent about 50
hours a week (7 to 8 hrs a day in slots of 2 to 3 hrs, one each in the
afternoon, evening and at night) coding; since summer vacations will be
going on till mid July. Even after college starts, very few classes are
held in the beginning of the semester and the college teachers make
exceptions for GSoC students. So, I can continue to code 50 hours a week
(with a similar schedule).

About Me: I am currently in my second undergraduate year in National
Institute of Technology, Durgapur, India, studying Computer Science and
Engineering. I have experience coding experience with C/C++, Java
(including Android and making GUI using Java swing), SQL and web services.
I have submitted patches to KDE (bug-fixes for
Rekonq[7]<https://git.reviewboard.kde.org/r/107662/>,
Amarok[8] <https://git.reviewboard.kde.org/r/110082/>[9]<https://git.reviewboard.kde.org/r/109295/>
[10] <https://git.reviewboard.kde.org/r/111038/>[11]<https://git.reviewboard.kde.org/r/109283/>,
Akonadi[12] <https://git.reviewboard.kde.org/r/110213/>) and Mixxx DJ
software[13] <https://bugs.launchpad.net/~vedu>. I took part in and
'passed' Season of KDE 2013.

I love coding for open source.

After GSoC I plan to become a core developer for Amarok.

External Links:

[1] https://bugs.kde.org/show_bug.cgi?id=314854

[2] http://en.wikipedia.org/wiki/LRC_(file_format)<http://qt-project.org/doc/qt-5/qtwidgets-painting-deform-example.html>

[3] https://apps.ubuntu.com/cat/applications/saucy/easytag/

[4] https://launchpad.net/lrcshow-x

[5] http://kde-apps.org/content/show.php/Ultimate+Lyrics?content=108967

[6] http://qt-project.org/doc/qt-5/qtwidgets-painting-deform-example.html

[7] https://git.reviewboard.kde.org/r/107662/

[8] https://git.reviewboard.kde.org/r/110082/

[9] https://git.reviewboard.kde.org/r/109295/

[10] https://git.reviewboard.kde.org/r/111038/

[11] https://git.reviewboard.kde.org/r/109283/

[12] https://git.reviewboard.kde.org/r/110213/

[13] https://bugs.launchpad.net/~vedu
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.kde.org/pipermail/amarok-devel/attachments/20140311/e2ba4e2c/attachment-0001.html>


More information about the Amarok-devel mailing list