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

502 lines
No EOL
23 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/yannespositocomfr"/>
<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>S'amuser avec un .wav</title>
</head>
<body lang="fr" class="article">
<script type="text/javascript">// <![CDATA[
document.write('<div id="blackpage"><img src="/Scratch/img/loading.gif" alt="Chargement en cours..."/></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/en/blog/2010-10-14-Fun-with-wav/" onclick="setLanguage('en')">in English</a>
</div>
<div class="flush"></div>
</div>
<div id="titre">
<h1>
S'amuser avec un .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="Trop long à lire">tlàl</abbr>&nbsp;: </span> Je me suis amusé à lire un fichier <code>wav</code>. Le <code>C</code> fut le langage le mieux adapté à ce traitement. Bien meilleur que Ruby par exemple.</p>
<p>edit: Je voulais que ce programme fonctionne sur une machine spécifique. En aucun cas je ne pensais publier ce code pour une utilisation autre que celle-ci.</p>
</div>
<p>Jai eu besoin de calculer la somme des valeurs absolue des données dun fichier <code>wav</code>.
Pour des raison defficacité (et aussi de fun), jai fait le programme en <code>C</code>.</p>
<p>Celà faisait longtemps que je navais pas programmé en <code>C</code>.
De mémoire il était peu aisé de manipuler des fichiers.
Mais je dois concéder que jai été étonné de la clarté du code que jai obtenu.</p>
<p>Tout dabord, un fichier <code>wav</code> se compose dun entête qui contient pas mal de meta données.
Cet entête a été optimisé pour prendre peu de place.
Donc on discute de lentête avec des nombres doctets&nbsp;:</p>
<ul>
<li>Les 4 premiers octets doivent contenir <code>RIFF</code> en ASCII&nbsp;;</li>
<li>les 4 octects suivant correspondent à un entier codé sur 32 bits qui donne la taille du fichier moins 8 octets. etc..</li>
</ul>
<p>Etonnamment je pense que lire ce type de fichier avec un langage de haut niveau aurait été plus pénible quen C.
La preuve, il ma suffit de chercher sur le net le format complet de lentête et de lécrire dans un 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>Si javais eu à faire ça en Ruby, je pense quil maurait fallu pour chaque bloc de lentête écrire un bout de code de lecture du bon nombre doctets.
Alors quen <code>C</code> il ma suffit décrire: </p>
<pre class="twilight">
fread(&amp;header,sizeof(header),1,wav)
</pre>
<p>Et en une seule étape ma structure de donnée a été remplie avec les valeurs souhaitées. Magique&nbsp;!</p>
<p>Ensuite, récupérer un entier à partir de deux octets nest pas non plus une opération naturelle dans les nouveaux langages de programmation.
Alors quen <code>C</code>. Pour récupérer un entier codé sur 16 bits il suffit décrire&nbsp;:</p>
<pre class="twilight">
short value=0;
while( fread(&amp;value,sizeof(value),1,wav) ) {
// do something with value
}
</pre>
<p>Finallement je suis arrivé au code suivant, sachant que le format de wav était connu, avec notamment échantillonage sur 16 bits en 48000Hz&nbsp;:</p>
<div class="code"><div class="file"><a href="/Scratch/fr/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>Bien entendu ce code nest quun <em>hack</em>.
Mais on voit bien comment on peut facilement améliorer ce code, ajouter des cas possibles par exemple.
Comme je dis souvent&nbsp;: le bon outil pour la bonne tâche.
On voit en effet que pour cette tâche <code>C</code> est bien supérieur à Ruby par exemple.</p>
<p>_màj&nbsp;: pour des raisons de compatibilité (machines 64 bits) jai utilisé <code>int16_t</code> au lieu de <code>short</code> et <code>int</code> au lieu de <code>int</code>.</p>
<p>Je serai curieux de savoir sil existe un manière plus propre en Ruby que je ne connais pas.
Certainement quen Python ça doit être la cas.</p>
<div class="intro">
<p>Màj (2)&nbsp;: après toutes les remarques concernant la portabilité.
Jai fait une nouvelle version qui devrait être plus portable.
Elle fait aussi plus de test pour vérifier le fichier.
Cependant jutilise une assertion spécifique à <code>gcc</code> pour être certain que la structure de donnée nai pas de “trou”&nbsp;:</p>
<pre class="twilight">
__attribute__((__packed__))
</pre>
<p>Le nouveau code nutilise pas mmap et devrait être plus compatible.<br />
Voici le dernier résultat&nbsp;:</p>
</div>
<div class="code"><div class="file"><a href="/Scratch/fr/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>Màj(3)&nbsp;:
Sur <a href="http://reddit.com">reddit</a>
<a href="http://www.reddit.com/user/Bogdanp">Bogdanp</a>
a proposé une version en Python&nbsp;:</p>
<div class="code"><div class="file"><a href="/Scratch/fr/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>et <a href="http://www.reddit.com/user/luikore">luikore</a> a proposé une version Ruby assez impressionnante&nbsp;:</p>
<div class="code"><div class="file"><a href="/Scratch/fr/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/yannespositocomfr">
s'abonner
</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">Commentaires</div>');
</script>
<div class="flush"></div>
<div class="corps" id="comment">
<h2 class="first">commentaires</h2>
<noscript>
Vous devez activer javascript pour commenter.
</noscript>
<script type="text/javascript">
var idcomments_acct = 'a307f0044511ff1b5cfca573fc0a52e7';
var idcomments_post_id = '/Scratch/fr/blog/2010-10-14-Fun-with-wav/';
var idcomments_post_url = 'http://yannesposito.com/Scratch/fr/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/fr/">Bienvenue</a></li>
<li><a href="/Scratch/fr/blog/">Blog</a></li>
<li><a href="/Scratch/fr/softwares/">Softwares</a></li>
<li><a href="/Scratch/fr/about/">À propos</a></li></ul>
</div>
<div class="flush"></div>
<hr/>
<div id="next_before_articles">
<div id="previous_articles">
articles précédents
<div class="previous_article">
<a href="/Scratch/fr/blog/2010-10-10-Secure-eMail-on-Mac-in-few-steps/"><span class="nicer">«</span>&nbsp;Sécurisez vos emails</a>
</div>
<div class="previous_article">
<a href="/Scratch/fr/blog/2010-10-06-New-Blog-Design-Constraints/"><span class="nicer">«</span>&nbsp;Contraintes du design de ce blog</a>
</div>
<div class="previous_article">
<a href="/Scratch/fr/blog/2010-09-02-Use-git-to-calculate-trusted-mtimes/"><span class="nicer">«</span>&nbsp;Utilisation de git pour calculer les mtimes</a>
</div>
</div>
<div id="next_articles">
articles suivants
<div class="next_article">
<a href="/Scratch/fr/blog/2010-10-26-LaTeX-like-macro-and-markdown/">Des macros LaTeX pour markdown&nbsp;<span class="nicer">»</span></a>
</div>
<div class="next_article">
<a href="/Scratch/fr/blog/2011-01-03-Happy-New-Year/">Bonne et heureuse année&nbsp;<span class="nicer">»</span></a>
</div>
<div class="next_article">
<a href="/Scratch/fr/blog/2011-01-03-Why-I-sadly-won-t-use-coffeescript/">Pourquoi je n'utiliserai pas CoffeeScript (malheureusement)&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/deed.fr">Droits de reproduction ©, Yann Esposito</a>
</div>
<div id="lastmod">
Écrit le : 14/10/2010
modifié le : 16/11/2011
</div>
<div>
Site entièrement réalisé avec
<a href="http://www.vim.org">Vim</a>
et
<a href="http://nanoc.stoneship.org">nanoc</a>
</div>
<div>
<a href="/Scratch/fr/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/fr/blog/feed/feed.xml">[rss]</a>
</div>
</div>
<div class="clear"></div>
</div>
</body>
</html>