Biblioteka BASS

Sheitar

<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

<font size="4">Wstęp</span>
<font size="1">Wróć do spisu treści</span>
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.

<font size="4">Instalacja</span>
<font size="1">Wróć do spisu treści</span>
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.

<font size="2">Instalacja - Biblioteka główna</span>
<font size="1">Wróć do spisu treści</span>
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;

<font size="4">Inicjalizacja</span>
<font size="1">Wróć do spisu treści</span>
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;

<font size="4">Finalizacja</span>
<font size="1">Wróć do spisu treści</span>
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;

<font size="4">Podstawowe funkcje odtwarzania</span>
<font size="1">Wróć do spisu treści</span>
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.

<font size="2">Podstawowe funckje odtwarzania - MP3, MP2, MP1, WAV, OGG</span>
<font size="1">Wróć do spisu treści</span>
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);

<font size="4">Zarządzanie kanałami</span>
<font size="1">Wróć do spisu treści</span>
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.

<font size="2">Zarządzanie kanałami - Kontrolowanie odtwarzania</span>
<font size="1">Wróć do spisu treści</span>
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.

<font size="2">Zarządzanie kanałami - Pozycja odtwarzania</span>
<font size="1">Wróć do spisu treści</span>
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.

<font size="4">MO3, IT, XM, S3M, MTM, MOD, UMX</span>
<font size="1">Wróć do spisu treści</span>
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.

<font size="4">Nagrywanie</span>
<font size="1">Wróć do spisu treści</span>
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;

<font size="4">Inicjalizacja</span>
<font size="1">Wróć do spisu treści</span>

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;

<font size="4">Wybór wejścia audio</span>
<font size="1">Wróć do spisu treści</span>

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;

<font size="4">Nagrywanie do pliku WAV</span>
<font size="1">Wróć do spisu treści</span>

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

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

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

[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.

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

Już tego dokonałem :P

Ja chętnie bym dokończył ;]

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

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

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)

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

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

no i co z dalsza czescia arta??

"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 :)

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ęść

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

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

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

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

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

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ę.

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

[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...

Ś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...

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ć ...

Sugeruję numerowanie wierszy zrobić na tagach

  1. - nie trzeba wtedy wycinać numerków przy kopiowaniu kodu :)</p>

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

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"?

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

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

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...

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.