scratch/output/Scratch/en/blog/2010-10-14-Fun-with-wav/index.html
2011-12-07 16:40:03 +01:00

506 lines
No EOL
22 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="fr" xml:lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="keywords" content="wav, C, format, programming">
<link rel="shortcut icon" type="image/x-icon" href="/Scratch/img/favicon.ico" />
<link rel="stylesheet" type="text/css" href="/Scratch/assets/css/main.css" />
<link rel="stylesheet" type="text/css" href="/Scratch/css/twilight.css" />
<link rel="stylesheet" type="text/css" href="/Scratch/css/idc.css" />
<link rel="alternate" type="application/rss+xml" title="RSS" href="http://feeds.feedburner.com/yannespositocomen"/>
<link rel="alternate" lang="fr" xml:lang="fr" title="S'amuser avec un .wav" type="text/html" hreflang="fr" href="/Scratch/fr/blog/2010-10-14-Fun-with-wav/" />
<link rel="alternate" lang="en" xml:lang="en" title="Fun with wav" type="text/html" hreflang="en" href="/Scratch/en/blog/2010-10-14-Fun-with-wav/" />
<script type="text/javascript" src="/Scratch/js/jquery-1.3.1.min.js"></script>
<script type="text/javascript" src="/Scratch/js/jquery.cookie.js"></script>
<script type="text/javascript" src="/Scratch/js/index.js"></script>
<!--[if lt IE 9]>
<script src="http://ie7-js.googlecode.com/svn/version/2.1(beta4)/IE9.js"></script>
<![endif]-->
<title>Fun with wav</title>
</head>
<body lang="en" class="article">
<script type="text/javascript">// <![CDATA[
document.write('<div id="blackpage"><img src="/Scratch/img/loading.gif" alt="loading..."/></div>');
// ]]>
</script>
<div id="content">
<div id="choix">
<div class="return"><a href="#entete">&darr; Menu &darr;</a></div>
<div id="choixlang">
<a href="/Scratch/fr/blog/2010-10-14-Fun-with-wav/" onclick="setLanguage('fr')">en Français</a>
</div>
<div class="flush"></div>
</div>
<div id="titre">
<h1>
Fun with wav
</h1>
</div>
<div class="flush"></div>
<div class="flush"></div>
<div id="afterheader">
<div class="corps">
<div class="intro">
<p><span class="sc"><abbr title="Too long; didn't read">tl;dr</abbr>: </span> Played to process a <code>wav</code> file. <code>C</code> was easier and cleaner than Ruby.</p>
<p>edit: I wanted this program to work only on one specific machine (a x86 on a 32 bit Ubuntu). Therefore I didnt had any portability consideration. This is only a <em>hack</em>.</p>
</div>
<p>I had to compute the sum of the absolute values of data of a <code>.wav</code> file.
For efficiency (and fun) reasons, I had chosen <code>C</code> language.</p>
<p>I didnt programmed in <code>C</code> for a long time.
From my memory it was a pain to read and write to files.
But in the end I was really impressed by the code I get.
It was really clean.
This is even more impressive knowing I used mostly low level functions.</p>
<p>A <code>wav</code> file has an header containing many metadata.
This header was optimized to take as few space as possible.
The header is then a block of packed bytes.</p>
<ul>
<li>The 4<sup>th</sup> first bytes must contains <code>RIFF</code> in ASCII,</li>
<li>the following 4<sup>th</sup> Bytes is an 32 bits integer giving the size of the file minus 8, etc…</li>
</ul>
<p>Surprisingly, I believe that reading this kind of file is easier in <code>C</code> than in most higher level language.
Proof: I only have to search on the web the complete header format and write it in a struct.</p>
<pre class="twilight">
struct wavfile
{
char <span class="Storage">id</span>[4]; // should always contain &quot;RIFF&quot;
int totallength; // total file length minus 8
char wavefmt[8]; // should be &quot;WAVEfmt &quot;
int format; // 16 for PCM format
short pcm; // 1 for PCM format
short channels; // channels
int frequency; // sampling frequency
int bytes_per_second;
short bytes_by_capture;
short bits_per_sample;
char data[4]; // should always contain &quot;data&quot;
int bytes_in_data;
};
</pre>
<p>To read this kind of data in Ruby, I certainly had to write a block of code for each element in the struct.
But in <code>C</code> I simply written:</p>
<pre class="twilight">
fread(&amp;header,sizeof(header),1,wav)
</pre>
<p>Only one step to fill my data structure. Magic!</p>
<p>Then, get an int value coded on two Bytes is also not a natural operation for high level language.
In <code>C</code>, to read a sequence of 2 Bytes numbers I only had to write:</p>
<pre class="twilight">
short value=0;
while( fread(&amp;value,sizeof(value),1,wav) ) {
// do something with value
}
</pre>
<p>Finally I ended with the following code. Remark I know the wav format (16 bit / 48000Hz):</p>
<div class="code"><div class="file"><a href="/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.c"> &#x27A5; wavsum.c </a></div><div class="withfile">
<pre class="twilight">
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;stdint.h&gt;
struct wavfile
{
char <span class="Storage">id</span>[4]; // should always contain &quot;RIFF&quot;
int totallength; // total file length minus 8
char wavefmt[8]; // should be &quot;WAVEfmt &quot;
int format; // 16 for PCM format
short pcm; // 1 for PCM format
short channels; // channels
int frequency; // sampling frequency
int bytes_per_second;
short bytes_by_capture;
short bits_per_sample;
char data[4]; // should always contain &quot;data&quot;
int bytes_in_data;
};
int main(int argc, char *argv[]) {
char *filename=argv[1];
FILE *wav = fopen(filename,&quot;rb&quot;);
struct wavfile header;
if ( wav == NULL ) {
fprintf(stderr,&quot;Can't open input file %s&quot;, filename);
exit(1);
}
// read header
if ( fread(&amp;header,sizeof(header),1,wav) &lt; 1 )
{
fprintf(stderr,&quot;Can't read file header\n&quot;);
exit(1);
}
if ( header.<span class="Storage">id</span>[0]&nbsp;!= 'R'
|| header.<span class="Storage">id</span>[1]&nbsp;!= 'I'
|| header.<span class="Storage">id</span>[2]&nbsp;!= 'F'
|| header.<span class="Storage">id</span>[3]&nbsp;!= 'F' ) {
fprintf(stderr,&quot;ERROR: Not wav format\n&quot;);
exit(1);
}
fprintf(stderr,&quot;wav format\n&quot;);
// read data
long sum=0;
short value=0;
while( fread(&amp;value,sizeof(value),1,wav) ) {
// fprintf(stderr,&quot;%d\n&quot;, value);
if (value&lt;0) { value=-value; }
sum += value;
}
printf(&quot;%ld\n&quot;,sum);
exit(0);
}
</pre>
</div></div>
<p>Of course it is only a hack.
But we can see how easy and clean it should be to improve.
As I say often: the right tool for your need instead of the same tool for all your needs.
Because here <code>C</code> is clearly far superior than Ruby to handle this simple tasks.</p>
<p>I am curious to know if somebody know a nice way to do this with Ruby or Python.</p>
<p><em>edit: for compatibility reasons (64bit machines) used <code>int16_t</code> instead of <code>short</code> and <code>int</code> instead of <code>int</code>.</em></p>
<div class="intro">
<p>Edit (2): after most consideration about portability I made an <em>hopefully</em> more portable version.
But I must confess this task was a bit tedious.
The code remain as readable as before.
But I had to use some compiler specific declaration to force the structure to be packed:</p>
<pre class="twilight">
__attribute__((__packed__))
</pre>
<p>Therefore this implementation should for big and little endian architecture.
However, it must be compiled with <code>gcc</code>.
The new code make more tests but still dont use <code>mmap</code>.
Here it is:</p>
</div>
<div class="code"><div class="file"><a href="/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum2.c"> &#x27A5; wavsum2.c </a></div><div class="withfile">
<pre class="twilight">
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt; // for memcmp
#include &lt;stdint.h&gt; // for int16_t and int32_t
struct wavfile
{
char <span class="Storage">id</span>[4]; // should always contain &quot;RIFF&quot;
int32_t totallength; // total file length minus 8
char wavefmt[8]; // should be &quot;WAVEfmt &quot;
int32_t format; // 16 for PCM format
int16_t pcm; // 1 for PCM format
int16_t channels; // channels
int32_t frequency; // sampling frequency
int32_t bytes_per_second;
int16_t bytes_by_capture;
int16_t bits_per_sample;
char data[4]; // should always contain &quot;data&quot;
int32_t bytes_in_data;
} __attribute__((__packed__));
int is_big_endian(void) {
union {
uint32_t i;
char c[4];
} bint = {0x01000000};
return bint.c[0]==1;
}
int main(int argc, char *argv[]) {
char *filename=argv[1];
FILE *wav = fopen(filename,&quot;rb&quot;);
struct wavfile header;
if ( wav == NULL ) {
fprintf(stderr,&quot;Can't open input file %s\n&quot;, filename);
exit(1);
}
// read header
if ( fread(&amp;header,sizeof(header),1,wav) &lt; 1 ) {
fprintf(stderr,&quot;Can't read input file header %s\n&quot;, filename);
exit(1);
}
// if wav file isn't the same endianness than the current environment
// we quit
if ( is_big_endian() ) {
if ( memcmp( header.<span class="Storage">id</span>,&quot;RIFX&quot;, 4)&nbsp;!= 0 ) {
fprintf(stderr,&quot;ERROR: %s is not a big endian wav file\n&quot;, filename);
exit(1);
}
} else {
if ( memcmp( header.<span class="Storage">id</span>,&quot;RIFF&quot;, 4)&nbsp;!= 0 ) {
fprintf(stderr,&quot;ERROR: %s is not a little endian wav file\n&quot;, filename);
exit(1);
}
}
if ( memcmp( header.wavefmt, &quot;WAVEfmt &quot;, 8)&nbsp;!= 0
|| memcmp( header.data, &quot;data&quot;, 4)&nbsp;!= 0
) {
fprintf(stderr,&quot;ERROR: Not wav format\n&quot;);
exit(1);
}
if (header.format&nbsp;!= 16) {
fprintf(stderr,&quot;\nERROR: not 16 bit wav format.&quot;);
exit(1);
}
fprintf(stderr,&quot;format: %d bits&quot;, header.format);
if (header.format == 16) {
fprintf(stderr,&quot;, PCM&quot;);
} else {
fprintf(stderr,&quot;, not PCM (%d)&quot;, header.format);
}
if (header.pcm == 1) {
fprintf(stderr, &quot; uncompressed&quot; );
} else {
fprintf(stderr, &quot; compressed&quot; );
}
fprintf(stderr,&quot;, channel %d&quot;, header.pcm);
fprintf(stderr,&quot;, freq %d&quot;, header.frequency );
fprintf(stderr,&quot;, %d bytes per sec&quot;, header.bytes_per_second );
fprintf(stderr,&quot;, %d bytes by capture&quot;, header.bytes_by_capture );
fprintf(stderr,&quot;, %d bits per sample&quot;, header.bytes_by_capture );
fprintf(stderr,&quot;\n&quot; );
if ( memcmp( header.data, &quot;data&quot;, 4)&nbsp;!= 0 ) {
fprintf(stderr,&quot;ERROR: Prrroblem?\n&quot;);
exit(1);
}
fprintf(stderr,&quot;wav format\n&quot;);
// read data
long long sum=0;
int16_t value;
int i=0;
fprintf(stderr,&quot;---\n&quot;, value);
while( fread(&amp;value,sizeof(value),1,wav) ) {
if (value&lt;0) { value=-value; }
sum += value;
}
printf(&quot;%lld\n&quot;,sum);
exit(0);
}
</pre>
</div></div>
<p><em>Edit(3)</em>:
On <a href="http://reddit.com">reddit</a>
<a href="http://www.reddit.com/user/Bogdanp">Bogdanp</a>
proposed a Python version:</p>
<div class="code"><div class="file"><a href="/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.py"> &#x27A5; wavsum.py </a></div><div class="withfile">
<pre class="twilight">
<span class="Comment"><span class="Comment">#</span>!/usr/bin/env python</span>
<span class="Keyword">from</span> struct <span class="Keyword">import</span> calcsize, unpack
<span class="Keyword">from</span> sys <span class="Keyword">import</span> argv, exit
<span class="Storage">def</span> <span class="Entity">word_iter</span>(<span class="Variable">f</span>):
<span class="Keyword">while</span> <span class="Constant">True</span>:
_bytes <span class="Keyword">=</span> f.read(<span class="Constant">2</span>)
<span class="Keyword">if</span> <span class="SupportFunction">len</span>(_bytes) <span class="Keyword">!=</span> <span class="Constant">2</span>:
<span class="Keyword">raise</span> <span class="Support">StopIteration</span>
<span class="Keyword">yield</span> unpack(<span class="String"><span class="String">&quot;</span>=h<span class="String">&quot;</span></span>, _bytes)[<span class="Constant">0</span>]
<span class="Keyword">try</span>:
<span class="Keyword">with</span> <span class="Support">open</span>(argv[<span class="Constant">1</span>], <span class="String"><span class="String">&quot;</span>rb<span class="String">&quot;</span></span>) <span class="Keyword">as</span> f:
wav <span class="Keyword">=</span> <span class="String"><span class="String">&quot;</span>=4ci8cihhiihh4ci<span class="String">&quot;</span></span>
wav_size <span class="Keyword">=</span> calcsize(wav)
metadata <span class="Keyword">=</span> unpack(wav, f.read(wav_size))
<span class="Keyword">if</span> <span class="String"><span class="String">&quot;</span><span class="String"><span class="String">&quot;</span></span></span>.join(metadata[:<span class="Constant">4</span>]) <span class="Keyword">!=</span> <span class="String"><span class="String">&quot;</span>RIFF<span class="String">&quot;</span></span>:
<span class="Keyword">print</span> <span class="String"><span class="String">&quot;</span>error: not wav file.<span class="String">&quot;</span></span>
exit(<span class="Constant">1</span>)
<span class="Keyword">print</span> <span class="SupportFunction">sum</span>(<span class="SupportFunction">abs</span>(word) <span class="Keyword">for</span> word <span class="Keyword">in</span> word_iter(f))
<span class="Keyword">except</span> <span class="Support">IOError</span>:
<span class="Keyword">print</span> <span class="String"><span class="String">&quot;</span>error: can't open input file '<span class="StringConstant">%s</span>'.<span class="String">&quot;</span></span> <span class="Keyword">%</span> argv[<span class="Constant">1</span>]
exit(<span class="Constant">1</span>)
</pre>
</div></div>
<p>and <a href="http://www.reddit.com/user/luikore">luikore</a>
proposed an impressive Ruby version:</p>
<div class="code"><div class="file"><a href="/Scratch/en/blog/2010-10-14-Fun-with-wav/code/wavsum.rb"> &#x27A5; wavsum.rb </a></div><div class="withfile">
<pre class="twilight">
data <span class="Keyword">=</span> <span class="Variable">ARGF</span>.<span class="Entity">read</span>
keys <span class="Keyword">=</span> <span class="String"><span class="String">%w[</span>id totallength wavefmt format</span>
<span class="String"> pcm channels frequency bytes_per_second</span>
<span class="String"> bytes_by_capture bits_per_sample</span>
<span class="String"> data bytes_in_data sum</span>
<span class="String"> <span class="String">]</span></span>
values <span class="Keyword">=</span> data.<span class="Entity">unpack</span> <span class="String"><span class="String">'</span>Z4 i Z8 i s s i i s s Z4 i s*<span class="String">'</span></span>
sum <span class="Keyword">=</span> values.<span class="Entity">drop</span>(<span class="Constant">12</span>).<span class="Entity">map</span>(<span class="Keyword">&amp;</span><span class="Constant"><span class="Constant">:</span>abs</span>).<span class="Entity">inject</span>(<span class="Constant"><span class="Constant">:</span>+</span>)
keys.<span class="Entity">zip</span>(values.<span class="Entity">take</span>(<span class="Constant">12</span>) <span class="Keyword">&lt;&lt;</span> sum) {|<span class="Variable">k</span>, <span class="Variable">v</span>|
puts <span class="String"><span class="String">&quot;</span><span class="StringEmbeddedSource"><span class="StringEmbeddedSource">#{</span>k<span class="StringEmbeddedSource"><span class="StringEmbeddedSource">.</span><span class="Entity">ljust</span></span> <span class="StringConstant">17</span><span class="StringEmbeddedSource">}</span></span>: <span class="StringEmbeddedSource"><span class="StringEmbeddedSource">#{</span>v<span class="StringEmbeddedSource">}</span></span><span class="String">&quot;</span></span>
}
</pre>
</div></div>
</div>
<div id="choixrss">
<a id="rss" href="http://feeds.feedburner.com/yannespositocomen">
Subscribe
</a>
</div>
<script type="text/javascript">
$(document).ready(function(){
$('#comment').hide();
$('#clickcomment').click(showComments);
});
function showComments() {
$('#comment').show();
$('#clickcomment').fadeOut();
}
document.write('<div id="clickcomment">Comments</div>');
</script>
<div class="flush"></div>
<div class="corps" id="comment">
<h2 class="first">comments</h2>
<noscript>
You must enable javascript to comment.
</noscript>
<script type="text/javascript">
var idcomments_acct = 'a307f0044511ff1b5cfca573fc0a52e7';
var idcomments_post_id = '/Scratch/en/blog/2010-10-14-Fun-with-wav/';
var idcomments_post_url = 'http://yannesposito.com/Scratch/en/blog/2010-10-14-Fun-with-wav/';
</script>
<span id="IDCommentsPostTitle" style="display:none"></span>
<script type='text/javascript' src='/Scratch/js/genericCommentWrapperV2.js'></script>
</div>
<div id="entete" class="corps_spaced">
<div id="liens">
<ul><li><a href="/Scratch/en/">Home</a></li>
<li><a href="/Scratch/en/blog/">Blog</a></li>
<li><a href="/Scratch/en/softwares/">Softwares</a></li>
<li><a href="/Scratch/en/about/">About</a></li></ul>
</div>
<div class="flush"></div>
<hr/>
<div id="next_before_articles">
<div id="previous_articles">
previous entries
<div class="previous_article">
<a href="/Scratch/en/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/"><span class="nicer">«</span>&nbsp;Secure eMail on Mac in few steps</a>
</div>
<div class="previous_article">
<a href="/Scratch/en/blog/2010-10-06-New-Blog-Design-Constraints/"><span class="nicer">«</span>&nbsp;New Blog Design Constraints</a>
</div>
<div class="previous_article">
<a href="/Scratch/en/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/"><span class="nicer">«</span>&nbsp;Use git to calculate trusted mtimes</a>
</div>
</div>
<div id="next_articles">
next entries
<div class="next_article">
<a href="/Scratch/en/blog/2010-10-26-LaTeX-like-macro-and-markdown/">LaTeX like macro for markdown&nbsp;<span class="nicer">»</span></a>
</div>
<div class="next_article">
<a href="/Scratch/en/blog/2011-01-03-Happy-New-Year/">Happy New Year&nbsp;<span class="nicer">»</span></a>
</div>
<div class="next_article">
<a href="/Scratch/en/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/">Why I won't use CoffeeScript (sadly)&nbsp;<span class="nicer">»</span></a>
</div>
</div>
<div class="flush"></div>
</div>
</div>
<div id="bottom">
<div>
<a rel="license" href="http://creativecommons.org/licenses/by-sa/3.0/">Copyright ©, Yann Esposito</a>
</div>
<div id="lastmod">
Created: 10/14/2010
Modified: 11/16/2011
</div>
<div>
Entirely done with
<a href="http://www.vim.org">Vim</a>
and
<a href="http://nanoc.stoneship.org">nanoc</a>
</div>
<div>
<a href="/Scratch/en/validation/">Validation</a>
<a href="http://validator.w3.org/check?uri=referer"> [xhtml] </a>
.
<a href="http://jigsaw.w3.org/css-validator/check/referer?profile=css3"> [css] </a>
.
<a href="http://validator.w3.org/feed/check.cgi?url=http%3A//yannesposito.com/Scratch/en/blog/feed/feed.xml">[rss]</a>
</div>
</div>
<div class="clear"></div>
</div>
</body>
</html>