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