GSoC *draft* Proposal for "Lyric Support Improvements"
vedant agarwala
vedant.kota at gmail.com
Thu Mar 6 13:51:27 UTC 2014
Hello everyone,
I am applying to GSoC 2014 for the idea "Lyric Support Improvements". I am
appending my draft proposal. The proposal isn't complete; only the
scrolling lyrics part is written while the lyrics fetching is left.
Please review and tell me where something is ambiguous or incorrect.
I hope you like it, I have done a lot of research this time.
Sincerely,
Vedant.
-------------Proposal below-------------------
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 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,
pasring .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, or scrolls. Any editing
to this will have to be made from the settings box, like changing the font
of the lyrics. Its 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[2]<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 be visible only in the
'scrolling lyrics' state. They will increase and decrease the offset of the
current song lyrics by 500 milliseconds for each click. 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 .lrc file itself will
be edited to persist these changes. If the offset value doesn't exist in
the .lrc file, it will be created.
-
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'. 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. 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.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.kde.org/pipermail/amarok-devel/attachments/20140306/11180abc/attachment-0001.html>
More information about the Amarok-devel
mailing list