V článku budu popisovat jednotlivé kroky podrobněji a uvádět větší množství odkazů do dokumentace, protože předpokládám čtenáře začatečníka.
Moc dobře si uvědomuji, že pokud zrovna neprogramujete hudební přehrávač je použití mojí třídy omezené, ale třídu jsem koncipoval tak, aby se dala jednoduše použít do každého projektu, kde je např. potřeba zvuk pro upomínky nebo zkrátka použít libolný zvuk.
Motivace
Hned na úvod bych rád zmínil pár vlastností, které jsem si na začátku stanovil:
- Třída musí být velmi jednoduše a rychle použitelná
- Vše v jednom souboru, který se dá jednoduše přidat do projektu
- Musí svými činnostmi vyvolávat události
- K dispozici by měla informace o skladbě (ID3) nezávisle na instanci hlavní třídy a měla by jít zapisovat
- Všechny metody dobře okomentované, aby byla jasná jejich funkčnost a přehledně rozčleněn kód
Ano, tyhle vlastnosti by měla splňovat každá napsaná třída, ale je s podivem kolik jich je takových, které moje požadavky vůbec nesplňují.
Základní princip
Operační systém Windows je sám o sobě schopen přehrát mp3 soubor a to přes Windows Media system (winmm.dll), obsahují funkci mciSendString, pomocí které lze posílat příkazy do multimediálních zařízení a získávat na ně odpovědi. Funkce má následující prototyp:
MCIERROR mciSendString( LPCTSTR lpszCommand, LPTSTR lpszReturnString, UINT cchReturn, HANDLE hwndCallback );
lpszCommand - je vstupní řetězec s příkazem
lpszReturnString - je výstupní řetězec
cchReturn - je maximální délka řetězce
Ostatní parametry nepoužívám.
Pro import této funkce použijeme atribut DllImport z namespace System.Runtime.InteropServices.
[DllImport("winmm.dll")]
private static extern long mciSendString(
string lpstrCommand,
StringBuilder lpstrReturnString,
int uReturnLength,
int hwndCallback);
Tímto si zpřístupníme tuto funkci v managed kódu. Ještě pro malé upřesnění: Pro návratovou hodnotu, se používá třída StringBuilder, která se většinou používá tam, kde by se v C++ použil ukazatel na řetězec.
Příkazy pro ovládání přehrávání
Celkový souhrn příkazů, které funkce mciSendString podporuje je dispozici v tomto seznamu. Každý příkaz je vázán na tzv. alias - textový jednoznačný identifikátor právě přehrávaného souboru. Ve všech příkladech, které jsem na internetu našel se vždy používal stejný indentifikátor pro všechny instance tříd pro přehrávání. Moje řešení je trochu více univerzální. Používám alias, který je jednoznačný pouze pro aktuální instanci mojí třidy Mp3File. Tím je zajišteno možnost přehrávat a pracovat s více soubory současně.
Abych dosáhl jednoznačnosti aliasu pro každou instanci třídy, použil jsem Guid. Inicializace členské proměnné vypadá takto:
private string m_Alias = Guid.NewGuid().ToString();
Pojďme si nyní probrat jednotlivé příkazy, které se posílají do výše zmíněné funkce mciSendString a které ve své třídě používám. Popis bude jen k těm důležitejším, protože ostatní si ani nezaslouží komentář.
open "[název souboru]" type mpegvideo alias [alias]
Otevře soubor a přiřadí mu uvedený alias.
status [alias] mode
Dotaz na stav souboru. V tomto připadě funkce vrací stavy "stopped", "playing", "paused" a nebo prázdný řetězec, pokud alias neexistuje.
status [alias] length status [alias] position
Vrací celkovou délku souboru v aktuální pozici v souboru v milisekundách.
seek [alias] to [čas v milisekundách]
Je příkaz, který nastaví soubor na danou pozici. Má ale jednu nevýhodu. Nastaví stav souboru na "stopped", což není žádoucí. Většinou chceme po nastavení pozice pokračovat v přehrávání nebo zůstat v pozastaveném stavu. Tento fakt jsem vyřešil ve své třídě, takže při práci s ní nic nepoznáte.
close [alias] play [alias] stop [alias] pause [alias] resume [alias]
To jsem ony příkazy, které komentář nepotřebují. Ani tento komentář. :)
Windows Media system má k dispozici hodně příkazů, které by ještě mohli eventuelně funkčnost třídy rozšířit. Myslím, že tato základní verze bude většině vyhovovat.
Interface a použití třídy
Myslím si, že nejen použití, ale i interface třidy se dá nejlépe ukázat na příkladu, takže když dovolíte...
// Vytvoříme novou instanci ze souboru C:\Test.mp3 Mp3File mp3 = new Mp3File(@"C:\Test.mp3");
// Přehrajeme (ekvivalent je mp3.Status = Mp3File.Mp3FileStatus.Playing) mp3.Play();
// Zastavíme (ekvivalent je mp3.Status = Mp3File.Mp3FileStatus.Stopped) mp3.Stop();
// Pozastavíme (ekvivalent je mp3.Status = Mp3File.Mp3FileStatus.Paused) // Opětovné spuštění přehrávání je pomocí metody Play() mp3.Pause();
// Zavřeme soubor (ekvivalent je mp3.Status = Mp3File.Mp3FileStatus.Closed) mp3.Close();
// Celkový čas určený v typu TimeSpan label1.Text = mp3.TotalTime.ToString();
// Uplynulý čas určený v typu TimeSpan label1.Text = mp3.ElapsedTime.ToString();
// Nastaví uplynulý čas na 10s od začátku mp3.ElapsedTime = TimeSpan.FromSeconds(10);
// Zbývající čas určený v typu TimeSpan label1.Text = mp3.Remaining.ToString();
// Nastaví zbývající čas na 10s od konce mp3.Remaining= TimeSpan.FromSeconds(10);
// Přetočí na začátek mp3.Rewind();
// Přetočí o 10s vpřed mp3.Forwards(TimeSpan.FromSeconds(10));
// Přetočí o 10s vzad mp3.Backwards(TimeSpan.FromSeconds(10));
// Stav mp3 souboru vyjmenovaný v enum Mp3File.Mp3FileStatus mp3.Status
// Jméno souboru, kterému přísluší instance třídy (pouze pro čtení) mp3.Filename
// ID3 informace podrobněji popsaná na následující stránce mp3.ID3
Třída dále obsahuje protected virtuální metody, které se volají vždy po akcích Close, Open, Play, Pause, Stop a po posunech v čase. Tyto virtuální metody potom volají eventy. Jedná se o stejný systém jako používají např. ovládací prvky z System.Windows.Forms.
Jména eventů jsou:
Mp3Close, Mp3Load, Mp3Play, Mp3Pause, Mp3Stop a Mp3Seek
Mp3Seek obsahuje navíc informaci o čase kam se čas posunul. Předávání se konná přes Mp3File.TimeSpanEventArgs.
Uznávám, že jména eventů nejsou zrovna nejšťastnější, ale zase na druhou stranu se alespoň nemotají s názvy metod.
ID3 tagy
Moje třida Mp3File obsahuje podtřídu Mp3File.Mp3ID3v1, která zprostředkovává informace z tagu ID3v1.* (verze řady 1). Je navržena tak, aby se dala použít nezávisle na hlavní třídě Mp3File.
Nedá se vytvořit pomocí operátoru new, ale obsahuje statickou metodu
public static Mp3ID3v1 CreateId3v1(string filename);
,která instanci vytvoří pokud soubor obsahuje ID3v1 tag. Pokud ne, tak vrátí null.
ID3v1 obsahují tyto položky (v závorce uvádím jméno vlastnosti třídy Mp3ID3v1):
- Jméno skladby (Title)
- Autor (Artist)
- Album (Album)
- Rok vydání (Year)
- Komentář (Comment)
- Číslo skladby [pouze u ID3v1.1] (Track)
- Hudebni žánr (Genre)
- ID hudebního žánru (GenreID)
Všechny vlastnosti uvedé v závorkách jsou i pro zápis. Do souboru se změny promítnou po zavolání metody Mp3ID3v1.Update();
Co se týče hudebních žánrů, tak třída podporuje místo standardních 80ti žánrů také další, které rozšířil WinAmp. Jejich seznam, ze kterého jsem vycházel je zde.
Jak jsem již psal v přehledu metod a vlastností třídy Mp3File na předchozí stránce, k informacím o ID3 tagu se lze dostat pomocí vlastnosti Mp3File.ID3, která v případě absence ID3v1 vrací null.
Závěr
Původně jsem měl v plánu vytvořit nějaký jednoduchoučký přehrávač, který by demonstroval všechny funkce mojí třídy, ale nakonec jsem usoudil, že použití je natolik jednoduché, že to snad nebude potřeba. Pokud jste jiného názoru, napište mi do komentářů a já nějaké demo spáchám. ;)
Určitě by se třída dala vylepšit minimálně o načítaní novějších druhů ID3 tagů, ale uvidíme jestli se rozšíření dočká, protože času máme všichni málo.
Doufám, že tento můj po večerech dělaný kód si najde své příznivce a pokud ho použije alespoň jeden z vás, pak měl on i tento článek smysl.
Děkuji za pozornost. :)