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