<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/">
<channel>
<atom:link href='http://www.marinbezhanov.com/rss/' rel='self' type='application/rss+xml' />
<title>Marin Bezhanov - freelance programmer, game developer, musician</title>
<link>http://www.marinbezhanov.com</link>
<description>I am a freelance programmer specializing in web development and game programming. I also play guitar and love making music.</description>
<lastBuildDate>Wed, 19 Jun 2013 09:27:46 +0300</lastBuildDate>
<item><title>Calculating Average Rating the Right Way using PHP and MySQL</title><link>http://www.marinbezhanov.com/web-development/18/calculating-average-rating-the-right-way-using-php-and-mysql/</link><description>
<![CDATA[<p>These days ratings are everywhere, especially with the increasing popularity of Rich Snippets and with Google encoraging webmasters to implement them on their websites. You've surely seen search results looking like
 this:</p>
<br/><p><img src="http://www.google.com/help/hc/images/webmasters_99170_rsreview.png" alt="Google Search Result" width="537" height="90"/></p>
<br/><p>Google Search displays these fancy 5-star ratings thanks to the <a href="http://support.google.com/webmasters/bin/answer.py?hl=en&answer=146645" target="_blank">Aggregate Reviews</a> snippet (consider checking it out if you 
haven't already done so). To make this rich snippet work, you feed it with an average rating (grading scale is from 1 to 5, 1 being the worst, 5 being the best) and optionally - with the total number of votes that formed
this average.</p>
<br/><p>The power of this rich snippet shouldn't be underestimated. Not only it has huge impact on clicktrough and bounce rates, but it is also extremely useful to people. Even more, using Rich Snippets can really make a 
difference and with content on your website being rated by users, people will be able to easily determine, whether visiting your website is worth their time or not at all. If
they search for a random keyword and your website appears at, say, #6 on the search results page, but is the only one utilizing the aggregated reviews snippet, it's very likely that they will still choose to check it out and ignore the
sites at #1 to #5.</p>
<br/><p>So, the benefit of having average ratings displayed along with your content is unarguable, but it's insane how many websites waste a ridiculous amount of server resources to do the necessary calculations for displaying 
that average rating on their pages.</p>
<br/><p>As you may already know, I love giving practical examples, so let's say we're dealing with a blog here and we want average ratings on each article on the blog. People are able to rate the articles with a grade from 1 to 5, with
only 1 unique vote being allowed per article per IP. Here are some of the most common scenarios:</p>
<br/><p><b>PLEASE NOTE: the code snippets presented in this article are simplified and only have informational purpose.</b></p>
<br/><h3>SCENARIO 1: The Simple Approach</h3>
<br/><p>All the ratings are stored in a single database table like this:</p>
<br/><div class="mysql" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="sy1">&gt;&gt;</span> <a href="http://search.mysql.com/search?site=refman-%35%31&amp;q=SELECT"><span class="kw1">SELECT</span></a> <span class="sy1">*</span> <a href="http://search.mysql.com/search?site=refman-%35%31&amp;q=FROM"><span class="kw1">FROM</span></a> <span class="st0">'votes'</span> <a href="http://search.mysql.com/search?site=refman-%35%31&amp;q=WHERE"><span class="kw1">WHERE</span></a> article_id <span class="sy1">=</span> <span class="st0">'12453'</span><span class="sy2">;</span></div></li>
<li class="li1"><div class="de1">article_id&nbsp; &nbsp; &nbsp; grade &nbsp; ip</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.1</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.45</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">1</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.12</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.32</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">4</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.2</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.77</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">3</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.65</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">3</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.89</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.12</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">1</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.5</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.7</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">2</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.3</div></li>
</ol><div class="foot">Parsed in 0.008 seconds at 39.30 KB/s</div></div>
<br/><p>...and whenever a new vote is cast, something like this happens in the PHP backend:</p>
<br/><div class="php" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="re0">$ipQuery</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"SELECT ip FROM votes WHERE article_id = '<span class="es4">$article_id</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/mysql_num_rows"><span class="kw3">mysql_num_rows</span></a><span class="br0">(</span><span class="re0">$ipQuery</span><span class="br0">)</span> <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="br0">)</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// whoops, a vote from this IP already exists for the current article_id, so</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// we should ouput some fancy error message</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">else</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// everything is cool, we can INSERT the new vote into the database</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"INSERT INTO votes (article_id,grade,ip) VALUES<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;('<span class="es4">$article_id</span>','<span class="es4">$grade</span>','<span class="es4">$_SERVER[REMOTE_ADDR]</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
</ol><div class="foot">Parsed in 0.032 seconds at 13.88 KB/s</div></div>
<br/><p>...and whenever the average rating needs to be displayed to the user, PHP does something like this:</p>
<br/><div class="php" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="re0">$gradeSum</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="co1">// total sum of all grades</span></div></li>
<li class="li1"><div class="de1"><span class="re0">$gradeCount</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="co1">// total number of grades</span></div></li>
<li class="li1"><div class="de1"><span class="re0">$gradeQuery</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"SELECT grade FROM votes WHERE article_id = '<span class="es4">$article_id</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">while</span> <span class="br0">(</span><a href="http://www.php.net/list"><span class="kw3">list</span></a><span class="br0">(</span><span class="re0">$grade</span><span class="br0">)</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_fetch_row"><span class="kw3">mysql_fetch_row</span></a><span class="br0">(</span><span class="re0">$gradeQuery</span><span class="br0">)</span><span class="br0">)</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$gradeSum</span> <span class="sy0">+=</span> <span class="re0">$grade</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$gradeCount</span><span class="sy0">++;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
<li class="li1"><div class="de1"><span class="re0">$averageRating</span> <span class="sy0">=</span> <a href="http://www.php.net/round"><span class="kw3">round</span></a><span class="br0">(</span><span class="re0">$gradeSum</span> <span class="sy0">/</span> <span class="re0">$gradeCount</span><span class="sy0">,</span><span class="nu0">2</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">echo</span> <span class="re0">$averageRating</span><span class="sy0">;</span></div></li>
</ol><div class="foot">Parsed in 0.032 seconds at 10.59 KB/s</div></div>
<br/><p>Now this is a simple, but terrible, TERRIBLE solution! For a small website it may work OK for a while, even for a longer period of time, but once your site gets big and you have millions of ratings stored in your database, 
you'll realize how slow and ineffective this is. Your database will get hit hard, table locks will start occuring, and during traffic spikes your website may become completely unresponsive.</p>
<br/><p>Let's speak with facts. If you have, say, 1000 votes cast on an article, every time you want to display the average rating, you'll have to retrieve all 1000 rows from your database, iterate through all them with a While Loop to calculate their total sum and
their number, so you can then calculate their weighted average like they taught us in math class - all of this being a huge waste of CPU processing time and RAM!</p>
<br/><h3>SCENARIO 2: The Relational Database Approach</h3>
<br/><p>Knowing the downsides of the first scenario, more experienced web developers will probably make a second database table to store the average rating. Something like this:</p>
<br/><div class="mysql" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="sy1">&gt;&gt;</span> <a href="http://search.mysql.com/search?site=refman-%35%31&amp;q=SELECT"><span class="kw1">SELECT</span></a> <span class="sy1">*</span> <a href="http://search.mysql.com/search?site=refman-%35%31&amp;q=FROM"><span class="kw1">FROM</span></a> <span class="st0">'average<span class="es1">_</span>ratings'</span> <a href="http://search.mysql.com/search?site=refman-%35%31&amp;q=WHERE"><span class="kw1">WHERE</span></a> article_id <span class="sy1">=</span> <span class="st0">'12453'</span><span class="sy2">;</span></div></li>
<li class="li1"><div class="de1">article_id&nbsp;&nbsp;average_rating&nbsp;&nbsp;total_votes</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453</span> &nbsp; <span class="nu0">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;3.66</span>&nbsp; &nbsp; <span class="nu0">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;12</span></div></li>
</ol><div class="foot">Parsed in 0.007 seconds at 15.33 KB/s</div></div>
<br/><p>That way, whenever the average rating needs to be retrieved for a given article, the system won't have to scan all the 1000 matching rows like in Scenario 1, but instead the rating can be displayed by just retrieving that single
row stored in the 'average_ratings' table. Like this:</p>
<br/><div class="php" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="re0">$avgRatingQuery</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"SELECT average_rating FROM average_ratings WHERE <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;article_id = '<span class="es4">$article_id</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><a href="http://www.php.net/list"><span class="kw3">list</span></a><span class="br0">(</span><span class="re0">$averageRating</span><span class="br0">)</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_fetch_row"><span class="kw3">mysql_fetch_row</span></a><span class="br0">(</span><span class="re0">$avgRatingQuery</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">echo</span> <span class="re0">$averageRating</span><span class="sy0">;</span></div></li>
</ol><div class="foot">Parsed in 0.032 seconds at 5.99 KB/s</div></div>
<br/><p>Of course, even in this scenario, we won't be able to fully avoid the retrieval of all the 1000 matching rows in the 'votes' table, because we will still have to update the values in the 'average_ratings' table, once a new
vote is cast. So in comparison to Scenario 1, the code that processes new votes would now more likely look like this:</p>
<br/><div class="php" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="re0">$ipQuery</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"SELECT ip FROM votes WHERE article_id = '<span class="es4">$article_id</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/mysql_num_rows"><span class="kw3">mysql_num_rows</span></a><span class="br0">(</span><span class="re0">$ipQuery</span><span class="br0">)</span> <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="br0">)</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// whoops, a vote from this IP already exists for the current article_id, so</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// we should ouput some fancy error message</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">else</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// everything is cool, we can INSERT the new vote into the database</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"INSERT INTO votes (article_id,grade,ip) VALUES ('<span class="es4">$article_id</span>',<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'<span class="es4">$grade</span>','<span class="es4">$_SERVER[REMOTE_ADDR]</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// new vote has been inserted, we should now re-calculate the average rating and</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// update its value in the 'average_ratings' table</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$gradeSum</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="co1">// total sum of all grades</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$gradeCount</span> <span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">;</span> <span class="co1">// total number of grades</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$gradeQuery</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"SELECT grade FROM votes WHERE article_id = '<span class="es4">$article_id</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">while</span> <span class="br0">(</span><a href="http://www.php.net/list"><span class="kw3">list</span></a><span class="br0">(</span><span class="re0">$grade</span><span class="br0">)</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_fetch_row"><span class="kw3">mysql_fetch_row</span></a><span class="br0">(</span><span class="re0">$gradeQuery</span><span class="br0">)</span><span class="br0">)</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$gradeSum</span> <span class="sy0">+=</span> <span class="re0">$grade</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$gradeCount</span><span class="sy0">++;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">}</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$averageRating</span> <span class="sy0">=</span> <a href="http://www.php.net/round"><span class="kw3">round</span></a><span class="br0">(</span><span class="re0">$gradeSum</span> <span class="sy0">/</span> <span class="re0">$gradeCount</span><span class="sy0">,</span><span class="nu0">2</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"UPDATE average_ratings SET average_rating = '<span class="es4">$averageRating</span>',<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;total_votes = '<span class="es4">$gradeCount</span>' WHERE article_id = '<span class="es4">$article_id</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
</ol><div class="foot">Parsed in 0.034 seconds at 31.00 KB/s</div></div>
<br/><p>Although you'll still have to waste a lot of server resources every time a new vote is cast, you'll waste way less resources for actually displaying the average rating compared to Scenario 1. Because, if you think about it,
the number of people reading an article will surely be way higher than the number of people that decided to not only read the entire article, but to also rate it, so it's much better that the resource waster is placed within
the code responsible for processing new votes than in the code responsible for displaying the average rating for an article. Makes sense, doesn't it? But still, why do you even have to have a resource waster in your code?
Isn't there a better solution?</p>
<br/><p>The answer is: yes, there is! This is the solution I'm using in all of my recent projects and being a bit of an egocentric (:D) I named it after myself, calling it <strong>The Bezhanov Algorithm</strong></p>
<br/><p>It took me quite some time to figure it out, because I never really learned this type of math or statistical calculations in high school and I never really studied anything else than music in the university
(and I never really finished university, but that's another thing :D), so I'm not sure I'm the one who invented it, but nevertheless - I've never seen anyone else using this implementation before and I've come up with it
all by myself, so I thought it would be great to share it with everyone and would be easier for people to refer to it using the name I gave it (The Bezhanov Algorithm), instead of saying: "hey, why don't you use that fancy algorithm a random Bulgarian guy
mentioned in his blog." :)</p>
<br/><h3>MY ALGORITHM</h3>
<br/><p>I basically take Scenario 2 as a starting point. There's a 'votes' table that I use to store each unique vote, so I can check against the user IP to see if the user has already rated a given article. I also have an
'average_ratings' table of some sort, where I store the article_id, the total number of votes cast for that article and its average rating.</p>
<br/><p>Whenever I need to display the average rating to users, I retrieve a single row from the 'average_ratings' table, just like in Scenario 2. And here's the exciting part: I also retrieve a single row to update the average rating
when a new vote is cast! And even if there are millions of votes related to an article - it's still just 1 row being retrieved from the database, both for displaying the average rating and for re-calculating the average rating
after a new vote. So, no CPU waste, no RAM waste, simple, powerful and effective!</p>
<br/><p>So how do I do this?</p>
<br/><div class="php" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="co1">// I check for duplicate votes just like in Scenario 1 and Scenario 2.</span></div></li>
<li class="li1"><div class="de1"><span class="re0">$ipQuery</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"SELECT ip FROM votes WHERE article_id = '<span class="es4">$article_id</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">if</span> <span class="br0">(</span><a href="http://www.php.net/mysql_num_rows"><span class="kw3">mysql_num_rows</span></a><span class="br0">(</span><span class="re0">$ipQuery</span><span class="br0">)</span> <span class="sy0">&gt;</span> <span class="nu0">0</span><span class="br0">)</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// whoops, a vote from this IP already exists for the current article_id, so</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// we should ouput some fancy error message</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">else</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// everything is cool, we can INSERT the new vote into the database</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"INSERT INTO votes (article_id,grade,ip) VALUES ('<span class="es4">$article_id</span>',<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;'<span class="es4">$grade</span>','<span class="es4">$_SERVER[REMOTE_ADDR]</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// retrieve data from the 'average_ratings' table</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$avgRatingQuery</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"SELECT average_rating,total_votes FROM average_ratings<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WHERE article_id = '<span class="es4">$article_id</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$avgRatingData</span> <span class="sy0">=</span> <a href="http://www.php.net/mysql_fetch_assoc"><span class="kw3">mysql_fetch_assoc</span></a><span class="br0">(</span><span class="re0">$avgRatingQuery</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// time to put the Bezhanov Algorithm in action</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// $grade is the grade that the user rates the current article with</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$total_votes</span> <span class="sy0">=</span> <span class="br0">(</span>int<span class="br0">)</span><span class="re0">$avgRatingData</span><span class="br0">[</span><span class="st_h">'total_votes'</span><span class="br0">]</span><span class="sy0">;</span> <span class="co1">// total number of votes we have so far</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$avg_rating</span> <span class="sy0">=</span> <span class="br0">(</span>float<span class="br0">)</span><span class="re0">$avgRatingData</span><span class="br0">[</span><span class="st_h">'average_rating'</span><span class="br0">]</span><span class="sy0">;</span> <span class="co1">// current average rating</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$new_vote_weight</span> <span class="sy0">=</span> <a href="http://www.php.net/round"><span class="kw3">round</span></a><span class="br0">(</span><span class="re0">$grade</span> <span class="sy0">/</span> <span class="br0">(</span><span class="re0">$total_votes</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="br0">)</span><span class="sy0">,</span><span class="nu0">2</span><span class="br0">)</span><span class="sy0">;</span> <span class="co1">// "weight" of the new vote</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$avg_rating_weight</span> <span class="sy0">=</span> <a href="http://www.php.net/round"><span class="kw3">round</span></a><span class="br0">(</span><span class="br0">(</span><span class="re0">$avg_rating</span> <span class="sy0">/</span> <span class="br0">(</span><span class="re0">$total_votes</span> <span class="sy0">+</span> <span class="nu0">1</span><span class="br0">)</span><span class="br0">)</span> <span class="sy0">*</span> <span class="re0">$total_votes</span><span class="sy0">,</span><span class="nu0">2</span><span class="br0">)</span><span class="sy0">;</span> <span class="co1"><br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// "weight" of the current average rating</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$newRating</span> <span class="sy0">=</span> <span class="re0">$new_vote_weight</span> <span class="sy0">+</span> <span class="re0">$avg_rating_weight</span><span class="sy0">;</span> <span class="co1">// we sum both "weights" and get<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// the new rating, simple &amp; effective</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// finally we update the 'average_ratings' table</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <a href="http://www.php.net/mysql_query"><span class="kw3">mysql_query</span></a><span class="br0">(</span><span class="st0">"UPDATE average_ratings SET total_votes = total_votes + 1, <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;average_rating = '<span class="es4">$newRating</span>' WHERE article_id = '<span class="es4">$article_id</span>'"</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
</ol><div class="foot">Parsed in 0.036 seconds at 44.16 KB/s</div></div>
<br/><p>You may be confused: "What's the logic behind this algorithm? How did you come up with it? How accurate is it?"</p>
<br/><p>First of all - I can assure you it's accurate, second of all - here's the detailed explanation of its logic. Take the sample data from Solution 1:</p>
<br/><div class="mysql" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="sy1">&gt;&gt;</span> <a href="http://search.mysql.com/search?site=refman-%35%31&amp;q=SELECT"><span class="kw1">SELECT</span></a> <span class="sy1">*</span> <a href="http://search.mysql.com/search?site=refman-%35%31&amp;q=FROM"><span class="kw1">FROM</span></a> <span class="st0">'votes'</span> <a href="http://search.mysql.com/search?site=refman-%35%31&amp;q=WHERE"><span class="kw1">WHERE</span></a> article_id <span class="sy1">=</span> <span class="st0">'12453'</span><span class="sy2">;</span></div></li>
<li class="li1"><div class="de1">article_id&nbsp; &nbsp; &nbsp; grade &nbsp; ip</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.1</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.45</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">1</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.12</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.32</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">4</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.2</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.77</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">3</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.65</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">3</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.89</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.12</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">1</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.5</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">5</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.7</div></li>
<li class="li1"><div class="de1"><span class="nu0">&nbsp;&nbsp;12453&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> &nbsp; <span class="nu0">2</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;192.168.1.3</div></li>
</ol><div class="foot">Parsed in 0.008 seconds at 39.41 KB/s</div></div>
<br/><p>If you take a calculator and sum these up, then divide by their amount, you'll get the following average rating:</p>
<br/><p><b>(5 + 5 + 1 + 5 + 4 + 5 + 3 + 3 + 5 + 1 + 5 + 2) / 12 = 44 / 12 = 3.67</b> (when we round to a precision of 2 decimal digits)</p>
<br/><p>Now imagine, we don't have the last grade yet (which is '2' coming from IP '192.168.1.3')</p>
<br/><p>We would have an average rating of:</p>
<br/><p><b>(5 + 5 + 1 + 5 + 4 + 5 + 3 + 3 + 5 + 1 + 5) / 11 = 42 / 11 = 3.82</b> (when we round to a precision of 2 decimal digits)</p>
<br/><p>Using my algorithm, what would happen if the next vote is '2'? Here's what:</p>
<br/><p>- First of all, we had 11 grades, with the new vote of '2', the grades will become 12 and each grade will form a 1/12 part of the new average rating, the idea being - when you sum all parts (1/12), you'll get the new average rating (12/12) </p>
<p>- If each grade forms a 1/12 part of the average ratng, the value of the new vote - '2' - will be equal to 2 * 1/12 = 2 / 12 = 0.17 (rounded to 2 digits)</p>
<p>- We don't need to know what the other grades are. We already know what we need, and that is - that their sum forms the rest 11/12 of the new average rating. So their "weight" as I call it (probably a wrong term to use) is
  equal to 3.82 * 11/12 = 3.50 (rounded to 2 digits)</p>
<p>- Now that we know these, we can sum 11/12 + 1/12 to get 12/12 aka the new average rating, so we sum up 3.50 + 0.17 and we get 3.67 - just what we got as a result in the simple calculations above.</p>
<br/><p>I guarantee this algorithm works every time and that it's 99.9% accurate! Throw in some random calculations if you want and see for yourself!</p>
<br/><p>If you like the algorithm, you're free to use it in your projects (both commercial and non-commercial).  My only conditions are:</p>
<br/><p>- That I receive attribution for this (mention me in your source code, or when you're sharing the algorithm with friends on the Internet - I worked hard to figure this out, so why not tell the other people where you learned
  this algorithm from, so they can say some nice words to me and keep me motivated to share more stuff like this)</p>
<p>- That you send me an email to show me the project you used it for - I always love to see how my work helped people, it's really motivating, makes my day and helps me go on.</p>
<br/><p>Thanks for dedicating your time to this article. Talk to you soon!</p>
<p>Marin</p>
<br/><h3>BENCHMARKS</h3>
<br/><p>Benchmarks confirm that the Bezhanov algorithm is faster than using SELECT AVG() in MySQL or using the standard arithmetic mean equation in PHP to calculate average ratings.</p>
<br/><p>Online benchmarks of the algorithm running on 1 000 000 row data set are hosted here: <a href="http://www.marinbezhanov.com/temp/benchmark/" target="_blank">Cast Vote & Re-Calculate Average Rating Benchmark</a> &amp; <a href="http://www.marinbezhanov.com/temp/benchmark/benchmark2.php" target="_blank">Display Average Rating Benchmark</a></p>
<br/><p>Alternatively, you can download the source code of that benchmark and run it in your own hosting environment, on a larger data set: <a href="http://www.marinbezhanov.com/temp/bezhanov-benchmark.zip" target="_blank">Benchmark Source Code</a></p>
<br/><h3>PEER REVIEWS</h3>
<p>- <a href="http://phpacademy.org/forum/viewtopic.php?f=38&t=16705" target="_blank">PHP Academy</a></p>
<p>- <a href="http://www.webdeveloper.com/forum/showthread.php?p=1213596" target="_blank">Webdeveloper.com</a></p>
<p>- <a href="http://www.php-forum.com/phpforum/viewtopic.php?f=1&t=19914" target="_blank">PHP Forum</a></p>
<br/>&nbsp;]]>
</description><guid isPermaLink='false'>marin-18-1341338107</guid><pubDate>Tue, 03 Jul 2012 20:55:07 +0300</pubDate></item><item><title>How Cloud Hosting Works (A Brief Technical Overview for Beginners)</title><link>http://www.marinbezhanov.com/web-development/17/how-cloud-hosting-works-a-brief-technical-overview-for-beginners/</link><description>
<![CDATA[<br/><p>Cloud Hosting was originally introduced to the public at around 2008 and has been enjoying an increasing popularity ever since, with more and more web businesses switching to cloud hosting solutions to run their websites on. 
Cloud Hosting has been described as the more effective, cheaper and much more reliable alternative to Traditional Hosting and that is true for the most part. Indeed - cloud  hosting makes sense, but only assuming we have larger
 businesses in mind, which have to serve millions of users on a weekly basis. For smaller companies, the traditional dedicated server model may still be the better and cheaper option.</p>
<br/><p>But we are here to talk about cloud hosting. Now, when you read on the Internet, everybody keeps pointing out the benefits of cloud hosting, but when you actually get your cloud hosting account, you realize that none of these 
articles explained in detail how you are supposed to set up your hosting environment afterwards, and all of a sudden hundreds of unanswered technical question arise that you have no idea how to deal with. Well, I will try to answer 
these in the present article.</p>
<br/><p>Let's imagine you are the owner of a site called <b>YOURSITE.COM</b> and want to configure your cloud hosting environment accordingly, so the site can operate from there.</p>
<br/><img src="http://www.marinbezhanov.com/img/17-fig01.jpg" alt="Cloud Hosting Illustration 01" width="276" height="374"/>
<br/><p>After you purchase a cloud hosting account, you usually start with some kind of an edge network device (usually a hardware firewall or a router of some sort) and are given access to the cloud network behind it. The cloud network 
consists of a huge collection of physical servers running in a data center at some random part of the world. The data center has perfect high bandwidth optic fiber connections to the outside world, with support engineers taking 
care for the maintenance of the network and the machines located in the data center.</p>
<br/><p>You are allowed to create server instances in the cloud network. When you create a server instance, that doesn't necessarily mean that it will take an entire physical server, because the server instance is actually a virtual 
device, which means that a physical server located in the data center may be easily hosting a few of your server instances or a collection of your instances and instances belonging to other users. However, that shouldn't bother 
you, as none of these users (including you) have access to the physical layer - you only have access to your own virtual server (aka the server instance) located on that physical machine.</p>
<br/><p>To distinguish between server instances, the cloud network assigns each instance a set of virtual IP addresses - 1 internal and 1 external. The external one is the address you can use to access your server instance from the 
outside world, while the internal one is the one you should use if you want to exchange data between some of your server instances.</p>
<br/><p>When you create a new server instance, you usually specify the amount of memory and disk space you would want it to have and also choose the operating system you want to be installed. What you get is a fresh installation with no 
server software at all. This means - you need to install Apache, or Nginx, or MySQL, or whatever you want to be using, all by yourself, by connecting to the server instance via SSH using its external IP and installing the necessary
 packages.</p>
<br/><p>So, assuming you are the owner of <b>YOURSITE.COM</b>, you can, for example, create 2 fresh server instances (<b>SERVER A</b> and <b>SERVER B</b>), installing Apache on one of them (<b>SERVER A</b>) and MySQL on the other one (<b>SERVER B</b>). The address you 
would use on <b>SERVER A</b> to reach the database at <b>SERVER B</b>, would be the local IP of <b>SERVER B</b> and vice-versa - when having to grant permissions on <b>SERVER B</b> to a database user connecting from <b>SERVER A</b>, you would use local IP address
 of <b>SERVER A</b>. As for the DNS records for <b>YOURSITE.COM</b> - you will have to point its DNS to the external IP address of <b>SERVER A</b>. The firewall rules on the edge network devices are usually very strict by default, so you will have to
 verify external access on port 80 (default web port) is allowed from the outside world to <b>SERVER A</b>, if you want your web server to be serving HTTP requests properly.</p>
<br/><p>But now comes the more complex part - what if you got the cloud hosting account exactly because you needed to run, say, 20 web servers and 5 database servers in parallel, so you can handle all the load. How would you configure 
everything?</p>
<br/><p>Well, along with the creation of server instances, most modern cloud hosting solutions allow you to create load balancers. In the cloud environment, load balancers are usually virtual devices as well (not to be mistaken with 
hardware load balancers). Their whole purpose is to accept incoming requests and forward them to the corresponding servers based on a given algorithm (least connections, round robin etc.)</p>
<br/><p>Load Balancers can either be local (visible only within the cloud network) or public (visible from the outside world). This allows you to accomplish various types of load balancing.</p>
<br/><p>For example, assuming your 20 web servers need to be able to access data from any of your 5 database servers depending on the database server load, you would place a local Load Balancer in front of your 5 database servers and tell
 your web servers to establish database connections through the private IP of the Load Balancer, which on its end will act as a gateway to the least overloaded database server.</p>
<br/><p>At the same time, if you want to spread incoming traffic to <b>YOURSITE.COM</b> through all of your 20 web servers, you would place a public Load Balancer in front of them and point the DNS records of <b>YOURSITE.COM</b> to the public IP of 
that Load Balancer and once again - it will act as a gateway, and redirect the requests to the web server doing least work at that very moment, of course, assuming that's the forwarding algorithm you decided to use.</p>
<br/><p>Here's an illustration:</p>
<br/><img src="http://www.marinbezhanov.com/img/17-fig02.jpg" alt="Cloud Hosting Illustration 02" width="329" height="459"/>
<br/><p>Of course for this solution to work properly, content on your web and database servers must be exactly the same at any given time. How is this accomplished? Through a method called content synchronization (also known as: 
content mirroring, folder syncing and many more similar terms). Content synchronization can be done with programs like Rsync and Unison. The idea is to use one of your 20 web servers as a master and the rest as mirrors, so when 
you need to update your website files, you would only apply changes to the master, then <a href="http://en.wikipedia.org/wiki/Rsync" rel="nofollow" target="_blank">Rsync</a>, or <a href="http://www.cis.upenn.edu/~bcpierce/unison/" target="_blank">Unison</a>, or whatever you choose to use, will apply these changes to the mirrors, by copying the files that have been added or updated
 and deleting the files that have been removed, from the master to all the mirrors. The same applies for the database servers.</p>
<br/><p>So, that's about it. I hope this article has been useful and that it shed some light on the technical side of cloud hosting from a beginner point of view. I decided this shouldn't be very detailed, so it can just give you a 
general hint on the cloud hosting concept and not fill your head with tons of additional information. If you think I missed something or want me to clarify anything in detail - please do no hesitate to comment on this post.</p>
<br/><p>Thanks for reading!</p>]]>
</description><guid isPermaLink='false'>marin-17-1337251215</guid><pubDate>Thu, 17 May 2012 13:40:15 +0300</pubDate></item><item><title>How to Embed Webfonts Properly and How to Solve the Ambiguous "CSS3111: @font-face encountered unknown error" </title><link>http://www.marinbezhanov.com/web-development/16/how-to-embed-webfonts-properly-and-how-to-solve-the-ambiguous-css3111-font-face-encountered-unknown-error/</link><description>
<![CDATA[<br/><img src="http://www.marinbezhanov.com/img/16-fig01.jpg" width="630" height="190" alt="F12 Developer Tools"/>
<br/><p>In the present article, we will be discussing how to embed webfonts properly with good cross-browser support, focusing especially on Internet Explorer, which is known to be one of the most troublesome browsers when it comes
to @font-face declarations. We will also clarify the meaning of the ambiguous CSS3111 error caused by certain webfonts in IE. Let's start, shall we?</p>
<br/><p>Getting a @font-face declaration to work properly in every browser can sometimes be a really challenging task for a web developer, especially when it comes to older versions of Internet Explorer.
Although IE9 offers a relatively good font embedding support, <a href="http://www.w3schools.com/browsers/browsers_explorer.asp" target="_blank">browser statistics</a> show that even in the beginning in 2012 a little over 10% of
the web users still use IE8 (which is actually more than the people currently using IE9) and almost 3% use IE7. There are even people still on IE6! These may sound like small numbers, but when you sum them up, you'll realize almost
15% of your users are using old versions of Internet Explorer and if you're the owner of a high traffic website with, say, 5 million unique visitors per month, that's 750,000 people!! Imagine the profits you may be missing, just
because of a faulty embedded font ruining the look of your website in their old browsers. It's just terrible!</p>
<br/><p>But why am I even talking about this? Well, because I have experienced and solved such issues myself for a number of times and sometimes the solutions were less than obvious. At the same time, a huge part of the information on the Internet
that discusses this topic is either a bit obsolete, or a bit too scarce. But before we focus on the errors, I'd like to talk about how to properly embed fonts in a way that works well in all browsers.</p>
<br/><p>Well, first of all, it is absolutely necessary that you have EOT, SVG, TTF &amp; WOFF versions of your web font. Yes, nowadays all major browsers support TTF &amp; WOFF font files, but everything older than Internet Explorer 9
requires EOT and some older versions of Mobile Safari &amp; Opera Mobile require SVG, so as a general rule of thumb it's good to have them all prepared, just to be 100% sure that the number of users not being able to see your 
fancy custom font is reduced to minimum. (Check this page for a more detailed <a href="http://www.webfonts.info/wiki/index.php?title=@font-face_browser_support" target="_blank">@font-face browser support</a> list)</p>
<br/><p>As for the actual CSS code used to embed fonts, there are about 3 popular code snippets that you can see reoccuring in most articles &amp; tutorials on the Internet. The one that works best for me is this one:</p>
<br/>
<div class="css" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="co1">@font-face{</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">font-family</span><span class="sy0">:</span><span class="st0">'somefont'</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">src<span class="sy0">:</span><span class="kw2">url</span><span class="br0">(</span><span class="st0">'http://www.example.com/somefont.eot'</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">src<span class="sy0">:</span> local<span class="br0">(</span><span class="st0">'O'</span><span class="br0">)</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">url</span><span class="br0">(</span><span class="st0">'http://www.example.com/somefont.woff'</span><span class="br0">)</span> format<span class="br0">(</span><span class="st0">'woff'</span><span class="br0">)</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">url</span><span class="br0">(</span><span class="st0">'http://www.example.com/somefont.ttf'</span><span class="br0">)</span> format<span class="br0">(</span><span class="st0">'truetype'</span><span class="br0">)</span><span class="sy0">,</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">url</span><span class="br0">(</span><span class="st0">'http://www.example.com/somefont.svg'</span><span class="br0">)</span> format<span class="br0">(</span><span class="st0">'svg'</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">font-weight</span><span class="sy0">:</span><span class="kw2">normal</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">font-style</span><span class="sy0">:</span><span class="kw2">normal</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
</ol><div class="foot">Parsed in 0.005 seconds at 68.72 KB/s</div></div>
<br/><p>...and I've used it in a good number of projects, but in fact I always minify that, so in my stylesheets it's usually all in one row, like this:</p>
<br/><p><strong style="font-size:10px;">@font-face{font-family:'somefont';src:url('http://www.example.com/somefont.eot');src: local('O'),url('http://www.example.com/somefont.woff') format('woff'),url('http://www.example.com/somefont.ttf') format('truetype'),url('http://www.example.com/somefont.svg') format('svg');font-weight:normal;font-style:normal;}</strong></p>
<br/><p>While some articles say that IE may fail loading your fonts if you minify your @font-face declaration, I can assure you this is
a myth and nothing bad will happen. So don't hesitate to minify your stylesheets - it's absolutely OK to do it and it won't choke the CSS processor.</p>
<br/><p>Another common myth claims that if your fonts are located on a remote server (a CDN for example) they won't render properly in all browsers. That's only partially true. Yes, without an explicit "Access-Control-Allow-Origin" header, 
Firefox and Internet Explorer won't display your webfonts (if you hit F12 to open up Developer Tools in IE and go to the Console tab, you will get the <strong>CSS3117: @font-face failed cross-origin request. Resource access is restricted.</strong> error)
That's simply because IE and Firefox don't allow cross-domain fonts by default. On the other hand Google Chrome will load the fonts without a problem and if you're not aware of the
cross-origin issue, debugging this may get really frustrating. While I personally prefer to place my fonts on the same domain too, you can still place them on a remote location and have them load successfuly in all browsers, as long as
you add this declaration to your main .htaccess file:</p>
<br/><div class="apache" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1">&lt;<span class="kw3">FilesMatch</span> <span class="st0">"<span class="es0">\.</span>(ttf|otf|eot|woff)$"</span>&gt;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &lt;<span class="kw3">IfModule</span> mod_headers.c&gt;</div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">Header</span> set Access-Control-Allow-Origin http://mydomain.com<span class="st0">"</span></div></li>
<li class="li1"><div class="de1"><span class="kw3"> &nbsp; &nbsp;&lt;/IfModule&gt;</span></div></li>
<li class="li1"><div class="de1"><span class="kw3">&lt;/FilesMatch&gt;</span></div></li>
</ol><div class="foot">Parsed in 0.006 seconds at 28.76 KB/s</div></div>
<br/><p>This will add an  "Access-Control-Allow-Origin" header to all of your font files, overriding the default setting for IE &amp; Firefox.</p>
<br/><p>Since we've already mentioned IE's F12 Developer Tools and discussed the CSS3117 error, it's worth pointing out that Developer Tools may actually be a really useful tool for debugging @font-face issues. All @font-face errors are reported
in the Console tab of Developer Tools. You can check this <a href="http://msdn.microsoft.com/en-us/library/hh180764%28v=vs.85%29.aspx#cssCodes" target="_blank">list of CSS Error Codes</a> and go through the error codes listed there to get
a better idea of what you may expect.</p>
<br/><p>CSS3117 is one of the common errors. Another common one is: <strong>CSS3114: @font-face failed OpenType embedding permission check. Permission must be Installable.</strong> CSS3114 usually occurs when the source of your 
EOT file was a locked TTF file (assuming you used a font face generator or did some type of TTF to EOT conversion). CSS3114 is almost always accompanied by the "mythical" CSS3111 error, but solving CSS3114 automatically
fixes both, as CSS3111 in that case is provoked by CSS3114.</p>
<br/><p>What is a locked TTF file? Well, all TTF files have an
"embeddable" flag stored in them. This flag determines whether the font can be embedded in a file or on a website, or whether such actions are prohibited. There is a great free tool that allows you to alter that flag.
It's called <a href="http://www.derwok.de/downloads/ttfpatch/" target="_blank">TTFPATCH</a>. However, you have to understand that using TTFPATCH doesn't make embedding the font legal - you can easily "cheat" and make
any non-embeddable font load in every browser, but this may be a copyright violation and the copyright holders may decide to sue you, so it's probably a better idea to just stick to fonts that are legally embeddable.</p>
<br/><p>And now to our main highlight - the <strong>"CSS3111: @font-face encountered unknown error"</strong>. This error is very ambiguous. If you have a look at
<a href="http://msdn.microsoft.com/en-us/library/hh180764%28v=vs.85%29.aspx#cssCodes" target="_blank">MSDN</a> again, you'll see its description says:
"An unknown problem was encountered with the "Web Open Font Format (WOFF)", and "Embedded OpenType font (EOT)" of the Cascading Style Sheets (CSS) font".
"Unknown Problem" doesn't sound too good to me - how am I supposed to solve an unknown problem? Fortunately we're given a hint here. It says: "Check source of the fonts". Indeed, CSS3111 is usually caused by 
an issue with the font's binary source. One of the popular <a href="http://www.kirsle.net/wizards/ttf2eot.cgi" target="_blank">online TTF to EOT converters</a> for example produces EOT files with a NAME table that doesn't
comply to the Microsoft standards, which results in EOT fonts that never load in IE and produce the CSS3111 error. So, when you experience a CSS3111, it is always good to try using a different TTF to EOT converter or font face
generator.</p>
<br/><p>Here's a list of my personal favorites:</p>
<p><a href="http://www.fontsquirrel.com/fontface/generator" target="_blank">http://www.fontsquirrel.com/fontface/generator</a></p>
<p><a href="http://fontface.codeandmore.com" target="_blank">http://fontface.codeandmore.com</a></p>
<p><a href="http://www.font2web.com/" target="_blank">http://www.font2web.com/</a></p>
<br/><p>Further reading:</p>
<p><a href="http://paulirish.com/2010/font-face-gotchas/" target="_blank">@font-face gotchas</a></p>
<p><a href="http://paulirish.com/2009/bulletproof-font-face-implementation-syntax/" target="_blank">Bulletproof @font-face syntax</a></p>
<br/><p>As a conclusion, @font-face errors are sometimes hard to diagnose. I've been using a tool for a while called <a href="http://www.my-debugbar.com/wiki/IETester/HomePage" target="_blank">IETester</a>. I liked IETester, because
it allowed me to view IE8, IE7, IE6 and even IE5.5 versions of my websites on my shiny new Windows 7 machine. I got used to it so much that I started relying entirely on it for my projects. A few of them used webfonts. Everything
seemed fine, because both the IE7 and IE8 tests displayed satisfying results - my @font-face declarations were being processed properly, but after a couple of months I received a complaint - the websites were "broken" in IE8.
"It can't be true" I said and opened up IETester to only see that I'm right. But after a while, I decided to turn on one of my old machines, that still had IE8 on it and what did I see - no custom fonts, instead - an ugly
system font that made the text way larger than what it was supposed to be, which on the other hand was breaking the layout and the site was looking really bad.</p>
<br/><p>Now, don't get me wrong, IETester is still a great tool, but my point is - when debugging always rely on multiple sources.
Nowadays, along with IETester, I use screenshot services like <a href="http://netrenderer.com/" target="_blank">NetRenderer</a> and <a href="http://browsershots.org/" target="_blank">BrowserShots</a> to make sure that what
IETester is displaying is 100% accurate.</p>]]>
</description><guid isPermaLink='false'>marin-16-1333189787</guid><pubDate>Sat, 31 Mar 2012 13:29:47 +0300</pubDate></item><item><title>Universal Web Scraper version 1.0 released!</title><link>http://www.marinbezhanov.com/web-development/15/universal-web-scraper-version-1-0-released/</link><description>
<![CDATA[<a href="http://code.google.com/p/universal-web-scraper/" target="_blank"><img src="http://www.marinbezhanov.com/img/15-fig01.jpg" alt="Universal Web Scraper" width="590" height="314"/></a>
<br/><p><strong>Universal Web Scraper</strong> is, as its name says, a universal web scraper and web crawler written in PHP and meant to be used primarily for data extraction tasks along with a tool like <a href="http://getfirebug.com/" target="_blank">Firebug</a> for example that allows you to view the source
code of webpages. I developed it for my own data extraction needs and used it for web scraping numerous times, but in my efforts to improve it and make it even more flexible, I figured out it may be a good idea to share it with the
rest of the world, so I can get some feedback and constructive criticism.</p>
<br/><p>There are many web scrapers out there, but most of them:</p>
<br/><p>- aren't free</p>
<p>- require good knowledge of regular expressions</p>
<br/><p>...and here's how <strong>Universal Web Scraper</strong> is different. It is:</p>
<br/><p>- freeware: you can download it for free from <a href="http://code.google.com/p/universal-web-scraper/downloads/list" target="_blank">Google Code</a></p>
<p>- requires no knowledge of regular expressions: all data scraping rules are provided in the form of simple English sentences</p>
<br/><p>I hope you'll find this little tool useful and contribute with feedback. As with all my other tools, I'm going to improve this one on a regular basis too.</p>]]>
</description><guid isPermaLink='false'>marin-15-1332772378</guid><pubDate>Mon, 26 Mar 2012 17:32:58 +0300</pubDate></item><item><title>ActionScript 3: Sound.extract() Demystified or How to Draw a Waveform in Flash</title><link>http://www.marinbezhanov.com/web-development/14/actionscript-3-sound-extract-demystified-or-how-to-draw-a-waveform-in-flash/</link><description>
<![CDATA[<p>I was working on a Flash project recently and had to draw the waveforms of certain files that were included in the project. To my surprise, there wasn't much information on the Internet on how to achieve that in Flash.</p>
<br/><p>There were indeed a couple of nice articles on the topic that I was able to locate: <a href="http://www.bytearray.org/?p=329" target="_blank">Rendering spectrums with Sound.extract()</a> &amp;
<a href="http://blog.efnx.com/?p=75" target="_blank">Plotting a Sound Wave in Flash AS3</a>, and although the code examples were really good, I realized from a beginner
point of view they were a bit hard to understand. I also felt some key concepts that a beginner needs to know about how sound data is stored, how byte arrays work, what does Sound.extract() do exactly etc. were missing
from these articles.</p>
<br/><p>On the other hand, the official Adobe Documentation concerning the same topic was also lacking detailed information on certain aspects of the Sound &amp; ByteArray classes that one needs to know in order to understand these
classes fully, so that's what made me write this article.</p>
<br/><p>If you have a certain amount of programming experience, the articles I mentioned above are more than a good start and you probably won't notice the lack of information I'm talking about, but
I thought it would be nice if there was a tutorial directed towards beginners with little programming experience, so that they don't have to read dozens of pages &amp; extra articles to understand what's going on.</p>
<br/><p>First of all, let me explain what a waveform is, because I've seen different people giving it different names. A waveform is the visual representation of an audio signal (see the screenshot below)</p>
<br/><img src="http://www.marinbezhanov.com/img/waveform_eggs.gif" alt="Waveform" width="600" height="429"/>
<br/><p>Waveforms are often used in audio editors &amp; audio players. One good example for a Flash application using waveforms is the <a href="http://soundcloud.com/marinbezhanov/inside" target="_blank">Soundcloud Audio Player</a></p>
<br/><p>To render a waveform of a certain audio file, you need to access and process its raw sound data. Sound data is stored as a sequence of sample blocks. A sample block is simply a collection of byte sequences. Each sequence of bytes
stores a number and corresponds to a certain audio channel. For example, in a mono file each sample block contains only 1 sequence of bytes, in a stereo file you would have 2 sequences of bytes per sample block - 1 for the left channel &amp; 1 for
the right one etc. The number stored in each sequence controls the fluctuation of the audio speaker (if you're unsure what this is, you may want to read this basic explanation on <a href="http://electronics.howstuffworks.com/speaker6.htm" target="_blank">How Speakers Work</a>),
or in other words: it "tells" the speaker coil how much to push or pull on the speaker cone in order to move the speaker diapraghm. This movement/fluctuation is in fact what produces the sound we hear.</p>
<br/><p>You may be really confused at this point, but things will get clearer now, when you realize how raw sound data is stored in Adobe Flash.</p>
<br/><p>Sounds in Flash are stored in <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html" target="_blank">Sound objects</a>. To extract the raw sound data of a Sound object, you would use
its <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Sound.html#extract%28%29" target="_blank">extract() method</a>. No matter what file you imported, the raw sound data is always returned as
44100Hz Stereo with the sample type being a 32-bit floating-point value - this means that no matter whether your original file was mono or stereo, whether its sample rate was 44100Hz or lower, its sample type 8-bit or 16-bit or
32-bit etc., the sound data returned by the extract() method will still be converted to 44100Hz Stereo, 32-bit, which on the other hand means:</p>
<br/><p>- <strong>Stereo</strong>: each sample block contains 2 sequences of bytes: 1 for the left channel, 1 for the right one</p>
<p>- <strong>32-bit Sample Type</strong>: each byte sequence is 32 bits long and since 1 byte = 8 bits, when we do a conversion we realize that each byte sequence is 4 bytes long, or in other words each sample block contains a total of 8 bytes.</p>
<p>- <strong>44100Hz Sample Rate</strong>: 1 second of audio is represented by 44100 sample blocks per channel. Since the file is Stereo (2 channels), this makes a total of 88200 sample blocks per second - 44100 for the left channel + 44100 for the right channel</p>
<br/><p>So, the <strong>Sound.extract()</strong> method stores the raw sound data in a <strong>ByteArray</strong>. Each element of the <strong>ByteArray</strong> contains a byte (8 bits) of information. This means we need to combine every 4 elements in order to form a 32-bit sequence
and since a sample block contains 2 of these sequences, because we are working with a Stereo file, every 8 elements (bytes) in the ByteArray form a sample block (see the schematic)</p>
<br/><img src="http://www.marinbezhanov.com/img/14-fig01.jpg" alt="Raw Sound Data Structure" width="507" height="477"/>
<br/><p>But what do these bytes actually represent? Every sequence of 4 bytes forms a 32-bit floating point number between -1 and 1. "-1" means the speaker cone is pulled at its maximum, while "1" means a maximum push on the speaker cone. "0"
on the other hand means "silence".</p>
<br/><p>Having to combine every 4 bytes in order to form a 32-bit floating point number of course might be a tedious process. Fortunately, the Adobe guys thought about this and provided the <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/ByteArray.html" target="_blank">ByteArray class</a>
with a <a href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/utils/ByteArray.html#readFloat%28%29" target="_blank">readFloat() method</a>. What readFloat() does is to read a sequence of 4 bytes (32 bits)
and return the corresponding floating point number.</p>
<br/><p>But how do we actually interpret the data and display in on screen. Well, in a typical waveform the X Axis shows how much time has elapsed since the start of the audio file and the Y Axis shows the speaker fluctuation (see the figure below)</p>
<br/><img src="http://www.marinbezhanov.com/img/14-fig02.jpg" alt="Waveform Data Explained" width="600" height="253"/>
<br/><p>However, if you have, say, a 5 minute audio file, that's over 26 million sample blocks - 13,230,000 per channel to be precise. That's an insanely big number - imagine you have 1 pixel on the X Axis representing a sample block, you would need
6890 24" computer monitors at 1920x1080 screen resolution to be able to see the waveform of the entire file. No, actually you won't need that many monitors, because Flash will probably stop responding long before the data is actually displayed, after all
that's over 26 million drawing operations as well!</p>
<br/><p>So, what you have to do is summarize the data before you put it on screen. In the example code I'm going to show you below, I split the second into 4 equal parts (that's 250ms or 11025 sample blocks per channel), but you can really
experiment with any ratio. So, I analyze these 11025 sample blocks and determine the minimum and maximum values, then I draw a line that connects these values, then I analyze the next sequence of 11025 sample blocks etc. etc. until I'm out of raw sound data to analyze.
This results in a good looking waveform that is also accurate and the smaller the ratio - the more accurate the waveform. Of course, this is a very, very simple waveform rendering algorithm, there are much more advanced ones out there, but
that's a good start in understanding how to draw waveforms in Flash.</p>
<br/><p>Here's a screenshot from my example waveform drawing application:</p>
<br/><img src="http://www.marinbezhanov.com/img/14-fig03.jpg" alt="Flash Generated Waveform" width="600" height="356"/>
<br/><p>Here's the actual example code:</p>
<br/>

<div class="actionscript3" style="margin-left:25px;"><div class="head">GeSHi &copy; 2004-2007 Nigel McNie, 2007-2010 Benny Baumann, 2008-2009 Milian Wolff</div><ol><li class="li1"><div class="de1"><span class="kw1">import</span> <span class="kw6">flash.display</span><span class="sy0">.</span><a href="http://www.google.com/search?q=sprite%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:sprite.html"><span class="kw5">Sprite</span></a><span class="sy0">;</span> &nbsp;<span class="co1">// we need a sprite to draw the waveform to</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">import</span> <span class="kw6">flash.media</span><span class="sy0">.</span><a href="http://www.google.com/search?q=sound%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:sound.html"><span class="kw5">Sound</span></a><span class="sy0">;</span> &nbsp; &nbsp; <span class="co1">// we need the sound class to extract raw sound</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// from our Sound objects </span></div></li>
<li class="li1"><div class="de1"><span class="kw1">import</span> <span class="kw6">flash.utils</span><span class="sy0">.</span><a href="http://www.google.com/search?q=bytearray%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:bytearray.html"><span class="kw5">ByteArray</span></a><span class="sy0">;</span> <span class="co1">// we need the byteArray class to store the sound</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// data extracted from our objects</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// I have imported a random MP3 to my Flash Project and gave it class name "TestSound"</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// So I just create a new Sound object using that "TestSound" MP3 that's in my library.</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">var</span> sound<span class="sy0">:</span><a href="http://www.google.com/search?q=sound%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:sound.html"><span class="kw5">Sound</span></a> = <span class="kw1">new</span> TestSound<span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// We will store the raw sound data in a ByteArray called "soundData"</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">var</span> soundData<span class="sy0">:</span><a href="http://www.google.com/search?q=bytearray%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:bytearray.html"><span class="kw5">ByteArray</span></a> = <span class="kw1">new</span> <a href="http://www.google.com/search?q=bytearray%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:bytearray.html"><span class="kw5">ByteArray</span></a><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// We need two sprites to draw the waveforms for the left and right channel</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">var</span> waveformLeft<span class="sy0">:</span><a href="http://www.google.com/search?q=sprite%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:sprite.html"><span class="kw5">Sprite</span></a> = <span class="kw1">new</span> <a href="http://www.google.com/search?q=sprite%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:sprite.html"><span class="kw5">Sprite</span></a><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">var</span> waveformRight<span class="sy0">:</span><a href="http://www.google.com/search?q=sprite%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:sprite.html"><span class="kw5">Sprite</span></a> = <span class="kw1">new</span> <a href="http://www.google.com/search?q=sprite%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:sprite.html"><span class="kw5">Sprite</span></a><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// We set a basic line style and reset the drawing position for each Sprite</span></div></li>
<li class="li1"><div class="de1">waveformLeft<span class="sy0">.</span><span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">moveTo</span><span class="br0">(</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">waveformLeft<span class="sy0">.</span><span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">lineStyle</span><span class="br0">(</span><span class="nu0">1</span><span class="sy0">,</span>0xff0000<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">waveformRight<span class="sy0">.</span><span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">moveTo</span><span class="br0">(</span><span class="nu0">0</span><span class="sy0">,</span><span class="nu0">0</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">waveformRight<span class="sy0">.</span><span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">lineStyle</span><span class="br0">(</span><span class="nu0">1</span><span class="sy0">,</span>0xff0000<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// this is how we extract the raw sound data from our Sound object. The tricky</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// part here is that the extract() method requires two parameters to be passed to it.</span></div></li>
<li class="li1"><div class="de1"><span class="co1">//</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// - A reference to a ByteArray object in which the extracted raw sound data will be placed,</span></div></li>
<li class="li1"><div class="de1"><span class="co1">//&nbsp; &nbsp; &nbsp; &nbsp;which is the "soundData" byteArray we defined at line 12</span></div></li>
<li class="li1"><div class="de1"><span class="co1">//</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// - A length parameter that specifies number of sample blocks to extract from that Sound</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// &nbsp; object. Since we already know the data will be returned as 32-bit, 44100Hz Stereo, we</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// &nbsp; can easily calculate the number of sound blocks by multiplying the sound length in</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// &nbsp; seconds by the number of samples blocks per second (aka sample rate), which is 44100,</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// &nbsp; but because the "length" property of the Sound object returns a value in milliseconds,</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// &nbsp; we need to do a conversion here dividing it by 1000, so we can get a value in seconds,</span></div></li>
<li class="li1"><div class="de1"><span class="co1">//&nbsp; hence:</span></div></li>
<li class="li1"><div class="de1"><span class="co1">//</span></div></li>
<li class="li1"><div class="de1"><span class="co1">//&nbsp; Total Sample Blocks to Extract = (sound.length/1000)*44100; </span></div></li>
<li class="li1"><div class="de1">sound<span class="sy0">.</span>extract<span class="br0">(</span>soundData<span class="sy0">,</span><a href="http://www.google.com/search?q=math%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:math.html"><span class="kw5">Math</span></a><span class="sy0">.</span><span class="kw7">floor</span><span class="br0">(</span><span class="br0">(</span>sound<span class="sy0">.</span><span class="kw7">length</span><span class="sy0">/</span><span class="nu0">1000</span><span class="br0">)</span><span class="sy0">*</span><span class="nu0">44100</span><span class="br0">)</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// the extract() method places the file pointer at the end of the ByteArray (something not</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// mentioned in the Adobe Flash Documentation). Therefore, we need to reset the file pointer</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// to the beginning of the ByteArray Object, which is position = 0</span></div></li>
<li class="li1"><div class="de1">soundData<span class="sy0">.</span><span class="kw7">position</span> = <span class="nu0">0</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// for drawing purposes, we set our initial X Axis position to 0 and define a variable called</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// "xStep", which determines how many pixels along the X Axis to move with each drawing step.</span></div></li>
<li class="li1"><div class="de1"><span class="co1">//</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// As I have decided to draw lines connecting the minimum and maximum values of every 11025</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// sample blocks (or very 250ms) that means with a "xStep" value of 1 pixel, 250ms = 2 pixels,</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// or 1 pixel = 125ms</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">var</span> xPos<span class="sy0">:</span><a href="http://www.google.com/search?q=uint%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:uint.html"><span class="kw5">uint</span></a> = <span class="nu0">0</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">var</span> xStep<span class="sy0">:</span><a href="http://www.google.com/search?q=uint%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:uint.html"><span class="kw5">uint</span></a> = <span class="nu0">1</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// since the raw sound data is a floating point number between -1 and 1, it will be really hard</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// to notice the fluctuations if we use that number as-is to draw our waveform lines. Therefore</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// we define an "yRatio" variable to expand the visible range of the fluctuations that can be</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// drawn on screen. I chose the number "100" based on error and trial - you can experiment with</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// different numbers.</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">var</span> yRatio<span class="sy0">:</span><a href="http://www.google.com/search?q=uint%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:uint.html"><span class="kw5">uint</span></a> = <span class="nu0">100</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// we loop through the soundData until we have enough bytes to read. We determine the number of</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// bytes left using the "bytesAvailable" property of the "soundData" ByteArray object.</span></div></li>
<li class="li1"><div class="de1"><span class="kw1">while</span><span class="br0">(</span>soundData<span class="sy0">.</span><span class="kw7">bytesAvailable</span> <span class="sy0">&gt;</span> <span class="nu0">88200</span><span class="br0">)</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">var</span> leftMin<span class="sy0">:</span><a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a> = <a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a><span class="sy0">.</span><span class="kw8">MAX_VALUE</span><span class="sy0">;</span> <span class="co1">// a variable to store the minimum value for the Left Channel</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">var</span> leftMax<span class="sy0">:</span><a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a> = <a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a><span class="sy0">.</span><span class="kw8">MIN_VALUE</span><span class="sy0">;</span> <span class="co1">// a variable to store the maximum value for the Left Channel</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">var</span> rightMin<span class="sy0">:</span><a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a> = <a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a><span class="sy0">.</span><span class="kw8">MAX_VALUE</span><span class="sy0">;</span><span class="co1">// a variable to store the minimum value for the Right Channel</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw2">var</span> rightMax<span class="sy0">:</span><a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a> = <a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a><span class="sy0">.</span><span class="kw8">MIN_VALUE</span><span class="sy0">;</span> <span class="co1">// a variable to store the maximum value for the Right Channel</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="kw1">for</span> <span class="br0">(</span><span class="kw2">var</span> i<span class="sy0">:</span><a href="http://www.google.com/search?q=uint%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:uint.html"><span class="kw5">uint</span></a> = <span class="nu0">0</span><span class="sy0">;</span>i<span class="sy0">&lt;</span><span class="nu0">11025</span><span class="sy0">;</span>i<span class="sy0">++</span><span class="br0">)</span> <span class="co1">// analyze every 11025 sample blocks and determine their</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="br0">{</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="co1">// minimum and maximum values</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// we use the "readFloat()" method of the "soundData" ByteArray object to retrieve</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// the raw sound data for the left and right channels. When you call "readFloat()"</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// it retrieves the next 4 bytes from the ByteArray object, converts them to a</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// 32-bit single precision floating point number and moves the file pointer to the</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// next sequence of 4 bytes (not explained in the Adobe Flash Documentation)</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// read raw sound data for left channel (4 bytes/32 bits)</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> leftChannel<span class="sy0">:</span><a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a> = soundData<span class="sy0">.</span><span class="kw7">readFloat</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// read raw sound data for right channel (next 4 bytes/32 bits)</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">var</span> rightChannel<span class="sy0">:</span><a href="http://www.google.com/search?q=number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:number.html"><span class="kw5">Number</span></a> = soundData<span class="sy0">.</span><span class="kw7">readFloat</span><span class="br0">(</span><span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// 4 bytes + 4 bytes = 8 bytes = 1 sample block, remember? :)</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// check if we have a new minumum or maximum values for the left or right channels</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">(</span>leftChannel <span class="sy0">&lt;</span> leftMin<span class="br0">)</span> leftMin = leftChannel<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">(</span>leftChannel <span class="sy0">&gt;</span> leftMax<span class="br0">)</span> leftMax = leftChannel<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">(</span>rightChannel <span class="sy0">&lt;</span> rightMin<span class="br0">)</span> rightMin = rightChannel<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">(</span>rightChannel <span class="sy0">&gt;</span> rightMax<span class="br0">)</span> rightMax = rightChannel<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="br0">}</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="co1">// draw lines connecting the minimum and maximum values of the left and right channels</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; <span class="co1">// to their corresponding sprites.</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; waveformLeft<span class="sy0">.</span><span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">lineTo</span><span class="br0">(</span>xPos<span class="sy0">,</span>leftMin<span class="sy0">*</span>yRatio<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; waveformRight<span class="sy0">.</span><span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">lineTo</span><span class="br0">(</span>xPos<span class="sy0">,</span>rightMin<span class="sy0">*</span>yRatio<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; xPos <span class="sy0">+</span>= xStep<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; waveformLeft<span class="sy0">.</span><span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">lineTo</span><span class="br0">(</span>xPos<span class="sy0">,</span>leftMax<span class="sy0">*</span>yRatio<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; waveformRight<span class="sy0">.</span><span class="kw7">graphics</span><span class="sy0">.</span><span class="kw7">lineTo</span><span class="br0">(</span>xPos<span class="sy0">,</span>rightMax<span class="sy0">*</span>yRatio<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; xPos <span class="sy0">+</span>= xStep<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// at this point the waveforms have been drawn to our left channel and right channel sprites.</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// it's time to position these sprites relative to the Stage and add them to the Stage as well.</span></div></li>
<li class="li1"><div class="de1">waveformLeft<span class="sy0">.</span><span class="kw7">x</span> = <span class="nu0">0</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">waveformLeft<span class="sy0">.</span><span class="kw7">y</span> = <span class="nu0">150</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">waveformRight<span class="sy0">.</span><span class="kw7">x</span> = <span class="nu0">0</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">waveformRight<span class="sy0">.</span><span class="kw7">y</span> = <span class="nu0">450</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">addChild</span><span class="br0">(</span>waveformLeft<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">addChild</span><span class="br0">(</span>waveformRight<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="co1">// and this is just a little extra, use the code as-is, it's not relevant to the actual</span></div></li>
<li class="li1"><div class="de1"><span class="co1">// waveform drawing process. it just enables you to scroll left and right using the</span></div></li>
<li class="li1"><div class="de1"><span class="co1">//  arrow keys, so you can see the entire waveform.</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">var</span> leftKey<span class="sy0">,</span>rightKey<span class="sy0">:</span><a href="http://www.google.com/search?q=boolean%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:boolean.html"><span class="kw5">Boolean</span></a><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw2">var</span> scrollStep<span class="sy0">:</span><a href="http://www.google.com/search?q=uint%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:uint.html"><span class="kw5">uint</span></a> = <span class="nu0">100</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">addEventListener</span><span class="br0">(</span><a href="http://www.google.com/search?q=keyboardevent%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:keyboardevent.html"><span class="kw5">KeyboardEvent</span></a><span class="sy0">.</span><span class="kw8">KEY_DOWN</span><span class="sy0">,</span>OnKeyDown<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">addEventListener</span><span class="br0">(</span><a href="http://www.google.com/search?q=keyboardevent%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:keyboardevent.html"><span class="kw5">KeyboardEvent</span></a><span class="sy0">.</span><span class="kw8">KEY_UP</span><span class="sy0">,</span>OnKeyUp<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1"><span class="kw7">stage</span><span class="sy0">.</span><span class="kw7">addEventListener</span><span class="br0">(</span><a href="http://www.google.com/search?q=event%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:event.html"><span class="kw5">Event</span></a><span class="sy0">.</span><span class="kw8">ENTER_FRAME</span><span class="sy0">,</span>OnEnterFrame<span class="br0">)</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw3">function</span> OnKeyDown<span class="br0">(</span>e<span class="sy0">:</span><a href="http://www.google.com/search?q=keyboardevent%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:keyboardevent.html"><span class="kw5">KeyboardEvent</span></a><span class="br0">)</span><span class="sy0">:</span><span class="kw1">void</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">switch</span><span class="br0">(</span>e<span class="sy0">.</span><span class="kw7">keyCode</span><span class="br0">)</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">39</span><span class="sy0">:</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rightKey = <span class="kw1">true</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; leftKey = <span class="kw1">false</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">break</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">37</span><span class="sy0">:</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rightKey = <span class="kw1">false</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; leftKey = <span class="kw1">true</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">break</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">}</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw3">function</span> OnKeyUp<span class="br0">(</span>e<span class="sy0">:</span><a href="http://www.google.com/search?q=keyboardevent%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:keyboardevent.html"><span class="kw5">KeyboardEvent</span></a><span class="br0">)</span><span class="sy0">:</span><span class="kw1">void</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">switch</span><span class="br0">(</span>e<span class="sy0">.</span><span class="kw7">keyCode</span><span class="br0">)</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">39</span><span class="sy0">:</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; rightKey = <span class="kw1">false</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">break</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">37</span><span class="sy0">:</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; leftKey = <span class="kw1">false</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">break</span><span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">}</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
<li class="li1"><div class="de1">&nbsp;</div></li>
<li class="li1"><div class="de1"><span class="kw3">function</span> OnEnterFrame<span class="br0">(</span>e<span class="sy0">:</span><a href="http://www.google.com/search?q=event%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:event.html"><span class="kw5">Event</span></a><span class="br0">)</span><span class="sy0">:</span><span class="kw1">void</span></div></li>
<li class="li1"><div class="de1"><span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">(</span>rightKey<span class="br0">)</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveformLeft<span class="sy0">.</span><span class="kw7">x</span> <span class="sy0">-</span>= scrollStep<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveformRight<span class="sy0">.</span><span class="kw7">x</span> <span class="sy0">-</span>= scrollStep<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">}</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">(</span>leftKey<span class="br0">)</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">{</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveformLeft<span class="sy0">.</span><span class="kw7">x</span> <span class="sy0">+</span>= scrollStep<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; waveformRight<span class="sy0">.</span><span class="kw7">x</span> <span class="sy0">+</span>= scrollStep<span class="sy0">;</span></div></li>
<li class="li1"><div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">}</span></div></li>
<li class="li1"><div class="de1"><span class="br0">}</span></div></li>
</ol><div class="foot">Parsed in 0.031 seconds at 218.62 KB/s</div></div>
<br/><p><a href="http://www.marinbezhanov.com/temp/waveform.fla" target="_blank">Download the *.fla file</a></p>
<br/><p>For more advanced examples, check these articles:</p>
<br/><p><a href="http://www.bytearray.org/?p=329" target="_blank">Rendering spectrums with Sound.extract()</a></p>
<p><a href="http://blog.efnx.com/?p=75" target="_blank">Plotting a Sound Wave in Flash AS3</a></p>
<br/><p>So that's about it, I hope this tutorial was useful, don't hesitate to write a comment if you have any additional questions or I was unclear at some point.</p>]]>
</description><guid isPermaLink='false'>marin-14-1332694179</guid><pubDate>Sun, 25 Mar 2012 19:49:39 +0300</pubDate></item><item><title>PHP File Migration Tool version 1.3 released!</title><link>http://www.marinbezhanov.com/web-development/13/php-file-migration-tool-version-1-3-released/</link><description>
<![CDATA[<br/><a href="http://codecanyon.net/item/php-download-manager-and-file-migration-tool/1148931?ref=maringtr" target="_blank"><img src="http://3.s3.envato.com/files/20586765/inline.jpg" alt="PHP File Migration Tool"/></a>
<br/><p>Version 1.3 of my <strong>PHP Download Manager</strong> and File Migration tool was released a couple of days ago and is now available for download at <a href="http://codecanyon.net?ref=maringtr" target="_blank">CodeCanyon</a></p>
<br/><p>The new version introduces some exciting new features such as support of multiple file transfer methods (fopen &amp; cURL), list filtering options that allow you to bulk delete unneeded files from your server quickly and easily, and a file browser for easier navigation. There are also a few other minor improvements included in this version (see the <a href="http://codecanyon.net/item/php-download-manager-and-file-migration-tool/1148931?ref=maringtr" target="_blank">Changelog</a> for more info)</p>
<br/><p>For those not familiar to what this application does, the <strong>PHP File Migration</strong> tool is a tool that allows you to transfer files such as images, file archives, text documents etc. from one web server to another, without the need of using a third computer (usually your PC) as a mediator and without having to deal with downloading and installing FTP clients to do the job. This server migration tool is addressed at webmasters and web developers who want to cut down time spent on data migration, so they can concentrate their efforts on more important things concerning their work.</p>
<br/><p>The PHP File Migration Tool will surely save you a lot of extra time if you often have to move content from one CDN to another, or between web servers located in different data centers. If you're still hesitating, you can try the <a href="http://www.marinbezhanov.com/migration-tool/" target="_blank">free online demo</a> and decide if that's the app for you or not. If you have any comments or suggestions - I'd be happy to hear from you!</p>
<br/><p>You can purchase the PHP File Migration Tool here: <a href="http://codecanyon.net/item/web-development-accelerator/1198287?ref=maringtr" target="_blank">Purchase Page</a></p>]]>
</description><guid isPermaLink='false'>marin-13-1331292168</guid><pubDate>Fri, 09 Mar 2012 13:22:48 +0200</pubDate></item><item><title>Web Development Accelerator 1.5 released!</title><link>http://www.marinbezhanov.com/web-development/12/web-development-accelerator-1-5-released/</link><description>
<![CDATA[<br/><a href="http://codecanyon.net/item/web-development-accelerator/1198287?ref=maringtr" target="_blank"><img src="http://1.s3.envato.com/files/18849741/temp.jpg" alt="Web Development Accelerator"/></a>
<br/><p><strong>Web Development Accelerator</strong> version 1.5 is here earlier than expected! My web based code editor now has Intellisense/Autocompletion, as well as in-browser code preview, which plays the role of a sandbox.</p>
<br/><p>If you haven't already purchased a copy, I'd suggest grabbing one from <a href="http://codecanyon.net?ref=maringtr" target="_blank">CodeCanyon</a> - it only costs $7 and you get a lifetime license, which means that you basically get all future version and updates FREE OF CHARGE!</p>
<br/><p>You can purchase Web Development Accelerator here: <a href="http://codecanyon.net/item/web-development-accelerator/1198287?ref=maringtr" target="_blank">Purchase Page</a></p>]]>
</description><guid isPermaLink='false'>marin-12-1329900469</guid><pubDate>Wed, 22 Feb 2012 10:47:49 +0200</pubDate></item><item><title>Web Development Accelerator 1.3 released!</title><link>http://www.marinbezhanov.com/web-development/11/web-development-accelerator-1-3-released/</link><description>
<![CDATA[<br/><a href="http://codecanyon.net/item/web-development-accelerator/1198287?ref=maringtr" target="_blank"><img src="http://0.s3.envato.com/files/17176650/temp.jpg" alt="Web Development Accelerator"/></a>
<br/><p>I'm happy to announce the release of <strong>Web Development Accelerator</strong> version 1.3!</p>
<br/><p><strong>Web Development Accelerator</strong> is a browser-based code editor and snippet repository that allows web developers to increase their productivity by developing directly in their browsers and saving commonly used code snippets for future reference and/or usage in future projects.</p>
<br/><p>The ultimate goal is to create a browser-based code editor that is as good as any other desktop-based code editor out there and I'm sure this will eventually happen with future releases. That's why I'm working hard on improving <strong>Web Development Accelerator</strong> more and more - version 1.3 has numerous improvements compared to 1.0, thanks to the great user feedback I received! Some exciting new features are planned for the next version, which will be labeled 1.5. These include: intellisense, in-browser preview of the code you develop, ability to work on multiple files simultaneously and save them directly to your server, and many, many more...</p>
<br/><p>You can buy a license for <strong>Web Development Accelerator</strong> on <a href="http://codecanyon.net?ref=maringtr" target="_blank">CodeCanyon</a> at: <a href="http://codecanyon.net/item/web-development-accelerator/1198287?ref=maringtr" target="_blank">http://codecanyon.net/item/web-development-accelerator/1198287</a> or at least give the free preview a try.</p>]]>
</description><guid isPermaLink='false'>marin-11-1328182494</guid><pubDate>Thu, 02 Feb 2012 13:34:54 +0200</pubDate></item><item><title>PHP Download Manager ver.1.1 released!</title><link>http://www.marinbezhanov.com/web-development/10/php-download-manager-ver-1-1-released/</link><description>
<![CDATA[<br/><a href="http://codecanyon.net/item/php-download-manager-and-file-migration-tool/1148931?ref=maringtr" target="_blank"><img src="http://3.s3.envato.com/files/13407991/temp02.jpg" alt="PHP Download Manager in action"/></a>
<br/><p>I've managed to release a new product called simply <strong>PHP Download Manager</strong>. It serves as a download manager and file migration tool and allows you to migrate your files directly from one server to another, without having to use your PC as a medium, so hopefully that'll save you some time and make your web development work a bit more efficient.</p>
<br/><p>As always, <strong>PHP Download Manager</strong> is available for sale on <a href="http://codecanyon.net?ref=maringtr" target="_blank">CodeCanyon</a> at: <a href="http://codecanyon.net/item/php-download-manager-and-file-migration-tool/1148931?ref=maringtr" target="_blank">http://codecanyon.net/item/php-download-manager-and-file-migration-tool/1148931</a></p>]]>
</description><guid isPermaLink='false'>marin-10-1325243087</guid><pubDate>Fri, 30 Dec 2011 13:04:47 +0200</pubDate></item><item><title>mySQL Server Load Monitor ver.1.0 released!</title><link>http://www.marinbezhanov.com/web-development/9/mysql-server-load-monitor-ver-1-0-released/</link><description>
<![CDATA[<br/><a href="http://codecanyon.net/item/mysql-server-load-monitor/758406?ref=maringtr" target="_blank"><img src="http://1.s3.envato.com/files/8598492/thumb01.jpg" alt="mySQL Server Load Monitor in action"/></a><br/><p>I'm happy to announce the release of my database server load monitoring app - <b>mySQL Monitor ver.1.0</b>!</p><br/><p><b>mySQL Monitor</b> allows you to monitor the load on your database server in real-time and helps you discover the most problematic areas in your MySQL configuration. The inspiration for developing it came a few months ago, when I was briefly working as a technical support engineer for a popular PHP product that used MySQL extensively and there were so many people experiencing MySQL misconfiguration issues that I felt someone has to do something about it, so <b>mySQL Monitor</b> is my response to people having database server misconfiguration issues.</p><br/><p>The app is available for sale at <a href="http://codecanyon.net?ref=maringtr" target="_blank">CodeCanyon</a> at: 
<a href="http://codecanyon.net/item/mysql-server-load-monitor/758406" target="_blank">http://codecanyon.net/item/mysql-server-load-monitor/758406</a></p>
]]>
</description><guid isPermaLink='false'>marin-9-1321535805</guid><pubDate>Thu, 17 Nov 2011 15:16:45 +0200</pubDate></item></channel>
</rss>