Jeder der Kanäle besitzt eigene ``Device-Einstellungen''
(struct audio_info), die die Benutzerprogramme
individuell wählen können (über ioctl() Aufrufe an
/dev/audio und /dev/audioctl; die Zuordnung
audioctl->audio erfolgt über die PIDs der
öffnenden Prozesse. Für unser Device sind relevant
ioctl()Aufrufe absetzen um Lautstärke etc. zu ändern.
Zur Steuerung des Mischvorgangs und der ``globalen'' Lautstärke haben wir ein kleines Tool, ``AudioMuxCtl'', geschrieben, das sowohl Zugriff auf das ``originale'' /dev/audioctl als auch auf unsern Devicetreiber hat (siehe obiges Diagramm).
Das Programm bietet folgende Optionen:
Usage:
audioctl [-help] [-link] [-xlink]
-help gives you this message
-link will only create the link and loop forever, without
the X interface
-xlink start the X interface and link the devices
The default behaviour is to start the X interface and expect
the devices to be linked by an earlier "audioctl -link" call
chwindpa 97
Beabsichtigte Nutzung ist: beim Booten mit
audioctl -link & die Verbindung zwischen
original-Audio-Device und Multiplex-Treiber herstellen, und später
unter X audioctl ohne Parameter starten, um Kontrolle über
die Kanäle und den Mischvorgang zu bekommen.
In unserem Fall handelt es sich um einen ``Multiplexor'' mit
mehreren I_LINK-ioctl()s erfolgen.
Die oberen Queues werden beim Öffnen von /dev/audio als sog. ``Clone-Devices'' angelegt, d.h. jeder öffnende Prozeß bekommt eine eigene Instanz des Stream-Heads geliefert.
In der Put-Prozedur der oberen ioctl() Aufrufe abgefangen (Ausnahme:
AUDIO_DRAIN -- diese Kontrollnachrichten werden erst
bestätigt, wenn alle Daten der Queue bearbeitet sind). Datenpakete
werden von der put-Prozedur in die entsprechende upper-queue
eingehängt. Die von dem Flow-Control Mechanismus angeworfene upper
write service Prozedur macht dann ein qenable() auf die
Ausgabequeue. Die dadurch getriggerte lower write service-Routine
liest dann aus allen upper queues Daten und mischt diese zu jeweils
neuen Paketen zusammen, die ans Audiodevice weitergereicht werden.
Um die Zusammenarbeit mit unserm audioctl-Tool zu
unterstützen haben wir einen zusätzlichen ioctl()
implementiert, mit dem das Device dem Userlevel-Programm
(i.e. audioctl) mitteilt, welche anderen Programme (PID und
Programmname) es geöffnet haben.
Zur Konvertierung der Bitrate benutzen wir den
Bresenham-Algorithmus, der eigentlich zum Zeichnen von Linien gedacht
ist. Er liefert, mittels einer Zustandsvariablen, eine elegante (und
schnelle) Entscheidung, ob ein Sample aus dem gegebenen Eingabestrom
benutzt werden soll bzw. ein ``erdachtes'' Sample eigefügt werden
muß. (Man kann es sich so vorstellen: es soll eine Linie von (0,0)
nach beispielsweise (44100,8000) gezogen werden. Es werden also 44100
``Schritte'' nach rechts gemacht, davon 8000 Schritte nach
rechts+oben. Bei jedem Schritt nach rechts+oben wird ein
``original''-Sample eingefügt).
Die erdachten Samples haben bei uns jeweils den Wert des letzten
eingelesenen Samples, so daß dadurch merkliche Oberwellen auftreten
(der Anti-Aliasing Tiefpaß der Audio-Einheit hat natürlich bei 44.1kHz
Samplingrate einen Durchlaßbereich bis 22kHz, d.h. aus dem
Bereich z.B. 0-8000kHz kommt einiges an Oberwellen durch).
Dieses Problem läßt sich mit einem digitalen Tiefpaß-Filter, der entsprechende Oberwellen aus dem Datenstrom entfernt lösen. Dieser sitzt ebenfalls in der ``generischen Funktion''. Im Idealfall wäre dieser Filter auf die jeweils zu konvertierende Samplingrate zugeschnitten, mangels Prozessorleistung unseres Testrechners (Sun SS10-20) benutzt die neueste Version allerdings eine stark vereinfachte Version (Mittelwert der letzten 11 Samples).
Da nur Rechenzeit benötigt wird, falls überhaupt ein Programm Audiodaten sendet, kann man die Benutzung im ``täglichen'' Betrieb trotzdem verantworten (weitergehende Tests zur Stabilität vorausgesetzt; das Modul macht jedenfalls bisher einen sehr stabilen Eindruck).
und