Hlavička MP3 souboru - Builder.cz - Informacni server o programovani

Odběr fotomagazínu

Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!

 

Zadejte Vaši e-mailovou adresu:

Kamarád fotí rád?

Přihlas ho k odběru fotomagazínu!

 

Zadejte e-mailovou adresu kamaráda:

Soutěž

Sponzorem soutěže je:

IDIF

 

Kdo je autorem výstavy obrazových fotografií „Očima Hanse Christiana Andersena“?

V dnešní soutěži hrajeme o:



C/C++

Hlavička MP3 souboru

21. května 2001, 00.00 | MP3 je snad nejrevolučnější kódování audia, které člověk vymyslel. V tomto seriálu si představíme základní principy dekódování hudebních dat, hlavičky a jiných informací, potřebných pro práci s tímto, bezpochyby skvělým formátem!

MP3. Snad nejrevolučnější kódování audia, které člověk vymyslel. V tomto seriálu si představíme základní principy dekódování hudebních dat, hlavičky a jiných informací, potřebných pro práci s tímto, bezpochyby skvělým formátem.
Pochopení ukázek vyžaduje alespoň základní znalosti programování v C++ a znalost významu hlavičkových souborů. Z důvodu rozsáhlosti tohoto seriálu nebudu uveřejňovat kompletní zdrojové soubory jako přílohu, ale pouze ty části, které se budou týkat příslušného dílu (zdrojové soubory budou v průběhu seriálu vzájemně navazovat).

Začneme tedy pěkně od začátku. V tomto díle si povíme něco o informacích skrytých v několika málo bytech hlavičky MP3 souboru. Konkrétně se jedná o 4 byty, ve kterých je uloženo vše co bude náš dekoder potřebovat pro zajištění správného dekódování. Jako první si musíme otevřít soubor, který budeme chtít dekódovat :

HANDLE StreamHandle = CreateFile(InName, GENERIC_READ, FILE_SHARE_READ,&Sec, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL);

Kde InName je jméno vstupního souboru a Sec je adresa proměnné typu struct SECURITY_ATTRIBUTES (viz nápověda kompilátoru).

Jak jsme si již řekli, hlavička souboru má velikost 4 byty.

Následující tabulka nám ukáže obsazení jednotlivých bitů hlavičky informacemi, které budeme dále dekódovat.

offset (bit) velikost (bit) obsah
00 12 sync header
12 1 version
13 2 layer
15 1 error protection
16 4 bit rate
20 2 sample rate
22 1 padding
23 1 extension
24 2 channel mode
26 2 mode extension
28 1 copyright
29 1 original
30 2 emphasis

Pro uložení těchto dat si musíme zvolit proměnnou typu unsigned long a to z důvodu využití plné škály od čísla 0 (mínusové hodnoty by nám zmenšily schonost pojmout informace na polovinu). Následující funkcí si tyto informační byty načteme :

int head_read(unsigned char *hbuf,unsigned long *newhead)
{
   long numread;
   bool res;
   res=ReadFile(StreamHandle, hbuf, 4, (DWORD *)&numread, NULL);
   if(!res||numread!=4) return FALSE;
  
   /* Data musí být uloženy jako unsigned. Proto musíme použít bitové kopírování */
   *newhead=((unsigned long) hbuf[0] << 24) |
   ((unsigned long) hbuf[1] << 16) |
   ((unsigned long) hbuf[2] << 8) |
   (unsigned long) hbuf[3];
   return TRUE;
}

Kde je handle na již otevřený MP3 soubor (viz. výše). V proměnné newhead máme tedy "nějaké 4 byty", ale co s nimi ? Každý programátor, který analyzuje nějaká data by měl mít jedno základní pravidlo : Před použitím dat zkontrolovat, zda jsou korektní !. Další krok tedy vede samozřejmě ke kontrole těchto dat. Kontrolu musíme provést na bitové úrovni a v podstatě kontrolujeme zda se při dekódování hlavičky budou používat "korektní" data (jako kdyby jsme ji "předdekódovali"). Provádět tuto kontrolu je lepší než ošetřovat možné chyby při samotném dekódování.

int head_check(unsigned long head)
{
   if ((head & 0xffe00000) != 0xffe00000) return FALSE;
   if ((head & 0xffff0000) == 0xffff0000) return FALSE;
   if(!((head>>17)&3)) return FALSE;
   if( ((head>>12)&0xf) == 0xf) return FALSE;
   if( ((head>>10)&0x3) == 0x3 ) return FALSE;
   return TRUE;
}

Nyní, pokud kontrola proběhla v pořádku, můžeme přistoupit k samotnému dekódování hlavičky. Budeme potřebovat následující definice, které slouží k uložení informací z hlavičky :

struct al_table { short bits; short d; };

Struktura frame nám poslouží jako "zastřešující záznam", který bude obsahovat všechna dekódovaná data.

struct frame
{
   const struct al_table *alloc;
   long stereo;
   long jsbound;
   long single;
   long II_sblimit;
   long lsf;
   long mpeg25;
   long down_sample;
   long header_change;
   unsigned long block_size;
   long lay;
   long WhatLayer;
   long error_protection;
   long bitrate_index;
   long sampling_frequency;
   long padding;
   long extension;
   long mode;
   long mode_ext;
   long copyright;
   long original;
   long emphasis;
};

Kompletní seznam datových toků pro každý layer.

// Definice datových toků
const int BitRateIndex[2][3][16]=
{{{0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,}, {0,32,48,56,64,80,96,112,128,160,192,224,256,320,384,}, {0,32,40,48,56,64,80,96,112,128,160,192,224,256,320,}}, {{0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,}, {0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,},
{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,}}};

Musíme mít také připraven seznam možných (standartních) frekvencí.

// Definice frekvencí
const long freqs[9] = { 44100, 48000, 32000, 22050, 24000, 16000 , 11025 , 12000 , 8000 };

A konečně deklarace proměnných a definic.

struct frame fr;
unsigned long oldhead,framesize;

#define MPG_MD_STEREO 0
#define MPG_MD_JOINT_STEREO 1
#define MPG_MD_DUAL_CHANNEL 2
#define MPG_MD_MONO 3

Nyní můžeme přistoupit k vlastnímu dekódování hlavičky. Zjištěné informace si uložíme do předem vytvořených proměnných (viz. výše). Pro případ změny hlavičky (hlavička se totiž přidává před každý "frame". Ale o tom až příště) si budeme vždy pamatovat i tu předcházející z důvodu urychlení (pokud se hlavičky neliší, není nutné novou hlavičku dekódovat).

A nyní již samotná funkce pro dekódování hlavičky

int decode_header(unsigned long newhead)
{
if( newhead & (1<<20) )
{
   fr.lsf = (newhead & (1<<19)) ? 0x0 : 0x1;
   fr.mpeg25 = 0;
}
else
{
   fr.lsf = 1;
   fr.mpeg25 = 1;
}

if (!oldhead) // původní hlavička
{
   fr.lay = 4-((newhead>>17)&3);
   if( ((newhead>>10)&0x3) == 0x3) return 0;
   if (fr.mpeg25) fr.sampling_frequency = 6 + ((newhead>>10)&0x3);
   else
   fr.sampling_frequency = ((newhead>>10)&0x3) + (fr.lsf*3);
   if (fr.sampling_frequency > 8) return 0;
   fr.error_protection = ((newhead>>16)&0x1)^0x1;
}

fr.error_protection = ((newhead>>16)&0x1)==0 ? 1: 0;
fr.bitrate_index = ((newhead>>12)&0xf);
fr.padding = ((newhead>>9)&0x1);
fr.extension = ((newhead>>8)&0x1);
fr.mode = ((newhead>>6)&0x3);
fr.mode_ext = ((newhead>>4)&0x3);
fr.copyright = ((newhead>>3)&0x1);
fr.original = ((newhead>>2)&0x1);
fr.emphasis = newhead & 0x3;
fr.stereo = (fr.mode == MPG_MD_MONO) ? 1 : 2;
oldhead = newhead;

if(!fr.bitrate_index)
   { return (0); }
fr.WhatLayer = fr.lay;

/*Podle Layeru si zjistíme datový tok a následně velikost "frame" */
switch(fr.lay)
{
case 1:   fr.jsbound = (fr.mode == MPG_MD_JOINT_STEREO) ? (fr.mode_ext<<2)+4 : 32;
   framesize = (long) BitRateIndex[fr.lsf][0][fr.bitrate_index] * 12000;
   framesize /= freqs[fr.sampling_frequency];
   framesize = ((framesize+fr.padding)<<2)-4;
   break;
case 2:
   fr.jsbound = (fr.mode == MPG_MD_JOINT_STEREO) ? (fr.mode_ext<<2)+4 : fr.II_sblimit;
   framesize = (long) BitRateIndex[fr.lsf][1][fr.bitrate_index] * 144000;
   framesize /= freqs[fr.sampling_frequency];
   framesize += fr.padding - 4;
   break;
case 3:    if(fr.lsf) ssize = (fr.stereo == 1) ? 9 : 17;
   else ssize = (fr.stereo == 1) ? 17 : 32;
   if (fr.error_protection) { ssize += 2; }
   framesize = (long) BitRateIndex[fr.lsf][2][fr.bitrate_index] * 144000;
   framesize /= freqs[fr.sampling_frequency]<<(fr.lsf);
   framesize = framesize + fr.padding - 4;
   break;
default: return (0);
}

return 1;
}

Myslím že obsah této lekce je již vyčerpán a dostatečně objasněn. V příštím díle si ukážeme MP3 soubor z "opačného" konce. Povíme si tedy něco o popisu skladby, o tzv. ID3 Tagu.

Zde si můžete stáhnou zdrojové kódy této lekce.

Obsah seriálu (více o seriálu):

Tématické zařazení:

 » Rubriky  » C/C++  

 

 

 

Nejčtenější články
Nejlépe hodnocené články

 

Přihlášení k mému účtu

Uživatelské jméno:

Heslo: