Ton digital: Wunderbare Welt der Soundformate, Teil 3

Weitere Formate und Wanderroutinen

In diesem Teil werden wir das besonders im Unix-Bereich übliche SND-Format und dessen typisches Packverfahren, das µ-Law, vorstellen. Außerdem präsentieren wir die eigentlichen, kurzen Wandelroutinen des UTILS.C-Quellcodes.

Das Hauptverbreitungsgebiet des SND-Formats ist die Unix-Welt. Auch im Macintosh-Bereich findet man dieses Format häufig, wobei es aber oft als AU-Format bezeichnet wird. Der File-Header ist in beiden Fällen gleich. Allerdings ist es möglich, daß ein File zwar die Extension .SND, jedoch nicht den dazugehörigen Header besitzt. Oft finden sich solche Files aber im Standarddatenformat der SUN-Workstations (8 kHz, µ-Law). Der Header besitzt den folgenden Aufbau:

typedef struct
{
    long magic; 
    long datalocation; 
    long datasize; 
    long dataformat; 
    long samplingrate; 
    long channelcount; 
    char info[4];
} SNDHEAD

,magic‘: Hat immer den Wert ,.snd‘ (0x2E736E64L). Er dient als Erkennung für das SND-Format.
,datalocation‘: Zeiger auf den Anfang der Sounddaten
,datasize‘: Sounddatenlänge in Bytes
,dataformat‘: Gibt das Format der Sounddaten an. Wir unterstützen die folgenden:

1 SND_FORMAT_MULAW_8
8 Bit µ-Law
2 SND_FORMAT_LINEAR_8
8 Bit linear, vorzeichenbehaftet
3 SND_FORMAT_LINEAR_16
16 Bit linear, vorzeichenbehaftet
Es gibt aber noch wesentlich mehr Datenformate, wie zum Beispiel:

0 SND_FORMAT_UNSPECIFIED
Undefiniertes Format
5 SND_FORMAT_LINEAR_32
32 Bit linear
6 SND_FORMAT_FLOAT
Fließkommadarstellung
7 SND_FORMAT_DOUBLE
Double-Darstellung
10 SND_FORMAT_DSP_CORE
DSP-Programm

Wirklich verbreitet ist aber nur 8 Bit µ-Law.

,Samplingrate‘: Sample-Frequenz in Hz. Bevorzugte Werte sind: 8000 bzw. 8013, 22050 und 44100.

,channelcount‘: Anzahl der Kanäle.
,info‘: Kann zusätzliche Textinformationen enthalten, wie zum Beispiel lange File-Namen. Es ist ein nullterminierter String, der aus mindestens 4 Bytes besteht. Danach folgen die eigentlichen Sounddaten.

Die SND-Wandelfunktionen

Das Modul besteht aus SND.C und SND.H. Die Header-Testfunktion SndTstHead überprüft anhand des ,magic‘, ob das Inputfile im SND-Format vorliegt. Wie oben erwähnt, werden nur 3 mögliche Datenformate unterstützt, die anderen bewirken einen Abbruch mit dem Ergebnis ,NOT_SUPPORTED‘. Die Information im ,info‘ Feld wird ignoriert. Um direkt zum Anfang der Sounddaten zu gelangen, wird zum Schluß ein ,datalocation‘ entsprechender fseek durchgeführt.

Die Wandlung in das Standardformat wird wiederum von ,AllToStd‘ übernommen.

Das Wandeln ins SND-Format übernimmt ,SndFormStd‘, die wie ,AvrFromStd’ aufgebaut ist, mit dem Unterschied, daß nun µ-Law erlaubt ist.

,SndWrtHead‘ belegt den Header zur Initialisierung mit Nullen vor, setzt die Werte (bis auf ,datasize‘) und schreibt den Header. Die Datenlänge wird später von ,SndFmish‘ eingefügt.

An der Kürze dieser Beschreibungen und der Listings kann man schon sehen, daß das modulare Programmkonzept das Einfügen weiterer Soundformate recht einfach und übersichtlich macht.

Die Allround-Wandelfunktionen

Das File UTILS.C enthält zunächst die allgemeinen Wandelfunktionen, die von den meisten Soundmodulen aufgerufen werden.

AllToStd:

Bei ,AllToStd‘ handelt es sich um eine komplette Wandlungsroutine für praktisch alle Soundformate außer Deltapack ins Standardformat. Die nötigen Konvertierungen werden nacheinander durchgeführt, indem die entsprechenden Low-Level-Funktionen aufgerufen werden. Dabei gehen wir davon aus, daß die Wandlung ins Standardformat (also 16 Bit Stereo) meist eine Expansion der Daten bedeutet, jedoch NIE eine Verkürzung.

Zuerst wird ein Datenblock gelesen, der genau die richtige Größe hat, um - nach der Wandlung ins Standardformat - den gesamten Buffer auszufüllen. Diese Größe wurde bereits von der Header-Testfunktion bestimmt und ist in der internen Formatbeschreibung gespeichert. Um die Wandlung und eine eventuell damit verbundene Expansion der Daten möglichst einfach zu gestalten, wird der Sounddatenblock ans hintere Ende des Buffers geladen. Wird der Block bei der Wandlung länger, wächst er nach vorne. Am Ende der Routine ergibt es sich dann automatisch, daß der Standardsounddatenblock am Buffer-Anfang beginnt. Das Blockende entspricht auch dem Buffer-Ende - es sei denn, es konnten nicht genug Daten gelesen werden. Um auch diese Situation zu berücksichtigen, liefert die Funktion die Länge des expandierten Sounddatenblocks in Bytes zurück.

Um mit möglichst wenig Konvertierfunktionen auszukommen, werden die Daten gleich auf 16-Bit-Motorolaformat gewandelt. Danach werden eventuell vorzeichenlose Daten ins signed-Format gebracht. Zum Schluß kommt dann die Expansion von Mono nach Stereo.

AllFromStd:

Dies ist die komplette Wandelroutine vom Standardformat in fast alle Soundformate - außer Deltapack. Die Wandlung läuft analog zu ,AllToStd‘, nur in der umgekehrten Reihenfolge ab. Während der gesamten Wandlung beginnen die Daten mit dem Buffer. Am Ende werden die Daten ins File geschrieben und die Anzahl der geschriebenen Bytes zurückgegeben.

Die Low-Level-Konvertierungsfunktionen

Alle Konvertierungsfunktionen erhalten als Argument einen Pointer auf die Anfangsadresse des Datenblocks sowie dessen Länge. Alle Funktionen, die die Länge des Datenblocks vergrößern, erhalten zusätzlich noch einen Pointer auf die Adresse, ab der sie die gewandelten Daten speichern sollen. Sie gehen davon aus, daß diese Adresse kleiner als die Quelladresse ist und daß sich die Datenbereiche überlagern können.

Folgende Konvertierungen sind implementiert:

Conv8_16:
8 Bit linear nach 16 Bit linear
ConvM_S:
16 Bit Mono nach Stereo
ConvU_S:
16 Bit unsigned nach signed
ConvMu_16:
8 Bit µ-Law nach 16 Bit linear
ConvD_Std:
8 Bit Deltapack nach 16 Bit linear
Conv16_8:
16 Bit linear nach 8 Bit linear
ConvS_M:
16 Bit Stereo nach Mono
ConvS_U:
16 Bit signed nach unsigned
Conv16_Mu:
16 Bit linear nach 8 Bit µ-Law
ConvStd_D:
16 Bit linear nach 8 Bit Deltapack
ConvIntel:
Bigendian nach Littleendian

Das µ-Law-Packverfahren

Bei diesem Verfahren wird ein 16-Bit-Wert über eine logarithmische Kennlinie in einen 8-Bit-Wert gewandelt (siehe Teil 1). Es gilt folgende Formel:

y = sgn(x)/ln(1 +(i) * ln(1 +µ*abs(x/xmax))

Dabei ist x der jeweils zu packende Wert, xmax der maximal mögliche Wert, µ hat typischerweise den Wert 255. Diese Formel liefert Werte zwischen -1 und 1. Um diese als 8-Bit-Werte zu speichern, muß noch mit 127 multipliziert und 128 addiert werden. Die daraus resultierende Zahlendarstellung ist etwas gewöhnungsbedürftig. Der kleinste negative Wert (-32768) wird auf 0 abgebildet, der größte negative Wert (-0) wird zu 127. Der größte positive Wert (32767) wird zu 128, und der kleinste positive Wert (0) wird zu 255. Bild 1 zeigt dies noch einmal im Überblick.

Diese Packvorschrift kann auf verschiedene Arten implementiert werden. Das Ausrechnen der Formel für jeden einzelnen Sample-Wert ist natürlich nicht besonders effizient - es sei denn, man hat zuviel CPU-Leistung. Einige der weiter verbreiteten Konvertierungsprogramme nähern die Kennlinie durch Geradenstücke an. Das ist zwar schnell und braucht wenig Speicher, ist aber etwas ungenau. Wir benutzen dagegen eine Tabelle, in der vor der Wandlung zu allen Eingangs werten die Ausgangswerte eingetragen werden. Dann muß zum Packen nur der richtige Wert aus der Tabelle gelesen werden. Daß dabei 32KB am Stück für die Tabelle benötigt werden, sollte auf zeitgemäßen Rechnern kein Problem darstellen.

Bild 1: Abbildung von 16-Bit-linear-Daten auf die µ-Law-Zahlendarstellung.

Die Rückwandlung erfolgt bei uns ebenfalls über eine Tabelle, die jedoch erheblich kleiner ist. Die nötigen Werte erhält man durch Auflösen der obigen Formel nach x.

Die Implementierung

Um wenigstens ein wenig Speicherplatz zu sparen, existiert die 32KB lange Wandeltabelle nur einmal für µ-Law und Deltapack (ein anderes Packverfahren). Je nachdem, für welchen Zweck sie gerade benötigt wird, wird sie mit den entsprechenden Werten belegt. Natürlich wird die Tabelle bei der Wandlung eines Files nur von EINER der Routinen benötigt - muß also nicht für jeden Block neu berechnet werden. Die Variable ,InvLkSet‘ gibt an, ob die Tabeile mit ,MULAW‘- oder ,DELTAPACK‘-Werten oder noch gar nicht belegt ist.

Zum Entpacken hat µ-Law seine Tabelle für sich alleine. ,MuLkSet‘ gibt an, ob die Tabelle schon aufgebaut wurde.

Sämtliche Tabellen und Belegungsvariablen sind nur lokal in UTILS.C bekannt.

SetMu: Baut die Entpacktabelle zum Wandeln von 8-Bit-µ-Law nach 16-Bit-linear mit 256 Int-Werten auf. Entsprechend der Zahlendarstellung im µ-Law-Format dienen die ersten 128 Einträge zur Rückwandlung der negativen Sample-Werte nach -32767 bis 0. Die oberen 128 Einträge sind dann für die positiven Werte von 32767 bis 0 zuständig.

SetInvMu:* Diese Routine baut die Tabelle zum Wandeln von 16-Bit-linear nach 8-Bit-(µ-Law auf. Nachdem die oben genannte Formel für negative und positive Werte bis auf das Vorzeichen dasselbe Resultat liefert, genügt es, die Tabelle nur für negative Werte aufzubauen. Dadurch werden immerhin 32KB Speicher gespart.

Eigentlich bräuchte jetzt nur für alle 32768 Werte die obige Formel ausgerechnet und das Resultat in die Tabelle geschrieben zu werden. Allerdings dauert das Berechnen des Logarithmus ohne Coprozessor doch ziemlich lange. Deswegen benutzen wir einen Trick: Wir gehen in einer Schleife jeden einzelnen der 128 möglichen Output-Werte durch und berechnen, mit welchem kleinsten 16-Bit-Input-Wert dieser erreicht werden kann. Dann werden alle Positionen zwischen diesem Input-Wert und dem vorhergehenden mit dem entsprechenden Output-Wert belegt.

Beide Routinen werden erst aufgerufen, wenn sie wirklich benötigt werden.

Die µ-Law-Konvertierungsroutinen

ConvMu_16: Konvertiert einen Block mit 8-Bit-µ-Law-Daten nach 16-Bit-linear. Dazu überprüft sie zuerst, ob die Wandeltabelle bereits, belegt wurde. Wenn nicht, wird ,SetMu‘ aufgerufen. Danach werden die Daten einfach über die Tabelle konvertiert.

Conv16_Mu: Konvertiert einen Block mit 16-Bit-linear-Daten nach 8-Bit-‘-Law. Auch hier wird zuerst - falls nötig - die Wandeltabelle durch ,SetInvMu‘ aufgebaut. Danach werden die Daten über die Tabelle gewandelt. Da negative Offsets beim Auslesen aus der Tabelle nicht möglich sind, wird der Betrag der Input-Werte benutzt. Das Ergebnis ist für negative Input-Werte korrekt, bei positiven muß dagegen 128 addiert werden.

Das Project-File für Pure C

Bekommen Sie keinen Schreck! Sie haben nichts verpaßt. Es fehlen noch einige Quellcodes, die in VLUN.PRJ aufgeführt sind. Das File compiliert alle Quellcodes und erzeugt dann ein TOS-Programm VLUN.TOS.

Im letzten Teil dieser Serie werden wir dann die etwas komplizierteren Formate Riff/WAVE und DVSM behandeln und die letzten fehlenden Module des Konverters vorstellen.

Harald Schönfeld und Bernd Spellenberg

/* SND Routinen zur SND Konvertierung        */
/* Modulname:   SND.C                        */
/* (c) MAXON Computer 1994                   */
/* Autoren:     H. Schönfeld, B. Spellenberg */

# include "vlun.h"
# include "snd.h"

/* Zahler für geschriebene Sound-Bytes      */
static long ByteCount=0L;
/* Header für Input und Export              */
static SNDHEAD SndHead;

/* SND-Headertest-Funktion                  */
int SndTstHead(FILE *InFH, SAMPLE_SPEC *Smp)
{
/* Soundformatbeschreibung initialisieren   */
    memset(Smp,0,sizeof(SAMPLE_SPEC));
/* SND-Header lesen und testen              */
    if (!fread(&SndHead,sizeof(SNDHEAD),1L,InFH)) 
        return(UNKNOWN); 
    if(strncmp(&SndHead.magic,".snd",4)) 
        return(UNKNOWN);

/* Header auswerten und Formatbeschreibung  */
/* aufbauen                                 */
    Smp->Typ=SNDHEADER;
    Smp->Freq=SndHead.Samplingrate;

    switch((int)SndHead.channelcount)
    {
        case 1:                     /* Mono */
            Smp->SizeFac=2; 
            break;

        case 2:                   /* Stereo */
            Smp->Format|=STEREO;
            Smp->SizeFac=1;
            break;

        default:               /* Unbekannt */
            return(NONSUPPORTED);
    }

    switch((int)SndHead.dataformat)
    {
        case SND_FORMAT_MULAW_8:
            Smp->Format|=MULAW|SIGNED;
            Smp->SizeFac*=2;
            break;

        case SND_FORMAT_LINEAR_8:
            Smp->Format|=SIGNED;
            Smp->SizeFac*=2;
            break;

        case SND_FORMAT_LINEAR_16:
            Smp->Format|=BIGENDIAN|SIGNED|BIT16; 
            break;

        default:
            return(NOT_SUPPORTED);
    }
    Smp->BufLen=BUFSIZE;

/* Falls Sounddaten nicht nach Header folgen*/ 
/* zum Anfang der Daten seeken              */
    if(SndHead.datalocation-sizeof(SNDHEAD)!=0) 
        fseek(InFH,SndHead.datalocation,0); 
    return(SUPPORTED);
}

/* Std.format nach SND konvertieren         */
long SndFromStd(FILE *OutFH,OUT_FORMAT *OutSmp, 
                long DataLen, char *StdBuf)
{
    long DataWrite;

/* Deltapack wird von SND nicht unterstützt */ 
    OutSmp->Format&=~DELTAPACK;
/* Werte immer vorzeichenbehaftet           */
    OutSmp->Format|=SIGNED;
/* 16Bit Samples immer im Motorolaformat    */
    if(OutSmp->Format&BIT16)
        OutSmp->Format|=BIGENDIAN;

/* nächsten Block konvertieren und speichern */ 
    DataWrite=AllFromStd(OutFH,OutSmp,DataLen,StdBuf);

/* Bytezähler entsprechend erhöhen          */
    ByteCount+=DataWrite; 
    return(DataWrite);
}

/* SND-Header setzen und schreiben          */
int SndWrtHead(FILE *OutFH,SAMPLE_SPEC *InSmp,OUT_FORMAT *OutSmp)
{
    int Fmt;

    Fmt=OutSmp->Format;
    memset(&SndHead,0,sizeof(SNDHEAD)); 
    strcpy(&SndHead.magic,".snd");

    SndHead.datalocation=sizeof(SNDHEAD);
    SndHead.datasize=0;

    if(Fmt&BIT16)
        SndHead.dataformat=SND_FORMAT_LINEAR_16; 
    else if(Fmt&MULAW)
        SndHead.dataformat=SND_FORMAT_MULAW_8; 
    else
        SndHead.dataformat=SND_FORMAT_LINEAR_8;

    SndHead.samplingrate=InSmp->Freq;

    if(Fmt&STEREO)
        SndHead.channelcount=2; 
    else
        SndHead.channelcount=1;

    return(fwrite(&SndHead,sizeof(SNDHEAD),1,OutFH));
}

/* Fehlende Information in SND-Header       */
/* schreiben                                */
int SndFinish(FILE *OutFH,OUT_FORMAT *OutSmp)
{
/* Anzahl der geschriebenen Bytes eintragen */ 
    SndHead.datasize=ByteCount;
    ByteCount=0;

/* kompletten Header an Fileanfang schreiben */ 
    fseek(OutFH,0L,0);
    return(fwrite(&SndHead,sizeof(SNDHEAD),1,OutFH));
}

/* UTILS Allgemeine Routinen zur Wandlung   */
/* Modulname:  UTILS.C                     */
/* (c) MAXON Computer 1994                  */
/* Autoren:    H. Schönfeld, B. Spellenberg */

# include "vlun.h"

/* Lookup-Tabellen für die Wandlung von     */
/* Mu-Law und Deltapack nach 16 Bit         */
static int MuLookUp[256],DeltaLookUp[128];
/* Lookup-Tabelle für die Wandlung nach     */
/* Mu-Law oder Deltapack                    */
static char InvLookUp[32769L];
/* Flags, ob Tabellen schon berechnet wurden */ 
static int MuLkSet=0, DeltaLkSet=0;
/* Gibt an ob und wie InvLookUp belegt ist */ 
static int InvLkSet=0;

/* Lädt einen Block Sampledaten und konver- */ 
/* tiert ihn ins Standardformat (16B/Stereo)*/ 
long AllToStd(FILE *InFH,SAMPLE_SPEC *Smp,
              char *StdBuf, long StdBufLen)
{
/* Anfangsadr. für die zu ladenden Daten    */
    char *InBuf;
/* Ergibt Länge im Standardformat           */
    long DataRead,
/* Offset für Dateneginn im Standardformat  */ 
         DataLen;

/* Einen Datenblock ans Bufferende laden    */ 
    DataLen=Smp->BufLen;
    InBuf=StdBuf+StdBufLen-DataLen;
    DataRead=fread(InBuf,1,DataLen,InFH);

/* Je nach Input-Format die zugehörigen     */
/* Wandelfunktionen aufrufen und die eventu-*/ 
/* elle Expansion der Daten in DataRead und */ 
/* DataLen nachvollziehen                   */
    if((Smp->Format&MULAW))
    {
        ConvMu_16(InBuf,InBuf-DataLen,DataRead); 
        InBuf-=DataLen;
        DataRead*=2;
        DataLen*=2;
    }
    else if(!(Smp->Format&BIT16))
    {
        Conv8_16(InBuf,InBuf-DataLen,DataRead); 
        InBuf-=DataLen;
        DataRead*=2;
        DataLen*=2;
    }
    else if(!(Smp->Format&BIGENDIAN))
        ConvIntel(InBuf,DataRead);

    if(!(Smp->Format&SIGNED))
        Convu_S(InBuf,DataRead);

    if(!(Smp->Format&STEREO))
    {
        ConvM_S(InBuf,InBuf-DataLen,DataRead); 
        InBuf-=DataLen;
        DataRead*=2;
    }
    return(DataRead);
}

/* Konvertiert den Block im Standardformat  */ 
/* in das durch OutSmp definierte Format und*/ 
/* speichert ihn ab                         */
long AllFromStd(FILE *OutFH,OUT_FORMAT *OutSmp, 
                long DataLen, char *StdBuf)
{
/* Länge der geschriebenen Daten            */
    long DataWrite;

/* Je nach Output-Format die zugehörigen    */ 
/* Wandelroutinen aufrufen und DataLen falls*/ 
/* nötig korrigieren                        */
    if(!(OutSmp->Format&STEREO))
    {
        ConvS_M(StdBuf,DataLen);
        DataLen/=2;
    }

    if(!(OutSmp->Format&SIGNED))
        ConvS_U(StdBuf,DataLen);

    if((OutSmp->Format&MULAW))
    {
        Conv16_Mu(StdBuf,DataLen);
        DataLen/=2;
    }
    else if(!(OutSmp->Format&BIT16))
    {
        Conv16_8(StdBuf,DataLen);
        DataLen/=2;
    }
    else if(!(OutSmp->Format&BIGENDIAN))
        ConvIntel(StdBuf,DataLen);

    DataWrite=fwrite(StdBuf,1,DataLen,OutFH); 
    return(DataWrite);
}

/* Konvertiert 8 Bit Sounddaten nach 16 Bit */ 
void Conv8_16(char *InData,char *OutData, long Len)
{
    long i;
    int *out=(int *)OutData;

    for(i=0;i<Len;i++)
        *(out++)=((int)*(InData++))<<8;
}

/* Konvertiert 16 Bit Mono nach Stereo */
void ConvM_S(char *InData,char *OutData,long Len) 
{
    long i;
    int *out=(int *)OutData, *in=(int *)InData;

    for(i=0;i<Len;i+=2)
    {
        *(out++)=*in;
        *(out++)=*(in++);
    }
}

/* Konvertiert einen Block vorzeichenloser */ 
/* 16 Bit Daten nach vorzeichenbehaftet    */
void ConvU_S(char *InData,long Len)
{
    long i;
    int *ptr=(int *)InData;

    for(i=0;i<Len;i+=2)
        *(ptr++) = (int)(*ptr-32768L);
}

/* Konvertiert 16 Bit Sounddaten nach 8 Bit */ 
void Conv16_8(char *Buf,long Len)
{
    int *in=(int *)Buf; 
    long i;

    for(i=0;i<Len;i+=2)
        *(Buf++)=(char)(*(in++)>>8);
}

/* Konvertiert 16 Bit Stereo nach Mono */
void ConvS_M(char *Buf,long Len)
{
    int *in=(int *)Buf, *out=(int *)Buf; 
    long i;

    for(i=0;i<Len;i+=4;in+=2)
        *(out++)=(int)((((long)in[0])+in[1])/2);
}

/* Konvertiert einen Block vorzeichenbehaf-     */
/* teter 16 Bit Daten nach vorzeichenlos        */ 
void ConvS_U(char *Buf,long Len)
{
    long i;
    int *ptr=(int *)Buf;

    for(i=0;i<Len;i+=2)
        *(ptr++)=(int)(*ptr*32768L);
}

/* Konvertiert 16 Bit bigendian nach */
/* littleendian und umgekehrt        */
void ConvIntel(char *InData,long Len)
{
    long i;
    int *ptr=(int *)InData;

    for(i=0;i<Len;i+=2;InData+=2)
        *(ptr++)=InData[0]+((int)InData[1]<<8);
}

/* Konvertiert einen Block 8 Bit Mu-Law */
/* nach 16 Bit (linear)                 */
void ConvMu_16(char *InData,char *OutData, long Len)
{
    long i;
    int *out=( int *)OutData;

/* Falls Mu-Law Lookup-Wandeltabelle noch */ 
/* nicht berechnet, diese berechnen       */
    if (MuLkSet!=MULAW)
    {
        SetMu();
        MuLkSet=MULAW;
    }

/* Daten über Lookup-Tabelle wandeln     */
    for(i=0;i<Len;i++)
        *(out++)=MuLookUp[*(unsigned char *) (InData++)];
}

/* Konvertiert einen Block 16 Bit linear */
/* nach 8 Bit Mu-Law                     */
void Conv16_Mu(char *Buf,long Len)
{
    int *in=(int *)Buf; 
    long i;

/* Falls inverse Lookup-Wandeltabelle nicht */ 
/* mit Mu-Law Daten belegt, diese berechnen */ 
    if(InvLkSet!=MULAW)
    {
        SetInvMu();
        InvLkSet=MULAW;
    }

/* Daten über Lookup-Tabelle wandeln         */
/* Negative Werte liegen im Bereich zwischen */ 
/* 127 bis 0, positive zwischen 255 und 128  */
    for(i=0;i<Len;i+=2) 
        if(*in<0)
            *(Buf++)=InvLookUp[-*(in++)]; 
        else
            *(Buf++)=InvLookup[*(in++)]+128;
}

/* Berechnet Lookup-Tabelle zur Wandlung von */ 
/* 8 Bit Mu-Law nach 16 Bit linear           */
void SetMu(void)
{
    unsigned int i;
    float u = 255.0, mp = 32767.0, l1pu;

    l1pu = log(1+u);
    for (i = 0; i <= 127; i++)
        MuLookUp[i]=-(exp(l1pu*(127-i)/127.0)-1)/u*mp;
    for (i = 128; i < 256; i++)
        MuLookUp[i]=(exp(l1pu*(255-i)/127.0)-1)/u*mp;
}

/* Berechnet Lookup-Tabelle zur Wandlung von */ 
/* 16 Bit linear nach Mu-Law                 */
void SetInvMu(void)
{
    long i, li=32768, mi, j;
    float u = 255.0, mp = 32767.0, l1pu;

    l1pu = log(1+u);
    for (i = 0; i <= 127; i++) {
        mi = (exp(l1pu*(127-i)/127.0)-1)/u*mp; 
        for (j = li; j >= mi; j—-)
            InvLookUp[j] = i; 
        li = mi-1;
    }
    for (j = li; j >= 0; j--)
        InvLookUp[j] = 127;
}


/* Konvertiert einen Block 8 Bit Deltapack */ 
/* (Stereo) nach 16 Bit (linear)           */
void ConvD_Std(char *InData,char *OutData, long Len)
{
    long i;
    int *out=(int *)OutData, 10, r0;

/* Falls Deltapack Lookup-Wandeltabelle noch */ 
/* nicht berechnet, diese berechnen          */
    if(DeltaLkSet!=DELTAPACK)
    {
        SetDelta();
        DeltaLkSet=DELTAPACK;
    }

/* Stützpunkte auslesen                     */
    *(out++)=10 = *(int *)InData;
    *(out++)=r0 = *(int *)(InData+2);
    InData += 4;

/* Differenzwerte über Lookup-Tabelle um-   */
/* rechnen und zu bisherigen addieren       */
    for(i=4;i<Len;i+=2)
    {
        if(*InData<0)
            *(out++)=10-=DeltaLookUp[-*(InData++)]; 
        else
            *(out++)=10+=DeltaLookUp[+*(InData++)]; 
        if(*InData<0)
            *(out++)=r0-=DeltaLookup[-*(InData++)]; 
        else
            (*out++)=r0+=DeltaLookup[+*(InData++)];
    }
}

/* Konvertiert einen Block 16 Bit (linear)  */
/* nach 8 Bit Deltapack (Stereo)            */
void ConvStd_D(char *Buf,long Len)
{
    int *in=(int *)Buf,10,r0,tmp; 
    long i;

/* Falls Deltapack Lookup-Wandeltabellen    */
/* noch nicht berechnet, diese berechnen    */
    if(InvLkSet!=DELTAPACK)
    {
        SetInvDelta();
        InvLkSet=DELTAPACK;
    }
    if(DeltaLkSet!=DELTAPACK)
    {
        SetDelta();
        DeltaLkSet=DELTAPACK;
    }

/* Stützwerte belassen                      */
    l0=*(in++); 
    r0=*(in++);
    Buf+=4;

/* Daten packen                             */
    for(i=4;i<Len;i+=4)
    {
/* Linker Kanal, Differenz berechnen        */
    tmp = 10;
    l0 = (*in)-10;
/* Differenz nähern und gleich wieder       */
/* entpacken                                */
    if(l0<0)
        l0=tmp-DeltaLookUp[-(*(Buf++)=-InvLookUp[-10])];
    else
        l0=tmp+DeltaLookUp[*(Buf++)=InvLookUp[10]);

/* Durch Näherung erzeugten Overflow        */
/* korrigieren                              */
    if (((l0^*(in++))&0x8000)&&(abs(10)>20000))
    {
        if(l0<0)
            l0=tmp-DeltaLookUp[-(*(Buf++)=-InvLookUp[DeltaLookUp[1-*(--Buf)])];
        else
            l0=tmp+DeltaLookUp[*(Buf++)=InvLookUp[DeltaLookUp[1+*(--Buf)]]];
    }

/* Rechter Kanal                            */
    tmp = r0; 
    r0 = (*in)-r0; 
    if(r0<0)
        r0=tmp-DeltaLookup[-(*(Buf++)=-InvLookUp[-r0])];
    else
        r0=tmp+DeltaLookUp[*(Buf++)=InvLookUp[r0]];

    if (((r0^*(in++))&0x8000)&&(abs(r0)>20000)) 
    {
        if(r0<0)
            r0=tmp-DeltaLookUp[-(*(Buf++)=-InvLookUp[DeltaLookUp[1-*(—-Buf)]])];
        else
            r0=tmp+DeltaLookUp[*(Buf++)=InvLookUp[DeltaLookUp[1+*(—-Buf)]]];
    }
}

/* Berechnet Lookup-Tabelle zur Wandlung von */ 
/* 16 Bit linear nach 8 Bit Deltapack        */
void SetDelta(void)
{
    int i;

    DeltaLookUp[0]=0; 
    for (i=1;i<128;i++)
        DeltaLookUp[i]=(int)pow(1.084618362,(double)i);
}

/* Berechnet Lookup-Tabelle zur Wandlung von */ 
/* 8 Bit Deltapack nach 16 Bit linear        */
void SetInvDelta(void)
{
    int i; 
    int val;

    memset(InvLookUp,127,32768L);
    InvLookUp[0]=0; 
    for (i=1;i<128;i++)
    {
        val=(int)pow(1.084618362,(double)i); 
        memset(&InvLookUp[val],i,32768L-val);
    }
}
/* SND Headerfile zur SND Konvertierung  */
/* Modulname:   SND.H                    */
/* (c) MAXON Computer 1994               */
/* Autoren: H. Schönfeld, B. Spellenberg */

typedef struct
{
    long magic;         /* Headerkennung        */
    long datalocation;  /* Sampledaten Beginn   */ 
    long datasize;      /* Datenlänge in Bytes  */
    long dataformat;    /* Datenformat s.u.     */ 
    long samplingrate;  /* Frequenz             */
    long channelcount;  /* Kanalanzahl          */
    char info[4];       /* optionale Info       */
} SNDHEAD;

/* Mögliche Datenformate für VLUN               */
# define SND_FORMAT_MULAW_8     1
# define SND_FORMAT_LINEAR_8    2
# define SND_FORMAT_LINEAR_16   3

; Pure C Project-File 
; Modulname: VLUN.PRJ 
; (c) MAXON Computer 1994 
; Autoren: H. Schönfeld, B. Spellenberg

VLUN.TOS ;Programmname

.C [ -Y ]
.L [ -L -Y ]
.S [ -Y ]

PCSTART.O ;Startup Code

; Rumpfprogramm 
VLUN.C  (VLUN.H)
; Einzele Formatmodule 
AVR.C   (VLUN.H, AVR.H)
SND.C   (VLUN.H, SND.H)
DVSM.C  (VLUN.H, DVSM.H)
WAV.C   (VLUN.H, WAV.H)
RAW.C   (VLUN.H)
; Wandelroutinen 
UTILS.C (VLUN.H)

; nötige Libraries 
PCFLTLIB.LIB 
PCSTDLIB.LIB 
PCEXTLIB.LIB 
PCTOSLIB.LIB


Aus: ST-Computer 04 / 1994, Seite 86

Links

Copyright-Bestimmungen: siehe Über diese Seite