!init OPT_STYLE="paper" !define DOC_NAME "Was Marc Zuhause macht, wenn ihm langweilig ist!" !define DOC_AUTHOR "Marc Lehmann " !build_title H1: Marc langweilt sich... immer dann, wenn er wenig Abwechslung hat. Wenn er vor einem (nicht sonderlich lebensbedrohlichem und eigentlich völlig unwichtigem) Problem steht, passiert es bisweilen, daß er versucht, es zu lösen. Allerdings nicht sehr ernsthaft, weshalb die "Lösung" meist ein ganz anderes Problem "löst". Manchmal kommt dabei ein Perl-Modul heraus, und einige davon werden sogar veröffentlicht. Oder auf dem Perl-Workshop 2001 in einer POSTER-Session vorgeführt... H2: PDL::Audio - Tonkrachklang mit Perl! Das Problem.. was war das Problem denn gleich noch.. Ach ja, ich wollte die gesprochenen Texte für meinen Anrufbeantworter nachbearbeiten (damit nicht jeder sofort erkennt, wen er anruft, ist ja schließlich nicht die Aufgabe des Telefons... ;). Zuerst habe ich das mit dem freien Sound-Editor "snd" (tolles Ding!) gemacht, teilweise in Scheme (Marc schüttelt sich). "Warum kann man das nicht in Perl???" schrie ich verzweifelt (ich hätte auch ein Kommandozeilentool genommen, so verzweifelt war ich). Jetzt kann man es, und jede Menge zusätzliches Schnickschnack gibt es auch. H3: Code-Beispiele "The Basics". Audiodateien einlesen, ein Klacks: !block perl use PDL; use PDL::Audio; $wav = raudio "kongas.wav"; # Laden $wav->scale2short->playaudio; # Abspielen $wav->scale2short->playaudio(rate => 1000); # Grausam! waudio $wav, "kongas.au"; # Und wieder Speichern! !endblock Da Audiodaten sehr umfangreich sein können, kommt eine Speicherung als normales Perl-Array nicht in Frage. Zum Glück gibt es {{die}} Lösung zur Speicherung und Verarbeitung großer Datenmengen (Vektoren und Matrizen!) in Perl, die {{Perl Data Language}} (PDL). Damit kann man nicht nur Daten kompakt speichern, sondern auch damit rechnen, z.B. eine Wellenform "normalisieren": !block perl $wav -= min($wav); $wav /= max($wav); $wav = ($wav - 0.5) * 2 !endblock Oder eine Sinus-Schwingung erzeugen: !block perl $sine = sin (zeroes(10000)->xlinvals(0,6.282)); !endblock C hat viele solcher Generatoren für Wellenformen. Und nun: "The Worx". Ok, ich übertreibe, aber es ist nur ein Beispiel: !block perl $snd = raudio "kongas.iff"; $snd *= gen_oscil $snd, 9/$snd->rate; # AM-Modulation "flirren" $snd->scale2short->playaudio; !endblock Oder ein bißchen Audio-Synthese (langweiliger Sinuston): !block perl $envelope = gen_env (22500*2, pdl(0,1,2,15,16), pdl(0,1,0.9,0.7,0)); $snd = $envelope * gen_oscil $envelope, 900/22500; $snd->scale2short->playaudio(rate => 22500); !endblock Raumschift Enterprise läßt grüßen (FM-Synthese): !block perl $env = gen_env (22050*2, pdl(0,1,2,15,16), pdl(0,1,0.9,0.7,0)); my $fm = gen_triangle $env, 16/22500; $fm *= $fm->xlinvals(0,0.1); $snd = $env * gen_oscil $env, 900/22500, 0, $fm; $snd->scale2short->playaudio(rate => 22500); !endblock Oder die original-Strong-Synthese, zum ersten Mal mit PDL! !block perl my $pdl = zeroes 22500*5; # der "Ton" my $freq = int (22500/220); # Grundfrequenz der Saite my $x = $pdl->slice("0:".($freq-1)); $x .= gen_rand $x, 1; # Am Anfang stehen nun Zufallszahlen, es folgt ein # einfacher Filter: $pdl = $pdl->filter_lir(0,1,pdl($freq,$freq+1),pdl(0.5,0.5)); $pdl->scale2short->playuaiod(rate => 22500); !endblock Wer das (und mehr) hören will, braucht entweder PDL::Audio oder den Perl-Workshop ;) H2: RCU - Mein Wecker: "Aus!" Irgendjemand hatte die dumme Idee, sich von seinem Anrufbeantworter wecken zu lassen (der Anrufbeantworter versteht natürlich Perl). Nur wie stellt man das Ding (es steht ziemlich weit weg) wieder ab, ohne aufzustehen? Natürlich per Infrarot-Fernbedienung. Klar, dafür gibt es auch ein Perl-Interface, genannt C (für Remote-Control-Unit). Damit kann man sein Programm per Linux-Lircd oder mittels Irman (ein sehr populäres Empfangsgerät für Infrarot-Fernbedienungen) fernsteuern. H3: Beispiel, primitive Variante !block perl use RCU; $rcu = new RCU "RCU:Irman"; while (($keycode, $repeat) = $rcu->get) { print "Taste $keycode wurde empfangen\n"; } !endblock Gibt z.B.: !block verbatim Taste 575000000000 wurde empfangen Taste 55d000000000 wurde empfangen Taste 558000000000 wurde empfangen Taste 558000000000 wurde empfangen !endblock Langweilig. Schlechtes Interface. Aber für Hacks ganz o.k. H3: Beispiel, korrekte Variante Fernbedienungen senden ihre Wiederholungssignale (z.B. wenn man die "Lauter"-Taste lange gedrückt hält) in 0.2s, 0.02, 0.1s oder anderen Intervallen, einen Standard gibt es nicht. Die meisten Programme ändern die Lautstärke jedesmal in festen Schritten, d.h. beim Programmautor klappt's toll, woanders aber weniger, weil es entweder zu langsam oder zu schnell geht. Deshalb gilt: nur Taste-drücken-/Taste-loslassen-Ereignisse sind von Belang, bzw. die Zeit dazwischen. Mit dem C-Modul ist es einfach: !block perl use Event; use RCU::Event; # ein Timer, der fünfmal pro Sekunde feuert $forward = Event->timer(interval => 0.2, hard => 1, parked => 1, cb => sub { print "Vorwärts, auf, auf!\n"; }); # der "Kontext", in dem die Tasten interpretiert werden my $ctx = new RCU::Context; $ctx->bind( "sony-cd-(\d+)" => sub { print "Taste ($_[0])\n" }, "sony-cd-fwd" => sub { $forward->at($_[1]); $forward->start }, "~sony-cd-fwd" => sub { $forward->stop }, "sony-cd-stop" => sub { Event::unloop }, ); # Fernbedienung öffnen $rcu = new RCU::Event "RCU:Irman"; # Startkontext setzen $rcu->set_context($ctx); # und los! Event::loop; !endblock H2: Compress::LZF, wenns mal schnell gehen soll Was macht man, wenn man sehr viele "Datenpäckchen" speichern muss, die alle unabhängig sind, Platz wegnehmen und ziemlich gut komprimierbar sind (bei mir z.B. viele Hashes, die mit C behandelt wurden). Klar, man komprimiert sie. Aber irgendwie war da nichts richtig freies ("blöde GPL, ich will das kommerziell ausbeuten!"), nichts richtig schnelles (LZO, na gut, aber das ist so umständlich) und irgendwie alles so groß und kompliziert. C ist da anders, es ist klein: !block perl -rwxr-xr-x 8236 Jan 13 19:36 LZF.so !endblock verglichen mit anderen "Monstern": !block perl -rwxr-xr-x 33417 Oct 18 21:56 Zlib.so -rwxr-xr-x 60926 Nov 5 06:48 libz.so.1.1.3 !endblock Ausserdem ist es schnell (ja, echt!), frei (BSD-Lizenz), einfach zu installieren (keine Library, kein autoconfig-Zeugs), klein, resistent gegen Bitfehler in seinen Datenstrukturen (naja, wers braucht) und komprimiert durchschnittlich halb so gut wie C (Bei kleinen Blöcken ist es allerdings viel besser ;), man kann nicht alles haben. Und die Bedienung ist nicht der Rede wert: !block perl use Compress::LZF; $meist_kleiner = compress $data; $original = decompress $meist_kleiner; !endblock C<$meist_kleiner> ist auch in Extremfällen maximal ein Byte größer als das Original. H2: Modem-VBox - Habe ich schon von meinem Anrufbeantworter erzählt? Ja, er spricht Perl, und damit auch C, ein Modulchen, mit dem man Anrufbeantworter, Voiceboxen oder anderes programmieren kann (hey, es ist Perl!). Leider wurde es bisher nur mit Linux-ISDN eingesetzt, aber die benutzten AT-Befehle sind eigentlich universell verbreitet. H3: Meine kleine Voicebox Das mitgelieferte Beispielskript ist etwas zu lang (unter anderem verschickt es aufgenommene Gespräche per e-mail) um es ganz abzudrucken, aber in aller Kürze sieht es so aus: !block perl use Modem::VBox; # Wir VBoxen uns durch $v = new Modem::VBox line => $ARGV[0] || "/dev/ttyI1", modeminit => 'ATZ&D2&B512&E9377358S13.4=0S13.2=1S13.6=0+FCLASS=8', rings => 5, # connect after 5 rings connect_cb => \&main_menu; $v->loop; # Das Hauptmenü sub main_menu { my $context = $v->context->set( # Beispiele für die Menüführung "1\$" => [\&record_menu], # Taste "1": Nachricht hinterlassen "2\$" => [\&music_menu], # Taste "2": Musik spielen "[^12]+\$" => \¬_valid,# alles andere ist ganz, ganz falsch ); # Wir sagen "Hallo" und vielleicht noch mehr: $v->play_file("$MSG/welcome"); $v->play_pause(0.2); # Aus dieser Schleife lassen wir sie nicht entkommen! while (my $ev = $v->event) { if ($ev->isbreak) { $v->play_flush; $v->play_pause(0.5); $ev->data->[0]->(); return unless $v->connected; } # Wenn nur noch ein Soundfile in der Warteschlange # steht, füge den Hilfetext ("das Menü") an: if ($v->play_count <= 1) { $v->play_file("$MSG/help"); $v->play_pause(1); } } } sub not_valid { my $context = $v->context->set(".*" => sub {}); $v->play_flush; # Das beliebte # AT&T-Multilingual-The-number-you-have-dialed-is-not-valid $v->play_file ("$MSG/number_not_valid"); $v->play_pause (0.4); $v->play_drain; } # usw... !endblock H2: Audio::Play::MPG123 - ich liebe Terminals Es gibt ja sooo tolle mp3-Player. Aber alle machen diese häßlichen "meine-Knöpfe-sind-noch-kleiner"-, "mein-Skin-ist-länger-als-deiner"- und "ich-nehme-gerne-Platz-weg"-Fenster auf. Das mag' ich nicht: Wenn ich Fenster will, mache ich Fenster, wenn ich beim Arbeiten mp3s hören will, muss das per Skript gehen. Das war das Problem... H3: Wie sage ichs mpg123? Das Modul spielt natürlich selbst keine mp3s ab, sondern startet nur (das eigene, gefixte) C nach und bedient es bzw. verarbeitet Ereignisse wie "Lied zu Ende, was nun?". Ein einfaches Abspielprogramm wäre z.B.: !block perl use Audio::Play::MPG123; $player = new Audio::Play::MPG123; $player->load("/root/mp2/Misc/kult.mp3"); print $player->artist,"\n"; $player->poll(1) until $player->state == 0; !endblock Diese bestechende Simplizität, diese Eleganz, mit der es wirklich nur mp3s abspielt, keine Playlists, keine Buttons, nichts. Schließlich muß man ja irgendwie seiner Creativität Ausdruck geben können. (Ich höre lieber auf bevor es allzu peinlich wird). Dieses Modul hat sogar schon ein Problem gelöst (oder bei der Lösung geholfen), bevor es ein Problem war (ganz untypisch für mich): Der Sound-Treiber meines Notebooks mag es gar nicht, wenn er beim Suspend noch geladen ist. Deshalb gibt mein Abspielprogramm das Audio-Device bei einem SIGPWR frei. Außerdem ist es ganz gut, wenn man sein Notebook Zuhause zuklappt, beim Workshop-Vortrag auf und er spielt {{nicht}} automatisch weiter... Und in der Praxis? Wie mir zugetragen wurde, gibt es für die Mitarbeiter von mp3.com einen Service, bei dem man per Web Musiktitel bestellen kann, mit denen dann die Arbeitsräume beschallt werden. Mit C und C natürlich. H2: Video Capturing mit Video4linux Hier war das Problem, daß ich eine regelmäßige Serie im Fernsehen aufnehmen wollte, die keinen festen Sendetermin hatte. Der Sender hatte auch eine merkwürdige Vorstellung von VPS, also entschloss ich mich, den Anfang der Sendung aufzunehmen und mit einem Skript täglich auf diese Sequenz zu warten. Als es endlich fehlerlos lief, hatte ich alle Folgen gesehen, aber dafür kann man mit Perl jetzt auch Fernsehbilder (oder Digitalkameras) benutzen, zumindest unter Linux. Doch dann entdeckte ich die vertikale Austastlücke. Intercast, Internet, Videotext, PDC, EPG, sogar MP3s... Toll! Genau beschrieben habe ich das in einem längeren Artikel im Perl-Journal bzw. auf meiner Homepage (http://home.plan9.de/). Vorführen werde ich das nicht können, aber wer mich besucht kann sicherlich einiges darüber erfahren...