Search Here

Saturday, July 25, 2015

Modern Charting tools for Desktop Applications

Hello Readers,

Time fly so fast. Its became months since I update my blog. Busy timings @work and the real reason is that I became so lazy to write. But that did not stop me exploring things. Here is my thought about the computer "charts" for dekstop applications.

Charts - A graphical representaion of the data (numbers mostly). We all love to see charts because human mind can easily grasp the picture than words or numbers. The right chart that displays the data will greatly assist human brain and help us to think more to make sense out of it.

Think of an example of weather forecast. If we have years and years of the data which is plotted nicely, we could predict the future, It might save human life !!

Example: weather data chart. Thanks to http://www.tgomagazine.co.uk
On the other side, Poor selection of charts / less flexibility of viewing data in various timelines (Ex: zoom into timelines) will not do any better job than just showing the huge data.

Its a developer / UI designers reposibility to choose right chart with maximum flexibility.

Charting Libraries:  Fortunatly for a desktop / web applicaion developers, There is no need to write a graphics code for plotting charts. There are tons of charting tools avilable both for desktop applications / web applications.

I used JFreeChart, GD::Graph, SWF::Chart etc ... (I don't remember which charting lib I have used in VB6).

All of these charting libaries are awesome and served their purpose at that point of view but we need to evolve.

I find few drawbacks in the traditional charting libraries,

  1. Developer need to write a code (Though minimal) to create, customize (colors, look and feel etc...) and plot the graph. This means that developer need to touch the code to update the Chart style.

  2. Usually traditional charting libraries wont provide any IDE / Char designer to visualize how your real data look in the particular chart. You really need to write code first (or see demo applications and visualize your data in your mind)

  3.  Traditional charting libraries render the data into some kind of image format internally (Ex: PNG, GIF, SVG etc..) and shows that image in the image viewer widget. This is not an efficient process if your data changes so quickly or User wants to zoom into the timeliness or for interactive charts. Keep rendering new image is a heay process for the GPUs (Though the modern GPUs are fast enough).

  4. Suddenly if you want to provide you application as a web application. At least there is an extra work to replace traditional charts to web ready.
How do we deal with this problem?

I always envy the JavaScript charting libraries.

  • Java script charting libaries are awesome looking

  • They are so interactive

  • They are not crated as a image and displayed, instead, The data is fed into Java script low level graphics functions and its drawn in browser canvas widget directly. So the zoom in, Zoom out are so fast

  • Java Script runs in the browser platform, Browsers are optimized to run the Java Script and Browsers are also optimized to use the GPUs

  • Most of the JavaScript libs provide an online WYSWYG editor to quickly visualize the real data with real charts.

  • Modern browser's memory foot print is optimized.

  • Most of the GUI frameworks support embedding a Browser inside the GUI applications. 

Did you get the answer?. Yes the idea is to embedded the Browser widget into desktop GUI application and leave the Charting work to Javascript. !!

I hear that. You need to see the code believe it. Here is my GTK2 based dektop application to demonstrate Java Script charting technique.


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
#!/usr/bin/perl
use strict;
use warnings;
use Gtk2 -init;
use Gtk2::WebKit;
use Glib qw(TRUE FALSE);
use Math::Random::MT::Auto qw(binomial);
use Data::Dumper;
use FindBin qw($RealBin);

#Globals
$| = 1;
my $webHTML = "";

#Create Binomial distribution gui
my $win = Gtk2::Window->new;
$win->set_default_size( 800, 600 );
$win->signal_connect( destroy => sub { Gtk2->main_quit } );
my $vbox = Gtk2::VBox->new( FALSE, 2 );
$vbox->set_border_width(2);
$win->add($vbox);
my $toolbar = Gtk2::Toolbar->new;
$toolbar->set_icon_size('large-toolbar');
$toolbar->set_show_arrow(FALSE);
$toolbar->set_orientation('GTK_ORIENTATION_HORIZONTAL');
my $button = Gtk2::ToolButton->new_from_stock('gtk-refresh');
$button->signal_connect( "clicked" => \&refreshBinomialDataandLoad, undef );
$toolbar->insert( $button,-1 );
$toolbar->insert( Gtk2::SeparatorToolItem->new, -1 );
$vbox->pack_start( $toolbar, FALSE, FALSE, 0 );

#Create Gtk WebKit browser widget and add to GUI
my $sw = Gtk2::ScrolledWindow->new();
$sw->set_policy( 'automatic', 'automatic' );
$sw->set_size_request( 600, 600 );    # hack to set initial empty browser size


my $webkitView = Gtk2::WebKit::WebView->new();
print "\n Gtk2 WebKit Version: ", Gtk2::WebKit->major_version(), ".",  Gtk2::WebKit->minor_version(), ".", Gtk2::WebKit->micro_version();
$webkitView->get_settings()->set_property( 'enable-file-access-from-file-uris', TRUE );
$sw->add($webkitView);
$vbox->pack_start( $sw, 1, 1, 0 );
$win->show_all;
Gtk2->main;

#Start GTK Main loop. Nothing after this
#Subs
sub genBinomialChartData
{
 my $probabilityP = 0.5;
 my $trialsN      = 100;
 my $count        = 10000;
 my %binomial;

 # Get random numbers and put them in bins
 $webHTML = ' Generating ' . $count . ' Random Numbers. Please wait...';
 loadAndShowHTML();
 for ( 1 .. $count )
 {
  my $exprimentResult = binomial( $probabilityP, $trialsN );
  $binomial{$exprimentResult}++
    ;    #Store the expriment results as a keys in hash
 }

 #Load chart html
 $webHTML = startHTMLChart( $probabilityP, $trialsN, $count );
 foreach my $exprimentResult ( sort keys(%binomial) )
 {
  my $exprimentResultProbability =
    ( $binomial{$exprimentResult} / $count );
  print "\n Probability for $exprimentResult = ",
    $exprimentResultProbability, " (", $binomial{$exprimentResult}, '/',
    $count, ')';

  #Add chart data
  $webHTML .=
    insertHTMLChartData( $exprimentResult, $exprimentResultProbability );
 }
 print "\n";

 #Add chart footer
 $webHTML .= finishHTMLChart();
 print "\n HTML \n\n", $webHTML, "\n\nHTML";
}

sub insertHTMLChartData
{
 my $value       = shift @_;
 my $probability = shift @_;
 return '{
                            "Value": "' . $value . '",
                            "Probability": "' . $probability . '"
                        },';
}

sub finishHTMLChart
{
 return ']
                }
            );
        </script>
    </head>
    <body>
        <div id="chartdiv" style="width: 100%; height: 400px; background-color: #FFFFFF;" ></div>
    </body>
</html>';
}

sub startHTMLChart
{
 my $p = shift @_;
 my $t = shift @_;
 my $c = shift @_;
 return '<!DOCTYPE html>
<html>
    <head>
        <title>chart created with amCharts | amCharts</title>
        <meta name="description" content="chart created using amCharts live editor" />

        <!-- amCharts javascript sources -->
        <script src="file://' . $RealBin
   . '/amcharts/amcharts.js" type="text/javascript"></script>
        <script src="file://' . $RealBin
   . '/amcharts/serial.js" type="text/javascript"></script>

        <!-- amCharts javascript code -->
        <script type="text/javascript">
            AmCharts.makeChart("chartdiv",
                {
                    "type": "serial",
                    "path": "https://www.amcharts.com/lib/3/",
                    "categoryField": "Value",
                    "mouseWheelZoomEnabled": true,
                    "startDuration": 1,
                    "handDrawn": true,
                    "categoryAxis": {
                        "gridPosition": "start"
                    },
                    "trendLines": [],
                    "graphs": [
                        {
                            "balloonText": " Value=[[category]],  Probability=[[value]]",
                            "bullet": "round",
                            "bulletBorderThickness": 5,
                            "bulletSize": 10,
                            "gapPeriod": 0,
                            "id": "AmGraph-1",
                            "labelOffset": 2,
                            "title": "Observed",
                            "type": "smoothedLine",
                            "valueField": "Probability"
                        }
                    ],
                    "guides": [],
                    "valueAxes": [
                        {
                            "id": "ValueAxis-1",
                            "title": "Probability"
                        }
                    ],
                    "allLabels": [],
                    "balloon": {},
                    "legend": {
                        "labelText": "Observed ",
                        "useGraphSettings": true,
                        "valueWidth": 20
                    },
                                    "titles": [
                        {
                            "id": "Title-1",
                            "size": 15,
                            "text": "Binomial Probability Distribution \n              (p='
   . $p . ', n='
   . $t
   . ', count='
   . $c . ')"
                        }
                    ],
                    "dataProvider": [
                    ';
}

sub loadAndShowHTML
{
 $webkitView->load_string( $webHTML, 'text/html', 'UTF-8', 'file://' );
}

sub refreshBinomialDataandLoad
{
 my $htmlString;
 genBinomialChartData();

 #Load HTML Now
 loadAndShowHTML();
 return TRUE;
}


Note: This code is not optimized. I create a full html string every time and then load into Gtk2::WebKit browser widget. The more elegant way would be writing the changed data into a PIPE / Other IPC mechanism, From Javascript read from the PIPE. Hey, didn't I tell you already, Its only for demo .... Having said that, This code runs very very fast. I see the charts are alive within no time.

What am I doing?

1. I created a binomial distribution data for the purpose of charting. Mostly inspired from http://maths.uncommons.org/

2.  I used perl language and GTK2 GUI framework. I use Perl + Gtk2 a lot @work for creating cross platform GUI applications. I really like the Gtk's Glade GUI designer. It gratly simplifies the effort of GUI design.

3. I choose WebKit as my browser of choice. Webkit is running in iPhone!!. I used Gtk2::WebKit perl binding of the WebKit

4. I AMCharts JavaScript awesome library. They do provide online chart creator tool. My most of the HTML and Java Script code is just copy paste from their online chart designer's output.

Note: AMCharts allows to use their charting library for free for personal usage and you need to use their javascript libs from their site. For commercial usage there is a license option. This blog post's idea is to illustae the concept, You can choose other free javascript charting library.

5. In the code, I load all the widgets, Bring up the GUI and calculate the Binomial probability distribution.

6. Once the data is avilable, I feed the data to the AMCharts via Java Script, JSON kind of data format.

7. Finally the whole HTML string (including Java script code) goes to browser widget, It renders the chart in micro seconds.

8. Any zoom in / Zoom out or Interactive popups are taken care by browser widget and my main application loop is free to do other jobs.

Here are the screenshots of the above code,

Sample Binomial Distribution

Interactive Chart, Shows the point details when cursor move on data point

Zoom in. So fast zoom in and Zoom out
Conclusion

In the early days of computer science, The thought process was to keep the whole system to limit to one programming language and limited libraries and toolkits.

In the modern object oriented thinking, Use the best toolkit or language which serves the purpose.

Javascripts and browsers are highly optimised for the chart works and very simple to use.

I think, Modern computer chartings to be best done by the Javascript + Browser.

P.S: Thanks to Shakthi, for patiently explaining the probability distribution mathematical concepts to me. 

Saturday, July 18, 2015

Income Tax India - EFile IT Returns for Salaried Employees


Income tax returns filling is an important activity for Income Tax payers in India. The Indian government is trying to simplify the process but still it might be confusion to people. So I have created this blog post to help with Income Tax Self E-Filing.'

Note: The steps mentioned in this blog post are applicable only for "Salaried Employees".

Note: From the Assessment year 2015-2016, you can E-Verify your income tax return. So don’t need to send the printout copy to Income Tax India, CPC office in Bangalore via Postal. One more step to simplify the process :). Keep your AADHHAR number ready.

Last date to file your Income Tax Returns for 2016 - 2017 is July 31 2017 (Usually gets extended to August 31) but do not wait for the last date, Income tax India website might be loaded with high usage.

Let’s Start...

Preparation

1.   Use IE / Chrome browser

2.      If you have AADHAR Card, keep it ready

3.      Set aside 1 – 1.30 hr. without any disturbance

4.     Get Form 16 from your Employer’s web site,

      2014 onwards Form 16 has two parts.
                                                    i.     PART A => Employer TDS details
                                                   ii.     PART B => Usual Form 16.
5.      Form 16 is a digitally signed PDF, In your PDF viewer, Make sure you trust the Signer’s digital signature and get green tick mark.
a.      Open the PDF in Adobe Reader
b.      If you see Green Tick in all signatures, You are done. Else follow instructions below,
c.      Open the “Signature Panel” (right top corner in the adobe reader)
d.      Show Signer’s certificate -> Trust ->Add to trusted certificates-> tick all the options
e.      In Signature properties dialog box -> “Validate Signature” -> Close
f.       You should see your HR’s digital signature is validated and Green Tick mark
g.      DO NOT proceed without validating Signature, It could be a wrong Form 16.

6.      You need a reliable internet connection during E-File process because it’s an online procedure

7.      If you do not understand any field in Form 16, ask me / google

8.      If you do not have account in https://incometaxindiaefiling.gov.in , Sign up and create an account.

9. Optional: Download Form 26AS from https://incometaxindiaefiling.gov.in -> Login -> My Account -> View Form26 AS. Form 26AS will help you know who are the entities TDS for you and submitted to IT department


E-File Income Tax in India Procedure


a.      Note: At the time of the login, income tax India web site asks for AADHAR number registration,  If you have AADHAR Number, Register it with income tax India, So you can “e-verify” the return. If you don’t have AADHAR, Press “LATER” button to continue.

2.       After Login, Top menu, click "e-File" -> "Income Tax Return"

3.       On the "Income Tax Return" page

               a. Check your PAN number
              
               b. ITR Form Name = ITR1  (for Salaried)

               c. Assessment Year = 2020-21 (Or what is mentioned in the Form  16 under "Assessment Year" column)

               d. Prefill Address => From Previous Return Filed (or appropriate)

               f. Filling Type -> Original /revised Return

               g. Submission Mode -> Prepare and Submit Online

4.      You will see instructions page, Press right Green arrow…

5.      Personal Information page

               a. Check Name , PAN, Address, Mobile No,  and other details

               b. “Employer Category” => Others

               c. "Return Filed .."  => 11 - On or Before...

               d. "Residential Status" => RES-Resident

               e. "Whether Original or Revised Return?" => Original

               f. “Are you governed by Portuguese Civil...” =>  "NO"

               g. Add your AADHAR details

               h. Nature of Employment -> other

               i.  Field "A20"  -> Filed u/s -> 139(1)-On or before due date

               j. Are you filing return of income under Seventh proviso to section 139(1) but otherwise not required to furnish return of income? -> NO
           
               k. Press “Save Draft” and Press Green right arrow

6.       Income Details page

               a. Income from Salary (B1) => Enter Salary amount after House Rent, Medical etc...
                              fill the details by looking in Form 16 Part B

               b. Enter Housing property details (If you have get rent / If you pay house loan)

                        Type Of house Property - "Self Occupied"
Income from One House Property -  Usually negative number from 
                            "Form 16"
7.Add: Any other income reported by the employee

Loss From House Property -<xxxx>

               c. Make sure B4 is correct, Add all the Form 16 “9.Deductions under Chapter VIA” details in B4.

                     Note: If you have multiple 80C categories in the Form 16, Add all the amount and Enter the total.

               e. Make sure C2 (Taxable Total Income) is matching with Form 16's Total income (11)

7.       Check 'Part D - Tax Payable on Total Income(C2) ' data is correct with Form 16 data from field "12.Tax on total income" on wards

8.       Don't worry about the D9, D10 and D11 , Its not for Salaried folks. It will not be taken for calculations.

9.      Check D12. “Total Tax and Interest”, It should match with Form 16’s “17.Tax Payable(15-16)”

a.      Don’t worry is your employer paid few rupees extra tax, It might have happen during rounding process at the time of tax payment (4 times in a year, So high chance that your employer paid 4Rs extra tax!!).

10.   Save Draft -> Click “Right Green Arrow”

11.    Tax Details Page

a.      Enter / Check the details as per the Form 16 PART A (Totals)
b.      Add / Delete additional tax deducted by Banks / Any other source of income other than your employer

               b. Press “Save Draft” -> Green right button

12.   Tax Paid & Verification details page

               a. Check D11 match with Form 16's “17.Tax Payable(15-16)”. If excess tax paid  , "D13. Refund" field will show the excess amount

               b. Enter Bank account details - Its mandatory

                       Note: Add one bank account details, preferably salary bank account details in “a) Bank Account in which …”
                             
Total Number of saving and current bank account … = 1 (or more if you want to disclose other bank accounts that you may have)

               c. Capacity as "Self", Enter Place

             d. I would like to e-Verify... (First option , if you are going to use AADHAR based verification. This is simple and recommended)

               d. Press Green right button

Note: In this page, you can also check for any tax refund details.


13.    Enter details of 80G, if you have any donation

14.   Save the Draft

15.   Check every page and details again

16.    Press "Submit"

17.    Press "OK”, “E-Verify Returns Page appears”

18.   If you have AADHAR card with Mobile Account linked Select “Option 3”

19.   OTP will be send to your AADHAR registered Mobile Number, Enter the OTP PIN number and Submit

a.      Note: The OTP is valid only for 10 Mins

20.   Your “IT-Returns” is successfully E verified. You can download the Acknowledgement now.
  
  a. Click top menu "Dashboard"
b. Click "View Returns / Forms"
c. Click last filed year "Ack.No" , Status shoule be "Successfully e-Verified"
d. Click "ITR-V / Acknowledgement"


In the Top Right side Find "Logout" (Yellow Color) and click to quit

21.   Open the Acknowledgement PDF and check the details (Store the PDF in local machine for future reference)

a.      Note: Password is (all small letters)< YOUR_PAN_NUMBER> <Your_Date_Of_Birth_In_the_format_of_DDMMYYYY>

22.   You are DONE. Your ITR is E Verified, So No need to take printout and send for CPC. Wait for mail from IT department with processed ITR details attached as a PDF. Usually, you will be receiving SMS from Indian IT department after they have processed the ITR. You will also receive an Email.


If you don’t have AADHAR Number

1.      Select appropriate option in Step #18 above

2.      If you need to send ITR Acknowledgment to CPC, Here is the tips

a.      Take print out in an A4 Sheet Laser printer

b.      Check the details and Sign It

                              Leave the “in my capacity as ______________" field blank in the ITR-V declaration.

c.      Send to following address within 120 days via Indian government postal service only,

               Income Tax Department - CPC,
              Post Box No - 1, Electronic City Post Office,
              Bangalore - 560100, Karnataka

Done :)

Sunday, March 22, 2015

Comparing Audio files with Sense

Hello Readers,

This week, I did an interesting (at least to me) experiment and study about how to compare the Audio files (Audio data).

Background

@Rajesh  is my good friend at @Work and my morning walk buddy. He is a hard core Electronics guy , He gives lots of gyan about the Radio waves, AM signals etc etc .. Mostly I wont get it but he is patient enough to explain in plain words which can be understood by a computer science engineer (its me :)).

@work, we are working on Digital Radios, To simplify the scenario, imagine,

1. Radio station pick up the song - Which may be raw PCM data / Compressed as high quality MP3

2. Radio station transmits the song over the Air

3. Your home radio box receives it and reproduce the same Song - By now, it wont be in original file format

4. You will hear the song with acceptable quality

5. Great

@Work in the labs we simulate this situation with various equipments and test the song received is in acceptable quality.

@Rajesh, explain me the pain of this testing. Everytime  we need to literally sit and listen the song to know that radio receiver is working fine and reproducing the song with good quality.

The main issue is that Songs / Real life record content is totally different than the audio test vectors (Ex: Sine wave tone). Some of the problems will only occurs to the Songs but in the same setup Sine wave tone works fine.

Obviously, @Rajesh told more about electronics and behaviour of the Radio Signals. I wont explain here (until I get to know it properly :) ).

We were talking about automation of audio listening tests. Due to the nature of the audio data, we can not compare them as a file comparison. Moreover in our situation, Source and Destination audio format is changing and Audio quality also changing. Opps lots of variables.

@Rajesh asked me to find some way to test it.
 
I was approching this problem from the computer science point of view.

My first thought was, we need to have some model which is similar to human ears. Irrespective of audio file format, quality changes, Humans can find out the similar songs.

Obviously, Google is the best friend while learning. I knew that, there are lots of music recognition applications available in mobile world. I have used Shazam, It could identify the songs properly.

I never gave a good thought about how Shazam kind of applications are working but now its needed for me.

I learnt that Shazam kind of music recognition apps are working using Acoustic fingerprint algorithms.

There are many Acoustic fingerprint algorithms are available. I chose the open source chromaprint algorithm developed by Lukáš Lalinský.

chromaprint converts the song / music as a musical notes and creates the fingerprints for various parts of the song. So the file format change and quality change is least affects this algorithm. Its a brilliant idea.

Read How chromaprint works, if you really interested in the algorithm.

chromaprint provides the pre built tool called fpcalc. fpcalc used to calculate the acoustic fingerprint. To install the fpcalc in Fedora linux,


1
2
3
4
5
6
[root@bakkisweety ~]# yum install libchromaprint-devel chromaprint-tools libchromaprint
Loaded plugins: langpacks, show-leaves, upgrade-helper
Package libchromaprint-devel-1.1-3.fc21.x86_64 already installed and latest version
Package chromaprint-tools-1.0-6.fc21.x86_64 already installed and latest version
Package libchromaprint-1.1-3.fc21.x86_64 already installed and latest version
Nothing to do


1
2
3
[root@bakkisweety Audacity]# fpcalc -version
fpcalc: /usr/lib64/nvidia-304xx/libOpenCL.so.1: no version information available (required by /lib64/libavutil.so.54)
fpcalc version 1.1.0

Note: Ignore the warning message from fpcalc

Now, I have a tool to find the acoustic fingerprints. I also need some test data to proceed. I have downloaded a public available song from youtube as MP3 and also recorded my own voice using Audacity.

If you like to work on MP3 files directly in Audacity, You need to install,


1
2
3
4
[root@bakkisweety ~]# yum install audacity-freeworld.x86_64
Loaded plugins: langpacks, show-leaves, upgrade-helper
Package audacity-freeworld-2.0.6-1.fc21.x86_64 already installed and latest version
Nothing to do


The normal Audacity package is not having support to import MP3 / MP4 audio.

I have created following files using Audacity,

1. hello_10.wav - My hello voice. Total length 10 sec


2. hello_10.mp3 - hello_10.wav is converted as a MP3 file


3. hello_10_noise1.wav - hello_10.wav, Added more noise in middle and small point in beginning.


4. hello_10_less_noise.wav - hello_10.wav, with less noise added.



Tools are installed, Test audio files are available, Lets starts the experiment,


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
[root@bakkisweety Audacity]# fpcalc hello_10.wav 
fpcalc: /usr/lib64/nvidia-304xx/libOpenCL.so.1: no version information available (required by /lib64/libavutil.so.54)
FILE=hello_10.wav
DURATION=10
FINGERPRINT=AQAAQWOiRRojodY0IZG-J0Eq7YLDo3cR86C4i5CbHXkvoXeRqVq4IDkfWL2EpmmK-0HYBUyebpA7HfmFOumERLryJEilXXB49C5iHg2XE1JWpkOuC3VuZKqmLUjOPbB6oQnTVDh9fDkyPd0gdzryC3XSItMVLgkS67hOVHUR82jIE1L2dMh1oc6NtJq2QDL3IFUvNKGPpym-Pch0b5AbIb-E3kWmKgoXIbE-XCeqNsXpIyxPAAGEA8wQQ4BQAgEEjBRAOMAQBEgQAYBBCBgpGBIMEQgQEQIBhIwDDAkAEA
[root@bakkisweety Audacity]# fpcalc hello_10.mp3 
fpcalc: /usr/lib64/nvidia-304xx/libOpenCL.so.1: no version information available (required by /lib64/libavutil.so.54)
FILE=hello_10.mp3
DURATION=10
FINGERPRINT=AQAAQWOiKNIYCXUyTUIifVeQSrvQhMddxDwo7iLkZkfeS-hdZKoWLkgsfoF_ognTFKePsMsJJk8HudORX6iTaUKm70mQUNrh8ETvIubRcLkIKWeO_LpQxzXSal2QyJwXWD2aME2F08eXI9PTDXKnI79QJy0yfVyCxPKJ60RVFzGPhssJKSvTIdeF50ZTTVuQyNyDVL3QhD6epvj2INO9QW6E_BJ6F5mqjEuCxPJxneib4j7CkCcAAYRTzBBDAAECAISAkQIYAxgRAgggADAIASMFQIIhCBAiQiCAkHEAIAEQQAA
[root@bakkisweety Audacity]# fpcalc hello_10_noise1.wav
fpcalc: /usr/lib64/nvidia-304xx/libOpenCL.so.1: no version information available (required by /lib64/libavutil.so.54)
FILE=hello_10_noise1.wav
DURATION=10
FINGERPRINT=AQAAQWOiKNIYCXXSCYl05UmQSrvg8Ohd5IezXYTEZjny6hJ6F5mqdUFy7oFVWWjyVPiDNsrRPJWKF0f4HuLH42GPTF-F5xeqoz3chCQ-HPmQ_PhOoaZwfqi1ywhzpobc6cgv9C4yVVG4JEis4zpR1UXMoyFPMHs6JNeF50ZTTSMSmbuCVL3QhMfjIt4O6hYhN0feS-hdZKqicBES6_gl1GmK-wjLAwGEA4YZYQAQxFCAEFMCCGIMAMBoiUQWAiCBBABGAoYEQ8QBhIBwCCFlCJBMIIAA
[root@bakkisweety Audacity]#


Ok. We have some acoustic fingerprints data but what do with this data. This acoustic fingerprints are encoded with specific format. We need a RAW acoustic fingerprints. Lets get the RAW acoustic fingerprints,


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@bakkisweety Audacity]# fpcalc hello_10.wav -raw
fpcalc: /usr/lib64/nvidia-304xx/libOpenCL.so.1: no version information available (required by /lib64/libavutil.so.54)
FILE=hello_10.wav
DURATION=10
FINGERPRINT=1445291460,1450701268,1375130111,1369883486,1353107262,1357172526,1348782959,1092933959,1126558149,1445293508,1449616852,1475801597,1375130622,1353106270,1357238126,1365560174,1365571967,1126693191,1109748165,1445293508,1450668500,1375130111,1369883486,1353107262,1357172526,1348782959,1097136479,1126689221,1176857028,1449485780,1475867133,1375130622,1369883486,1357238062,1348782958,1365571950,1126693191,1109748165,1445293508,1449619924,1375130109,1370936190,1353107326,1357172526,1348782959,1097132383,1126689221,1176857028,1449485780,1475867125,1375130623,1369883486,1353172782,1357171502,1365563758,1126553927,1126525381,1445293508,1449616852,1408684541,1370936190,1353107326,1357238062,1348782958,1097132415

[root@bakkisweety Audacity]# fpcalc hello_10.mp3 -raw
fpcalc: /usr/lib64/nvidia-304xx/libOpenCL.so.1: no version information available (required by /lib64/libavutil.so.54)
FILE=hello_10.mp3
DURATION=10
FINGERPRINT=1445293508,1467478484,1375130111,1369883486,1353107246,1357172526,1348782959,1092933959,1126558149,1445293508,1449616852,1475801597,1370936190,1353106270,1357238062,1348782958,1097136511,1126693191,1109748165,1445293508,1450701268,1375130109,1370932062,1353107262,1357172526,1348782959,1092942175,1126689093,1176857028,1449616852,1475867125,1370936318,1369883486,1357238062,1348782958,1365571950,1126693191,1109748165,1445293508,1449619924,1442206205,1370936190,1353107326,1357172526,1348782959,1097136479,1126689221,1176857028,1449485764,1475867124,1375130623,1369883486,1353172782,1357171502,1365563758,1126553927,1126525381,1445293508,1449616852,1375097341,1370936190,1353107326,1357238126,1348782958,1097132383

[root@bakkisweety Audacity]# fpcalc hello_10_noise1.wav -raw
fpcalc: /usr/lib64/nvidia-304xx/libOpenCL.so.1: no version information available (required by /lib64/libavutil.so.54)
FILE=hello_10_noise1.wav
DURATION=10
FINGERPRINT=1445293508,1450668500,1375130111,1369883486,1353107262,1357172526,1348782895,1092933967,1126689221,1445293508,1449616852,1475867133,1375130622,1369867102,1357218606,1365541678,1348777278,-718818034,-718953202,-718951154,-735593185,-197747395,-734078147,-730969324,-781317164,-714208380,-680654444,-696314444,-696307276,-687918668,-692637259,-692767818,-744148490,-744196634,-773554842,1399122294,1126693191,1109748165,1445293508,1449616852,1375130109,1370936190,1353107326,1357172526,1348782959,1097132383,1126689223,1176857028,1449485764,1467478516,1375130623,1369883486,1353107246,1357171502,1348786543,1092999495,1126558149,1445293508,1449616852,1408684541,1375130494,1353106302,1357238126,1348782958,1365567871
 
[root@bakkisweety Audacity]# fpcalc hello_10_less_noise.wav -raw
fpcalc: /usr/lib64/nvidia-304xx/libOpenCL.so.1: no version information available (required by /lib64/libavutil.so.54)
FILE=hello_10_less_noise.wav
DURATION=10
FINGERPRINT=1445293508,1450668500,1375130111,1369883486,1353107262,1357172526,1348782895,1092933967,1126689221,1445293508,1449616852,1475867133,1375130622,1369883486,1357236014,1365561134,1365571966,1395128654,1445324878,1445325006,1445428447,1429624063,1362515327,1348880221,1357270604,1353075276,1369725916,1474587644,1449422308,1450468836,1475858924,1375130622,1369883486,1357367086,1357171502,1365571950,1126684999,1109748165,1445293508,1449616852,1375130109,1370936190,1353107326,1357172526,1348782959,1097132383,1126689223,1176857028,1449485764,1467478516,1375130623,1369883486,1353107246,1357171502,1348786543,1092999495,1126558149,1445293508,1449616852,1408684541,1375130494,1353106302,1357238126,1348782958,1365567871


Sweet, Now we have some numerical data. But still we need to make sense out this numerical data. For my situation, I need to see that two songs (one from the source which is radio station and other from radio receiver) are matching with in the threshold. For sure, The two songs wont be same.

Lets consider the numerical (signed 32bit integers) fingerpoint data as a data sets. So we have two sets in our hand. I choose the R Square assess of goodness of fit method.

In my situation, Since the source song and destination song content is same, I should get a positive correlation and in ideal lab conditions, R square should be 1.0 but so many things will happen during the radio transmission. So we can't get 1.0 but the it should be grater than 0.0. If I get 0.0 as a R square, something really wrong happen during the trasnmission.

Dont ask me why I have chose goodness of fit statistics method instead of something in electronics domain like FFT and its derivatives... I yet to test it with real data. I chose it based on pure instinct.

I wrote a perl script to do the whole process of running fpcalc to get the fingerprints, Calculate the R square and report the goodness of fit. Here is the script,


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
#!/usr/bin/env perl
#Script to calculate the similarity of the two audio files using chromaprint fingerprint algorithem 
#License: LGPL2.1+
#Author: Bakkiaraj M [http://npointsolutions.blogspot.in/]
#Usage: audio_chromaprint_diff.pl audio1_file audio2_file
#Note: Install the fpcalc tool version 1.1 before you run this script 

#Refer URLs
#Refer: http://acoustid.org/chromaprint
#Refer: http://en.wikipedia.org/wiki/Coefficient_of_determination

use strict;
use warnings;
use Statistics::LineFit;
use Data::Dumper;
use Capture::Tiny ':all';

#Globals
my $fp1ArrRef;
my $fp2ArrRef;
my $audioLen = 9999; #Len in secs for fingerprint calculation. Just give rough high number. Later enhance based on songs.

my $fpcalctool = '/usr/bin/fpcalc -raw -length '.$audioLen.' ';

#Get the file names from the command line 
my $fn1 = $ARGV[0];
my $fn2 = $ARGV[1];

my $fn1FPs = 0;
my $fn2FPs = 0;
my $fpDiff = 0;

if (!defined ($fn1) or !defined ($fn2))
{
 print "\n Usage: perl $0 audio_file1 audio_file2";
 exit (-1);
}

if (! -s $fn1 or ! -s $fn2)
{
 print "\n ERROR: $fn1 or $fn2 is not a proper file.";
 exit (-1);
}


#Function
sub calcFingerPrint
{
 my $fileName = shift @_;
 
 #print "\n EXEC: $fpcalctool \"$fileName\"";
 
 my ($fpdata, $stderr, $exit) = capture {
  
        system( $fpcalctool.' "'.$fileName.'"' );
     };
    
    unless ($exit == 0)
    {
     print "\n ERROR: While running fpcalc tool";
     print "\n CMD: ",$fpcalctool.' "'.$fileName.'"';
     print "\n STDERR: ", $stderr;
     exit ($exit);
    }
    
 if ($fpdata =~m/FINGERPRINT=(.*)/g)
 {
  my @fpDataArray = ();
     @fpDataArray = split (/,/,$1);
     return \@fpDataArray;
 }
 else
 {
  return [];
 }
}

$fp1ArrRef = calcFingerPrint($fn1);
$fn1FPs = scalar @$fp1ArrRef;
$fp2ArrRef = calcFingerPrint($fn2);
$fn2FPs = scalar @$fp2ArrRef;

#$,=" ";
print "\n File1: $fn1 Tot FPs ",$fn1FPs;
#print "\n ", @$fp1ArrRef;
print "\n File2: $fn2 Tot FPs ", $fn2FPs;
#print "\n ", @$fp1ArrRef;

if ($fn1FPs != $fn2FPs)
{
 #Equalise th array items , so it will be equal.
 if ($fn1FPs > $fn2FPs)
 {
  splice ($fp1ArrRef,$fn2FPs);
 }
 else
 {
     splice ($fp2ArrRef,$fn1FPs);
 }
    print "\ Note: File1 & File2 have different FingerPrints, Lowest number of Fingerprints will be used in R2"; 
}



my $lfit = Statistics::LineFit->new();
$lfit->setData($fp1ArrRef, $fp2ArrRef);

printf "\n\n Goodnees of fit R2 for File1 & File2 = %.8f \n", $lfit->rSquared();

exit (0);

This script takes the two audio files and calculate the goodness of fit based on the acoustic fingerprints data. Lets run it,


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
[root@bakkisweety Audacity]# /mnt/WinD/Eclipse_WorkSpace/PerlExamples/audio_chromaprint_diff.pl hello_10.wav hello_10.mp3 

 File1: hello_10.wav Tot FPs 65
 File2: hello_10.mp3 Tot FPs 65

 Goodnees of fit R2 for File1 & File2 = 0.92182237 
[root@bakkisweety Audacity]# /mnt/WinD/Eclipse_WorkSpace/PerlExamples/audio_chromaprint_diff.pl hello_10.wav hello_10_less_noise.wav 

 File1: hello_10.wav Tot FPs 65
 File2: hello_10_less_noise.wav Tot FPs 65

 Goodnees of fit R2 for File1 & File2 = 0.50358946 
[root@bakkisweety Audacity]# /mnt/WinD/Eclipse_WorkSpace/PerlExamples/audio_chromaprint_diff.pl hello_10.wav hello_10_noise1.wav 

 File1: hello_10.wav Tot FPs 65
 File2: hello_10_noise1.wav Tot FPs 65

 Goodnees of fit R2 for File1 & File2 = 0.02005060 


The output clearly shows that my method of comparing the audio files is not greatly affected by the audio file formats (MP3 / WAV).

When I calculate the goodness of fit for hello_10.wav & hello_10.mp3, I get 92%, which shows that these are almost same songs.

When  more noise added the goodness of fit goes low.

I have also tested with real songs / music files. The outcome is convincing.

Can we automate the audio listening test with this approach?

Yes. we need to already set the goodness of fit tolerance level before we start the audio listening test.

Goodness of fit tolerance level is based on the source song, compressing techniques, transfer medium (AM / FM) etc ... Which I still need to workout.

I hope, with this method, we can reduce the human audio listening times during the testing.

Nevertheless, Nothing can replace human ears when it comes to music / audio testing. The above kind of methods only assists and provides the likely hood of problematic test vectors.

I will update once I work real captured data in the lab.

If this method works well, I am planning to create a perl module to wrap the libchromaprint functionality. so we can call the fpcalc as a perl function instead of subprocess.

Bye Bye ...