| 1 |
root |
1.1 |
!init OPT_STYLE="paper" |
| 2 |
|
|
|
| 3 |
|
|
!define DOC_NAME "Was Marc Zuhause macht, wenn ihm langweilig ist!" |
| 4 |
|
|
!define DOC_AUTHOR "Marc Lehmann <pcg@goof.com>" |
| 5 |
|
|
!build_title |
| 6 |
|
|
|
| 7 |
|
|
H1: Marc langweilt sich... |
| 8 |
|
|
|
| 9 |
|
|
immer dann, wenn er wenig Abwechslung hat. Wenn er vor einem (nicht |
| 10 |
|
|
sonderlich lebensbedrohlichem und eigentlich völlig unwichtigem) Problem |
| 11 |
|
|
steht, passiert es bisweilen, daß er versucht, es zu lösen. Allerdings |
| 12 |
|
|
nicht sehr ernsthaft, weshalb die "Lösung" meist ein ganz anderes Problem |
| 13 |
|
|
"löst". Manchmal kommt dabei ein Perl-Modul heraus, und einige davon |
| 14 |
|
|
werden sogar veröffentlicht. Oder auf dem Perl-Workshop 2001 in einer |
| 15 |
|
|
POSTER-Session vorgeführt... |
| 16 |
|
|
|
| 17 |
|
|
H2: PDL::Audio - Tonkrachklang mit Perl! |
| 18 |
|
|
|
| 19 |
|
|
Das Problem.. was war das Problem denn gleich noch.. Ach ja, ich wollte |
| 20 |
|
|
die gesprochenen Texte für meinen Anrufbeantworter nachbearbeiten (damit |
| 21 |
|
|
nicht jeder sofort erkennt, wen er anruft, ist ja schließlich nicht |
| 22 |
|
|
die Aufgabe des Telefons... ;). |
| 23 |
|
|
|
| 24 |
|
|
Zuerst habe ich das mit dem freien Sound-Editor "snd" (tolles Ding!) |
| 25 |
|
|
gemacht, teilweise in Scheme (Marc schüttelt sich). "Warum kann man |
| 26 |
|
|
das nicht in Perl???" schrie ich verzweifelt (ich hätte auch ein |
| 27 |
|
|
Kommandozeilentool genommen, so verzweifelt war ich). Jetzt kann man es, |
| 28 |
|
|
und jede Menge zusätzliches Schnickschnack gibt es auch. |
| 29 |
|
|
|
| 30 |
|
|
H3: Code-Beispiele |
| 31 |
|
|
|
| 32 |
|
|
"The Basics". Audiodateien einlesen, ein Klacks: |
| 33 |
|
|
|
| 34 |
|
|
!block perl |
| 35 |
|
|
use PDL; |
| 36 |
|
|
use PDL::Audio; |
| 37 |
|
|
|
| 38 |
|
|
$wav = raudio "kongas.wav"; # Laden |
| 39 |
|
|
$wav->scale2short->playaudio; # Abspielen |
| 40 |
|
|
$wav->scale2short->playaudio(rate => 1000); # Grausam! |
| 41 |
|
|
waudio $wav, "kongas.au"; # Und wieder Speichern! |
| 42 |
|
|
!endblock |
| 43 |
|
|
|
| 44 |
|
|
Da Audiodaten sehr umfangreich sein können, kommt eine Speicherung als |
| 45 |
|
|
normales Perl-Array nicht in Frage. Zum Glück gibt es {{die}} Lösung zur |
| 46 |
|
|
Speicherung und Verarbeitung großer Datenmengen (Vektoren und Matrizen!) |
| 47 |
|
|
in Perl, die {{Perl Data Language}} (PDL). Damit kann man nicht nur Daten |
| 48 |
|
|
kompakt speichern, sondern auch damit rechnen, z.B. eine Wellenform |
| 49 |
|
|
"normalisieren": |
| 50 |
|
|
|
| 51 |
|
|
!block perl |
| 52 |
|
|
$wav -= min($wav); |
| 53 |
|
|
$wav /= max($wav); |
| 54 |
|
|
$wav = ($wav - 0.5) * 2 |
| 55 |
|
|
!endblock |
| 56 |
|
|
|
| 57 |
|
|
Oder eine Sinus-Schwingung erzeugen: |
| 58 |
|
|
|
| 59 |
|
|
!block perl |
| 60 |
|
|
$sine = sin (zeroes(10000)->xlinvals(0,6.282)); |
| 61 |
|
|
!endblock |
| 62 |
|
|
|
| 63 |
|
|
C<PDL::Audio> hat viele solcher Generatoren für Wellenformen. |
| 64 |
|
|
|
| 65 |
|
|
Und nun: "The Worx". Ok, ich übertreibe, aber es ist nur ein Beispiel: |
| 66 |
|
|
|
| 67 |
|
|
!block perl |
| 68 |
|
|
$snd = raudio "kongas.iff"; |
| 69 |
|
|
$snd *= gen_oscil $snd, 9/$snd->rate; # AM-Modulation "flirren" |
| 70 |
|
|
$snd->scale2short->playaudio; |
| 71 |
|
|
!endblock |
| 72 |
|
|
|
| 73 |
|
|
Oder ein bißchen Audio-Synthese (langweiliger Sinuston): |
| 74 |
|
|
|
| 75 |
|
|
!block perl |
| 76 |
|
|
$envelope = gen_env (22500*2, pdl(0,1,2,15,16), pdl(0,1,0.9,0.7,0)); |
| 77 |
|
|
$snd = $envelope * gen_oscil $envelope, 900/22500; |
| 78 |
|
|
$snd->scale2short->playaudio(rate => 22500); |
| 79 |
|
|
!endblock |
| 80 |
|
|
|
| 81 |
|
|
Raumschift Enterprise läßt grüßen (FM-Synthese): |
| 82 |
|
|
|
| 83 |
|
|
!block perl |
| 84 |
|
|
$env = gen_env (22050*2, pdl(0,1,2,15,16), pdl(0,1,0.9,0.7,0)); |
| 85 |
|
|
my $fm = gen_triangle $env, 16/22500; |
| 86 |
|
|
$fm *= $fm->xlinvals(0,0.1); |
| 87 |
|
|
$snd = $env * gen_oscil $env, 900/22500, 0, $fm; |
| 88 |
|
|
$snd->scale2short->playaudio(rate => 22500); |
| 89 |
|
|
!endblock |
| 90 |
|
|
|
| 91 |
|
|
Oder die original-Strong-Synthese, zum ersten Mal mit PDL! |
| 92 |
|
|
|
| 93 |
|
|
!block perl |
| 94 |
|
|
my $pdl = zeroes 22500*5; # der "Ton" |
| 95 |
|
|
my $freq = int (22500/220); # Grundfrequenz der Saite |
| 96 |
|
|
my $x = $pdl->slice("0:".($freq-1)); $x .= gen_rand $x, 1; |
| 97 |
|
|
# Am Anfang stehen nun Zufallszahlen, es folgt ein |
| 98 |
|
|
# einfacher Filter: |
| 99 |
|
|
$pdl = $pdl->filter_lir(0,1,pdl($freq,$freq+1),pdl(0.5,0.5)); |
| 100 |
|
|
$pdl->scale2short->playuaiod(rate => 22500); |
| 101 |
|
|
!endblock |
| 102 |
|
|
|
| 103 |
|
|
Wer das (und mehr) hören will, braucht entweder PDL::Audio oder den |
| 104 |
|
|
Perl-Workshop ;) |
| 105 |
|
|
|
| 106 |
|
|
H2: RCU - Mein Wecker: "Aus!" |
| 107 |
|
|
|
| 108 |
|
|
Irgendjemand hatte die dumme Idee, sich von seinem Anrufbeantworter |
| 109 |
|
|
wecken zu lassen (der Anrufbeantworter versteht natürlich Perl). Nur |
| 110 |
|
|
wie stellt man das Ding (es steht ziemlich weit weg) wieder ab, ohne |
| 111 |
|
|
aufzustehen? Natürlich per Infrarot-Fernbedienung. Klar, dafür gibt es |
| 112 |
|
|
auch ein Perl-Interface, genannt C<RCU> (für Remote-Control-Unit). Damit |
| 113 |
|
|
kann man sein Programm per Linux-Lircd oder mittels Irman (ein sehr |
| 114 |
|
|
populäres Empfangsgerät für Infrarot-Fernbedienungen) fernsteuern. |
| 115 |
|
|
|
| 116 |
|
|
H3: Beispiel, primitive Variante |
| 117 |
|
|
|
| 118 |
|
|
!block perl |
| 119 |
|
|
use RCU; |
| 120 |
|
|
|
| 121 |
|
|
$rcu = new RCU "RCU:Irman"; |
| 122 |
|
|
|
| 123 |
|
|
while (($keycode, $repeat) = $rcu->get) { |
| 124 |
|
|
print "Taste $keycode wurde empfangen\n"; |
| 125 |
|
|
} |
| 126 |
|
|
!endblock |
| 127 |
|
|
|
| 128 |
|
|
Gibt z.B.: |
| 129 |
|
|
|
| 130 |
|
|
!block verbatim |
| 131 |
|
|
Taste 575000000000 wurde empfangen |
| 132 |
|
|
Taste 55d000000000 wurde empfangen |
| 133 |
|
|
Taste 558000000000 wurde empfangen |
| 134 |
|
|
Taste 558000000000 wurde empfangen |
| 135 |
|
|
!endblock |
| 136 |
|
|
|
| 137 |
|
|
Langweilig. Schlechtes Interface. Aber für Hacks ganz o.k. |
| 138 |
|
|
|
| 139 |
|
|
H3: Beispiel, korrekte Variante |
| 140 |
|
|
|
| 141 |
|
|
Fernbedienungen senden ihre Wiederholungssignale (z.B. wenn man die |
| 142 |
|
|
"Lauter"-Taste lange gedrückt hält) in 0.2s, 0.02, 0.1s oder anderen |
| 143 |
|
|
Intervallen, einen Standard gibt es nicht. Die meisten Programme ändern |
| 144 |
|
|
die Lautstärke jedesmal in festen Schritten, d.h. beim Programmautor |
| 145 |
|
|
klappt's toll, woanders aber weniger, weil es entweder zu langsam oder zu |
| 146 |
|
|
schnell geht. |
| 147 |
|
|
|
| 148 |
|
|
Deshalb gilt: nur Taste-drücken-/Taste-loslassen-Ereignisse sind von |
| 149 |
|
|
Belang, bzw. die Zeit dazwischen. Mit dem C<Event>-Modul ist es einfach: |
| 150 |
|
|
|
| 151 |
|
|
!block perl |
| 152 |
|
|
use Event; |
| 153 |
|
|
use RCU::Event; |
| 154 |
|
|
|
| 155 |
|
|
# ein Timer, der fünfmal pro Sekunde feuert |
| 156 |
|
|
$forward = Event->timer(interval => 0.2, hard => 1, parked => 1, cb => sub { |
| 157 |
|
|
print "Vorwärts, auf, auf!\n"; |
| 158 |
|
|
}); |
| 159 |
|
|
|
| 160 |
|
|
# der "Kontext", in dem die Tasten interpretiert werden |
| 161 |
|
|
my $ctx = new RCU::Context; |
| 162 |
|
|
$ctx->bind( |
| 163 |
|
|
"sony-cd-(\d+)" => sub { print "Taste ($_[0])\n" }, |
| 164 |
|
|
"sony-cd-fwd" => sub { $forward->at($_[1]); $forward->start }, |
| 165 |
|
|
"~sony-cd-fwd" => sub { $forward->stop }, |
| 166 |
|
|
"sony-cd-stop" => sub { Event::unloop }, |
| 167 |
|
|
); |
| 168 |
|
|
|
| 169 |
|
|
# Fernbedienung öffnen |
| 170 |
|
|
$rcu = new RCU::Event "RCU:Irman"; |
| 171 |
|
|
# Startkontext setzen |
| 172 |
|
|
$rcu->set_context($ctx); |
| 173 |
|
|
|
| 174 |
|
|
# und los! |
| 175 |
|
|
Event::loop; |
| 176 |
|
|
!endblock |
| 177 |
|
|
|
| 178 |
|
|
H2: Compress::LZF, wenns mal schnell gehen soll |
| 179 |
|
|
|
| 180 |
|
|
Was macht man, wenn man sehr viele "Datenpäckchen" speichern muss, die |
| 181 |
|
|
alle unabhängig sind, Platz wegnehmen und ziemlich gut komprimierbar sind |
| 182 |
|
|
(bei mir z.B. viele Hashes, die mit C<Storable> behandelt wurden). Klar, |
| 183 |
|
|
man komprimiert sie. Aber irgendwie war da nichts richtig freies ("blöde |
| 184 |
|
|
GPL, ich will das kommerziell ausbeuten!"), nichts richtig schnelles (LZO, |
| 185 |
|
|
na gut, aber das ist so umständlich) und irgendwie alles so groß und |
| 186 |
|
|
kompliziert. |
| 187 |
|
|
|
| 188 |
|
|
C<Compress::LZF> ist da anders, es ist klein: |
| 189 |
|
|
|
| 190 |
|
|
!block perl |
| 191 |
|
|
-rwxr-xr-x 8236 Jan 13 19:36 LZF.so |
| 192 |
|
|
!endblock |
| 193 |
|
|
|
| 194 |
|
|
verglichen mit anderen "Monstern": |
| 195 |
|
|
|
| 196 |
|
|
!block perl |
| 197 |
|
|
-rwxr-xr-x 33417 Oct 18 21:56 Zlib.so |
| 198 |
|
|
-rwxr-xr-x 60926 Nov 5 06:48 libz.so.1.1.3 |
| 199 |
|
|
!endblock |
| 200 |
|
|
|
| 201 |
|
|
Ausserdem ist es schnell (ja, echt!), frei (BSD-Lizenz), einfach zu |
| 202 |
|
|
installieren (keine Library, kein autoconfig-Zeugs), klein, resistent |
| 203 |
|
|
gegen Bitfehler in seinen Datenstrukturen (naja, wers braucht) und |
| 204 |
|
|
komprimiert durchschnittlich halb so gut wie C<gzip -9> (Bei kleinen |
| 205 |
|
|
Blöcken ist es allerdings viel besser ;), man kann nicht alles haben. |
| 206 |
|
|
|
| 207 |
|
|
Und die Bedienung ist nicht der Rede wert: |
| 208 |
|
|
|
| 209 |
|
|
!block perl |
| 210 |
|
|
use Compress::LZF; |
| 211 |
|
|
|
| 212 |
|
|
$meist_kleiner = compress $data; |
| 213 |
|
|
$original = decompress $meist_kleiner; |
| 214 |
|
|
!endblock |
| 215 |
|
|
|
| 216 |
|
|
C<$meist_kleiner> ist auch in Extremfällen maximal ein Byte größer als |
| 217 |
|
|
das Original. |
| 218 |
|
|
|
| 219 |
|
|
H2: Modem-VBox - Habe ich schon von meinem Anrufbeantworter erzählt? |
| 220 |
|
|
|
| 221 |
|
|
Ja, er spricht Perl, und damit auch C<Modem::VBox>, ein Modulchen, |
| 222 |
|
|
mit dem man Anrufbeantworter, Voiceboxen oder anderes programmieren kann |
| 223 |
|
|
(hey, es ist Perl!). Leider wurde es bisher nur mit Linux-ISDN eingesetzt, |
| 224 |
|
|
aber die benutzten AT-Befehle sind eigentlich universell verbreitet. |
| 225 |
|
|
|
| 226 |
|
|
H3: Meine kleine Voicebox |
| 227 |
|
|
|
| 228 |
|
|
Das mitgelieferte Beispielskript ist etwas zu lang (unter anderem |
| 229 |
|
|
verschickt es aufgenommene Gespräche per e-mail) um es ganz abzudrucken, |
| 230 |
|
|
aber in aller Kürze sieht es so aus: |
| 231 |
|
|
|
| 232 |
|
|
!block perl |
| 233 |
|
|
use Modem::VBox; |
| 234 |
|
|
|
| 235 |
|
|
# Wir VBoxen uns durch |
| 236 |
|
|
$v = new Modem::VBox |
| 237 |
|
|
line => $ARGV[0] || "/dev/ttyI1", |
| 238 |
|
|
modeminit => 'ATZ&D2&B512&E9377358S13.4=0S13.2=1S13.6=0+FCLASS=8', |
| 239 |
|
|
rings => 5, # connect after 5 rings |
| 240 |
|
|
connect_cb => \&main_menu; |
| 241 |
|
|
$v->loop; |
| 242 |
|
|
|
| 243 |
|
|
# Das Hauptmenü |
| 244 |
|
|
sub main_menu { |
| 245 |
|
|
my $context = $v->context->set( |
| 246 |
|
|
# Beispiele für die Menüführung |
| 247 |
|
|
"1\$" => [\&record_menu], # Taste "1": Nachricht hinterlassen |
| 248 |
|
|
"2\$" => [\&music_menu], # Taste "2": Musik spielen |
| 249 |
|
|
"[^12]+\$" => \¬_valid,# alles andere ist ganz, ganz falsch |
| 250 |
|
|
); |
| 251 |
|
|
|
| 252 |
|
|
# Wir sagen "Hallo" und vielleicht noch mehr: |
| 253 |
|
|
$v->play_file("$MSG/welcome"); $v->play_pause(0.2); |
| 254 |
|
|
|
| 255 |
|
|
# Aus dieser Schleife lassen wir sie nicht entkommen! |
| 256 |
|
|
while (my $ev = $v->event) { |
| 257 |
|
|
if ($ev->isbreak) { |
| 258 |
|
|
$v->play_flush; |
| 259 |
|
|
$v->play_pause(0.5); |
| 260 |
|
|
$ev->data->[0]->(); |
| 261 |
|
|
return unless $v->connected; |
| 262 |
|
|
} |
| 263 |
|
|
|
| 264 |
|
|
# Wenn nur noch ein Soundfile in der Warteschlange |
| 265 |
|
|
# steht, füge den Hilfetext ("das Menü") an: |
| 266 |
|
|
if ($v->play_count <= 1) { |
| 267 |
|
|
$v->play_file("$MSG/help"); |
| 268 |
|
|
$v->play_pause(1); |
| 269 |
|
|
} |
| 270 |
|
|
} |
| 271 |
|
|
} |
| 272 |
|
|
|
| 273 |
|
|
sub not_valid { |
| 274 |
|
|
my $context = $v->context->set(".*" => sub {}); |
| 275 |
|
|
$v->play_flush; |
| 276 |
|
|
# Das beliebte |
| 277 |
|
|
# AT&T-Multilingual-The-number-you-have-dialed-is-not-valid |
| 278 |
|
|
$v->play_file ("$MSG/number_not_valid"); |
| 279 |
|
|
$v->play_pause (0.4); |
| 280 |
|
|
$v->play_drain; |
| 281 |
|
|
} |
| 282 |
|
|
|
| 283 |
|
|
# usw... |
| 284 |
|
|
!endblock |
| 285 |
|
|
|
| 286 |
|
|
H2: Audio::Play::MPG123 - ich liebe Terminals |
| 287 |
|
|
|
| 288 |
|
|
Es gibt ja sooo tolle mp3-Player. Aber alle machen diese häßlichen |
| 289 |
|
|
"meine-Knöpfe-sind-noch-kleiner"-, "mein-Skin-ist-länger-als-deiner"- |
| 290 |
|
|
und "ich-nehme-gerne-Platz-weg"-Fenster auf. Das mag' ich nicht: Wenn ich |
| 291 |
|
|
Fenster will, mache ich Fenster, wenn ich beim Arbeiten mp3s hören will, |
| 292 |
|
|
muss das per Skript gehen. |
| 293 |
|
|
|
| 294 |
|
|
Das war das Problem... |
| 295 |
|
|
|
| 296 |
|
|
H3: Wie sage ichs mpg123? |
| 297 |
|
|
|
| 298 |
|
|
Das Modul spielt natürlich selbst keine mp3s ab, sondern startet nur (das |
| 299 |
|
|
eigene, gefixte) C<mpg123> nach und bedient es bzw. verarbeitet Ereignisse |
| 300 |
|
|
wie "Lied zu Ende, was nun?". |
| 301 |
|
|
|
| 302 |
|
|
Ein einfaches Abspielprogramm wäre z.B.: |
| 303 |
|
|
|
| 304 |
|
|
!block perl |
| 305 |
|
|
use Audio::Play::MPG123; |
| 306 |
|
|
|
| 307 |
|
|
$player = new Audio::Play::MPG123; |
| 308 |
|
|
$player->load("/root/mp2/Misc/kult.mp3"); |
| 309 |
|
|
print $player->artist,"\n"; |
| 310 |
|
|
$player->poll(1) until $player->state == 0; |
| 311 |
|
|
!endblock |
| 312 |
|
|
|
| 313 |
|
|
Diese bestechende Simplizität, diese Eleganz, mit der es wirklich nur |
| 314 |
|
|
mp3s abspielt, keine Playlists, keine Buttons, nichts. Schließlich muß |
| 315 |
|
|
man ja irgendwie seiner Creativität Ausdruck geben können. (Ich höre |
| 316 |
|
|
lieber auf bevor es allzu peinlich wird). |
| 317 |
|
|
|
| 318 |
|
|
Dieses Modul hat sogar schon ein Problem gelöst (oder bei der Lösung |
| 319 |
|
|
geholfen), bevor es ein Problem war (ganz untypisch für mich): Der |
| 320 |
|
|
Sound-Treiber meines Notebooks mag es gar nicht, wenn er beim Suspend noch |
| 321 |
|
|
geladen ist. Deshalb gibt mein Abspielprogramm das Audio-Device bei einem |
| 322 |
|
|
SIGPWR frei. Außerdem ist es ganz gut, wenn man sein Notebook Zuhause |
| 323 |
|
|
zuklappt, beim Workshop-Vortrag auf und er spielt {{nicht}} automatisch |
| 324 |
|
|
weiter... |
| 325 |
|
|
|
| 326 |
|
|
Und in der Praxis? Wie mir zugetragen wurde, gibt es für die |
| 327 |
|
|
Mitarbeiter von mp3.com einen Service, bei dem man per Web Musiktitel |
| 328 |
|
|
bestellen kann, mit denen dann die Arbeitsräume beschallt werden. Mit |
| 329 |
|
|
C<Audio::Play::MPG123> und C<mpg123> natürlich. |
| 330 |
|
|
|
| 331 |
|
|
H2: Video Capturing mit Video4linux |
| 332 |
|
|
|
| 333 |
|
|
Hier war das Problem, daß ich eine regelmäßige Serie im Fernsehen |
| 334 |
|
|
aufnehmen wollte, die keinen festen Sendetermin hatte. Der Sender hatte |
| 335 |
|
|
auch eine merkwürdige Vorstellung von VPS, also entschloss ich mich, den |
| 336 |
|
|
Anfang der Sendung aufzunehmen und mit einem Skript täglich auf diese |
| 337 |
|
|
Sequenz zu warten. Als es endlich fehlerlos lief, hatte ich alle Folgen |
| 338 |
|
|
gesehen, aber dafür kann man mit Perl jetzt auch Fernsehbilder (oder |
| 339 |
|
|
Digitalkameras) benutzen, zumindest unter Linux. |
| 340 |
|
|
|
| 341 |
|
|
Doch dann entdeckte ich die vertikale Austastlücke. Intercast, Internet, |
| 342 |
|
|
Videotext, PDC, EPG, sogar MP3s... Toll! Genau beschrieben habe ich das |
| 343 |
|
|
in einem längeren Artikel im Perl-Journal bzw. auf meiner Homepage |
| 344 |
|
|
(http://home.plan9.de/). Vorführen werde ich das nicht können, aber wer |
| 345 |
|
|
mich besucht kann sicherlich einiges darüber erfahren... |
| 346 |
|
|
|
| 347 |
|
|
|
| 348 |
|
|
|
| 349 |
|
|
|
| 350 |
|
|
|
| 351 |
|
|
|
| 352 |
|
|
|
| 353 |
|
|
|
| 354 |
|
|
|