BASS (bibl. dzwiękowa) Problem z rysowaniem widma

0

Witam. Przedstawiam tutaj żywcem skopiowane z projektu przykładowego biblioteki bass.dll przykład rysowania widma.

 
if (!(chan=BASS_StreamCreateFile(FALSE,file,0,0,0))
		&& !(chan=BASS_MusicLoad(FALSE,file,0,0,BASS_MUSIC_RAMPS|BASS_MUSIC_POSRESET|BASS_MUSIC_PRESCAN,1))) {
		Error("Can't play file");
		return FALSE; // Can't load the file
	}
	{
		BYTE data[2000]={0};
		BITMAPINFOHEADER *bh=(BITMAPINFOHEADER*)data;
		RGBQUAD *pal=(RGBQUAD*)(data+sizeof(*bh));
		int a;
		bh->biSize=sizeof(*bh);
		bh->biWidth=WIDTH;
		bh->biHeight=-HEIGHT;
		bh->biPlanes=1;
		bh->biBitCount=8;
		bh->biClrUsed=bh->biClrImportant=HEIGHT/2+1;
		// setup palette
		for (a=1;a<=HEIGHT/2;a++) {
			pal[a].rgbRed=(255*a)/(HEIGHT/2);
			pal[a].rgbGreen=255-pal[a].rgbRed;
		}
		// create the bitmap
		wavebmp=CreateDIBSection(0,(BITMAPINFO*)bh,DIB_RGB_COLORS,(void**)&wavebuf,NULL,0);
		wavedc=CreateCompatibleDC(0);
		SelectObject(wavedc,wavebmp);
	}
	bpp=BASS_ChannelGetLength(chan,BASS_POS_BYTE)/WIDTH; // bytes per pixel
	if (bpp<BASS_ChannelSeconds2Bytes(chan,0.02)) // minimum 20ms per pixel (BASS_ChannelGetLevel scans 20ms)
		bpp=BASS_ChannelSeconds2Bytes(chan,0.02);
	BASS_ChannelSetSync(chan,BASS_SYNC_END|BASS_SYNC_MIXTIME,0,LoopSyncProc,0); // set sync to loop at end
	BASS_ChannelPlay(chan,FALSE); // start playing
	{ // start scanning peaks in a new thread
		DWORD chan2=BASS_StreamCreateFile(FALSE,file,0,0,BASS_STREAM_DECODE);
		if (!chan2) chan2=BASS_MusicLoad(FALSE,file,0,0,BASS_MUSIC_DECODE,1);
		scanthread=_beginthread(ScanPeaks,0,(void*)chan2);
	}

oraz

void __cdecl ScanPeaks(void *p)
{
	DWORD decoder=(DWORD)p;
	DWORD cpos=0,peak[2]={0};
	while (!killscan) {
		DWORD level=BASS_ChannelGetLevel(decoder); // scan peaks
		DWORD pos;
		if (peak[0]<LOWORD(level)) peak[0]=LOWORD(level); // set left peak
		if (peak[1]<HIWORD(level)) peak[1]=HIWORD(level); // set right peak
		if (!BASS_ChannelIsActive(decoder)) pos=-1; // reached the end
		else pos=BASS_ChannelGetPosition(decoder,BASS_POS_BYTE)/bpp;
		if (pos>cpos) {
			DWORD a;
			for (a=0;a<peak[0]*(HEIGHT/2)/32768;a++)
				wavebuf[(HEIGHT/2-1-a)*WIDTH+cpos]=1+a; // draw left peak
			for (a=0;a<peak[1]*(HEIGHT/2)/32768;a++)
				wavebuf[(HEIGHT/2+1+a)*WIDTH+cpos]=1+a; // draw right peak
			if (pos>=WIDTH) break; // gone off end of display
			cpos=pos;
			peak[0]=peak[1]=0;
		}
	}
	BASS_StreamFree(decoder); // free the decoder
	scanthread=0;
}
 

Jak dobrze rozumiem w kodzie #1 przedstawiony jest sposób rysowania "gradientu" i przygotowywanie do wypełnienia bitmapy widmem. kod #2 opisuje strukturę wątku, który rysuje widmo. Jak wielokrotnie przeleciałem przez ten wątek wywnioskowałem, że:

  • na początku tworzymy zmienną przechowującą uchwyt utworu (zmienna decoder)
  • następnie pobieramy dane utworu (nie wiem czy chodzi tu o wartości amplitud, natezenia dzwieku czy o coś innego jak możecie to pomóżcie) i zapisujemy je do zmiennej "level"
  • peek[] zawierają wartości (czegoś tam) na każdym głośniku.
    ... Dalej nieogarniam xD Czarna magia :D Jeśli możecie wytłumaczcie mi w jaki sposob jest tworzona paleta kolorów. Przerabiałem kurs GDI i kurs o bitmapach, lecz nie spotkałem się z niektórymi pojęciami z kodu #1. Nastepnie w jaki sposób pobrać natężenie tego dźwięku i przelać je na obraz? Nie rozumiem jak to jest przedstawione.

Cały kod programu przedstawiam w załączniku

0

w jaki sposob jest tworzona paleta kolorów.

                for (a=1;a<=HEIGHT/2;a++) {
                        pal[a].rgbRed=(255*a)/(HEIGHT/2);
                        pal[a].rgbGreen=255-pal[a].rgbRed;
                }

Paleta ma tyle kolorów, co połowa wysokości. Poszczególne kolory (pal[a]) tworzone są w taki sposób, że rgbRed zmienia się płynnie od 0 wartości bliskiej zeru do 255, a rgbGreen jest dopełnieniem, czyli płynne przejście od pelnej zieleni, przez jakieś burobrązowe (*) do pełnej czerwieni.

(*) pani od plastyki mówiła, że czerwony i zielony się „dopełniają od szarości”. oczywiście nie miała racji.

Nastepnie w jaki sposób pobrać natężenie tego dźwięku i przelać je na obraz?
Samo tworzenie bitmapy myślę że jest mało istotne. Skup się na zrozumieniu części BASSowej, jak pobierane są te dane (i jakie). Tu za bardzo nie pomogę, ale chyba każda użyta funkcja ma jakąś dokumentację?

0

O boże ale to proste z tymi kolorami ;] faktycznie. Przejrzę jeszcze raz te dokumentacje bo mam ją w pdfie ...

EDIT:
Chyba już wiem o co chodzi. Z dokumentacji http://www.un4seen.com/doc/ wynika, że:

 
if (peak[0]<LOWORD(level)) peak[0]=LOWORD(level); // set left peak
		if (peak[1]<HIWORD(level)) peak[1]=HIWORD(level); // set right peak
		if (!BASS_ChannelIsActive(decoder)) pos=-1; // reached the end
		else pos=BASS_ChannelGetPosition(decoder,BASS_POS_BYTE)/bpp;
		if (pos>cpos) {
			DWORD a;
			for (a=0;a<peak[0]*(HEIGHT/2)/32768;a++)
				wavebuf[(HEIGHT/2-1-a)*WIDTH+cpos]=1+a; // draw left peak
			for (a=0;a<peak[1]*(HEIGHT/2)/32768;a++)
				wavebuf[(HEIGHT/2+1+a)*WIDTH+cpos]=1+a; // draw right peak
			if (pos>=WIDTH) break; // gone off end of display
			cpos=pos;
			peak[0]=peak[1]=0;
		}

peak[0]=LOWORD(level); // set left peak
		if (peak[1]<HIWORD(level)) peak[1]=HIWORD(level); // set right peak 

Ustawiamy poziom lewego głośnika na niższe słowo level a prawy na wyższe słowo level

if (!BASS_ChannelIsActive(decoder)) pos=-1; // reached the end
		else pos=BASS_ChannelGetPosition(decoder,BASS_POS_BYTE)/bpp; 

Sprawdzamy, czy kanał jest aktywny (jeżeli nie jest aktywny ustawiamy pozycję na -1). Jeżeli jest ustawiamy bpp. Jest to jak mniemam skrót od "byte per pixel". Oznaczało to by wtedy, że będę zapisywał do bitmapy co n*bpp bajt. Pozostaję dalej w niewiedzy jak pobrać natężenie dźwięku z tego bajtu (czy klatki). I tutaj Azarien miał rację, abym przezwyciężył lenistwo i zajrzał do dokumentacji po raz kolejny.

 DWORD a;
			for (a=0;a<peak[0]*(HEIGHT/2)/32768;a++)
				wavebuf[(HEIGHT/2-1-a)*WIDTH+cpos]=1+a; // draw left peak
			for (a=0;a<peak[1]*(HEIGHT/2)/32768;a++)
				wavebuf[(HEIGHT/2+1+a)*WIDTH+cpos]=1+a; // draw right peak

Wiem już w 90% co oznacza ten kawałek kodu.
Na poczatku dzialanie: peak[0](HEIGHT/2)/32768 oraz peak[1](HEIGHT/2)/32768.
peak - natężenie dźwięku od wartości 0 do 32768.
HEIGHT / 2 - wysokosc na jakiej narysowana będzie linia symbolizująca natężenie odpowiedniej klatki (n*bpp)
Tak więc natężenie nasze to (peak / 32768) pomnozone przez (HEIGHT / 2) da nam ilosc pixeli w pionie jakie mamy pomalowac.


A teraz coś czego dalej nie rozumiem:
wavebuf[(HEIGHT/2-1-a)*WIDTH+cpos]=1+a;

Nie wiem, czy dobrze wymysliłem to sobie, ale myslę, że wavebuf jest tablicą wielowymiarową [y][x] lub [x][y] (nie potrafię jednoznacznie określić).
(HEIGHT/2-1-a)WIDTH jest Y, a cpos jest X (czyli jak mniemam jest naszym "n" we wzorze n*bpp).
Jeżeli nawet tak jest, to co się przypisuje do tego wavebuf[ ] ? "1+a" jest jakimś konkretnym kolorem czy jak? wiem, że w systemie RGB kolor jest reprezentowany w postaci szestnastkowej 0x ff (R) ff (G) ff (B) i używam do konwertowania funkcji RGB(int R, int G, int B). Czy dobrze myślę? Jeżeli dalej moje wypociny mają jako taki sens to przejdę dalej.

Najmniejszego pojęcia nie mam gdzie jest zmieniana wartość cpos. Bo z tego co przeleciałem po kodzie wynika, że odczytuję cały czas wartość cpos = pos, a pos = BASS_ChannelGetPosition(decoder,BASS_POS_BYTE)/bpp; Nieogarniam :D Jak macie czas to poprawcie wszystko to, co napisałem źle. Każda uwaga jest cenna.

0

Wykombinowałem w swoim kodzie coś takiego, ale nie działa...

case WM_DROPFILES:{
            struct WIDMO *wsk = (struct WIDMO *)GetWindowLong(hwnd, 0);

            HDROP hDrop = (HDROP) wParam;
            DragQueryFile(hDrop, 0, wsk->Sciezka, 256);

            wsk->chan =BASS_StreamCreateFile(FALSE,wsk->Sciezka,0,0,BASS_STREAM_DECODE);
            if (!wsk->chan) wsk->chan=BASS_MusicLoad(FALSE,wsk->Sciezka,0,0,BASS_MUSIC_DECODE,1);
            if(wsk->chan) ///rysujemy widmo
            {
                int WIDTH = 800;
                int HEIGHT = 150;

                wsk->IsEnable = true;
                for(int y=0; y<160; y++)
                for(int x=0; x<800; x++)
                {
                    double odleglosc = sqrt(pow(80-y, 2));
                    double col = 255 -  (odleglosc / 80) * 255;
                    SetPixel(wsk->hDCbmp, x, y, RGB((int)col,0,(int)col));
                }

                wsk->bpp=BASS_ChannelGetLength(wsk->chan,BASS_POS_BYTE)/800; // bytes per pixel
                if (wsk->bpp < (int)BASS_ChannelSeconds2Bytes(wsk->chan,0.02)) // minimum 20ms per pixel (BASS_ChannelGetLevel scans 20ms)
                 wsk->bpp=BASS_ChannelSeconds2Bytes(wsk->chan,0.02);


                DWORD decoder = wsk->chan;
                DWORD peak[2] = {0};
                DWORD level=BASS_ChannelGetLevel(decoder);
                if (peak[0]<LOWORD(level)) peak[0]=LOWORD(level);
                if (peak[1]<HIWORD(level)) peak[1]=HIWORD(level);
                if (BASS_ChannelIsActive(decoder))
                {
                    for(int x=0; x<WIDTH; x++)
                    {
                        BASS_ChannelSetPosition(decoder,x * wsk->bpp,BASS_POS_BYTE);
                        DWORD level=BASS_ChannelGetLevel(decoder);
                        if (peak[0]<LOWORD(level)) peak[0]=LOWORD(level);
                        if (peak[1]<HIWORD(level)) peak[1]=HIWORD(level);
                        for (DWORD y=0; y < peak[0] * (HEIGHT/2)/32768; y++)
                            SetPixel(wsk->hDCbmp, HEIGHT/2-1-y, x, RGB(0,0,0));
                        for (DWORD y=0; y < peak[1] * (HEIGHT/2)/32768; y++)
                            SetPixel(wsk->hDCbmp, HEIGHT/2+1+y, x, RGB(0,0,0));
                        InvalidateRect(hwnd, NULL, TRUE);
                    }
                }

                BASS_StreamFree(decoder);
            }
            DragFinish(hDrop);
            break;
        }
 

Po wrzucenie pliku muzycznego na obszar kontrolki jest rysowane widmo tego utworu w kontekscie wsk->hDCbmp ... Ale nie widze tego

0

Już sobie poradziłem. temat do usuniecia

0

Hih. Miałem kopię na pendrivie.
A więc tak. Struktura kontrolki wygląda tak:

 
struct WIDMO{
    HBITMAP bitmapa;
    HDC hDC, hDCbmp;

    INT cX;
    INT cY;

    bool IsEnable;
    DWORD chan;
    DWORD bpp;
    DWORD Output;
    char Sciezka[256];
    DWORD start_pos;
    DWORD end_pos;
    int Volume;
    DWORD xSTART, xMETA, xPOS;

    bool wyswietlaj_aktualny_pixel;
    DWORD aktualny_pixel;
};

A część odpowiedzialna za rysowanie kontrolki gdy nałożę na nią jakiś plik muzyczny tak:

 
case WM_DROPFILES:{
            struct WIDMO *wsk = (struct WIDMO *)GetWindowLong(hwnd, 0);

            HDROP hDrop = (HDROP) wParam;
            DragQueryFile(hDrop, 0, wsk->Sciezka, 256);
            if (!(wsk->chan=BASS_StreamCreateFile(FALSE,wsk->Sciezka,0,0,0))
            && !(wsk->chan=BASS_MusicLoad(FALSE,wsk->Sciezka,0,0,BASS_MUSIC_RAMPS|BASS_MUSIC_POSRESET|BASS_MUSIC_PRESCAN,1))) {
            break;
            }

            const DWORD WIDTH = 800;
            const DWORD HEIGHT = 150;

            wsk->bpp=BASS_ChannelGetLength(wsk->chan,BASS_POS_BYTE)/WIDTH; // bytes per pixel
            if (wsk->bpp<BASS_ChannelSeconds2Bytes(wsk->chan,0.02)) // minimum 20ms per pixel (BASS_ChannelGetLevel scans 20ms)
                wsk->bpp=BASS_ChannelSeconds2Bytes(wsk->chan,0.02);
            { // start scanning peaks in a new thread
                DWORD chan2=BASS_StreamCreateFile(FALSE,wsk->Sciezka,0,0,BASS_STREAM_DECODE);
                if (!chan2) chan2=BASS_MusicLoad(FALSE,wsk->Sciezka,0,0,BASS_MUSIC_DECODE,1);
                if (!chan2)
                  break;
                wsk->IsEnable = true;
                wsk->xPOS = 0;
                wsk->xSTART = 0;
                wsk->xMETA = 0;
                wsk->start_pos = 0;
                wsk->end_pos = 0;
                wsk->Volume = 100;

                { /// rysowanie widma
                    for(int y=0; y<160; y++)
                    for(int x=0; x<800; x++)
                    {
                        double odleglosc = sqrt(pow(80-y, 2));
                        double col = 255 -  (odleglosc / 80) * 255;
                        SetPixel(wsk->hDCbmp, x, y, RGB((int)col,0,(int)col));
                    }

                    DWORD decoder=chan2;
                    DWORD cpos=0,peak[2]={0};
                    while (true)
                    {
                        DWORD level=BASS_ChannelGetLevel(decoder); // scan peaks
                        DWORD pos;
                        if (peak[0]<LOWORD(level)) peak[0]=LOWORD(level); // set left peak
                        if (peak[1]<HIWORD(level)) peak[1]=HIWORD(level); // set right peak
                        if (!BASS_ChannelIsActive(decoder)) pos=-1; // reached the end
                        else pos=BASS_ChannelGetPosition(decoder,BASS_POS_BYTE)/wsk->bpp;
                        if (pos>cpos)
                        {
                            DWORD a;
                            for (a=0;a<peak[0]*(HEIGHT/2.2)/32768;a++)
                            {
                                SetPixel(wsk->hDCbmp, cpos, (HEIGHT/2-1-a), RGB(200,0,150));
                            }

                            for (a=0;a<peak[1]*(HEIGHT/2.2)/32768;a++)
                            {
                                SetPixel(wsk->hDCbmp, cpos, (HEIGHT/2+1+a), RGB(200,0,150));
                            }
                            if (pos>=WIDTH)
                                break; // gone off end of display
                            cpos=pos;
                            peak[0]=peak[1]=0;
                        }
                        InvalidateRect(hwnd,NULL,TRUE);
                    }
                    BASS_StreamFree(decoder); // free the decoder
                }
            }

            DragFinish(hDrop);
            break;
        }

To było banalnie proste jak poczytałem dokumentację. Ale teraz mam kolejny problem bo nie wiem jak zmienić głośność poszczególnego kanału xD I z dokumentacji już się doczytać nie mogę :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1