Der echte Haino: Harddisk-Rekorder für den Falcon030

In der letzten Ausgabe hatten wir uns schon mit dem Sound-Subsystem des Falcon 030 beschäftigt und alle neuen XBIOS-Funktionen besprochen. Die Einbindung in die Programmiersprache C ist damit schon abgehandelt, aber was ist mit dem guten alten GFA-BASIC? Kann man auch damit das Sound-Subsystem des Falcon ansprechen? Die Antwort lautet: „Ja, man kann!“. Wir wollen im folgenden anhand des Beispiels eines einfachen Harddisk-Rekorders aufzeigen, daß auch den (leider oft belächelten) GFA-BASIC-Programmierern die tollen Sound-Fähigkeiten des Falcon 030 nicht verschlossen bleiben müssen.

Voraussetzung ist natürlich ein auf dem Falcon 030 funktionierender GFA-BASIC-Interpreter. Die Version 3.6-TT tut tatsächlich (fast) einwandfrei ihre Dienste. Man sollte den Interpreter jedoch nicht in dem 256- oder 32768-Farben-Modus des Falcon starten, dort gibt es leider nur Bildschirmmüll. In 16 Farben dagegen funktioniert alles korrekt. Ein kleiner Hinweis noch am Rande: Der Befehl TT? liefert beim Falcon eine 3, was auf einen TT mit FPU schließen läßt. Im Falcon ist aber serienmäßig keine FPU eingebaut. Die Ausführung dieses Befehls auf einem Falcon 030 ohne FPU führt also dazu, daß GFA-BASIC seine eigenen Floating-Point-Routinen patcht und FPU-Kommandos einfügt. Das gibt natürlich eine bombige Stimmung beim ersten Aufruf einer Fließkommaoperation. TT? sollte man also auf jeden Fall meiden bzw. vorher den _FPU-Cookie abfragen, um sicherzugehen, daß auch tatsächlich ein Coprozessor vorhanden ist.

Ans Eingemachte

Die Basis für die Verwirklichung des Harddisk-Rekorders, den wir auf den schönen Namen „Haino“ getauft haben (Ähnlichkeiten mit lebenden Personen sind wirklich rein zufällig), ist das XBIOS-Interface (Listing, ab Zeile 116), das alle neuen Funktionen des Falcon-Sound-Subsystems mit ihren offiziellen Namen (Quelle: [1]) in GFA-BASIC zur Verfügung stellt. Die Funktionen an sich wurden bereits in der letzten Ausgabe (Digitaloszilloskop) besprochen. Hinzu kommen noch eine Prozedur, in der diverse Konstanten (als GFA-BASIC-Variablen) definiert werden, und jeweils ein Unterprogramm zum Auslesen und Wiederherstellen des momentanen Registerzustandes des CODEC-Chips. Dieser Chip ist bekanntlich für die Digital/ Analog- undAnalog/Digital-Wandlung im Sound-Subsystem verantwortlich. Jedes Programm, das den CODEC benutzt, sollte die vorherigen Inhalte der Register retten und nach Beendigung wieder zurückschreiben, um Probleme mit anderen Applikationen, die ebenfalls das Sound-Subsystem benutzen, zu vermeiden. Dies kann man (glücklicherweise) mit Betriebssystemfunktionen durchführen, ein direkter Registerzugriff ist also nicht notwendig.

Das Listing

Das abgedruckte Listing stellt lediglich die Grundroutinen für einen Harddisk-Rekorder dar, ist aber schon als eigenständiges Programm lauffähig. Jeder kann selbst seine individuelle Bedieneroberfläche drumherum programmieren. Auf der Monatsdiskette befindet sich allerdings zusätzlich die Vollversion von Haino, die bereits mit einer kompletten GEM-Bedienung ausgestattet ist. Mit der Variablen vorteiler& wird die Sample-Frequenz eingestellt. Beachten Sie dazu die Tabelle 1. Input- und Output-Level (input_level&, output_level&) sind als lokale Variablen in der Aufnahme- bzw. Wiedergabeprozedur definiert.

Der Puffer für die Sample-Daten wird fortlaufend in einer Schleife beschrieben bzw. gelesen.

Der Ablauf

„Direct-to-Harddisk-Recording“ bedeutet, daß die analog/digitalgewandelten Daten, noch während die Aufnahme läuft, auf der Platte abgelegt bzw. direkt von der Festplatte abgespielt werden. Doch wie läuft nun der eigentliche Aufnahme- und Wiedergabevorgang ab? Zunächst wird ein Puffer angelegt, der im Loop-Modus ständig „besamplet“ wird. Da der CODEC seine Sample-Daten per DMA-Zugriff, also ohne Zutun der CPU, in den Speicher schiebt, kann das Programm in Ruhe abwarten, bis die Hälfte des Puffers beschrieben worden ist, hierzu existiert die Funktion buffptr (XBIOS &8D). Sie liefert die Adresse der Speicherzelle, die im Augenblick vom CODEC beschrieben wird. Hat dieser Zeiger die Hälfte des Puffers überschritten, kann das Programm die erste Hälfte gemütlich auf die Festplatte schreiben. Währenddessen samplet der CODEC lustig weiter die zweite Hälfte des Puffers voll. Dabei muß das Programm abwarten, bis der Schreibzeiger wieder am Anfang des Puffers steht, der CODEC also beginnt, den Speicherbereich wieder zu überschreiben. Erst jetzt wird die zweite Hälfte des Puffers gespeichert. Danach wiederholt sich der Vorgang, bis man per Maustaste abbricht oder die Platte voll ist. Auf der Festplatte entsteht so eine Datei mit ununterbrochenen Sample-Daten. Das ganze funktioniert allerdings nur dann, wenn die Platte eine Pufferhälfte schneller ablegen kann, als der CODEC die jeweils andere mit Daten füllt. Die eingebaute AT-Bus-Festplatte im Falcon hat sich als durchaus geeignet dafür erwiesen. Bereits ab 65 KB Puffergröße gibt es keine Probleme, was das Programm besonders RAM-speicherplatzfreundlich macht. Ganz anders natürlich sieht das mit dem Speicherplatz auf der Festplatte aus. Innerhalb kürzester Zeit wächst die Sample-Datei (in Abhängigkeit von der Sample-Frequenz) stark an. Die 65 MB der internen Platte sind in ca. 4 Minuten (bei 50 KHz-Stereo) randvoll (sofern man sie in eine einzige Partion aufgeteilt hat). Der Wiedergabevorgang läuft quasi identisch ab, mit dem Unterschied, daß der Puffer zunächst mit den Daten von der Festplatte gefüllt und dann die jeweilige Pufferhälfte abgespielt wird. Bild 2 zeigt noch einmal grafisch den Aufbau des Sample-Puffers.

In der Zwischenzeit

Während das Programm den CODEC-Schreib-/Lesezeiger zyklisch abfragt (also eigentlich die Zeit mit Warten vertrödelt), könnte es natürlich noch eine Menge anderer Dinge tun. Haino stellt zum Beispiel noch einen Stereo-Peak-Level-Indikator mit gedämpftem Rücklauf dar, gibt die Sample-Zeit und einen Balken für den restlichen Platz auf der Festplatte aus. Das alles findet parallel zum eigentlichen Sample-Vorgang statt, der davon völlig unberührt bleibt. Sogar bei 50 KHz Stereo-Sampling kommt der Rechner nicht aus dem Tritt. Experimente mit einer 105-Megabyte-SCSI-Festplatte haben ergeben, daß sie für ca. 11 Minuten Platz für Stereo-Samples in CD-Qualität bietet. Fatal kann sich allerdings die bei einigen Platten häufiger auftretende Rekalibrierung auswirken. Dabei stellt sich der Schreib-/Lese-kopf der Festplatte in regelmäßigen Abständen neu ein, so daß das Speichermedium für ca. 0.5 bis 1 Sekunde nicht in der vollen Geschwindigkeit ansprechbar ist. Manchmal kann dieser Effekt dazu führen, daß der Fluß der Sample-Daten kurzzeitig unterbrochen wird. Verständlich, daß Diskettenlaufwerke gar keine Chance haben. Selbst bei niedrigster Sample-Frequenz und Verwendung von HD-Disketten reicht die Geschwindigkeit des Floppy-Laufwerkes nicht aus. Eine Festplatte muß es also schon sein.

Vorteiler Sample-Frequenz
0 STE-Kompatibilitätsmodus
1 49170
2 33880
3 24585
4 20770
5 16490
6 nicht erlaubt
7 12292
8 nicht erlaubt
9 9834
10 nicht erlaubt
11 8195
12 nicht erlaubt
13 nicht erlaubt
14 nicht erlaubt
15 nicht erlaubt

Tabelle 1

Und der DSP?

Sie haben sicherlich schon gemerkt, daß der DSP-56K im Falcon 030 bei der ganzen Angelegenheit noch nicht einen Handschlag getan hat. Er wird in der aktuellen Haino-Version überhaupt nicht benötigt. Ein mögliches Einsatzfeld für den Signalprozessor wäre eine Online-Kompression der Sample-Daten während der Aufnahme bzw. Dekompression bei der Wiedergabe. Da auch der DSP per DMA auf die Sample-Daten zugreifen kann, würde dafür nicht einmal Rechenzeit von der 68030-CPU benötigt. Vielleicht hat ja der ein oder andere Leser bereits eine Kompressionsroutine für den DSP geschrieben, die sich zu diesem Zweck eignen würde. Wir würden dies gern in einer der nächsten Ausgaben veröffentlichen.

CM

Literatur:

[1] Hendricks, Herzlinger, Pittelkow - Das Buch zum ATARI Falcon 030, Data Becker


' Direct-to-Harddisk-Sampling mit dem FALCON 030 
' Basisroutinen 30.09.1992 
' (c)1992 by MAXON-Computer
' Autor: Christian Möller •
'
$m100000 
RESERVE 100000
'
init_sound_data !*** Konstanten definieren 
get_codec_data !*** momentane Codec-Werte sichern 
sound_buffer_len%=65536 
half_buffer%=sound_buffer_len% DIV 2 
sound_buffer_adr%=MALLOC(sound_buffer_len%) 
vorteiler&=2 !*** 33KHz Samplefrequenz 
hd_record !*** Aufnahme starten
hd_stop ! *** Aufnahme stoppen
restore_codec_data !*** Codec-Werte zuruckschreiben 
hd_play !*** Wiedergabe starten
hd_stop !*** Wiedergabe stoppen
restore codec_data !*** Codec-Werte zuruckschreiben 
~MFREE(sound_buffer_adr%)
'
RESERVE !*** Speicher freigeben
END !*** Schluß
'
PROCEDURE hd_stop
    ' *** Aufnahme/Wiedergabe stoppen 
    ~@buffoper(0)
RETURN
PROCEDURE hd_record 
    LOCAL datei$,handles 
    LOCAL output_level&,input_level&
    '
    output_level&=0 !*** 0 dB Abschwächung 
    input_level&=15 !*** 0 dB Eingangsempfindlichkeit
    '
    PRINT "Aufnahme läuft!" 
    datei$="SAMPLE.SMP"+CHR$(0)
    handle&=GEMDOS(60, L: V: datei$, 0) !*** Datei Öffnen
    '
    ~@soundcmd(ltatten_6t,SHL(output_level&,4)) 
    ~@soundcmd(rtatten_St,SHL(output_level&,4)) 
    ~Gsoundcmd(ltgain_&,SHL(input_level&,4)) 
    ~@soundcmd(rtgain_&,SHL(input_level&,4))
    ~@soundcmd(adderin_&, 1)
    ~@soundcmd(adcinput_&, 0) !*** input über ADC 
    ~@setbuffer(1,sound_buffer_adr%,sound_buffer_adr%+sound_buffer_len%+1)
    ~@setsndmode(1) !*** 16 Bit Stereo 
    ~@settrack(0,0) !*** Kanal 0 setzen 
    ~@setmontrack(0) !*** Lautsprecher spielt mit 
		' *** Aufnahme im Loop-Modus 
    ~@devconnect(adc_&,dmarec_&,0,vorteiler&, 1) 
    ~@buffoper(12) !*** und los gehts!
    '
    ' Diese Routine speichert während der Aufnahme 
    ' schon einen Teil des Samples ab.
    '
    REPEAT
        REPEAT
            ~@buffptr(V:buffpointer%(0))
        UNTIL buffpointer%(1)>=sound_buffer_adr%+half_buffer%
        ~GEMDOS(64,handles,L:half_buffer%,L:sound_buffer_adr%)
        REPEAT
            ~@buffptr(V:buffpointer%(0))
        UNTIL buffpointer%(1)<sound_buffer_adr%+half_buffer%
        ~GEMDOS(64,handles,L:half_buffer%,L:sound_buffer_adr%+half_buffer%)
    UNTIL MOUSEK !*** Abbrechen mit Maustaste 
    ~GEMDOS(62,handlest) !*** Datei schließen 
RETURN
PROCEDURE hd_play
    LOCAL datei$,handle&, smp_len%
    LOCAL anz_buffer%,buffer_count%,output_level&
    '
    PRINT "Wiedergabe läuft!"
    output_level&=0 !*** 0 dB Abschwächung
    '
    OPEN "i",#1,"SAMPLE.SMP" 
    smp_len%=LOF(#1) !*** Länge des Samples 
    CLOSE #1
    anz_buffer%=smp_len% DIV sound_buffer_len% 
    '
    datei$="SAMPLE.SMP"+CHR$(0)
    handle&=GEMDOS(61,L:V:datei$,0) !*** Datei öffnen 
    ' *** Puffer erst komplett füllen 
    ~GEMDOS(63,handle&,L:sound_buffer_len%, L: sound_buffer_adr%) 
    buffer.count%=1
    '
    ~@soundcmd(ltatten_&,SHL(output_level&,4)) 
    ~@soundcmd(rtatten_&,SHL(output_level&,4))
    ~@setbuffer(0,sound_buffer_adr%,sound_buffer_adr%+sound_buffer_len%+1)
    ~@setsndmode(1) !*** 16 Bit Stereo
    ~@settrack(0,0) !*** Kanal 0 setzen
    ~@setmontrack(0) !*** Lautsprecher läuft mit
    ' *** Wiedergabe im Loop-Modus 
    ~@devconnect(dmaplay_&,dac_&,0,vorteiler&,1) 
    ~@buffoper(3) !*** und los gehts!
    '
    ' *** Diese Routine holt während das Sample spielt 
    ' *** schon die nächsten Daten.
    '
    REPEAT
        REPEAT
            ~@buffptr(V:buffpointer%(0)) !*** Zeiger holen
        UNTIL buffpointer%(0)>=sound_buffer_adr%+half_buffer%
        ' *** halben Puffer holen 
        ~GEMDOS(63,handle&,L: half_buffer%,L: sound_buffer_adr%)
        REPEAT
            ~@buffptr(V:buffpointer%(0)) !*** Zeiger holen
        UNTIL buffpointer%(0)<sound_buffer_adr%+half_buffer%
        ' *** halben Puffer holen 
        ~GEMDOS(63,handles,L:half_buffer%, L: sound_buffer_adr%+half_buffer%)
        INC buffer_count%
    UNTIL buffer_count%>=anz_buffer%
    ~GEMDOS (62, handle&) !*** Datei schließen 
RETURN
'
' GFA-BASIC Library für den FALCON 030 
' Sound-Sub-System  (c)1992 by MAXON-Computer 
' Autor: Christian Möller
'
' *** Sound-Subsystem
'
'
PROCEDURE init_sound_data 
    ltatten_&=0 
    rtatten_&=1 
    ltgain_&=2 
    rtgain_&=3 
    adderin_&=4 
    adcinput_&=5 
    setprescale_&=6 
    dmaplay_&=0 
    dspxmit_&=1 
    extinp_&=2 
    adc_&=3 
    dmarec_&=1
    dsprec_&=2 
    extout_&=4 
    dac_&=8
    DIM codec_data&(6)
    DIM buffpointer%(3)
RETURN
PROCEDURE get_codec_data 
    LOCAL n&
    FOR n&=0 TO 6
        codec_data&(n&)=@soundcmd(n&, -1)
    NEXT n&
RETURN
PROCEDURE restore_codec_data 
    LOCAL n&
    FOR n&=0 TO 6
        ~@soundcmd(n&, codec_data&(n&))
    NEXT n&
RETURN
FUNCTION locksnd 
    RETURN XBIOS(&H80)
ENDFUNC
FUNCTION unlocksnd 
    RETURN XBIOS(&H81)
ENDFUNC
FUNCTION soundcmd(mode&,data&)
    RETURN XBIOS(&H82,mode&, data&)
ENDFUNC
FUNCTION setbuffer(reg&,begaddr%, endaddr%)
    RETURN XBIOS(&H83, regst, L:begaddr%, L: endaddr%) 
ENDFUNC
FUNCTION setsndmode(mode&)
    RETURN XBIOS(&H84,mode&)
ENDFUNC
FUNCTION settrack(playtracks&,rectracks&)
    RETURN XBIOS(&H85,playtracks&,rectracks&) 
ENDFUNC
FUNCTION setmontrack(montrack&)
    RETURN XBIOS(&H86,montrack&)
ENDFUNC
FUNCTION setinterrupt(scr_inter&, cause&)
    RETURN XBIOS(&H87,scr_inter&, cause&)
ENDFUNC
FUNCTION buffoper(mode&)
    RETURN XBIOS(&H88,mode&)
ENDFUNC
FUNCTION dsptristate (dspxmit&, dsprec&)
    RETURN XBIOS(&H89,dspxmit&, dsprec&)
ENDFUNC
FUNCTION gpio(mode&,data&)
    RETURN XBIOS(&H8A,mode&, data&)
ENDFUNC
FUNCTION devconnect(src&,dst&,srcclk&,prescale&,protocol&)
    RETURN XBIOS(&H8B,src&,dst&,srcclk&,prescale&,protocol&)
ENDFUNC
FUNCTION sndstatus(reset&)
    RETURN XBIOS(&H8C,reset&)
ENDFUNC
FUNCTION buffptr(pointer%)
    RETURN XBIOS(&H8D,L:pointer%)
ENDFUNC


Aus: ST-Computer 12 / 1992, Seite 110

Links

Copyright-Bestimmungen: siehe Über diese Seite