FMOD: Improved Spectrum Analyser?

BlitzMax Forums/BlitzMax Programming/FMOD: Improved Spectrum Analyser?

Hi guys!

Anyone here that ever made a Winamp(tm) like spectrum anaylser with fmod?



So far I have only managed to come up with this. But that's not half as good as it should be... :/

Example: (you need a tune and the fmod.dll inside your folder!)
'Grisu's little & crappy Spectrum analyser using fmod 3.5

Graphics 800,600,0

FSOUND_Init(44100, 32,FSOUND_INIT_ACCURATEVULEVELS)
FSOUND_DSP_SetActive(FSOUND_DSP_GetFFTUnit(), True)

Function Refresh_Spectrum()
Local spec:Byte Ptr= FSOUND_DSP_GetSpectrum()
            Local bnk:TBank =Null
        	bnk:TBank = CreateStaticBank(spec,512*4)
            SetBlend Alphablend
			SetAlpha 0.6
			For Local x:Int=0 To 253 'Step 2
					Local yp:Int=(PeekFloat(bnk,x*4)*500)
					SetColor (x/8*32,x/8*16,(128)+x/8*2)
                    yp=yp*4 
                    If yp>60 Then yp=60
					DrawLine 40+x,274+60-yp,40+x,274+60
			Next
End Function  

Music=FSOUND_Stream_Open("trailer_120.ogg",FSOUND_LOOP,0,0) ' <- Insert a tune here!
FSOUND_Stream_Play(FSOUND_FREE,music)

Repeat
	
	Cls()

	SetAlpha 0.1 ' Draw grey rectangle area of maximum visualisation.
    SetColor 255,255,255
	DrawRect 40,0,336,336

    Refresh_Spectrum()	
	
	Flip()
	
Until KeyHit(KEY_ESCAPE) Or AppTerminate()

FSOUND_Stream_Close(music)
Fsound_Close()

End


Another problem is that when I turn down the volume of a tune the size of the spectrum bars shrinks accordingly. Any way to prevent that from happening? (So that the maximum width and height stay the same all time.)

Thanks in advance!
Grisu

Can you not just divide the values returned into your static bank by the current volume as you draw the bars? Thus keeping the bars relative to the total volume at all times?

Not quite sure what you mean Gabriel. Let alone know how to implement that.

Why would i divide the value, wouldn't that decrease the total output. Sorry, I'm a newb in terms of this audio stuff.

What I mean is this :

If the volume is 1

OriginalBarheight/1= OriginalBarheight

If the volume is 0.5

Barheight/0.5 = Barheight*2 = OriginalBarheight

If the volume is 0.25

Barheight/0.25 = Barheight*4 = OriginalBarheight

If the volume doesn't come as a decimal fraction then yes, it would make it smaller, in which case you'd have to turn it into a decimal fraction. ( EG: If volume can be 0-50 where 50 is the highest you'd have to divide the volume by 50 to get the decimal fraction.

In which case you'd simplify and end up with

CurrentBarheight=OriginalBarheight/MaxVolume*CurrentVolume

What would happen if the sound was muted, or set to 0?

For my game (http://www.nicholaskingsley.co.uk/FightTune), I use the following code :

int FMOD_Spectrograph5(int channel,int difficultyLevel,char *storeSpec,int SPECHEIGHT,int BANDS,
			 int NIGHTMARE)
{
register int b0,x,sc,b1,loop;
register char y;
float fft[3][FFT_SIZE];
float sum;
int	result;

	result=0;
	if (FMOD_Channel_GetSpectrum((FMOD_CHANNEL *) channel,&fft[0][0],FFT_SIZE,0,FMOD_DSP_FFT_WINDOW_TRIANGLE)==FMOD_OK &&
		FMOD_Channel_GetSpectrum((FMOD_CHANNEL *) channel,&fft[1][0],FFT_SIZE,1,FMOD_DSP_FFT_WINDOW_TRIANGLE)==FMOD_OK)
	{
		for (x=0; x<FFT_SIZE; x++)
		{
			fft[2][x]=(fft[0][x]+fft[1][x])/2.0;
		}

		b0=0;
		for (x=0;x<BANDS;x++) 
		{
			sum=0.0;
			
			b1=pow(2,x*10.0/(BANDS-1));
			if (b1>=FFT_SIZE) 
			{
				b1=FFT_SIZE-1;
			}
			
			if (b1<=b0) 
			{
				b1=b0+1; // make sure it uses at least 1 FFT bin
			}
			
			sc=10+b1-b0;
			for (;b0<b1;b0++) 
			{
				sum+=fft[2][1+b0];
			}
			
			y=(char) ((sqrt(sum/log10(sc))*1.7*SPECHEIGHT)-4.0); // scale it
			y=(y>SPECHEIGHT ? SPECHEIGHT : \
				y<0 ? 0 : y); 
			
			*(storeSpec+x)=(char) y;
			
			if (diff[x]!=y)
			{
				diff[x]=y;
				result=(x==0 ? result | LEFT_BIT : \
						x==1 ? result | UP_BIT : \
						x==2 ? result | RIGHT_BIT : result | DOWN_BIT);
			}
		}
     }


Which produces 4 bars

For waveform, I used :

void FMOD_Spectrograph1(int channel,int SPECWIDTH,int SPECHEIGHT,char *specBuff)
{
register int c,x,v,y,loop;
int channels;
float *buf;
float fft[FFT_SIZE];

	if (specBuff==NULL)	return;

	memset(specBuff,(char) 0,SPECWIDTH*SPECHEIGHT);
	if (FMOD_System_GetSoftwareFormat(FMODSystem,NULL,NULL,&channels,NULL,NULL,NULL)==FMOD_OK)
	{
		for (loop=0; loop<2; loop++)
		{
			if (FMOD_System_GetWaveData(FMODSystem,&fft,FFT_SIZE,loop)==FMOD_OK)
			{
				for (x=0; x<SPECWIDTH; x++)
				{
					v=(1-fft[x])*SPECHEIGHT/2;

					if (v<0)
					{
						v=0;
					}
					else
					if (v>SPECHEIGHT)
					{
						v=SPECHEIGHT;
					}

					if (!x) 
					{
						y=v;
					}
					
					do { // draw line from previous sample...
						if (y<v) 
						{
							y++;
						}
						else 
						if (y>v) 
						{
							y--;
						}
							
						specBuff[y*SPECWIDTH+x]=(loop==0 ? 127 : 1); 
					} while (y!=v);
				}
			}
		}
	}
}


However, this will only work with the latest version of FMOD

What would happen if the sound was muted, or set to 0?

You'd do what ( I hope ) you always do when dividing by an unknown variable. Put an If..EndIf around it to make sure it isn't zero.

Thanks everyone. Slowly getting there. Still looking for the winamp fading effect when the bars are displayed.

'Grisu's little & crappy Spectrum analyser using fmod 3.5

Graphics 800,600,0

FSOUND_Init(44100, 32,FSOUND_INIT_ACCURATEVULEVELS)
FSOUND_DSP_SetActive(FSOUND_DSP_GetFFTUnit(), True)

Function Refresh_Spectrum2(xpos:Int, ypos:Int)
Local spec:Byte Ptr= FSOUND_DSP_GetSpectrum()
Local bnk:TBank    = CreateStaticBank(spec,32)

 'SetBlend Alphablend
 'SetAlpha 1.0
 SetColor 0,0,0
 DrawRect xpos-2,ypos+1,53,-28

 SetColor 105,105,105
 DrawLine xpos-1,ypos+1,xpos-1+51,ypos+1

			For Local x:Int=0 To 32 Step 2
					Local output:Float=(PeekFloat(bnk,x*8)*100)

                    output=output*(100/CurrentVolume)  					
                 
                    If output > 1 Then 
                      SetColor 210,50,10
                      DrawRect xpos+x,ypos-1,2,2 
 
                    If output > 2 Then 
                       SetColor 215,95,0       
                       DrawRect xpos+x,ypos-4,2,2 

                    If output > 3 Then 
                       SetColor 215,110,0
                       DrawRect xpos+x,ypos-7,2,2 

                    If output > 4 Then 
                       SetColor 202,130,10
                       DrawRect xpos+x,ypos-10,2,2 
    	         
                    If output > 5 Then 
    	               SetColor 220,170,30
                       DrawRect xpos+x,ypos-13,2,2 

                    If output > 6 Then 
                       SetColor 200,205,40
                       DrawRect xpos+x,ypos-16,2,2 

                    If output > 7 Then 
                       SetColor 150,220,30
                       DrawRect xpos+x,ypos-19,2,2 

                    If output > 8 Then 
                       SetColor 45,200,20
                       DrawRect xpos+x,ypos-22,2,2 

                    If output > 9 Then 
                       SetColor 55,185,15
                       DrawRect xpos+x,ypos-25,2,2 
    
                    If output = 10 Then 
                       SetColor 0,255,0                    
                       DrawRect xpos+x,ypos-28,2,2 

                    EndIf      ' = 10

                    EndIf      ' > 9
    
                    EndIf      ' > 8
                
                    EndIf      ' > 7
 
                    EndIf      ' > 6
 
                    EndIf      ' > 5

                    EndIf      ' > 4

                    EndIf      ' > 3

                    EndIf      ' > 2

                    EndIf      ' > 1
   
                    xpos=xpos+1
			Next
End Function  

Global CurrentVolume:Int=100 '50%

Music=FSOUND_Stream_Open("trailer_120.ogg",FSOUND_LOOP,0,0) ' <- Insert a tune here!
FSOUND_SetSFXMasterVolume(CurrentVolume)
FSOUND_Stream_Play(FSOUND_FREE,music)


Repeat

    SetClsColor 50,50,50
    Cls 

    Refresh_Spectrum2(40,40)	
	
	Flip()
	
Until KeyHit(KEY_ESCAPE) Or AppTerminate()

FSOUND_Stream_Close(music)
Fsound_Close()

End


Is this what you mean Grisu?

'Graphics(257,201,0,0)
Graphics(640,480,0,0)

FSOUND_Init(44100, 32,FSOUND_INIT_ACCURATEVULEVELS)
FSOUND_DSP_SetActive(FSOUND_DSP_GetFFTUnit(), True)

Global	SPECypos:Float[32]
Global	SPECtop:Float[32]
Global	SPECspeed:Float[32]

Global	SPECgrab:timage=CreateImage(254,198)
SetImageHandle(SPECgrab,127,99)
SetBlend(ALPHABLEND)

Function	Speccy(posx:Int,posy:Int)
	Local	x:Int,y:Int,beat:Float
	Local	spec:Byte Ptr=FSOUND_DSP_GetSpectrum()
	Local	bnk:TBank=CreateStaticBank(spec,32)

	For x=0 To 31
		SPECypos[x]=(PeekFloat(bnk,x*8)*100)
		If SPECypos[x]>SPECtop[x]
			SPECtop[x]=SPECypos[x]
			SPECspeed[x]=0
		EndIf
	Next

	SetAlpha(1)
	beat=1+(((SPECypos[1]+SPECypos[2]+SPECypos[3])/3)/200)
	For x=0 To 31
		For y=0 To 200 Step 2
			If (y Mod 8)=0
				SetColor(96*beat,96*beat,96*beat)
			Else
				SetColor(64*beat,64*beat,64*beat)
			EndIf
			DrawLine((posx+(x*8))+1,posy-y,(posx+(x*8))+7,posy-y)
		Next
	Next

	For x=0 To 31
		SetColor(200,200,200)
		SetAlpha(0.5)
		DrawRect(posx+(x*8),posy-(SPECypos[x]*2),8,(SPECypos[x]*2))
		SetColor(255,255,255)
		SetAlpha(1)
		DrawRect(posx+(x*8),posy-(SPECtop[x]*2),8,1)
	Next

	For x=0 To 31
		If SPECtop[x]>0
			SPECtop[x]=SPECtop[x]-SPECspeed[x]
			SPECspeed[x]=SPECspeed[x]+0.0125	'<--- Gravity
		EndIf
	Next

	SetAlpha(1)
	SetColor(255,255,255)
	DrawLine(posx,posy,posx+256,posy)
	DrawLine(posx,posy,posx,posy-200)
	DrawLine(posx,posy-200,posx+256,posy-200)
	DrawLine(posx+256,posy-200,posx+256,posy)
	
End Function

Global CurrentVolume:Int=256

Music=FSOUND_Stream_Open("02.mp3",FSOUND_LOOP,0,0)	'<--- Music
FSOUND_SetSFXMasterVolume(CurrentVolume)
FSOUND_Stream_Play(FSOUND_FREE,music)

Repeat
	Cls()
	Speccy((GraphicsWidth()-256)/2,(GraphicsHeight()/2)+100)	
	SetScale(0.99,0.99)
	SetAlpha(0.6)
	DrawImage(SPECgrab,GraphicsWidth()/2,(GraphicsHeight()/2))
	SetAlpha(1)
	SetScale(1,1)
	GrabImage(SPECgrab,(GraphicsWidth()-254)/2,(GraphicsHeight()-198)/2)
	Flip()
Until KeyHit(KEY_ESCAPE) Or AppTerminate()

FSOUND_Stream_Close(music)
Fsound_Close()

End



EDIT:Pasted the wrong source first time :)

Thanks a lot Andy! That is what I meant... :o)

Best is to run spectrum analysis on the playing sound buffer. It's not dependent from the volume settings, since it is outputted to the mixing buffers which are the ones which are affected by volume settings. So dividing by the volume level becomes useless.