So
Mai
04
2008
Praktische Hashes of Arrays
Einer der recht häufig besuchten Artikel hier im Blog ist Hashes of Arrays oder Hashes of Hashes. Allem Anschein nach besteht noch immer großes Interesse an praktischen Beispielen für deren Anwendung in Perl. Dem soll nun Rechnung getragen werden.
Die Aufgabe:
Meine MP3- (und inzwischen AAC-)Sammlung ist mittlerweile recht groß. Leider sind auch die Lautstärkeschwankungen zwischen zwei Alben ziemlich ausgeprägt, so dass ich mittels aacgain eine Anpassung vornehmen möchte1.
Der Code:
| # | Code |
|---|---|
| 0001 | #!/usr/bin/perl -w |
| 0002 | # aacgain.pl benoetigt ein ausfuehrbares aacgain im pfad |
| 0003 | # v1 - 02.05.2008 - initial release |
| 0004 | print "### aacgain.pl ###\n"; |
| 0005 | use strict; |
| 0006 | use DirHandle; |
| 0007 | use Cwd; |
| 0008 | use File::Find; |
| 0009 | |
| 0010 | # Variablendeklaration |
| 0011 | my ( %directories, @files, $current_directory, $options, $command ); |
| 0012 | |
| 0013 | &show_help |
| 0014 | if $ARGV[0] =~ /^(help|-h|h|--help|\?|-\?)/; # zeige Hilfetext wenn notwendig |
| 0015 | |
| 0016 | @ARGV = qw(.) |
| 0017 | unless @ARGV; # suche im Startverzeichnis, wenn nichts anderes angegeben wurde |
| 0018 | $options = '-a -k'; # Album-Modus, kein clipping, 89db (Standardeinstellung) |
| 0019 | |
| 0020 | # Finde alle Songs im angegebenen Verzeichnis inkl. Unterverzeichnisse |
| 0021 | find( \&find_songs, @ARGV ); |
| 0022 | |
| 0023 | &process_files; |
| 0024 | |
| 0025 | sub find_songs { |
| 0026 | return |
| 0027 | unless /(\.m4a|\.mp3)/; # finde nur Dateien, mit entsprechender Endung |
| 0028 | $current_directory = cwd; |
| 0029 | push( @{ $directories{$current_directory} }, $_ ); |
| 0030 | } |
| 0031 | |
| 0032 | sub process_files { |
| 0033 | foreach my $key ( keys %directories ) { |
| 0034 | @files = qw//; |
| 0035 | foreach ( @{ $directories{$key} } ) { |
| 0036 | push @files, $key . '/' . $_; |
| 0037 | } |
| 0038 | $command = 'aacgain ' . $options; # konstruiere den aacgain-Befehl |
| 0039 | foreach (@files) { |
| 0040 | $command .= ' ' . "\"$_\""; |
| 0041 | } |
| 0042 | print qx($command); # fuehre aacgain aus und zeige die Ausgabe |
| 0043 | } |
| 0044 | } |
| 0045 | |
| 0046 | sub show_help { |
| 0047 | print "aacgain.pl erlaubt die Mehrfachbearbeitung von MP3/AAC-Dateien.\n"; |
| 0048 | print "Hierzu werden alle Unterverzeichnisse nach Dateien durchsucht.\n"; |
| 0049 | print "Songs innerhalb eines Ordners werden als Album behandelt.\n"; |
| 0050 | print "Syntax: perl aacgain.pl <Verzeichnisname>\n"; |
| 0051 | exit; |
| 0052 | } |
| 0053 |
Dies ist ein recht kurzes Beispiel, wie Hashes of Arrays genutzt werden können. Im Hash %directories werden die Verzeichnisse hinterlegt, in denen Songs (mit den Endungen mp3 oder m4a) gefunden wurden. Entscheidend ist Zeile 29, die Speicherung der Suchergebnisse:
Hier werden die gefundenen Songs in ein Array gepusht, welches innerhalb eines Hashes existiert. Der Key des Hashes ist der Speicherort der Musikdateien, Inhalt des Verzechnisses ist ein Array aus den gefundenen Dateinamen.
Die eigentliche Verarbeitung der Songs erfolgt in zwei foreach-Schleifen.
Für jedes Verzeichnis (=Element des Hashes) sollen die darin enthaltenen Dateien (=Elemente des Arrays) verarbeitet werden. Das Kommando hat folgende Syntax:
aacgain -a -k /path/to/song1 /path/to/song2
Um etwaige Leerzeichen im Pfad- oder Songnamen zu unterstützen werden die Dateinamen in Anführungszeichen gesetzt. Eine Rückmeldung von aacgain wird durch die qx in Verbindung mit print ermöglicht.
Damit ich das Programm auch in der nächsten Woche noch verstehen kann, gibt es eine minimale Hilfefunktion und großzügige Kommentare im Quelltext.
1 Ja, mir ist bewusst, dass man dieses Problem auch mit MacMP3Gain lösen kann, aber mit Perl macht es mehr Spaß. aacgain kann man auf dem Mac übrigens mit MacPorts einfach und schnell selbst kompilieren.