25 May '13 - 00:22 *
Welcome, Guest. Please login or register.
Did you miss your activation email?

Login with username, password and session length
 
   Home   Help Search Login Register  
Pages: [1]
  Reply  |  Print  
Author Topic: Problem with BASSWASAPI version of livespec  (Read 536 times)
dperrow
Posts: 5


« on: 5 Apr '12 - 20:36 »
Reply with quoteQuote

Hi,
   Hope someone can help, I've modified the BASS c sample livespec to use BASSWASAPI so that it will run correctly on Windows 7.

It runs fine except when using VLC and when I stop the music playing by exiting the program I get a continuous buzzing from the speakers until I either stop livespec or play something else.

I want to use this code for something else I'm working on so any help would be appreciated.

The code looks like:

/*
BASS "live" spectrum analyser example
Copyright (c) 2002-2010 Un4seen Developments Ltd.
*/

#define BASS_CONFIG_REC_LOOPBACK 28

#include <windows.h>
#include <stdio.h>
#include <math.h>
#include "basswasapi.h"
#include "bass.h"

#define SPECWIDTH 368 // display width
#define SPECHEIGHT 127 // height (changing requires palette adjustments too)

HWND win=NULL;
DWORD timer=0;
static DWORD rcounter=0;

HRECORD chan; // recording channel

HDC specdc=0;
HBITMAP specbmp=0;
BYTE *specbuf;

int audio_device=(-1);

int specmode=0,specpos=0; // spectrum mode (and marker pos for 2nd mode)
int spectrum_width=16;

// display error messages
void Error(const char *es)
{
char mes[200];
sprintf(mes,"%s\n(error code: %d)",es,BASS_ErrorGetCode());
MessageBox(win,mes,0,0);
}

// display error messages
void Errori(const char *es, DWORD i)
{
char mes[200];
sprintf(mes,"%s\n return=%d\n (error code: %d)",es,i,BASS_ErrorGetCode());
MessageBox(win,mes,0,0);
}
static int cval=0;
// update the spectrum display - the interesting bit :)
void CALLBACK UpdateSpectrum(UINT uTimerID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
static DWORD quietcount=0;
HDC dc;
int x,y,y1;
char str[80];
DWORD ires;

    float fft[2048]; // 1024 fft values returned *2
    int i;
    for (i=0; i<2048;i++) fft[i]=0.0;
    y1=0;
        ires=BASS_WASAPI_GetData(fft,BASS_DATA_FFT2048 ); // get the FFT data
        if ((ires!=0) && (ires!=16384)) { Errori("GetData ",ires); return; }
  if (!specmode)
          { // "normal" FFT
memset(specbuf,0,SPECWIDTH*SPECHEIGHT);
for (x=0;x<SPECWIDTH/2;x++)
            {
y=sqrt(fft[x+1])*3*SPECHEIGHT-4; // scale it (sqrt to make low values more visible)
if (y<0) y=(0-y);
                if (y>SPECHEIGHT) y=SPECHEIGHT; // cap it
if (x && (y1=(y+y1)/2)) // interpolate from previous to make the display smoother
while (--y1>=0) specbuf[y1*SPECWIDTH+x*2-1]=y1+1;
y1=y;
while (--y>=0) specbuf[y*SPECWIDTH+x*2]=y+1; // draw level
}
  }
int b0=0; int maxy=0;

memset(specbuf,0,SPECWIDTH*SPECHEIGHT);
for (x=0;x<spectrum_width;x++)
            {
float peak=0;
int b1=pow(2,x*10.0/(spectrum_width-1));
if (b1>1023) b1=1023;
if (b1<=b0) b1=b0+1; // make sure it uses at least 1 FFT bin
for (;b0<b1;b0++)
if (peak<fft[1+b0]) peak=fft[1+b0];
y=sqrt(peak)*3*SPECHEIGHT-4; // scale it (sqrt to make low values more visible)
if (y>SPECHEIGHT) y=SPECHEIGHT; // cap it
while (--y>=0)
memset(specbuf+y*SPECWIDTH+x*(SPECWIDTH/spectrum_width),y+1,0.9*(SPECWIDTH/spectrum_width)); // draw bar
if (y>maxy) maxy=y;
}
// update the display
dc=GetDC(win);
BitBlt(dc,0,0,SPECWIDTH,SPECHEIGHT,specdc,0,0,SRCCOPY);
if (LOWORD(BASS_WASAPI_GetLevel(audio_device))<500) { // check if it's quiet
quietcount++;
if (quietcount>40 && (quietcount&16)) { // it's been quiet for over a second
RECT r={0,0,SPECWIDTH,SPECHEIGHT};
SetTextColor(dc,0xffffff);
SetBkMode(dc,TRANSPARENT);
DrawText(dc,"make some noise!",-1,&r,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
}
} else
quietcount=0; // not quiet
ReleaseDC(win,dc);
}

// Recording callback - not doing anything with the data
DWORD CALLBACK DuffRecording(void *buffer, DWORD length, void *user)
{   rcounter=length;
return rcounter; // continue recording
}

// window procedure
long FAR PASCAL SpectrumWindowProc(HWND h, UINT m, WPARAM w, LPARAM l)
{
switch (m) {
case WM_PAINT:
if (GetUpdateRect(h,0,0)) {
PAINTSTRUCT p;
HDC dc;
if (!(dc=BeginPaint(h,&p))) return 0;
BitBlt(dc,0,0,SPECWIDTH,SPECHEIGHT,specdc,0,0,SRCCOPY);
EndPaint(h,&p);
}
return 0;

case WM_LBUTTONUP:
specmode=(specmode+1)%4; // swap spectrum mode
memset(specbuf,0,SPECWIDTH*SPECHEIGHT); // clear display
return 0;

case WM_CREATE:
win=h;
DWORD i; char str[80];     
            BASS_WASAPI_DEVICEINFO info;

            for (i=0; BASS_WASAPI_GetDeviceInfo(i, &info); i++)
               {
                if (
                  (info.type==BASS_WASAPI_TYPE_SPEAKERS)
                && (info.flags&BASS_DEVICE_INPUT) // device is an loopback device (not input)
                      && (info.flags&BASS_DEVICE_LOOPBACK)
                      && (info.flags&BASS_DEVICE_ENABLED)
                      ) // and it is enabled
                         { audio_device=i;
                         break;
                         }}

// initialize BASS recording (default device)
              if (!BASS_Init(0, 44100, 0, h, NULL))
{ Error("Can't initialize device");
return -1;
}
             
              if(!BASS_WASAPI_Init(audio_device,44100,2,BASS_WASAPI_BUFFER ,0.5,0,&DuffRecording,NULL))
{ Error("Can't initialize device");
return -1;
}
// start recording (44100hz mono 16-bit)
if (!(BASS_WASAPI_Start())) {
Error("Can't start recording");
return -1;
}

{ // create bitmap to draw spectrum in (8 bit for easy updating)
BYTE data[2000]={0};
BITMAPINFOHEADER *bh=(BITMAPINFOHEADER*)data;
RGBQUAD *pal=(RGBQUAD*)(data+sizeof(*bh));
int a;
bh->biSize=sizeof(*bh);
bh->biWidth=SPECWIDTH;
bh->biHeight=SPECHEIGHT; // upside down (line 0=bottom)
bh->biPlanes=1;
bh->biBitCount=8;
bh->biClrUsed=bh->biClrImportant=256;
// setup palette
for (a=1;a<128;a++) {
pal[a].rgbGreen=256-2*a;
pal[a].rgbRed=2*a;
}
for (a=0;a<32;a++) {
pal[128+a].rgbBlue=8*a;
pal[128+32+a].rgbBlue=255;
pal[128+32+a].rgbRed=8*a;
pal[128+64+a].rgbRed=255;
pal[128+64+a].rgbBlue=8*(31-a);
pal[128+64+a].rgbGreen=8*a;
pal[128+96+a].rgbRed=255;
pal[128+96+a].rgbGreen=255;
pal[128+96+a].rgbBlue=8*a;
}
// create the bitmap
specbmp=CreateDIBSection(0,(BITMAPINFO*)bh,DIB_RGB_COLORS,(void**)&specbuf,NULL,0);
specdc=CreateCompatibleDC(0);
SelectObject(specdc,specbmp);
}

// setup update timer (40hz)
timer=timeSetEvent(25,25,(LPTIMECALLBACK)&UpdateSpectrum,0,TIME_PERIODIC);
break;

case WM_DESTROY:
if (timer) timeKillEvent(timer);
BASS_WASAPI_Free();
if (specdc) DeleteDC(specdc);
if (specbmp) DeleteObject(specbmp);
PostQuitMessage(0);
break;
}
return DefWindowProc(h, m, w, l);
}

int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS wc={0};
    MSG msg; char str[256];

// check the correct BASS was loaded
if (HIWORD(BASS_GetVersion())!=BASSVERSION) {
MessageBox(0,"An incorrect version of BASS.DLL was loaded",0,MB_ICONERROR);
return 0;
}

// register window class and create the window
wc.lpfnWndProc = SpectrumWindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = "BASS-Spectrum";
if (!RegisterClass(&wc) || !CreateWindow("BASS-Spectrum",
"\"Live\" spectrum display",
WS_POPUPWINDOW|WS_CAPTION|WS_VISIBLE, 200, 200,
SPECWIDTH+2*GetSystemMetrics(SM_CXDLGFRAME),
SPECHEIGHT+GetSystemMetrics(SM_CYCAPTION)+2*GetSystemMetrics(SM_CYDLGFRAME),
NULL, NULL, hInstance, NULL)) {
Error("Can't create window");
return 0;
}
ShowWindow(win, SW_SHOWNORMAL);

while (GetMessage(&msg,NULL,0,0)>0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}
Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #1 on: 6 Apr '12 - 15:33 »
Reply with quoteQuote

It runs fine except when using VLC and when I stop the music playing by exiting the program I get a continuous buzzing from the speakers until I either stop livespec or play something else.

That sounds most likely to be a driver issue. If you can, please try to reproduce it with a different soundcard and/or on another system. Does the problem only happen when it's VLC playing the music, not another player? If so, you could try playing with its output options to find what's triggering the problem.
Logged
dperrow
Posts: 5


« Reply #2 on: 6 Apr '12 - 15:54 »
Reply with quoteQuote

Thanks Ian but the problem also occurs. on my media pc with Media portal. Also windows 7 but sound via an ati graphics adapter/hdmi.
Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #3 on: 6 Apr '12 - 18:13 »
Reply with quoteQuote

Ah yes, I have now tried your code, and I see/hear what you mean. The buzzing seems to be a very short piece of old sample data being repeated, eg. if you were playing silence then there will be no buzzing. I got it with an on-board Realtek device and an ATI HDMI device, but not with a couple of USB devices. So it does appear to be somewhat driver related. One way to get around it is to keep the output device initialized, so you could try adding a BASS_WASAPI_Init call for that (without BASS_WASAPI_Start). With there being 2 initalized devices, you would then also need to use BASS_WASAPI_SetDevice to set the device context. For example, you could make these modifications...

     BASS_WASAPI_SetDevice(audio_device); // set device context to input/loopback device
      ires=BASS_WASAPI_GetData(fft,BASS_DATA_FFT2048 ); // get the FFT data
...
              if(!BASS_WASAPI_Init(audio_device,44100,2,BASS_WASAPI_BUFFER ,0.5,0,&DuffRecording,NULL))
{ Error("Can't initialize device");
return -1;
}
              BASS_WASAPI_Init(audio_device-1,0,0,0,0.5,0,NULL,NULL); // initialize corresponding output device
              BASS_WASAPI_SetDevice(audio_device); // set device context back to input/loopback device
...
BASS_WASAPI_Free();
BASS_WASAPI_Free(); // release the 2nd device too
Logged
dperrow
Posts: 5


« Reply #4 on: 6 Apr '12 - 20:46 »
Reply with quoteQuote

Excellent,
              thanks. I don't know that I fully understand why this works but it does. Question about the close down code though. I have now initialised 3 devices: nosound, audio_device and audio_device-1. So should the code look like:

   BASS_WASAPI_SetDevice(audio_device);     // set device context back to input/loopback device
   BASS_WASAPI_Free();
   BASS_WASAPI_SetDevice(audio_device-1);  // set device context back to playback device
   BASS_WASAPI_Free();
   BASS_SetDevice(0);                      // set device context back to nosounddevice
   BASS_Free();                               // free null device too

Also is there any difference between BASS_WASAPI_FREE() and BASS_FREE() ?

BTW I'm using this code for a spectrum display plugin for LCDSmartie which I'll be releasing in the not too distant future as freeware. I'm currently using it with MediPortal on my media PC but this should work with media centre, winamp and whatever else.

Thanks again for your help,
Dave
Logged
Ian @ un4seen
Administrator
Posts: 15276


« Reply #5 on: 10 Apr '12 - 16:19 »
Reply with quoteQuote

I don't know that I fully understand why this works but it does.

I'm not certain what/where the problem is myself, but it seems to be that there is a small piece of data left in a buffer somewhere in the device driver (or perhaps Windows) once the output device has been released by the player. So the purpose of the extra BASS_WASAPI_Init call is just to keep the device initialized throughout.

Question about the close down code though. I have now initialised 3 devices: nosound, audio_device and audio_device-1. So should the code look like:

   BASS_WASAPI_SetDevice(audio_device);     // set device context back to input/loopback device
   BASS_WASAPI_Free();
   BASS_WASAPI_SetDevice(audio_device-1);  // set device context back to playback device
   BASS_WASAPI_Free();
   BASS_SetDevice(0);                      // set device context back to nosounddevice
   BASS_Free();                               // free null device too

When the intention is to free every device, you don't really need to bother with the SetDevice calls as the thread's device context will automatically be set to another initialized device after the current device has been freed. To free all initialized devices, regardless of how many there are, you could simply do this...

while (BASS_WASAPI_Free()) ; // free all BASSWASAPI devices
while (BASS_Free()) ; // free all BASS devices

Also is there any difference between BASS_WASAPI_FREE() and BASS_FREE() ?

Yes, BASS_WASAPI_Free can only be used to free devices that have been initialized with BASS_WASAPI_Init, and BASS_Free can only be used to free devices that have been initialized with BASS_Init.
Logged
dperrow
Posts: 5


« Reply #6 on: 10 Apr '12 - 16:36 »
Reply with quoteQuote

Thanks for all your help Ian.
                                    I've released the program as freeware and it can be found at: http://lcdspc.codeplex.com/

It's working great on my Media PC. Feel free to use the BASSWASAPI livespec source as an example if you wish.

regards,
Dave
Logged
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.18 | SMF © 2013, Simple Machines