Sonntag, 11. Dezember 2011

Replay Gain on Sansa Clip+ with mp3gain

If you play MP3 songs from different sources in shuffle mode on a MP3 player, their difference in loudness become apparent and you are constantly keep adjusting the volume.

In order to prevent that, Replay Gain can be used. With this function the MP3 player uses loudness information in the header of a MP3 file to adjust the volume automatically.

However, the loudness information is not present by default. You need software to analyse the content and store those values in the MP3 header. Replay Gain does not change the underlying (music) data, thus avoiding loss in quality by decoding, processing and re-encoding.

A popular utility in the Linux world to calculate these values and tagging the file is mp3gain.

I used it regularly, but I was still adjusting the volume on my Sansa Clip+ ...

What I didn't know was, that by default mp3gain stores the information in the APEv2 tag which the Sansa Clip does not read. In order to be used by the Sansa Clip's Replay Gain function, it must be stored in the ID3v2 tag.

mp3gain has an option to do exactly that: -s i

I'm now using the following command to analyse and tag my MP3 files:

mp3gain -p -s i *

Where the option -p also preserves the original file date.














Donnerstag, 3. März 2011

Lift and AutoComplete

The Lift-Web-Framework allows you easy access to JQuery's AutoComplete widget. If you have used Google, you might agree that the drop down list which is populated with entries depending on your current input is a nice thing to have.

Here is how you integrate it into your Lift application.

First you have to add a dependency to your project (in this case Lift 2.2, using Scala 2.8.0):

If you use SBT in ./project/build/Project.scala

override def libraryDependencies = Set(
...
"net.liftweb" % "lift-widgets_2.8.0" % “2.2” % "compile->default",

In a Maven project this should translate to (in pom.xls - not tested, I'm using SBT):



<dependency>
<groupid>net.liftweb</groupid>
<artifactid>lift-widgets</artifactid>
<version>2.2-scala280</version>
</dependency>


The first step in your application is the initialisation, by adding the following code – usually into into Boot.scala

import _root_.net.liftweb.widgets.autocomplete._

class Boot {
def boot {

AutoComplete.init

In your template, the widget is represented like any other input field with a tag of your choice. This example uses

Here the snippet code to bind it:

Helpers.bind("dt", in,
...
"autocomplete" -> AutoComplete(startValue,
buildQuery _,
value => processResult(value)
),
...

where:
startValue is a string for the initial value of the AutoComplete text field.
buildQuery is a function to populate the drop down list.
processResult is a function called when the form is submitted, containing the value of the widget (or not, see bug report below).

Here is a simple example of processResult

def processResult(s : String) = {
println("%s".format(s))
}

The interesting part is the buildQuery function. It has the following signature:

def buildQuery(current: String, limit: Int): Seq[String] = {
...
}

where
current is the string typed into the autoComplete text field.
limit is an integer to limit the number of Strings returned by the function

A valid but not very useful implementation would be (from http://demo.liftweb.net/ajax)

def buildQuery(current: String, limit: Int): Seq[String] = {
(1 to limit).map(n => current+""+n)
}

Most application however will query a database. Lets assume that Mydatabase has a field called name (e.g. with the names of employees). A query might look like this:

def buildQuery(current: String, limit: Int): Seq[String] = {
Mydatabase.findAll(
Like(Mydatabase.name,(current + "%")),
OrderBy(Mydatabase.name,Ascending),
MaxRows(limit)
).map( _.name.is)
}

This will findAll names in Mydatabase which start with the currently entered string, sorts them, and limit the number of results.

That's all you need.

Bug or a feature?

The current implementation of the AutoComplete functionality (Lift 2.2) only allows the selection from a drop down list as a valid input. This sounds reasonable, but it also prevents you from clearing the input after you have made a selection. This can lead to the following situation:

At a shopping site, the user selects an article via an autocomplete field. Then he changes his mind and clears the input field using the delete key. When he clicks on the submit button the previous selected article is sent to the server, even if the screen shows an empty text field.

With the help of Sergey Andreev, I have developed a small patch which has also been published in Lift ticket 892. Even though it is only 8 lines long, it is unlikely to make it into Lift due to Intellectual Property policy consideration by DPP.

The patched version is available on GitHub.

If you copy it into your project, you might want to change the package name (first and last three lines) and replace

import _root_.net.liftweb.widgets.autocomplete._

with your package name.

Keep in mind that the returned result may contain strings (including an empty string) that might be invalid.