Die ganze Diplomarbeit als PDF - Alice

Transcription

Die ganze Diplomarbeit als PDF - Alice
LEHRSTUHL UND LABORATORIUM FÜR TECHNISCHE
ELEKTRONIK
Universität Erlangen-Nürnberg
Vorstand: Prof. Dr.-Ing. D. Seitzer
Diplomarbeit
Simulation eines AudioCodierverfahrens für
professionelle Anwendungen
Bearbeiter:
Betreuer:
Robert Henke
Dr. Ing., Dipl. math. Karlheinz Brandenburg
Beginn:
Abgabe:
22. August 1992
22. Dezember 1992
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
Erklärung
Ich versichere, daß ich die Arbeit ohne fremde Hilfe und ohne Benutzung anderer als
der angegebenen Quellen angefertigt habe, und daß die Arbeit in gleicher oder ähnlicher
Form noch keiner anderen Prüfungsbehörde vorgelegen hat und von dieser als Teil einer
Prüfungsleistung angenommen wurde. Alle Ausführungen, die wörtlich oder sinngemäß
übernommen wurden, sind als solche gekennzeichnet.
Robert Henke
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
II
Inhalt
1.
Einleitung .........................................................................................................1
2.
Moderne Audiocodierverfahren .......................................................................4
2.1. Prinzipien moderner Audiocodierverfahren ............................................................ 4
2.1.1. Zeitbereichsverfahren.......................................................................................... 4
2.1.2. Frequenzbereichs- (Transformations-) Verfahren............................................... 4
2.1.2.1. Das Gehörmodell (Psychoakustisches Modell) ................................................ 5
2.1.2.2. Quantisierung.................................................................................................... 6
2.1.2.3. Bitzuteilung ...................................................................................................... 7
2.1.2.4. Entropiecodierung mit Tabellen ....................................................................... 7
2.1.2.5. Bitstrom-Generierung ....................................................................................... 8
2.2. Struktur der ISO-Encoder ........................................................................................ 8
2.2.1. Encoder für Layer I und II................................................................................... 9
2.2.2. Encoder für Layer III......................................................................................... 10
3.
Prinzipien des neuen Verfahrens ....................................................................12
3.1. Near Lossless Coding ............................................................................................ 12
3.2. Codierung im Spektralbereich ............................................................................... 13
3.3. Huffman Codierung ............................................................................................... 13
3.3.1. 'Stacked' Tabellen.............................................................................................. 14
3.3.2. Mehrdimensionale Codierung........................................................................... 14
3.4. Ausnutzung signalstatistischer und psychoakustischer Effekte............................. 14
4.
Implementierung des Verfahrens....................................................................16
4.1. Codierung durch Transformation per MDCT ........................................................ 17
4.1.1. Definition der MDCT........................................................................................ 17
4.1.2. Der Algorithmus nach Brandenburg ................................................................. 19
4.2. Huffman Code Tabellen......................................................................................... 20
4.2.1. Unterdrückung von Null-Werten ...................................................................... 20
4.2.2. Erzeugung der Huffman Codes......................................................................... 21
4.3. Eindimensionale Stacked Huffman Tabellen ........................................................ 22
4.3.1. Codierung der Spektralwerte mit eindimensionalen Tabellen .......................... 22
4.3.2. Histogrammerfassung zur Generierung der eindimensionalen SHC ................ 23
4.4. Zweidimensionale Stacked Huffman Code Tabellen ............................................ 24
4.4.1. Codierung der Spektralwerte mit zweidimensionalen Tabellen ....................... 24
4.4.2. Histogrammerfassung zur Generierung der zweidimensionalen SHC.............. 25
4.5. Spezialfälle: Frequenzlinien mit kleinen Beträgen ................................................ 26
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
III
4.6. Berechnung der benötigten Bits............................................................................. 27
4.7. Simulation eines Bitreservoirs ............................................................................... 28
4.8. Erfassung der Meßergebnisse ................................................................................ 28
4.8.1. Genauigkeit der Codierung in Bit ..................................................................... 28
4.8.2. Noise Masking Ratio......................................................................................... 29
5.
Messungen der Datenrate für Near Lossless Coding (NLC)..........................30
5.1. Eindimensionale Huffman Code Tabellen............................................................. 31
5.2. Zweidimensionale Huffman Code Tabellen .......................................................... 36
5.3. Einfluß der Tabellengröße ..................................................................................... 39
5.3.1. Rekonstruktionsgenauigkeit pro Abtastwert ..................................................... 39
5.3.2. Summarische Betrachtung der Rekonstruktionsgenauigkeit............................. 41
5.4. Bitreservoir ............................................................................................................ 44
5.5. Vergleich mit verlustfreier Kompression............................................................... 45
5.6. Bewertung der einzelnen Maßnahmen................................................................... 46
6.
Anwendung der Programme...........................................................................47
6.1. Simulation mit 'pacse' ............................................................................................ 48
6.2. Tabellengenerierung mit 'huffgen' ......................................................................... 49
6.3. Absoluter Vergleich mit 'snddiff'........................................................................... 50
6.4. Beurteilung der Hörbarkeit von Artefakten mit 'nmr' ............................................ 52
6.5. Weitere Hilfsprogramme: 'convbits', 'convab', 'sndchk', 'testsig' ........................... 53
6.5.1. convbits: Konvertierung zwischen 16-Bit-linear- und 32-Bit-linearSound Dateien ................................................................................................... 53
6.5.2. convab: Konvertierung von ASCII- in Binärdarstellung und
umgekehrt.......................................................................................................... 53
6.5.3. sndchk: Informationen über .snd-Dateien, 'Endianess'-Konvertierung............. 54
6.5.4. testsig: 32 Bit Testsignal-Erzeugung ................................................................ 55
6.6. Beispiele für die Kombination der Programme ..................................................... 55
6.6.1. Generierung neuer Huffman Tabellen............................................................... 56
6.6.2. Simulationen ..................................................................................................... 57
7.
Zusammenfassung ..........................................................................................58
8.
Anhang ...........................................................................................................59
8.1. Programmlistings ................................................................................................... 59
8.1.1. cliutil.c .............................................................................................................. 60
8.1.2. cliutil.h .............................................................................................................. 63
8.1.3. coder.c ............................................................................................................... 64
8.1.4. coder.h............................................................................................................... 70
8.1.5. convab.c ............................................................................................................ 71
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
IV
8.1.6. convbits.c .......................................................................................................... 74
8.1.7. defhenke.h ......................................................................................................... 76
8.1.8. fft.c .................................................................................................................... 77
8.1.9. huffgen.c ........................................................................................................... 82
8.1.10. hufflib.c ............................................................................................................. 84
8.1.11. hufflib.h............................................................................................................. 91
8.1.12. miscutil.c........................................................................................................... 93
8.1.13. miscutil.h........................................................................................................... 95
8.1.14. pacse.c ............................................................................................................... 96
8.1.15. sndchk.c........................................................................................................... 106
8.1.16. snddiff.c........................................................................................................... 108
8.1.17. sndutil.c........................................................................................................... 111
8.1.18. soundstruct.h ................................................................................................... 112
8.1.19. testsig.c............................................................................................................ 113
8.2. Shell - Scripts....................................................................................................... 115
8.3. Huffman Code Tabellen....................................................................................... 117
8.3.1. Neu generierte Tabellen .................................................................................. 117
8.3.2. Übernommene Tabellen.................................................................................. 117
8.3.2.1. Quadrupel-Tabelle A .................................................................................... 117
8.3.2.2. Quadrupel-Tabelle B .................................................................................... 117
8.4. Quellen................................................................................................................. 118
8.5. Liste der verwendeten Musikstücke..................................................................... 121
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
V
Glossar
.snd-Format
NeXT-Sound Datei. Dieses Format wird für alle Sound-Dateien vorausgesetzt,
speziell die Varianten 16 Bit und 32 Bit lineare PCM
ATW
Abtastwert, englisch Sample
CD
Compact Disc
Optisch abgetasteter nur lesbarer, scheibenförmiger Datenträger, Musikdaten als 2·16Bit PCM Kanäle mit Tonsignalen, 44.1 kHz Abtastrate
CD-ROM
Compact Disc Read Only Memory
Gleiches Medium wie die CD, jedoch anderes Datenformat für allgemeine
Digitaldaten
DAB
Digital Audio Broadcasting
Digitaler Rundfunk auf Basis des ISO/MPEG Standard 11172, Layer II
DAT
Digital Audio Tape
Kassette mit magnetischem Datenträger zur digitalen Tonaufzeichnung ohne
Datenreduktion, 48 kHz Abtastrate, 2·16-Bit PCM Kanäle
DCC
Digital Compact Cassette
Magnetkassette zur Tonaufzeichnung mit Datenreduktion, Codierung kompatibel zu
ISO/MPEG Standard 11172, Layer I
DIV
Ganzzahlige Division
Endianess
Gibt an, in welcher Reihenfolge Multi-Byte Wörter in einem Rechner gespeichert
werden. Es gibt little endian und big endian Anordnungen, bei letzterem werden die
MSBs in der höheren Adresse gespeichert
Entropiecodierung
Codierverfahren, bei dem die Länge von Zeichen umgekehrt proportional zu
Auftretenswahrscheinlichkeit des Zeichens ist
FFT
Fast Fourier Transformation
HC
Huffman Code
Code variabler Wortlänge zur Entropiecodierung
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
VI
HCT
Huffman Code Tabelle
ISO
International Organization for Standardization
LC-ATC
Low Complexity Adaptive Transform Coding
Transformationscoder mit variabler Bitzuteilung für die Frequenzbänder unter
Verwendung der Psychoakustik
LSB
Least Significant Bit
Geringstwertiges Bit, Bit Nummer 0
MD
MiniDisc
Beschreibbares optisches digitales Audio-Medium in Scheibenform, Tonsignale
werden mit gehörangepaßter Codierung datenreduziert
MDCT
Modifizierte Direkte Cosinus Transformation
Transformiert Zeitbereichswerte in Spektralwerte. Unterabtastung im Spektralbereich,
TDAC im Zeitbereich. Auch als OBT bezeichnet
MOD
Modulo-Operation für Ganzzahlen
MPEG
Moving Pictures Experts Group
ISO Gruppe zur Standardisierung von Video- und Audiokompressionsverfahren
MSB
Most Significant Bit, höchstwertiges Bit
Nachecho
s. Vorecho, nur daß es nach dem Signal auftritt. Ist aufgrund der Gehöreigenschaften
meist nicht relevant
NeXT
NeXT Station
Unix-Rechner mit Sound-Möglichkeiten und definiertem Sound-Format
NLC
Near Lossless Coding
Fast verlustfreie Codierung für professionelle Anforderungen
NMR
Noise to Mask Ratio
Abstand des Rauschens von der Maskierungsschwelle
OBT
Overlapping Block Transform
Transformation in den Frequenzbereich, meist MDCT genannt
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
VII
pacse
Professional Audio Coding using Spectral Entropy
Simulationsprogramm zu Untersuchungen zur Bitrate für NLC
PCM
Pulse Code Modulation
Repräsentation eines analogen Signals, das in mit einer festen Abtastrate (linear) in
digitale Zahlenwerte einer festen Wortlänge umgesetzt wird
SHC
Stacked Huffman Code
spezieller HC, der auf Seite 22 dieser Arbeit definiert wird
SHCT
Stacked Huffman Code Tables
Ein Paar von Tabellen, mit denen SHCs erzeugt werden können
SNR
Signal to Noise Ratio
Signal / Rausch Abstand
TDAC
Time Domain Aliasing Cancellation
Dient Verbindung mit MDCT/OBT zum Auslöschen der Alias-Terme
Transformationscoder / -decoder
Encoder und Decoder, der das Tonsignal aus dem Zeitbereich in den Frequenzbereich
transformiert, codiert, und schließlich wieder in den Frequenzbereich
rücktransformiert
Vorecho
Artefakt bei Transformationscodern: man hört das Signal, bevor es hörbar sein dürfte.
Auffällig besonders bei starken Lautstärkeanstieg (z.B. Triangel-Anschlag)
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
VIII
1. Einleitung
Seit man Töne elektrisch aufnehmen und übertragen kann, versucht man mit gleichem
Aufwand bessere Qualität zu erreichen. Der Begriff "gleicher Aufwand" ist dabei relativ:
was vor zehn Jahren als Spitze der Technik galt, ist heutzutage Massenprodukt und für
weniger als ein Zehntel des ursprünglichen Preises zu erstehen, wie das Beispiel der
Compact Disk (CD) Abspielgeräte zeigt. Mit dem Fortschritt des Machbaren sind auch
die Anforderungen an die Leistung gestiegen, der Begriff HIFI-Qualität ist seit langem
von dem der CD-Qualität im Consumer-Bereich ersetzt worden.
Herkömmliche Analogtechnik kann diese steigenden Forderungen schon lange nicht
mehr erfüllen; digitale Aufzeichnungsverfahren haben in ersten Ansätzen schon Ende der
70er Jahre [1] ihren Einzug ins Studio begonnen, was bei den Tonträgern im
Massenmarkt durch CD (Compact Disc) [2], DAT (Digital Audio Tape) und seit
neuestem MD (MiniDisc) und DCC (Digital Compact Cassette) schnell nachvollzogen
wurde.
Die Qualität der Endgeräte ermöglicht eine für den Menschen als perfekt wahrgenommene Tonaufzeichnung und -wiedergabe. Die Studiotechnik muß dieses Niveau immer
übertreffen, damit trotz aller Bearbeitungsschritte während der Produktion, die immer
eine Qualitätseinbuße mit sich bringen können, keine wahrnehmbare Beeinträchtigung
der Qualität stattfindet. Die Fehler der Endgeräte verschleiern die Fehler bei der
Produktion nicht mehr.
Ein Problem bei hochqualitativer Tonaufzeichnung liegt in den sehr großen
Datenmengen, die aufgezeichnet, übermittelt und bearbeitet werden müssen. Bandgeräte
oder Festplatten für diese Datenraten und -mengen sind teuer, für Übertragungsleitungen
und Satellitenkanäle gilt dies noch mehr.
Die Datenmenge sollte also deutlich reduziert werden, ohne daß die Qualität der bearbeiteten Fassung wahrnehmbar beeinträchtigt würde, und sei es auch nur für 'goldene
Ohren'.
Datenreduktionsverfahren, die diese Anforderung für bearbeitete Signale erfüllen, gibt
es seit neuestem1 als ASPEC für die Übertragung über ISDN-, also Telefonleitungen, als
DCC für Kassetten und als DAB für den Rundfunk. Diese Verfahren nutzen die Tatsache,
daß das menschliche Gehör viele Töne nicht wahrnehmen kann, wenn sie von lauteren
Tönen verdeckt ('maskiert') werden. Der Effekt ist umso stärker, je näher die Töne in
Frequenz und zeitlicher Abfolge sind. Man nennt Codierverfahren, die sich dieses
Effektes bedienen, gehörangepaßte Codierung (perceptual Coding). Das Blockdiagramm
eines so arbeitenden Encoders ist in Abb. 1 dargestellt:
1
ASPEC: Herbst 1991, DCC: Herbst 1992
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
1
Digitale
Audio-Daten
768 kBit/s
FrequenzAnalyse
32 Bänder
Lineare
Quantisierung
BitstromErzeugung
Datenreduzierter
Bitstrom
192 - 96/32 kBit/s
SkalenfaktorBerechnung
Freq.-Anal.
512/1024
Linien
Gehör-Modell
Bitzuteilung
Abb. 1: Encoder mit einem Modell des menschlichen Gehörs
Die digitalisierten Tonsignale werden in eine Frequenzdarstellung (links oben) umgewandelt, die vom Gehörmodell beurteilt wird (links unten). Alle wichtigen Signalbestandteile werden dann platzsparend (datenreduziert) codiert (Huffman Codierung, Mitte)
und stehen als Bitstrom zur Weiterverarbeitung bereit. Das Gegenstück des Encoders, der
Decoder, wandelt die Daten wieder in herkömmliche PCM-Audio-Daten zurück.
Der Einsparungseffekt der gehörangepaßten Codierung ist beträchtlich, mit ASPEC
können mit 64 kBit/s Signale in UKW-Sendequalität übertragen werden, verglichen mit
den 705,6 kBit/s eines CD-Kanals ist das 1/11 der Datenrate bei unwesentlich reduzierter
Qualität. Neuere Verfahren, die gerade in der Entwicklung sind, sollen diese Datenrate
nochmals halbieren können.
Für die Arbeit im Studio, wo die Signale gemischt, gefiltert, verhallt, und auf andere
Arten bearbeitet werden sollen, lassen sich diese Verfahren nicht einsetzen: sie sind zu
sehr darauf optimiert, wie das Ohr hört. Durch die Nachbearbeitung gilt eine äußerst
wichtige Voraussetzung nicht mehr: daß das Ohr das Signal direkt so, wie es ist,
angeboten bekommt. Unhörbare Störungen könnten durch diese Art der Codierung und
Nachbearbeitung hörbar werden. Man hätte einen ähnlichen Effekt wie in der
Analogtechnik, man hätte 'Generationen' der Signale.
Aus dieser Überlegung entstand die Forderung nach einer speziellen datenreduzierenden Codierung für Studioanwendungen. Kernidee ist die Verwendung eines
gehörangepaßten Codierverfahrens gleichen Prinzips wie in Abb. 1, jedoch mit einem
anderen Modell und einer modifizierten Codierung, die, neben den Gehöreigenschaften,
vor allem die Bearbeitung im Studio berücksichtigen, und so eine genauere Codierung
des Signals bewirken.
Um abschätzen zu können, welche Datenraten mit einem neuen Verfahren möglich
sein werden, sollen in dieser Arbeit mit einem Simulationsprogramm Messungen
durchgeführt werden, die aufzeigen, mit welcher Datenrate fast verlustfreie Codierung
erreichbar ist.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
2
Zuerst wird ein kurzer Überblick über die aktuellen Verfahren zur Tonsignalübertragung gegeben und es werden die in dieser Arbeit verwendeten Algorithmen vorgestellt.
In Messungen werden die Fälle konstanter Datenrate und vorgegebener Qualität bei
Variation anderer Parameter untersucht. Es folgt eine Übersicht über die entstandenen
Programme und ihre Benutzung für weitere Untersuchungen, eine Diskussion der
Ergebnisse und Vorschläge für weitere Arbeiten.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
3
2. Moderne Audiocodierverfahren
Das neue Verfahren, für das in dieser Arbeit Voruntersuchungen durchgeführt werden,
wurde im März 1992 auf einer Tagung der Audio Engineering Society in Wien erstmals
skizziert [5]. Es ähnelt in vielem dem Entwurf zum ISO/MPEG Standard 11172, Layer
III2, der ebenfalls auf der Tagung erläutert wurde [6].
Zum besseren Verständnis werden zuerst die Grundprinzipien und Fachbegriffe kurz
erklärt und dann wird auf den ISO/MPEG Standard eingegangen.
2.1. Prinzipien moderner Audiocodierverfahren
2.1.1.
Zeitbereichsverfahren
Bei früheren Verfahren zur digitalen Übertragung von Audiosignalen wird versucht,
die Daten im Zeitbereich zu codieren. Das bedeutet, daß die Audiosignale zuallererst von
einer Analog- in eine Digitaldarstellung umgesetzt werden. Dies wird auch bei allen
Verfahren auf den nachfolgenden Seiten der Arbeit vorausgesetzt.
Die einfachste Codierung im Zeitbereich liegt bei der CD und dem DAT vor: die
Daten werden unverändert, nur mit zusätzlichen Fehlerkorrekturinformationen versehen,
übertragen, und beim Decodierer wieder vom Digitalen ins Analoge umgesetzt. Man
nennt dieses Verfahren PCM (Puls Code Modulation3).
Datenreduzierende Verfahren wie DPCM, ADPCM oder NICAM nutzen die zeitliche
Ähnlichkeit des Signals mit sich selbst (Autokorrelation) oder die spezielle Amplitudenverteilung von Sprache und Musik, um mit geringeren Datenraten die gleiche Qualität zu
erreichen. Es ist relativ schwer, aus Tonsignalen im Zeitbereich charakteristische Größen
zu gewinnen. Das ist jedoch die Vorbedingung für eine effektive, platzsparende und
genaue Codierung. Diese Verfahren nutzen Eigenschaften, die die Signalquelle selbst hat,
man nennt sie deshalb auch Verfahren mit source coding.
2.1.2.
Frequenzbereichs- (Transformations-) Verfahren
Wesentlich mehr Möglichkeiten bieten sich, wenn man die Eigenschaften des
Signalempfängers, bei Audiosignalen also des Gehörs, ausnutzt (perceptual Coding,
2
Definition in [12], Beschreibung in [6]
3
englische, speziell selbstgeprägte, Begriffe werden zur Verbesserung der Lesbarkeit kursiv dargestellt
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
4
gehörangepaßte Codierung)4. Für das Gehör ist ein Tonsignal in erster Linie ein Gemisch
von Frequenzen, also nicht, wie bei der Verarbeitung in den Zeitbereichscodern, ein
wechselnd starker Schalldruck. Töne sind natürlich beides, aber die Betrachtung als
Frequenzgemisch erweist sich als diejenige, die bessere Resultate liefert.
Man muß das Audiosignal also nach der Analog-Digital-Umsetzung zuerst in seine
Frequenzanteile zerlegen (Transformation in den Frequenzbereich), und dann die resultierenden Daten weiter verarbeiten. Dieses Verfahren bietet speziell bei Sprache und Musik
Vorteile. Sprache und Musik bestehen, vereinfacht gesehen, zum großen Teil aus relativ
wenigen Tönen im Vordergrund, und allen anderen Tönen im Hintergrund. Die wenigen
Töne im Vordergund lassen sich leicht mit wenigen Angaben, wie Frequenz, Phase,
Energie, genau beschreiben, während die unwichtigen Töne ohne Einbuße am
Höreindruck mit geringerer Genauigkeit übertragen werden können. Das Problem liegt
hier in der Entscheidung, welche Töne wichtig, welche unwichtig sind.
Digitale
AudioDaten
FrequenzAnalyse
Quantisierung
+
Bitzuteilung
EntropieCodierung
Bitstrom
Generierung
Codierter,
datenreduzierter
Bitstrom
GehörModell
Abb. 2: Prinzip eines Transformationscoders
In Abbildung 2 wird das Prinzip der Codierung durch Transformation in den
Frequenzbereich skizziert. Die beschriebene Umwandlung in den Frequenzbereich ist
dabei der Block "Frequenz-Analyse", die anderen Blöcke werden im nachfolgenden
erläutert.
2.1.2.1.
Das Gehörmodell (Psychoakustisches Modell)
Zur Entscheidung, welche Frequenzbestandteile des Signals für einen guten
Höreindruck am wichtigsten sind, werden die Audiosignale von einem Modell des
menschlichen Gehörs beurteilt (in der Abbildung der Block "Gehör-Modell").
Das verwendete Gehörmodell beruht auf Untersuchungen von Zwicker [24]. Es beschreibt, welche Töne wann durch andere Töne verdeckt werden. Dies ist zum einen der
Fall bei Tönen, die auch als Einzeltöne nicht gehört werden können, weil sie zu leise sind
(Grundhörschwelle), zum anderen, weil sie von einem anderen Ton oder anderen Tönen
verdeckt werden (Mithörschwelle)5.
4
vgl. dazu z.B. [7], S. 59f
5
s. [24], S. 35ff
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
5
Die Mithörschwelle setzt sich wiederum aus zwei Erscheinungen zusammen: Liegen
zwei Töne in ihrer Frequenz dicht genug beieinander, dann kann das Fehlen des leiseren
Tons nicht mehr bemerkt werden. Dieser Effekt tritt insbesondere auf, wenn die
verdeckenden Töne ein (schmalbandiges) Rauschen ergeben, das Signal also teilweise
atonal ist. Zur Beurteilung dieses Sachverhaltes teilt man den gesamten Hörbereich in
24 verschieden große Frequenzgruppen (critical bands)6 ein, die der Gehörcharakteristik
nachempfunden sind. Die Breite der Gruppen nimmt von tiefen zu hohen Frequenzen zu,
da die Frequenzauflösung des Gehörs mit steigender Frequenz abnimmt.
Ein weiterer Verdeckungseffekt ergibt sich durch die zeitliche Abfolge von Tönen:
während einiger Millisekunden vor und nach einer lauten Stelle sind leisere Töne nicht
hörbar (Vor- und Nachverdeckung). Laut Zwicker7 liegt die Vorverdeckungszeit bei
1-5 ms bis zu 20 ms, die Nachverdeckungszeit beträgt bis zu 100 ms.
Der genaue Verlauf der Maskierungsschwelle hängt also von der Lautstärke der Töne,
ihrer Frequenz und ihrem Verhältnis zueinander in Frequenz und zeitlicher Abfolge ab.
Alle Töne, die unterhalb dieser Schwelle liegen, kann das Ohr nicht wahrnehmen; sie
müssen also auch nicht übertragen werden. Daß diese Annahme stimmt, wurde in groß
angelegten Feldstudien des schwedischen Rundfunks bestätigt.
2.1.2.2.
Quantisierung
Mit 'Quantisierung', bezeichnet man das Runden der Zahlenwerte auf die gewünschte
Genauigkeit bzw. Ungenauigkeit. Damit wird erreicht, daß nur soviel Information
übertragen wird, wie benötigt wird.
Beispielsweise habe man die vier Zahlen
333, 1066, 1984 , 53.
Reicht eine Genauigkeit von 500-er Schritten, dann werden die Zahlen auf
500, 1000, 2000, 0
gerundet, sie lassen sich durch je nur eine Ziffer, nämlich
1, 2, 4, 0
ausdrücken.
Es gibt verschiedene Arten der Quantisierung, Abb. 3 gibt eine Übersicht über die verschiedenen Grundtypen8. Bei der nichtlinearen Quantisierung (a,c) werden die Zahlen vor
dem Runden nichtlinear verändert (z.B. logarithmisiert). Dabei werden für große Werte
die Abstufungen ebenfalls größer. Bei der linearen Quantisierung werden für alle Werte
gleiche Stufen verwendet (b,d), wobei sich die relative Quantisierungsstufengröße ändert.
6
lt. [24], S. 55 24 Frequenzgruppen bis 16 kHz. Für die Codierung bis 22 kHz erweitert.
7
[24], S. 93
8
entnommen aus [11], S. 117
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
6
Abb. 3: Vier Grundtypen von Quantisierern
Welches der beiden Verfahren besser ist, hängt von der Anwendung ab. Eine weitere
Unterscheidung der Quantisierung liegt darin, ob Werte dicht bei der Null (im
Rechenbeispiel z.B. -30, 53) zu 0 und 0 wie in (c,d) oder zu -1 und 1 wie in (a,b)
quantisiert werden. Wenn wichtig ist, ob die Werte 'fast null' sind, wird man das erste
Verfahren (midtread), wenn das Vorzeichen wichtig ist, das zweite (midrise) verwenden.
2.1.2.3.
Bitzuteilung
Bei der Audiosignalübertragung hat man fast immer eine feste Datenrate, mit der das
Signal übertragen werden muß. Sie muß für eine optimale Übertragung möglichst voll
ausgeschöpft werden, kann jedoch nie überschritten werden.
Falls der Encoder feststellt, daß die Bitrate überschritten würde, muß er also dafür sorgen, daß die Frequenzwerte mit weniger Bits übertragen werden können, selbst wenn das
zur Folge hat, daß das Signal nicht mehr einwandfrei, ohne hörbare Artefakte, codiert
werden kann. Dies kann durch eine gröbere Quantisierung, oder durch eine andere
Gewichtung der einzelnen Frequenzgruppen des Signals geschehen (z.B. durch
Unterdrücken der höheren Töne).
2.1.2.4.
Entropiecodierung mit Tabellen
Die Information, die in einem quantisierten Frequenzwert enthalten ist, ist bei Sprache
und Musik für die einzelnen Werte sehr unterschiedlich. Häufig vorkommende Werte
enthalten wenig Information und umgekehrt enthalten selten vorkommende Werte viel
Information9. Es macht daher Sinn, nicht für alle Werte die gleiche Anzahl von Bits zur
Darstellung zu verwenden.
Würden die Werte durch Codes mit einer Codelänge genau proportional zu ihrem
Informationsgehalt codiert, hätte man die kürzest mögliche Darstellung dieser Werte.
9
Eine mathematischere Beschreibung findet sich z.B. in [11], S. 617ff
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
7
Dies ist aus vielen Gründen nicht möglich, zum Beispiel haben die Codes immer
ganzzahlige Codelängen, und es kann die Wahrscheinlichkeitsverteilung weniger Codes
damit nur grob angenähert werden. Bei real vorliegenden Signalen kann man zudem nur
Aussagen über 'Tonsignale an sich', also Aussagen, die durch möglichst viele
repräsentative Beispiele gewonnen wurden, beim Entwurf von Codern ausnutzen;
spezielle Statistiken für das jeweils vorliegende Signal sind im allgemeinen nicht
praktikabel.
Das Verfahren, ein Signal entsprechend dem mittleren Informationsgehalt, der
Entropie, optimal zu codieren, nennt man Entropiecodierung. Sie wird durch das
Verfahren nach Huffman (vgl. [21], [14]) erreicht.
Die zu codierenden Werte werden dabei in einem binären Baum entsprechend ihrer
Auftretenswahrscheinlichkeit eingeordnet. Der Baum wird oft als Tabelle realisiert und
besitzt ebenso viele Tabelleneinträge wie unterschiedliche Werte zu codieren sind. Bei
sehr vielen unterschiedlichen Werten müssen Maßnahmen zur Verkleinerung der
Tabellen getroffen werden, weil sonst Speicherplatzprobleme entstehen10. Diese
Verfahren reduzieren allerdings auch die Effizienz der Huffman Codes.
2.1.2.5.
Bitstrom-Generierung
Zum Schluß werden die codierten Daten mit den für die Decodierung nötigen Informationen zusammengefaßt, und mit Fehlerkorrektur- und weiteren Zusatzinformationen
(z.B. Texten für die Musiktitel, Kopierschutzinformation) als ein definierter Bitstrom
ausgegeben. Diese Funktion ist für die technische Qualität des Encoders nicht entscheidend, jedoch hängt die Durchsetzungskraft eines Verfahrens zu einem sehr großen
Teil davon ab, daß der Bitstrom standardisiert ist. Ein solcher Standard wird erläutert im
nächsten Abschnitt erläutert.
2.2. Struktur der ISO-Encoder
Im ISO/MPEG Standard 1117211 werden Verfahren standardisiert, mit denen bewegte
Bilder und Tonsignale mittels verschieden aufwendiger Verfahren komprimiert werden
können, um die Bitraten soweit zu reduzieren, daß die Signale preisgünstig gespeichert
werden können, bzw. über Übertragungskanäle mit beschränkter Kapazität (z.B. ISDN
mit 64 kBit/s als Medium für digitale Rundfunk Audiosignale) übertragen werden
können. Es wird der erste internationale Standard für datenreduzierte digitale Ton- und
Bilddaten sein.
10
bei ISO/MPEG Layer III durch mehrere Tabellen und einem Skalierungsfaktor 'linbits', in dieser Arbeit durch stacked
Huffman code tables
11
Diese Einführung stützt sich bei der Beschreibung des Standards vorwiegend auf [6].
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
8
Der Standard gliedert sich in drei Layer, also Schichten oder Lagen. Die unterste
Schicht, Layer I, ist die am wenigsten aufwendige, aber auch die, welche die geringste
Datenkompression erzielt. Die Komplexität der Encoder, aber auch ihre Leistungsfähigkeit, steigen von Layer I bis zu Layer III. Alle Layer arbeiten mit verschiedenen
Bitraten, wobei die Tonqualität mit sinkender Bitrate selbstverständlich abnimmt. Bei
gleicher Bitrate ist jedoch der Layer III dem Layer II überlegen, ebenso wie der Layer II
dem Layer I.
Alle drei Layer, die im ISO/MPEG Standard zur Audiocodierung festgelegt sind,
nutzen die gehörabhängige Codierung. Bei der Übertragung kann die Datenrate zusätzlich
durch Entropiecodierung reduziert werden, wie es im Layer III geschieht.
Der prinzipielle Aufbau eines Encoders mit Berücksichtigung von Psychoakustik
(Perceptual Audio Encoder) wurde schon in Abb. 2 auf Seite 5 vorgestellt. Das Tonsignal
wird zuerst in den Frequenzbereich transformiert, wobei die Zahl der Frequenzlinien oder
Teilbänder zwischen 32 (Layer I, II) und 576 (Layer III) liegt.
Layer I eignet sich somit für Anwendungen, bei denen keine extrem niedrigen Datenraten gefordert werden, also 192 kBit/s bis 96 kBit/s pro Monokanal. Anwendungen sind
Festplattenspeicherung, Bandlaufwerke, optische Disks. Ein mit dem Layer I kompatibles
Verfahren wird z.B. in der DCC verwendet.
Layer II bietet durch verbesserte Codierung Datenraten bis hinunter zu 32 kBit/s an;
Anwendungen sind hier digitaler Rundfunk (DAB), multimediale Anwendungen, Telekommunikation, sowie weitere Anwendungen im professionellen und dem Endverbraucher-Markt.
Layer III schließlich ermöglicht bei Datenraten von ebenfalls 192 kBit/s bis 32 kBit/s
professionelle Audioübertragungen über Schmalband - ISDN (64 kBit/s) und andere
Anwendungen, wo ein relativ hoher Aufwand durch die erreichte Qualität bei niedrigen
Bitraten gerechtfertigt ist.
2.2.1.
Encoder für Layer I und II
In den Layern I und II werden die Frequenz-Teilbänder mit dem psychoakustischen
Modell nach Wichtigkeit für den Höreindruck mit Skalierungsfaktoren gewichtet und die
in jedem Block zur Verfügung stehenden Bits entsprechend zugeteilt. Ziel der Zuteilung
ist es, das Quantisierungsrauschen unter der Hörbarkeitsschwelle zu halten, also die
Noise to Mask Ratio möglichst klein zu halten.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
9
Digitale
Audio-Daten
768 kBit/s
FrequenzAnalyse
32 Bänder
Lineare
Quantisierung
Datenreduzierter
Bitstrom
192 - 96/32 kBit/s
BitstromErzeugung
SkalenfaktorBerechnung
Freq.-Anal.
512/1024
Linien
Gehör-Modell
Bitzuteilung
Abb. 4: Blockdiagramm des ISO/MPEG Audio Encoders Layer I/II
Die Skalierungsfaktoren für die Teilbänder und die Amplituden der Teilbänder werden
dann zusammengefaßt und mit Fehlerkorrektur- und Zusatzinformation versehen als
Bitstrom ausgegeben. Layer II verwendet ein genaueres psychoakustisches Modell und
spart Bits bei der Übertragung von Skalierungsfaktoren aufeinanderfolgender Blöcke ein,
die einer besseren Signalqualität zugute kommen.
Im Gegensatz zum Layer III wird nicht kontrolliert, welche Qualität aus der Skalierung
resultiert. Dadurch wird der Encoder einfacher im Aufbau, aber auch nicht so leistungsfähig.
2.2.2.
Encoder für Layer III
Bei Layer III wird durch mehr Aufwand eine bessere Datenkompression erzielt (vgl.
Abb. 5). Das Gehörmodell modelliert das Gehör genauer als das normalerweise in den
Layern I und II verwendete Modell, es modelliert die kritischen Bänder des Gehörs, und
damit mit einer Tonalitätsabschätzung auf Basis eines polynominalen Prädiktors und der
spreading function des Innenohrs die Maskierungsschwelle des Gehörs. Die spreading
function beschreibt den Zusammenhang zwischen Maskierung eines Tones als Funktion
der benachbarten Töne. Die Tonalitätsabschätzung ermöglicht eine Unterscheidung
zwischen rauschähnlichen und tonalen Signalstellen, und damit eine Aussage über die
Maskierung durch Rauschen. Die Grundhörschwelle wird vorsichtigerweise zu weniger
als ein LSB bei 4 kHz angenommen, da ja der absolute Hörpegel nicht bekannt ist.
PCM
Daten
768 kBit/s
FensterlängenUmschaltung
FFT
1024 Linien
Nichtlin. Quantisierer
BitratenKontrollschleife
GehörModell
HuffmanEncoder
Codierung
der Seiteninformation
BitstromFormatierung
Verzerrungs - Kontrollschleife
Filterbank +
MDCT
576 Linien
Codierte
Audio-Daten
192 - 32 KBit/s
Abb. 5: Blockdiagramm des ISO/MPEG Audio Encoders Layer III
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
10
Zusätzlich schätzt das Modell den Umfang von Vorechos ab, die durch den
plötzlichen Anstieg der Signalenergie in wenigstens einem Teil der Bänder hervorgerufen
werden können. Diese Information wird zur Steuerung der Fensterlänge des Hybridfilters
verwendet. Durch ein kurzes Fenster wird die Frequenzauflösung auf 192 Linien
verringert, aber die Vorechos werden gleichzeitig auf einen kurzen, meist nicht mehr
hörbaren Bereich von 8 ms beschränkt. Das normale lange Fenster von 24 ms (bei 48 kHz
Abtastrate) weist eine bessere Frequenzauflösung von 576 Linien auf, eignet sich jedoch
nur für sich nicht zu schnell ändernde Signale.
Die eigentliche Codierung besteht aus zwei ineinander verschachtelten Schleifen. In
der äußeren, der Verzerrungs-Kontrollschleife, wird die Einhaltung der Signalqualität
nach den durch das Gehörmodell gegebenen Anforderungen überwacht.
In der inneren Schleife, der Bitraten-Kontrollschleife, wird durch Anpassung der
Quantisierung die Bitrate unter das vorgegebene Maximum gesenkt. Der Quantisierer
arbeitet nichtlinear, da die daraus entstehenden Signalverfälschungen das Ohr weniger
stören, als die Verfälschungen eines linearen Quantisierers. Die Bitrate wird durch
Entropiecodierung im Huffman Encoder reduziert; diese Reduktion wird bei jedem
Durchlaufen der inneren Schleife neu berechnet. Durch die Technik des sog. Bitreservoirs
können Bits, die in einem Block nicht benötigt werden, in späteren Blocks, die mehr Bits
benötigen, verwendet werden, und somit Störungen an kritischen Stellen vermindern oder
ganz unterdrücken.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
11
3. Prinzipien des neuen Verfahrens
Das neue Verfahren unterscheidet sich durch die Zielsetzung von den ISO/MPEG Codierverfahren: das nahezu verlustfreie Codieren bei höheren Bitraten mit Lauflängencodierung der Daten im Frequenzbereich.
Die Betonung liegt dabei auf "nahezu verlustfrei" (Near Lossless Quality), und erst in
zweiter Linie auf der Reduzierung der Bitraten. Der Ansatz ähnelt dabei den Konzepten
der ISO Encoder, wobei die Komplexität des fertigen Verfahrens deutlich unter
derjenigen des Layer III und über derjenigen der LC-ATC liegen soll12. Ein aufwendiges
Teilverfahren, wie die Fensterumschaltung bei Layer III, wird deshalb nicht Bestandteil
des neuen Verfahrens sein.
Ziel ist eine Anwendung im gesamten professionellen Bereich, während die bisherigen
ISO Encoder mehr auf den Endanwender ausgerichtet sind, und Layer III auf spezielle
Anwendungen im professionellen Bereich.
Neu in dieser Arbeit ist neben der anderen Zielvorgabe und einer daraus resultierenden
anderen Optimierungszielrichtung die Verwendung von Stacked Huffman Codes zur Entropiecodierung. Als weiterer Schritt ist der Einsatz eines psychoakustischen Modells zur
Detektierung rauschähnlicher Signale vorgesehen, was aus Zeitgründen jedoch leider
nicht realisiert werden konnte.
3.1. Near Lossless Coding
Der Begriff des Near Lossless Coding (NLC) ist neu. Er wird indirekt, über sein Verhalten auf Tonsignale definiert. In dieser Arbeit gilt folgende vorläufige Definition13:
1.
Für tonale Signale soll das Quantisierungsrauschen auf 1 LSB oder
weniger beschränkt bleiben.
2.
Für nichttonale, rauschähnliche Signale soll das Quantisierungsrauschen
mindestens 20 dB unter der Maskierungsschwelle liegen. Mit einem
psychoakustischem Modell soll garantiert werden, daß diese Bedingung
nicht verletzt wird.
3.
Die Zahlenwerte beziehen sich auf ein Signal mit 16 Bit pro Abtastwert.
12
[5], S. 9, die Komplexität der LC-ATC entspricht ungefähr der des Layer I
13
Brandenburg, persönliches Gespräch 6/92, sowie [5]
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
12
Diese Anforderungen sollten genügen, um auch nach mehrmaligen Codierungs- / Decodierungsschritten (Tandemcodierungen) und weiterer Bearbeitung nach jedem Schritt
keine hörbaren Artefakte zuzulassen. Andererseits läßt diese Definition genügend Spiel,
um effizient codieren zu können.
Das 2. Kriterium beinhaltet allerdings, daß herkömmliche Meßmethoden, z.B. das
SNR, keine aussagekräftigen Meßwerte für die Qualität der Codierung liefern können,
wie schon bei anderen Encodern, die mit Psychoakustik arbeiten (OCF, ASPEC), gezeigt
wurde. Diese Problematik ist in [7] ausführlich erläutert. Als Meßmethode wurde
deshalb, wie bei den erwähnten Codierverfahren, das Programm NMR14 benutzt, das
unter anderem die Noise to Mask Ratio (NMR) direkt in dB angibt.
3.2. Codierung im Spektralbereich
Das neue Verfahren soll möglichst unveränderte Signale liefern, ersatzweise Signale,
deren Veränderung weit unter der Hörbarkeitsschwelle liegt. Wie bei den ISO-Codern
lassen sich Entropiecodierung und Psychoakustik nutzen, um bei Engpässen in der zur
Verfügung stehenden Datenrate die psychoakustisch irrelevanten, weil maskierten,
Signalanteile vorrangig zu vermindern.
Dies kann zu einer nicht perfekten Rekonstruktion führen, werden die Ein- und Ausgangssignale durch Differenzenbildung verglichen. Entsprechend der Forderung nach
NLC dürfen jedoch durchaus Abweichungen größer 1 LSB auftreten, solange die
Maskierungsschwelle um den geforderten Betrag unterschritten wird. Das bedeutet in der
Praxis, daß auch bei Unterschieden in den 3-4 LSB NLC vorliegen kann.
3.3. Huffman Codierung
Bei Codierverfahren mit guter Reproduktionsgenauigkeit treten zu codierende
Spektralwerte einer großen Zahl relevanter Bits, also in einem großen Wertebereich auf.
Um die Tabellen zur Codierung nicht zu groß machen zu müssen, werden Escape-Werte
verwendet. Werte größer oder gleich dem Escape-Wert werden nicht direkt durch ihren
Huffman Code codiert, sondern in einen Basisanteil und den Rest zerlegt.
In der Implementation von Layer III werden dazu verschiedene Tabellen in
Verbindung mit einem Skalierungsfaktor linbits benutzt, die jedoch gerade große Werte
nur mit beschränkter Genauigkeit beschreiben. In dieser Arbeit wurde deshalb erstmals
ein neues Verfahren mit Escape-Codes verwendet.
14
s. [7], S. 24ff, ursprünglich in [8]
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
13
3.3.1.
'Stacked' Tabellen
In dem neuen Verfahren treten häufig (je nach Signal 30%-80%) Spektralwerte auf,
die die Tabellengröße überschreiten. Wegen der Forderung nach hoher
Reproduktionsgenauigkeit erscheint ein anderer Lösungsansatz zur Codierung großer
Werte sinnvoller: sog. Stacked Huffman Code Tables (SHCT)15.
Werte, die größer oder gleich dem Escape-Wert sind, werden mit einer weiteren
Tabelle, der Escape-Tabelle, codiert. Zunächst wird der Wert an sich codiert, falls er kleiner als der Escape-Wert ist. Andernfalls wird der jeweilige Restwert, der nach der
Subtraktion des Escape-Wertes verbleibt, durch wiederholte Subtraktion und Codierung
um den Escape-Wert vermindert, bis ein Wert kleiner als der Escape-Wert als letzter
Wert codiert wird. Das Verfahren wird auf Seite 22 genauer erläutert.
3.3.2.
Mehrdimensionale Codierung
Wie in Layer III [12] werden 2 oder 4 Werte jeweils zu einem Huffman Code zusammengefaßt. Dadurch können die zur Verfügung stehenden Bits besser ausgenutzt werden;
die Codelängenzuteilung ist effektiver, weil die Wahrscheinlichkeitsverteilung der Spektralwerte besser approximiert wird, so daß mittlere Codelängen von unter 1 Bit/Spektralwert möglich werden.
Der Nachteil liegt in der Tabellengröße: Kann der Spektralwert n Werte annehmen, so
benötigt man für die Codierung von k Werte in einer Huffman Code Tabelle eine
Tabellengröße t von t = n k Werten. Bei Maximalwerten von n=128 und k=2 für
Untersuchungen in dieser Arbeit liegt der Speicherverbrauch schon bei t = 16384 Worten,
und damit über dem Speicherbereich gängiger Signalprozessoren, wie dem DSP56001.
Diese Größe ist für praktische Implementierungen sicher zu groß, dort wird man Werte
von n=16 bis n=64 vorziehen.
3.4. Ausnutzung signalstatistischer und psychoakustischer
Effekte
Das neue Verfahren soll in einer späteren Version ein psychoakustisches Modell zur
Kontrolle der Qualität des codierten Signals benutzen, ähnlich dem in Layer III
verwendeten16. Im Rahmen dieser Arbeit wurde ein solches Modell nicht eingesetzt;
einige einfache Erkenntnisse der Psychoakustik und der Signalstatistik werden allerdings
angewandt.
Der einzige psychoakustische Effekt, der in dieser Arbeit ausgenutzt wird, ist die
Tatsache, daß die nicht bitgetreue Reproduktion von rauschähnlichen Signalen vom Ohr
15
nach einer Idee von Brandenburg, pers. Gespräch 8/92
16
s. [5], S. 7f
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
14
nicht bemerkt wird, so daß das Rauschen, was für die Entropiecodierung wegen seiner
geringen Entropie ein Problem darstellt, durch das Kriterium 2 des Abschnitts "3.1. Near
Lossless Coding" gelöst werden kann.
Bei Sprache und Musik fällt die Energiedichte mit steigender Frequenz. Die
quantisierten Werte werden deshalb in drei Kategorien eingeteilt:
•
Die Spektrallinien im unteren Spektralbereich enthalten die größten
Spektralanteile und müssen am besten übertragen werden (hier big_values
genannt). Sie werden durch 2-dimensionale Huffman Tabellen vollständig
codiert, d.h. der quantisierte Wert wird mit unter Umständen sehr langen,
meist jedoch kurzen Codes codiert.
•
Es schließen sich die kleinen Spektrallinien, nur mit den Werten zwischen
-1 und +1(small_values) an. Sie werden in dieser Arbeit in Vierergruppen
(Quadrupeln) codiert.
•
Die obersten Spektrallinien (zero_values) sind nullwertig und werden
nicht übertragen.
Je nach Signal kann die Gruppe der big_values den ganzen Spektralbereich abdecken,
oder alle Spektrallinien können zero_values sein. Die Aufteilung wird für jeden Block
neu berechnet. Die Bitrate läßt sich durch den Einsatz eines psychoakustischen Modells
sicherlich weiter stark reduzieren, wie es die Messungen der NMR vermuten lassen (vgl.
S. 35f).
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
15
4. Implementierung des Verfahrens
In diesen Abschnitt wird auf die Algorithmen, die dieser Arbeit zugunde liegen, eingegangen. Einen Überblick über den Signalfluß des Simulationsprogramms 'pacse' gibt
Abb. 6:
32 Bit
PCM
Daten
Huffman-Code
Tabellen
Coder/Decoder
NLC-Ermittlung
Statistiken-Erfassung
ich
ch
ere
rei
itb
be
z
Ze
n
ue
eq MDCT
Fr
Bitraten- oder
Qualitätskontrolle
Quantisierer
EntropieCoder
StatistikenErfassung
Dequantisierer
Fr
eq
u
Ze enzb
itb
e
ere reic
h
ich
IMDCT
NLC - Schleife
32 Bit PCM Daten
codiert und decodiert
Frequenzamplituden-Histogramm
2D-Huffmancode-Histogramm
Quantisierte Frequenzwerte
Bitraten für NLC
Blockweise Fehlerstatistiken
PE-Schätzungen
Abb. 6: Signalfluß im Encoder-Simulationsprogramm 'pacse'
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
16
Die Eingangsdaten werden per MDCT in den Frequenzbereich transformiert, quantisiert, mit ein- oder zweidimensionalen 'stacked Huffman codes' codiert, dequantisiert und
wieder in den Zeitbereich zurücktransformiert.
Je nach Fragestellung wird dabei eine feste Bitrate vorgegeben und die Signalqualität
bestimmt, oder Near Lossless Coding vorgegeben und die Bitrate bestimmt. Dabei werden Statistiken erstellt, mit denen neue Huffman Code Tabellen generiert und iterativ optimiert werden können. Die Statistiken geben außerdem Auskunft über Codierfehler, sowohl blockweise und als auch summarisch.
4.1. Codierung durch Transformation per MDCT
Es wird eine MDCT mit M Spektrallinien, also N=2M Zeitbereichswerten verwendet.
Die transformierten Zeitbereichsblöcke überlappen sich jeweils um M Werte, so daß sich
nach der Rücktransformation die entstehenden Überfaltungsterme kompensieren (time
domain aliasing cancellation). Die Untersuchungen in dieser Arbeit wurden mit M=512
durchgeführt; das Programm 'pacse' unterstützt jedoch auch kleinere Zweierpotenzen als
Blockgröße.
4.1.1.
Definition der MDCT
Die in dieser Arbeit verwendete MDCT ist in [10] als OBT beschrieben und
folgendermaßen definiert:
X t ( k )=
1 M −1
2π
1
⋅ ∑ xt ( n ) ⋅ cos
k+
M n=0
M
2
n+
M 1
+
4 2
(1)
dabei sind:
N
die Blocklänge im Zeitbereich
M = N/2
die Blocklänge im Frequenzbereich
Xt ( k )
die Frequenzlinien des t-ten Blockes
k = 0 K M −1
xt ( n)
das Zeitsignal des t-ten Blockes
n = 0K N −1
Die inverse Transformation ist entsprechend definiert zu:
M −1
y t ( n) =
∑X
k =0
t
⋅ cos
2πk
M 1
n+
+
M
4 2
(2)
Wie dort ausgeführt, reichen für eine gerade Zahl von N Werten M Koeffizienten zu
einer vollständigen Rekonstruktion aus. Es gilt nämlich:
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
17
Xt ( M − 1 − k ) = − Xt ( k )
(3)
Es liegt also eine Unterabtastung im Frequenzbereich vor, die einen Aliasing-Term
zum transformierten und rücktransformierten Signal hinzufügt:
M
−1− n
2
2
y t ( n) =
3M
xt (n) + x t
−1− n
2
2
x t ( n) − x t
0≤n<
M
2
(4)
M
≤n<M
2
Das Zeitsignal yt ( n ) setzt sich nach der Transformation und Rücktransformation aus
dem Original- und dem Alias-Anteil zusammen; der Alias-Anteil der zweiten Blockhälfte
besitzt dabei das entgegengesetzte Vorzeichen des Anteils der ersten Hälfte.
Werden die Transformationen auf aufeinanderfolgende Blöcke angewandt, die sich jeweils um M Werte überlappen, dann löschen sich die Alias-Anteile vollständig aus. Das
Zeitsignal x% t ( n) ergibt sich also zu
q = 0K M −1
x% t ( n ) = yt −1 ( q + n ) + yt ( q )
(5)
Daraus folgt, daß in jedem Codierungsschritt M neue Daten verarbeitet werden.
Fensterfunktion
Wird zur Vermeidung von Blockgrenzeneffekten eine Fensterfunktion f ( n) mit den
Charakteristika
f (n) = f (2 N − 1 − n)
f 2 (n) + f 2 ( N − 1 − n) = 2
0≤n<N
in die Formeln (1) und (2) folgendermaßen eingefügt,
X t (k )=
1 M −1
2π
1
⋅ ∑ f (n) ⋅ xt ( n ) ⋅ cos
k+
M n=0
M
2
M −1
xt ( n ) = f (n) ⋅ ∑ X t ( k ) ⋅ cos
k =0
n+
M 1
+
4 2
(6)
2π k
M 1
n+
+
M
4 2
(7)
so gilt die Auslöschung der Alias-Terme immer noch, wie in [10] gezeigt wurde.
In dieser Arbeit wird als Fensterfunktion ein Sinusfenster verwendet:
f (n) = sin π
2n + 1
4M
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
0≤n<N
(8)
18
4.1.2.
Der Algorithmus nach Brandenburg
Der bisher effizienteste Algorithmus ist in [22] beschrieben; in dieser Arbeit wird statt
dessen der leichter zu implementierende schnelle "Algorithmus nach Brandenburg" angewandt.17
Der Algorithmus beruht auf einer DCT, deren Daten vor und nach der Transformation
bzw. Rücktransformation umgespeichert und im Vorzeichen angepaßt werden. Er wird in
folgenden Schritten durchgeführt:
Hintransformation:
1.
Umspeichern
3N
)
4
N
x( j − )
4
j = 0K
− x( j +
v( j ) =
2.
N
j = K N −1
4
(9)
Berechnung der Diskreten Cosinus Transformation
N −1
C (m) = DCT{v(k)} = ∑ v(k ) ⋅ cos π ⋅
k =0
3.
N
−1
4
(2k + 1) ⋅ m
2N
m = 0K N −1
(10)
λ = 0 K M −1
(11)
Unterabtastung
X (λ ) = C (2λ + 1)
Rücktransformation
1.
Umspeichern und Verdoppeln der Anzahl der Werte
w( j ) =
2.
X ( j)
j = 0 K M −1
− X ( N − j − 1)
j = M K N −1
(12)
Berechnung der Diskreten Cosinus Transformation
N −1
D(m) = DCT{w(k )} = ∑ w(k ) ⋅ cos π ⋅
k =0
17
(2k + 1) ⋅ m
2N
m = 0K N −1
(13)
nach dem Zitat von [21] in [7], S. 69ff, C-Fassung von Max Miegler 1992
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
19
3.
Umspeichern
D( 2 p + 1 + M ) ⋅
1
2N
1
2N
1
− D(2 p − 3M + 1) ⋅
2N
y ( p ) = − D(3M − 1 − 2 p) ⋅
M
−1
2
M 3M
p= K
− 1 (14)
2
2
3M
p=
− 1KM − 1
2
p = 0K
4.2. Huffman Code Tabellen
Alle Huffman Code Tabellen (HCT), die für die Entropiecodierung benötigt werden,
werden mit dem Programm 'huffgen' generiert. Der Algorithmus wurde aus [21] übernommen, in C-Code umgesetzt, und für große Tabellen nahezu beliebiger Größe optimiert18.
Der Algorithmus erhält als Eingangsdaten eine Tabelle H ( k ) der Größe n, in der die
absolute Häufigkeit der zu codierenden Werte eingetragen sind. In der gegebenen
Aufgabenstellung handelt es sich um das Häufigkeitshistogramm des Betrages der
Spektralwerte nach der Quantisierung, das heißt an einem konkreten Beispiel, daß in
H( 5) die absolute Anzahl der Werte über alle untersuchten Musikdateien steht, die genau
5 betrugen.
4.2.1.
Unterdrückung von Null-Werten
Liegen viele Nullwerte in der Tabelle vor, dann degeneriert der binäre Baum, der daraus gewonnen wird (siehe nächsten Abschnitt), zu einer linearen Liste. Die daraus
gewonnenen Huffman Codes werden als Konsequenz sehr lang. Durch eine einfache Ersetzungsoperation kann dies vermieden werden:
Für H (k )
= 0 setze H (k ):= 1
<> 0 setze H (k ):= 2 ⋅ H (k )
(15)
Mit dieser Maßnahme werden zwei nullwertige Einträge in ihrer Wertigkeit Einträgen
mit dem Wert 1 gleichgestellt. Eine genauere Anpassung an die durch die Häufigkeitsverteilung angenäherte Wahrscheinlichkeitsverteilung kann durch Wahl einer höheren
Zweierpotenz anstelle von 2 in der unteren Zeile erzielt werden; für große absolute Häufigkeiten besteht dann allerdings eine größere Gefahr eines Überlaufs, wenn die Werte
wie im Programm 'huffgen' bzw. der Bibliothek 'hufflib' als ganzzahlige Werte gespeichert werden.
18
Zur Beschreibung des Algorithmus s. [14], [21]
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
20
4.2.2.
Erzeugung der Huffman Codes
Die Erzeugung des Huffman Codes sei an einem Beispiel erläutert. Gegeben sei eine
Tabelle H ( k ) in Tab. 1 mit 8 Einträgen, sowie die aufbereiteten Daten H ′ ( k ) in Tab. 2:
k
H(k )
k
H ′( k )
0
10
0
20
1
2
1
4
2
3
2
6
3
5
3
10
4
3
4
6
5
0
5
1
6
1
6
2
7
0
7
1
Tab. 1: Beispieldaten für Huffman Code Generierung
Tab. 2: nach Nullwertbeseitigung
Die Huffman Codes werden ermittelt, indem zuerst eine binäre Baumstruktur nach den
Daten aufgebaut wird. Die Knoten des Baumes werden mit den Indizes der Tabelle
durchnummeriert.
Die Schritte zur Konstruktion des Baumes:
1.
Die Zeilen werden nach Größe geordnet.
2.
Der kleinste Wert wird rechts (bzw. in Abb. 7 unten) eingeordnet, er erhält
den Binärcode 1, der zweitkleinste den Wert 0.
3.
Die beiden kleinsten Werte werden zu einem neuen Wert
zusammengefaßt, die Tabelle wird um eine Zeile verkürzt. Die neue Zeile
erhält die nächsthöhere Nummer.
Zugleich wird ein neuer Knoten im Baum erzeugt, der die beiden Werte
als Kind-Knoten hat, sein Wert ist die Summe aller Knoten, die unter ihm
liegen.
4. - n.
1.-3. werden wiederholt, bis nur noch ein Knoten übrig ist: die Baumwurzel (W). Der Baum (mit den Ausgangsknoten als stärker umrandete
Kreise) sieht nach einer Drehung um -90° gegenüber der üblichen Darstellung im Beispiel folgendermaßen aus:
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
21
010
2
11
011
4
13
000
3
1
6
10
9
5
W 0010
12
0
00110
1
001110
8
7
001111
Abb. 7: Beispiel Huffman Baum mit Huffman Codes (um -90° gekippt)
Danach wird der Baum durchlaufen, und die auf der rechten Seite aufgeführten Codes
als Codewort plus Längeninformation in zwei Tabellen C ( k ) , L ( k ) mit Größe n abgelegt,
und die Baumstruktur in einer Tabelle T ( k ) der Größe 2n, wobei in T ( 0 ) K T ( n − 1) die
linken Baumäste (in Abb. 7 die oberen Äste), und in T ( n) K T (2 n − 1) die rechten
(unteren) Äste zur Tabelle H ( k ) abgespeichert sind.
4.3. Eindimensionale Stacked Huffman Tabellen
Mit den Stacked Huffman Code Tabellen (SHCT) sollen die Beträge der quantisierten
Frequenzamplituden durch Entropiecodierung effizienter übertragen werden. Dazu wird
zuerst vom Programm 'pacse' ein Histogramm mit den Häufigkeitsverteilungen der
Beträge der quantisierten Frequenzamplituden erstellt. Dieses Histogramm sollte über
eine repräsentative Auswahl an Musikstücken erfaßt werden.
Diese Histogramm-Tabelle, die einen sehr großen Umfang haben kann (216-219 verschiedene Werte), wird vom Programm 'huffgen' in eine stacked table (rekursiv
verschachtelt codierte Tabelle) umgewandelt. Sie besteht aus zwei kleinen Tabellen der
Größe von üblicherweise n = 25-26. Der Anreiz, verschachtelt codierte Huffman Codes
(stacked Huffman codes) zu verwenden, liegt in dem wesentlich verringerten Speicherplatzverbrauch für die Codierung und Decodierung der genau übertragenen, quantisierten
Spektralwerte. Mit den oben angegebenen Zahlen ergibt sich eine Einsparung um das 29bis 214-fache, wobei die Bitrate durch die Verwendung kleinerer Tabellen nicht
wesentlich ansteigt.
4.3.1.
Codierung der Spektralwerte mit eindimensionalen Tabellen
Ein Spektralwert19 S wird mit den Huffman Codes aus zwei Tabellen H Low ( k ) und
HEsc ( k ) jeweils der Größe N codiert. Diese Tabellen werden als stacked Huffman Code
19
es wird auch im folgenden nur der Betrag betrachtet
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
22
Tables (SHCT) bezeichnet. Der maximale Wert, der mit einer Tabelle direkt codiert werden kann, ist
Smax = esc − 1 = N − 2
(16)
Der Escape-Wert esc hat den Wert
esc = N − 1
(17)
da die Tabellen den Bereich 0 KN − 1 umfassen. Die Huffman Codes werden folgendermaßen generiert:
1.1
Ist S ≤ Smax , dann wird der Wert mit H Low ( k ) codiert, und dieser Spektralwert ist damit vollständig übertragen.
1.2
Für S ≥ esc wird H Low (esc ) ausgegeben, und damit angezeigt, daß weitere
Codes für diesen Spektralwert folgen.
2.
Es wird solange HEsc (esc ) ausgegeben, wie für S% := S% − esc gilt: S% > Smax .
3.
~
~
Schließlich wird H Esc (S ) ausgegeben, wobei H Esc ( S ) ≠ H Esc (esc) gilt.
Damit wird angezeigt, daß keine weiteren Codes für S mehr folgen.
4.
4.3.2.
Falls S ≠ 0 war, wird als Vorzeichen für S < 0 eine 1, sonst eine 0
übertragen.
Histogrammerfassung zur Generierung der eindimensionalen SHC
Die absoluten Häufigkeiten aus den Statistikdateien20 werden in eine Eingangstabelle
E (i ) der Größe I eingelesen und anschließend zwei Tabellen HAlow ( k ) und HAesc ( k ) der
absoluten Häufigkeiten (Größe I ≥ N ) generiert.
Die beiden Tabellen HA geben die absoluten Häufigkeiten der Codes an, die für das
Verfahren im letzten Abschnitt benötigt werden. Sie ergeben sich durch folgende Beziehungen:
HAlow (i ) := E (i )
HAlow (esc ) := HAlow (esc ) + E (i )
HAesc (esc ) := HAesc (esc ) + ((i − esc ) DIV esc ) ⋅ E (i )
HAesc ((i − esc ) MOD esc ) := HAesc ((i − esc ) MOD esc ) + E (i )
i < esc
(18)
i ≥ esc
Die letzten beiden Zeilen lassen sich noch etwas vereinfachen, z.B. durch Einführung
einer temporären Variablen mit dem Wert i − esc .
20
Zur Gewinnung der Statistikdateien siehe S. 56
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
23
Beide Tabellen werden zu einer Tabelle HAstacked zusammengefaßt, die bei der
Generierung der Huffman Codes wie eine einzige Tabelle behandelt wird:
HAstacked (k ) =
HAlow (k )
HAesc (k − N )
0≤k < N
N ≤ k < 2N
(19)
4.4. Zweidimensionale Stacked Huffman Code Tabellen
Als weitere Verbesserung zu den eindimensionalen HCT lassen sich je zwei Spektralwerte durch einen Code codieren, womit erreicht wird, daß die erzeugten Codes häufig
vorkommender Spektralwertpaare kürzer werden, also die Entropie von Wertepaaren statt
nur diejenige von Einzelwerten ausgenutzt wird.
Bei zweidimensionalen Tabellen wächst die Größe mit dem Quadrat der erlaubten
Spektralwerte, so daß aus Gründen der Speicherbelegung die Umsetzung in verschachtelte Tabellen schon bei der Datenerfassung erfolgen muß.
4.4.1.
Codierung der Spektralwerte mit zweidimensionalen Tabellen
Es werden immer zwei aufeinander folgende Spektralwerte Sx , S y gleichzeitig codiert.
Wie im eindimensionalen Fall werden zwei Tabellen zur Codierung verwendet, wobei jedoch die Dimensionen anders sind. Mit den Vereinbarungen (16), (17) wird definiert:
H 2 Low ( x , y )
0 ≤ x ≤ esc
0 ≤ y ≤ esc
(20)
H 2 Esc ( z )
0 ≤ z ≤ esc
(21)
Die Schreibweise x / y bedeute, daß die jeweiligen Schritte jeweils für x und y
durchgeführt werden müssen. Die Huffman Codes werden damit folgendermaßen generiert:
1.1
Ist S x / y ≤ Smax , dann wird der Wert mit H 2 Low ( x , y ) codiert, und diese
Spektralwerte sind damit vollständig übertragen.
1.2
Andernfalls beginnt eine längeren Codesequenz, die durch Verwendung
eines Tabelleneintrages mit mindestens einem esc - Wert markiert wird:
Sx ≤ Smax
Sy ≤ Smax
ja
nein
ja
nein
ja
ja
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
Ausgabe von
H 2 Low ( x, esc )
H 2 Low (esc, y )
H 2 Low (esc. esc )
24
2.
Es werden die Restwerte mit der Tabelle H 2 Esc ( z ) codiert, wobei
Smax , Smin den kleineren bzw. größeren Wert von Sx , Sy bezeichne:
Sx ≤ Smax
Sy ≤ Smax
Ausgabe folgender Codes:
ja
nein
(( S x DIV esc) − 1) * H 2 Esc (esc)
nein
ja
(( S y DIV esc) − 1) * H 2 Esc (esc)
ja
ja
Flagbit: S x DIV esc
(min(S x div esc, S y div esc ) − 1) * H 2 Esc (esc )
H 2 Esc ( S min mod esc)
H 2 Esc (esc) * (( S max div esc) − ( S min div esc))
H 2 Esc ( Smax mod esc )
3.
Für S x / y ≠ 0 wird jeweils 1 Bit als Vorzeichen übertragen. Für S x / y < 0
wird eine 1, sonst eine 0 übertragen.
4.4.2.
Histogrammerfassung zur Generierung der zweidimensionalen
SHC
Die absoluten Häufigkeiten der Spektralamplituden werden nicht erst im Programm
'huffgen', sondern schon im Programm 'pacse' in zwei Histogramm-Tabellen HA2 Low ( k ),
HA2 Esc ( k ) eingeordnet. Wie im eindimensionalen Fall werden in diesen Tabellen die
Häufigkeiten der einzelnen Huffman Codes gezählt.
Während des Programmlaufs von 'pacse' wird für jedes Paar von Spektralwerten Sx / y
die tatsächlichen Codierung simuliert, es werden also folgende Operationen durchgeführt:
1.1
Sx / y ≤ Smax
HA2 Low ( x , y ) := HA2 Low ( x , y ) + 1
Die Spektralwerte sind damit abgearbeitet.
1.2
Andernfalls wird in HA2 Low ein Escape-Eintrag erhöht:
Sx ≤ Smax
Sy ≤ Smax
ja
nein
ja
nein
ja
ja
Inkrementierung um 1 von
HA2 Low (esc, y )
HA2 Low ( x , esc )
HA2 Low (esc, esc )
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
25
2.
Die Zahl der Escape-Codes in der Escape-Tabelle HA2 Esc ( z ) wird erhöht:
Sx ≤ Smax
3.
Sy ≤ Smax
ja
nein
nein
ja
ja
ja
Inkrementierung von HA 2 Esc ( esc )
um
(S x DIV esc ) − 1
(S
y
DIV esc ) − 1
max ((S x DIV esc ) + (S y DIV esc )) − 1
Als letzter Wert bzw. letzte Werte werden die Reste von S x / y gezählt:
Sx ≤ Smax
Sy ≤ Smax
ja
nein
ja
nein
ja
ja
Inkrementierung um 1 von
HA 2 Esc ( S x MOD esc )
HA2 Esc ( Sy MOD esc )
HA 2 Esc ( Sx MOD esc )
HA2 Esc ( Sy MOD esc )
4.5. Spezialfälle: Frequenzlinien mit kleinen Beträgen
Im Normalfall werden Musik- und Sprachdateien mit steigender Frequenz sinkende
Frequenzamplituden aufweisen. Aus diesem Grund wird der Bereich mit Beträgen ≤ 1 zu
den small_values und die daran anschließenden Nullwerte zu den zero_values
zusammengefaßt. Der Bereich von der untersten Frequenzlinie 0 bis zu den small_values
heißt big_values.
Es wird davon ausgegangen, daß die Gesamtzahl der Frequenzlinien eine durch 4 teilbare Zahl ist, z.B. 256 oder 512. Die big_values werden auf eine gerade Anzahl aufgerundet, da sie paarweise codiert werden, die small_values werden auf eine durch 4
teilbare Zahl aufgerundet, sie werden ja als Quadrupel codiert. Die restlichen zero_values
besitzen dann eine gerade Anzahl.
Die small_values-Quadrupel werden nach dem gleichen Verfahren und den gleichen
Tabellen, wie in Layer III verwendet, codiert. Es stehen zwei Tabellen zur Auswahl;
dabei wird diejenige ausgewählt, bei der weniger Bits benötigt werden.
Die zero_values werden implizit durch die Übertragung der Obergrenze für die
small_values übertragen.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
26
4.6. Berechnung der benötigten Bits
Zur Berechnung der Datenrate wird die Gesamtzahl bit_sum der Bits pro Block aus
den quantisierten Spektralwerten q( k ) folgendermaßen ermittelt:
bit _ sum = static _ bits + big _ values _ bits1/2 + small _ values _ bits
(22)
Es ist dabei:
static _ bits = prescaler _ bits + ld(M ) − 1 + ld(M ) − 2
(23)
prescaler_bits dienen zur Übertragung des Skalierungsfaktors, der hier, sehr vorsichtig, als 16 Bit Zahl angenommen wird. Es werden sicher
weniger Bits benötigt.
ld(M ) − 1, ld(M ) − 2
übertragen die zwei Angaben, wo die Grenzen für big_values und
small_values liegen. Die Zahlen sind durch 2 bzw. 4 teilbar, wobei
berücksichtigt werden muß, daß die Zählung bei 0 beginnt.
Die Zahl der big_values_bits wird je nachdem, ob ein- oder zweidimensionale SHCT
verwendet werden, nach den Beziehungen (24) oder (25) berechnet:
big _ values
∑ huff
big _ values _ bits1 =
1
(q(k ))
k =0
(24)
huff1 ( q( k )) berechnet die Zahl der Bits pro big_value nach dem Algorithmus auf
Seite 23, wobei nur die Längeninformation ausgewertet wird.
( big _ values +1) / 2
big _ values _ bits 2 =
∑ huff
2
(q(2k − 2), q(2k − 1))
k =1
(25)
huff 2 (q(2k − 2), q(2k − 1))
berechnet die Zahl der Bits pro Paar von big_values nach dem Algorithmus auf Seite 24, wobei ebenfalls nur die Längeninformation ausgewertet wird.
Zur Berechnung der Bits für die small_values wird das Verfahren aus Layer III mit den
beiden Tabellen aus Abschnitt "8.3.2. Übernommene Tabellen", S. 117 angewandt:
small _ values _ bits = min(small _ values _ bits0,small _ values _ bits1)
(26)
small _ values
small _ values _ bits0 =
∑ TabelleA(q(4k ) + 2 ⋅ q(4k + 1) + 4 ⋅ q(4k + 2) + 8 ⋅ q(4k + 3)
k = big _ values +1
(27)
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
27
small _ values
small _ values _ bits1 =
∑ TabelleB(q(4k ) + 2 ⋅ q(4k + 1) + 4 ⋅ q(4k + 2) + 8 ⋅ q(4k + 3)
k = big _ values +1
(28)
Zusätzlich wird ein Bit zur Tabellenauswahl übertragen, falls
small _ values <> big _ values gilt.
4.7. Simulation eines Bitreservoirs
Bits, die in einem Block nicht verwendet werden können, weil für die gewählte
Quantisierung weniger Bits benötigt werden, werden im bit_bucket gesammelt, wobei die
Obergrenze durch bit_bucket_size gesetzt ist. Die durch die Datenrate bit_limit gegebene
Obergrenze für die Bits pro Block wird vor jedem Block um den aktuellen Inhalt von
bit_bucket erhöht, bit_bucket wird danach wieder auf 0 gesetzt.
Den größten Vorteil bietet das Bitreservoir erst, wenn die Codierqualität nicht immer
blockweise maximiert wird, wie im vorliegenden Programm 'pacse', sondern wenn die
sparsam codierbaren Stellen auch entsprechend sparsam codiert werden, und damit mehr
Bits für das Reservoir frei werden.
4.8. Erfassung der Meßergebnisse
Die Encoderqualität wurde mit zwei Verfahren beurteilt, entsprechend den beiden Kriterien des Near Lossless Coding. Zum einen wurde der absolute Fehler, hier bezeichnet
als "Genauigkeit" der Datenreproduktion nach Codierung und Decodierung, gemessen,
zum anderen die Qualität unter Berücksichtigung der Gehöreigenschaften. Sie wird als
Noise Masking Ratio erfaßt, also als Abstand des Störsignals von jenem Pegel, ab dem
die Störungen hörbar wären.
4.8.1.
Genauigkeit der Codierung in Bit
Die Genauigkeit der Codierung mit anschließender Decodierung wird folgendermaßen
bestimmt: Die Abtastwerte des Eingangs- und des Ausgangssignals werden daraufhin
verglichen, in wie vielen Bit sie, ausgehend vom MSB übereinstimmen.
(
G = W − int ld ATWorig − ATWcod + 0,5
)
(29)
Ein- und Ausgangsdaten liegen als 32 Bit Worte vor, der hauptsächlich interessierende
Bereich besitzt als Wortbreite W nur 16 Bit, da die Testsignale als 16 Bit CD-Daten vorliegen. Die Testsignale belegen dabei die MSBs.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
28
Wird, wie im Programm 'snddiff' (s. S. 50), der Vergleich nach obiger Formel über
alle Abtastwerte durchgeführt und das Minimum unveränderter Bits ermittelt, dann erhält
man eine worst-case-Abschätzung, wie genau die Daten reproduziert werden können.
Allerdings kann ein einziger Abtastwert mit einem schlechten Ergebnis die geforderte
Aussage, n Bit seien unverändert, so stark beeinflussen, daß das Resultat schlechter
wirkt, als es für den betrachteten Anwendungsfall ist.
Der Vergleich der Abtastwerte läßt sich jedoch schnell durchführen, und seine Aussagekraft kann durch eine Prozentangabe, wieviele Abtastwerte in den oberen 16 bzw. in
allen 32 Bit unverändert blieben, wesentlich erhöht werden.
Im Simulationsprogramm 'pacse' (s. S. 48) wird blockweise die Häufigkeitsstatistik
der Abweichungen mit 16 − G Bit Ungenauigkeit berechnet, und ermöglicht so eine genauere Analyse der Encodergenauigkeit als Funktion der Zeit.
4.8.2.
Noise Masking Ratio
Zur Messung der Noise to Masking Ratio wurde das Programm NMR in der Version
für 32 Bit Integer Eingangsdaten verwendet [9].
Das Programm vergleicht die Leistungsspektren der Originaldaten mit den codierten
Daten, faßt sie in Frequenzgruppen zusammen und wertet die Gruppen mittels der
Maskierungsfunktion aus. Es schätzt zusätzlich die Auswirkung der Grundhörschwelle
mit einem Pegel von 0,5 LSB (bei 16 Bit Daten) ab. Beide Beurteilungen sind so gewählt,
daß sie Störungen empfindlicher registrieren, als das menschliche Gehör, so daß die
Abschätzung der Hörbarkeit von Störungen auf der sicheren Seite liegt.
Als Ausgabe des Programms wurde die blockweise Angabe der Noise to Masking
Ratio gewählt, die angibt, um wieviel dB die Störungen unter der Hörbarkeitsschwelle
liegen.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
29
5. Messungen der Datenrate für Near Lossless Coding
(NLC)
Die erreichbare Datenrate für NLC muß aus mehreren Messungen abgeschätzt werden,
da sie noch nicht direkt gemessen, sondern nur durch Simulation eines Encoders
gewonnen werden kann. Dementsprechend ist damit zu rechnen, daß bei der Realisierung
des Encoders bessere Ergebnisse durch weitere Optimierungen zu erzielen sind.
Die Untersuchungen wurden an einer repräsentativen Auswahl von Tonbeispielen
durchgeführt, und zwar vier der vierzehn ISO-Tonbeispiele, mit denen auch die verwendeten Tabellen zur Codierung gewonnen wurden (s. S. 121). Eine Untersuchung aller
Stücke war aus Zeitgründen nicht möglich, sie hätte aber voraussichtlich auch keinen
wesentlichen Informationsgewinn mehr gebracht. Es sind im einzelnen folgenden Stücke:
ISO 3
Eine einzelne Viola. Dieses Stück eignet sich wegen des Vorherrschens
des Grundtones und der relativ geringen Obertöne gut zur Codierung mit
einem Transformationscoder.
ISO 5
Glockenspiel, leise ausgesteuert. Probleme in der Codierung können bei
den harten Anschlägen durch Vorechos und beim Ausklingen durch die
Quantisierung der Frequenzwerte entstehen.
ISO 6
Frauenstimme, lesend. Sie ist durch Explosiv- und Zischlaute potentiell
problematisch für den verwendeten Encoder. Im übrigen wurde dieses
Stück mit aufgenommen, weil zur Beurteilung der Encodergüte ein
Sprachsignal unverzichtbar ist.
ISO 16 Suzanne Vega, eine weibliche Singstimme a cappella, elektronisch
nachbearbeitet. Dieses Signal ist besonders problematisch, da der
Frequenzumfang der leicht heiser klingenden Stimme von wenigen
Kilohertz bis an die Bandgrenze von ungefähr 20 kHz geht. Durch die
a-cappella-Darbietung können Fehler sehr gut vom Ohr wahrgenommen
werden, da sie nicht durch Instrumente verdeckt werden.
Zum einen wird ermittelt, wie genau die Signale rekonstruiert werden, also in wie
vielen Bits die einzelnen Abtastwerte übereinstimmen, wenn mit festen Bitraten codiert
wird. Aus den Messungen werden hier diejenigen Resultate vorgestellt, bei denen das
erste Kriterium der Near Lossless Codierung, das Erreichen von 15 Bit Übereinstimmung, eingehalten wurde.
Zum anderen werden die codierten Stücke mit dem Programm NMR darauf untersucht, ob die Fehler, die beim Codieren auftreten, mindestens 20 dB unter der Maskierungsschwelle liegen, und somit das zweite Kriterium des NLC erfüllen.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
30
5.1. Eindimensionale Huffman Code Tabellen
Als erstes wurde die Codierqualität mit Codegenerierung durch eindimensionale Huffman Code Tabellen untersucht. Die Teststücke wurden zuerst mit 3 Bit/Abtastwert
(ATW) untersucht, um einen Eindruck zu bekommen, wie gut es sich jeweils für eine
Codierung der hier behandelten Art eignet. Bei 3 Bit/ATW sind in allen Stücken sicher
Fehler zu beobachten, so daß man aus dem Ausmaß der Fehler einen Anhaltspunkt für
die Codierbarkeit gewinnen kann. Die Messungen wurden bei einer Tabellengröße von
64 Codes im eindimensionalen Fall, und 64*64 = 4096 Codes im zweidimensionalen Fall
durchgeführt.
Eine zweite Bitrate wurde so gewählt, daß gerade das Kriterium der 15 Bit
Genauigkeit eingehalten wird. Die Messungen wurden in Schritten ganzzahliger Bitraten
durchgeführt. In Abb. 8 sind die zwei Kurven für das Stück iso3 dargestellt. Sie geben an,
wie viele Bits im schlechtesten Fall jeweils über einen Block von 512 Werten
unverändert bleiben.
16
14
Genauigkeit (Bit)
12
10
8
6
4
2
0
0
100
200
300
400
500
600
700
Block
Abb. 8: Genauigkeit in Bit für iso3
Die untere, durchgezogene Kurve für die Bitrate 3 Bit/ATW zeigt, daß das Signal bei
dieser Datenrate im Extremfall nur 11 gültige Bits aufweist. Ganz am Anfang wird die
Stille erwartungsgemäß mit 16 Bit Genauigkeit codiert. Die Einbrüche entsprechen den
lauteren Stellen, an denen die Viola jeweils die nächste Note beginnt, um dann leiser zu
werden. Mit der Lautstärke steigt auch die Zahl der benötigten Bits, sie sinkt danach auch
wieder mit der Lautstärke ab. Dieser Effekt tritt hier allerdings nur schwach auf, er ist jedoch wahrnehmbar.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
31
Die obere Linie (gestrichelt) berührt zunehmend ab Block 350 die 15 Bit Grenze, also
bei den lauteren Stellen. Bei der gewählten Abtastrate von 6 Bit/ATW ist also schon die
erste, strenge Forderung nach NLC erfüllt. Bei 5 Bit/ATW war das jedoch noch nicht der
Fall.
Vergleicht man dieses Ergebnis mit der Messung der NMR in Abb. 9, dann ergibt
sich, daß auch schon 4-5 Bit/ATW die Forderung nach NLC erfüllen. Meist reichen
4 Bit/ATW, stellenweise sind 5 Bit/ATW nötig (nach dem NMR - Kriterium). Es kann
also davon ausgegangen werden, daß eine Bitrate von 4-5 Bit/ATW in diesem Stück für
NLC ausreichend ist. In diesem Fall eines tonalen Signals liegen die durch beide
Kriterien ermittelten Bitraten nicht sehr auseinander, wobei das Kriterium der bitgenauen
Reproduktion das stärker einschränkende Kriterium darstellt.
-15
-20
Noise to Mask Ratio (dB)
-25
-30
-35
-40
-45
iso3 5 Bit/ATW
iso3 4 Bit/ATW
-50
0
100
200
300
400
500
600
700
Block
Abb. 9: NMR für iso3
Weniger leicht zu codieren ist das Stück iso5, das Glockenspiel, welches durch seine
geringe Aussteuerung und seine Anschläge geringfügig höhere Bitraten erfordert. In
Abb. 10 sieht man wieder an der unteren 3 Bit/ATW Linie, welche Stellen dem Encoder
Probleme bereiten: in der Nähe der Blöcke 50 und 330 sind Einbrüche, die durch das
Anschlagen des Glockenspiels hervorgerufen wurden. Aber auch hier wird das Signal mit
6 Bit/ATW mit 15 Bit oder meist sogar 16 Bit Genauigkeit codiert.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
32
16
14
Genauigkeit (Bit)
12
10
8
6
4
2
0
0
100
200
300
400
500
600
700
Block
Abb. 10: Genauigkeit für das Glockenspiel, iso5
-15
-20
Noise to Mask Ratio (dB)
-25
-30
-35
-40
-45
iso5 6 Bit/ATW
iso5 5 Bit/ATW
-50
0
100
200
300
400
500
600
700
Block
Abb. 11: NMR für das Glockenspiel, iso5
Die NMR Kurven für 4 und 5 Bit/ATW in Abb. 11 zeigen, daß, bis auf den ersten Anschlag, deutlich weniger als 5 Bit/ATW ausreichen würden. Der erhöhte kurzfristige
Bitbedarf bei den Anschlägen ließe sich z.B. durch Einführung eines Bitreservoirs
decken, ohne die Bitrate insgesamt anheben zu müssen. Auch hier scheint ein Near
Lossless Coding mit 4-5 Bit/ATW möglich zu sein.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
33
Ein weniger homogenes Signal liegt mit der Sprache in dem Stück iso6 vor (Abb. 12):
16
14
Genauigkeit (Bit)
12
10
8
6
4
2
0
0
100
200
300
400
500
600
700
Block
Abb. 12: Genauigkeit in Bits für eine weibliche Sprechstimme
-15
-20
Noise to Mask Ratio (dB)
-25
-30
-35
-40
-45
iso6 5 Bit/ATW
iso6 4 Bit/ATW
-50
0
100
200
300
400
500
600
700
Block
Abb. 13: NMR der Sprechstimme bei verringerter Bitrate
Will man mit konstanter Bitrate 15 Bit Genauigkeit erreichen, dann sind 7 Bit/ATW
nötig, während andere Stellen, z.B. die Sprechpausen bei Block 220-250, auch mit 3
Bit/ATW codiert werden können.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
34
Wie das NMR Diagramm Abb. 13 zeigt, kann die Sprache mit 4 Bit/ATW und
weniger codiert werden. Die mittlere Datenrate könnte sicher viel niedriger sein, wenn
kurzfristige Spitzen der Entropie durch eine intelligente Bitreservoirverwaltung abgefangen werden könnten.
Das vierte Tonbeispiel stellt die höchsten Anforderungen an den Encoder, betrachtet
man die Abbildung 14. In der hier vorliegenden Form des Encoders werden 10 Bit/ATW
für NLC benötigt, betrachtet man nur die Rekonstruktionstreue der Binärwerte. Auch bei
13 Bit/ATW (nicht abgebildet) werden nur 14 Bit Genauigkeit erreicht.
16
14
12
Genauigkeit (Bit)
10
8
6
4
2
0
0
100
200
300
400
500
600
700
Block
Abb. 14: Singstimme (Suzanne Vega) erst mit 10 Bit/ATW mit NLC
Dieses Verhalten des Encoders liegt in dem teilweise extrem breitbandigen Signal.
Der hohe Rauschanteil der heiseren Stimme, der vielleicht studiotechnisch noch verstärkt
wurde, und die Zischlaute nähern sich stark dem für einen Transformationscoder ungünstigsten Fall eines weißen Rauschens. Bei Rauschsignalen kann die Entropiecodierung
die Datenrate nicht verringern, sie wird sie wahrscheinlich sogar noch erhöhen. Dies kann
damit erklärt werden, daß die Codierung für den häufigen Fall relativ weniger starker
Spektrallinien der dominierenden Töne zusammen mit den kleinen Linien des Hintergrundes optimiert wurde. Die (längeren) Codes zur Übertragung größerer Werte sind in
ihrer Anzahl normalerweise gering, die der kleinen, sofern sie eine bestimmte Größe
nicht überschreiten,21 sind sehr kurz. Liegen jedoch viele mittelgroße Werte vor, dann
werden diese unter Umständen mit mehr Bits codiert, als sie uncodiert hatten. Man nennt
den Fall eines solchen flachen Spektrums atonal bzw. rauschähnlich.
21
Genauer: Werte, die ausschließlich mit der unteren der beiden Stacked Huffman Code Tabellen codiert werden können.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
35
Ein atonales Signal muß aufgrund der Psychoakustik weit weniger genau übertragen
werden, als ein tonales Signal, ohne daß der Höreindruck beeinträchtigt würde. Deshalb
resultieren bei Anwendung des 2. Kriteriums der Near Lossless Quality deutlich
geringere Bitraten, wie in Abb. 15 zu sehen ist.
-15
-20
Noise to Mask Ratio (dB)
-25
-30
-35
-40
-45
iso16 5 Bit/ATW
iso16 4 Bit/ATW
-50
0
100
200
300
400
500
600
700
Block
Abb. 15: NMR Werte für deutlich niedrigere Bitraten
Bei 5 Bit/ATW liegt der schlechteste Wert der NMR bei -25 dB (ca. Block 60), alle
anderen Werte sind weitaus besser. Eine Bitrate von ebenfalls 4,5-5 Bit/ATW müßte also
NLC ermöglichen.
5.2. Zweidimensionale Huffman Code Tabellen
Die gleichen Messungen, wie für die eindimensionalen Huffman Code Tabellen, wurden auch für zweidimensionale Tabellen durchgeführt. Als erstes Beispiel sei wieder das
iso3 - Stück angeführt, mit den gleichen Bitraten, wie sie in den Abbildungen 8 und 9
verwendet worden waren. Wie man in den Abbildungen 16 und 17 deutlich erkennen
kann, muß die Bitrate um 1 Bit/ATW höher gewählt werden. Die NMR ist ebenfalls
schlechter geworden, um ca. 3-5 dB gegenüber der eindimensionalen Codierung.
Zweidimensionale Tabellen wurden eingeführt, weil sie bessere Ergebnisse liefern
sollten. Das scheint nicht der Fall zu sein. Eine Erklärung dafür könnte sein, daß die
Codelängen der zweidimensionalen Tabellen wegen der größeren Anzahl der
Tabelleneinträge länger sein müssen, als bei den eindimensionalen Tabellen - der Gewinn
durch eine bessere Anpassung an die Wahrscheinlichkeitsverteilung kann dieses Manko
nicht wieder wettmachen.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
36
16
14
Genauigkeit (Bit)
12
10
8
6
4
2
0
0
100
200
300
400
500
600
700
Block
Abb. 16: iso3 mit zweidimensionalen Tabellen codiert
-15
-20
Noise to Mask Ratio (dB)
-25
-30
-35
-40
-45
iso3 5 Bit/ATW
iso3 4 Bit/ATW
-50
0
100
200
300
400
500
600
700
Block
Abb. 17: NMR mit zweidimensionaler Tabelle
Bei dem zweiten Stück, dem Glockenspiel, sieht das Ergebnis für die
zweidimensionalen Tabellen nicht ganz so schlecht aus. Zwar ist die Genauigkeit bei den
Anschlägen deutlich um 2 Bit/ATW schlechter, als bei der eindimensionalen Tabelle,
auch wenn man wie in Abb. 18 die Bitrate um eines auf 7 Bit/ATW erhöht. Erst bei
8 Bit/ATW wird das erste Kriterium des NLC erfüllt. Ähnlich sieht das Bild für die
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
37
Beurteilung des Gehöreindruckes aus; er ist bei höherer Datenrate als bei der
eindimensionalen Codierung in den Anschlägen schlechter, wie die NMR-Messungen in
Abb. 19 zeigt. Die NMR an den übrigen Stellen liegt jedoch besser als die, die bei der
eindimensionalen Tabelle gemessen wurde. Hier ist die 4 Bit/ATW Linie aus Abb. 19 mit
der 5 Bit/ATW Linie aus Abb. 11 vergleichbar.
16
14
Genauigkeit (Bit)
12
10
8
6
4
2
0
0
100
200
300
400
500
600
700
Block
Abb. 18: Genauigkeit bei erhöhter Bitrate
-15
-20
Noise to Mask Ratio (dB)
-25
-30
-35
-40
-45
iso5 7 Bit/ATW
iso5 4 Bit/ATW
-50
0
100
200
300
400
500
600
700
Block
Abb. 19: NMR mit schlechterem Anschlagsverhalten
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
38
Für die restlichen untersuchten Stücke ergeben sich ganz ähnliche Ergebnisse, so daß
auf eine genaue Vorstellung verzichtet werden soll. Zweidimensionale Tabellen in der
hier verwendeten Form scheinen keinen Gewinn, sondern vielmehr einen Verlust an
Codierqualität bei gleicher Datenrate wie bei der Verwendung eindimensionaler Tabellen
zu ergeben.
Die Abhängigkeit der Codelänge von der Zahl der Tabellenelemente legt nahe, statt
der bisher verwendeten 64 ⋅ 64 Elemente großen Tabellen z.B. 128 ⋅ 128 als Dimension zu
verwenden, auch wenn dies aus Gründen des Speicherplatzbedarfs nicht empfehlenswert
erscheint.
Wie sich gezeigt hat, ändert sich dadurch jedoch nicht das Verhältnis der Ergebnisse
der beiden Tabellenarten zueinander. Die eindimensionalen Tabellen sind also auf jeden
Fall effektiver, mit der Einschränkung, daß kurze Verschlechterungen der NMR Werte
besser durch die zweidimensionalen Tabellen vermieden werden, wie hier nicht
aufgeführte Untersuchungen an iso5 und iso16 ergaben.
5.3. Einfluß der Tabellengröße
Ein weiterer wichtiger Parameter des Encoders ist die Größe der verwendeten
Tabellen. Sie müssen so klein sein, daß sie leicht in den Speicheradressraum von
Signalprozessoren passen, andererseits darf die Länge der Huffman Codes nicht durch
Wahl einer zu kleinen Tabelle leiden. Je kleiner die Tabelle gewählt wird, desto öfter
müssen Escape-Sequenzen verwendet werden. Ein einzelnes Escape-Zeichen ist dabei
mindestens 1 Bit groß, so daß für die Tabelle zu große Spektralwerte schnell auch eine
große Huffman Code Länge erreichen können.
5.3.1.
Rekonstruktionsgenauigkeit pro Abtastwert
Als mögliche Tabellengrößen wurden n=32, 64 und 128 Worte pro Eingangsvektor
untersucht, woraus für die Stacked Huffman Code Tables ein Speicherbedarf von
2 ⋅ n ⋅ ( SpCode )
2
(n + n) ⋅ ( Sp Code )
für eindimensionale Tabellen und
für zweidimensionale Tabellen benötigt werden.
SpCode = Codelängenspeicherplatz + Codespeicherplatz für einen Code
Die Tabellengrößen wurden beispielhaft am Stück iso16 bei einer Bitrate von
6 Bit/ATW untersucht. In Abb. 20 werden die Tabellengrößen 32 und 64 Worte verglichen, in Abb. 21 dann die Größen 64 und 128. Aufgetragen sind die Zahl der korrekten
Abtastwerte pro Block von 512 Abtastwerten über die Blöcke. Korrekt bedeutet hier wieder, daß mindestens die höchstwertigen 15 Bit übereinstimmen.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
39
550
> 14 Bits ok 512-er Block
500
450
400
350
300
250
0
100
200
300
400
500
600
700
Block
Abb. 20 Eindimensionale SHCT, Zahl der korrekten Abtastwerte/Block bei
32 und 64 Worten
Die gestrichelte Linie der Tabelle mit 64 Worten liegt durchweg über derjenigen der
Tabelle mit 32 Worten, liefert also bei sonst gleichen Bedingungen die besseren Ergebnisse. Der Unterschied zwischen beiden Tabellengrößen ist signifikant.
540
> 14 Bits ok 512-er Block
520
500
480
460
440
iso16 6 Bit, 128 T
420
400
0
100
200
300
400
500
600
700
Block
Abb. 21: Eindimensionale SHCT, Zahl der korrekten Abtastwerte/Block bei
64 und 128 Worten
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
40
In Abb. 21 fallen beide Kurven fast vollständig zusammen, die 128-er Tabelle bringt
demnach gegenüber der 64-er Tabelle keine nennenswerten Vorteile. Im Falle der zweidimensionalen SHCT liegen die Verhältnisse ähnlich, wie die Abbildung 22 zeigt, nur daß
die Ergebnisse insgesamt auf einem schlechteren Niveau liegen:
500
> 14 Bits ok 512-er Block
400
300
200
100
0
0
100
200
300
400
500
600
700
Block
Abb. 22: Zweidimensionale SHCT, Korrekte ATW/Block bei 32, 64 Worten
Überall sind die Werte für die 64-er Tabelle besser, als für die 32-er, wenn man von
kleinen Abweichungen von dieser Regel, z.B. bei Block 120, absieht.
5.3.2.
Summarische Betrachtung der Rekonstruktionsgenauigkeit
Um Abschätzen zu können, wie klein Tabellen gewählt werden können, ohne die
Effizienz des Coders zu stark zu beeinflussen, wurden die vier Teststücke mit verschiedenen Tabellengrößen codiert, und die Zahl der in 16 Bit gültigen Abtastwerte als
Funktion der Tabellengröße aufgetragen. Diese Untersuchungen wurden für Bitraten von
4 und 7 Bit/ATW im eindimensionalen Fall, und für 4 und 6 Bit/ATW im
zweidimensionalen Fall durchgeführt.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
41
100
% 16 Bits ok in Datei
90
80
70
60
50
40
0
50
100
150
200
1D - Tabellengroesse, 4 Bit
250
300
Abb. 23: Einfluß der Tabellengröße bei 4 Bit/ATW
Man erkennt in Abb. 23, daß für eine relativ niedrige Bitrate bei Tabellengrößen von
28 und 32 Codes die Zahl der bitgetreu reproduzierten Abtastwerte deutlich unter dem
Optimum liegt, daß jedoch 64 Bit/ATW schon einen guten Kompromiß darstellen. Für
sehr große Tabellen mit den Größen 128 und 256 Codes sind die Verbesserungen nicht
mehr entscheidend. Interessant ist das unterschiedliche Verhalten je nach Musikstück.
Das Glockenspiel mit seinen wenigen Spektrallinien, läßt sich erwartungsgemäß schon
mit kleinen Tabellen gut codieren. Die weibliche Sprechstimme, iso5, läßt sich mit
größeren Tabellen relativ gesehen schlechter codieren, als z.B. die Viola, iso3.
Anscheinend müssen bei iso5 wesentlich mehr mittelgroße Werte, also Werte mit
weniger Escape-Codes, codiert werden, als bei iso3. Das typische Problemstück für
Transformationscoder, Suzanne Vega, iso16, wird mit ansteigender Tabellengröße
langsam ansteigend besser codiert, vergleichbar mit der Sprechstimme.
Die gleiche Tendenz ergibt sich bei einer relativ hohen Bitrate von 7 Bit/ATW in
Abb. 24: Der Qualitätssprung, der in der Grafik so groß erscheint, liegt allerdings in der
Skalierung begründet, zeigt jedoch, daß Tabellen von einer Größe von 128 Codes dem
Optimum, das bei unendlich großer Tabelle erreicht wäre, recht nahe kommen.
Der nicht monotone Anfang der Kurven könnte auf einen Fehler während der
Messungen hindeuten, diese Frage wurde nicht mehr geklärt, da diese Tabellengrößen
nicht interessant erscheinen.
Tabellengrößen von 64-128 Codes scheinen schon gute Ergebnisse zu liefern,
256 Codes bringen kaum noch eine Verbesserung im Falle der eindimensionalen
Tabellen.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
42
100
99
% 16 Bits ok in Datei
98
97
96
95
94
93
92
91
0
50
100
150
200
1D - Tabellengroesse, 7 Bit
250
300
Abb. 24: Einfluß der Tabellengröße bei 7 Bit/ATW
100
90
% 16 Bits ok in Datei
80
70
60
50
iso16
iso6
iso5
iso3
40
30
20
0
20
40
60
80
2D - Tabellengroesse, 4 Bit
100
120
140
Abb. 25: Einfluß der Tabellengröße bei 4 Bit/ATW und 2-D Tabellen
Für zweidimensionale Tabellen als Tabellengrößen die Werte 16, 20, 24, 28, 32, 64
und 128 Codes pro Vektor, also 256 bis 4096 Codes insgesamt, ist die Abhängigkeit von
der Tabellengröße nicht so stark, wie im eindimensionalen Fall.
Gut zu erkennen ist, daß schon eine Tabellengröße von 32·32 Codes dem Optimum
nahe ist, und daß das schlecht zu codierende Stück von Suzanne Vega, iso16, bei kleinen
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
43
Tabellengrößen wieder besser codiert wird, während gut zu codierende Stücke, wie das
Glockenspiel, iso5, mit steigender Tabellengröße monoton zunehmend besser codiert
wird.
100
95
90
% 16 Bits ok in Datei
85
80
75
70
65
60
55
50
45
0
20
40
60
80
2D - Tabellengroesse, 6 Bit
100
120
140
Abb. 26: Einfluß der Tabellengröße bei 6 Bit/ATW und 2-D Tabellen
In der Grafik für 6 Bit/ATW wird bestätigt, daß eine Tabellengröße von 32·32 Codes
dem Optimum anscheinend nahe kommt. Auffällig ist die Trennung in der Qualität der
Codierung der Stücke iso16 einerseits und der Stücke iso3, iso5, iso6 andererseits. Die
verwendeten 6 Bit/ATW sind für die letzteren drei Stücke ausreichend, für iso16 sind
offensichtlich mehr Bit nötig.
5.4. Bitreservoir
Für Transformationscoder sind atonale Stellen, wie sie als Zischlaute in der Sprache,
oder z.B. als Anblasgeräusche in der Musik auftreten, wegen der Notwendigkeit einer
wesentlich höheren Bitrate problematisch zu codieren. Für die häufig vorkommenden
kurzzeitigen atonalen Stellen kann das Codierergebnis verbessert werden, indem nicht
benötigte Bits bei der Codierung eines Blockes an die nachfolgenden Blöcke
weitergegeben werden. Als maximaler Wert solchermaßen angesparter Bits wird bei den
durchgeführten Messungen ein Wert von 2000 Bit verwendet. Ein größerer Wert
erzwingt eine größere Signalverzögerung durch Puffer im Decoder, ein kleinerer Wert
verschlechtert jedoch die Codierung von Stellen, die kurzfristig mehr Bits benötigen. Zur
Feststellung optimaler Puffergrößen müßten aktiv Bits an den Stellen gespart werden, die
mit wenig Bits sehr gut zu codieren sind, damit sie zur Verfügung stehen, wenn schwer
zu codierende Stellen diese Reserve benötigen.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
44
In dem verwendeten Programm werden nur durch Abrundungen anfallende Bits gesammelt, was zur Folge hat, daß der Einfluß des Bitreservoirs kaum meßbar ist. In der
nachfolgenden Grafik wurde es trotzdem versucht:
-15
-20
NMR in dB
-25
-30
-35
iso16 Bitres. 30000
-40
0
100
200
300
400
500
600
700
Block
Abb. 27: Codierung mit und ohne Bitreservoir
Das Stück iso16 wurde mit 6 Bit/ATW und einer Tabellengröße von 64·64 Codes
einmal mit 30000 Bit Bitreservoir und einmal ohne codiert. Man erkennt eine leicht
bessere NMR in der Codierung mit Bitpuffer (durchgezogene Linie), die bei kurzen
Spitzen (Stellen höherer Entropie) deutlich besser codiert werden, als in der Codierung
ohne Bitreservoir, z.B. bei Block 350 um 5 dB. Eine vergleichende Messung mit einem
Reservoir von 2000 Bit ergab gleiche Resultate.
Der Effekt ist, wie bereits erläutert, in der vorliegenden Implementation des
Bitreservoirs jedoch insgesamt eher gering, da das Bitreservoir in gut zu codierenden
Signalabschnitten nicht gefüllt wird.
5.5. Vergleich mit verlustfreier Kompression
Um die Effizienz des Verfahrens vergleichen zu können, wurden die verwendeten
Stücke mit verschiedenen, verlustfrei komprimierenden Verfahren komprimiert. Die Verfahren sind allerdings nicht für Musikdaten optimiert:
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
45
Messungen über alle vorliegenden ISO-Stücke22
zoo
V2.1, 7/91
lha
V1.0, '92
zip
V1.9, 8/92
27,8 %
27,8%
23,3%
compress
V66.2, 5/90
14,1%
Die Daten werden also maximal um 27,8% komprimiert, wobei die
Komprimieralgorithmen z.T. sehr lange Rechenzeiten und Datenblockgrößen benötigen.
Beides verhindert einen Einsatz in der Audiosignalverarbeitung. Die resultierende
Datenrate wäre im Mittel mit 11,6 Bit/ATW immer noch hoch, sie entspricht 0,5 MBit/s
bei einer Abtastrate von 44,1 kHz.
5.6. Bewertung der einzelnen Maßnahmen
Aus den Messungen lassen sich aus der aktuellen Implementierung des Codierverfahrens folgende Schlüsse ziehen:
22
•
Optimale Resultate lassen sich mit dem beschriebenen Verfahren
erreichen, wenn eindimensionale Tabellen der Größe 128 oder 256 Codes
gewählt werden.
•
Ein Bitreservoir verbessert die Ergebnisse bei kurzen Entropiespitzen. Das
Reservoir ist mit 2000 Bit bereits genauso gut, wie eines von 30000 Bit,
was auf eine schlechte Nutzung durch das Programm schließen läßt.
•
An der Implementierung der Codierung mittels zweidimensionaler
Tabellen muß noch gearbeitet werden, da die gewonnenen Ergebnisse den
bisherigen Erfahrungen mit ähnlichen Codierverfahren widersprechen.
•
Die Datenraten liegen für alle vier betrachteten Stücke im Bereich von 4,55 Bit/ATW für Near Lossless Coding. Dies gilt, obwohl die Stücke sich in
ihren Anforderungen an den Coder stark unterscheiden.
•
Gegenüber den verlustlosen Kompressionsverfahren läßt sich schon in der
vorliegenden Simulation eine Datenreduktion um den Faktor 2,3-2,5
erreichen, oder von 3,2-3,6 gegenüber der Ausgangsdatenrate.
S. Anhang "8.5. Liste der verwendeten Musikstücke"
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
46
6. Anwendung der Programme
Alle Programme werden über die Kommandozeile angesprochen, und erhalten ihre
Parameter nur von dort, damit der Einsatz in Shellscripts (Stapelverarbeitung) problemlos
möglich ist, also nicht interaktiv. Die hier aufgeführten Eigenschaften sind mit der
Bibliothek 'cliutil' realisiert und gelten deshalb für alle Programme.
Die Argumente bestehen aus Dateinamen und Optionen, diese wiederum können je ein
Argument nach sich haben. Eine Option wird daran erkannt, daß sie mit einem '-' und
einem unmittelbar folgenden Optionsbuchstaben beginnt. Zwischen dem Optionsbuchstaben und dem Argument können Leerzeichen stehen. Optionen können nicht
zusammengefaßt werden. Die Reihenfolge ist beliebig, solange nicht ein Dateiname mit
dem Argument einer Option verwechselt werden kann, d.h. Optionen, die ein Argument
haben können, müssen von einer weiteren Option gefolgt werden, oder am Zeilenende
stehen.
Alle Programme geben mit der Option '-h' und bei ungültigem Aufruf eine
Kurzbeschreibung ihrer Bedienung aus. Bei Aufruf ohne Parameter geben die Programme
'testsig' und 'convab' keine Hilfe aus, da sie dann eine Standardfunktion ohne Parameter
ausführen (Testsignalerzeugung bzw. als Filter arbeiten).
Programme, die NeXT-Sound-Dateien benutzen, suchen diese zuerst im aktuellen
Verzeichnis, dann in dem Verzeichnis, das durch die Umgebungsvariable SNDDIR angegeben ist. Für zu lesende Dateien wird '.snd' als Endung angenommen, falls die Datei
nicht mit dem angegebenen Namen gefunden werden kann.
Wird als Optionsargument ein Dateiname erwartet, dann kann er durch einen
unmittelbar folgenden Bindestrich ('-') ersetzt werden. In diesem Fall wird stdin bzw.
stdout als Datei benutzt.
Zusammengefaßt sieht eine Kommandozeile z.B. so aus:
program -a opta datei1 datei2 -v datei3 -n1000
opta und 1000 sind Optionsargumente, -v ist eine Option ohne Argument, die
Argumente datei1, ..., datei3 werden als Dateinamen interpretiert. Diese Dateien
werden als
datei1, datei1.snd, /user/x/snd/datei1, /user/x/snd/datei1.snd
gesucht, falls SNDDIR so gesetzt wurde:
SET SNDDIR=/user/x/snd
EXPORT SNDDIR
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
47
6.1. Simulation mit 'pacse'
'Pacse' ist das zentrale Programm in dieser Arbeit. Es codiert die Sound-Datei mit vorgegebener Bitrate und Huffman Code Tabellen und simuliert die Decodierung. Neben der
codierten / decodierten Ausgangsdatei können verschiedene Statistiken generiert werden.
Optionen
pacse infile [outfile] -c huffman [-option [optionvalue][...]]
-a
-b
-B
-c
n
n
Datei
-C
Datei
-d
1
2
4
8
16
32
64
128
256
512
-e
-f
n
Datei
-g
-n
-N
-l
23
Datei
Unterdrückt Standardmeldungen.
Setzt MDCT Zeitbereichs - Blockgröße auf n (Standard: 1024).
bit-reservoir set to n (default 2000)
Die Huffman Code Längen Tabelle wird aus Datei.hcl gelesen.
Bei Option '-S2', also 2-dim Tabellen, wird zusätzlich die EscapeTabelle aus Datei.hce gelesen.
Gibt blockweise Histogramme über die Größe der Abweichung zwischen den Ein- und Ausgabe-Sound-Dateien an.
Debugging-Optionen, werden als Summe der Debug-Flags angegeben, 1 gibt Initialisierungswerte der Variablen aus.
Es werden nur 3072 Abtastwerte bearbeitet.
Alle Arrays werden gelistet.
Kurze Angabe, welche Operation gerade durchgeführt wird
Belegte Array-Größen (ungefähr)
Debugging Informationen aus dem Encoder:
z.B. Quantisierungsteiler, Zahl der zero_values und small_values
Erweiterte Encoder Informationen: Maximale Frequenzwerte
dto. count_bit Statistiken
dto. count_big_bit Statistiken
Near Lossless Coding: Bitrate in Bit/Block und Informationen zur
Adaption der Bitrate
Arrays werden nur bis Position n gelistet (Option '-d4')
Quantisierte Frequenzwerte werden als long integers in Datei geschrieben (Zahl pro Block durch '-e n' begrenzbar)
Erster Lauf zur Generierung neuer Huffman Tabellen. Es werden dazu Huffman Tabellen mit Codelängen von 1 für alle Werte
angenommen. Weitere benötigte Optionen: '-c Datei', '-s Datei'
Keine statistischen Auswertungen, es wird auch kein Speicher dafür
alloziert. Nützlich für große Werte bei '-t n', wenn nur die AusgabeSound-Datei interessiert.
Near Lossloss Simulation: Bitrate für 15 Bit NLC wird blockweise
in die ASCII Datei Datei ausgegeben.23
Zeilennummern in ASCII Ausgabedateien
Noch nicht voll funktionsfähig. Programm wird bei manchen Teststücken instabil, und liefert kurze Spitzen mit der
maximalen Bitrate als Ergebnis.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
48
-p
-r
-s
Datei
n.n
Datei
-S
n
-t
n
-T
n
ASCII Ausgabe der blockweisen 'Pseudo Perceptual Entropy'
Maximal zulässige Bitrate wird auf n.n Bit/Abtastwert begrenzt
Generierung binärer Statistikdateien zur Berechnung von Huffman
Code Tabellen.
Die Endungen werden ergänzt: Datei.bg1 für die nicht
verschachtelte Tabellen; sonst Datei.bgl für die Low-Tabelle,
Datei.bge für die Escape-Tabelle
Stacked Huffman Codes:
1 (Standard): Eindimensionale SHC, verwendet *.hcl als SHCT
2 Zweidimensionale SHCT, verwendet *.hcl, *.hce als SHCT
0 Eindimensionale, nicht verschachtelte Huffman Codes,
verwendet
*.hcl als HCT
Wenn '-S0' oder '-S1':
Reservierung einer Tabelle der Größe 2n zur Erfassung der
Verteilung der Spektralwerte (eindimensional). Standard: 16. Gibt
zugleich an, wieviel Bit die Spektralwerte maximal haben dürfen.
Wenn '-S2':
Tabellengröße in Worten (für eine Variable gerechnet) = esc+1
Speicherallozierung läßt sich mit '-n' unterdrücken
ld(Tabelle für small_values), Standard: 4. Für andere Werte noch
nicht getestet.
Die mit '-C' erzeugte blockweise Statistik hat folgendes Aussehen:
0
512
512
512
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
16
16
16
16
Die ersten 17 Spalten geben an wie viele Abtastwerte des Blocks in n MSB vom
Original abweichen, 0 ≤ n ≤ 16 . Die letzte Spalte gibt an, wie viele Bits (MSBs) im
ganzen Block unverändert blieben. Die erste Zeile hat immer 0 Abtastwerte.
Anwendungsbeispiel
pacse -f frequenzen.bin -r6 -c vega8 -S2 vega32.snd -s
vegasum.bin
Codiert die Datei 'vega32.snd' mit den Huffman Tabellen 'vega8.hcl' und 'vega8.hce'
(2-Dim SHCT) bei einer Bitrate von 6 Bit/Abtastwert, mit Ausgabe der Frequenzwerte in
'frequenzen.bin' und der Daten für eine neue, besser optimierte Huffman Tabelle in
'vegasum.bin'.
6.2. Tabellengenerierung mit 'huffgen'
'Huffgen' erzeugt aus Häufigkeitstabellen Huffman Codes. Diese werden in max. drei
Ausgabedateien ausgegeben: Die Codes, die Codelängen und der Baum zum Decodieren.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
49
Im Fall der eindimensionalen Tabellen kann 'huffgen' die Tabellen in SHCT umwandeln,
die Größe der Tabelle (esc+1) wird dabei mit '-b n' angegeben. Die 'mean code length' die
das Program angibt, ist nur als ungefährer Richtwert zu verstehen.
Optionen
huffgen Eingabedatei [Optionen]
-b
-c
-l
-s
-S
-t
-v
-z
Größe Tabellengröße für eindimensionale SHCT: Standard ist die Größe
der Eingabedatei.
Datei Ausgabe der Huffman Codes in Datei. Fehlt Datei, dann wird
Eingabedateiname.hcc gesetzt. Die Codes werden als 32 Bit
unsigned long integer ausgegeben.
Datei Ausgabe der Huffman Code Längen in Datei. Fehlt Datei, dann wird
Eingabedateiname.hcl gesetzt. Die Längen werden als 8 Bit
unsigned char ausgegeben.
Stacked Huffman Codes in einer Tabelle. Erfordert Option '-b n'.
Eine nicht mehr benötigte Funktion.
Stacked Huffman Codes in zwei Tabllen. Erfordert Option '-b n'.
Wird nur für eindimensionale SHCT benötigt, zweidimensionale
Tabellen werden ohne '-S' generiert.
Datei Ausgabe des Huffman Code Baums in Datei. Fehlt Datei, dann wird
Eingabedateiname.hct gesetzt. Die Tabelle wird als unsigned long
integer ausgegeben, wobei die erste Hälfte die linken, die zweite die
rechten Verzewigungen angibt.
Ausführlichere Meldungen werden ausgeben.
Unterdrückung der Beseitigung von Nullwerten
Anwendungsbeispiel
huffgen vegasum.bin -cvega5.hcc -lvega5.hcl -tvega5.hct -v -S b32
Generiert die Huffman Codes (vega8.hcc) und ihren Längeninformation (vega8.hcl)
als stacked codes (-S) mit einer Tabellengröße (esc-Wert+1) von 32 (-b32). Die
angezeigte mittlere Codelänge stimmt NICHT, sie dient momentan als Anhalt, ob die
Huffman Codes weiter verfeinert werden müssen, also weiter iteriert werden muß:
(Abfolge: pacse, huffgen, pacse...), oder ob die Tabellen den vorherigen Tabellen
entsprechen (gleicher Wert für die Codelänge).
6.3. Absoluter Vergleich mit 'snddiff'
'Snddiff' vergleicht zwei Sound-Dateien (16 oder 32 Bit), und liefert in einer summarischen Statistik die Prozentzahl der Abweichungen (32 und 16 Bit für 32 Bit Dateien). Es
wird außerdem angegeben, wieviele MSBs über die ganze Länge gesehen unverändert
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
50
blieben (worst-case-Betrachtung). Sind die Dateien unterschiedlich lang, dann wird bis
zum Ende der kürzeren Datei verglichen. Eine Ausgabe der Unterschiede in jedem
Abtastwert ermöglicht detailierte Fehleranalysen und graphische Ausgaben.
Optionen
snddiff orig-file test-file [options]
-a
Datei
-i
n
-n
n
-o
n
-s
Datei
-v
ASCII Tabelle der Unterschiede. Zur Auswertung per Gnuplot
geeignet.
Die letzten n Abtastwerte werden ignoriert. Ohne Angabe von n:
512 Abtastwerte
Maximal n Zeilen werden in die ASCII Tabelle geschrieben. Ohne
Angabe von n: 100 Zeilen. Beide Dateien werden trotzdem bis zum
Schluß verglichen, und die Schlußstatistik über die ganze Dateilänge
ausgegeben.
Die ersten n Datenbytes der zweiten Datei werden als Offset übersprungen, bevor mit dem Vergleich begonnen wird. Die Dateilänge
der zweiten Datei wird entsprechend angepaßt.
Die Differenzen in den obersten 16 Bit werden als NeXT .snd-Datei
ausgegeben.
Gibt Informationen aus dem Header beider Dateien im Klartext aus.
Anwendungsbeispiel
[S:\C\SND]snddiff vega32 vega32pac -v -atest.asc -i -n3
Structure of vega32.snd
Datalocation:
28
Data size (bytes): 1955328
Data Format:
linear 32 bit
Sampling rate:
44100
Channels:
1
Infostring[ 4]:
<EMPTY STRING>
Structure of vega32pac.snd
Datalocation:
28
Data size (bytes): 1955328
Data Format:
linear 32 bit
Sampling rate:
44100
Channels:
1
Infostring[ 4]:
<EMPTY STRING>
Writing up to 3 differences to test.asc, only !
Ignoring the last 512 samples, comparing 488320 samples.
Differences between vega32.snd and vega32pac.snd: 488106, that's
99.96%.
The upper 12 bits of 32 bits are unchanged.
Differences in the 16 MSBs: 35940, that's 7.36%.
Die Ausgabedatei test.asc sieht in diesen Falle so aus:
#
pos|
0
1
2
orig-test|ld| orig. file|
1 1
0
7 3
0
-5 3
0
test file
-1
-7
5
Die Felder bedeuten von links nach rechts: die Position als Nummer des Abtastwertes,
die Differenz zwischen Original- und Testdatei, der Zweierlogarithmus des Betrages der
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
51
Differenz, der Originalwert und der Wert in der Testdatei. Nur die Unterschiede werden
ausgegeben. Die erste Zeile wird von Gnuplot ignoriert, sie dient nur als Legende.
6.4. Beurteilung der Hörbarkeit von Artefakten mit 'nmr'
'NMR' (näheres s. [9]) wurde in der Version 'nmr_d'24 für 32-Bit Daten benutzt. Hier
sind nur die Ausgaben zur NMR wichtig, das ebenfalls ermittelte Noise Masking Flag
darf bei der verwendeten Codierung nie gesetzt sein.
Optionen
nmr_d musicfile1 musicfile2 #measurement_blocks [options]
Es werden hier die für die hier angestellten Untersuchungen wichtigen Optionen aus
der Hilfe-Funktion des Programmes zitiert:
-mversion
-m01
-m02
-bbegin
-ooffset
-pprotocol
-p1:
-p2:
-p4:
-p5:
measurement version (def: 1)
NMR4 44.1 kHz
Samplingrate: 44100
FFT-parameters: 1024 512
NMR4 48.0 kHz (not tested)
Samplingrate: 48000
FFT-parameters: 1024 512
first sample in inputfile (def:0)
offset between inputfiles (def:0)
protocol output
(def: 1,2)
Standardausgabe
overview: table of SNR over critical bands
actual: SNR of each block over critical bands
actual: NMR of each block over critical bands
Anwendungsbeispiel
nmr_d vega32.snd vega32PAC.snd 690 -p2 -m2 > vega_6_64.nmr
Vergleicht die beiden Sound-Dateien 'vega32.snd' und 'vega32PAC.snd' (in deren
Header in den betrachteten Fällen immer die falsche Abtastrate 44.1 kHz stand) für eine
Abtastrate von 48 kHz über 690 Blöcke à 512 ATW, und gibt die NMR blockweise in
'vega_6_64.nmr' aus.
24
Version: NMR V 1.0 - spo 2-apr-1992 14:30:25 -
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
52
6.5. Weitere Hilfsprogramme: 'convbits', 'convab', 'sndchk',
'testsig'
6.5.1.
convbits: Konvertierung zwischen 16-Bit-linear- und 32-Bit-linearSound Dateien
Mit 'convbits' lassen sich aus 16 Bit Sound-Dateien 32 Bit Sound-Dateien erzeugen.
Die unteren 16 Bit können dabei wahlweise mit weißem Rauschen oder mit Nullbits
belegt werden. Bei der Umwandlung von 32 zu 16 Bit kann das Signal durch Hinzufügen
eines Rauschens von 1/2 LSB (16 Bit) gedithert [23] werden, und somit der
Klangeindruck besser erhalten bleiben.
Falls nur ein Argument angegeben wird, wird der zweite Dateiname durch Anhängen
von '16' vor die Endung '.snd' bei der Konvertierung zu 16 Bit, und durch Anhängen von
'32' bei der Konvertierung zu 32 Bit erzeugt.
Optionen
convbits [-n] inputfile [outputfile]
-n
Dithering bzw. Rauschen hinzufügen
Anwendungsbeispiel
convbits vega32
Convertiert die 32 Bit Sound-Datei 'vega32.snd' in eine 16 Bit Datei 'vega3216.snd',
die unteren Bits werden gerundet, und nicht einfach abgeschnitten.
6.5.2.
convab: Konvertierung von ASCII- in Binärdarstellung und
umgekehrt
Convab konvertiert 1-, 2-, 4-Byte Worte mit und ohne Vorzeichen von ASCII- nach
Binärdarstellung und umgekehrt. Anwendungen sind die Erzeugung und Ausgabe der binären Huffman Codes zu Testzwecken, der Transport von Binärdaten von einer Plattform
zu einer anderen (z.B. HPUX zu OS/2), sowie die Aufbereitung der Daten zur
Darstellung mit Gnuplot. Je eine Zeile entspricht einem Binärwort. Fehlen einer oder
beide Dateinamen, dann arbeitet das Programm als Filter.
Optionen
convab -a|-b [options] [infile] [outfile]
-a
-b
Konvertierung von Binärdaten nach ASCII
Konvertierung von ASCII-Daten zu Binärdaten
(entweder '-a' oder '-b' muß angegeben werden)
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
53
-f
Format Format ist eine Formatangabe ähnlich wir in den C-Funktionen
printf/scanf:
d,i
signed Integer |
u
unsigned Integer
x,X
hexadezimal |
o
octal
Dieses Format wird bei Ein- und Ausgabe ausgewertet
n
Wortlänge in Bytes [n=1,2,4]
Format Beim Schreiben von ASCII:
Zeilennummern durch Format getrennt hinzufügen
Standard: Leerzeichen
Beim Lesen von ASCII: Verwendung als Scanf()-Format-String.
Standard: Überspringen der Zeilennummern mit %*s
Gibt Zahl der konvertierten Zeilen auf stderr aus
Kehrt die Byte-Anordnung um: Little
Big endian
(ja nach Rechner, auf dem das Programm läuft)
Daten werden als vorzeichenbehaftet betrachtet
-l
-n
-p
-r
-s
Anwendungsbeispiel
[S:\C\SND]convab -a i2826.hce -l1 -n
0 5
1 5
2 5
3 5
4 5
5 6
K
Gibt die binäre Huffman Code Tabelle 'i2826.hce' mit Zeilennummern als ASCII auf
stdout aus, wobei die Binärdaten als einzelne, vorzeichenlose Bytes betrachtet werden.
6.5.3.
sndchk: Informationen über .snd-Dateien, 'Endianess'Konvertierung
'Sndchk' gibt die Informationen in NeXT-.snd-Datei Header aus, vergleicht die
Dateilänge mit der Länge, die sich aus den Informationen im Header ergibt, und gibt die
Länge in Sekunden und in Blöcken zu 512 Bytes an. Bei inkonsistenten Daten erfolgt
eine Warnung.
Wurde die Datei auf einem Rechner mit einer anderen Byteanordnung erstellt, dann
kann durch Angabe eine zweiten Dateinamens eine Konvertierung der Datei in das
Format des gerade verwendeten Rechners gestartet werden. Die vier Bytes pro Wort
können dabei beliebig vertauscht sein, da die richtige Ordnung an der Magic Number im
Header erkannt wird.
Optionen
sndchk inputfile [outputfile]
Keine weiteren Optionen, nur die Angabe eines oder zweier Dateinamen als Schalter.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
54
Anwendungsbeispiel
[S:\C\SND]sndchk vega32
Structure of vega32.snd
Datalocation:
28
Data size (bytes): 1955328
Data Format:
linear 32 bit
Sampling rate:
44100
Channels:
1
Infostring[ 4]:
<EMPTY STRING>
Samples:
488832
Playing time:
11.08 sec
File length ok:
1955356 bytes, 954.8 blocks a 512 words
Bei fehlerhaften Angaben der Dateilänge wird die Differenz angezeigt.
6.5.4.
testsig: 32 Bit Testsignal-Erzeugung
'Testsig' kann Sinus- und Rechtecksignale mit 32 Bit Auflösung, variabler Frequenz,
Amplitude und Länge generieren. Die Ausgabedatei hat immer die Endung '.snd'.
Optionen
testsig [-option [optionargument]] [...] [file]
-a
-c
-d
-f
-r
-t
n
n
n
n
s|r
Amplitude: 1.0 (Standard) bis 0.0
Kanäle: 1 oder 2 (mono / stereo)
Länge in Sekunden
Frequenz in Hz (0...22050)
Invertierter rechter Kanal (Standard: gleichphasig)
Typ: Sinus oder Rechteck
Standardwerte: Defaults: f=1000 Hz, n=sin1000m.snd, d=0.5 sec, c=1, t=sin, a=1.0
Anwendungsbeispiel
testsig rechteck -a0.5 -c 2 -d5 -f1992 -r
Generiert eine stereo - Sound-Datei mit einem Rechteck halber Aussteuerung der Frequenz 1992 Hz, mit einem rechten Kanal, der gegenüber dem linken Kanal um 180°
verschoben ist, und einer Länge von 5 Sekunden.
6.6. Beispiele für die Kombination der Programme
Das Zusammenwirken der drei wichtigsten Programme, 'pacse', 'huffgen' und 'nmr_d'
läßt sich am besten anhand von Skript-Dateien (s. S. 111) für die Kornshell erläutern, mit
denen die Tabellen zur Codierung generiert, und die Simulationen durchgeführt wurden.
Zur Übersicht wurden einige hier überflüssigen Zeilen weggelassen.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
55
6.6.1.
Generierung neuer Huffman Tabellen
Zuerst sei die Generierung neuer zweidimensionaler SHCT vorgestellt:
1 # isotables2
2 # generate 2D huffman code tables for selected bitrates and iso music samples
3 # call example:
4 cd $SNDDIR
5 for bitrate in $1
6 do
7
echo Generating first start table
8
rm isotab_$2_2_$bitrate.bg?
9
for isofile in *iso*32.snd
10
do
11
# generate first huffman code table
12
pacse $isofile /dev/null -S2 -t$2 -r$bitrate -cisotab_$2_2_$bitrate \
13
-sisotab_$2_2_$bitrate -g
14
done
15
huffgen isotab_$2_2_$bitrate.bge -lisotab_$2_2_$bitrate.hce
16
huffgen isotab_$2_2_$bitrate.bgl -lisotab_$2_2_$bitrate.hcl
17
rm isotab_$2_2_$bitrate.bg?
18
# refine table
19
(( i_counter = 1 ))
20
while [[ i_counter -lt 6 ]]
21
do
22
echo Refining ... step $i_counter
23
for isofile in *iso*32.snd
24
do
25
# generate refined huffman code table
26
pacse $isofile /dev/null -S2 -t$2 -r$bitrate -cisotab_$2_2_$bitrate\
27
-sisotab_$2_2_$bitrate
28
done # for
29
huffgen isotab_$2_2_$bitrate.bge -lisotab_$2_2_$bitrate.hce
30
huffgen isotab_$2_2_$bitrate.bgl -lisotab_$2_2_$bitrate.hcl
31
(( i_counter = i_counter + 1 ))
32
rm isotab_$2_2_$bitrate.bg?
33
done # while
34
echo
35 done # for bitrate
36 echo Done !
zu 8:
Die Häufigkeitsstatistiken der SHCs werden in zwei Tabellen gesammelt:
*.bgl und *.bge (im eindimensionalen Fall nur *.bg1). 'pacse' ergänzt die
Werte, die in dieser Tabelle stehen, für einen neuen Durchlauf müssen
etwa vorhandene alte Statistiken deshalb gelöscht werden.
zu 12,13:
Die ISO-Stücke werden zuerst mit einer Dummy-SHCT codiert, in der alle
Längeninformationen zu 1 gesetzt sind ('-g'). Die Daten werden über alle
Stücke gesammelt. Es werden Daten für zweidimensionale Tabellen (-S2)
generiert, deren Namen mit '-s' angegeben wurden. Die Bitrate wird mit '-r'
festgelegt, mit '-t' die Größe der Tabelle. Die codierten Daten werden
verworfen.
zu 15,16:
Aus der Summenstatistik werden Huffman Codes Tabellen mit den
Längeninformationen erstellt ('-l'), die Option '-S' für stacked Codes wird
ausschließlich im eindimensionalen Fall angewandt.
zu 18-Ende:
Die Statistiken werden gelöscht, und mit den gewonnenen Tabellen ein
neuer Satz von SHCT gewonnen. Versuche haben gezeigt, daß insgesamt
6 Durchläufe (mit dem ersten) ausreichen, um bei der Iteration einen
stabilen Zustand zu erreichen.
Die wesentlichen Unterschiede im eindimensionalen Fall:
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
56
pacse $isofile /dev/null -S1 -t18 -r$bitrate -cisotab_$2_1_$bitrate \
-sisotab_$2_1_$bitrate -g
huffgen isotab_$2_1_$bitrate.bg1 -lisotab_$2_1_$bitrate.hcl -b$2 -S
zu 'pacse':
Die Option '-t' gibt jetzt an, wie groß die Tabelle für das Histogramm der
quantisierten Spektralwerte sein soll, bei kleineren Werten als 218, wie
hier verwendet, besteht die Gefahr eines Programmabbruchs. Die Option 'S1' könnte entfallen, sie ist Standardeinstellung und bedeutet, daß
eindimensionale Tabellen zur Codierung verwendet werden sollen. Die
Ausgabedaten haben die Endung '*.bg1'.
zu 'huffgen'
Mit '-b' wird die Größe der gewünschten SHCT angegeben, mit '-S' die
große Eingangstabelle intern in ein SHCT-Paar umgewandelt.
Standardmäßig werden die Nullwerte unterdrückt. Das Paar wird in eine
Datei mit Endung '*.hcl' ausgegeben.
6.6.2.
Simulationen
Zur Simulation einer Codierung mit zweidimensionalen Tabellen wurden folgende
Scriptzeilen verwendet (vgl. S. 116):
pacse $isofile isotest2_$2_$bitrate.snd -S2 -r$bitrate \
-cisotab_$2_2_$bitrate -CP${isofile%%32.snd}_2_$2_$bitrate.asc
nmr_d $isofile isotest2_$2_$bitrate.snd 690 -b7 -p5 -m2 | \
awk '/NMR:/ { print $2 }' > P${isofile%%32.snd}2_$2_$bitrate.nmr
rm isotest2_$2_$bitrate.snd
zu 'pacse':
'-S2' und '-c' geben die Verwendung zweidimensionaler SHCT an, die
tabellarische Ausgabe der blockweisen Statistiken der veränderten Bits
wird mit '-C' in eine Datei ausgegeben.
zu 'nmr_d'
Die Zahl '690' gibt die Zahl der zu vergleichenden Blöcke an, '-b' die Zahl
der am Anfang zu überspringenden Abtastwerte (4 Bytes·7=28 Byte NeXT
Sound Header). '-m' gibt die verwendeten 48 kHz Abtastrate an, '-p' die
blockweise Ausgabe der Statistiken, die von awk gefiltert werden.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
57
7. Zusammenfassung
Aufgabe der Arbeit war die Simulation der Codierung von Audiosignalen, mit dem
Ziel, die Datenrate zu senken, und dabei eine professionelle Signalqualität zu bewahren.
Das Verfahren soll so gut sein, daß bei einer Weiterverarbeitung im Studio mit
mehrfachen Codierungs- und Decodierungsschritten (Tandemcodierungsschritten) keine
Artefakte aufgrund der Codierung hörbar werden. Das Ziel wurde als Near Lossless
Coding bezeichnet.
Dazu wurden in der Sprache C mehrere Programme geschrieben, mit denen
Audiosignale mit hoher Genauigkeit be- und verarbeitet werden können. Das Hauptprogramm simuliert den Encoder und Decoder eines neuen, noch zu entwickelnden
Codierverfahrens. Bei der Simulation können mehrere Parameter des Encoders
vorgegeben werden. Mit unterschiedlichen, vorgebbaren Datenraten wurden Tabellen zur
Entropiecodierung generiert und die damit erreichbare Signalqualität bewertet. Zwei
Tabellentypen wurden verglichen: ein- und zweidimensionale Huffman Code Tabellen.
Als weitere Optimierungsmöglichkeit wurde die Einführung eines Bit-Reservoirs
untersucht. Dabei zeigte sich deutlich, daß im endgültigen Codierverfahren durch das BitReservoir eine weitere Absenkung der Datenrate erzielt werden kann. Das
Simulationsprogramm erlaubt es, Statistiken zur Verfahrensoptimierung auszugeben.
Zusammen mit ebenfalls angefertigten Hilfsprogrammen konnten die Huffman Code
Tabellen iterativ optimiert werden. Für diese Tabellen wurde mit den Stacked Huffman
Codes ein neues, Speicherplatz sparendes Verfahren erstmals implementiert.
Nach dem letzten Stand der Simulationen wurde für die gewünschte Signalqualität
eine Datenrate von 4-5 Bit pro Abtastwert und damit eine Datenkompression um den
Faktor 3,6 erreicht. Weitere Steigerungen des Faktors durch Verfeinerung des Verfahrens
sind absehbar.
Für nachfolgende Arbeiten steht ein Grundstock an neuen, rechnerunabhängig
geschriebenen Einzelprogrammen und Bibliotheksfunktionen in der Sprache C für
Simulationen zur Verfügung.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
58
8. Anhang
8.1. Programmlistings
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
59
8.1.1.
cliutil.c
/*
General Utilities for Command Line Programs: cliutil.c
Author:
Start:
Changed:
*/
result: 0
not found
>0
found, return position and copy string to ret
<0
no matching argument
*/
{
int position;
position=getarg_flag(argc,argv,optchar);
if (!position)
/* need optionchar */
return(0);
if (argv[position][2]) {
/* arg directly after optionchar */
strcpy(ret,&(argv[position][2]));
return(position);
} else {
/* arg as a separate argument */
if (argc > ++position) {
/* next arg must exist and not be an option */
if ((argv[position][0]!='-') && argv[position][0]) {
strcpy(ret,argv[position]);
*argv[position]=0; /* mark read, so that it is not considered */
/* to be a filename */
return(position);
} /* endif */
else
return (-1);
} else
return (-1);
} /* endif */
Robert Henke
10. 8.92
25.10.92
/* Command line parsing:
These routines look for a given argument, return it and modify it
in the command line buffer argv[]
Sample command line:
progam -a -b -CargC -d argd -f filename1 filname2 filename3
getarg_name has to be called after getarg_flag and getarg_option
getarg_illegals has to be called last
*/
#include <cliutil.h>
#define STRBUF 128
/* buffer for strings */
/* local GLOBALS */
static unsigned char files = 0;
/* number of files in command line */
int getarg_flag(int argc, char **argv, char flagchar)
/*
looks for option (-a) if flagchar equals a, searches all
args starting with result: 0
not found
>0
found, return position
*/
{
char i;
/* counter */
char option[3]="-";
/* build template here */
option[1]=flagchar;
for (i=1; i<argc; i++) {
if (!strncmp(argv[i],option,2)) {
*argv[i]=0;
/* mark read */
return (i);
}
} /* endfor */
return (0);
}
int getarg_dbl_option(int argc, char **argv, char optchar, double *ret)
/* like getarg_option, but converts argument to double */
{
char buffer[STRBUF];
int result;
if ((result=getarg_option(argc, argv, optchar, buffer))<=0) {
return (result); /* return without altering ret, of unsuccessful */
}
*ret=atof(buffer);
return (result);
}
int getarg_long_option(int argc, char **argv, char optchar, long *ret)
/* like getarg_option, but converts argument to long */
{
char buffer[STRBUF];
int result;
if ((result=getarg_option(argc, argv, optchar, buffer))<=0) {
return (result); /* return without altering ret, of unsuccessful */
}
*ret=atol(buffer);
return (result);
}
int getarg_option(int argc, char **argv, char optchar, char *ret)
/*
like getarg_flag, but looks for string following optchar or in next
argument
}
read strings (as a separate argument) are marked with a (char) 0
to not to conflict with the parsing of filenames, so it can be called
ONCE PER OPTION !
int getarg_int_option(int argc, char **argv, char optchar, int *ret)
/* like getarg_option, but converts argument to int */
{
int result;
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
60
long rvalue;
if ((result=getarg_long_option(argc, argv, optchar, &rvalue))<=0) {
return (result); /* return without altering ret, of unsuccessful */
}
*ret=(int) rvalue;
return (result);
void getarg_illegals(int argc, char **argv, const char **usage_txt)
/*
looks for any unevaluated args and prints usage-text, or
returns to calling program
*/
{
char i, n=0;
}
for (i=1; i<argc; i++) {
n += argv[i][0] != 0;
} /* endfor */
n -= files;
if (n>0) {
fprintf(stderr,
"I found %d filename%s, but I didn't recognize %d command line "
"parameter%s.\nFiles and unrecognized parameters:\n",files,
(files==1) ? "" : "s", n, (n==1) ? "" : "s");
for (i=1; i<argc; i++) {
if (argv[i][0])
fprintf(stderr,"\t\"%s\"\n",argv[i]);
} /* endfor */
int getarg_sht_option(int argc, char **argv, char optchar, short *ret)
/* like getarg_option, but converts argument to short */
{
int result;
long rvalue;
if ((result=getarg_long_option(argc, argv, optchar, &rvalue))<=0) {
return (result); /* return without altering ret, of unsuccessful */
}
*ret=(short) rvalue;
return (result);
}
usage(usage_txt,argv[0]);
} /* endif */
int getarg_char_option(int argc, char **argv, char optchar, char *ret)
/* like getarg_option, but converts argument to char */
{
int result;
long rvalue;
if ((result=getarg_long_option(argc, argv, optchar, &rvalue))<=0) {
return (result); /* return without altering ret, of unsuccessful */
}
*ret=(char) rvalue;
return (result);
}
void usage(const char **text, const char *progname)
/* Prints an array of text-strings, until an empty string is encountered */
/* A name may be given by using '%s' in the strings */
{
while (**text)
printf((char *) *text++,(char *) progname);
exit(EXIT_FAILURE);
}
}
int getarg_name(int argc, char **argv, int number, char *ret)
/*
looks for argument number 'number' that is no option, copies it into ret
number starts at 0
parsing for options has to be already completed (getarg_option)
Special filename '-' is allowed for possible use for stdin / stdout
result: 0
not found
>0
found, return position
*/
{
int i;
for (i=1; i<argc; i++) {
if (((argv[i][0]!='-') && (argv[i][0]!=0)) || !strcmp("-",argv[i])) {
if (!(number--)) {
strcpy(ret,argv[i]);
files++;
return (i);
} else {
continue;
} /* endif --number */
} /* endif */
} /* endfor */
return (0);
}
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
void error_usage(char *error_msg, const char **text, const char *progname)
/* Prints 'error_msg' followed by general usage text */
{
fprintf( stderr, "%s. Aborted.\n\n", error_msg );
usage (text, progname);
} /* error_usage */
char *add_extension(char *filename, char *extension)
/* makes sure the filename ends with ONE occurrence of extension */
{
int p;
if ((p=strstr(filename,extension)-filename)<=0) /* extension found */
return (strcat(filename,extension));
if (p>=0)
filename[p]=0;
/* cut filename for shorter extension*/
return (strcat(filename,extension));
}
char *remove_extension(char *filename, char *extension)
/* cuts filenam at first occurrence of extension */
{
61
int p;
if ((p=strstr(filename,extension)-filename)>0) /* extension found */
filename[p]=0;
/* cut filename for shorter extension*/
return (filename);
}
char *add_to_name(char *filename, char *extension, char *text)
/* adds text to body of filename, before extension. Adds extension, too,
if not present
*/
{
int p;
p=strstr(filename,extension)-filename;
if (p>=0)
filename[p]=0;
/* cut filename */
strcat(filename,text);
strcat(filename,extension);
return (filename);
}
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
62
8.1.2.
cliutil.h
/*
Standard definitions for command line argument evaluation and filename handling
Author:
Started:
Changed:
*/
Robert Henke
10. 8.92
29.10.92
#ifndef CLI_FLAG
#define CLI_FLAG
#include
#include
#include
#include
<strings.h>
<stdlib.h>
<stdio.h>
"defhenke.h"
number starts at 0
parsing for options has to be already completed (getarg_option)
result: 0
not found
>0
found, return position
*/
void getarg_illegals(int argc, char **argv, const char **usage_txt);
/*
looks for any unevaluated args and prints usage-text, or
returns to calling program
*/
void usage(const char **text, const char *progname);
/* Prints an array of text-strings, until an empty string is encountered */
/* A name may be given by using '%s' in the strings */
void error_usage(char *error_msg, const char **text, const char *progname);
/* Prints 'error_msg' followed by general usage text */
char *add_extension(char *filename, char *extension);
/* makes sure the filename ends with ONE occurrence of extension */
int getarg_flag(int argc, char **argv, char flagchar);
/*
looks for option (-a) if flagchar equals a, searches all
args starting with result: 0
not found
>0
found, return position
*/
char *remove_extension(char *filename, char *extension);
/* cuts filename at first occurrence of extension */
int getarg_option(int argc, char **argv, char optchar, char *ret);
/*
like getarg_flag, but looks for string following optchar or in next
argument
#endif /* CLI_FLAG */
char *add_to_name(char *filename, char *extension, char *text);
/* adds text to body of filename, before extension. Adds extension, too,
if not present
*/
read strings (as a separate argument) are marked with a (char) 0
to not to conflict with the parsing of filenames, so it can be called
ONCE PER OPTION !
result: 0
>0
<0
*/
not found
found, return position
no matching argument
int getarg_dbl_option(int argc, char **argv, char optchar, double *ret);
/* like getarg_option, but converts argument to double */
int getarg_long_option(int argc, char **argv, char optchar, long *ret);
/* like getarg_option, but converts argument to long */
int getarg_int_option(int argc, char **argv, char optchar, int *ret);
/* like getarg_option, but converts argument to int */
int getarg_sht_option(int argc, char **argv, char optchar, short *ret);
/* like getarg_option, but converts argument to short */
int getarg_char_option(int argc, char **argv, char optchar, char *ret);
/* like getarg_option, but converts argument to char */
int getarg_name(int argc, char **argv, int number, char *ret);
/*
looks for argument number 'number' that is no option, copies it into ret
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
63
8.1.3.
#define MAX_ITERATIONS 300
*/
coder.c
/*
coder.c
Functions for encoding and decoding sound data for PACSE
Robert Henke
15.10.92
Changed:
21.12.92
BUGS:
void init_encoder(encoder_parm *par)
/*
Allocate memory, set up variables and fields for the encoder, return if
successful
*/
{
ULONG i,j;
memory error in mdct (uses n+1 places for 1st block)
par->data_size = par->mdct_size*2;
differentiation causes block limit errors
<math.h>
<stdlib.h>
"miscutil.h"
"hufflib.h"
"coder.h"
/* get memory */
par->windowfunc = (double *) mycalloc( par->mdct_size, sizeof(double) );
par->aux_block = (double *) mycalloc( par->data_size+10, sizeof(double) );
par->quant_block = (double *) mycalloc( par->mdct_size, sizeof(double) );
/* misceallanous functions: my?alloc */
/* library for statistics and huffman coding */
#undef DEBUG
#define DEBUG TRUE
#define DIF_ENABLED FALSE
/* differentiate before encoding */
#define DIF_DEBUG FALSE
/* Diag. Messges for debugging differentiation */
#define CNT_SML_DEBUG FALSE
/* Count small bits error checking */
#define NONLINEAR_QUANTIZATION FALSE
/* declarations for fft.c */
extern mdct(double *xr,int iex,int n, double *yr);
extern imdct(double *xr, int iex, int n, double *yr);
extern rkof(int m);
/* prototypes for local functions */
static ULONG count_sml_bits( double *array, encoder_parm *par, char *hcl_tab );
static void set_value_ranges( encoder_parm *par, double *data );
static ULONG count_big_bits( double *array, encoder_parm *par,
ULONG bigbit_limit);
void enter_2_2d(double *data, ULONG big_values, ULONG **table_l, ULONG
*table_h,
unsigned short esc_value);
ULONG count_bits(double *array, ULONG bit_limit, encoder_parm *par);
/* Some hard - coded tables for small values */
ULONG small_tab_code[2][16] =
{
{ 1, 5, 4, 5, 6, 5, 4, 4, 7, 3, 6, 0, 7, 2, 3, 1 },
{ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 }
};
char small_tab_lengths[2][16] =
{
{ 1, 4, 4, 5, 4, 6, 5, 6, 4, 5, 5, 6, 5, 6, 6, 6 },
{ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4 }
};
/* data_size for fft */
/* calculate ld(data_size) */
par->data_ld = ldi(par->data_size);
par->mdct_ld = ldi(par->mdct_size);
to do:
*/
#include
#include
#include
#include
#include
/* abort encoder inner loop to prevent lockup
#if DIF_ENABLED
par->sign_bits = (int *) mymalloc( par->mdct_size, sizeof(int));
#endif
if (par->stacked == 1) {
par->huffman_lengths = (char *) mycalloc( par->huffman_size, sizeof(char)
);
par->huffman_lengths_low = NULL;
par->huffman_lengths_high = NULL;
for (i=0; i<par->huffman_size; i++)
par->huffman_lengths[i] = 1;
/* preset with 1
} /* if */
else {
par->huffman_lengths = NULL;
/* not used
par->huffman_lengths_low = create_char_tab2d(par->huffman_size,
par->huffman_size);
par->huffman_lengths_high = (char *) mymalloc(par->huffman_size,
sizeof(char*));
for (i=0; i< par->huffman_size; i++) {
/* preset with 1
for (j=0; j< par->huffman_size; j++) {
par->huffman_lengths_low[i][j]=1;
}
par->huffman_lengths_high[i]=1;
}
} /* else */
rkof(par->data_size);
/* initialize FFT twiddle factors
*/
*/
*/
/* preset windowing array */
for (i=0; i<par->mdct_size; i++) {
par->windowfunc[i]=sin(M_PI*(i+0.5)/par->mdct_size/2);
} /* for i<mdct_size */
if (par->debug_flags & 32)
fprintf(stderr,"init_encoder: mdct_size=%lu, data_size=%lu, hc_size=%lu"
"\n\tstacked=%d, qstep=%.3f, data_ld=%ld\n",par->mdct_size,
par->data_size, par->huffman_size, par->stacked, par->quantstep,
par->data_ld);
par->bit_bucket = 0;
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
*/
64
out_array(data, par->array_debug_len, "n MDCT");
} /* init_encoder */
#endif
void cleanup_encoder(encoder_parm *par)
/* Deallocate memory used by encoder */
{
if (par->stacked == 1) {
free (par->huffman_lengths);
} /* if */
else {
free ( par->huffman_lengths_low );
free ( par->huffman_lengths_high );
} /* else */
#if DIF_ENABLED
free (par->sign_bits);
#endif
free (par->windowfunc);
free (par->aux_block);
} /* cleanup_encoder */
double encode_block(double *data, double qquant, encoder_parm *par)
/*
encode one block: count bits necessary and change quantization until
specified bit_limit is reached
*/
{
ULONG iterations=0,
/* iterations before reaching acceptable bitrate */
bits=0,
/* bits needed to code this block */
bucket_save;
/* save for bit_bucket */
/* TEST FOR DEBUGGING */
static char first_time = TRUE;
#if DIF_ENABLED
differentiate_block(data, par->mdct_size, par->sign_bits);
#endif
if (par->debug_flags & 64) {
printf("encode_block: max=%18.2f
",
array_max(data, par->mdct_size, 0.0));
fflush(stdout);
}
/* look whether quantization is fine enough */
do {
if (qquant <= 1.0) {
/* no amplification, please */
qquant = 1.0;
break;
}
par->bit_bucket = bucket_save;
memcpy(par->quant_block, data, sizeof(double)*par->mdct_size);
arrayquant(par->quant_block, par->mdct_size, qquant, par->nl_factor);
bits = count_bits(par->quant_block, par->bit_limit, par);
iterations++;
if (bits)
/* array_quant scales 'quant_block' by qquant */
qquant /= par->quantstep;
if (iterations >= MAX_ITERATIONS) {
fprintf(stderr,"encode_block: aborted upscaling after %d
iterations\n",
MAX_ITERATIONS);
exit (EXIT_FAILURE);
} /* if */
} while (bits && (array_max(par->quant_block, par->mdct_size,1.0)
<= par->max_value));
/* look whether quantization is coarse enough */
do {
par->bit_bucket = bucket_save;
memcpy(par->quant_block, data, sizeof(double)*par->mdct_size);
arrayquant(par->quant_block, par->mdct_size, qquant, par->nl_factor);
bits=count_bits(par->quant_block, par->bit_limit, par);
qquant *= par->quantstep; /* array_quant scales 'quant_block' by qquant
/* function called for the first time */
#if DEBUG
if ((par->debug_flags & 36) == 36)
out_array(data, 2*par->array_debug_len, "v MDCT");
#endif
*/
bucket_save = par->bit_bucket;
apply_window(data, par->windowfunc, par->data_size);
mdct(data, par->data_ld, par->data_size, par->aux_block);
if (first_time) {
first_time = FALSE;
if (par->stacked) {
if (array_max(data,par->data_size,1.0) > pow(2.0,sizeof(ULONG)*8-1))
qquant = (array_max(data,par->data_size,1.0)/
pow(2.0,sizeof(ULONG)*8-1));
else
qquant = 1.0;
}
else
qquant = (array_max(data,par->data_size,1.0)/par->huffman_size);
} /* if (first_time) */
#if DEBUG
if ((par->debug_flags & 36) == 36)
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
iterations++;
if (iterations >= MAX_ITERATIONS) {
fprintf(stderr,"encode_block: aborted downscaling after %d
iterations\n",
MAX_ITERATIONS);
exit (EXIT_FAILURE);
} /* if */
} while (!bits || (array_max(par->quant_block, par->mdct_size, 0.0)
> par->max_value));
qquant /= par->quantstep;
/* get right quantization */
/*
printf("encode_block: bits=%ld, max=%ld, bucket=%ld, limit=%ld\n", bits,
par->bit_limit, par->bit_bucket, par->bit_bucket_size);
*/
memcpy(data, par->quant_block, sizeof(double)*par->mdct_size);
65
if (par->debug_flags & 32)
fprintf(stderr,"encode_block: %4ld 0s -- %4ld 1s, \n",
par->mdct_size-1 - par->small_values,
par->small_values - par->big_values);
#if DEBUG
if ((par->debug_flags & 36) == 36)
out_array(data, par->array_debug_len, "Quanti");
#endif
#if DEBUG
if (par->debug_flags & 32) {
fflush(stderr);
fprintf(stderr,"encode_block: bits=%ld, bit_limit=%ld, qquant=%f"
" iter=%lu\n", bits, par->bit_limit+par->bit_bucket, qquant,
iterations);
}
#endif
/*
count bits needed to code 'array' using 'huffman_lengths', 'huffman_size',
'stacked', 'data_size' and 'par'; if 'bit_limit' is exceeded,
0 is returned, otherwise the number of bits for this data block
*/
{
ULONG i,
temp_variable;
long bit_sum=0;
/* number of bits needed for this block */
set_value_ranges(par, array);
bit_limit += par->bit_bucket;
par->bit_bucket = 0;
/* insert actual coding routines for big values HERE
OR as an extra function called with the coded data */
bit_sum += sizeof(short)*8;
/* one temp_variable scaling factor */
bit_sum += par->mdct_ld-1 + par->mdct_ld-2;
/* value position space channels number info
divideable by 2 and 4, one and two bits can be dropped */
if (par->debug_flags & 128)
fprintf(stderr,"count_bits: static: %4ld ",bit_sum);
if (par->debug_flags & 64) {
printf (" %18.2f\t%18.2f\n",
qquant, array_max(data,par->data_size,0.0));
fflush(stdout);
}
if (bit_sum > bit_limit) {
if (par->debug_flags & 128)
fprintf(stderr,"static bits limit exceeded.\n");
return(0);
}
return (qquant);
/* count number for small values */
par->sml_table_number = 0;
temp_variable = count_sml_bits( array, par, small_tab_lengths[0]);
} /* encode_block */
void decode_block(double *data, double qquant, encoder_parm *par)
/*
decode one block, quantization factor has to be given
*/
{
/* huffman decoding has to be done HERE -- later */
arraydequant(data, par->mdct_size, qquant, par->nl_factor);
#if DIF_ENABLED
integrate_block(data, par->mdct_size, par->sign_bits);
#endif
#if DEBUG
if ((par->debug_flags & 36) == 36)
out_array(data, par->array_debug_len, "n. Deq");
#endif
imdct(data, par->data_ld, par->data_size, par->aux_block);
apply_window(data, par->windowfunc, par->data_size);
}
void cleanup_decoder(encoder_parm *par)
{
;
}
ULONG count_bits(double *array, ULONG bit_limit, encoder_parm *par)
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
if ((i = count_sml_bits( array, par, small_tab_lengths[1])
< temp_variable)) {
par->sml_table_number = 1;
temp_variable = i;
} /* if */
bit_sum += temp_variable;
if (bit_sum > bit_limit) {
if (par->debug_flags & 128)
fprintf(stderr,"small bits limit exceeded.\n");
return(0);
}
if (par->debug_flags & 128)
fprintf(stderr,"small: %4ld ",temp_variable);
/* count number for big values */
temp_variable = count_big_bits( array, par, bit_limit-bit_sum);
if (!temp_variable) {
if (par->debug_flags & 128)
fprintf(stderr,"big bits limit exceeded.\n");
return (0);
}
bit_sum += temp_variable;
if (par->debug_flags & 128) {
if (bit_sum <= bit_limit) {
fprintf(stderr," sum: %4ld/%4ld (=%ld spare)\n",bit_sum, bit_limit,
(long) bit_limit-bit_sum);
}
66
else
fprintf(stderr,"\n");
fflush (stderr);
#if NONLINEAR_QUANTIZATION
double temp;
#endif
}
if ((long) bit_limit-bit_sum > 0)
{
par->bit_bucket = bit_limit-bit_sum;
if (par->bit_bucket > par->bit_bucket_size)
{
par->bit_bucket = par->bit_bucket_size;
}
return (bit_sum);
}
if (par->debug_flags & 128)
fprintf(stderr,"bits/block limit exceeded.\n");
return (0);
} /* count_bits */
void arrayquant(double *array, unsigned long arraysize, double divisor,
double nl_factor)
/* quantizise the whole array by dividing and rounding to nearest integer */
{
unsigned long i;
double temp;
for (i=0; i<arraysize; i++) {
#if DEBUG_QUANT
printf("aq: *array=%.2f - *array/%.2f=",*array,divisor);
#endif
if (*array < 0.0) {
temp = - *array / divisor;
#if NONLINEAR_QUANTIZATION
temp = pow (temp, nl_factor);
#endif
#if DEBUG_QUANT
printf("%.2f\n",ceil(-temp-0.5));
#endif
*array++ = ceil(-temp-0.5);
} /* if */
else {
temp = *array / divisor;
#if NONLINEAR_QUANTIZATION
temp = pow (temp, nl_factor);
#endif
#if DEBUG_QUANT
printf("%.2f\n",floor(temp+0.5));
#endif
*array++ = floor(temp+0.5);
} /* else */
} /* for */
} /* arrayquant */
void arraydequant(double *array, unsigned long arraysize, double multiplier,
double nl_i_factor)
/* multiply the whole array by multiplier */
{
unsigned long i;
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
for (i=0; i<arraysize; i++) {
#if NONLINEAR_QUANTIZATION
if (*array < 0.0) {
temp = - pow(- *array, nl_i_factor) * multiplier;
} /* if */
else {
temp = pow(*array, nl_i_factor) * multiplier;
} /* else */
*array++ = temp;
#else
*array++ *= multiplier;
#endif
} /* for */
} /* arraydequant */
void apply_window(double *array, double *window, unsigned size)
{
unsigned i, s_half=size/2;
for (i=0; i<s_half; i++) {
array[i] *= *window;
array[size-1-i] *= *window++;
}
} /* apply_window */
static ULONG count_sml_bits( double *array, encoder_parm *par, char *hcl_tab)
/*
count bits for small_values in 'array' using 'hcl_tab' as length
only for values != 0 sign bits are transmitted
*/
{
unsigned short i, j, index;
ULONG sum;
double *temp;
temp = array;
/* add bit for table selection */
sum = ! (par->small_values-par->big_values-1);
if (!sum)
return (0);
array += par->big_values+1;
for ( i=par->big_values+1; i <= par->small_values;
i += par->sml_values_bits ) {
index = 0;
for ( j=0; j < (unsigned char) par->sml_values_bits; j++, array++) {
index >>= 1;
#if CNT_SML_DEBUG
if (ABS(*array) > 1.0) {
fprintf(stderr,
"count_sml_bits: Wrong small bits range: %f, %d, %d",
ABS(*array), par->big_values, par->small_values);
fflush(stderr);
exit (EXIT_FAILURE);
} /* error handling */
#endif
sum += (*array != 0);
/* sign bit */
67
index |= ( ABS(*array) ) ? ( 1 << par->sml_values_bits ) : 0;
} /* for */
sum += hcl_tab[index];
} /* for */
return ( sum );
} /* count_sml_bits */
static ULONG count_big_bits( double *array, encoder_parm *par,
ULONG bigbit_limit)
/*
count bits for big_values in 'array' using 'hcl'.
Aborts and returns 0 if bigbit_limit is exceeded
*/
{
unsigned short i,
esc,
/* last element of array */
esc_length;
/* code length for esc value */
ULONG x_value, y_value,
/* pair of data to be coded */
sum=0,
one_code,
big_values;
static void set_value_ranges( encoder_parm *par, double *data)
/*
set big_value range to even number of big_values and to a multiple of
sml_values_bits for the small_values, leaving an even number of
zero_values
*/
{
ULONG i;
esc = par->huffman_size-1;
big_values = par->big_values >> 1;
if (par->stacked == 1) {
esc_length = par->huffman_lengths[esc];
for (i=0; i <= big_values; i++) {
x_value = (ULONG) fabs(*array++);
sum += x_value / esc * esc_length +
par->huffman_lengths[x_value % esc];
sum += (x_value != 0);
if (sum > bigbit_limit)
return (0);
} /* endfor */
} /* stacked == 1 */
else {
for (i=0; i <= big_values; i++) {
x_value = (ULONG) fabs(*array++);
y_value = (ULONG) fabs(*array++);
sum += (x_value != 0) + (y_value != 0);
/* sign bit */
/* Scan for first non-zero value */
for ( i = par->mdct_size-1; i && (data[i] == 0.0) ; i--)
;
par->small_values = i;
/* Count small values */
for ( ; i && (fabs(data[i]) <= 1.0) ; i--)
;
par->big_values = i;
/* sign bit */
if (x_value < esc) {
if (y_value < esc) {
one_code = par->huffman_lengths_low[x_value][y_value];
/* a */
} /* y < esc */
else {
/* b1 */
one_code =par-> huffman_lengths_low[x_value][esc];
y_value -= esc;
one_code += par->huffman_lengths_high[esc] * y_value / esc;
one_code += par->huffman_lengths_high[y_value % esc];
} /* x < esc, y >= esc */
} /* x < esc */
else {
/* b2 */
if (y_value < esc) {
one_code = par->huffman_lengths_low[esc][y_value];
x_value -= esc;
one_code += par->huffman_lengths_high[esc] * x_value / esc;
one_code += par->huffman_lengths_high[x_value % esc];
} /* y < esc */
else {
/* c */
one_code = par->huffman_lengths_low[esc][esc];
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
x_value -= esc;
y_value -= esc;
one_code += par->huffman_lengths_high[esc] *
MAX (x_value / esc, y_value / esc);
one_code++;
/* flag x > y */
one_code += par->huffman_lengths_high[x_value % esc];
one_code += par->huffman_lengths_high[y_value % esc];
} /* y >= esc */
} /* x >= esc */
sum += one_code;
if (par->debug_flags & 256) {
printf("count_big_bits: [%4lu/%4lu] %6lu, sum/lim=%4lu/%4lu, x=%10u
y=%10u\n",
i, big_values, one_code, sum, bigbit_limit, x_value, y_value);
} /* if debug_flags */
if (sum > bigbit_limit)
return (0);
} /* for i in big_values */
} /* stacked == 2 */
return ( sum );
} /* count_big_bits */
/* big_values has to be an even number, the number of small values dividable
by 4 (=sml_values_bits)
*/
if (! ( par->big_values & 1 )) {
/* even number of bits */
par->big_values++;
if (par->big_values > par->mdct_size-1 - par->sml_values_bits)
par->big_values = par->mdct_size-1;
if (par->small_values < par->big_values)
par->small_values = par->big_values;
}
par->small_values += par->sml_values_bits (par->small_values - par->big_values ) % par->sml_values_bits;
if (par->small_values > par->mdct_size-1 - par->sml_values_bits)
par->big_values = par->small_values = par->mdct_size-1;
} /* set_value_ranges */
void differentiate_block(double *block, ULONG blocksize, int *sign)
/*
replace block by differences to last value. First value of each block
68
set to 0
*/
{
ULONG i;
double last = 0.0,
actual;
#if DIF_DEBUG
printf("\n");
#endif
for (i=0; i<blocksize; i++) {
actual = block[i];
sign[i] = actual < last;
#if DIF_DEBUG
if ((i<10)||(i>500)) printf("d: block[%3d]=%16.2f sign=%d,
dif=",i,block[i],sign[i]);
#endif
block[i] = fabs(actual-last);
#if DIF_DEBUG
if ((i<10)||(i>500)) printf("%16.2f\n",block[i] );
#endif
last = actual;
} /* for */
} /* differentiate_block */
void integrate_block(double *block, ULONG blocksize, int *sign)
/*
replace block by integrated value. First sum of each block is set to 0.
*/
{
ULONG i;
double last = 0.0;
#if DIF_DEBUG
printf("\n");
#endif
for (i=0; i<blocksize; i++) {
#if DIF_DEBUG
if ((i<10)||(i>500)) printf("i: block[%3d]=%16.2f, sum=",i,block[i]);
#endif
block[i] = last + (block[i] * ((sign[i]) ? -1.0 : 1.0));
last = block[i];
#if DIF_DEBUG
if ((i<10)||(i>500)) printf("%16.2f\n",block[i]);
#endif
} /* for */
} /* integrate_block */
Each table has a size of esc_value*esc_value. table_l is the table
for low values, table_h the one for esc-caped ones.
This table is the frequency table for the codes (to be used by huffgen)
*/
{
unsigned short i,
esc;
/* last element of array */
ULONG x_value, y_value;
/* pair of data to be coded */
esc = esc_value-1;
for (i=0; i<= (big_values >> 1) ; i++) {
x_value = (ULONG) fabs(*data++);
y_value = (ULONG) fabs(*data++);
if (x_value < esc) {
if (y_value < esc) {
stat_bothlow++;
table_l[x_value][y_value]++;
} /* y < esc */
else {
stat_onelow++;
table_l[x_value][esc]++;
y_value -= esc;
table_h[esc] += y_value / esc;
table_h[y_value % esc]++;
} /* x < esc, y >= esc */
} /* x < esc */
else {
if (y_value < esc) {
stat_onelow++;
table_l[esc][y_value]++;
x_value -= esc;
table_h[esc] += x_value / esc;
table_h[x_value % esc]++;
} /* y < esc */
else {
stat_bothhigh++;
table_l[esc][esc]++;
x_value -= esc;
y_value -= esc;
table_h[esc] += MAX (x_value / esc, y_value / esc);
table_h[x_value % esc]++;
table_h[y_value % esc]++;
} /* y >= esc */
} /* x >= esc */
} /* for i in big_values */
} /* enter_2_2d */
/* a */
/* b1 */
/* b2 */
/* c */
/* Collect some statistics */
ULONG stat_onelow = 0,
stat_bothlow = 0,
stat_bothhigh = 0;
void enter_2_2d(double *data, ULONG big_values, ULONG **table_l, ULONG
*table_h,
unsigned short esc_value)
/*
enter 2 frequency value into the two 2 - dim tables pointed to by 'tables'.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
69
void cleanup_decoder(encoder_parm *par);
8.1.4.
coder.h
void apply_window(double *array, double *window, unsigned size);
/* apply window to 'array' */
void arrayquant(double *array, unsigned long arraysize, double divisor,
double nl_factor);
/* divide the whole 'array' by 'divisor' */
/*
coder.h
Function prototypes for encoding and decoding sound data for PACSE
Robert Henke
17.10.92
Changed:
8.12.92
*/
typedef struct
/* parameters set for the encoder */
{
ULONG data_size,
/* size of time domain data block
mdct_size,
/* size of frequency domain block=data_size/2
mdct_ld,
/* ld(mdct_size)
data_ld,
/* ld(data_size)
debug_flags,
/* activate debug info printing
huffman_size,
/* size of huffman code table
array_debug_len,
/* number of array elements to print
bit_limit,
/* allowed bits / block
bit_bucket,
/* number of spare bits in last block
bit_bucket_size;
/* max. number of spare bits
double *aux_block,
/* aux. block
*quant_block,
/* for testing quantizied data
*windowfunc,
/* windowing function array
quantstep,
/* quantization step size
max_value;
/* max. value allowed for quantized values
char *huffman_lengths,
/* pointer to huffman code length tables
**huffman_lengths_low,
/* same for 2D tables, low values
*huffman_lengths_high,
/* same for 2D tables, escaped values
stacked,
/* should hc table be considered a stacked one ?
sml_values_bits, /* number of small values combined into one entry
sml_table_number,
/* which of the two tables is used
*sign_bits;
/* temp. storage for sign bits
double nl_factor;
/* non linear quantization factor:
pow(x, nl_factor)
unsigned short big_values,
/* starting with freq 0 until small_values
small_values;
/* lines with abs(freq) <= 1
} encoder_parm;
void arraydequant(double *array, unsigned long arraysize, double multiplier,
double nl_i_factor);
/* multiply the whole 'array' by 'multiplier' */
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*
*/
*/
*/
void differentiate_block(double *block, ULONG blocksize, int *sign);
/*
replace block by differences to last value. First value of each block
set to 0
*/
void integrate_block(double *block, ULONG blocksize, int *sign);
/*
replace block by integrated value. First sum of each block is set to 0.
*/
void enter_2_2d(double *data, ULONG big_values, ULONG **table_l, ULONG
*table_h,
unsigned short esc_value);
/*
enter 2 frequency value into the two 2 - dim tables pointed to by 'tables'.
Each table has a size of esc_value*esc_value. table_l is the table
for low values, table_h the one for esc-caped ones.
This table is the frequency table for the codes (to be used by huffgen)
*/
void init_encoder(encoder_parm *parameters);
/*
Allocate memory, set up variables and fields for the encoder, return if
successful
*/
void cleanup_encoder(encoder_parm *par);
/* Deallocate memory used by encoder */
double encode_block(double *data, double qquant, encoder_parm *par);
/*
encode one block: count bits necessary and change quantization until
specified bit_limit is reached
*/
void decode_block(double *data, double qquant, encoder_parm *par);
/*
decode one block, quantization factor has to be given
*/
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
70
8.1.5.
convab.c
/*
Converter
binary file to ASCII, ASCII to binary
Format may be given by command line options
Robert Henke
Changed:
Input:
Output:
26.8.92
27.9.92
8,16,32 bit signed or unsigned binary, ASCII on value per line
same, for ASCII hex, decimal, octal, binary
Input and output may be from stdin, stdout or file
BUGS:
to do:
*/
#define BUFSIZE
#define FMTLEN
#include
#include
#include
#include
4096
20
"cliutil.h"
"miscutil.h"
<stdio.h>
<string.h>
void reverse_bytes(char *word, char length);
/* change word order of word pointed to by word with length bytes */
int main(int argc, char *argv[], char *envp[])
{
long items,
/* number of items read / written */
allitems=0,
/* all items read */
blockitems=0,
/* items in this block */
i;
/* loop counter */
char bin,
/* conversion direction */
debug=0,
lines,
/* add lines separated by add */
print,
/* print number of lines processed */
reverse=FALSE,
/* reverse byte order */
working=TRUE,
word=4,
/* bytes in a word */
wsigned;
/* interpret as signed */
char asc_format[FMTLEN]="", /* format string for scanf/printf */
separator[FMTLEN]="",
/* separator between line number and value */
tmp_string[FMTLEN]=" "; /* format string for scanf/printf, temporary*/
FILE *infile,*outfile;
/* you guessed it: our files */
char infilename[FNLEN]="",
outfilename[FNLEN]="";
union allptr
/* the compiler has to do the type checking */
{
char *chr;
short *sht;
long *lng;
} fbuffer,
/* file buffer for i/o */
fbptr;
/* file buffer temp. pointer */
union allval
{
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
char chr;
short sht;
long lng;
} nbuffer;
const char *usage_txt[] = {
"usage: %s -a|-b [options] [infile] [outfile]\n"
"\tConversion between binary and ASCII data as filter or on files\n",
"\tFormat defaults to unsigned 4 Bytes/word, ASCII values as decimals\n",
"\t-a\toutput ASCII data from binary input\n",
"\t-b\toutput binary data from ASCII input\n",
"\t-f fmt\tfmt is a format specifier for ASCII like in printf/scanf:\n",
"\t\t d,i\tsigned\t| u\tunsigned\n",
"\t\t x,X\thex\t| o\toctal\n",
"\t\tThis string is used for input and output before the data values\n",
"\t-l n\tword length n bytes [n=1,2,4]\n",
"\t-n fmt\tadd line numbers separated by the string fmt (default is
space)\n",
"\t\tFor reading: ignore line numbers (%%*s)\n",
"\t-p\tprint number of lines processed (to stderr)\n",
"\t-r\treverse byte order\n",
"\t-s\tsigned data\n",
VERSION_STRING,
""
};
/* need some help ?! */
if (getarg_flag(argc,argv,'h') || getarg_flag(argc,argv,'?'))
usage(usage_txt,argv[0]);
/* check options, defaults are in definition of variables */
if (getarg_flag(argc,argv,'b'))
bin = TRUE;
else
if (getarg_flag(argc,argv,'a'))
bin = FALSE;
else
errormsg("Either -a or -b have to be given");
getarg_char_option(argc,argv,'l',&word);
getarg_option(argc,argv,'f',asc_format);
lines=getarg_option(argc,argv,'n',separator);
if (lines < 0)
/* no separator given - defaulting */
if (bin)
strcpy(separator,"%*s"); /* remove line numbers */
else
strcpy(separator," ");
wsigned=getarg_flag(argc,argv,'s');
reverse=getarg_flag(argc,argv,'r');
print=getarg_flag(argc,argv,'p');
getarg_name(argc,argv,0,infilename); /* first filename is input */
getarg_name(argc,argv,1,outfilename); /* second is output */
if (*infilename)
infile=opfile(infilename, (bin) ? "r" : "rb", " (input file)");
else infile=stdin;
if (*outfilename)
outfile=opfile(outfilename, (bin) ? "wb" : "w", " (output file)");
else outfile=stdout;
/* my secret switch */
debug=getarg_flag(argc,argv,'d');
71
/* after input of last parameter: */
getarg_illegals(argc, argv, usage_txt);
/* adjust format string */
if (word==4)
strcpy(tmp_string,"%l");
else
strcpy(tmp_string,"%h");
/* add size info */
if (!*asc_format) {
/* no format string given */
switch (word) {
case 1:
case 2:
case 4:
if (wsigned)
strcat(tmp_string,"d");
else
strcat(tmp_string,"u");
break;
default: errormsg("Word length only 1,2 or 4");
} /* switch */
} /* default string */
if (!bin)
strcat(tmp_string,strcat(asc_format,"\n"));
strcpy(asc_format,tmp_string);
if (bin && lines) {
if (separator[0] != '%') { /* insert char */
memmove(&separator[1],separator,strlen(separator)+1);
separator[0]='%';
}
if (separator[1] != '*') { /* insert char */
memmove(&separator[2],&separator[1],strlen(separator)+1);
separator[1]='*';
}
strcat(separator,asc_format);
strcpy(asc_format,separator);
}
/* print debug info */
if (debug)
fprintf(stderr,
"Type=%s %s, format=\"%s\", wordlength=%d%s%c, in=\"%s\", out=\"%s\"\n",
(bin) ? "binary" : "ASCII", (wsigned) ? "signed" : "unsigned",
asc_format,word,(lines) ? ", line separator=" : ", no line numbers",
separator[0],infilename, outfilename);
/* memory for buffer */
switch (word) {
case 1: fbuffer.chr=(char *) mymalloc(BUFSIZE,word); break;
case 2: fbuffer.sht=(short *) mymalloc(BUFSIZE,word); break;
case 4: fbuffer.lng=(long *) mymalloc(BUFSIZE,word); break;
}
/* convert */
while (working) {
if (bin) {
fbptr.chr=fbuffer.chr;
for (i=0; i<BUFSIZE; i++) {
switch (word) {
case 1: items=fscanf(infile, asc_format, &nbuffer.sht);
*fbptr.chr = (char) nbuffer.sht & 0xFF;
if (reverse)
reverse_bytes(fbptr.chr,word);
fbptr.chr++;
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
break;
case 2: items=fscanf(infile, asc_format, fbptr.sht);
if (reverse)
reverse_bytes(fbptr.chr,word);
fbptr.sht++;
break;
case 4: items=fscanf(infile, asc_format, fbptr.lng);
if (reverse)
reverse_bytes(fbptr.chr,word);
fbptr.lng++;
break;
} /* case word */
if (debug)
fprintf(stderr,
"Read ascii, items=%ld, blockitems=%ld, allitems=%ld\n",
items, blockitems, allitems);
fileerr("Input file read",infile);
if (items != 1)
if (items != EOF)
errormsg("Error reading ASCII file-wrong format");
else {
working = FALSE;
break;
/* out of for loop */
} /* else */
if (debug)
fprintf(stderr,"Pointer=%lX\n", fbptr);
allitems++;
blockitems++;
} /* for i<BUFSIZE */
items=fwrite (fbuffer.chr, word, blockitems, outfile);
if (debug)
fprintf(stderr,
"Wrote binary, items=%ld, blockitems=%ld, allitems=%ld\n",
items, blockitems, allitems);
fileerr("Output file write",outfile);
if (items != blockitems)
errormsg("Error writing binary data");
blockitems=0;
} /* ascii to bin */
else {
switch (word) {
case 1: items=fread(fbuffer.chr,1,BUFSIZE,infile); break;
case 2: items=fread(fbuffer.sht,2,BUFSIZE,infile); break;
case 4: items=fread(fbuffer.lng,4,BUFSIZE,infile); break;
}
if (debug)
fprintf(stderr,
"Read binary, items=%ld, allitems=%ld\n",items, allitems);
fileerr("Input file read",infile);
if (feof(infile))
working = FALSE;
fbptr.chr=fbuffer.chr;
for (i=0; i<items; i++) {
if (reverse)
reverse_bytes(fbptr.chr,word);
if (lines)
fprintf(outfile,"%lu%s",allitems,separator);
allitems++;
switch (word) {
case 1: nbuffer.sht = (wsigned) ? (char) *fbptr.chr++
:(unsigned char) *fbptr.chr++;
fprintf(outfile,asc_format,nbuffer.sht); break;
case 2: fprintf(outfile,asc_format,*fbptr.sht++); break;
72
case 4: fprintf(outfile,asc_format,*fbptr.lng++); break;
}
if (debug)
fprintf(stderr,"Pointer=%X\n",fbptr.chr);
} /* for i<BUFSIZE */
} /* bin to ascii */
} /* while (working) */
/* print number of lines processed */
if (print)
fprintf(stderr,"%s did process %lu lines.\n",argv[0],allitems);
/* close and free and clean up */
if (*infilename)
fclose(infile);
if (*outfilename)
fclose(outfile);
free (fbuffer.chr);
return EXIT_SUCCESS;
} /* main */
void reverse_bytes(char *word, char length)
/* change word order of word pointed to by word with length bytes */
{
char i, tmp;
for (i=0; i<length >> 1; i++) {
tmp = word[i];
word[i] = word[length-i-1];
word[length-i-1] = tmp;
} /* endfor */
}
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
73
/* get command line args */
8.1.6.
convbits.c
if (getarg_flag(argc,argv,'h') || getarg_flag(argc,argv,'?'))
usage(usage_txt,argv[0]);
/*
Converter 16 to 32 bits and vice versa
Robert Henke
1.7.92
Changed:
29.8.92
noise=getarg_flag(argc, argv,'n');
/* noise flag set ? */
if (getarg_name(argc, argv, 0, infilename))/* get input file */
infile=openfile(infilename,"rb");
else usage(usage_txt, argv[0]);
Conversion of
16 bits linear --> 32 bits linear
32 bits linear --> 16 bits linear
/* read input header */
fread(&snd1,sizeof(SNDSoundStruct),1,infile);
fileerr("Header could not be read",infile);
Option:
Addition of noise (16 --> 32)
Dithering
(32 --> 16) (1/2 LSB = 16 bit noise)
To do:
Integration of better rand-function from Numerical recipes in C
*/
#include
#include
#include
#include
"sndutil.h"
"cliutil.h"
"miscutil.h"
"defhenke.h"
#ifdef DOS
long int jrand48(unsigned short int xsubi[3])
/* Fake HP-random function */
{
return ((unsigned long) rand());
}
#endif
int main(int argc, char *argv[], char *envp[])
{
FILE *infile=NULL, *outfile=NULL;
char infilename[FNLEN], outfilename[FNLEN]; /* space for filenames */
SNDSoundStruct snd1, snd2;
/* Header of snd-data */
long int jrand48(unsigned short int xsubi[3]); /* white noise */
unsigned long i, clips=0;
long act_value_l;
short act_value_s;
double act_value_d;
char stol=0,
noise=0;
int no_samples;
/*
/*
/*
/*
/*
/*
actual value during con- */
-version */
for testing for overflow */
conversion short to long */
add noise / dither */
number of samples */
unsigned short random1[3] = {22,06,92},/* two independent random */
random2[3] = {31,10,64};/* number generators */
const char *usage_txt[] = {
"Usage: %s [-n] inputfile [outputfile]\n",
"\tConversion from 16 to 32 bits vice versa.\n",
"\t-n\tadds 16 bits noise to 32 bit data for testing or\n",
"\t\tdithers (32 bits --> 16 bits) to improve acoustics.\n",
"\tYou may use 'stdin' and 'stdout' as filenames\n",
VERSION_STRING,
""
};
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
if (sndinfo(snd1,infilename,FALSE))
exit(EXIT_FAILURE);
/* test for valid .snd file */
switch (snd1.dataFormat)
{
case SND_FORMAT_LINEAR_16:
stol = 1; break;
case SND_FORMAT_LINEAR_32:
stol = 0; break;
default:
nuerr("Unknown snd format (not 16/32 bits lin.), ID",
snd1.dataFormat);
} /* switch snd-format */
if (!getarg_name(argc, argv, 1, outfilename)) {
strcpy (outfilename,infilename);
i = strstr(outfilename, ".snd")-outfilename;
if (i>0)
outfilename[i]= (char) 0;
if (noise)
strcat(outfilename,"n");
if (stol)
strcat(outfilename,"32.snd");
else
strcat(outfilename,"16.snd");
}
outfile=openfile(outfilename,"wb");
/* after input of last parameter: */
getarg_illegals(argc, argv, usage_txt);
/* Calculate new data Size */
no_samples = (stol) ? snd1.dataSize/2 : snd1.dataSize/4;
/* change header of outfile */
memcpy(&snd2,&snd1,sizeof(snd1));
if (stol) {
snd2.dataFormat=SND_FORMAT_LINEAR_32;
snd2.dataSize = snd1.dataSize *2;
}
else {
snd2.dataFormat=SND_FORMAT_LINEAR_16;
snd2.dataSize = snd1.dataSize /2;
}
/* position to data, set to default length */
fseek(infile, snd1.dataLocation,0);
snd2.dataLocation=sizeof(snd2);
74
sndinfo(snd2,outfilename,FALSE);
/* write header */
fwrite(&snd2,sizeof(snd2),1,outfile);
fileerr("Header write",outfile);
if (noise) {
if (stol)
printf("Adding 16 bit noise to 32 bit output.\n");
else
printf("Dithering during 32 to 16 bit conversion.\n");
} /* if noise */
if (stol) { /* expand data */
for (i=0; i<no_samples; i++) {
fread(&act_value_s,sizeof(short),1,infile);
act_value_l = act_value_s << 16; /* 16 to 32 bit */
if (noise) { /* 16 bit noise triangle dist.*/
act_value_l |= ((jrand48(random1)+jrand48(random2)) >> 16 \
& 0xffff); /* upper 16 bits have better distr. [rand()] */
}
fwrite(&act_value_l,sizeof(long),1,outfile);
}
} /* if stol */
else { /* shorten data */
/* implement dithering */
for (i=0; i<no_samples; i++) {
fread(&act_value_l,sizeof(long),1,infile);
act_value_d = act_value_l;
if (noise) /* 16 bit noise */
act_value_d += (double) ((jrand48(random1)+\
jrand48(random2)) >> (32-16));
/* Clipping */
if (fabs(act_value_d) > (double) 0x7fffffff){
clips++;
if (act_value_d > 0)
act_value_s = 0x7fff;
else act_value_s = 0x8001;
}
else
act_value_s = (long) (act_value_d) >> 16; /* For fwrite */
fwrite(&act_value_s,sizeof(short),1,outfile);
}
} /* else */
if (clips)
printf("Clippings: %u, that's %3.2f%%.\n",clips,
clips*100.0/no_samples);
/* be tidy */
fclose(outfile);
fileerr("Couldn't close outputfile.\n",outfile);
fclose(infile);
fileerr("Couldn't close inputfile.\n",infile);
return EXIT_SUCCESS;
/* end without error */
} /* main */
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
75
8.1.7.
defhenke.h
/*
These are the defaults for me,
Robert Henke ;-)
Started: 24. 9.92
Changed: 19.10.92
*/
#ifndef DEF_HENKE_FLAG
#define DEF_HENKE_FLAG
/* I like to see what version is running, this 'VERSION_STRING' gets included
before the last line of 'usage_text' of function 'usage'
*/
#define VERSION_STRING \
"====
%s by R. Henke, compiled at " __DATE__ ", " __TIME__ "
#ifndef FALSE
#define TRUE
#define FALSE
#endif
====\n"
1
!TRUE
#ifndef MAX_LONG
#define MAX_LONG
#endif
0x7fffffff
#ifndef M_PI
#define M_PI
#endif
3.14159265358979323846
/* Limit for 32 bit integer */
#ifndef FNLEN
#define FNLEN 255
#endif
#ifndef ULONG
#define ULONG unsigned long
#define ULONG_BITS (sizeof(ULONG) << 3)
#endif
/* how many bits has ULONG ? */
#ifndef ABS
#define ABS(x) ((( x ) <0) ? -( x ) : ( x ))
#endif
#ifndef MAX
#define MAX(x,y) ((( x ) > ( y )) ? ( x ) : ( y ) )
#endif
#define OUT2ERR(x) fprintf(stderr,x); fflush(stderr);
#endif /* DEF_HENKE_FLAG */
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
76
8.1.8.
fft.c
/**************************************************************************
*
Universitaet Erlangen - Nuernberg
*
*
Lehrstuhl fuer technische Elektronik
*
*
Cauerstrasse 9
*
*
8520 Erlangen
*
*
*
*
Programmed in Fortran by Karlheinz Brandenburg
*
*
Converted to C by Max Miegler
*
*
Minor changes by Robert Henke
*
*
*
*
Module description:
*
*
*
*
- rbtrv
Bit reversal
*
*
- fftr
Fast Fourier Transformation
*
*
- rkof
Calculation of complex factors for FFT
*
*
- spcr
Umrechnung ,falls reelle Zeitbereichsabtastwerte
*
*
dicht gespeichert sind.
*
*
- fctr
Discrete Cosinus Transformation
*
*
- idct
Inverse Discrete Cosinus Transformation
*
*
- mdct
Modified Cosinus Transformation
*
*
- imdct
Inverse Modified Cosinus Transformation
*
**************************************************************************/
/**************************************************************************
* rbtrv sorts values in "bit reversed" order
*
* INPUT:
*
*
- x
:
array of complex values
*
*
- mc :
number of complex values (block length)
*
**************************************************************************/
int rbtrv(double *x,int mc)
{
int i;
/* loop count */
int j;
int k;
int i1;
int jx;
int mm1;
/* block length - 1 */
int mv2;
/* block length / 2 */
double vr,vi;
/* bit reversal algorithm with inplace sorting taken from Langenbucher */
mv2 = mc/2;
mm1 = mc-1;
j=1;
for(i = 1;i <= mm1;i++)
{
if(i < j)
{
i1 = 2*i - 1;
jx = 2*j - 1;
#include <stdio.h>
#include <math.h>
/* exchange */
vr = x[jx-1];
vi = x[jx];
x[jx-1] = x[i1-1];
x[jx] = x[i1];
x[i1-1] = vr;
x[i1] = vi;
#ifdef DOS
#include "soundstr.h"
#else
#include "soundstruct.h"
#endif
}
k = mv2;
while(k < j)
{
j = j - k;
k = k / 2;
}
j = j+k;
#define SNDPATH ""
#define max(x,y) (((x) > (y)) ? (x) : (y))
#define min(x,y) (((x) < (y)) ? (x) : (y))
#
define M_PI
3.14159265358979323846
/* Complex factors needed by FFT */
struct koeffizienten
{
int nn;
/* number of coefficients */
double koef4[4104];
/* array of complex coefficients */
} koef;
/* prototypes */
int fftr(double *x,int iexc,int m,int itran);
int rbtrv(double *x,int mc);
int rkof(int n);
int fctr(double *x,int iex,int n,double *y);
int idct(double *x,int iex,int n,double *y);
int fftr(double *x,int iexc,int m,int itran);
int trfr(double *xe,int iex,int n,int itran);
int mdct(double *xr,int iex,int n,double *yr);
int imdct(double *xr,int iex,int n,double *yr);
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
}
return(0);
}
/**************************************************************************
* rkof calcuates the complex factors for FFT and stores them into a global*
* structure called 'koef'. Dont't forget to call rkof before using the
*
* the FFT !
*
* INPUT :
*
*
- n number of complex values to be calculated
*
**************************************************************************/
int rkof(int n)
{
double di,hc;
int i;
koef.nn = n*4;
77
hc = 2*M_PI/koef.nn;
for(i = 0;i < 4*(n+2);i+=2)
{
di = i/2;
koef.koef4[i] = cos(di*hc);
koef.koef4[i+1] = -sin(di*hc);
}
return(0);
for (i = 2; i <= l;i++)
{
k = lh - i;
ir = 2*i - 1;
kr = 2*k - 1;
kindex = (ir - 1) * indfak + 1;
}
zzr = koef.koef4[kindex - 1];
zzi = koef.koef4[kindex];
int spcr(double *z,int mc,int itran)
{
int ir,
kr,
i,
indfak,
kindex,
k,
l,
l1,
lh,
lr1;
double gr1,
gr2,
tran,
wi,
zzr,
zzi,
gi1,
gi2,
zui,
zur,
zgi,
zgr,
zhi,
zhr;
l = mc;
l1 = mc + 1;
lr1 = 2*l1 - 1;
tran = itran;
indfak = koef.nn / (2*mc);
gr1 = z[0];
if (itran == 1)
{
gr2 = z[1];
z[0] = gr1 + gr2;
z[1] = 0.;
z[lr1-1] = gr1 - gr2;
z[lr1] = 0.;
}
else
{
gr2 = z[lr1-1];
z[0] = (gr1 + gr2) * .5;
z[1] = (gr1 - gr2) * .5;
}
lh = l + 2;
l = l / 2 + 1;
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
zhr
zhi
zur
zui
=
=
=
=
z[ir-1];
z[ir];
z[kr-1];
z[kr];
zgr
zgi
zur
zui
=
=
=
=
zhr
zhi
zhr
zhi
+
+
-
zur;
zui;
zur;
zui;
wi = zzi * zur + tran * zzr * zgi;
gr2 = zgr - wi;
gr1 = zgr + wi;
gi1 = zzi * zgi - tran * zzr * zur;
gi2 = gi1 - zui;
gi1 += zui;
z[ir-1]
z[ir] =
z[kr-1]
z[kr] =
= gr1
gi1 *
= gr2
gi2 *
* .5;
.5;
* .5;
.5;
}
return(0);
}
int fctr(double *x,int iex,int n,double *y)
{
int iexc, iksw;
int i, m1, mc, ix, iy;
double wri, wrr;
iksw = koef.nn / (4*n);
m1 = n - 1;
for (i = 1; i <= m1; i += 2)
{
iy = i / 2 + 1;
y[iy-1] = x[i-1];
ix = i+1 ;
iy = n - i / 2;
y[iy-1] = x[ix-1];
}
mc = n / 2;
iexc = iex - 1;
fftr(y,iexc,mc,1);
78
spcr(y,mc,1);
for (i = 1; i <= n+1; i += 2)
{
wrr = koef.koef4[(i - 1) * iksw];
wri = koef.koef4[(i - 1) * iksw + 1];
ix = i / 2 + 1;
x[ix-1] = y[i-1] * wrr - y[i] * wri;
ix = n - i / 2 + 1;
x[ix-1] = -y[i] * wrr - y[i-1] * wri;
}
return(0);
} /* fctr */
int idct(double *x,int iex,int n,double *y)
{
int i, i1, i2;
double wri, wrr;
x[n] = 0.;
for (i = 1; i <= n+1; i += 2)
{
wrr = koef.koef4[i - 1];
wri = koef.koef4[i];
i1 = i / 2 + 1;
i2 = n - i / 2 + 1;
y[i-1] = x[i1-1] * wrr - x[i2-1] * wri;
y[i] = -x[i2-1] * wrr - x[i1-1] * wri;
}
spcr(y,n/2,-1);
fftr(y,iex-1,n/2,-1);
for (i =
{
i2 = i
x[i-1]
i2 = n
x[i] =
1; i <= n-1; i += 2)
/ 2 + 1;
= y[i2-1];
- i / 2;
y[i2-1];
*
itran: 1 = FFT , -1 = IFFT
*
* OUTPUT :
*
*
x
: Calculated spectral values
*
***************************************************************************/
int fftr(double *x,int iexc,int m,int itran)
{
int le3;
/* step width in outer loop */
int iksw;
/* index factor for the coefficients array */
int ik4;
/* coefficients index of w^m/4 */
int ik;
/* actual coeff. index */
int l;
/* step counter (outer loop) */
int le1;
/* difference between the input addresses of the */
/* butterflies with equal coefficients */
int iq;
/* loop variable */
int ir;
/* loop variable inner loop */
int iqx;
/* address actual real part */
int ipx;
/* address actual imaginary part */
int i2,i3,i4;
double
double
double
double
double
double
double
double
double
xpi;
xpr;
tran;
vr;
vi;
wrr;
wri;
xqi;
xqr;
tran = itran;
/*printf("tran = %f\n",tran);*/
/* step rate in algorithm */
le3 = m;
/* step rate in coeff. array */
iksw = koef.nn/m;
/* index coeff. */
ik4 = koef.nn/2+1;
for(l = 1;l <= iexc;l++)
{
le1 = le3;
le3 = le3 / 2;
iksw = iksw * 2;
i2 = m - le3;
for(iq = 1;le1 < 0 ? iq >= i2 : iq <= i2 ; iq += le1)
{
iqx = 2*iq - 1;
ipx = 2*(iq+le3) - 1;
}
return 0;
} /* idct */
/***************************************************************************
* fftr executes a FFT on the array x. The algorithm is taken from
*
* Langenbucher and Schramm.
*
* The result values are in bit reversed order and are sorted by rbtrv.
*
* INPUT :
*
*
x
: Array of complex values in order of re,im,re,im ...
*
*
Block length of x must be a power of 2
*
*
iexc : ld of block length
*
*
m
: block length
*
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
xpr
xpi
xqr
xqi
=
=
=
=
x[ipx-1];
x[ipx];
x[iqx-1];
x[iqx];
x[iqx-1] = xqr + xpr;
x[iqx] = xqi + xpi;
x[ipx-1] = xqr - xpr;
x[ipx] = xqi - xpi;
}
if(le3 > 1)
{
ik = 1 + iksw;
79
static double x[1026];
static double y[1026];
for(ir = 2;ir <= le3;ir++)
{
if(ik == ik4)
{
i3 = m-le3;
for(iq = ir;le1 < 0 ? iq >= i3 : iq <= i3;iq +=le1)
{
iqx = 2*iq-1;
ipx = 2*(iq+le3)-1;
xpr = x[ipx-1];
xpi = x[ipx];
xqr = x[iqx-1];
xqi = x[iqx];
*/
*/
if(initflag)
{
rkof(n);
kon1 = 1.0/(1024.0 * (n/2));
initflag = 0;
}
if(itran > 0)
{
for(i = 0;i < n;i++)
{
x[i] = xe[i]/32767;
}
if(itran == 2)
{
/*printf("DCT aufrufen\n");*/
fctr(x,iex,n,y);
x[0] /= sqrt(2.0);
}
else
{
/*printf("DFT aufrufen\n");*/
iexc = iex - 1;
nc = n/2;
fftr(x,iexc,nc,1);
spcr(x,nc,1);
x[0] = x[0] * sqrt(2.0);
x[1] = x[n]/sqrt(2.0);
}
x[iqx-1] = xqr + xpr;
x[iqx] = xqi + xpi;
x[ipx-1] = tran * (xqi - xpi);
x[ipx] = tran * (-xqr + xpr);
}
}
else
{
wrr = koef.koef4[ik-1];
wri = koef.koef4[ik];
/*printf("ik = %d\n",ik);
printf("wr = %5.2f+j%5.2f\n",wrr,wri);*/
i4 = m-le3;
for(iq = ir;le1 < 0 ? iq >= i4 : iq <= i4;iq +=le1)
{
iqx = 2*iq-1;
ipx = 2*(iq+le3)-1;
xpr = x[ipx-1];
xpi = x[ipx];
xqr = x[iqx-1];
xqi = x[iqx];
for(i = 0;i < n;i++)
{
xe[i] = x[i]*1024;
}
}
else
{
for(i = 0;i < n;i++)
{
x[i] = xe[i]*kon1;
}
/*printf("Inverse Transformation , kon1 = %f\n",kon1);*/
if(itran == -2)
{
/*printf("IDCT aufrufen\n");*/
x[0] = x[0] * sqrt(2.0);
idct(x,iex,n,y);
vr = xqr - xpr;
vi = xqi - xpi;
x[iqx-1] = xqr + xpr;
x[iqx] = xqi + xpi;
x[ipx-1] = vr * wrr - tran * vi * wri;
x[ipx] = vi * wrr + tran * vr * wri;
}
}
ik = ik + iksw;
}
}
}
rbtrv(x,m);
return(0);
for(i = 0;i < n;i++)
{
xe[i] = x[i]*32767;
}
}
int trfr(double *xe,int iex,int n,int itran)
{
static int initflag=1; /* Initialisierung beim ersten Programmdurchlauf */
int iexc,
/* Stufenzah FFT bei real-Berechnung
*/
nc,
/* Zahl komplexe Koeffizienten bei real-Berech. */
i;
static double kon1 ;
/* internes Datenfeld
/* Datenfeld fuer FCT-Algorithmus
/* Normierungskonstante
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
*/
}
else
{
/*printf("IDFT aufrufen\n");*/
iexc = iex - 1;
nc = n/2;
x[0] = x[0] * sqrt(2.0);
x[n] = x[1] * sqrt(2.0);
x[1] = 0.0;
80
x[n+1] = 0.0;
spcr(x,nc,-1);
fftr(x,iexc,nc,-1);
for(i = 0;i < n;i++)
{
xe[i] = x[i]*32767.0;
}
}
}
return(0);
}
/**********************************************************************
*
*
Programmbeschreibung:
*
*
mdct ist der Teil Hintransformation der TDAC oder MDCT
*
(Time Domain Aliasing Cancellation)
*
*
hin verwendet als schnelle Transformation die Ruecktransformation
*
der DCT und speichert die Werte passend um
*
*
***********************************************************************/
int mdct(double *xr,int iex,int n,double *yr)
{
/*uebergebene Variable:
double
xr(1),
yr(1)
int
n,
iex,
*
*
Programmbeschreibung:
*
*
imdct ist der Teil Ruecktransformation der TDAC
*
(Time Domain Aliasing Cancellation)
*
*
imdct verwendet als schnelle Transformation die DCT und speichert
*
die Werte um, so dass als Ergebnis eine TDAC berechnet wird.
*
*
***********************************************************************/
int imdct(double *xr,int iex,int n,double *yr)
{
/*
uebergebene Variable:
double
int
double flnh;
int i;
!Transformationslaenge
!ld der Transformationslaenge
/* =float(N/2), Normierungskonstante */
/*allgemeine Schleifenvariable*/
/*********************************************************************
Hauptteil:
*/
/*
obere Haelfte der Spektralwerte ergaenzen:
Umspeichern der Eingangsdaten
*/
Datenfeld
zweites Datenfeld, Zwischenspeicher
flnh = n/2;
for(i = 1;i <= n/2;i++)
{
yr[i-1] = xr[i-1];
yr[n-i] = - yr[i-1];
}
Transformationslaenge
ld der Transformationslaenge
/*allgemeine Schleifenvariable*/
int dnviertel,
nviertel;
n,
iex,
!Datenfeld
!zweites Datenfeld zum Zwischenspeichern
*/
*/
int i;
xr[],
yr[],
/* Konstante 3*N/4*/
/*Konstante N/4*/
/*
eigentliche Ruecktransformation:
zunaechst 'klassische' DCT:
*/
nviertel = n/4;
dnviertel = 3*n/4;
fctr(yr, iex, n, xr);
for (i = 0;i < dnviertel;i++)
yr[i+nviertel] = xr[i];
/*
for (i = dnviertel;i < n;i++)
yr[i-dnviertel] = - xr[i];
for(i = 1;i <= n/4;i++)
xr[i-1] = (yr[(2*i + n/2) - 1]) / flnh;
/*
/*
eigentliche Transformation:
DCT ('klassisch') aufrufen:
*/
for(i = n/4+1;i <= 3*n/4;i++)
xr[i-1] = (- yr[((3*n/2)+1) - 2*i]) / flnh;
*/
*/
for(i = 3*n/4+1;i <= n;i++)
xr[i-1] = (- yr[(2*i - 3*n/2) -1]) / flnh;
fctr(yr, iex, n, xr);
/*
Umspeichern:
for(i = 1;i <= n/2;i++)
xr[i-1] = yr[2*i-1];
return(0);
vorzeichenrichtiges Umspeichern der Daten in 3 Teilen
*/
return(0);
}
}
/**********************************************************************
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
81
8.1.9.
huffgen.c
/*********************************************************************/
/*Generates huffman codes from input table
*/
/*Started:
1. 9.92
*/
/*Changed:
20.11.92
*/
/*
*/
/*Input: 32 bit unsigned absolute frequency values
*/
/*Output: Huffman tree: 32 bit per code, all codes, then
*/
/*
8 bit per code: codelengths
*/
/*Bugs:
*/
/*To Do: removal of superfluous code
*/
/*********************************************************************/
#define _HPUX_SOURCE
#include
#include
#include
#include
#include
<sys/types.h>
<sys/stat.h>
"hufflib.h"
"cliutil.h"
"miscutil.h"
int main(int argc, char *argv[], char *envp[])
{
const char *usage_txt[] = {
"usage: %s infile [-c [file]][-l [file]][-t [file]][-v]\n",
"Generation of a huffman coding table, code and code length files\n",
"infile\t32 bit unsigned integers with absolute frequencies\n",
"-b size\ttable size: default is size of data field\n",
"-c file\tcodes into file (if filename is omitted: *.hcc)\n",
"\t32 bit unsigned long * number of codes\n",
"-l file\tcode lengths into file (if filename is omitted: *.hcl)\n",
"\t8 bit * number of codes\n",
"-s\tstacked huffman codes in one table [-b size as table size]\n",
"-S\tstacked huffman codes in two tables [-b size as table size]\n",
"-t file\ttree into file (if filename is omitted: *.hct)\n",
"\t[left * number],[right * number] as HTYPE (32/16 bit unsigned)\n",
"-v\tverbose mode\n",
"-z\tdo not: replace 0s by 1 and any other value by 2*value\n",
VERSION_STRING,
""
};
char infilename[FNLEN]="",
/* input file, mandatory */
ccfilename[FNLEN]="",
/* only codes */
clfilename[FNLEN]="",
/* codelengths */
ctfilename[FNLEN]="";
/* code table */
FILE *infile=NULL,
*ccfile=NULL,
*clfile=NULL,
*ctfile=NULL;
struct stat file_statistics;
/* buffer for file statistics */
long length;
/* file length */
HTYPE **huffman;
/* pointer to huffman table allocated elsewhere*/
unsigned long *hc_codes,
/* codes for values
"
" */
*indata;
/* input data */
char *hc_bits,
/* bit lengths of values
"
" */
verbose=FALSE;
/* print (more) info */
int tables=1;
/* number of tables */
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
long tablesize=0;
char algorithm[FNLEN]="d",
no_zero,
stacked,
debug;
/* if user gives a number, it will be != 0
/* select table algorithm
/* avoid 0s in input
/* generate table for stacked codes
/* print debug info
*/
*/
*/
*/
*/
/* help info */
if (getarg_flag(argc,argv,'h') || getarg_flag(argc,argv,'?'))
usage(usage_txt,argv[0]);
/* check for flags and options */
verbose=getarg_flag(argc,argv,'v');
debug=getarg_flag(argc,argv,'d');
verbose |= debug;
no_zero=!getarg_flag(argc,argv,'z');
/* debug IS verbose */
stacked=getarg_flag(argc,argv,'s');
if (!stacked) {
/* none or two tables for stacked codes ? */
stacked=getarg_flag(argc,argv,'S');
/* two tables */
if (stacked)
tables=2;
}
getarg_long_option(argc,argv,'b',&tablesize);
/* default remain if no
valid option is given */
/* getarg_int_option(argc,argv,'p',&tables);
getarg_option(argc,argv,'a',algorithm);
*/
/* input file */
if (!getarg_name(argc,argv,0,infilename))
usage(usage_txt,argv[0]);
infile=opfile(infilename,"rb","(Frequency table)");
/* what output files ? */
if (getarg_option(argc,argv,'c',ccfilename)<0) {
strcpy(ccfilename,infilename);
remove_extension(ccfilename,".");
add_extension(ccfilename,".hcc");
}
if (getarg_option(argc,argv,'l',clfilename)<0) {
strcpy(clfilename,infilename);
remove_extension(clfilename,".");
add_extension(clfilename,".hcl");
}
if (getarg_option(argc,argv,'t',ctfilename)<0) {
strcpy(ctfilename,infilename);
remove_extension(ctfilename,".");
add_extension(ctfilename,".hct");
}
/* change extention */
/* huffman code table */
/* change extention */
/* huffman code lengths */
/* change extention */
/* huffman code table */
if (debug)
printf("DEBUG> Flags:\tdebug, %szero, %sstacked\n"
"DEBUG> Files:\tin:'%s', code:'%s', len:'%s', tab:'%s'\n",
no_zero ? "no_" : "", stacked ? "" : "not ",
infilename,ccfilename,clfilename,ctfilename);
/* check for unknown command line options */
getarg_illegals(argc,argv,usage_txt);
/* open files _IF_ they are required */
82
}
if (*ccfilename)
ccfile=opfile(ccfilename,"wb","(Huffman codes)");
if (verbose) {
printf("calculating\n");
fflush(stdout);
}
if (*clfilename)
clfile=opfile(clfilename,"wb","(Huffman code lengths)");
if (*ctfilename)
ctfile=opfile(ctfilename,"wb","(Huffman code table)");
/* calculate huffman codes */
huffman=huffgen(indata, tablesize, &hc_bits, &hc_codes);
/* calculate memory for tables */
stat(infilename, &file_statistics);
length = file_statistics.st_size;
/* get filesize in bytes */
fseek(infile, 0, SEEK_SET);
/* begin of file */
if (length % sizeof(unsigned long))
/* data must be unsigned long */
errormsg("Data file is not a multiple of 32 bit words");
length /= sizeof(unsigned long);
/* output tables */
if (ccfile)
if (tablesize !=
fwrite(hc_codes, sizeof(unsigned long), tablesize, ccfile))
errormsg("Could not write all the codes");
if (clfile)
if (tablesize != fwrite(hc_bits, sizeof(char), tablesize, clfile))
errormsg("Could not write all the code lengths");
if (ctfile) {
if (tablesize !=
fwrite(huffman[left], sizeof(HTYPE), tablesize, ctfile))
errormsg("Could not write the huffman table (left)");
if (tablesize !=
fwrite(huffman[right], sizeof(HTYPE), tablesize, ctfile))
errormsg("Could not write the huffman table (right)");
}
if (tablesize > length)
fprintf(stderr,"ERROR: Tablesize bigger than number of input values\n");
/* allocate for input and for stacking, if necessary */
indata=mycalloc(tables*length, sizeof(unsigned long));
if (!tablesize)
/* default table size is same as no. of input words
*/
tablesize=length;
if (!stacked || (stacked && (tables==2)))
printf("Mean code length for this data: %.6f\n",
mean_code_length(indata, tablesize, hc_bits,stacked,TRUE));
switch (algorithm[0]) {
case 'd':
case 'l':
break;
default:
errormsg(strcat("Wrong table type: ",algorithm));
} /* switch */
/* finish */
free_tab2d(huffman, 2); /* this table space was allocated in func. huffgen
*/
if (verbose) {
printf("Processing %ld codes in ",length);
printf("%d table%s, type %c,\nsize per table = %ld %s\n",
tables, (tables==1) ? "" : "s", algorithm[0], tablesize,
stacked ? "stacked huffman codes" : "");
printf("Reading, ");
fflush(stdout);
}
/* read whole table into memory */
if (length != fread(indata, sizeof(unsigned long), length, infile))
errormsg("Could not read all the data");
free (hc_bits);
free (hc_codes);
fclose(infile);
if (ccfile)
fclose(ccfile);
if (clfile)
fclose(clfile);
if (ctfile)
fclose(ctfile);
return EXIT_SUCCESS;
} /* huffgen main */
if (stacked) {
if (verbose) {
printf("stacking, ");
fflush(stdout);
}
make_stacked(indata, length, tablesize, tables);
tablesize = tables * tablesize;
}
/* adjust entries, so that there is no 0 */
if (no_zero) {
if (verbose) {
printf("de-zero-fying, ");
fflush(stdout);
}
no_zeros(indata, tablesize);
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
83
2.
8.1.10.
hufflib.c
/*
Huffman coding functions
The following functions were partly modelled after Hufflib2.for of
Thomas Sporer, 880502.1039:
huffgen, insert_sorted, heapsort
Robert Henke
Changed:
21. 8.92
20.11.92
BUGS:
to do:
mean_code_length returns nonsense
*/
#include
#include
#include
#include
#include
#include
#define
#define
#define
#define
<stdio.h>
<string.h>
<stdlib.h>
<math.h>
"hufflib.h"
"miscutil.h"
DEBLTAB FALSE
DEBUG FALSE
DEBIS FALSE
DEBENC FALSE
#define CODEWORDS 40 /* temporary storage size for encoded data (stacked hc) */
HTYPE **huffgen(ULONG *abs_counts, ULONG no_ac, char **bit_p_value,
ULONG **codes)
/* allocates memory for huffman tree, codes table an bit counts,
calculates these tables
Remember to free the memory !
Initialization:
1.
The input frequencies of the values expressed by the index of the
frequencies in abs_counts are entered into the right leafs of the
tree. The first position is reseved for the root. ntree points to
the last leaf added. Leafes have a right branch of 0, nodes have
both left and right branches
2.
hc_list contains the frequencies of abs_counts and their index
numbers. nlist is the lenght of the list.
Construction of the Huffman tree
1.
Sorting of hc_list
2.
hc_tree gets a new node:
2.1
The last two elements of hc_list are entered as its successors
2.2
The absolute count (frequency) of the node is the sum of its
successors. Its number is the next free number in hc_tree, and
ntree is incremented.
2.3
The new node replaces the two last entries in hc_list, and nlist
is decremented accordingly.
3.
Steps 1 and 2 are repeated until nlist=0.
Generation of the Huffman code
1.
Starting from the root all codes and code lengths are calculated
iteratively. This is done in one pass.
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
The root is placed in position 0, ntree is decremented.
abs_counts:
bit_p_value:
codes:
hc-tree:
unsorted list of absolute frequencies of data, number = no_ac
valid bits for each code in codes
codes with max. 32 bits
hc_tree[left]: left successor or 0 for leafs
hc_tree[right]: right successor or value for leafs
root in hc_tree[...][0]
*/
{
HTYPE i,
/* loop counters */
nlist,
/* number of valid list entries */
ntree,
/* number of tree members */
root,
/* number of root node */
**hc_tree,
/* storage place for huffman code tree */
*hc_t_left,
/* left branch */
*hc_t_right,
/* right branch */
*hc_list_number;
/* number = value to be coded (should be
HTYPE) */
ULONG *hc_list_freq;
/* absolute frequency */
long index;
char maxbits = sizeof(HTYPE) * 8; /* depending on HTYPE bits per code */
/* These data structures are returned to caller - please free them there */
hc_tree = (HTYPE **) mymalloc(2,sizeof(HTYPE *));
hc_t_left = (HTYPE *) mymalloc(3*(no_ac+1),sizeof(HTYPE));
hc_t_right = (HTYPE *) mymalloc(3*(no_ac+1),sizeof(HTYPE));
hc_tree[left]=hc_t_left;
hc_tree[right]=hc_t_right;
*bit_p_value = (char *) mymalloc(no_ac,sizeof(char));
*codes = (ULONG *) mymalloc(no_ac,sizeof(ULONG));
/* local list */
hc_list_number = (HTYPE *) mymalloc(2*(no_ac+1),sizeof(HTYPE));
hc_list_freq = (ULONG *) mymalloc(2*(no_ac+1),sizeof(ULONG));
/* fill list with input data, fill tree and mark each entry as leaf,
reserve space for root
*/
/* Adjust pointers to range 1...n like in FORTRAN code */
hc_tree[left]--; hc_tree[right]--; hc_list_number--; hc_list_freq--;
#if DEBUG
parr(abs_counts,no_ac+1,"Abs Counts");
#endif
for (i=2; i<=no_ac+1; i++) {
hc_tree[left][i]
= 0;
/* add leafs to tree */
hc_tree[right][i]
= i-1;
hc_list_number[i-1] = i;
/* create list: numbers und frequencies */
hc_list_freq[i-1]
= *abs_counts++;
}
nlist=no_ac;
ntree=no_ac+1;
/* used entries in list */
/* last entry in tree */
#if DEBUG && 0
parr(hc_list_freq+1, no_ac+1, "Vor heap: freq");
parr(hc_list_number+1, no_ac+1, "Vor heap: num");
#endif
heapsort(hc_list_number, hc_list_freq, no_ac);
#if DEBUG
parr(hc_list_freq+1, no_ac+1, "Nach heap: freq");
parr(hc_list_number+1, no_ac+1, "Nach heap: num");
#endif
84
/* build tree */
while (nlist > 1) {
insert_sorted(hc_list_number, hc_list_freq, nlist);
/* create new node */
ntree++;
hc_tree[left][ntree] = hc_list_number[nlist-1];
hc_tree[right][ntree] = hc_list_number[nlist];
nlist--;
hc_list_freq[nlist] += hc_list_freq[nlist+1];
hc_list_number[nlist] = ntree;
#if DEBUG
parr(hc_tree[left]+1, ntree, "Blt:");
parr(hc_tree[right]+1, ntree, "Brt:");
parr(hc_list_freq+1,nlist,"Blf:");
parr(hc_list_number+1,nlist,"Bln:");
#endif
} /* while nlist */
/* Generate Code */
root = hc_list_number[1];
/* code length of root is 0 */
hc_list_number[root] = hc_list_freq[root] = 0;
/* generate for each (!) node of tree code length and code */
/* hc_list_number contains code lengths */
/* hc_list_freq contains the codes */
for (i=ntree; i>1; i--) {
/* hc_list_number[i] is 0 for a leaf, else it is a node */
index = hc_tree[left][i];
#if DEBUG
printf("l ntree=%u, index=%d, hc_l_n[%d]=%d, hc_l_f[%d]=%d\n",ntree,index,
i,hc_list_number[i],i,hc_list_freq[i]);
#endif
if (index) {
hc_list_number[index] = hc_list_number[i]+1; /* one bit longer */
hc_list_freq[index] = hc_list_freq[i] << 1; /* add bit=0 */
index = hc_tree[right][i];
#if 0
printf("r ntree=%u, index=%d, hc_length[%d]=%d, hc_code[%d]=%x\n",ntree,index,
i,hc_list_number[i],i,hc_list_freq[i]);
#endif
hc_list_number[index] = hc_list_number[i]+1; /* one bit longer */
hc_list_freq[index] = (hc_list_freq[i] << 1) + 1;/* add bit=1 */
} /* if index */
} /* for i */
#if DEBUG
parr (hc_list_number+1,2*no_ac,"CL:");
parr (hc_list_freq+1,2*no_ac,"CC:");
#endif
/* copy into result arrays */
for (i=0; i<no_ac; i++) {
if (((*bit_p_value)[i]=(char) hc_list_number[i+2]) > maxbits)
fprintf(stderr,
"huffgen: length=%d, more than %d bits (max. wordlength)!\n",
(*bit_p_value)[i], maxbits);
(*codes)[i]=hc_list_freq[i+2];
} /* for i */
/* root as first element in tree */
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
hc_tree[left][1] = hc_tree[left][root];
hc_tree[right][1] = hc_tree[right][root];
free (++hc_list_freq);
free (++hc_list_number);
/* change pointers to state of allocation */
hc_tree[left]++; hc_tree[right]++;
#if DEBUG
parr(hc_tree[left], ntree, "HCl:");
parr(hc_tree[right], ntree, "HCr:");
#endif
return hc_tree;
}
void insert_sorted(HTYPE *nums, ULONG *freqs, HTYPE size)
/* nums, freqs gets new entry in position size sorted for 2nd array */
/* array range 1...size */
{
long i,j,k;
/* loop counter */
HTYPE t1;
/* values to be inserted */
ULONG t2;
#if DEBIS
parr(nums+1,size,"Ins_sort Nums");
parr(freqs+1,size,"Ins_sort Freqs");
#endif
t1=nums[size];
t2=freqs[size];
/* change from Sporer's algorithm: *fptr<t2 to *fptr<=t2 matters for
large numbers */
#if 0
/* disabled because too slow for large fields */
for (fptr=&freqs[size-1], i=size-1; i && (*fptr <= t2); i--, fptr-- );
/* avoid too "linear" trees */
/* if (fptr[1]==t2)
fptr++; */
#else
i = size >> 1;
/* start in mid of table */
if (freqs[i] < t2)
j = 1;
else
j = size-1;
while ((i-j) && (freqs[i] != t2)) {
k = i;
/* last value of i */
if (freqs[i] < t2)
i -= ABS((j-i) >> 1);
else
i += ABS((j-i) >> 1);
j = k;
} /* endwhile */
while (freqs[i] > t2)
/* size need not be power of 2 --> correct */
i++;
/* misplaced pointer */
while (i && (freqs[i] < t2))
i--;
#endif
memmove(&freqs[i+1],&freqs[i],sizeof(ULONG)*(size-i));
memmove(&nums[i+1],&nums[i],sizeof(HTYPE)*(size-i));
nums[i+1]=t1;
freqs[i+1]=t2;
85
#if DEBIS
parr(nums+1,size,"Ins_sort ende Nums");
parr(freqs+1,size,"Ins_sort ende Freqs");
#endif
} /* insert_sorted */
void heapsort(HTYPE *nums, ULONG *freqs, HTYPE size)
/* both arrays are sorted, second one is the key
changed to work with array beginning with 1 !
*/
{
HTYPE j,
/* loop counter */
left, right, /* pointers for heap building */
i;
ULONG tmp,
/* for swapping */
t1, t2;
/* temporary register */
char cont;
/* continue flag */
left = size/2 +1;
right = size;
while (right != 1) {
if (left > 1)
left--;
else
if (right > 1) {
/* swap */
tmp = nums[left];
nums[left] = nums[right];
nums[right] = tmp;
tmp = freqs[left];
freqs[left] = freqs[right];
freqs[right--] = tmp;
}
i = left;
j = i << 1;
t1 = nums[i];
t2 = freqs[i];
cont = j <= right;
while (cont) {
if (j < right)
if (freqs[j] > freqs[j+1]) j++;
if (j <= right)
cont = t2 > freqs[j];
else
cont = FALSE;
if (cont) {
nums[i]=nums[j];
freqs[i]=freqs[j];
i = j;
j = i << 1;
}
} /* while cont */
nums[i] = t1;
freqs[i] = t2;
} /* while right */
}
/* local global for quick 'n dirty calculation of stacked bits/sample */
static ULONG esc_sum = 0;
/* mean code length for data used by huffgen */
{
double sum=0.0,
number=0.0;
unsigned esc_bits;
ULONG i;
if (stacked) {
esc_bits = bit_p_value[(no_ac/2)-1];
#if DEBENC
printf("ESC-Bits: %lu, ESC-Zahl: %d\n", esc_bits, esc_sum);
#endif
for (i=0; i < no_ac; i++) {
number += (*abs_counts >> 1);
sum += (*abs_counts++ >> 1) * *bit_p_value++;
} /* for */
if (signbit)
sum += number;
#if DEBENC
printf("Grob-Nummer: %.0f, sum=%.0f\n",number,sum);
#endif
number -= esc_sum;
} /* if */
else {
for (i=0; i < no_ac; i++) {
number += *abs_counts;
sum += *abs_counts++ * *bit_p_value++;
} /* for */
if (signbit)
sum += no_ac;
} /* else */
#if DEBENC
printf("Summe: %.0f, Nummer: %.0f\n",sum,number);
#endif
return (sum / number);
}
/* ***************** debugging aid ***************** */
void parr(ULONG *arr, ULONG size, char *text)
{
ULONG i;
printf("----------------------------\n");
for (i=0;i<size;i++)
printf("%s[%lu]=%lu\n",text,i+1,arr[i]);
fflush(stdout);
}
void print_bits(ULONG code, int bits)
{
fflush(stderr);
fflush(stdout);
while (bits) {
printf("%c",(code & 1) ? '1' : '0');
code >>= 1;
bits--;
}
fflush(stdout);
}
double mean_code_length(ULONG *abs_counts, ULONG no_ac, char *bit_p_value,
char stacked, char signbit)
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
86
ULONG **create_tab2d(ULONG size, ULONG slots)
/* allocate memory for 2 dim ULONG table and clear it to 0, return
{
ULONG **tab;
ULONG i;
pointer */
tab = (ULONG **) mymalloc (slots, sizeof(ULONG *));
for (i=0; i<slots; i++) {
tab[i] = (ULONG *) mycalloc (size, sizeof(ULONG));
}
return tab;
} /* create_tab2d */
char **create_char_tab2d(ULONG size, ULONG slots)
/* allocate memory for 2 dim char table and clear it to 0, return
{
char **tab;
ULONG i;
pointer */
tab = (char **) mymalloc (slots, sizeof(char *));
for (i=0; i<slots; i++) {
tab[i] = (char *) mycalloc (size, sizeof(char));
}
return tab;
} /* create_char_tab2d */
void free_tab2d(ULONG **tab, ULONG slots)
/* deallocate memory for 2 dim table */
{
ULONG i;
for (i=0; i<slots; i++) {
free (tab[i]);
}
free (tab);
} /* free_tab2d */
void enter_tab2d(long *array, ULONG asize, ULONG **tab,
ULONG size, ULONG slots)
/* sort data from array into number 'slots' tables of size 'size'. Selection
of tables:
1st: max array value 0..1*size -1
2nd:
0..2*size -1
3rd:
0..3*size -1
...
slots-th:
0..(slots-1)*size -1
*/
{
ULONG avalue,
max,
j;
long *tmpptr,
i;
max= l_array_max(array,asize,0.0);
if ((max > size * slots)+1) {
fprintf(stderr, "max=%ld, size=%lu, slots=%u, s*s=%lu\n",max,size,slots,
size*slots);
/*
errormsg("Max arrayvalue to big to be processed"); */
}
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
for (i=1; i<=slots; i++) {
/* find table */
if ((max/i)<= size) {
for (j=0, tmpptr=array; j<asize; j++) {
avalue = ABS( *tmpptr/i );
(tab[i-1][avalue])++;
#if DEBLTAB
printf("tab[%ld][%ld]=%ld\n",i,avalue,tab[i-1][avalue]);
#endif
tmpptr++;
} /* for */
return;
} /* if */
} /* for */
} /* enter_tab2d */
void enter_ld_tab2d(long *array, ULONG asize, ULONG **tab, ULONG size,
ULONG slots)
/* sort data from array into number 'slots' tables of size 'size'. Selection
of tables:
1st: max array value 0..size -1
2nd:
0..2*size -1
3rd:
0..4*size -1
...
slots-th:
0..(2 to the (slots-1)-th power)*size -1
*/
{
ULONG max, avalue;
ULONG i;
ULONG j;
long *tmpptr;
max=(ULONG) l_array_max(array,asize,0);
if (!(size << (slots-1)))
errormsg("(size << (slots-1)) bigger than max_ULONG");
if (max > size << (slots-1))
errormsg("Max arrayvalue to big to be processed");
for (i=0; i<slots; i++) {
/* find table */
if (max >> i <= size-1) {
for (j=0, tmpptr=array; j<asize; j++) {
avalue = ABS(*tmpptr);
avalue >>= i;
(tab[i][avalue])++;
#if DEBLTAB
printf("tab[%ld][%ld]=%ld\n",i,avalue,tab[i][avalue]);
#endif
tmpptr++;
} /* for */
return;
} /* if */
} /* for */
} /* enter_tab2d */
void enter_stk_tab2d(long *array, ULONG asize, ULONG **tab, ULONG size)
/* sort data from array into two stacked tables of size 'size'. Selection
of tables:
1st: array value 0..size-1
2nd:
>= size, set tab[0][size-1] as ESC (recursively)
Upon return, the absolute frequency of values is stored in the tables
*/
{
87
*/
{
ULONG i;
for (i=0; i<asize; i++){
/* printf("av1=%lu\n",*array); */
if (*array >= size) {
(tab[0][size-1]) += *array / size;
(tab[1][*array % size])++;
}
else
(tab[0][*array])++;
array++;
} /* for i */
} /* enter_stk_tab2d */
int read_tab1d(FILE *fileptr, ULONG *tab, ULONG size)
/* read from binary data file 32 bit data into tab
returns:
0 read ok
-1 file too short or data not readable
*/
{
if (fread(tab,sizeof(ULONG),size,fileptr) != size)
return (-1);
return (0);
/* read ok */
} /* read_tab1d */
int read_char_tab1d(FILE *fileptr, char *tab, ULONG size)
/* read from binary data file 8 bit data into tab
returns:
0 read ok
-1 file too short or data not readable
*/
{
if (fread(tab,sizeof(char),size,fileptr) != size)
return (-1);
return (0);
/* read ok */
} /* read_char_tab1d */
int read_tab2d(FILE *fileptr, ULONG **tab, ULONG size, ULONG slots)
/* read from binary data file 32 bit data into tab
returns:
0 read ok
-1 file too short or data not readable
*/
{
ULONG j;
ULONG j;
for (j=0; j<slots; j++) {
if (fread(tab[j],sizeof(char),size,fileptr) != size)
return (-1);
} /* for j */
return (0);
} /* read_tab2d */
int write_tab1d(FILE *fileptr, ULONG *tab, ULONG size)
/* write to binary data file 32 bit data from tab
returns:
0 write ok
-1 file not writable or error during write
*/
{
if (fwrite(tab,sizeof(ULONG),size,fileptr) != size)
return (-1);
return (0);
/* read ok */
} /* write_tab2d */
int write_tab2d(FILE *fileptr, ULONG **tab, ULONG size, ULONG slots)
/* write to binary data file 32 bit data from tab
returns:
0 write ok
-1 file not writable or error during write
*/
{
ULONG j;
for (j=0; j<slots; j++) {
if (fwrite(tab[j],sizeof(ULONG),size,fileptr) != size)
return (-1);
} /* for j */
return (0);
} /* write_tab2d */
/* read ok */
void no_zeros(ULONG *array, ULONG asize)
/* changes 0s to 1, other values to double of the value */
{
ULONG i;
for (i=0; i<asize; i++) {
if (*array >> ((sizeof(ULONG)<<3)-1))
errormsg("no_zeros: abs. count too big to be doubled");
*array <<= 1;
if (!*array)
(*array)++;
array++;
}
for (j=0; j<slots; j++) {
if (fread(tab[j],sizeof(ULONG),size,fileptr) != size)
return (-1);
} /* for j */
return (0);
} /* read_tab2d */
/* read ok */
/* read ok */
}
/* Routines to generate and read / decode and read stacked huffman codes */
int read_char_tab2d(FILE *fileptr, char **tab, ULONG size, ULONG slots)
/* read from binary data file 8 bit data into tab
returns:
0 read ok
-1 file too short or data not readable
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
/* 'local globals', may be accessed only by functions in this file,
do not remove the 'static' keyword
*/
static ULONG code_buffer[CODEWORDS];
/* for building the code words */
88
static int valid_code_bits=0;
/* valid bits in stacked
huffman code */
static int add_hc_code(ULONG code, int bits)
/* enter one code in shift register code_buffer
'bits' has to be no moreC than ULONG_BITS
Return
n <> 0 successful: n bits length
n = 0 buffer length was exceeded
*/
{
ULONG carry_bits_new,
carry_bits_old=0;
int i;
if (CODEWORDS*ULONG_BITS < valid_code_bits+bits)
fprintf(stderr,"WARNING: add_hc_bits: too big a code, bits: %d\n",
valid_code_bits+bits);
#if DEBENC
printf("\n\tadd_hc_bits:"); print_bits(code,bits);printf("\n");
#endif
carry_bits_old = code & ((1<<bits)-1); /* mask out only significant bits */
for (i=0; i<CODEWORDS; i++) {
/* shift up whole array */
carry_bits_new = code_buffer[i] >> (ULONG_BITS-bits);
/* save carry */
code_buffer[i] <<= bits;
code_buffer[i] |= carry_bits_old;
carry_bits_old = carry_bits_new;
} /* for */
valid_code_bits += bits;
#if DEBENC
printf("\tadd_hc_bits, END:");
print_bits(code_buffer[0],valid_code_bits);printf("\n");
#endif
return (valid_code_bits);
} /* add_hc_code */
int encode_huffman(long value, ULONG *hc_codes, char *hc_code_lengths,
ULONG maxv)
/* Get an 'value' and encode it using stacked huffman codes and return it
long-word wise by function 'get_encoded'; upper limit for stacked codes is
set by 'maxv'
Return
n > 0
codeword to be retuned in 'get_encoded' has n bits
*/
{
long stacked = 0;
#if DEBENC
printf("encode_huffman: v=%ld, max=%lu\n",value,maxv);
#endif
add_hc_code(value<0,1);
/* add one sign bit */
value = ABS(value);
valid_code_bits=1;
/* in case buffer has not yet been read, clear it */
while (value >= maxv) {
stacked = maxv;
/* flag for stacked codes */
#if DEBENC
printf(" ESC \n");
#endif
value -= maxv;
add_hc_code(hc_codes[maxv-1], hc_code_lengths[maxv-1]);
/* ESC - code */
} /* while code >= maxv */
#if DEBENC
printf(" value=%lu, bits=%d\n",value,hc_code_lengths[value+stacked]);
#endif
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
add_hc_code(hc_codes[value+stacked], hc_code_lengths[value+stacked]);
return valid_code_bits;
/* number of valid code bits for this data */
} /* encode_huffman */
int get_encoded(ULONG *code_slice)
/* get a slice of code after encoding by 'encode_huffman', return number
of valid bits.
Least significant word comes last.
*/
{
register int tmp, tmp_p;
if (!valid_code_bits)
return 0;
*code_slice = code_buffer[tmp_p=((valid_code_bits-1) / ULONG_BITS)];
if (valid_code_bits >= ULONG_BITS) {
tmp = valid_code_bits;
valid_code_bits -= ULONG_BITS;
memmove(code_buffer,code_buffer+1,sizeof(ULONG)*(CODEWORDS-1));
/* shift words down */
return ULONG_BITS;
}
else {
tmp = valid_code_bits;
valid_code_bits = 0;
*code_slice &= (1L<<tmp) -1;
/* mask out irrelevant bits */
return tmp;
}
} /* get_encoded */
int get_bits()
/* return number of bits in code buffer */
{
return valid_code_bits;
}
ULONG decode_huffman(ULONG *array, ULONG asize, HTYPE **hc_tree,
ULONG *in_array, ULONG in_bits)
/* Get input of in_bits size in in_array, decode it using hc_tree, and
return the decodes values in array. Read max. asize words.
Return
0 if not successful
number of words if sucessful
*/
{
/* to be implemented ############################################ */
return 0;
}
void make_stacked(ULONG *data, long size, long cut, int tables)
/*
Fits inputdata into one or two smaller tables of size cut;
If using 1 table, build a table containing the frequency of the original
data folded into one table of size 'cut'
If using 2 tables, split 'data' into two tables, the first containing the
frequencies up to 'cut'-1, the second containing the folded (stacked)
frequencies of all bigger values
After calculating, the new table overwrites the input data table
*/
{
ULONG i,control;
ULONG *cutptr,
*escptr,
*tmpptr;
89
ULONG **stk_tab;
/* here, I'll put the tables pointers for calculating
*/
if ((tables!=1) && (tables!=2))
/* not THAT error please ! */
errormsg("make_stacked: not 1 or 2 tables");
stk_tab=create_tab2d(cut,tables);
/* allocate memory
cutptr=stk_tab[0];
/* the first and only
for (i=0; i<cut; i++)
*cutptr++ = *data++;
/* first values are copied
escptr=*stk_tab + cut-1;
/* point to cut-1 (ESC)
cutptr=stk_tab[tables-1];
for (i=cut; i<size; i++, data++) {
/* enter stacked values
control = *escptr;
/*
if ((i/cut) * (*data))
printf("Stacked %d times for %lu\n",(i/cut),i);
*/
/* quick 'n DIRTY way to help calculating bits/sample */
esc_sum += i/cut*(*data/tables);
*escptr += (*data/tables)*(i/cut);
/* stacked values count more
if (control > *escptr)
/* overflow !
errormsg("\nERROR in make_stacked: ESC-value got too large.\n"
"Try larger tablesize");
control = *(tmpptr=cutptr+(i%cut));
*tmpptr += *data;
/* printf("m_s: i=%lu, i/cut=%lu, i%%cut=%lu, sum=%lu\n",i,i/cut,i%cut,
*tmpptr); */
if (control > *tmpptr)
/* overflow !
nuerr("\nERROR in make_stacked: stacked value got too large.\n"
"Try larger tablesize. Pos",i%cut);
} /* for */
data -= size;
/* reset data pointer
for (i=0; i<tables; i++)
/* rearrange data into one array: data
memcpy(data+i*cut,stk_tab[i],sizeof(ULONG)*cut);
/* copy data back
free_tab2d(stk_tab,tables);
}
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
90
8.1.11.
double mean_code_length(ULONG *abs_counts, ULONG no_ac, char *bit_p_value,
char stacked, char signbit);
/* mean code length for data used by huffgen */
hufflib.h
/*
Header for Huffman coding functions
Modelled after Hufflib2.for of Thomas Sporer, 880502.1039
Robert Henke
Changed:
*/
23. 8.92
15.11.92
/* ***************** debugging aid ***************** */
void parr(ULONG *arr, ULONG size, char *text);
ULONG **create_tab2d(ULONG size, ULONG slots);
/* allocate memory for 2 dim ULONG table and clear it to 0, return
char **create_char_tab2d(ULONG size, ULONG slots);
/* allocate memory for 2 dim char table and clear it to 0, return
pointer */
pointer */
#ifndef HUFFMAN_FLAG
#define HUFFMAN_FLAG
void free_tab2d(ULONG **tab, ULONG slots);
/* deallocate memory for 2 dim table */
#define HTYPE
void enter_tab2d(long *array, ULONG asize, ULONG **tab, ULONG size, ULONG
slots);
/* sort data from array into number 'slots' tables of size 'size'. Selection
of tables:
1st: max array value 0..1*size -1
2nd:
0..2*size -1
3rd:
0..3*size -1
...
slots-th:
0..(slots-1)*size -1
*/
ULONG
#include <stdio.h>
#include "defhenke.h"
enum {left,right};
/* define order of indices */
HTYPE **huffgen(ULONG *abs_counts, ULONG no_ac, char **bit_p_value,
ULONG **codes);
/* allocates memory for huffman tree, codes table an bit counts,
calculates these tables
Remember to free the memory !
Initialization:
1.
The input frequencies of the values expressed by the index of the
frequencies in abs_counts are entered into the right leafs of the
tree. The first position is reseved for the root. ntree points to
the last leaf added. Leafes have a right branch of 0, nodes have
both left and right branches
2.
hc_list contains the frequencies of abs_counts and their index
numbers. nlist is the lenght of the list.
Construction of the Huffman tree
1.
Sorting of hc_list
2.
hc_tree gets a new node:
2.1
The last two elements of hc_list are entered as its successors
2.2
The absolute count (frequency) of the node is the sum of its
successors. Its number is the next free number in hc_tree, and
ntree is incremented.
2.3
The new node replaces the two last entries in hc_list, and nlist
is decremented accordingly.
3.
Steps 1 and 2 are repeated until nlist=0.
Generation of the Huffman code
1.
Starting from the root all codes and code lengths are calculated
iteratively. This is done in one pass.
2.
The root is placed in position 0, ntree is decremented.
*/
void insert_sorted(HTYPE *nums, ULONG *freqs, HTYPE size);
/* nums, freqs gets new entry in position size-1 sorted for 2nd array */
void heapsort(HTYPE *nums, ULONG *freqs, HTYPE size);
/* both arrays are sorted, second one is the key */
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
void enter_ld_tab2d(long *array, ULONG asize, ULONG **tab, ULONG size,
ULONG slots);
/* sort data from array into number 'slots' tables of size 'size'. Selection
of tables:
1st: max array value 0..size -1
2nd:
0..2*size -1
3rd:
0..4*size -1
...
slots-th:
0..(2 to the (slots-1)-th power)*size -1
*/
void enter_stk_tab2d(long *array, ULONG asize, ULONG **tab, ULONG size);
/* sort data from array into two stacked tables of size 'size'. Selection
of tables:
1st: max array value 0..size-1
2nd:
>=size, set tab[0][size-1] as ESC (recursively)
*/
int read_tab1d(FILE *fileptr, ULONG *tab, ULONG size);
/* read from binary data file 32 bit data into tab
returns:
0 read ok
-1 file too short or data not readable
*/
int read_char_tab1d(FILE *fileptr, char *tab, ULONG size);
/* read from binary data file 8 bit data into tab
returns:
0 read ok
-1 file too short or data not readable
*/
int read_tab2d(FILE *fileptr, ULONG **tab, ULONG size, ULONG slots);
/* read from binary data file 32 bit data into tab
returns:
0 read ok
-1 file too short or data not readable
*/
91
int write_tab1d(FILE *fileptr, ULONG *tab, ULONG size);
/* write to binary data file 32 bit data from tab
returns:
0 write ok
-1 file not writable or error during write
*/
int write_tab2d(FILE *fileptr, ULONG **tab, ULONG size, ULONG slots);
/* write to binary data file 32 bit data from tab
returns:
0 write ok
-1 file not writable or error during write
*/
int read_char_tab2d(FILE *fileptr, char **tab, ULONG size, ULONG slots);
/* read from binary data file 8 bit data into tab
returns:
0 read ok
-1 file too short or data not readable
*/
void no_zeros(ULONG *array, ULONG asize);
/* changes 0s to 1, other values to double of the value */
int encode_huffman(long value, ULONG *hc_codes, char *hc_code_lengths,
ULONG maxv);
/* Get an 'value' and encode it using stacked huffman codes and return it
long-word wise by function 'get_encoded'; upper limit for stacked codes is
set by 'maxv'
Return
n > 0
codeword to be retuned in 'get_encoded' has n bits
*/
int get_encoded(ULONG *code_slice);
/* get a slice of code after encoding by 'encode_huffman', return number
of valid bits.
Least significant word comes last.
*/
int get_bits();
/* return number of bits in code buffer */
void make_stacked(ULONG *data, long size, long cut, int tables);
/*
Fits inputdata into one or two smaller tables of size cut;
If using 1 table, build a table containing the frequency of the original
data folded into one table of size 'cut'
If using 2 tables, split 'data' into two tables, the first containing the
frequencies up to 'cut'-1, the second containing the folded (stacked)
frequencies of all bigger values
After calculating, the new table overwrites the input data table
*/
#endif /* HUFFMAN_FLAG */
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
92
}
8.1.12.
miscutil.c
void a_lng_2_dbl(long *inarray, double *outarray, ULONG arraysize)
/* Copy inarray to outarray assuming that rounding aready has been done */
{
/*
Standard definitions for misc. functions
Author:
Started:
Changed:
*/
#include
#include
#include
#include
Robert Henke
26. 8.92
26.10.92
<stdlib.h>
<math.h>
<string.h>
"miscutil.h"
/* Local globals */
static int calls=0;
void *mymalloc(ULONG number, ULONG size)
/* args as calloc, aborts on error */
{
void *ptr;
calls++;
ptr = malloc(number*size);
if (!ptr) {
fprintf(stderr,
"mymalloc: Not enough memory for allocation of %lu bytes, call no. %d.\n",
number*size,calls);
exit (EXIT_FAILURE);
}
return (ptr);
} /* mymalloc */
void *mycalloc(ULONG number, ULONG size)
/* args as calloc, aborts on error, presets array with 0 */
{
void *ptr;
calls++;
ptr = calloc(number,size);
if (!ptr) {
fprintf(stderr,
"mycalloc: Not enough memory for allocation of %lu bytes, call no.
%d.\n",
number*size,calls);
exit (EXIT_FAILURE);
}
return (ptr);
} /* mycalloc */
while (arraysize--)
*outarray++ = (double) *inarray++;
}
long l_array_max(long *array, ULONG arraysize, long max)
/* returns max or biggest absolute value of array if bigger than max */
{
ULONG i;
long tmp;
for (i=0; i<arraysize; i++)
if ((tmp=ABS(*array++)) > max)
max=tmp;
return (max);
}
double array_max(double *array, ULONG arraysize, double max)
/* returns max or biggest absolute value of array if bigger than max */
{
ULONG i;
double tmp;
for (i=0; i<arraysize; i++)
if ((tmp=fabs(*array++)) > max)
max=tmp;
return (max);
}
void out_array(double *array, ULONG size, char *text)
/* lists array from 0 to size, each line beginning with text */
{
int i;
for (i=0; i<size; i++) {
fflush(stdout);
printf("%s [%4d]=%18.3f\n",text,i,*array++);
} /* endfor */
}
ULONG used_tab(ULONG *tab, ULONG blocksize)
/* looks for first tab entry not 0 startiing from blocksize backwards */
{
ULONG i;
for (i=blocksize-1;i;i--)
if (*tab--) return(i);
return(EXIT_SUCCESS);
}
void a_dbl_2_lng(double *inarray, long *outarray, ULONG arraysize)
/* Copy inarray to outarray assuming that rounding aready has been done */
{
while (arraysize--)
*outarray++ = (long) *inarray++;
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
/* check for fileerror and abort */
void fileerr(char *errstrg, FILE *fileptr)
{
char errbuffer[FNLEN]="File error on ";
93
if (ferror(fileptr) || !fileptr) {
fflush(stdout);
/* in order to have the right order in the output */
perror (strcat(errbuffer,errstrg));
exit(EXIT_FAILURE);
}
}
/* give errormessage with a string and a number and abort*/
void nuerr(char *errstrg, int error)
{
fflush(stdout);
/* in order to have the right order in the output */
fprintf(stderr,"%s: %d. Aborted.\n",errstrg,error);
exit(EXIT_FAILURE);
}
/* give errormessage with a string and abort*/
void errormsg(char *errstrg)
{
fflush(stdout);
/* in order to have the right order in the output */
fprintf(stderr,"%s. Aborted.\n",errstrg);
exit(EXIT_FAILURE);
}
/* preset double-array with MARK_VAL */
void d_clear(double *array, unsigned size)
{
unsigned i;
for (i=0; i<size; i++)
array[i]=MARK_VAL;
}
/* returns number of non-MARK_VAL values from beginning, check from size
downwards */
unsigned is_zero(double *array, unsigned size)
{
unsigned i;
i=size;
while (i) {
if (array[--i] != MARK_VAL)
break;
}
return (i);
}
/* output array as ASCII file to fileptr */
void print_array(double *sum, unsigned size, FILE *fileptr)
{
unsigned i;
/* set data to +- u_limit as min, max _after_rounding_it_ */
char clipping(double *data, double u_limit)
{
*data = (*data < 0.0) ? (*data-0.5) : (*data+0.5);
if (fabs(*data) > u_limit) {
*data=(*data > 0) ? (u_limit) : (-u_limit);
return (1);
}
*data = (*data < 0) ? (ceil(*data)) : (floor(*data));
return (0);
}
FILE *opfile(const char *filename, const char *mode, const char *errorm)
/* Opens file like fopen, prints errorm if file open fails and aborts */
/* special filename '-' for stdin and stdout is recognized */
{
FILE *fileptr=NULL;
char string[FNLEN];
if (!strcmp("-",filename)) {
/* use stdin / stdout */
if (strchr(mode,'r'))
/* open as stdin */
return stdin;
else
return stdout;
}
strcpy (string, filename);
strcat (string," ");
strcat (string, errorm);
fileptr=fopen(filename,mode);
fileerr(string, fileptr);
if (!fileptr)
errormsg(strcat(string, "Could not be accessed"));
return (fileptr);
} /* opfile */
int ldi(ULONG number)
/* returns ld(number) if number is power of 2, else returns -ld(number) */
{
int bit_number=0;
if (!number)
return (-9999);
/* ERROR */
for (; !(number & 1); number >>= 1)
/* power of 2? Only one bit set */
bit_number++;
if (number == 1)
return ( bit_number );
for (; number; number >>= 1)
/* power of 2? Only one bit set */
bit_number++;
return ( - bit_number);
} /* ld */
for (i=0; i<size; i++) {
fprintf(fileptr,"%d; %f\n",i,sum[i]);
fileerr("Error writing statistics", fileptr);
} /* endfor */
}
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
94
/* Opens file like fopen, prints errorm if file open fails and aborts */
8.1.13.
miscutil.h
void a_dbl_2_lng(double *inarray, long *outarray, ULONG arraysize);
/* Copy inarray to outarray assuming that rounding aready has been done */
/*
Standard definitions for misc. functions
void a_lng_2_dbl(long *inarray, double *outarray, ULONG arraysize);
/* Copy inarray to outarray assuming that rounding aready has been done */
Author:
Started:
Changed:
*/
int ldi(ULONG number);
/*
returns ld(number) if number is power of 2, else returns -ld(number)
returns on zero -9999
*/
Robert Henke
26. 8.92
26.10.92
#ifndef MISC_FLAG
#define MISC_FLAG
/* include only ONCE */
#endif /* MISC_FLAG */
#include <stdio.h>
#include "defhenke.h"
#define MARK_VAL
0.0
/* mark for unused tabs, should be 0.0*/
void *mymalloc(ULONG number, ULONG size);
/* args as calloc, aborts on error */
void *mycalloc(ULONG number, ULONG size);
/* args as calloc, aborts on error, presets array with 0 */
long l_array_max(long *array, ULONG arraysize, long max);
/* returns max or biggest absolute value of array if bigger than max */
double array_max(double *array, ULONG arraysize, double max);
/* returns max or biggest absolute value of array if bigger than max */
void out_array(double *array, ULONG size, char *text);
/* lists array from 0 to size, each line beginning with text */
ULONG used_tab(ULONG *tab, ULONG blocksize);
/* looks for first tab entry not 0 startiing from blocksize backwards */
void fileerr(char *errstrg, FILE *fileptr);
/* check for fileerror and abort */
void nuerr(char *errstrg, int error);
/* give errormessage with a number and abort*/
void errormsg(char *errstrg);
/* give errormessage with a string and abort*/
char clipping(double *data, double u_limit);
/* set data to +- u_limit as min, max */
void d_clear(double *array, unsigned size);
/* preset double-array with 0 */
unsigned is_zero(double *array, unsigned size);
/* returns number of non-zero values from beginning, check from
size on downwards */
void print_array(double *sum, unsigned size, FILE *fileptr);
/* output array as ASCII file to fileptr */
FILE *opfile(const char *filename, const char *mode, const char *errorm);
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
95
8.1.14.
int valid_bits(long *original, long *coded, unsigned short size,
FILE *output_file, ULONG *global_stat, int channels,
char ok);
pacse.c
/* for debugging only */
unsigned is_zero(double *array, unsigned size);
int max_debug_samples = 8192;
/* short data packet for debugging */
/*
PACSE
Professional Audio Coder using Spectral Entropy
Robert Henke
7.10.92
Changed:
15.12.92
double max_amplitude=0.0,
max_q_amplitude=0.0;
Input:
Output:
32 bit lin. NeXT .snd - file
32 bit .snd, analysis data
BUGS:
memory bug in mdct (uses n+1 places for 1st block)
Does not check if bstat_tab is big enough
right channel: 2-D histo missing --> only mono allowed
to do:
better mdct;
options preset by environment variable;
*/
#include
#include
#include
#include
#include
#include
#include
#include
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
"sndutil.h"
"cliutil.h"
"miscutil.h"
"hufflib.h"
"coder.h"
<sys/types.h>
<sys/stat.h>
<math.h>
BLOCKLEN
IMDCT
QUANTVALS
QUANTMAX
PETAB
Q_STEP_SIZE
BUCKET
NUM_BITS
NLC_BITS
NLC_HYST
/* handling of sound data and files
/* command line input functions
/* misceallanous functions: mycalloc.
/* library for statistics and huffman coding
/* endcoding / decoding functions
*/
*/
*/
*/
*/
/* file statistics */
1024
/* size of FFT block
TRUE
/* output IMDCT values
16
41
/* max. quantization
256
/* size of PE-Table
pow(2.0,0.25)
/* quantization step size
2000
/* bit bucket
16
/* number of soundfile bits to be compared
15
/* number of bits for near lossless quality
FALSE /* NLC with hystereses : NLC_BITS...NLC_BITS+2
/* statistics for writing of 2D tables */
extern ULONG stat_bothlow, stat_onelow, stat_bothhigh;
int main(int argc, char *argv[], char *envp[])
{
/* local functions */
void output_coeffs(ULONG *sum, ULONG size, char line,
FILE *fileptr);
int input_coeffs(ULONG *sum, ULONG size, FILE *fileptr);
void histo(double *block, ULONG blocksize, ULONG *sum,
long hsize);
void sml_histo(double *block, ULONG blocksize, ULONG *sum, long hsize,
encoder_parm *par);
void pe_histo(ULONG pe_value, ULONG *pe_tab);
int perceptual_entropy(double *block, ULONG blocksize);
void output_freqs(double *array, ULONG number, FILE *fileptr);
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
*/
*/
*/
*/
*/
*/
*/
*/
*/
/* max. frequency amplitude */
/* max. quantizised amplitude */
FILE *infile=NULL,
/* snd data input in NeXT 32 lin fmt
*outfile=NULL,
/* snd data output
*ppestatfile=NULL,
/* pseudo perceptual entropy
*bigstatfilel=NULL,
/* binary statistics file low
*bigstatfileh=NULL,
/* binary statistics file high
*freqfile=NULL,
/* quantisized frequencies
*hcefile=NULL,
/* huffman codes
*hclfile=NULL,
/* huffman code lengths
/*
*smlstatfile=NULL; */
/* stats for small values
*blockstatfile=NULL,
/* blockwise ascii statistics
*nlcfile=NULL;
char infilename[FNLEN],
outfilename[FNLEN]="",
/* space for filenames
ppestatfilename[FNLEN]="ppefile.asc",
bigstatfilelname[FNLEN]="statfile.bgl",
bigstatfilehname[FNLEN]="statfile.bge",
freqfilename[FNLEN]="frequencies.bin",
hcefilename[FNLEN]="huffman.hce",
hclfilename[FNLEN]="huffman.hcl",
/*
smlstatfilename[FNLEN]="statfile.sml"; */
blockstatfilename[FNLEN]="",
nlcfilename[FNLEN]="";
SNDSoundStruct snd1, snd2;
/* Header of snd-data
struct stat file_statistics;
/* buffer for file statistics
long i;
ULONG clips=0;
int samples;
int result;
double bit_limit = 6.0;
long bit_bucket = BUCKET;
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
/* general purpose loop counter
/* count of signal clippings
/* bytes really transferred
/* temporary result of functions
/* limit of bits per block
/* bit reservoir
*/
*/
*/
*/
*/
*/
/* some variables and their defaults */
int blocklen = BLOCKLEN,
mdctblock,
/* size of big arrays
new_datasamples;
/* number of samples
double quant;
/* factor for quantization
int blockprint;
/* max. arraymembers on debug listing
long bstat_size = QUANTVALS;
/* size of binary stat file
short sstat_size=4;
/* small values stat file size
/*
bigval_group=2; */
/* number of big values to be grouped together
ULONG val_bits[NUM_BITS+1];
/* number of samples with n valid bits
int correctness = NLC_BITS,
/* accuracy of reproduction
nlc_limit = NLC_BITS;
ULONG bit_bucket_save,
/* for NLC mode
bit_limit_save;
*/
*/
*/
*/
*/
*/
*/
*/
*/
/* data buffers */
long *iobuffer,
*iobuffer_save,
*orig_block;
double *data_block,
*/
/* for file I/O of both channels */
/* uncoded block */
/* block to be coded */
96
*liwindow,
*lowindow,
*riwindow,
*rowindow,
*liwindow_save,
*lowindow_save,
*riwindow_save,
*rowindow_save;
double add_buffer;
ULONG *pe_tab;
ULONG **bstat_tab_l;
ULONG *bstat_tab_h;
ULONG **sstat_tab;
ULONG hc_size;
/* flags */
int process = TRUE;
int skipped_zeros = FALSE;
int debug = 0;
int line = TRUE;
int pe_stat_flag = FALSE;
int bstat_flag = FALSE;
int freq_flag = FALSE;
int no_stats = FALSE;
int quiet = FALSE;
int hc_generate = FALSE;
char stacked=1;
char nlc_flag = FALSE,
nlc_ok_flag = TRUE;
char blockstat_flag = FALSE;
"\t-r n.n\tset max. data rate to n.n bits/sample (NLC too!)\n",
"\t-s file\tbinary statisics base-filename. Ext=*.bgl or *.bge\n",
"\t-S n\tstacked huffman codes: disable with 0, default is enabled\n",
"\t\t2 enables 2-Dim stacked huffman codes\n",
"\t-t n\tld(table size=bits in spectral domain) set to n (default 16)\n",
"\t\tfor -S < 2; for -S 2: n gives table size in words \n",
"\t-T n\tld(table for small values), default 4\n",
"\tYou may use '-' for stdin, stdout as filename\n",
VERSION_STRING,
""
/* left input windowing buffer */
/* left output windowing buffer */
/* right input windowing buffer */
/* left input windowing buffer */
/* buffer's buffer for nlc */
/* buffer for overlap - add
/* table for perceptual entropy
/* table for big stat low values
/* table for big stat escaped values
/* table for small stat tables
/* raw size of huffman tables
*/
*/
*/
*/
*/
*/
/* flag: process data
/* very first data block
/* various debugging levels
/* add line numbers in out-files
/* ASCII values of pseudo perceptual
entropy into file
/* binary statistics file wanted
/* frequency file wanted
/* no quantization & stats
/* quiet run
/* generate data for new huffman code tables
/* use 'stacked' huffman tables
/* near lossles coding -- for n bits
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
*/
};
/* Get command line options */
/* need some help ? */
if (getarg_flag(argc,argv,'h') || getarg_flag(argc,argv,'?'))
usage(usage_txt,argv[0]);
quiet=getarg_flag(argc,argv,'a');
/* no messages
getarg_int_option(argc,argv,'d',&debug);
/* debug level
line=getarg_flag(argc,argv,'l');
/* add line numbers
pe_stat_flag=getarg_option (argc,argv,'p',ppestatfilename); /* ppe stats
*/
*/
*/
*/
getarg_long_option(argc,argv,'t',&bstat_size);
/* number of bits: frequency, or 2D table size */
getarg_sht_option(argc,argv,'T',&sstat_size);
getarg_int_option(argc,argv,'b',&blocklen);
new_datasamples = blocklen/2;
/* num. of bits sml_values */
/* MDCT blocklength */
no_stats=getarg_flag(argc,argv,'n'); /* no statistics, no memory for them */
encoder_parm encode_p;
/* all parameters and tables for encoder */
getarg_long_option(argc,argv,'B',&bit_bucket);
const char *usage_txt[] = {
"usage: %s infile [outfile] -c huffman [-option [optionvalue][...]]\n",
"Professional Audio Coder using Perceptual Entropy.\n",
"Options may be\n",
"\t-a\tno auxillary messages (quiet mode)\n",
"\t-A n\tAccuracy for Near Lossless Coding (default 15 Bits)\n",
"\t-b n\tset MDCT time domain blocksize to n (default 1024)\n",
"\t-B n\tbit-reservoir set to n (default 2000)\n",
"\t-c file\thuffman code lengths in *.hcl, escaped in *.hce (-S2)\n",
"\t-C file\tcompare blocks and write val bits statistic (0 to 31)\n",
"\t-d 1\tdebug\tsummaries (ORed with)\n",
"\t
2\t\tonly 8192 values\n",
"\t
4\t\tlist arrays\n",
"\t
8\t\tprint progress\n",
"\t
16\t\tprint used array spaces\n",
"\t
32\t\tcoder debug data\n",
"\t
64\t\textended coder debug data\n",
"\t 128\t\tcoder debuging: count_bit data\n",
"\t 256\t\tcoder debuging: count_big_bit data\n",
"\t 512\t\tNLC: bitrate in bits / block & other info\n",
"\t-e n\tprint n samples on option -d4\n",
"\t-f file\tfrequency values (32 bit) [-e n samples]\n",
"\t-g\tgenerate new huffman code data (-s) used with -c file, -s file\n",
"\t-n\tno statistics for stat-files, only quantization\n",
"\t-N file\tnear lossloss mode: bitrate for 15 Bit NLC output to file\n",
"\t-l\tprint line numbers in ASCII output files\n",
"\t-p file\tASCII pseudo perceptual entropy file\n",
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
/* bit reservoir override */
blockprint=blocklen/2;
/* lines to print for debugging */
getarg_int_option(argc,argv,'e',&blockprint);
getarg_char_option(argc,argv,'S',&stacked);
/* stacked huffman codes */
if (stacked > 2)
errormsg("Only 1-D and 2-D stacked huffman code allowed");
hc_generate=getarg_flag(argc,argv,'g');
/* generate new data */
/* get huffman table names */
if (getarg_option(argc,argv,'c',hcefilename) <= 0)
error_usage("Huffman table files are needed", usage_txt, argv[0]);
strcpy(hcefilename,remove_extension(hcefilename,"."));
strcpy(hclfilename,hcefilename);
/* hcl file has to have extension hcl */
add_extension(hcefilename,".hce"); /* hce file has to have extension hce */
add_extension(hclfilename,".hcl");
/* filenames for binary stats */
bstat_flag=getarg_option(argc,argv,'s',bigstatfilelname);
remove_extension(bigstatfilelname,".");
strcpy(bigstatfilehname, bigstatfilelname);
if (stacked == 2) {
add_extension(bigstatfilelname,".bgl");
add_extension(bigstatfilehname,".bge");
}
else
add_extension(bigstatfilelname,".bg1");
/* strcpy(smlstatfilename,bigstatfilename); */
97
lowindow =
riwindow =
rowindow =
data_block
/* add_extension(smlstatfilename,".sml"); */
/* frequency file output */
freq_flag=getarg_option(argc,argv,'f', freqfilename);
/* NLC statstics output */
nlc_flag=getarg_option(argc, argv, 'N', nlcfilename);
getarg_int_option(argc,argv,'A',&nlc_limit);
/* NLC accuracy */
getarg_dbl_option(argc,argv,'r',&bit_limit);
bit_limit *= blocklen/2.0;
/* max. bit rate */
/* block statistics */
blockstat_flag=getarg_option(argc, argv, 'C', blockstatfilename);
if (debug)
fprintf(stderr,"Debug mode set to %d\n",debug);
/* no statistics: no memory and no collecting */
if (no_stats)
pe_stat_flag=bstat_flag=line=FALSE;
if (debug & 1)
fprintf(stderr,
"Block=%d, Line=%d, PPE=%d, ld(t_size)=%d,\nbitrate=%.0f/%ld, "
"stacked=%d, new HCT= %s, bit reservoir=%ld, nlc_limit=%d\n",
blocklen, line, pe_stat_flag, bstat_size, bit_limit, new_datasamples,
stacked, (hc_generate) ? "yes" : "no", bit_bucket, nlc_limit);
/* check for valid blocksize */
if ((ldi(blocklen) > 10) || (ldi(blocklen) < 0))
errormsg("blocklen must be a power of 2 up to 1024");
mdctblock = blocklen * 2;
/* play safe, bug in mdct needs larger blocks */
if (stacked == 1) {
/* check for valid bstat_size */
if ((bstat_size < 1) || (bstat_size > 31))
errormsg("ld (table size) [option -t n] must be between 1 and 31");
bstat_size = 1L << bstat_size;
}
encode_p.sml_values_bits = sstat_size;
sstat_size = 1L << sstat_size;
if (debug & 1)
if (no_stats)
printf("No statistical data collection\n");
else {
printf("Using %ld as fieldlength\n",bstat_size);
}
(double *) mycalloc(new_datasamples, sizeof(double));
(double *) mycalloc(new_datasamples, sizeof(double));
(double *) mycalloc(new_datasamples, sizeof(double));
= (double *) mycalloc(mdctblock, sizeof(double));
if (!no_stats) {
pe_tab = (ULONG *) mycalloc(PETAB, sizeof(ULONG));
}
if (bstat_flag) {
if (stacked != 2)
bstat_tab_h = (ULONG *) mycalloc(bstat_size, sizeof(ULONG));
else {
bstat_tab_l = create_tab2d(bstat_size, bstat_size);
bstat_tab_h = (ULONG *) mycalloc(bstat_size, sizeof(ULONG));
}
sstat_tab=create_tab2d(sstat_size, 1);
/* storage for small values */
}
/* mark block for check for used area */
if (debug & 16) {
d_clear(data_block, mdctblock);
d_clear(liwindow, new_datasamples);
d_clear(lowindow, new_datasamples);
d_clear(riwindow, new_datasamples);
d_clear(lowindow, new_datasamples);
if (!no_stats) {
memset (pe_tab, 0, PETAB*sizeof(ULONG));
}
}
/**********************************************************************
open files
**********************************************************************/
if (debug & 8)
printf("Starting, opening in- and outfile\n");
if (!getarg_name(argc,argv,0,infilename))
usage(usage_txt,argv[0]);
infile = openfile(infilename,"rb");
if (debug & 8)
printf("Opened infile\n");
if (!getarg_name(argc,argv,1,outfilename)) {
strcpy(outfilename,infilename);
add_to_name(outfilename,".snd","PAC");
}
outfile = openfile(outfilename,"wb");
/* after input of last parameter: */
getarg_illegals(argc, argv, usage_txt);
/* ***************** Allocate memory for buffers ***************** */
iobuffer = (long *) mycalloc(blocklen, 2*sizeof(long));
if (nlc_flag) {
iobuffer_save = (long *) mycalloc(blocklen, 2*sizeof(long));
liwindow_save = (double *) mycalloc(new_datasamples, sizeof(double));
lowindow_save = (double *) mycalloc(new_datasamples, sizeof(double));
riwindow_save = (double *) mycalloc(new_datasamples, sizeof(double));
rowindow_save = (double *) mycalloc(new_datasamples, sizeof(double));
}
orig_block = (long *) mycalloc(blocklen+new_datasamples, 2*sizeof(long));
liwindow = (double *) mycalloc(new_datasamples, sizeof(double));
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
/* read input header */
fread(&snd1,sizeof(SNDSoundStruct),1,infile);
fileerr("Header could not be read",infile);
if (debug & 8)
printf("Read header... ");
/* check for valid file format */
if (sndinfo(snd1,infilename,FALSE)) {
fprintf(stderr,
"No NeXT \".snd\"-file, or false word order.\n");
98
exit(EXIT_FAILURE);
}
if (snd1.dataFormat!=5) {
fprintf(stderr,".snd format is not 32 bits lin.\n");
sndinfo(snd1,infilename,1);
exit(EXIT_FAILURE);
}
/* Set to start of data */
fseek(infile, snd1.dataLocation, SEEK_SET);
if (hc_generate) {
if (debug & 8)
OUT2ERR("Assuming 1 as length for all numbers\n");
}
else {
if (debug & 8)
OUT2ERR("Opening huffman tables\n");
if (stacked == 2)
hcefile=opfile(hcefilename,"rb","(Huffman code lengths escaped)");
hclfile=opfile(hclfilename,"rb","(Huffman code lengths file)");
} /* else */
/*
smlstatfile=opfile(smlstatfilename,"wb","(Small values statistics file)");
*/
if (blockstatfilename[0] && blockstat_flag)
blockstatfile=opfile(blockstatfilename,"w",
"(blockwise ascii statistics)");
if (nlcfilename[0])
nlcfile=opfile(nlcfilename,"w","(near lossless coding statistics)");
/* ********************* prepare snd - header ******************* */
/* Header is assumed to have no info data with a length > 4,
*/
/* else info strings have undefined value, sound data will be ok */
/******************************************************************/
if (debug & 8)
printf("Writing out-.snd header... ");
/* copy header information to outfile */
memcpy(&snd2,&snd1,sizeof(snd1));
fwrite(&snd2,sizeof(snd2),1,outfile);
fileerr("Header write",outfile);
/* ******************* initialize data structures ****************** */
if (debug & 8)
printf("Initializing data structures... \n");
if (stacked == 2)
quant = pow(2.0,QUANTMAX-2*ldi(bstat_size)-1);
/* secure limit */
else
quant = pow(2.0,QUANTMAX-ldi(bstat_size));
/* secure limit */
hc_size = bstat_size;
/* look for size of huffman tables */
if (!hc_generate) {
stat(hclfilename, &file_statistics);
hc_size = file_statistics.st_size;
if (debug & 1)
printf("Huffman code file l size: %ld words.\n",
file_statistics.st_size);
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
switch (stacked) {
case 0: break;
case 1: hc_size /= 2;
break;
case 2: stat(hcefilename, &file_statistics);
i=file_statistics.st_size;
if (debug & 1)
printf("Huffman code length file hce size: %ld
words.\n",i);
if (i != 1L << (ldi(hc_size) / 2))
errormsg("Huffman code length files do not match in size");
hc_size = i;
break;
default: errormsg("Variable 'stacked' has wrong value");
} /* switch */
} /* if */
/* reserve space for huffman tables and preset some variables */
encode_p.mdct_size = blocklen/2;
encode_p.debug_flags = debug;
encode_p.huffman_size = hc_size;
encode_p.quantstep = Q_STEP_SIZE;
encode_p.bit_limit = bit_limit;
encode_p.stacked = stacked;
encode_p.bit_bucket_size = bit_bucket;
encode_p.array_debug_len = blockprint;
if ((stacked == 1) && (bstat_tab_h))
encode_p.max_value = bstat_size-1;
else {
encode_p.max_value = 1L << (sizeof(ULONG) * 8 - 2);
}
init_encoder(&encode_p);
memset(val_bits, 0, (NUM_BITS+1)*sizeof(ULONG));
/* ***************** Read huffman files ***************** */
if (!hc_generate) {
if (debug & 8)
printf("Reading huffman codes, ");
if (stacked == 1) {
/* later for codes
*
if (encode_p.huffman_size!=fread(hc_codes, sizeof(ULONG),
*
encode_p.huffman_size, hcefile))
*
errormsg("Cannot read huffman code file");
*/
if (encode_p.huffman_size != fread(encode_p.huffman_lengths,
sizeof(char), encode_p.huffman_size, hclfile))
errormsg("Cannot read huffman code length file");
} /* if stacked == 1 */
else {
if (
read_char_tab2d( hclfile, encode_p.huffman_lengths_low,
encode_p.huffman_size, encode_p.huffman_size) ||
(encode_p.huffman_size != fread( encode_p.huffman_lengths_high,
sizeof(char), encode_p.huffman_size, hcefile))
) {
OUT2ERR("\nWarning: 2D huffman files not read properly\n");
}
else
if (debug & 1)
99
printf("Read 2D tables (dimension %d) without errror.\n",
encode_p.huffman_size);
} /* else */
} /* if */
/* ***************** Read PE statistics file ***************** */
if (pe_stat_flag) {
if (debug & 8)
OUT2ERR("Reading PE stat-file... ");
ppestatfile = fopen(ppestatfilename,"r"); /* read data */
result=1;
if (ppestatfile) {
if (debug & 8)
OUT2ERR("PE-Statistics file exists, reading\n");
result=input_coeffs(pe_tab, PETAB, ppestatfile);
if (debug & 8)
fprintf(stderr,"PE-Statistics file %ssucessfully read\n",
(result) ? "not " : ""); fflush(stderr);
fclose (ppestatfile);
fileerr("Could not close PE-statistics file\n",ppestatfile);
}
if (result) { /* no valid statfile */
printf("Preparing a new PE-statistics file\n");
memset(pe_tab,0,sizeof(ULONG)*PETAB);
}
else
fprintf(stderr,
"Updating table for PE-statistics file - writing to the file "
"will be at the end\n");
printf("Tab used: %u\n",used_tab(pe_tab, PETAB));
}
/* ***************** Read binary statistics ***************** */
if ( bstat_flag ) {
if ( debug & 8 )
OUT2ERR( "Looking for binary statistics file(s) ... " );
bigstatfilel = fopen( bigstatfilelname, "rb" );
/* read data */
bigstatfileh = fopen( bigstatfilehname, "rb" );
/* read data */
if (stacked != 2) {
if (bigstatfilel) {
memset(bstat_tab_h,0,sizeof(long)*bstat_size);
read_tab1d( bigstatfilel, bstat_tab_h, bstat_size);
result = 0;
/* input usually is shorter tha table size,
no error checking */
fclose (bigstatfilel);
fileerr("Could not close big value statistics file\n",
bigstatfilel);
}
else {
if ( debug & 8 )
OUT2ERR( "found no file\n" );
result = 1;
} /* else */
}
else {
if (bigstatfilel || bigstatfileh) {
result = read_tab2d( bigstatfilel, bstat_tab_l, bstat_size,
bstat_size );
result |= read_tab1d( bigstatfileh, bstat_tab_h, bstat_size);
fclose (bigstatfilel);
fileerr("Could not close big value statistics file hcl",
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
bigstatfilel);
fclose (bigstatfileh);
fileerr("Could not close big value statistics file hce",
bigstatfileh);
}
else {
if ( debug & 8 )
OUT2ERR( "found no file\n" );
result = 1;
} /* else */
} /* else stacked == 2 */
if (result) {
printf("Preparing a new big value statistics file\n");
/* clear statistics table */
if (stacked != 2)
memset(bstat_tab_h,0,sizeof(long)*bstat_size);
else {
int i;
for (i=0; i<bstat_size; i++) {
memset(bstat_tab_l[i],0,sizeof(long)*bstat_size);
bstat_tab_h[i]=0;
} /* for */
} /* else */
} /* result not sucessful */
} /* if (bstat_flag) */
/* ***************** Open frequency file ***************** */
if (freq_flag)
freqfile = opfile(freqfilename,"wb","Frequencies output");
/* initializise */
clips = 0;
max_amplitude=0.0;
max_q_amplitude=0.0;
/* position to start of data */
fseek (infile, snd1.dataLocation, SEEK_SET);
/* Clear windowing buffers */
d_clear(liwindow, new_datasamples);
d_clear(lowindow, new_datasamples);
d_clear(riwindow, new_datasamples);
d_clear(rowindow, new_datasamples);
/**********************************************************************
main processing loop
**********************************************************************/
process = TRUE;
while (process) {
/* read next data block */
if (debug & 8)
OUT2ERR("R ");
samples=fread((void *) iobuffer, sizeof(long),
snd1.channelCount*new_datasamples, infile);
if (debug & 2) {
100
max_debug_samples -= blocklen;
if (!max_debug_samples)
samples=0;
} /* endif debug */
/* at the end, fill up with zeros */
if (samples<0)
nuerr("Reading error",samples);
else if (samples < snd1.channelCount*new_datasamples) {
for (i = samples; i<snd1.channelCount*blocklen; i++)
iobuffer[i]=0;
process=FALSE;
}
/* set up buffer for comparison */
memmove(orig_block, orig_block+snd1.channelCount*new_datasamples,
sizeof(long)*snd1.channelCount*new_datasamples);
memcpy(orig_block+snd1.channelCount*new_datasamples, iobuffer,
sizeof(long)*snd1.channelCount*new_datasamples);
if (nlc_flag) {
memcpy(iobuffer_save,
sizeof(long) *
memcpy(liwindow_save,
memcpy(lowindow_save,
memcpy(riwindow_save,
memcpy(rowindow_save,
}
iobuffer,
2*blocklen);
liwindow, sizeof(double)
lowindow, sizeof(double)
riwindow, sizeof(double)
rowindow, sizeof(double)
*
*
*
*
new_datasamples);
new_datasamples);
new_datasamples);
new_datasamples);
/* save some variables for NLC mode */
if (nlc_flag) {
bit_limit_save = encode_p.bit_limit;
} /* if nlc_flag */
do {
/* *** *** *** *** loop for NLC mode *** *** *** *** */
/* This is a last minute addition - sorry, and check for bugs ! */
/* ************ process left (mono) channel ************ */
/* restore variables for NLC mode, output statistics */
if (nlc_flag) {
encode_p.bit_bucket = 0;
#if NLC_HYST
nlc_ok_flag = ((correctness >= nlc_limit) &&
(correctness <= nlc_limit+2));
#else
nlc_ok_flag = (correctness == nlc_limit);
#endif
memcpy(iobuffer, iobuffer_save,
sizeof(long) * 2*blocklen);
memcpy(liwindow, liwindow_save, sizeof(double) *
new_datasamples);
memcpy(lowindow, lowindow_save, sizeof(double) *
new_datasamples);
memcpy(riwindow, riwindow_save, sizeof(double) *
new_datasamples);
memcpy(rowindow, rowindow_save, sizeof(double) *
new_datasamples);
encode_p.bit_limit += (nlc_limit - correctness)
* blocklen / 20;
nlc_ok_flag = TRUE;
if (debug & 512)
printf("NLC: NLC at 1 Bit/sample\n");
} /* not enough bits */
else {
if (encode_p.bit_limit > 16*blocklen/2) {
encode_p.bit_limit = 16*blocklen/2;
nlc_ok_flag = TRUE;
if (debug & 512)
printf("NLC: Cannot obtain NLC\n");
} /* too many bits */
}
if (!nlc_ok_flag) {
if (debug & 512)
printf("NLC: dif = %2d, limit= %ld\n",
nlc_limit - correctness, encode_p.bit_limit);
} /* !nlc_ok_flag */
if (nlc_ok_flag) {
if (debug & 512)
printf("NLC: OK (%d)
limit= %ld\n",
nlc_limit - correctness, encode_p.bit_limit);
if (nlcfile)
fprintf(nlcfile, "%6.3f\n", (double)
encode_p.bit_limit / blocklen * 2.0);
} /* nlc_ok_flag */
} /* if nlc_flag */
/* read in input buffer */
if (debug & 8)
OUT2ERR("l ");
for (i = 0; i < blocklen; i++) {
if (i < new_datasamples) {
data_block[i] = liwindow[i];
} /* if i < new_datasamples */
else {
data_block[i] = liwindow[i-new_datasamples] =
iobuffer[(i-new_datasamples)*snd1.channelCount];
} /* else */
} /* for */
quant = encode_block(data_block, quant, &encode_p);
if (!no_stats && bstat_flag && nlc_ok_flag) {
if (stacked == 1)
histo(data_block, encode_p.big_values, bstat_tab_h,
bstat_size);
else
enter_2_2d(data_block, encode_p.big_values, bstat_tab_l,
bstat_tab_h, bstat_size);
/* to be completed later
sml_histo(data_block, blocklen/2, sstat_tab[0], sstat_size,
*encode_p);
*/
}
if (freq_flag && nlc_ok_flag) /* output frequencies */
output_freqs(data_block, blocklen/2, freqfile);
decode_block(data_block, quant, &encode_p);
if (encode_p.bit_limit < 1*blocklen/2) {
encode_p.bit_limit = 1*blocklen/2;
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
/* overlap add */
101
correctness = valid_bits(orig_block, iobuffer+new_datasamples,
samples-new_datasamples*snd1.channelCount,
blockstatfile, val_bits, snd1.channelCount,
nlc_ok_flag);
if (nlc_ok_flag) {
fwrite((char *)(iobuffer+new_datasamples), sizeof(long),
samples-new_datasamples*snd1.channelCount, outfile);
fileerr("Data write",outfile);
skipped_zeros = TRUE;
} /* nlc_ok_flag */
} /* if (!skipped_zeros) */
else {
if(!process && nlc_ok_flag)
samples += new_datasamples*snd1.channelCount;
if (debug & 8)
OUT2ERR("W ");
if (blockstat_flag || nlc_flag)
correctness = valid_bits(orig_block, iobuffer, samples,
blockstatfile, val_bits, snd1.channelCount,
nlc_ok_flag);
if (nlc_ok_flag) {
fwrite((char *)iobuffer, sizeof(long), samples, outfile);
fileerr("Data write", outfile);
} /* nlc_ok_flag */
} /* else */
} while (nlc_flag && !nlc_ok_flag);
if (nlc_flag) { /* flag new value as to be recalculated */
correctness = nlc_limit -1;
if ((encode_p.bit_limit > (nlc_limit -1) * new_datasamples) ||
(encode_p.bit_limit < new_datasamples))
encode_p.bit_limit = (nlc_limit / 2) * new_datasamples;
}
/* *** *** *** *** nlc loop *** *** *** *** */
} /* while process */
for(i = 0;i < blocklen;i++) {
if(i < new_datasamples) {
add_buffer = data_block[i] + lowindow[i];
clips += clipping(&add_buffer, MAX_LONG);
iobuffer[i*snd1.channelCount] = add_buffer;
} /* if */
else {
add_buffer = lowindow[i-new_datasamples] = data_block[i];
clips += clipping(&add_buffer, MAX_LONG);
iobuffer[i*snd1.channelCount] = add_buffer;
} /* else */
} /* for */
/* ************ process right channel ************ */
if (snd1.channelCount==2) {
if (debug & 8)
OUT2ERR("r ");
/* read in input buffer */
for (i = 0;i < blocklen; i++) {
if (i < new_datasamples) {
data_block[i] = riwindow[i];
} /* if */
else {
data_block[i] = riwindow[i-new_datasamples] =
iobuffer[(i-new_datasamples)*2+1];
} /* else */
} /* for */
quant = encode_block(data_block, quant, &encode_p);
if (!no_stats && bstat_flag && nlc_ok_flag) {
histo(data_block, encode_p.big_values, bstat_tab_h, bstat_size);
/* to be completed later
sml_histo(data_block, sstat_size, sstat_tab[0], sstat_size,
*encode_p);
*/
} /* if */
/**********************************************************************
statistics & clean-up
**********************************************************************/
if (freq_flag && nlc_ok_flag) /* output frequencies */
output_freqs(data_block, blocklen/2, freqfile);
decode_block(data_block, quant, &encode_p);
/* overlap add */
for(i = 0; i < blocklen; i++) {
if(i < new_datasamples) {
add_buffer = data_block[i] + rowindow[i];
clips += clipping(&add_buffer, MAX_LONG);
iobuffer[(i*2) + 1] = add_buffer;
} /* if */
else {
add_buffer = rowindow[i-new_datasamples] = data_block[i];
clips += clipping(&add_buffer, MAX_LONG);
iobuffer[(i*2)+1] = add_buffer;
} /* else */
} /* for */
} /* if snd1.channelCount==2 */
/* ************ Write next data block ************ */
if (!skipped_zeros) {
if (debug & 8)
OUT2ERR("W\n");
if (blockstat_flag || nlc_flag)
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
if (!quiet) {
if (bstat_flag && (stacked == 1))
{/* give length of used table space
*/
for (i=bstat_size-1; i; i--)
if (bstat_tab_h[i])
break;
printf("Quant. value table used up to pos. %ld (%.0f bits)\n",
i++, fabs(log((double) i)/log(2.0)+0.5));
} /* if (bstat_flag) */
if (clips)
printf("Clippings: %u, that's %3.2f%%.\n",clips,
clips * 100.0 / (double) snd1.dataSize / (double) sizeof(long));
}
/* Output statistic values */
if ( bstat_flag ) {
if ( !quiet )
printf( "(Re-)writing big values statistics file\n" );
if (stacked != 2) {
bigstatfilel = fopen( bigstatfilelname, "wb" );
/* rewrite data */
fileerr("Could not rewrite big values statfile l", bigstatfilel );
write_tab1d( bigstatfilel, bstat_tab_h, i);
fclose( bigstatfilel );
102
fileerr("Couldn't close big values statistics file h\n",
bigstatfilel);
}
else {
if ( !quiet )
printf("2D statistics: x,y low: %lu, one low: %lu, both high:
%lu\n",
stat_bothlow, stat_onelow, stat_bothhigh);
bigstatfilel = fopen( bigstatfilelname, "wb" );
/* rewrite data */
fileerr("Could not rewrite big values statfile l", bigstatfilel );
bigstatfileh = fopen( bigstatfilehname, "wb" );
/* rewrite data */
fileerr("Could not rewrite big values statfile h", bigstatfileh );
write_tab2d( bigstatfilel, bstat_tab_l, bstat_size, bstat_size);
write_tab1d( bigstatfileh, bstat_tab_h, bstat_size);
fclose( bigstatfilel );
fileerr("Couldn't close big values statistics file l\n",
bigstatfilel);
fclose( bigstatfileh );
fileerr("Couldn't close big values statistics file h\n",
bigstatfileh);
}
/*
fclose( smlstatfile );
fileerr( "Couldn't close small values statistics file.\n", smlstatfile );
*/
}
if (pe_stat_flag) {
if (!quiet)
printf("(Re-)writing PE-statfile\n");
ppestatfile = fopen(ppestatfilename,"w"); /* rewrite data */
fileerr("Could not rewrite PE-statfile",ppestatfile);
output_coeffs(pe_tab, PETAB, line, ppestatfile);
fclose(ppestatfile);
fileerr("Couldn't close PE-statistics file.\n",ppestatfile);
}
if (blockstat_flag) {
if (!quiet) {
ULONG samples = 0,
worst_case = 16;
printf("\nPercentage of samples correct in at least n bits of the "
"upper 16 Bits\n
");
for (i=0; i<= NUM_BITS; i++) {
samples += val_bits[i];
} /* for */
{
ULONG bad_sum = 0;
for (i=0; i <= NUM_BITS; i++) {
bad_sum += val_bits[i];
printf("%2d=%8.4f%% ", NUM_BITS-i,
(double) bad_sum / samples * 100.0);
if (!((i+1) % 5))
printf("\n
");
if (val_bits[i])
worst_case=i;
} /* for */
}
printf("\n%d Bits remain unchanged. Total %ld samples.\n",
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
16-worst_case, samples);
} /* if not quiet */
if (blockstatfile) {
fclose(blockstatfile);
fileerr("Couldn't close blockwise statistics file.\n",blockstatfile);
}
}
/* be tidy */
if (nlcfile) {
fclose(nlcfile);
fileerr("Could not NLC statistics file\n", nlcfile);
}
if (freqfile) {
fclose(freqfile);
fileerr("Could not close frequency output file\n", freqfile);
}
fclose(outfile);
fileerr("Couldn't close outputfile.\n",outfile);
fclose(infile);
fileerr("Couldn't close inputfile.\n",infile);
if (!hc_generate) {
if (stacked == 2) {
fclose(hcefile);
fileerr("Couldn't close huffman code lengths (escaped) file.\n",
hcefile);
}
fclose(hclfile);
fileerr("Couldn't close huffman code lengths file.\n",hclfile);
} /* if */
/* de-allocate memory */
if ( bstat_flag ) {
if (stacked == 1)
free( bstat_tab_h );
else {
free_tab2d( bstat_tab_l, bstat_size );
free( bstat_tab_h );
}
free_tab2d( sstat_tab, 1 );
}
if ( !no_stats ) {
free( pe_tab );
}
free( orig_block );
free( data_block );
free( rowindow );
free( riwindow );
free( lowindow );
free( liwindow );
if (nlc_flag) {
free( iobuffer_save );
free( rowindow );
free( riwindow );
free( lowindow );
free( liwindow );
}
free( iobuffer );
103
exit (EXIT_FAILURE);
cleanup_encoder( &encode_p );
cleanup_decoder( &encode_p );
}
if (debug & 1)
OUT2ERR("\nHappy end :-)\n");
return (0);
/* proclaim success */
} /* main */
/* ****************** functions ******************* */
void output_coeffs(ULONG *sum, ULONG size, char line,
FILE *fileptr)
{
ULONG i;
}
} /* for */
if (failed) {
fprintf(stderr,
"Scaling has to be inreased at least by factor=%.0f,
ld(factor)=%.0f\n",
too_much/(hsize-1), log(too_much/(hsize-1))/log(2.0)+1);
fprintf(stderr,
"Biggest value=%.2f, biggest allowed value=%ld\n",too_much,hsize-1);
exit (EXIT_FAILURE);
}
}
void sml_histo(double *block, ULONG blocksize, ULONG *sum, long hsize,
encoder_parm *par)
/*
History table for small values from 'block' into 'sum', limits given by
f_limits
*/
if (line)
for (i=0; i<size; i++) {
fprintf(fileptr,"%2.1f; %u\n",i/10.0,sum[i]);
fileerr("Error writing statistics", fileptr);
} /* endfor */
else
for (i=0; i<size; i++) {
fprintf(fileptr,"%u\n",sum[i]);
fileerr("Error writing statistics", fileptr);
} /* endfor */
{
ULONG i;
unsigned short point, lower, upper, bits, j;
upper = par->small_values;
lower = par->big_values;
bits = ABS(ldi(blocksize));
block += lower+1;
}
int input_coeffs(ULONG *sum, ULONG size, FILE *fileptr)
{
ULONG i;
int result;
for ( i=lower+1; i<=upper-bits; i += bits ) {
point = 0;
for (j=0; j<bits; j++) {
point >>= 1;
if ( fabs(*block) > 1.0 )
fprintf(stderr,
"Wrong limits for small values: no %u-%u = %.0f\n", i,
j, fabs(*block));
point |= (unsigned short) fabs(*block++) << bits;
}
(sum[point])++;
if (!sum[point]) {
/* overrun of table entry */
fprintf(stderr,"Sum too big: Nr. %u, freq=%u\n",point,i);
exit (EXIT_FAILURE);
}
} /* for */
for (i=0;i<size;i++) {
result = fscanf(fileptr,"%lu",sum++)!=1;
if (result) return (result);
}
return (0);
}
void histo(double *block, ULONG blocksize, ULONG *sum, long hsize)
{
ULONG i,point;
char failed=FALSE;
/* any errors ?
*/
double too_much=0.0;
}
for (i=0;i<blocksize;i++) {
point = fabs(*block++);
if (point>hsize-1) {
/* found value to be too much for table
*/
failed=TRUE;
if (too_much < point)
too_much=point;
}
else {
int perceptual_entropy(double *block, ULONG blocksize)
/* sum(0,512,(ldi(2*abs(quantwert)+1)))/512 */
{
ULONG i;
double pe=0;
for (i=0;i<blocksize;i++) {
pe += log((fabs(*block++)*2.0+1.0))/log(2.0);
}
return ((int) 10*pe/blocksize);
/* valid number
*/
(sum[point])++;
if (!sum[point]) {
/* overrun of table entry
}
*/
fprintf(stderr,"Sum too big: Nr. %u, freq=%u\n",point,i);
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
104
void pe_histo(ULONG pe_value, ULONG *pe_tab)
{
if (pe_value > PETAB-1) {
fprintf(stderr,
"PE value too big. factor=%d, ld(factor)=%.0f\n",
pe_value/PETAB, log(pe_value/PETAB)/log(2.0));
}
(pe_tab[pe_value])++;
}
if (size == 0)
worst_case = 0;
if (ok) {
if (output_file)
fprintf(output_file, " %3d\n", NUM_BITS - worst_case);
}
/* printf("vb: worst_case = %d\n", NUM_BITS - worst_case); */
return (NUM_BITS - worst_case);
} /* valid_bits */
void output_freqs(double *array, ULONG number, FILE *fileptr)
/* output an double array as long to fileptr, data has to be clipped */
{
long buffer;
do {
buffer = *array++;
if (fwrite(&buffer,sizeof(long),1,fileptr)!=1)
errormsg("Cannot write binary frequency value");
} while (--number);
}
int valid_bits(long *original, long *coded, unsigned short size,
FILE *output_file, ULONG *global_stat, int channels,
char ok)
/*
compares both blocks and returns number of unchanged bits in this block
*/
{
unsigned short i,
local_stat[NUM_BITS+1];
ULONG difference=0;
int worst_case=NUM_BITS;
memset(local_stat, 0, (NUM_BITS+1)*sizeof(unsigned short));
for (i=0; i<size; i++, original++, coded++) {
difference = (ULONG) (ABS( *original - *coded )) >> 16;
/*
if (i<20)
printf("vb: [%4d] o=%l16d -- c=%l16d\n",i,*original,*coded);
*/
if (difference)
difference = abs( ldi(difference) ) + 1;
if (difference > NUM_BITS)
difference = NUM_BITS;
local_stat[difference]++;
} /* for */
for (i=0; i<= NUM_BITS; i++) {
if (ok) {
global_stat[i] += local_stat[i];
if (output_file)
fprintf(output_file, "%3d ", local_stat[i]);
}
if (local_stat[i])
worst_case=i;
}
if (worst_case < 0)
worst_case = NUM_BITS;
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
105
8.1.15.
const char *usage_txt[] = {
"usage: %s inputfile [outputfile]\n",
"\tPrints info on inputfile.\n",
"\tWrites outputfile in wordorder of THIS computer, if outputfile is \n",
"\tgiven and inputfile has an incompatible wordorder.\n",
"\tYou may use '-' for stdin, stdout as filename\n",
VERSION_STRING,
""
};
sndchk.c
/*
Print information about .snd - file
Change word order if it is wrong
Robert Henke
Changed:
29. 7.92
9.11.92
if ((argc==1) || (argc>3))
/* check only or check & convert */
usage(usage_txt,argv[0]);
getarg_name(argc,argv,0,infilename);
getarg_name(argc,argv,1,outfilename);
Input: NeXT .snd - file (any word order)
Output: NeXT .snd - file for this computer
BUGS:
if (getarg_flag(argc,argv,'h') || getarg_flag(argc,argv,'?'))
usage(usage_txt,argv[0]);
*/
#include
#include
#include
#include
#include
/* after input of last parameter: */
getarg_illegals(argc, argv, usage_txt);
"sndutil.h"
"cliutil.h"
"miscutil.h"
<sys/types.h>
<sys/stat.h>
#define BLOCKSIZE
infile = openfile(infilename,"rb");
/* file statistics */
1024
char magicin[4],
magicout[4];
/* one data block */
/* position of magic bytes in input */
/* magic bytes on this computer */
/* check for byte order */
strncpy(magicstrg,(char *) &shdr.headerword,4);/* build testpattern */
magicstrg[4]=0;
/* terminate string */
strncpy(magicostrg,(char *) &int2strg,4);/* build reference */
magicostrg[4]=0;
/* terminate string */
int main (int argc, char *argv[])
{
/* function declarations */
int set_order(char *magicstrg, char *order);
void correct_array(unsigned long *array, unsigned long size);
next=set_order(magicstrg, magicin); /* byte order of inputfile */
set_order(magicostrg, magicout);
/* byte order of outputfile */
/* one inputfile, one outputfile (max) */
FILE *infile=NULL, *outfile=NULL;
char infilename[FNLEN]="",
outfilename[FNLEN]="";
/* one header for both files */
union header_union {
SNDSoundStruct snd;
unsigned long headerword[7];
} shdr;
struct stat file_statistics;
unsigned long int2strg=SND_MAGIC;
char magicstrg[5]="",
magicostrg[5];
double dvalue,
samples;
unsigned long items_read;
unsigned long iobuffer[BLOCKSIZE];
/* flags */
char next=1;
char not_my_format;
char not_EOF=TRUE;
/* read input header */
fread(&shdr.snd,sizeof(SNDSoundStruct),1,infile);
fileerr("Header could not be read",infile);
if (next) {
not_my_format=sndinfo(shdr.snd, infilename, TRUE);
if (not_my_format) {
fprintf(stderr,
"Warning! Magic number appears as \"%s\", should be \"%s\"\n",
magicstrg, magicostrg);
/* Header of snd-data */
/* for correct alignment in memory */
/* buffer for file statistics */
/* int equv. of ".snd"
/* for displaying magic
/* original magic for this machine
/* playing time
/* number of samples
*/
*/
*/
*/
*/
/* buffer for input/output */
/* valid NeXT - file */
/* .snd - file with dif. word order */
/* true until EOF */
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
/* re-order header bytes */
correct_array((unsigned long *) &shdr.headerword,6);
sndinfo(shdr.snd, "after reordering", TRUE);
if (outfilename[0]) {
/* convert if 2nd filename given */
printf("Outputting into %s\n",outfilename);
outfile = openfile(outfilename,"wb");
/* convert header, copy info, convert data */
fwrite((char *) &shdr.snd, sizeof(unsigned long), 6, outfile);
fileerr("Writing header",outfile);
/* copy info string */
fseek (infile, 6*sizeof(unsigned long), SEEK_SET);
items_read=fread((char *) iobuffer, sizeof(iobuffer[0]),
shdr.snd.dataLocation/sizeof(iobuffer[0])-6,infile);
fwrite((char *) iobuffer, sizeof(iobuffer[0]),
items_read,outfile);
fileerr("Writing info string",outfile);
/* convert data */
fseek (infile, shdr.snd.dataLocation, SEEK_SET);
106
while (not_EOF) {
/* read word 32 bits */
items_read=fread((long *) iobuffer,sizeof(iobuffer[0]),
BLOCKSIZE,infile);
not_EOF=BLOCKSIZE==items_read;
correct_array(iobuffer,BLOCKSIZE);
/* write word */
fwrite((char *)iobuffer,sizeof(iobuffer[0]),items_read,outfile);
} /* endwhile */
} else {
fprintf(stderr,
"For conversion please restart with an output file name.\n");
} /* endif outfilename[0] */
} /* if not_my_format */
dvalue=shdr.snd.dataSize*1.0/shdr.snd.channelCount;
if (next) {
switch (shdr.snd.dataFormat) {
case SND_FORMAT_MULAW_8:
case SND_FORMAT_LINEAR_8:
case SND_FORMAT_DSP_DATA_8: break;
case SND_FORMAT_LINEAR_16:
case SND_FORMAT_DSP_DATA_16:
dvalue /= 2.0;
break;
case SND_FORMAT_LINEAR_24:
case SND_FORMAT_DSP_DATA_24: dvalue /= 3.0;
break;
case SND_FORMAT_LINEAR_32:
case SND_FORMAT_DSP_DATA_32: dvalue /= 4.0;
break;
default:
dvalue = 0.0;
} /* case */
fprintf(stderr," Samples:
%.0f\n", samples = dvalue);
fprintf(stderr," Playing time:
%.2f sec\n",
dvalue/shdr.snd.samplingRate);
} /* endif next */
} /* if next */
/* get file size */
stat (infilename, &file_statistics);
dvalue = file_statistics.st_size;
if ((dvalue==shdr.snd.dataLocation+shdr.snd.dataSize) || (!next))
fprintf(stderr," File length %s:
%.0f bytes, %.1f blocks a 512
words\n",
(next) ? "ok" : "physically", dvalue, samples/512.0);
else
fprintf(stderr,
">>>> File length incorrect: %.0f instead of %u (diff=%.0f)\n",
dvalue, shdr.snd.dataLocation+shdr.snd.dataSize,
dvalue-shdr.snd.dataLocation-shdr.snd.dataSize);
/*
reorder a word according to the two global order arrays magicin, magicout
*/
{
return (((((old_value & (0xff << magicin[0])) >> magicin[0]) <<
magicout[0])|
(((old_value & (0xff << magicin[1])) >> magicin[1]) << magicout[1])|
(((old_value & (0xff << magicin[2])) >> magicin[2]) << magicout[2])|
((old_value & (0xff << magicin[3])) >> magicin[3]) << magicout[3]));
}
void correct_array(unsigned long *array, unsigned long size)
/* reorder array according using function correct */
{
unsigned long i;
for (i=0; i<size; i++) {
array[i]=correct(array[i]);
} /* endfor */
}
int set_order(char *magicstrg, char *order)
/* analyse magic word and record order of bytes in array order
input:
pointer to the magic word
output: pointer to the order-array in the form '.snd'
*/
{
char i;
char next_magic=TRUE;
/* all characters present */
for (i=0;i<4*8;i+=8) {
/* we count the bits in a byte */
switch (*(magicstrg+(i>>3))) {
case '.': order[0] = i;
break;
case 's': order[1] = i;
break;
case 'n': order[2] = i;
break;
case 'd': order[3] = i;
break;
default: next_magic=FALSE;
} /* endswitch */
} /* endfor */
return (next_magic);
}
/* be tidy */
if (outfile) {
fclose(outfile);
fileerr("Couldn't close outputfile.\n",outfile);
} /* endif outfile */
fclose(infile);
fileerr("Couldn't close inputfile.\n",infile);
return 0;
} /* main */
unsigned long correct(unsigned long old_value)
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
107
8.1.16.
const char *usage_txt[] = {
"usage: %s orig-file test-file [options]\n",
"\tLists differences between orig-file and test-file\n",
"\tboth can be either 16 or 32 bit linear format\n",
"\t-a file\tascii table of differences (format described in first
line)\n",
"\t-i\tignore last 512 samples\n",
"\t-i n\tignore last n samples\n",
"\t-n\twrite max. 100 samples to ascii file\n",
"\t-n n\twrite max. n samples to ascii file\n",
"\t-o n\toffset of 2nd file to first in bytes\n",
"\t-s file\tDifferences as 16 bit NeXT .snd file. Only upper 16
bits.\n",
"\t-v\tverbose mode: print more info\n",
"\tYou may use '-' for stdin, stdout as filename\n",
VERSION_STRING,
""
};
snddiff.c
/*
Differences between two .snd Files
Robert Henke
29. 7.92
Changed:
20.11.92
Args:
origfile, testfile, [-a difference file] [-s difference file] [-n n]
Formats: input: 16 bits linear, 32 bits linear
output: ASCII table, 16 bit linear .snd
BUGS,
to do:
*/
#include
#include
#include
#include
#include
"sndutil.h"
"cliutil.h"
"miscutil.h"
<sys/types.h>
<sys/stat.h>
/* first test for options, later for filenames */
verbose_flg=getarg_flag(argc,argv,'v');
/* file statistics */
int main (int argc, char *argv[])
{
/* asciifile */
resulta=getarg_option(argc, argv, 'a', diffilename);
FILE *origfile=NULL, *testfile=NULL,
*diffile=NULL, *difsndfile=NULL;
/* difference as ASCII and .snd */
char origfilename[FNLEN],
testfilename[FNLEN],
diffilename[FNLEN],
difsndname[FNLEN];
SNDSoundStruct snd1,
snd2,
snd3;
struct stat file1_statistics,
file2_statistics;
unsigned long i;
long act_value_lo,
act_value_lt,
offset=0;
short act_value_so,
act_value_st;
char short_word;
double difference;
short diffint;
long no_samples;
long no_diffs=0,
diff16=0;
int max_n=100;
int shorter=512;
short resulta, results;
char ubits,
uflag=FALSE,
lddiff;
char verbose_flg = FALSE;
/* space for filenames
/* original file header
/* test file header
/* difference sound file header
/* buffer for file statistics
/* actual value during compare */
/* offset 2nd to 1st file */
/* 16 bits, else 32 bits */
/*
/*
/*
/*
/*
if (getarg_flag(argc,argv,'h') || getarg_flag(argc,argv,'?'))
usage(usage_txt,argv[0]);
number of samples */
number of differences */
number of difference in 16 MSB */
maximum number of samples */
do not compare last n samples */
/* upper unchanged bits */
/* limit number of output lines ? */
/* ld(difference) */
/* be verbose */
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
*/
*/
*/
*/
*/
/* sndfile */
results=getarg_option(argc, argv, 's', difsndname);
/* must give 1 or 2 filenames, first: inputfile */
if (!getarg_name(argc, argv, 0, origfilename)) {
usage(usage_txt, argv[0]);
}
origfile=openfile(origfilename,"rb");
getarg_long_option(argc, argv, 'o', &offset);
/* testfile */
if (!getarg_name(argc, argv, 1, testfilename)) {
usage(usage_txt, argv[0]);
}
testfile=openfile(testfilename,"rb");
/* asciifile */
if (resulta) {
if (resulta<0) { /* build filename */
strcpy(diffilename,testfilename);
remove_extension(diffilename,".snd");
add_extension(diffilename,".asc");
if (verbose_flg)
printf("Writing differences in ascii to %s\n",diffilename);
}
diffile=openfile(diffilename,"w");
fprintf(diffile,"# pos| orig-test|ld| orig. file| test file\n");
}
/* sndfile */
if (results) {
108
if (results<0) { /* build filename if none given */
strcpy(difsndname,testfilename);
add_to_name(difsndname,".snd","d");
if (verbose_flg)
printf("Writing differences as .snd file to %s\n",difsndname);
}
difsndfile=openfile(difsndname,"wb");
}
/* read headers */
fread(&snd1,sizeof(SNDSoundStruct),1,origfile);
fileerr("Header could not be read",origfile);
if (sndinfo(snd1,origfilename, verbose_flg))
exit(EXIT_FAILURE);
fread(&snd2,sizeof(SNDSoundStruct),1,testfile);
fileerr("Header could not be read",testfile);
if (sndinfo(snd2,testfilename, verbose_flg))
exit(EXIT_FAILURE);
/* check validity */
switch (snd1.dataFormat)
{
case SND_FORMAT_LINEAR_16:
short_word = 1;
ubits = 16;
break;
case SND_FORMAT_LINEAR_32:
short_word = 0;
ubits = 32;
break;
default:
nuerr("Unknown .snd format (not 16/32 bits lin.), ID",
snd1.dataFormat);
} /* switch snd-format */
if (memcmp(&snd1.dataFormat,&snd2.dataFormat,4*sizeof(long))) {
if (!verbose_flg) {
sndinfo(snd1, origfilename, TRUE);
sndinfo(snd2, testfilename, TRUE);
errormsg(".snd format headers are not equal");
}
}
} /* else */
/* check for user limit to ascii output */
if (getarg_int_option(argc,argv,'n',&max_n) && (diffile)) {
if (verbose_flg)
printf("Writing up to %d differences to %s, only !\n",max_n,
diffilename);
uflag=TRUE;
}
/* check for user limit to filesize */
if (getarg_int_option(argc,argv,'i',&shorter)) {
if (no_samples > shorter)
no_samples -= shorter;
else {
shorter = no_samples;
/* comply with user's wish */
no_samples = 0;
uflag=TRUE;
}
if (verbose_flg)
printf("Ignoring the last %d samples, comparing %d samples.\n",
shorter, no_samples);
}
/* after input of last parameter: */
getarg_illegals(argc, argv, usage_txt);
/* position to data, set to default length */
fseek(origfile, snd1.dataLocation,SEEK_SET);
fileerr("Seek error in original file",origfile);
fseek(testfile, snd2.dataLocation+offset,SEEK_SET);
fileerr("Seek error in test file",testfile);
if (difsndfile) {
/* prepare and write header of difference-sound */
snd3.magic = SND_MAGIC;
snd3.dataLocation = sizeof(snd3);
snd3.dataSize = no_samples * sizeof(short) * snd1.channelCount;
snd3.dataFormat = SND_FORMAT_LINEAR_16;
snd3.samplingRate = snd1.samplingRate;
snd3.channelCount = snd1.channelCount;
strcpy(snd3.info,"dif");
fwrite(&snd3,sizeof(snd3),1,difsndfile);
}
}
else {
/* Calculate new data Size */
no_samples = (short_word) ? snd1.dataSize/sizeof(short)
: snd1.dataSize/sizeof(long);
if (short_word) {
for (i=0; i<no_samples; i++) {
if (fread(&act_value_so,sizeof(short),1,origfile)!=1) {
sndinfo(snd1, origfilename, TRUE);
sndinfo(snd2, testfilename, TRUE);
errormsg("Error reading original file");
}
if (fread(&act_value_st,sizeof(short),1,testfile)!=1) {
sndinfo(snd1, origfilename, TRUE);
sndinfo(snd2, testfilename, TRUE);
errormsg("Error reading test file");
}
difference = (double)act_value_so-(double)act_value_st;
if (difference!=0.0) {
lddiff = log(fabs(difference))/log(2.0)+1;
if (16-lddiff < ubits)
ubits = 16-lddiff;
if (diffile && ((no_diffs<max_n) || !uflag))
fprintf(diffile,"%6u %6.0f %2d %6d% 6d\n",i,difference,
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
109
/* check for equal length */
stat (origfilename, &file1_statistics);
stat (testfilename, &file2_statistics);
file2_statistics.st_size -= offset;
if (file1_statistics.st_size != file2_statistics.st_size) {
printf("WARNING: Files have different length ! Comparing until end of"
" shorter file.\n");
/* shorten data length */
no_samples = (file1_statistics.st_size < file2_statistics.st_size) ?
file1_statistics.st_size : file2_statistics.st_size;
no_samples = (short_word) ? no_samples/sizeof(short)
: no_samples/sizeof(long);
no_samples -= snd1.dataLocation;
lddiff,act_value_so,act_value_st);
no_diffs++;
}
if (difsndfile) {
clipping(&difference, (2<<15)-1);
diffint = difference;
if (fwrite(&diffint,sizeof(diffint),1,difsndfile)!=1)
errormsg("Error writing snd difference file");
}
}
} /* if short_word */
else {
for (i=0; i<no_samples; i++) {
if (fread(&act_value_lo,sizeof(long),1,origfile)!=1) {
sndinfo(snd1, origfilename, TRUE);
sndinfo(snd2, testfilename, TRUE);
errormsg("Error reading origfile");
}
if (fread(&act_value_lt,sizeof(long),1,testfile)!=1) {
sndinfo(snd1, origfilename, TRUE);
sndinfo(snd2, testfilename, TRUE);
errormsg("Error reading testfile");
}
difference = (double) act_value_lo - (double) act_value_lt;
if (difference!=0.0) {
lddiff = ABS ( ldi( fabs(difference) ) ) + 1;
if (32-lddiff < ubits)
ubits = 32-lddiff;
if (diffile && ((no_diffs<max_n) || !uflag))
fprintf(diffile, "%6u %11.0f %2.0f %11d %11d\n", i, difference,
log(fabs(difference))/log(2.0)+0.5,
act_value_lo,act_value_lt);
no_diffs++;
}
printf("Files are equal\n");
if (difsndfile) {
fclose(difsndfile);
fileerr("Couldn't close difference sound file.\n",difsndfile);
}
if (diffile) {
fclose(diffile);
fileerr("Couldn't close difference file.\n",diffile);
}
fclose(testfile);
fileerr("Couldn't close test file.\n",testfile);
fclose(origfile);
fileerr("Couldn't close original file.\n",origfile);
return EXIT_FAILURE;
/* a good end
*/
} /* main */
difference /= 65536.0; /* only 16 MSB significant */
clipping(&difference, (2<<15)-1);
diffint = difference;
if (diffint)
diff16++;
if (difsndfile) {
if (fwrite(&diffint,sizeof(diffint),1,difsndfile)!=1)
errormsg("Error writing snd difference file");
}
}
} /* else */
/* conclusion */
if (verbose_flg || no_diffs) {
printf("Differences between %s and %s: %u, that's %3.2f%%.\n",
origfilename, testfilename, no_diffs, 100.0*no_diffs/no_samples);
printf("The upper %d bits of %d bits are unchanged.\n",ubits,
(short_word) ? 16 : 32);
if (!short_word) {
if (diff16)
printf("Differences in the 16 MSBs: %u, that's %3.2f%%.\n",
diff16, 100.0*diff16/no_samples);
else
printf("Differences in the 16 MSBs: none\n");
}
}
else
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
110
8.1.17.
}
} /* if */
sndutil.c
if (fileptr==NULL) {
fprintf(stderr,
"Cannot open \"%s\" or \"%s.snd\" in working dir or
%s.\nAborted.\n",
oldname, oldname, snddir);
exit(EXIT_FAILURE);
}
return fileptr;
}
/*
Utilities for .snd file handling and manipulation
Robert Henke
Updated
*/
29. 7.92
5.11.92
#include <sndutil.h>
#include "defhenke.h"
#include "miscutil.h"
#define DEBUG FALSE
/* open file with extension ".snd", handle errors, return name and handle */
/* special filename '-' for stdin and stdout is recognized */
FILE *openfile(char *name, char *mode)
{
char oldname[FNLEN];
char snddir[FNLEN];
FILE *fileptr;
/* analyse .snd header, return error if invalid */
int sndinfo(SNDSoundStruct snd, char *name, char print)
{
short infolen;
char sndformats[][23]= {"unspecified","mu-law 8","linear 8 bit",
"linear 16 bit","linear 24 bit","linear 32 bit","float","double",
"indirect","nested","DSP core","DSP data 8","DSP data 16","DSP data
24",
"DSP data 32","UNKNOWN","display","mu-law squelch","emphasized",
"compressed","compressed emphasized","DSP commands",
"DSP commands samples"};
if (print)
fprintf (stderr,"Structure of %s\n",name);
if (snd.magic != SND_MAGIC){
fprintf(stderr,
"No NeXT \".snd\"-file, or unusual word order.\n");
return (-3);
}
if (!print)
return(0);
fprintf (stderr," Datalocation:
%u\n",snd.dataLocation);
fprintf (stderr," Data size (bytes): %u\n",snd.dataSize);
if ((snd.dataFormat>0) && (snd.dataFormat<23))
fprintf (stderr," Data Format:
%s\n",sndformats[snd.dataFormat]);
else
fprintf (stderr," Data Format:
%u (unknown)\n",snd.dataFormat);
fprintf (stderr," Sampling rate:
%u\n",snd.samplingRate);
fprintf (stderr," Channels:
%u\n",snd.channelCount);
infolen=strlen(snd.info);
fprintf (stderr," Infostring[%3d]:
%s\n",snd.dataLocation-6*4,
(infolen) ? snd.info : "<EMPTY STRING>");
return EXIT_SUCCESS;
#if DEBUG
fprintf(stderr,"OPENFILE: opening \"%s\", mode \"%s\"\n", name, mode);
#endif
if (!strcmp("-",name)) {
if (strchr(mode,'r'))
return stdin;
else
return stdout;
}
/* use stdin / stdout */
/* open as stdin */
strcpy(oldname,name);
/* first look in default directory, then in the SNDDIR directory */
fileptr=fopen(name,mode);
/* for reading also look for files with extension ".snd" */
if ((!fileptr) && (strchr(mode,'r'))) {
strcat(name,".snd");
fileptr=fopen(name,mode);
}
if (!fileptr) {
/* add .snd path if defined */
if (getenv("SNDDIR")) {
strcpy(snddir,getenv("SNDDIR"));
if (snddir[strlen(snddir)-1]!='/')
strcat(snddir,"/");
}
else
strcpy(snddir,"");
strcpy(name,snddir);
strcat(name,oldname);
fileptr=fopen(name,mode);
/* for reading also look for files with extension ".snd" */
if ((fileptr==NULL) && (strchr(mode,'r'))) {
strcat(name,".snd");
fileptr=fopen(name,mode);
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
}
111
8.1.18.
soundstruct.h
/*
*
soundstruct.h
*
Copyright 1988-89 NeXT, Inc.
*
* SNDSoundStruct - This data format for sound is used as the soundfile
* format, and also the "NeXT sound pasteboard type". It consists of a header
* and two variable length quantities: textual information (the info string)
* and raw data. The raw data starts past the info; the dataLocation is
* normally used to specify an offset from the beginning of the SNDSoundStruct
* structure to this data. The dataSize is the length of the raw data in bytes.
* The dataFormat, samplingRate, and channelCount further describe the data.
* The data itself may be anything; the format determines what the data
* actually means (i.e. sample data, dsp core structure).
* The magic number value may be used to determine the byte order of the data.
* The info string is any null-terminated data that the application may need
* (i.e. copyright information, textual description). The four bytes allocated
* are a minimum; the info string may extend any length beyond the structure.
*/
typedef struct {
int magic;
/* must be equal to SND_MAGIC */
int dataLocation;
/* Offset or pointer to the raw data */
int dataSize; /* Number of bytes of data in the raw data */
int dataFormat;
/* The data format code */
int samplingRate;
/* The sampling rate */
int channelCount;
/* The number of channels */
char info[4]; /* Textual information relating to the sound. */
} SNDSoundStruct;
#define
#define
#define
#define
SND_FORMAT_COMPRESSED
(19)
SND_FORMAT_COMPRESSED_EMPHASIZED (20)
SND_FORMAT_DSP_COMMANDS
(21)
SND_FORMAT_DSP_COMMANDS_SAMPLES
(22)
/*
* Sampling rates directly supported in hardware.
*/
#define SND_RATE_CODEC
(8012.8210513)
#define SND_RATE_LOW
(22050.0)
#define SND_RATE_HIGH
(44100.0)
/*
* The magic number must appear at the beginning of every SNDSoundStruct.
* It is used for type checking and byte ordering information.
*/
#define SND_MAGIC ((int)0x2e736e64)
/*
* NeXT data format codes. User-defined formats should be greater than 255.
* Negative format numbers are reserved.
*/
#define SND_FORMAT_UNSPECIFIED
(0)
#define SND_FORMAT_MULAW_8
(1)
#define SND_FORMAT_LINEAR_8
(2)
#define SND_FORMAT_LINEAR_16
(3)
#define SND_FORMAT_LINEAR_24
(4)
#define SND_FORMAT_LINEAR_32
(5)
#define SND_FORMAT_FLOAT
(6)
#define SND_FORMAT_DOUBLE
(7)
#define SND_FORMAT_INDIRECT
(8)
#define SND_FORMAT_NESTED
(9)
#define SND_FORMAT_DSP_CORE
(10)
#define SND_FORMAT_DSP_DATA_8
(11)
#define SND_FORMAT_DSP_DATA_16
(12)
#define SND_FORMAT_DSP_DATA_24
(13)
#define SND_FORMAT_DSP_DATA_32
(14)
#define SND_FORMAT_DISPLAY
(16)
#define SND_FORMAT_MULAW_SQUELCH (17)
#define SND_FORMAT_EMPHASIZED
(18)
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
112
8.1.19.
"\tGenerate test signal as NeXT 32 bit linear .snd file\n",
"\tOptions may be:\n",
"\t-a n\tamplitude: 1.0 (default) to 0.0\n",
"\t-c n\tchannels:
1 or 2 (mono / stereo)\n",
"\t-d n\tduration:
length in seconds\n",
"\t-f n\tfrequency: frequency in Hz (0...22050)\n",
"\t-r\treverse:
right = -1 * left\n",
"\t-t s|r\ttype:
sin or rectangular with max. amplitude\n",
"\tfile\tfilename
'.snd' is added automatically\n",
"\tDefaults: f=1000 Hz, n=sin1000m.snd, d=0.5 sec, c=1, t=sin, a=1.0\n",
VERSION_STRING,
""
testsig.c
/*
Output of testsignals: sinus and square wave;
parameters:
frequency, duration, mono or stereo (inverted right ch.)
filename
Robert Henke
Changed:
24.6.92
24.9.92
};
BUGS, to do:
*/
if (getarg_flag(argc,argv,'h') || getarg_flag(argc,argv,'?'))
usage(usage_txt,argv[0]);
char *int2ascii(int n, char *s);
reverse = (getarg_flag(argc,argv,'r')) ? -1 : 1;
#include "sndutil.h"
#include "cliutil.h"
#include "miscutil.h"
getarg_dbl_option(argc,argv,'a',&amplitude);
#define BIT16
FALSE
getarg_dbl_option(argc,argv,'f',&frequency);
getarg_dbl_option(argc,argv,'d',&time);
/* Output of 16-bit instead of 32 bit */
getarg_int_option(argc,argv,'c',&ch);
if ((ch<1) || (ch>2)) {
fprintf(stderr,"1 or 2 channels only. 1 is default.\n");
exit(1);
}
#if BIT16
#define WORDTYPE short
#define WORD_ID SND_FORMAT_LINEAR_16
#else
#define WORDTYPE long
#define WORD_ID SND_FORMAT_LINEAR_32
#endif
if (getarg_option(argc,argv,'t',tmpstr)>0) {
if (*tmpstr == 'r')
strcpy(form,"rec");
else if (*tmpstr == 's')
strcpy(form,"sin");
else
usage(usage_txt,argv[0]);
}
#define MAX_VAL (((unsigned WORDTYPE)(~0))>>1)
/* For simplicity the parameters are global */
/* for all parameters there are defaults */
int main (int argc, char *argv[])
{
void iserr(char *errstrg, FILE *fileptr);
unsigned long i, imax;
char freqstr[20]="";
double factor, tmpvalue;
long sampling_rate = SND_RATE_HIGH;
FILE *soundfile=NULL;
WORDTYPE sample;
SNDSoundStruct snd;
char tmpstr[FNLEN];
char reverse=FALSE;
double amplitude = 1.0;
double frequency = 1000.0;
double time = 0.5;
char filename[FNLEN] = "sinxxxx.snd";
int ch = 1;
char form[] = "sin";
/* loop counter */
/* Frequenz als String */
/* buffer for strings */
/* reverse phases */
/* 1.0 is full 32 bit range
/* frequency of signal generated
/* Laenge in Sekunden
/* Dateiname
/* mono or stereo
/* sine oder rectangle
const char *usage_txt[] = {
"Usage: %s [-option [optionargument]] [...] [file]\n",
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
*/
*/
*/
*/
*/
*/
if (!getarg_name(argc,argv,0,filename)) {
/* Create file name, if not given */
int2ascii((int) frequency, freqstr);
strcpy(filename,form);
strcat(filename,freqstr);
if (ch==1) strcat(filename,"m.snd");
else strcat(filename,"s.snd");
add_to_name(filename,".snd",(reverse==-1) ? "r" : "");
}
add_extension(filename,".snd");
/* after input of last parameter: */
getarg_illegals(argc, argv, usage_txt);
soundfile=fopen(filename,"wb");
if (soundfile == NULL) {
printf("Cannot open \"%s\". Aborted.\n",filename);
exit (EXIT_FAILURE);}
/* Abort */
printf("f=%g Hz, name=%s, dur=%g s, ch=%d, type=%s ampl=%1.3f, %s\n",
frequency,filename,time,ch,form,amplitude,(reverse==-1) ? "reverse" :
"" );
113
snd.magic = SND_MAGIC;
strcpy(snd.info,form);
snd.dataLocation = sizeof(snd);
snd.dataSize = (int) (sampling_rate*time*ch*sizeof(long));
snd.samplingRate = (int) sampling_rate;
snd.dataFormat = WORD_ID;
snd.channelCount = ch;
int i, sign;
int low, hi;
char c;
if ((sign=n) < 0) n = -n;
i = 0;
do {
s[i++] = n%10 + '0';
} while ((n /= 10) > 0);
if (sign < 0)
s[i++] = '-';
s[i] = 0;
fwrite(&snd,sizeof(snd),1,soundfile);
iserr("Header write",soundfile);
/* Werte berechnen:
Maximale Amplitude
*/
for (low=0, hi=i-1; low<hi; low++, hi--) {
c=s[low];
s[low]=s[hi];
s[hi]=c;
imax = sampling_rate*time;
factor = frequency/sampling_rate*M_PI*2;
amplitude *= (double) MAX_VAL;
if (form[0]=='s') {
factor = frequency/sampling_rate*M_PI*2;
for (i=0;i < imax; i++) {
tmpvalue = sin(i*factor) * amplitude;
tmpvalue = (tmpvalue < 0.0) ? (tmpvalue-0.5) : (tmpvalue+0.5);
sample = (WORDTYPE) tmpvalue;
fwrite (&sample,sizeof(sample), 1, soundfile);
iserr("Write value",soundfile);
if (ch==2) {
sample *= reverse;
fwrite (&sample,sizeof(sample), 1, soundfile);
iserr("Write value",soundfile);
}
}
} /* if ... sinus */
else {
factor = sampling_rate/frequency;
for (i=0;i < imax; i++) {
sample = (WORDTYPE)
(i % (unsigned) factor << 1) > (unsigned) factor ?
((WORDTYPE) amplitude) : ((WORDTYPE) -amplitude);
fwrite (&sample,sizeof(sample), 1, soundfile);
if (ch==2) {
sample *= reverse;
fwrite (&sample,sizeof(sample), 1, soundfile);
iserr("Write value",soundfile);
}
}
} /* else ... rechteck */
fclose(soundfile);
return EXIT_SUCCESS;
}
return(s);
}
}
void iserr(char *errstrg,FILE *fileptr)
{
if (ferror(fileptr)) {
fprintf(stderr,"%s error #%d. Aborted.\n",errstrg,ferror(fileptr));
exit (EXIT_FAILURE);
}
}
char *int2ascii(int n, char *s)
{
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
114
8.2. Shell - Scripts
Mit den folgenden Scripts wurden die ein- und
zweidimensionalen Tabellen generiert, sowie anschließend die
Codierqualität ermittelt.
Generierung eindimensionaler Tabellen
#
#
#
#
#
#
#
#
#
isotables1 -- 23.11.92 R.Henke
generate 1D huffman code tables for selected bitrates and iso music samples
call example:
isotables '4 5 6' 64
Don't use decimal fractions, e.g. don't use "isotables '4.5'" 64
ksh: for batches use
batch
ksh /users/henke/bin/isotables1 '15 1' 64
^D
if [[ $# -ne 2 ]]
then
echo "$# is wrong parameter count: isotables1 'bitsrates' tablesize"
echo "e.g. isotables '4 5 6' 64"
exit
fi
cd $SNDDIR
for bitrate in $1
do
echo ==============================================================
echo
Starting for bitrate $bitrate
echo ==============================================================
echo Generating first start table
date
echo rm isotab_$2_1_$bitrate.bg1
rm isotab_$2_1_$bitrate.bg1
for isofile in *iso*32.snd
do
# generate first huffman code table
echo pacse $isofile /dev/null -S1 -t18 -r$bitrate -cisotab_$2_1_$bitrate
\
-sisotab_$2_1_$bitrate -g
pacse $isofile /dev/null -S1 -t18 -r$bitrate -cisotab_$2_1_$bitrate \
-sisotab_$2_1_$bitrate -g
done
echo
echo huffgen isotab_$2_1_$bitrate.bg1 -lisotab_$2_1_$bitrate.hcl -b$2 -S
huffgen isotab_$2_1_$bitrate.bg1 -lisotab_$2_1_$bitrate.hcl -b$2 -S
echo rm isotab_$2_1_$bitrate.bg1
rm isotab_$2_1_$bitrate.bg1
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
# refine table
(( i_counter = 1 ))
while [[ i_counter -lt 6 ]]
do
echo
echo Refining ... step $i_counter
for isofile in *iso*32.snd
do
# generate refined huffman code table
echo pacse $isofile /dev/null -S1 -t18 -r$bitrate \
-cisotab_$2_1_$bitrate -sisotab_$2_1_$bitrate
pacse $isofile /dev/null -S1 -t18 -r$bitrate -cisotab_$2_1_$bitrate\
-sisotab_$2_1_$bitrate
done # for
echo huffgen isotab_$2_1_$bitrate.bg1 -lisotab_$2_1_$bitrate.hcl -b$2 -S
huffgen isotab_$2_1_$bitrate.bg1 -lisotab_$2_1_$bitrate.hcl -b$2 -S
(( i_counter = i_counter + 1 ))
echo rm isotab_$2_1_$bitrate.bg1
rm isotab_$2_1_$bitrate.bg1
date
done # while
echo
done # for bitrate
echo Done !
Generierung zweidimensionaler Tabellen
#
#
#
#
#
#
#
#
#
isotables2 -- 23.11.92 R.Henke
generate 2D huffman code tables for selected bitrates and iso music samples
call example:
isotables '4 5 6' 64
Don't use decimal fractions, e.g. don't use "isotables '4.5'" 64
ksh: for batches use
batch
ksh /users/henke/bin/isotables '15 1' 64
^D
if [[ $# -ne 2 ]]
then
echo "$# is wrong parameter count: isotables 'bitsrates' tablesize"
echo "e.g. isotables '4 5 6' 64"
exit
fi
cd $SNDDIR
for bitrate in $1
do
echo ==============================================================
echo
Starting for bitrate $bitrate
echo ==============================================================
echo Generating first start table
date
echo rm isotab_$2_2_$bitrate.bg?
rm isotab_$2_2_$bitrate.bg?
for isofile in *iso*32.snd
do
# generate first huffman code table
echo pacse $isofile /dev/null -S2 -t$2 -r$bitrate -cisotab_$2_2_$bitrate
\
-sisotab_$2_2_$bitrate -g
pacse $isofile /dev/null -S2 -t$2 -r$bitrate -cisotab_$2_2_$bitrate \
115
-sisotab_$2_2_$bitrate -g
done
echo
echo huffgen isotab_$2_2_$bitrate.bge -lisotab_$2_2_$bitrate.hce
huffgen isotab_$2_2_$bitrate.bge -lisotab_$2_2_$bitrate.hce
echo huffgen isotab_$2_2_$bitrate.bgl -lisotab_$2_2_$bitrate.hcl
huffgen isotab_$2_2_$bitrate.bgl -lisotab_$2_2_$bitrate.hcl
echo rm isotab_$2_2_$bitrate.bg?
rm isotab_$2_2_$bitrate.bg?
# refine table
(( i_counter = 1 ))
while [[ i_counter -lt 6 ]]
do
echo
echo Refining ... step $i_counter
for isofile in *iso*32.snd
do
# generate refined huffman code table
echo pacse $isofile /dev/null -S2 -t$2 -r$bitrate \
-cisotab_$2_2_$bitrate -sisotab_$2_2_$bitrate
pacse $isofile /dev/null -S2 -t$2 -r$bitrate -cisotab_$2_2_$bitrate\
-sisotab_$2_2_$bitrate
done # for
echo huffgen isotab_$2_2_$bitrate.bge -lisotab_$2_2_$bitrate.hce
huffgen isotab_$2_2_$bitrate.bge -lisotab_$2_2_$bitrate.hce
echo huffgen isotab_$2_2_$bitrate.bgl -lisotab_$2_2_$bitrate.hcl
huffgen isotab_$2_2_$bitrate.bgl -lisotab_$2_2_$bitrate.hcl
(( i_counter = i_counter + 1 ))
echo rm isotab_$2_2_$bitrate.bg?
rm isotab_$2_2_$bitrate.bg?
date
done # while
echo
done # for bitrate
echo Done !
Simulation mit eindimensionalen Tabellen
# isoquality1 -- 14.12.92 Robert Henke
# Measure quality of coding as differences and per NMR_D at
# selected bitrates with iso music samples for 1D tables
if [[ $# -ne 2 ]]
then
echo "$# ist wrong parmeter count -- use 2"
echo "isoquality1 'bitrates' tablesize"
exit
fi
echo File: $isofile, Tablesize: $2 '(1D)', Bitrate: $bitrate
pacse $isofile isotest1_$2_$bitrate.snd -S1 -t18 -r$bitrate \
-cisotab_$2_1_$bitrate -CP${isofile%%32.snd}_1_$2_$bitrate.asc
nmr_d $isofile isotest1_$2_$bitrate.snd 690 -b7 -p5 -m2 |\
awk '/NMR:/ { print $2 }' > P${isofile%%32.snd}_1_$2_$bitrate.nmr
rm isotest1_$2_$bitrate.snd
done # for
echo
done # for bitrate
echo Done !
Simulation mit zweidimensionalen Tabellen
# isoquality2 -- 15.12.92 Robert Henke
# Measure quality of coding as differences and per NMR_D at
# selected bitrates with iso music samples for 2D tables
if [[ $# -ne 2 ]]
then
echo "$# ist wrong parmeter count -- use 2"
echo "isoquality2 'bitrates' tablesize"
exit
fi
cd $SNDDIR
for bitrate in $1
do
echo ==============================================================
echo
Starting for bitrate $bitrate
echo ==============================================================
# generate sample, compare it
date
for isofile in liso1632.snd liso532.snd liso632.snd liso332.snd
do
echo File: $isofile, Tablesize: $2*$2, Bitrate: $bitrate
pacse $isofile isotest2_$2_$bitrate.snd -S1 -t18 -r$bitrate \
-cisotab_$2_2_$bitrate -CP${isofile%%32.snd}_2_$2_$bitrate.asc
nmr_d $isofile isotest2_$2_$bitrate.snd 690 -b7 -p5 -m2 | \
awk '/NMR:/ { print $2 }' > P${isofile%%32.snd}2_$2_$bitrate.nmr
rm isotest2_$2_$bitrate.snd
done # for
echo
done # for bitrate
echo Done !
cd $SNDDIR
for bitrate in $1
do
echo ==============================================================
echo
Starting for bitrate $bitrate
echo ==============================================================
# generate sample, compare it
date
for isofile in liso1632.snd liso532.snd liso632.snd liso332.snd
do
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
116
8.3. Huffman Code Tabellen
8.3.1.
Neu generierte Tabellen
Die in dieser Arbeit generierten Tabellen sind zu zahlreich und zu umfangreich, um
abgedruckt zu werden. Sie liegen auf Datenträger vor. Die Untersuchungen benutzten nur
die Längeninformationstabellen.
Eindimensionale Tabellen wurden nach dem Schema
isotab_tabellengröße_1_bitrate.hcl benannt.
Zweidimensionale Tabellen bestehen aus der Tabelle H2Low, die nach dem Schema
isotab_tabellengröße_2_bitrate.hcl benannt wurden, und den Tabellen H2Esc, die nach
dem Schema isotab_tabellengröße_2_bitrate.hce benannt wurden.
8.3.2.
Übernommene Tabellen
Aus [12], Annex, Table B8, wurden aus der Beschreibung des Layer III folgende zwei
Tabellen übernommen:
8.3.2.1.
Quadrupel-Tabelle A
Wert
Länge
0000
0001
0010
0011
8.3.2.2.
Quadrupel-Tabelle B
Code
Wert
Länge
Code
1
1
0000
4
1111
4
0101
0001
4
1110
4
0100
0010
4
1101
5
00101
0011
4
1100
0100
4
0110
0100
4
1011
0101
6
000101
0101
4
1010
0110
5
00100
0110
4
1001
0111
6
000100
0111
4
1000
1000
4
0111
1000
4
0111
1001
5
00011
1001
4
0110
1010
5
00110
1010
4
0101
1011
6
000000
1011
4
0100
1100
5
00111
1100
4
0011
1101
6
000010
1101
4
0010
1110
6
000011
1110
4
0001
1111
6
000001
1111
4
0000
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
117
8.4. Quellen
[1]
"C Language Reference" sowie
"Run-Time Library Reference", beide zu
Microsoft C/C++ Version 7.0
Microsoft Corporation 1992
[2]
"Philips-Compact-Schallplatte wurde in Japan vorgeführt"
Funkschau 24/80, S. 46
[3]
"Tonstudio-PCM: dem Standard näher"
Funkschau 4/79, S. 12
[4]
Brandenburg, Karlheinz; Johnston James D.
"Wideband Coding - Perceptual Considerations for Speech and Music"
[5]
Brandenburg, Karlheinz; Herre, Jürgen
"Digital Audio Compression for Professional Applications",
1992, AES Preprint 3330
[6]
Brandenburg, Karlheinz; Stoll, Gerhard
"The ISO/MPEG-Audio Codec: A Generic Standard for Coding of High
Quality Audio",
1992, AES Preprint 3336
[7]
Brandenburg, Karlheinz
"Ein Beitrag zu den Verfahren und der Qualitätsbeurteilung für hochwertige
Musikcodierung"
Dissertation, Erlangen 1989
[8]
Brandenburg, Karlheinz
"Evaluation of quality for audio encoding at low bit rates",
1987, AES Preprint 2433
[9]
Brandenburg, Karlheinz; Sporer, Thomas
" 'NMR' and 'masking flag' - Evaluation of Quality using Perceptual
Criteria"
Paper, Universität Erlangen-Nürnberg
[10]
Edler, Bernd
"An Overlapping Block Transform for Source Coding with a CoefficientRate equal to the Sampling-Rate"
Paper, Institut für Theoretische Nachrichtentechnik und
Informationsverarbeitung, Universität Hannover
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
118
[11]
Jayant, N.S.; Noll, Peter
Digital Coding Of Waveforms
Principles and Applications to Speech and Video
Prentice-Hall Inc., Englewood Cliffs, 1984
[12]
Layer III ISO Standard 11172-3
"Coding of Moving Pictures and Associated Audio for Digital Storage
Media at up to about 1.5 Mbit/s"
Part 3, Audio als Draft vom 18.12.90
3-Annex C vom 24.4.92
3-2 Technical Normative Elements vom 22.11.91
[13]
Miegler, Max;
Unterprogramme FFT.C im Rahmen der Studienarbeit "Erstellung von
Simulationsprogrammen zur Demonstrierung von einfachen
Audiokodierverfahren", Lehrstuhl für technische Elektronik, Universität
Erlangen-Nürnberg, 1992
[14]
Nedden, zur, Herbert
"Squeeze, LZH & Co."
c't 7/92, S. 231-238
[15]
Oppenheim, Alan V.; Schafer, Ronald W.
"Digital Signal Processing"
Prentice-Hall 1975
[16]
Oppenheim, Alan V.; Willsky Alan S.
"Signale und Systeme" Lehrbuch
VCH Verlagsgesellschaft Weinheim 1990
[17]
Press, W.H; Flannery, B. P.; Teukolsky, S. A.; Vetterling, W. T.
"Numerical Recipes in C, The Art of Scientific Computing"
Cambridge University Press, Cambridge USA (5) 1990
ISBN 0-521-35465-X
[18]
Princen, J.P.; Johnson, A.W.; Bradley, A. B.
"Subband/Transform Coding Using Filter Bank Designs Based on Time
Domail Aliasing Cancellation"
IEEE 1987
[19]
Rabiner, L. R.; Schafer, R.W.
"Digital Processing of Speech Signals"
Prentice-Hall Inc., Englewood Cliffs, 1978
[20]
Schrüfer E.
"Signalverarbeitung, Numerische Verarbeitung digitaler Signale"
Carl Hansa Verlag München Wien 1990
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
119
[21]
Sporer, Thomas;
"Realisierung einer OCF in Integer-Arithmetik"
Diplomarbeit, Erlangen 1988
[22]
Sporer, Thomas; Brandenburg, Karlheinz; Edler, Bernd
"The use of multirate filter banks for coding of high quality digital audio"
[23]
Vanderkooy, John; Lipshitz Stanley P.
"Digital Dither: Signal Processing with Resolution Far Below the Least
Significant Bit"
The Procedings of The AES 7th International Conference
AES New York
[24]
Zwicker, E.
Psychoakustik
Springer-Verlag Berlin-Heidelberg-New York 1982
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
120
8.5. Liste der verwendeten Musikstücke
Die Stücke bestehen jeweils aus 2 Dateien für den linken und rechten Kanal. Sie sind
mit 48 kHz PCM Code aufgezeichnet worden, und besizten eine Wortbreite von
16 Bit/ATW.
Kurzbezeichnung
Länge in
Abtastwerten
ganze Blöcke à
512 Abtastwerten
Beschreibung
iso2
358272
699
Fagott
iso3
614400
1200
Viola
iso4
358272
699
Kastagnetten
iso5
783360
1530
Glockenspiel
iso6
360960
705
Frauenstimme
iso7
388992
759
Männerstimme
iso8
604032
1174
Orgel
iso9
505344
987
Flöte
iso10
557952
1089
Mondscheinsonate
iso11
529920
1035
Cabassa
iso12
491520
960
Sopran und Oboe
iso13
726912
1419
Trompeten
iso15
875520
1710
Glockenspiel
iso16
488832
954
Suzanne Vega
Robert Henke, Simulation eines Audio-Codierverfahrens für professionelle Anwendungen
121

Similar documents