Programowanie w języku Delphi » Artykuły

Biblioteka BASS

  • 2010-02-14 15:14
  • 29 komentarzy
  • 4015 odsłon
  • Oceń ten tekst jako pierwszy
<justify>UWAGA: Artykuł w wersji rozwojowej, będę go dopisywał w miarę możliwości na dniach, aż powstanie z niego książka, nie będę ukrywał, że materiału jest sporo i może to trochę zająć, ale robię to dla wszystkich zwolenników BASSa i może uda się zmniejszyć ilość pytań o niego na łamach forum.</justify>
Spis treści:
 ? Wstęp
 ? Instalacja
   ? Biblioteka główna
   ? Rozszerzenie do plików WMA
   ? Rozszerzenie do plików FLAC
   ? Rozszerzenie funkcji CD-ROMu
 ? Inicjalizacja
 ? Finalizacja
 ? Konfiguracja
 ? Obsługa błędów
 ? Podstawowe funkcje odtwarzania
   ? MP3, MP2, MP1, WAV, OGG
   ? MO3, IT, XM, S3M, MTM, MOD, UMX
   ? WMA
   ? FLAC
   ? Wszystko w jednej funkcji
   ? Obsługa CD-ROMu
 ? Zarządzanie kanałami
   ? Kontrolowanie odtwarzania
   ? Pozycja odtwarzania
   ? Przewijanie utworu
   ? Zmiana atrybutów
   ? Sliding atrybutów
 ? Generowanie dźwięków
 ? Nagrywanie
   ? Inicjalizacja
   ? Wybór wejścia audio
   ? Nagrywanie do pliku WAV
 ? Wizualizacje
   ? Prosty analizator widma
   ? Płynny analizator widma
   ? Peak's
   ? Wizualizacje Winampa
   ? Wizualizacje Sonique
 ? Efekty DSP
   ? Equalizer
   ? Własne efekty DSP
 ? Synchronizacje
 ? Streaming plików
 ? Internetowe radia SHOUTcast
   ? Odtwarzanie
   ? Zgrywanie do pliku


Wstęp
Wróć do spisu treści
Biblioteka BASS jest bardzo małym plikiem dll, która może dodać do aplikacji kompleksową obsługę większości popularnych formatów plików audio, funkcje odtwarzania muzyki z płyt kompaktowych (oraz ripowania), streamingu z rozgłośni SHOUTcast, dekodowania skompresowanych plików do czystej postaci WAV, nagrywania z wejścia, a także generowania dźwieków. Główne zalety można wymienić w kilku następujących podpunktach:
   ?  Pliki MP3, WAV, OGG
       Mono/Stereo, 8/16 bitowe, także kompresowane pliki WAV
   ?  Generowanie dźwięków
   ?  Streamowanie z internetu
       Odsłuch plików "na żywo", radia SHOUTcast
   ?  Wielokanałowość
       Wspomaga kilkukanałowe pliki OGG/WAV, (także WMA i FLAC dzięki rozszerzeniom)
   ?  Pliki typu MOD
       Wszystkie popularne formaty, oraz MO3 (mody z samplami skompresowanymi MP3/OGG)
   ?  Wiele wyjść
       Obsługuje wiele kart dźwiękowych jednocześnie
   ?  Efekty DirectX 8
       Efekty dźwiękowe zawarte w DirectX 8 (echo, flanger, reverb...)
   ?  Synchronizacja
       Ustaw wywołanie własnej procedury w określonym momencie odtwarzania
   ?  Efekty DSP
       Modyfikacja dźwięku "w locie"
   ?  FFT
       Dane do np wizualizacji dźwięku
   ?  Mały rozmiar
       Poniżej 100kB (90,5kB dla wersji 2.1)
   ?  DARMOWA DLA NIEKOMERCYJNYCH ZASTOSOWAŃ
       Używaj do woli jeżeli tworzysz freeware!

Samą bibliotkę oraz mnóstwo dodatków znajdziecie na stronie Un4Seen.

Instalacja
Wróć do spisu treści
W tej części artykułu opiszę sposoby "instalacji" biblioteki BASS. W zasadzie trudno tu mówić o jakiejkolwiek instalacji gdyż cały proces polega na wrzuceniu kilku plików w odpowienie miejsce, a można to zrobić na kilka sposobów.

Instalacja - Biblioteka główna
Wróć do spisu treści
Po pierwsze należy się zaopatrzyć w biblotekę BASS. Pobierzesz ją z tej strony. W ściągnietym archiwum ZIP, należy odszukać kilka plików. Pierwszym z nich, na jaki się natrafi to bass.chm. Jest to podręczna pomoc na temat wszelkich funkcji BASSa, zawiera także przykładowe, krótkie przykłady (niestety w C) do wykorzystania. Zalecam ten plik mieć gdzieś pod ręką i zerkać sobie po szerszy opis funkcji. Kolejnym plikiem będzie bass.dll. Można z nim poradzić sobie na kilka sposobów.

Wrzucić do katalogu %windir%\system32\, ale wtedy musisz pamiętać, aby każdy użytkownik twojej aplikacji też tam miał tą dllke.

Mieć ją w katalogu wyjściowym kompilatora, tak żeby w efekcie dllka leżała obok exeka.

Jeśli nie chcesz dllki w system32, ani koło exeka, tylko w jakimś podkatalogu, to musisz zmodyfikować plik bass.pas, w linii 727 (dla wersji 2.1) jest stała bassdll, określa ona względne położenie dllki wobec exeka aplikacji, np. jeśli dll ma być w podkatalogu plugins to kod ten należy tak zmodyfikować:

const
 bassdll = 'plugins\bass.dll';

Sam plik bass.pas (znajdziesz w podkatalogu delphi w zipie) najlepiej wrzucić do katalogu Delphi\Lib, lub mieć wśród źródeł projektu wtedy będą one bardziej przenośne (nie trzeba będzie ciągle pamiętac o dołączaniu bass.pas gdy zechce się komuś dać źródła). Ogólnie ujmując, plik ten musi być w ścieżce wyszukiwania kompilatora. Aby używac BASSa w swoim projekcie należy dodać go do sekcji uses modułu.

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, BASS;

Inicjalizacja
Wróć do spisu treści
Pierwszą czynnością jaką należy wykonać przy pracy z tą biblioteką to jej inicjalizacja. Służy do tego funkcja BASS_Init. Posiada ona pięć parametrów, oto one:

Device: Integer
Wybór urządzenia wyjściowego, 0 - brak dźwięku, 1 - domyślne urządzenie, można użyć funkcji BASS_GetDeviceDescription, aby pobrać opis urządzenia wyjściowego. Poniższy kod wypisuje do kontrolki TListBox wszystkie możliwe urządzenia.

Kod dla wersji biblioteki BASS < 2.4
var
 n: Integer;
begin
 n := 0;
 repeat
  ListBox.Items.Add(BASS_GetDeviceDescription(n));
  Inc(n);
 until BASS_ErrorGetCode = BASS_ERROR_DEVICE;
end;


Kod dla wersji bibliteki bass >= 2.4
var
 n: Cardinal;
 info: BASS_DEVICEINFO;
begin
  n := 0;
  repeat
    BASS_GetDeviceInfo(n, info);
    ListBox.Items.Add(info.name);// a w info.driver jest nazwa draivera audio output
    Inc(n);
  until BASS_ErrorGetCode = BASS_ERROR_DEVICE;
end;
  
Freq: DWord
Częstotliwość próbkowania na wyjściu, zazwyczaj 44100 (44.1kHz) lub 48000 (48kHz)

Flags: DWord
Flagi, czyli wartości specjalne. Jakakolwiek kombinacja poniższych flag (łączymy ORem):
 ? BASS_DEVICE_8BITS
    8mio bitowa rozdzielczość, zamiast 16sto bitowej
 ? BASS_DEVICE_MONO
    Mono, zamiast Stereo
 ? BASS_DEVICE_3D
    Uaktywnia funkcje dźwięku przestrzennego
 ? BASS_DEVICE_LATENCY
    Oblicza czas opóźnienia i minimalny rozmiar bufora, wydłuża czas inicjalizacji
 ? BASS_DEVICE_SPEAKERS
    Zmusza do przestawienia kanałów przypisanych do poszczególnych głośników  
 
Win: HWND
Uchwyt-matka dla instacji BASSa, zazwyczaj handle aplikacji, głównego okna, etc

clsid: PGUID
Identyfikator klasy obiektu, jaki zostanie użyty do inicjalizacji DirectSound. nil - użycie domyślnego.  

Trzeba pamiętać, że nie zawsze dany dll będzie zawierał funkcje, które mamy zamiar używać. Może się zdarzyć iż użytkownik będzie posiadał wersję starszą/nowszą, które nie będzie zbyt współgrać z naszym kodem. Należy więc sprawdzić wersję ładowanej biblioteki dll. Pierwszy kod pokazuje okienko dialogowe z wersją biblioteki.

var
 Major, Minor: DWord;
begin
 Major := BASS_GetVersion and $FFFF;
 Minor := BASS_GetVersion shr 16;
 ShowMessage(IntToStr(Major) + '.' + IntToStr(Minor));
end;

Natomiast ten pozwala na szybsze sprawdzenie wersji.

if BASS_GetVersion = MAKELONG(2, 1) then

Najprościej rzecz ujmując inicjalizację BASSa mozna przeprowadzić w następujący sposób.

procedure TMainForm.FormCreate(Sender: TObject);
begin
 if BASS_GetVersion = MAKELONG(2,1) then
  begin
   BASS_Init(1, 44100, 0, Handle, nil);
   BASS_Start;
  end else begin
   ShowMessage('Wymagane bass.dll w wersji 2.1!');
  end;
end;

Finalizacja
Wróć do spisu treści
Z zakończeniem pracy biblioteki nie ma już tylu komplikacji co z jej uruchomieniem. Warto pozamykać wszelkie otwarte uchwyty w obrębie BASSa, pozwalniać zasoby (no, chyba że używa się flagi automatycznie zwalniającej uchwyt przy zatrzymaniu). Tak byłoby ładnie i fanie, ale załóżmy jednak iż nie będziemy tacy szczegółowi, bo i tak zasoby zwolnią się same, a zatrzymanie wszystkich aktywnych kanałów można wykonać jedną instrukcją, więc zakończenie pracy z dllką, odbywa się za pomocą dwóch instrukcji.

procedure TMainForm.FormDestroy(Sender: TObject);
begin
 BASS_Stop;
 BASS_Free;
end;

Podstawowe funkcje odtwarzania
Wróć do spisu treści
Najwyższa pora zaprzęc dllkę do pracy! W poszczególnych podrozdziałach przedstawię sposoby obsługi poszczególnych formatów plików, a na koniec omówię funkcję, która sama rozpozna rodzaj pliku i zastosuje odpowiednią metodę otwarcia pliku, oraz obsługę odtwarzania płyt audio cd.

Podstawowe funckje odtwarzania - MP3, MP2, MP1, WAV, OGG
Wróć do spisu treści
Załadowanie tego typu plików jest stosunkowo proste, cała filozofia sprowadza się do zaledwie jednej linijki. I niech ktoś powie, że BASS jest skomplikowany.

private
 { Private declarations }
 Uchwyt: Cardinal;
 
if OD.Execute then
 begin
  Uchwyt := BASS_StreamCreateFile(False, pChar(OD.FileName), 0, 0, 0);
  // odtwarzamy wczytany plik
  Bass_ChannelPlay(Uchwyt, False);
 end;
 

Najpierw musimy zadeklarować zmienną pod uchwyt utworku, najlepiej aby to była zmienna globalna. W samym BASSie istnieją na to poszczególne typy jak HMUSIC, HSAMPLE, HSTREAM, etc, ale wszystko sprowadza się do Cardinal. Uchwyt ten jest potrzebny, aby wykonywać dalsze operacje, takie jak ściszanie, zgłaśnianie, pauzowanie, wznawianie, zatrzymywanie, nakładanie efektów, pobieranie danych FFT i masa innych funkcji. Jak widać przykładowy kod ma za zadanie poprosić użytkownika o wybranie pliku z odpowiedniego dialogu i odtworzenie go. Jeżeli wykonanie dialogu się powiedzie, następuje wykonanie dwóch instrukcji. Pierwszą jest BASS_StreamCreateFile czyli utworzenie strumienia z pliku, nazwa może być trochę myląca, ponieważ funkcja ta dopuszcza także wczytanie strumienia bezpośrednio z adresu w pamięci. Opis jej parametrów.

Mem: Bool
Czy utwór ma być wczytany z pamięci czy też nie, odpowiednio True lub False

F: Pointer
W przypadku gdy mem = True, podajemy tutaj wskaźnik na początek danych utworu, w przeciwnym wypadku pChar zawierający ścieżkę pliku

Offset: DWord
Jeżeli plik ma jakiś nietypowy nagłówek to należy podać przesunięcie (np bierzemy 100 bajtów jakiegoś śmiecia i do tego dopisujemy plik mp3 to należy podać tutaj 100), parametr ma zastosowanie tylko gdy mem = False

Length: DWord
Długość bloku danych, gdy mem = True, należy podać wielkość obszaru pamięci z danymi, dla pliku ilość bajtów do użycia, lub 0 jeśli ma czytać do końca pliku

Flags: DWord
Flagi, dowolna kombinacja poniższych, łączymy operatorem bitowym OR
 ? BASS_SAMPLE_FLOAT
    Używa 32 bitowych próbek, wymaga sterowników WDM lub trybu dekodowania
    (załączona flaga BASS_STREAM_DECODE)
 ? BASS_SAMPLE_MONO
    Dekoduje strumień jako mono, zmniejszając zużycie CPU jeśli strumień oryginalnie jest stereo
 ? BASS_SAMPLE_SOFTWARE
    Zmusza strumień do miksowania programowego
 ? BASS_SAMPLE_3D
    Uaktywnia funkcje 3D, falaga ta jest ignorowana jeżeli BASS nie zosatał zainicjowany z
    flagą BASS_DEVICE_3D
 ? BASS_SAMPLE_LOOP
    Zapętla odtwarzanie utworu, ta flaga może być ustawiana lub zdejmowana w każdej chwili
    poprzez BASS_ChannelSetFlags
 ? BASS_SAMPLE_FX
    Wymaga DirectX 8 lub wyższego i pozwala na wykorzystanie efektów pseudoprzestrzennych,
    można je dodawać poprzez BASS_ChannelSetFX
 ? BASS_MP3_SETPOS
    Umożliwia dokładnie wskazanie bajtu przy przeskakiwaniu pozycji w utworze, wydłuża to
    jednak czas otwarcia pliku
 ? BASS_STREAM_AUTOFREE
    Automatycznie zwalnia zasoby strumienia, kiedy skończy się jego odtwarzanie lub zostanie
    on zatrzymany
 ? BASS_STREAM_DECODE
    Dekoduje próbki bez odtwarzania ich, należy użyć BASS_ChannelGetData aby pobrać dane,
    flagi BASS_SAMPLE_SOFTWARE/3D/FX/AUTOFREE są ignorowane tak samo jak wszelkie
    ustawienia głośników
 ? BASS_SPEAKER_xxx
    Flagi przypisania do poszczególnego głośnika, utwór może grać np tylko w lewym głosniku
 ? BASS_UNICODE
    Nazwa pliku jest zakodowana w UNICODE (2 bajty na znak)

Drugą instrukcją (BASS_ChannelPlay) jest rozpoczęcie odtwarzania utworu. Parametr pierwszy to uchwyt utworu, a drugi to wartość Boolean oznaczająca zresetowanie pozycji utworu. Więcej na temat kontroli odtwarzania utworu opiszę w dalszym podpunkcie, gdyż sterowanie kanałami odbywa się tak samo dla poszczególnych typów utworów. Innym ważnym aspektem jest pobranie długości utworu. Można to wykonać w następujący sposób. Najpierw pobierana jest długość w bajtach i zapisywana w zmiennej Czas, a następnie konwertowana na ilość sekund bazując na formacie utworu. Ilość sekund zwracana jest w formie zmiennoprzecinkowej (np 54 i pół sekundy etc) więc aby mieć bardziej przejrzysty wynik mozna uciąć część ułamkową. Na dialogu zostanie wyświetlona długość utworu w sekundach.

var
 Czas: Cardinal;
begin
 Czas := BASS_StreamGetLength(Uchwyt);
 Czas := Trunc(BASS_ChannelBytes2Seconds(Uchwyt, Czas));
 ShowMessage(IntToStr(Czas));

Aby nie powodować memory leaków po zakończeniu wszelkich operacji na zmiennej Uchwyt, należy zwolnić zużyte zasoby.

BASS_StreamFree(Uchwyt);

Zarządzanie kanałami
Wróć do spisu treści
Pora omówić sposób sterowania kanałami. Kanałem jest dowolny uchwyt reprezentujący strumień, sampel, muzykę mod czy też nagrywanie. Dla tych wszystkich typów dostępne są uniwersalne funkcje służące do wykonywania podstawowych operacji jak zatrzymanie, wznowienie odtwarzania, ściszanie, zgłaśnianie, etc.

Zarządzanie kanałami - Kontrolowanie odtwarzania
Wróć do spisu treści
Wiemy już jak wczytać i odtworzyć plik, pora teraz poznać nieco więcej na temat sterowania utworem. Odwrotnością do odtwarzania jest zatrzymanie go poprzez polecenie BASS_ChannelStop.

BASS_ChannelStop(Uchwyt);

Wznowienie odtwarzania można uzyskać już poznaną funkcją.

BASS_ChannelPlay(Uchwyt, False);

Drugi parametr określa rozpoczęcie odtwarzania od początku utworu, przy False funkcja ta zachowuje się jak wznowienie odtwarzania, jednak pod żadnym pozorem proszę nie używać tego do wykonania funkcji pauzy! Dlaczego? Ponieważ BASS_ChannelStop czyści zawartość bufora i część utworu po prostu zniknie. Od tego jest specjalna funkcja BASS_ChannelPause.

if BASS_ChannelIsActive(Uchwyt) = BASS_ACTIVE_PAUSED then
  BASS_ChannelPlay(Uchwyt, False)
 else
  BASS_ChannelPause(Uchwyt);

Powyższy kod, zapewnia pełną obsługę funkcji pauzowania i wznawiania. Najpierw sprawdzany jest warunek czy kanał jest zapauzowany i w zależności od wyniku, wstrzymuje go lub wznawia.

Zarządzanie kanałami - Pozycja odtwarzania
Wróć do spisu treści
Wcześniej opisałem metody pobrania czasu utworu dla poszczególnych typów, jednak co zrobić gdy posiada się sam uchwyt i nie wiadomo, której funkcji do pobrania długości użyć? Jest dostępne polecenie pobierające odpowiednią informację, do której można odpowiednio dostosować się. Mowa o BASS_ChannelGetInfo.

var
 Info: BASS_CHANNELINFO;
 Czas: Cardinal;
begin
 BASS_ChannelGetInfo(Uchwyt, Info);
 
 if Info.ctype and BASS_CTYPE_STREAM = BASS_CTYPE_STREAM then
  Czas := BASS_StreamGetLength(Uchwyt)
 else
  if Info.ctype and BASS_CTYPE_MUSIC_MOD = BASS_CTYPE_MUSIC_MOD then
   Czas := BASS_MusicGetLength(Uchwyt, True);
 
 Czas := Trunc(BASS_ChannelBytes2Seconds(Uchwyt, Czas));
 ShowMessage(IntToStr(Czas));
end;

W strukturze BASS_CHANNELINFO znajduje się pole ctype, które mówi nam o typie utworu przypisanym do uchwytu, możliwe są następujące wartości.
 ? BASS_CTYPE_SAMPLE
    Sampel zdefiniowany przez użytkownika
 ? BASS_CTYPE_STREAM
    Może zostać użyte jako flaga do sprawdzenia każdego rodzaju strumienia
 ? BASS_CTYPE_STREAM_WAV
    Format WAV
 ? BASS_CTYPE_STREAM_OGG
    Format OGG
 ? BASS_CTYPE_STREAM_MP1
    Format MP1
 ? BASS_CTYPE_STREAM_MP2
    Format MP2
 ? BASS_CTYPE_STREAM_MP3
    Format MP3
 ? BASS_CTYPE_STREAM_WMA
    Format WMA
 ? BASS_CTYPE_STREAM_FLAC
    Format FLAC
 ? BASS_CTYPE_STREAM_CD
    Ścieżka z płyty CD
 ? BASS_CTYPE_MUSIC_MOD
    Format MOD, może zostać użyte jako flaga do sprawdzenia każdego rodzaju muzyki mod
 ? BASS_CTYPE_MUSIC_MTM
    Format MTM
 ? BASS_CTYPE_MUSIC_S3M
    Format S3M
 ? BASS_CTYPE_MUSIC_XM
    Format XM
 ? BASS_CTYPE_MUSIC_IT
    Format IT
 ? BASS_CTYPE_MUSIC_MO3
    Format MO3
 ? BASS_CTYPE_RECORD
    Kanał nagrywania
   
Powyższy przykładowy kod pokaże długość trwania utworu w sekundach. Mamy całkowity czas utworu, a co z obecnie odtwarzaną pozycją? Od tego mamy kolejną - tym razem jedną dla wszystkich - funkcję, a mianowicie BASS_ChannelGetPosition.


MO3, IT, XM, S3M, MTM, MOD, UMX
Wróć do spisu treści
Czas dokończyć dzieło autora (w pewnym sensie) i napisać coś o muzyce MOD.
Aby odtwarzać takie formaty, musimy skorzystać z funkcji BASS_MusicLoad();
  Uchwyt := BASS_MusicLoad(False, pChar(OD.FileName), 0, 0, 0, 0);


To akurat wystarczy do załadowania muzyki w jednym z tych formatów.

Nagrywanie
Wróć do spisu treści
Nagrywanie dźwięku z biblioteką BASS jest banalnie proste.

Wykrywanie sprzętu wejściowego realizuje prosta procedura:

procedure FindAudioInput;
var
 n: Cardinal;
 info: BASS_DEVICEINFO;
begin
  Memo.Clear;
  n := 0;
  repeat
    BASS_RecordGetDeviceInfo(n, info);
    Memo.Lines.Add(IntToStr(n) + ': ' + info.name + ', ' + info.driver);
    Inc(n);
  until BASS_ErrorGetCode = BASS_ERROR_DEVICE;
end;


Inicjalizacja
Wróć do spisu treści

Teraz jak wykryliśmy nasz sprzęt to możemy w kolejnej procedurze podać naszą zmienną n lub po prostu podać wartośc 0 oznaczającą domyślne urządzenie nagrywające.

Inicjalizacja urządzenia nagrywającego realizuje procedura InitRecord:

procedure InitRecord;
begin
  if (not BASS_RecordInit(0)) then // tu możemy podać także zmienną n
    MessageBox(0, 'Nie można zainicjalizować urządzenia!', 'ERROR', 0);
end;


Wybór wejścia audio
Wróć do spisu treści

Na tym etapie musimy zdecydować skąd będzie pochodziło źródło naszego sygnału audio. Aby tego dokonać skorzystamy z funkcji BASS_RecordGetInput, która zwróci nam identyfikator wejścia, a funkaja BASS_RecordGetInputName zwróci nam nazwę identyfikatora (np. Głośność CD, Miks Stereo, Głośność Mikrofonu, itp.)

Napiszemy teraz prostą procedurę zwracjającą nam nazwy urządzeń wejściowych:

procedure GetInputAudio;
var
  i: Integer;
  dName: PAnsiChar;
  level: Single;
begin
  dName:= BASS_RecordGetInputName(i); // wyciągamy nazwę urządzenia wejściowego.
  while dName <> nil do
  begin
    ComboBox1.Items.Add(StrPas(dName));// dodajemy nazwę naszego urządzenia wejściowego
    {A teraz sprawdzimy czy aktualne urządzenie jest domyślnym urządzeniem wejściowym i ustawiamy go
      w ComboBox jako domyślne.
      W okienku "Regulacja nagrywania" (Panel Sterowania/Dźwięki i urządzenia audio/Zakładka Audio/
      /Nagrywanie dźwięku/Głośność) można wybrać jedno z urządzeń nagrywających, a my sprawdzamy
      które jest teraz zahaczone ptaszkiem w polu "Zaznacz".}
    if (BASS_RecordGetInput(i, level) and BASS_INPUT_OFF) = 0 then
      ComboBox1.ItemIndex := i;
    Inc(i);
    dName := BASS_RecordGetInputName(i);
  end;
  ComboBox1Change(Self);
end;


Strumień audio możemy przechwytywać w zależności od wybranego urządzenia wejściowego. Jako przykład podam, że wybranie "Głośność Mikrofonu" będzie powodować przechwytywanie sygnału audio z tego właśnie urządzenia (to chyba oczywiste). Ale wybranie "Miks Stereo" da ten efekt, że gdy włączymy odtwrzanie filmu DivX to źródło dźwięku będzie pochodziło z odtwarzanego filmu.

Żebyśmy mogli zmienić źródło sygnału audio musimy wybrać inne urządzenie wejściowe, co realizuje procedura ComboBox1Change w zdarzeniu OnChange komponentu CheckBox.

procedure TForm1.ComboBox1Change(Sender: TObject);
var
  i: Integer;
  r: Boolean;
begin
  r:= True;
  i:= 0;
  // Najpierw wszystkie urządzenia wejściowe wyłączamy ...
  while r do
  begin
    r:= BASS_RecordSetInput(i, BASS_INPUT_OFF, -1);
    Inc(i);
  end;
  // ... a potem wybrane urządzenie włączamy.
  BASS_RecordSetInput(ComboBox1.ItemIndex, BASS_INPUT_ON, -1);
end;


Nagrywanie do pliku WAV
Wróć do spisu treści

A teraz to co tygrysy lubią najbardziej - nagrywanie do pliku w formacie WAV.

Na początek będziemy musieli zdefiniować nagłówek pliku WAV. Zrobimy to deklarując nowy typ WAVHDR.

Tak wygląda nagłówek pliku WAV:

type
  WAVHDR = packed record
    riff: array[0..3] of AnsiChar;
    len: DWord;
    cWavFmt: array[0..7] of AnsiChar;
    dwHdrLen: DWord;
    wFormat: Word;
    wNumChannels: Word;
    dwSampleRate: DWord;
    dwBytesPerSec: DWord;
    wBlockAlign: Word;
    wBitsPerSample: Word;
    cData: array[0..3] of AnsiChar;
    dwDataLen: DWord;
  end;


Deklarujemy nasz nagłówek globalnie:

  WaveHdr: WAVHDR;


Gdy już będziemy mieli ustawiony w strumieniu nagłówek pliku WAV oraz gdy nagrywanie działa (RecordChannel < 0) to będziemy musieli jakoś przechwycić dane audio i skierować je do naszego strumienia WaveStream. W tym celu przed procedurą StartRecord zadeklarujemy procedure RecordingCallBack:

function RecordingCallBack(Handle: HRECORD; buffer: Pointer; length, user: DWord): boolean; stdcall;
begin
  // Kopiujemy buffer do strumienia WaveStream
  // Jako ciekawostkę dodam że w tym miejscu możemy zmodyfikować zawartość naszego buffer
  // aby otrzymać ciekawy efekt audio.
  // Np. możemy pobrać (probka:= Pointer(Integer(buffer) + i)) w pętli for próbkę dźwięku
  // która jest typu probka: byte^, a następnie taką próbkę podzielić na 2 (probka^:= probka^ div 2)
  // i po modyfikacji całego bufora zapisać go do strumienia.
  Form1.WaveStream.Write(buffer^, length);
  Result:= True; // True nakazuje kontynuować nagrywanie
end;


Opis parametrów funkcji RecordingCallBack w których znajdziemy interesujące nas informacje:

Handle: HRECORD
Uchwyt do danych audio.

buffer: Pointer
Adres do bufora w którym znajdziemy nagrane dane audio.

length: DWord
Liczba bajtów danych przechowywana w buforze.

user: DWord
Dane użytkownika.


Procedura StartRecord przechwytuje nam strumień audio z wybranego źródła wejściowego do strumienia WaveStream zadeklarowanego globalnie jako typ TMemoryStream.
Należy wcześniej zadeklarować globalnie zmienną RecordChannel który jest typu HRECORD.

Funkcja BASS_RecordStart przyjmuje kilka parametrów:

freq: DWord
Częstotliwość z którą będziemy przechwytywać dane audio.

chans: DWord
Liczba kanałów które będą nagrywane. 1 - mono, 1 - stereo, itd.

flags: DWord
Flagi informujące procedure o dodatkowych parametrach nagrywania:
BASS_SAMPLE_8BITS - używa 8-bitowej rozdzielczości próbkowania zamiast 16-bitowej.
BASS_SAMPLE_FLOAT - używa 32-bitowej zmiennoprzecinkowej rozdzielczości próbkowania.
BASS_RECORD_PAUSE - włącza tryb nagrywania i go pauzuje.

proc: RECORDPROC
Adres procedury która będzie zapisywała w strumieniu bufor audio.

user: Pointer
Dane użytkownika.

I oto nasza procedura StartRecord nagrywająca z wybranego źródła wejścia:

procedure StartRecord;
begin
  with WaveHdr do // ustawiamy nagówek pliku WAV
  begin
    riff := 'RIFF';
    len := 36;
    cWavFmt := 'WAVEfmt ';
    dwHdrLen := 16;
    wFormat := 1;
    wNumChannels := 2;
    dwSampleRate := 44100;
    wBlockAlign := 4;
    dwBytesPerSec := dwSampleRate * wBlockAlign; // czyli 176400; 
    wBitsPerSample := 16;
    cData := 'data';
    dwDataLen := 0;
  end;
  WaveStream.Write(WaveHdr, SizeOf(WAVHDR)); // Najpierw do strumienia zapisujemy nagłówek pliku
  // Zaczynamy nagrywanie z parametrami 44100hz i 16-bit stereo
  // a zamiast 0 możemy podać flagę np. BASS_SAMPLE_FLOAT, BASS_RECORD_PAUSE, itp.
  RecordChannel:= BASS_RecordStart(44100, 2, 0, @RecordingCallBack, nil);
  if RecordChannel = 0 then // będziemy przynajmniej wiedzieli co jest powodem błędu
    MessageBox(0, PAnsiChar('Nie można uruchomić nagrywania! Numer błędu: ' + IntToStr(BASS_ErrorGetCode()), 'ERROR', 0);


Nagrywamy, nagrywamy, a my byśmy chcieli aby to co już nagraliśmy znalazło się na dysku w postaci pliku WAV. Do tego napiszemy sobie procedurę StopRecord, w której zrobimy następujące rzeczy:
1. Zatrzymamy nagrywanie przy pomocy funkcji BASS_ChannelStop(Handle: DWord).
Funkcja ta przyjmuje jako parametr uchwyt do naszego kanału audio (RecordChannel).
2. Ostateczne ustawienie naszego nagłówka WAV (bo przecież nie wiedzieliśmy ile danych będzie nagrane).
3. Zapisanie strumienia do pliku.

procedure StopRecord;
var
  i: integer;
begin
  BASS_ChannelStop(RecordChannel);
  // kompletujemy nasz nagłówek WAV
  WaveStream.Position := 4;
  i:= WaveStream.Size - 8;
  WaveStream.Write(i, 4);
  i:= i - $24;
  WaveStream.Position := 40;
  WaveStream.Write(i, 4);
 
  WaveStream.Position:= 0;
  WaveStream.SaveToFile('Bass.wav');// tu jest nasz zapisany plik audio w formacie WAV
end;


I tak oto tym sposobem mamy nagrany nieskompresowany plik WAV z dowolnie wybranego źródła audio.

Mała uwaga!
Gdybyś tylko chciał na bieżąco z pamięci odtworzyć nagranie audio to zamiast je zapisać do pliku wystraczy wykonać poniższy kod (wcześniej zadeklaruj Channel: HSTREAM):

  Channel := BASS_StreamCreateFile(True, WaveStream.Memory, 0, WaveStream.Size, 0);
  if Channel <> 0 then
  begin
    BASS_ChannelPlay(Channel, True);// a to "skieruje" strumień dźwięku na głośniki
  end;


Po zabawie w nagrywanie dźwięku należy jeszcze po sobie posprzątać:

WaveStream.Free;
BASS_RecordFree;
BASS_Free;
BASS_Stop;

29 komentarzy

ziomalski 2012-03-27 16:51

Bardzo bym prosił żeby ktoś dokończył ten artykuł.

Wiw3K 2008-08-21 10:43

mnóstwo errorów...
1.
[Error] Unit1.pas(63): Declaration expected but 'IF' found
[Error] Unit1.pas(65): Undeclared identifier: 'OD'
[Error] Unit1.pas(65): 'END' expected but ')' found
linijka if OD.Execute then

2.
[Error] Unit1.pas(64): Undeclared identifier: 'OD'
linijka   Uchwyt := BASS_StreamCreateFile(False, pChar(OD.FileName), 0, 0, 0);

w czym problem?

chce tylko odtworzyć w tle muzykę ale szukając od 3 dni na google stwierdzam że jest to niemożliwe.

drplayer 2008-05-29 17:54

ja się znam nieco :) na tej Bassie -> jak coś to pisać

Bagno 2008-02-22 19:58

Już tego dokonałem :P

Bagno 2008-02-22 19:45

Ja chętnie bym dokończył ;]

modrih 2006-11-12 10:56

bardzo fajny artykuł, szkoda że nie jest w stanie ciągłego rozwoju

nie wiem może ktoś zna się na bibliotece BASS, i móglby go dokończyć?

myślę że wiele osób byłoby zadowolonych z przeczytania tak pożytecznego artykułu ;P

Marooned 2006-06-28 22:13

szkoda tylko, że Shejt się od nas odciął...

othello 2006-06-28 21:10

Podziwiam inicjatywe - oczywiscie znajac 4p nie dziwie sie ze dotyczy to akurat Delphi nie np BCB :P (jakas dyskryminacja czy co? :)).

Chociaz, moim skromnym zdaniem, BASS posiada bardzo dobra i wyczerpujaca dokumentacje oraz wiele przykladow kodu (gotowe aplikacje) - a ten art jest uklonem w strone "delphiarzy" i ludzi nie znajacych angielskiego (czyt. jest to troche tlumaczenie dokumentacji na polski i przekladanie przykladow na Delphi).

Ale powdziwiam ze komus sie chcialo (dla mnie by sie nie chcialo)

puchi 2006-06-17 17:56

kiedy dalsza część arta?? Mam nadzieję że szybko bo to ciekawy temat i z chęcią dowiem się coś więcej o tej bibliotece

aristo89 2006-05-26 17:31

witam. mam pytanie czy zna ktoś stronke z kursem pisania z tymi bibliotekami? bardzo potrzebuje gdyż chce stworzyć programik do radia internetowego.. z gory THX. pozdro

puchi 2006-05-24 16:37

no i co z dalsza czescia arta??

Adam.Pilorz 2005-12-29 12:27

"Hmm nie można nic zarzucić artykułowi aczkolwiek większość czytelników chciała by przeczytać jak utworzyć biblotekę w stylu bass..."
A po co wyważać otwarte drzwi? Bass to biblioteka napisana przez specjalistów, operująca wszystkim na niskim poziomie. Po to powstała, byś do napisania odtwarzacza nie musiał się w tym wszystkim babrać. Nie chcę Cię oceniać bezpodstawnie, ale nie sądzę, byś był w stanie napisać coś lepszego niż bass (szybszego, lżejszego i wygodniejszego).
Ale to tylko moja bardzo "humble" opinia :)

k3cz 2005-11-03 23:48

W VII a) powinno być (a przynajmniej tak mi się wydaje :) ):

Czas := BASS_ChannelGetLength(Uchwyt);
a jest:
Czas := BASS_StreamGetLength(Uchwyt);

Ps. Czekam z niecierpliwością na dalszą część

patrol_x 2005-10-02 12:44

Napewno się przyda przy zabawach w programowaniu :D masz 6+

GnOm 2005-09-14 12:26

Spoko fajny art mam nadzieje, że po części to odpowiedź na moje pytanie na formu.
Jak dla mnie 6+ pozdro

Fallout 2005-08-28 13:22

hmm dawno dawno temu napisalem odtwarzacz ktory korzysta z teh biblioteki :) ma pare bogow ale po prostu juz sie nim nie bawie z braku czasu.. http://fallnet.sytes.net/playbox tam macie opis... do pobrania: fallnet.sytes.net/playbox/wersje/0.9.9.127/Playbox_v099127_Alpha.rar

Patyk 2005-08-19 21:17

Świetny pomysł z tym artykułem. Wreszcie nauczę się BASS\'a - wcześniej jakoś nie miałem okazji.

Sheitar 2005-08-16 22:36

Deti, zwykła tabelka i "a", u mnie pod FF pod Simple i Motion wygląda ślicznie

Sheitar 2005-08-16 07:15

FModEx 4
fmodex.dll - 224kB
fmodexp.dll - 96,5kB (wymaga dodatkowych dllek dla każdego typu)
pliki nagłówkowe do Delphi - 181,4kB

BASS 2.1
sam bass.dll - 90,5kB
cały komplet bass (wma+cd+flac) - 169,7kB
pliki nagłówkowe do Delphi - 49,2kB

AklimX wytłumacz swoją teorię.

Deti 2005-08-16 13:36

Niezły opis BASS .. - szczególnie brawa za formę .. [ czy używałeś jakichś dziwnych tagów do zrobienia linków \"Wróć do...\" .. - na Motion wychodzą za linie i nie widać ich do końca ].
W kazdym razie szósteczka ;0

AklimX 2005-08-12 17:28

[quote]FMOD może i jest łatwiejszy w obsłudze, ale zobacz ile BASS zajmuje[/quote]
że co? ściągnij sobie najnowsze api fmod\'a i zobacz ile tak naprawdę zajmuje. To co napisałeś to marny argument...

Pepe 2005-08-10 16:35

Świetny artykuł. Mam nadzieję, że w miarę szybko zostanie uzupełniony. Czekam z niecierpliwością. Powodzenia!
Ps: Kiedyś też chciałem zrobić jakiegoś arta o Bass, ale pokonała mnie mnogość funkcji...

Talib 2005-08-10 11:11

FMOD może i jest łatwiejszy w obsłudze, ale zobacz ile BASS zajmuje :> do małych programików ok :) tylko trzeba trochę bardziej się narobić ...

Marooned 2005-08-09 09:10

Sugeruję numerowanie wierszy zrobić na tagach <ol><li> - nie trzeba wtedy wycinać numerków przy kopiowaniu kodu :)

Brawo za chęci :)
<font size="1">[szkoda tylko, że w Delphi a nie w BCB/VC++ :P]</font>

brodny 2005-08-09 11:04

Eee tam... Kwestie składniowe się pominie, ważne, że jest :P

BTW: "Używaj dowoli jeżeli tworzysz freeware" - nie powinno być "do woli"?

AklimX 2005-08-09 12:07

fajnie gdyby ktoś chciał napisać takiego tutka o FMOD Ex... Ja bardziej lubię FMOD\'a

migajek 2005-08-09 15:31

Jak to rozbudujesz to bedzie hitem :P A narazie jest OK :)

voodoo46 2005-09-02 14:15

Wreszcie drzwi zostały otwarte, by inni nie musieli ich wyważać... Sam całkiem niedawno szukałem czegoś na temat biblioteki bass.dll. Wszędzie tylko \"użyj bass\" a tu , proszę jaka miła niespodzianka...

Baszczo 2005-11-01 12:36

Hmm nie można nic zarzucić artykułowi aczkolwiek większość czytelników chciała by przeczytać jak utworzyć biblotekę w stylu bass... :)
Wiem, że nie jest to proste i dlatego właśnie przydały by się jakieś informacje na ten temat.