Retrouverz cet article dans : Linux Magazine 84
Découper des MP3 avec Perl
Définition du besoin
Avec la mode des podcasts (" balladodiffusion " en bon français), il est de plus en plus facile et courant d’avoir des émissions sur son baladeur MP3. Certaines de ces émissions valent la peine de définir une politique de stockage, mais d’autres entrent plus dans un registre de consommation. Pourtant, même ces dernières offrent des moments que l’on souhaite conserver. Par exemple, certains podcasts musicaux offrant des sessions acoustiques d’artistes connus, ce serait intéressant de pouvoir extraire ces quelques morceaux du podcast original. Évidemment, ce genre de manipulation est possible avec un grand nombre de logiciels, dont le plus connu est probablement Audacity. Malheureusement, la procédure est manuelle et, au final, assez fastidieuse si vous devez le faire avec plusieurs fichiers par semaine. L’idéal serait d’avoir un script qui se chargerait de l’opération de découpe. Mais appeler le script à chaque fois que nécessaire n’est pas encore suffisant pour ma paresse : ce que je souhaiterais, c’est lancer le script sur un ensemble de fichiers MP3. Le plus simple pour moi est d’utiliser des fichiers CSV ou un tableur (que ce soit Excel ou encore OpenCalc), afin de saisir les informations nécessaires à l’opération de découpage. En effet, le script ne sera pas utilisé uniquement par moi, mais également par des personnes un peu moins geek. Maintenant que j’ai défini mes besoins, voyons ce que le CPAN peut m’offrir :MP3::Splitterse chargera de la découpe des fichiers MP3 ;Spreadsheet::Readse chargera de lire les informations dans mon fichier tableur afin d’opérer les découpes.
MP3::Splitter
Utilisermp3split(‘monemission.mp3’, [ ‘00:00:01’, 23 ]);va découper le fichier MP3 à partir de la première seconde et va créer un nouveau fichier d’une durée de 23 secondes. J’aurais préféré l’utilisation suivante :
mp3split(‘monemission.mp3’, [ ‘00:00:01’, ‘00:00:24’ ]);c’est-à-dire de donner le début et la fin du morceau souhaité, ainsi la tâche consistant à calculer la durée du morceau échoue à l’ordinateur.
Spreadsheet::Read
Concernant ce module, le fonctionnement est également fort simple. my $ref = ReadData("test.xls"); # Excel
my $ref = ReadData("test.csv"); # CSV
my $ref = ReadData("test.sxc"); # OpenOffice
my $ref = ReadData("test.ods"); # OpenOffice
Une autre fonction utile dans le cas présent est la fonction use Spreadsheet::Read qw( rows ReadData );J’ai constaté que lorsque j’importais la fonction
my $file = ReadData("monfichier.ods");
my @rows = rows($file);
print join(“, “, @rows), “\n”;
Ce code affichera le contenu du fichier "Nom du fichier", "Nom du morceau", "Début du morceau", "Fin du morceau"Voyons maintenant comment lier ces deux modules afin d’obtenir ce que nous souhaitons.
My::MP3::Splitter
Pour ajouter les fonctionnalités souhaitées, et les lier de manière simple, j’ai décidé de créer un objet regroupant le tout. Ainsi, avec un simpleMy::MP3::Splitter->new( "fichier.csv" )->run();l’ensemble du traitement serait lancé. Pour cela, il est encore nécessaire de régler quelques détails :
- Le calcul de la durée afin de pouvoir utiliser
mp3split()de la manière souhaitée ; - Le renommage de fichier, en effet, par défaut,
mp3split()renomme un fichierexemple.mp3en01_exemple.mp3.
Le calcul de la durée
Pour effectuer ce calcul, il suffit simplement de réduire les expressions de début et de fin de morceau en seconde, et de soustraire la valeur obtenue pour la fin du morceau avec la valeur obtenue pour le début du morceau. De cette manière, l’information souhaitée est obtenue. Pour effectuer la conversion entre l’expression des bornes du morceau, je me suis contenté de lire le code utilisé parLe renommage
Pour le renommage, j’ai d’abord pensé à une solution compliquée, pour me rendre compte finalement que la fonction mp3split(
‘test.mp3’,
{ name_callback => sub { ‘monmorceau.mp3’ } },
[ ‘00h01m00’, ‘00h25m00’ ]
);
Le script final
Finalement, notre script est assez simple puisqu’il ressemble à ce qui suit :#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
package My::MP3::Splitter;
use MP3::Splitter;
use Spreadsheet::Read qw( ReadData rows );
use Carp;
sub new {
my $class = shift;
my $self = bless {}, $class;
# on vérifie si l’utilisateur a passé un
# paramètre lors de la création de l’objet
$self->{input_file} = shift if scalar @_ >= 1;
}
sub _process_input_file {
my $self = shift;
if ( -e $self->{input_file} ) {
my $mp3_files = ReadData( $self->{input_file} );
my @files = rows($mp3_files->[1]);
# par souci de documentation, la première ligne des
# fichiers traités sont ignorés, permettant ainsi
# d’indiquer le type de données attendu
shift @files;
foreach my $row (@files) {
# on passe si...
# - cellule vide
next if $row->[0] eq "";
# - le fichier MP3 n’existe pas
next if not -e $row->[0];
# - pas assez d’information
next if scalar @{$row} < 4;
$self->_split_file(@{$row});
}
}
else {
croak “Le fichier $self->{input_file} n’existe pas...”;
}
}
sub _split_file {
my ($self, $mp3_file, $new_file, $begin_part, $end_part) = @_;
my $duration = $self->_compute_duration($begin_part, $end_part);
mp3split($mp3_file, { name_callback => sub { $new_file } },
[ $begin_part, $duration ]);
}
sub _compute_duration {
my ( $self, $begin, $end ) = @_;
my ( $b_hour, $b_min, $b_sec )
= $begin
=~ /^(?:([\d.]+)(?:h|:(?=.*[m:])))?(?:([\d.]+)[m:])?(?:([\d.]+)s?)?$/;
for ( $b_hour, $b_min, $b_sec ) {
next unless defined $_;
/^(\d+\.?|\d*\.\d+)$/;
}
my $begin_total
= ( $b_hour || 0 ) * 3600 + ( $b_min || 0 ) * 60 + ( $b_sec || 0 );
my ( $e_hour, $e_min, $e_sec )
= $end
=~ /^(?:([\d.]+)(?:h|:(?=.*[m:])))?(?:([\d.]+)[m:])?(?:([\d.]+)s?)?$/;
for ( $e_hour, $e_min, $e_sec ) {
next unless defined $_;
/^(\d+\.?|\d*\.\d+)$/;
}
my $end_total
= ( $e_hour || 0 ) * 3600 + ( $e_min || 0 ) * 60 + ( $e_sec || 0 );
return $end_total > $begin_total ? $end_total - $begin_total : 0;
}
sub run {
my ($self) = shift;
if (scalar @_ >= 1) {
# on vérifie si l’utilisateur a spécifié
# un paramètre à la fonction, et le cas
# échéant, on se prépare à traiter ce fichier
$self->{input_file} = shift;
} else {
if (not defined $self->{input_file}) {
# on gère le cas où aucun fichier à
# traiter n’a été spécifié. Que ce
# soit lors de la création de l’objet,
# ou lors de l’appel de la méthode
croak "No input file...\n";
}
}
$self->_process_input_file();
}
package main;
my %conf;
GetOptions( \%conf, "input=s" );
usage() if not exists $conf{input};
My::MP3::Splitter->new( $conf{input} )->run();
sub usage {
die "$0 --input file, or $0 -i file\n";
}





Donnez votre avis
Vous devez avoir ouvert une session pour écrire un commentaire.